逆向序列号生成算法(一)

对逆向工程一直很感兴趣,工作之余自己也研究一下,好久没有练手了,OllyDBG的使用都感觉生疏了,晚上抽空先去补了补OllyDBG的使用方法,然后看到一个叫做CycleCrackMe的序列号保护练手程序(如图1),刚好是OllyDBG入门文章里面提到的一个演示用程序,只是把OllyDBG的消息断点及RUN的跟踪相关的知识讲了一下,对CycleCrackMe里面具体的加密算法留给读者自己去研究,正好给了我一个练手的机会。

        逆向序列号生成算法(一)_第1张图片

              (图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......




你可能感兴趣的:(算法,null,加密,扩展,工作,c,逆向C++,Windows平台相关)