Author: uuk
[Software name]: Total Commander
[Software Version]: 7.56a
[Shelling method]: new version without shelling
[Programming language]: Borland Delphi 2.0 [Overlay]
[Tools]: OD PEID IDA
[Operating platform]: Windows XP
[Software introduction]: A pretty good dual-column File Management Software
[Author's statement]: it is only for research purposes. Please purchase a genuine version for use.
We know that Total Commander has self-verification. by tracking the CreateFile and ReadFile functions, it is determined that the verification process is carried out in the sub_47823C function.
Code:
0047823C push ebp; self-validation Function
0047823D mov ebp, esp
0047823F add esp,-824
00478245 mov dword ptr [ebp-4],-1; Initialization CHECKSUM =-1
00471_c 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
The above is the function initialization.
Code:
004782F7 xor eax, eax
004782F9 mov dword ptr [ebp-24], eax
004782FC mov eax, dword ptr [ebp-10]
004782FF call 00419874; Calculate the file size
00478304 sub eax, 18
00478307 sub eax, 0C; file size minus 36 bytes
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 (): Read data blocks 0 x from the file
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; clear Checksum and digital signature before calculating the Checksum for the first time, and calculate the data size to be verified
0047834A lea ecx, dword ptr [ebp-4]
0047834D movzx edx, word ptr [ebp-14]
00478351 mov eax, dword ptr [ebp-C]
00478354 call 0067FF10; Calculate the checksum using multipart partitioning
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; read the last 36 bytes, And the checksum is stored in 16th ~ 20 bytes
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; the storage checksum is exclusive or
004783AD mov dword ptr [ebp-8], eax
004783B0 push ebp
004783B1 call 004781E4; compare two checksum values after an exclusive or exclusive sum, and use two checksum values to calculate other data.
So far, the checksum calculation and comparison are completed.
The disassembly code of the sub_0067FF10 function is as follows:
Code:
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, 0 FFFF; take the LOW 16 bits of the last checksum to LOW
0067FF2B mov esi, dword ptr [esp + 4]
0067FF2F mov esi, dword ptr [esi]
0067FF31 shr esi, 10
0067FF34 and esi, 0 FFFF; return the 16-bit height of the last checksum to the 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 0067 FFAD
0067FF50 cmp dword ptr [esp], 15B0; divided into 0x15B0 size data for Calculation
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]; get one byte at a time and calculate cyclically
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; modulo 0x1 for LOW, and return the result to the LOW 16-bit CHECKSUM
0067FF99 mov ebx, edx
0067FF9B mov eax, esi
0067FF9D mov esi, 0FFF1
0067FFA2 cdq
0067FFA3 idiv esi; modulo the HIGH pair 0xFFF1 and return the result to the 16-bit HIGH of CHECKSUM.
0067FFA5 mov esi, edx
0067FFA7 cmp dword ptr [esp], 0
0067 FFAB jg short 0067FF50
0067 FFAD shl esi, 10
0067FFB0 or ebx, esi
0067FFB2 mov eax, dword ptr [esp + 4]
0067FFB6 mov dword ptr [eax], ebx
0067FFB8 add esp, 0C
0067 FFBB pop edi
0067 FFBC pop esi
0067 FFBD pop ebx
0067 FFBE retn
The C language code for self-verification is as follows:
Code:
// Total1__fixcrc.cpp: Self-validation
# Include "stdafx. h"
// Instructions for use
Void Usage ();
// Calculate the checksum
Void CalculateCRC (unsigned char * pData, unsigned int nSize, unsigned int * nCRC );
// Fix the data and its size
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 = 0 xffffffff;
HANDLE hFile = NULL;
Printf ("\ ntotal1__fixcrc by uuk 2012.03.23 \ 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 );
// Read the checksum stored in the file
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 & 0 xffff;
NCRCHigh = (* nCRC> 16) & 0 xffff;
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 ';}
}
The registration algorithm adopted by the new program has not changed, but the license files published on the Internet are blacklisted. The blacklist contains the license number of the license file (4 bytes each ). The blacklist of total316756a is as follows:
004E0ABC Totalcmd mov eax, 006C8A3C; blacklist 1
004E0AD5 Totalcmd mov eax, 006C8BE0; blacklist 2
004E0AEB Totalcmd mov eax, 006C90F4; blacklist 3
004E0B07 Totalcmd mov eax, 006C9168; blacklist 4
What is interesting is that we can modify the data in the blacklist in a special way without changing the checksum. Calculate the checksum in the CalculateCRC () function:
(Bn indicates the value of nth byte)
Because (n-1) + (n + 1) = 2n, according to the formula of the verification code, we can add 1 to the values of Bn-1 and Bn + 1, and then subtract 2 from the value of Bn, the summary result remains unchanged. Therefore, you can use this method to remove the license number from the license blacklist. For example, if the license number #1283 is in the blacklist, its HEX value is 0x00000503, which is stored in the file at 0x03050000 and changed to 0x04030100, the license file can continue to be used and the program will not report an error.