网站地图    收藏   

主页 > 后端 > 网站安全 >

Total Commander 自校验算法分析 - 网站安全 - 自学

来源:自学PHP网    时间:2015-04-17 13:03 作者: 阅读:

[导读] 作 者: uuk【软件名称】: Total Commander【软件版本】: 7.56a【加壳方式】: 新版不加壳【编写语言】: Borland Delphi 2.0 [Overlay]【使用工具】: OD PEID IDA【操作平台】: Windows XP【软件介......

作 者: uuk
【软件名称】: Total Commander
【软件版本】: 7.56a
【加壳方式】: 新版不加壳
【编写语言】: Borland Delphi 2.0 [Overlay]
【使用工具】: OD PEID IDA
【操作平台】: Windows XP
【软件介绍】: 一款挺不错的双栏文件管理软件
【作者声明】: 只是研究用,使用请购买正版
 
我们知道Total Commander是有自校验的,通过跟踪CreateFile和ReadFile函数,确定校验过程是在函数sub_47823C中进行的。
 
代码:
0047823C    push    ebp            ;  自校验函数
0047823D    mov    ebp, esp
0047823F    add    esp, -824
00478245    mov    dword ptr [ebp-4], -1    ;  初始化校验和为CHECKSUM=-1
0047824C    lea    eax, dword ptr [ebp-814]
00478252    mov    dword ptr [ebp-18], eax
00478255    lea    eax, dword ptr [ebp-824]
0047825B    mov    edx, 3FF
00478260    call    006BE3AC            ;  GetModuleFileName()
00478265    lea    edx, dword ptr [ebp-1C]
00478268    mov    eax, dword ptr [6C7D58]
0047826D    mov    ecx, 4
00478272    call    004027B4
00478277    mov    eax, 8001
0047827C    call    00402684             ;  AllocateMem()
00478281    mov    dword ptr [ebp-C], eax
00478284    lea    eax, dword ptr [ebp-824]
0047828A    mov    ecx, 1
0047828F    xor    edx, edx
00478291    call    0041D70C            ;  CreateFile()
00478296    mov    dword ptr [ebp-10], eax
00478299    call    004035AC
0047829E    cmp    dword ptr [eax+C], 0
004782A5    je    short 004782BF
上面是函数的初始化。
 
代码:
004782F7    xor    eax, eax
004782F9    mov    dword ptr [ebp-24], eax
004782FC    mov    eax, dword ptr [ebp-10]
004782FF    call    00419874            ;  计算文件大小
00478304    sub    eax, 18
00478307    sub    eax, 0C            ;  文件大小减去36字节
0047830A    mov    dword ptr [ebp-20], eax
0047830D    cmp    dword ptr [ebp-20], 8000
00478314    jle    short 0047831E
00478316    mov    word ptr [ebp-12], 8000
0047831C    jmp    short 00478326
0047831E    mov    ax, word ptr [ebp-20]
00478322    mov    word ptr [ebp-12], ax
00478326    lea    eax, dword ptr [ebp-14]
00478329    push    eax
0047832A    mov    edx, dword ptr [ebp-C]
0047832D    mov    cx, word ptr [ebp-12]
00478331    mov    eax, dword ptr [ebp-10]
00478334    call    0041DE4C    ;  ReadFile():从文件中读取0x8000大小的数据块
00478339    cmp    dword ptr [ebp-24], 0
0047833D    jnz    short 0047834A
0047833F    lea    edx, dword ptr [ebp-20]
00478342    mov    eax, dword ptr [ebp-C]
00478345    call    00478054        ;  首次计算校验和前先清除Checksum和数字签名,并计算要校验的数据大小
0047834A    lea    ecx, dword ptr [ebp-4]
0047834D    movzx    edx, word ptr [ebp-14]
00478351    mov    eax, dword ptr [ebp-C]
00478354    call    0067FF10        ;  分块分片计算校验和
00478359    movzx    eax, word ptr [ebp-14]
0047835D    sub    dword ptr [ebp-20], eax
00478360    movzx    eax, word ptr [ebp-14]
00478364    add    dword ptr [ebp-24], eax
00478367    movzx    eax, word ptr [ebp-14]
0047836B    cmp    eax, 8000
00478370    jnz    short 00478378
00478372    cmp    dword ptr [ebp-20], 0
00478376    jnz    short 0047830D
00478378    mov    edx, 8001
0047837D    mov    eax, dword ptr [ebp-C]
00478380    call    0040269C        ;  FreeMem()
00478385    lea    eax, dword ptr [ebp-14]
00478388    push    eax
00478389    lea    edx, dword ptr [ebp-824]
0047838F    mov    cx, 24
00478393    mov    eax, dword ptr [ebp-10]
00478396    call    0041DE4C        ;  读取最后36的字节,校验和存储在第16~20字节里面
0047839B    mov    eax, dword ptr [ebp-10]
0047839E    call    0041DDE4        ;  CloseHandle()
004783A3    mov    eax, dword ptr [ebp-18]
004783A6    mov    eax, dword ptr [eax]
004783A8    xor    eax, 2A67BE65        ;  对存储的校验和进行异或
004783AD    mov    dword ptr [ebp-8], eax
004783B0    push    ebp
004783B1    call    004781E4        ;  比较异或后的两个校验和,并利用两个校验和计算其它数据
至此完成了校验和的计算和比较。
sub_0067FF10函数的反汇编代码如下:
 
代码:
0067FF10    push    ebx
0067FF11    push    esi
0067FF12    push    edi
0067FF13    add    esp, -0C
0067FF16    mov    dword ptr [esp+4], ecx
0067FF1A    mov    dword ptr [esp], edx
0067FF1D    mov    ecx, eax
0067FF1F    mov    ebx, dword ptr [esp+4]
0067FF23    mov    ebx, dword ptr [ebx]
0067FF25    and    ebx, 0FFFF        ;  取上次校验和的低16位给LOW
0067FF2B    mov    esi, dword ptr [esp+4]
0067FF2F    mov    esi, dword ptr [esi]
0067FF31    shr    esi, 10
0067FF34    and    esi, 0FFFF        ;  取上次校验和的高16位给HIGH
0067FF3A    test    ecx, ecx
0067FF3C    jnz    short 0067FF4A
0067FF3E    mov    eax, dword ptr [esp+4]
0067FF42    mov    dword ptr [eax], 1
0067FF48    jmp    short 0067FFB8
0067FF4A    cmp    dword ptr [esp], 0
0067FF4E    jle     short 0067FFAD
0067FF50    cmp    dword ptr [esp], 15B0        ;  分成0x15B0大小的数据来计算
0067FF57    jge     short 0067FF62
0067FF59    mov     eax, dword ptr [esp]
0067FF5C    mov     dword ptr [esp+8], eax
0067FF60    jmp     short 0067FF6A
0067FF62    mov     dword ptr [esp+8], 15B0
0067FF6A    mov     eax, dword ptr [esp+8]
0067FF6E    sub     dword ptr [esp], eax
0067FF71    mov     eax, dword ptr [esp+8]
0067FF75    dec     eax
0067FF76    test    eax, eax
0067FF78    jb      short 0067FF89
0067FF7A    inc     eax
0067FF7B    xor     edx, edx
0067FF7D    movzx   edi, byte ptr [ecx+edx]        ;  每次取一个字节,循环计算
0067FF81    add    ebx, edi
0067FF83    add    esi, ebx
0067FF85    inc    edx
0067FF86    dec    eax
0067FF87    jnz    short 0067FF7D
0067FF89    mov    eax, dword ptr [esp+8]
0067FF8D    add    ecx, eax
0067FF8F    mov    eax, ebx
0067FF91    mov    ebx, 0FFF1
0067FF96    cdq
0067FF97    idiv    ebx    ;  将LOW对0xFFF1取模,结果给CHECKSUM的低16位
0067FF99    mov    ebx, edx
0067FF9B    mov    eax, esi
0067FF9D    mov    esi, 0FFF1
0067FFA2    cdq
0067FFA3    idiv    esi    ;  将HIGH对0xFFF1取模,结果给CHECKSUM的高16位
0067FFA5    mov    esi, edx
0067FFA7    cmp     dword ptr [esp], 0
0067FFAB    jg      short 0067FF50
0067FFAD    shl     esi, 10
0067FFB0    or      ebx, esi
0067FFB2    mov     eax, dword ptr [esp+4]
0067FFB6    mov     dword ptr [eax], ebx
0067FFB8    add     esp, 0C
0067FFBB    pop     edi
0067FFBC    pop     esi
0067FFBD    pop     ebx
0067FFBE    retn
自校验的C语言代码如下:
 
代码:
// Totalcmd_FixCRC.cpp : 自校验
#include "stdafx.h"
// 使用说明
void Usage();
// 计算校验和
void CalculateCRC(unsigned char * pData, unsigned int nSize, unsigned int * nCRC);
// 修复数据及其大小
void FixData(char * pData, unsigned int * nSize);
 
int _tmain(int argc, _TCHAR* argv[])
{
  TCHAR * szFileName = NULL;
  char lpBuffer[0x8001] = {0};
  unsigned int nNumberOfBytesToRead = 0;
  unsigned int nNumberOfBytesRead = 0;
  unsigned int nSize = 0;
  unsigned int nSizeRead = 0;
  unsigned int nCRC = 0xffffffff;
  HANDLE hFile = NULL;
 
  printf("\nTotalcmd_FixCRC by uuk   2012.03.23\n\n");
 
  if ( argc != 2 )
  {
    Usage();
    return 0;
  }
 
  szFileName = argv[1];
 
  hFile = CreateFile(szFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  if ( hFile == INVALID_HANDLE_VALUE )
  {
    printf("CreateFile Error!\n");
    return 1;
  }
 
  nSize = SetFilePointer(hFile, 0, NULL, FILE_END);
  if ( nSize == INVALID_SET_FILE_POINTER)
  {
    printf("SetFilePointer Error!\n");
    CloseHandle(hFile);
    return 2;
  }
  nSize -= 36;
  SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
 
  do
  {
    if ( nSize >= 0x8000 )
      nNumberOfBytesToRead = 0x8000;
    else
      nNumberOfBytesToRead = nSize;
 
    ReadFile(hFile, lpBuffer, nNumberOfBytesToRead, (LPDWORD)&nNumberOfBytesRead, NULL);
 
    if ( nSizeRead == 0 )
      FixData(lpBuffer, &nSize);
 
    CalculateCRC((unsigned char *)lpBuffer, nNumberOfBytesRead, &nCRC);
 
    nSizeRead += nNumberOfBytesRead;
    nSize -= nNumberOfBytesRead;
  } while ( (nNumberOfBytesRead==0x8000) && nSize );
 
  // 读取文件中存储的校验和
  ReadFile(hFile, lpBuffer, 36, (LPDWORD)&nNumberOfBytesRead, NULL);
  unsigned int nCRC2 = *(unsigned int *)(lpBuffer+0x10);
 
  nCRC = nCRC ^ 0xF5A3E289;
  nCRC2 = nCRC2 ^ 0x2A67BE65;
  if ( nCRC != nCRC2 )
  {
    unsigned int nCRC3 = nCRC ^ 0x2A67BE65;
    SetFilePointer(hFile, nSizeRead+0x10, NULL, FILE_BEGIN);
    int nWrite = 0;
    WriteFile(hFile, &nCRC3, 4, (LPDWORD)&nWrite, NULL);
    printf("File is Fixed!\n");
  }
  else
  {  printf("File is not modified!\n");  }
 
  CloseHandle(hFile);
  return 0;
}
 
void Usage()
{  printf("Usage: Totalcmd_FixCRC.exe FileName\n");  }
 
void CalculateCRC(unsigned char * pData, unsigned int nSize, unsigned int * nCRC)
{
  unsigned int nSizeRead;
  int nCRCLow, nCRCHigh;
 
  nCRCLow = *nCRC & 0xffff;
  nCRCHigh = (*nCRC>>16) & 0xffff;
 
  if ( pData )
  {
    if ( nSize > 0 )
    {
      do
      {
        if ( nSize >= 5552 )
          nSizeRead = 5552;
        else
          nSizeRead = nSize;
        nSize -= nSizeRead;
        do
        {
          nCRCLow += *pData;
          nCRCHigh += nCRCLow;
          pData++;
          nSizeRead--;
        } while ( nSizeRead );
        nCRCLow %= 65521;
        nCRCHigh %= 65521;
      } while ( nSize > 0 );
    }
    *nCRC = ( nCRCHigh << 16 ) | nCRCLow;
  }
  else
  {  *nCRC = 1;  }
}
 
void FixData(char * pData, unsigned int * nSize)
{
  int i = 0;
  if ( pData[0x198] != '\0' )
  {
    *nSize = *(unsigned int *)(pData+0x198) - 36;
    for (i=0; i<8; i++)
    {  pData[0x198+i] = '\0';  }
  }
  for (i=0; i<4; i++)
  {  pData[0x158+i] = '\0';  }
}
新程序采用的注册算法没有变,只是将网上公开的许可文件加入黑名单。黑名单中是许可文件的许可号(每个占4字节)。Totalcmd756a版本的黑名单如下所示:
004E0ABC    Totalcmd    mov    eax, 006C8A3C        ;  黑名单1
004E0AD5    Totalcmd    mov    eax, 006C8BE0        ;  黑名单2
004E0AEB    Totalcmd    mov    eax, 006C90F4        ;  黑名单3
004E0B07    Totalcmd    mov    eax, 006C9168        ;  黑名单4
 
比较有意思的是我们可以通过特殊的方法修改黑名单里面的数据而不改变校验和。推导CalculateCRC() 函数中校验和的计算公式得:
  
(Bn代表第n个字节的值)
 
由于(n - 1) + (n+1) = 2n,所以根据校验码的计算公式可以将Bn-1和Bn+1的值各加1,然后将Bn值减2,则总结果不变。因此可以通过这个办法将许可证黑名单中的许可号改掉。如许可号#1283在黑名单中,其HEX值为0x00000503,在文件中是以0x03050000存储的,将其改成0x04030100则许可文件可继续使用,程序也不报错

自学PHP网专注网站建设学习,PHP程序学习,平面设计学习,以及操作系统学习

京ICP备14009008号-1@版权所有www.zixuephp.com

网站声明:本站所有视频,教程都由网友上传,站长收集和分享给大家学习使用,如由牵扯版权问题请联系站长邮箱904561283@qq.com

添加评论