• 标 题:ePublisher Gold v1.4 (9千字)
  • 作 者:blowfish
  • 时 间:2001-1-15 18:06:58
  • 链 接:http://bbs.pediy.com

URL: search in http://www.google.com

首先找到判断注册码的地方,如下。文件Reg.dat显然是用来存放注册码的。
//---------------------------------------------------------------------------
* Possible StringData Ref from Code Obj ->"Reg.dat"
                                  |
:0045EB77 BA84EC4500              mov edx, 0045EC84
:0045EB7C E83F50FAFF              call 00403BC0
:0045EB81 8B952CFEFFFF            mov edx, dword ptr [ebp+FFFFFE2C]
:0045EB87 8D8530FEFFFF            lea eax, dword ptr [ebp+FFFFFE30]
:0045EB8D E8F06BFAFF              call 00405782
:0045EB92 8D8530FEFFFF            lea eax, dword ptr [ebp+FFFFFE30]
:0045EB98 E8116EFAFF              call 004059AE
:0045EB9D E8DA3BFAFF              call 0040277C
:0045EBA2 8B55FC                  mov edx, dword ptr [ebp-04]
:0045EBA5 8D8530FEFFFF            lea eax, dword ptr [ebp+FFFFFE30]
:0045EBAB E84053FAFF              call 00403EF0
:0045EBB0 E8486CFAFF              call 004057FD
:0045EBB5 E8C23BFAFF              call 0040277C
:0045EBBA 8D8530FEFFFF            lea eax, dword ptr [ebp+FFFFFE30]
:0045EBC0 E85F6CFAFF              call 00405824
:0045EBC5 E8B23BFAFF              call 0040277C
:0045EBCA E8C1FBFFFF              call 0045E790
:0045EBCF 84C0                    test al, al
:0045EBD1 745B                    je 0045EC2E

* Possible StringData Ref from Code Obj ->"Thank you for registering "
                                  |
:0045EBD3 BA8CEC4500              mov edx, 0045EC8C
:0045EBD8 8D8528FDFFFF            lea eax, dword ptr [ebp+FFFFFD28]
:0045EBDE E8513DFAFF              call 00402934
:0045EBE3 8D8528FCFFFF            lea eax, dword ptr [ebp+FFFFFC28]
:0045EBE9 50                      push eax

* Reference To: epgname.ShellExecuteA, Ord:0001h
                                  |
:0045EBEA E829EBFFFF              Call 0045D718
:0045EBEF 8D9528FCFFFF            lea edx, dword ptr [ebp+FFFFFC28]
:0045EBF5 8D8528FDFFFF            lea eax, dword ptr [ebp+FFFFFD28]
:0045EBFB E80C3DFAFF              call 0040290C
:0045EC00 BAA8EC4500              mov edx, 0045ECA8
:0045EC05 8D8528FDFFFF            lea eax, dword ptr [ebp+FFFFFD28]
:0045EC0B E8FC3CFAFF              call 0040290C
:0045EC10 8D9528FDFFFF            lea edx, dword ptr [ebp+FFFFFD28]
:0045EC16 8D852CFEFFFF            lea eax, dword ptr [ebp+FFFFFE2C]
:0045EC1C E83B4FFAFF              call 00403B5C
:0045EC21 8B852CFEFFFF            mov eax, dword ptr [ebp+FFFFFE2C]
:0045EC27 E82CEDFEFF              call 0044D958
:0045EC2C EB0A                    jmp 0045EC38

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0045EBD1(C)
|

* Possible StringData Ref from Code Obj ->"You entered an incorrect registration "
                                        ->"key.  Please try again."
                                  |
:0045EC2E B8B4EC4500              mov eax, 0045ECB4
:0045EC33 E820EDFEFF              call 0044D958
//-----------------------------------------------------------------------------------------------
跟进上面的call 0045E790(IDA告诉我们上述代码是TOrderForm的成员函数OkButtonClick( )的一部分)。这里
它两次调用LStrPos( )函数检查同一个字节串中是否同时包含字符串“v:1.0”和“m:g”,如果是的话则返回1,
表明注册码正确。
//------------------------------------------------------------------------------------------------
sub_45E790    proc near        ; CODE XREF: _TOrderForm_OkButtonClick+166
                    ; sub_45F098+1F ...

var_4        = dword    ptr -4

        push    ebp
        mov    ebp, esp
        push    0
        push    ebx
        xor    eax, eax
        push    ebp
        push    offset loc_45E7EA
        push    dword ptr fs:[eax]
        mov    fs:[eax], esp
        lea    eax, [ebp+var_4]
        call    sub_45E51C
        mov    edx, [ebp+var_4]
        mov    eax, offset _str_v_1_0.Text
        call    @System@@LStrPos$qqrv ;    System __linkproc__ LStrPos(void)
        test    eax, eax
        jle    short loc_45E7CE
        mov    edx, [ebp+var_4]
        mov    eax, offset _str_m_g.Text
        call    @System@@LStrPos$qqrv ;    System __linkproc__ LStrPos(void)
        test    eax, eax
        jg    short loc_45E7D2

loc_45E7CE:
        xor    ebx, ebx            //返回0
        jmp    short loc_45E7D4
loc_45E7D2:
        mov    bl, 1                //返回1

loc_45E7D4:
        xor    eax, eax
        pop    edx
        pop    ecx
        pop    ecx
        mov    fs:[eax], edx
        push    offset loc_45E7F1

loc_45E7E1:
        lea    eax, [ebp+var_4]
        call    @System@@LStrClr$qqrr17System@AnsiString
        retn

loc_45E7EA:
        jmp    @System@@HandleFinally$qqrv
loc_45E7F1:
        mov    eax, ebx
        pop    ebx
        pop    ecx
        pop    ebp
        retn
sub_45E790    endp

        align 4
_str_v_1_0    dd 0FFFFFFFFh        ; _top ; DATA XREF: sub_45E790+1F
        dd 5            ; Len
        db 'v:1.0',0            ; Text
        align 4
_str_m_g    dd 0FFFFFFFFh        ; _top ; DATA XREF: sub_45E790+30
        dd 3            ; Len
        db 'm:g',0              ; Text
//-----------------------------------------------------------------------------------------------
对上面所检查的那个字节串设一个“BPM xxxxxxxx W”断点,看一下该字节串是如何来的。最终会发现它是
根据我们输入的假注册码变换得来的。这说明我们的假注册码经过变换之后应包含字符串“v:1.0”和“m:g”。

下面要做的就是把这个变换搞清楚。经过跟踪,发现它先是把我们输入的注册码作为密文,用blowfish算法
对其进行解密,然后再将解密所得到的明文与一个固定的字节串(DB, 35, 25, 0A, 90, E8, 3C, 9D,...,
该串是blowfish算法产生的中间值)逐字节进行异或,异或的结果中应包含字符串“v:1.0”和“m:g”。
所以注册码应有很多个。

识别blowfish算法的一种方法就是靠它的那些常数表格,只要在内存中看到这些常数,基本上就可以确定
是blowfish算法(当然最终要分析算法本身才能确定)。例如blowfish的P-数组是18个32-bit的子密钥:
static const unsigned long P[16 + 2] =
{
        0x243F6A88L, 0x85A308D3L, 0x13198A2EL, 0x03707344L,
        0xA4093822L, 0x299F31D0L, 0x082EFA98L, 0xEC4E6C89L,
        0x452821E6L, 0x38D01377L, 0xBE5466CFL, 0x34E90C6CL,
        0xC0AC29B7L, 0xC97C50DDL, 0x3F84D5B5L, 0xB5470917L,
        0x9216D5D9L, 0x8979FB1BL
};
其4个S-盒则包含共1024个32-bit的数。实际上该程序的反汇编列表中包含“Blowfish: Invalid key size - %d”、
“Blowfish: Not initialized”、“TDCP_blowfish”等含义明显的串,隐约也可以猜到。

识别出是blowfish算法之后,只需要知道下面几个东西就可以将其解开:
1、密钥。
2、使用的是加密算法还是解密算法。
3、算法的输入或输出值所要满足的条件。
因为数学家已经告诉了我们blowfish算法的加密和解密算法,我们并不需要去发明其逆算法(即使想破脑袋也想不出来的)。

如何找到第1个条件(即密钥)呢?因为该算法在初始化的时候要将P-数组和密钥进行循环异或,所以只要对最初的
P[0](值为0x243F6A88L)设个BPM断点,就可以看见异或的地方,从而找到16字节的密钥:
Key = { 0x00, 0x37, 0x25, 0x5E, 0x76, 0x6B, 0x44, 0x30,0x7B, 0x5C, 0x7E, 0x73, 0x63, 0x26, 0x4D, 0x40};
//----------------------------------------------------------------------------------------------------
:0045C4AC 33FF                    xor edi, edi
:0045C4AE 33DB                    xor ebx, ebx

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0045C505(C)
|
:0045C4B0 8D4703                  lea eax, dword ptr [edi+03]
:0045C4B3 99                      cdq
:0045C4B4 F77DFC                  idiv [ebp-04]
:0045C4B7 8B45F8                  mov eax, dword ptr [ebp-08]
:0045C4BA 33C9                    xor ecx, ecx
:0045C4BC 8A0C10                  mov cl, byte ptr [eax+edx]          //密钥
:0045C4BF 8D4702                  lea eax, dword ptr [edi+02]
:0045C4C2 99                      cdq
:0045C4C3 F77DFC                  idiv [ebp-04]
:0045C4C6 8B45F8                  mov eax, dword ptr [ebp-08]
:0045C4C9 0FB60410                movzx eax, byte ptr [eax+edx]      //密钥
:0045C4CD C1E008                  shl eax, 08
:0045C4D0 03C8                    add ecx, eax
:0045C4D2 8D4701                  lea eax, dword ptr [edi+01]
:0045C4D5 99                      cdq
:0045C4D6 F77DFC                  idiv [ebp-04]
:0045C4D9 8B45F8                  mov eax, dword ptr [ebp-08]
:0045C4DC 0FB60410                movzx eax, byte ptr [eax+edx]        //密钥
:0045C4E0 C1E010                  shl eax, 10
:0045C4E3 03C8                    add ecx, eax
:0045C4E5 8B45F8                  mov eax, dword ptr [ebp-08]
:0045C4E8 0FB60438                movzx eax, byte ptr [eax+edi]        //密钥
:0045C4EC C1E018                  shl eax, 18
:0045C4EF 03C8                    add ecx, eax
:0045C4F1 318C9E50100000          xor dword ptr [esi+4*ebx+00001050], ecx //与P-数组异或
:0045C4F8 8D4704                  lea eax, dword ptr [edi+04]
:0045C4FB 99                      cdq
:0045C4FC F77DFC                  idiv [ebp-04]
:0045C4FF 8BFA                    mov edi, edx
:0045C501 43                      inc ebx
:0045C502 83FB12                  cmp ebx, 00000012                      //P-数组有18个数
:0045C505 75A9                    jne 0045C4B0
//--------------------------------------------------------------------------------------------
至于第2个条件,根本不用分析其算法,只需要自己写个程序把加密和解密算法都调用一下,并将输出和
该软件的算法输出对比一下就知道采用的是解密算法。

第3个条件比较简单,即blowfish算法的输入为假注册码,输出与一固定字节异或之后应包含“v:1.0”和“m:g”。


异或在如下的地方进行,在这里可以找到所需的固定字节串:
//--------------------------------------------------------------------------------------------
:0045B6CF 33F6                    xor esi, esi

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0045B6DF(C)
|
:0045B6D1 8A1C30                  mov bl, byte ptr [eax+esi]
:0045B6D4 321C32                  xor bl, byte ptr [edx+esi]
:0045B6D7 8B7DFC                  mov edi, dword ptr [ebp-04]
:0045B6DA 881C37                  mov byte ptr [edi+esi], bl

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0045B724(C)
|
:0045B6DD 46                      inc esi
:0045B6DE 49                      dec ecx
:0045B6DF 75F0                    jne 0045B6D1
//--------------------------------------------------------------------------------------------
有兴趣的自己写keymaker吧。