对逆向工程一直很感兴趣,工作之余自己也研究一下,好久没有练手了,OllyDBG的使用都感觉生疏了,晚上抽空先去补了补OllyDBG的使用方法,然后看到一个叫做CycleCrackMe的序列号保护练手程序(如图1),刚好是OllyDBG入门文章里面提到的一个演示用程序,只是把OllyDBG的消息断点及RUN的跟踪相关的知识讲了一下,对CycleCrackMe里面具体的加密算法留给读者自己去研究,正好给了我一个练手的机会。
(图1)
首先我得把其加密的算法部分剥离出来,通过OllyDBG的动态分析,我把其加密的过程分成了三个函数抽离了出来,分别是:
void ExtendNameTo16Bytes( char *pStrName );
void CalculateKeyStep1( const char *pStrName, const char *pStrSerialNum,
DWORD &outTempNum1, DWORD &outTempNum2 );
bool CalculateKeyStep2( const DWORD nTempNum1, const DWORD nTempNum2,
const char *pStrName, const char *pStrSerialNum );
将抽离的函数实现都抽离出来,容易用C++表示的就用C++逆向出来。得出的实现代码如下:
void ExtendNameTo16Bytes( char *pStrName )
{
assert( NULL != pStrName );
if ( NULL == pStrName ) return;
size_t nStrLen = strlen(pStrName);
int nNeedtoExtend = 16 - nStrLen;
if ( nNeedtoExtend <=0 )
return;
int nCount = 0;
while ( nNeedtoExtend-- )
{
*(pStrName+nStrLen+nCount) = *(pStrName+nCount);
++nCount;
}
}
void CalculateKeyStep1( const char *pStrName, const char *pStrSerialNum,
DWORD &outTempNum1, DWORD &outTempNum2 )
{
assert( NULL != pStrName );
assert( NULL != pStrSerialNum );
if ( NULL == pStrName || NULL == pStrSerialNum )
return;
DWORD nNameTemp1 = *(DWORD*)pStrName;
DWORD nNameTemp2 = *(DWORD*)(pStrName+4);
DWORD nSerialTemp1 = *(DWORD*)pStrSerialNum;
DWORD nSerialTemp2 = *(DWORD*)(pStrSerialNum+4);
nNameTemp1 ^= nSerialTemp1;
nNameTemp2 ^= nSerialTemp2;
nNameTemp1 &= 0x7F3F1F0F;
nNameTemp2 &= 0x7030100;
DWORD nEAX=0;
DWORD nEBX=0;
DWORD nECX=0;
DWORD nEDX=0;
__asm
{
xor ecx, ecx
mov eax, nNameTemp1
mov ebx, nNameTemp2
label3: mov esi, eax ; esi=eax
mov edi, ebx ; edi=ebx
shl esi, cl
shl edi, cl
and esi, 0x80808080 ; esi 与 80808080h
and edi, 0x80808080 ; edi 与 80808080h
mov edx, esi ; edx=esi
shr dh, 7 ; dh,8位右移7位
shl dx, 7
shr edx, 8
shr dh, 7
shl dx, 7
shr edx, 8
shr dh, 7
shr dx, 1
mov esi, edx ; esi=edx
mov edx, edi ; edx=edi
shr dh, 7
shl dx, 7
shr edx, 8
shr dh, 7
shl dx, 7
shr edx, 8
shr dh, 7
shr dx, 5
mov edi, edx ; edi=edx
xor edi, esi ; edi = edi xor edx
mov edx, edi ; edx = edi
and edx, 0x0FF ; edx &= 0xff
push ecx
push edx
mov edx, 8 ; edx=8
xchg eax, ecx
cmp eax, 3
jg short label1
mul dl
pop edx
add eax, 8
xchg eax, ecx
rol eax, cl
xor eax, edx
ror eax, cl
jmp short label2
label1: sub eax, 3
mul dl
pop edx
xchg eax, ecx
rol ebx, cl
xor ebx, edx
ror ebx, cl
label2 :pop ecx
inc ecx ;ecx++
cmp ecx, 8 ;循环8次
jnz label3
mov nEAX, eax
mov nEBX, ebx
}
outTempNum1 = nEAX;
outTempNum2 = nEBX;
}
bool CalculateKeyStep2( const DWORD nTempNum1, const DWORD nTempNum2,
const char *pStrName, const char *pStrSerialNum )
{
DWORD nMagicNum = 0xfedcba98;
__asm
{
mov eax, nTempNum1
mov ebx, nTempNum2
mov ecx, 0xff01
push ecx
call Func
cmp ecx, 1
je label6
jmp label7
Func: pop edi
pop ecx
push edi
cmp ecx, 0x80
jle short label1
push ecx
mov esi, ecx
and ecx, 0xFF
mov edi, eax
cmp ecx, 8
jle short label2
mov edi, ebx
shr ecx, 4
label2: rol edi, 8
shr ecx, 1
jnz short label2
shr esi, 8
and edi, esi
and edi, 0xFF
pop ecx
label4: mov esi, 0x80
label5: test esi, edi
je short label3
xor edi, esi
push edi
and ecx, 0xFF00
xchg esi, ecx
xor ch, cl
xor esi, ecx
xchg ecx, esi
push ecx
inc nMagicNum ;nMagicNum++
call Func
pop edi
jmp short label4
label3: shr esi, 1
jnz short label5
label1: retn
label6: mov eax, DWORD ptr [pStrName+8]
mov ebx, DWORD ptr [pStrName+ 0xc]
xor eax, ebx
xor eax, nMagicNum
or eax, 0x40404040
and eax, 0x77777777
xor eax, DWORD ptr [pStrSerialNum+8]
xor eax, DWORD ptr [pStrSerialNum+0xc]
je label8
label7: xor eax, eax ;符合序列号要求,返回false.
pop edi
pop esi
pop ebx
mov esp, ebp
pop ebp
ret
label8: mov al, 1 ;符合要求返回true.
}
}
算法流程为:首先根据输入的用户名,若不足16字节,则扩展至16字节,然后进入Step1,Step2进行计算判断用户名与序列号是否相配。
To be continued......