查看: 13106|回复: 7

一个简单的CrackMe分析

[复制链接]
发表于 2013-10-22 22:51:03 | 显示全部楼层 |阅读模式
    这是一个简单的CrackMe分析,大牛勿笑,欢迎指教。
    这个CrackMe没有关键字符串提醒,没有敏感函数的调用,常规的查找字符串和对敏感函数下断点都行不通,只能另想办法。用IDA打开软件,在函数列表中看到一个函数UpDateData,于是眼前一亮。因为在MFC中,这个函数是用来更新控件数据的,所以就猜想这个CrackMe是通过这个函数来获取用户名和序列号,找到这个函数的调用位置,然后用OD加载CrackMe,找到UpDateData函数的调用位置,并下断点。按下F9运行,输入用户名和序列号,点击“确定”后,程序停在下面这个位置:
[AppleScript] 纯文本查看 复制代码
00401A09   .  E8 1E070000   call    <jmp.&MFC71.#6236_CWnd::UpdateData>
00401A0E   .  68 A8394000   push    004039A8                               ; /FileName = "Reg.dll"
00401A13   .  FF15 34304000 call    dword ptr [<&KERNEL32.LoadLibraryA>]   ; \LoadLibraryA

      这是通过UpDateData获取用户名和序列号,然后调用LoadLibraryA函数加载Reg.dll,然后取出用户名,代码如下:
[AppleScript] 纯文本查看 复制代码
;获取用户名和序列号
00401A09   .  E8 1E070000   call    <jmp.&MFC71.#6236_CWnd::UpdateData>
;调用LoadLibraryA来加载Reg.dll
00401A0E   .  68 A8394000   push  004039A8  ; /FileName = "Reg.dll"
00401A13   .  FF15 34304000 call    dword ptr [<&KERNEL32.LoadLibraryA>]               
; \LoadLibraryA
00401A19   .  68 70394000   push    00403970
00401A1E   .  8D4C24 18     lea     ecx, dword ptr [esp+18]
;保存Reg.dll的句柄
00401A22   .  894424 24     mov     dword ptr [esp+24], eax
;取出用户名
00401A6A   .  FF15 9C314000 call    dword ptr [<&MFC71.#876_ATL::CSimpleStringT<char,1>;  MFC71.7C158BCD

     到这里,eax中已经存放了用户输入的用户名的首地址,接下来先将用户名存入前面定义的数组中,代码如下:
[AppleScript] 纯文本查看 复制代码
00401A70   .  8D6C24 24     lea     ebp, dword ptr [esp+24]
00401A74   >  8A08          mov     cl, byte ptr [eax]
00401A76   .  40            inc     eax
00401A77   .  884D 00       mov     byte ptr [ebp], cl
00401A7A   .  45            inc     ebp
00401A7B   .  84C9          test    cl, cl
00401A7D   .^ 75 F5         jnz     short 00401A74

     然后取出用户名的前三个字节,通过一些算法来对其进行计算,代码如下:
[AppleScript] 纯文本查看 复制代码
00401A7F   .  33ED          xor     ebp, ebp
;取出用户名的一个字节
00401A81   >  8A442C 24     mov     al, byte ptr [esp+ebp+24]
;判断其是否为字符0,如果是则结束注册
00401A85   .  3C 30         cmp     al, 30
00401A87   .  0F84 D1000000 je      00401B5E
;将al扩充为eax
00401A8D   .  0FBEC0        movsx   eax, al
00401A90   .  57            push    edi
;将eax的最高位扩充到edx
00401A91   .  99            cdq
00401A92   .  56            push    esi
00401A93   .  52            push    edx
00401A94   .  50            push    eax
;这个函数对输入的参数进行计算
00401A95   .  E8 96070000   call    00402230
00401A9A   .  45            inc     ebp
;通过ebp来限制获取用户名的字节数
00401A9B   .  83FD 03       cmp     ebp, 3
;将用户名计算结果的低32位存入esi,高32位存入edi
00401A9E   .  8BF0          mov     esi, eax
00401AA0   .  8BFA          mov     edi, edx
00401AA2   .^ 7C DD         jl      short 00401A81

    现在来看看00401A95   .  E8 96070000   call    00402230这个函数里都做了些什么,代码如下:
[AppleScript] 纯文本查看 复制代码
;获取第二个参数
00402230  /$  8B4424 08     mov     eax, dword ptr [esp+8]
;获取第四个参数
00402234  |.  8B4C24 10     mov     ecx, dword ptr [esp+10]
;用第二个参数和第四个参数进行或运算
00402238  |.  0BC8          or      ecx, eax
;获取第三个参数
0040223A  |.  8B4C24 0C     mov     ecx, dword ptr [esp+C]
;如果或的结果部位0则跳转
0040223E  |.  75 09         jnz     short 00402249
;获取第一个参数
00402240  |.  8B4424 04     mov     eax, dword ptr [esp+4]
;第一个参数与第三个参数相乘,结果低32位存eax,高32位存edx
00402244  |.  F7E1          mul     ecx
00402246  |.  C2 1000       retn    10
;前面相或后结果不为0的计算
00402249  |>  53            push    ebx
;用相或后的结果乘以第二个参数
0040224A  |.  F7E1          mul     ecx
0040224C  |.  8BD8          mov     ebx, eax
;由于前面有个push ebx,所以这里esp+8是取出第一个参数
0040224E  |.  8B4424 08     mov     eax, dword ptr [esp+8]
;第一个参数乘以第四个参数,结果存与eax
00402252  |.  F76424 14     mul     dword ptr [esp+14]
;将所得的积与前面所得的积相加
00402256  |.  03D8          add     ebx, eax
;取出第一个参数
00402258  |.  8B4424 08     mov     eax, dword ptr [esp+8]
;ecx存放的是第三个参数,这里将第一个参数与第三个参数相乘
0040225C  |.  F7E1          mul     ecx
;将所得结果与前面的和相加
0040225E  |.  03D3          add     edx, ebx
00402260  |.  5B            pop     ebx
00402261  \.  C2 1000       retn    10

    到这里,用户名前三个字节的计算已经结束,接下来是用户名长度的判断,代码如下:
[AppleScript] 纯文本查看 复制代码
;取出用户名数组的第七个元素
00401AA4   .  8A4424 2A     mov     al, byte ptr [esp+2A]
;判断其是否为0,如果不为0则结束注册
00401AA8   .  84C0          test    al, al
00401AAA   .  0F85 AE000000 jnz     00401B5E
;取出用户名数组的第6个原素,如果为0则结束注册
00401AB0   .  8A4424 29     mov     al, byte ptr [esp+29]
00401AB4   .  84C0          test    al, al
00401AB6   .  0F84 A2000000 je      00401B5E

    通过上面的两个判断可知,用户名的长度只能为6个字节。接下来是通过00401AC1   .  E8 BA010000   call    00401C80这个函数来获取CPU信息的函数,这个函数代码如下:
[AppleScript] 纯文本查看 复制代码
;实例化4个CString对象
00401CE1  |.  FF15 A8314000 call    dword ptr [<&MFC71.#310_ATL::CStringT<char,StrTraitMFC>;  MFC71.7C173199
00401CE7  |.  8D4C24 0C     lea     ecx, dword ptr [esp+C]
00401CEB  |.  FF15 A8314000 call    dword ptr [<&MFC71.#310_ATL::CStringT<char,StrTraitMFC>;  MFC71.7C173199
00401CF1  |.  8D4C24 10     lea     ecx, dword ptr [esp+10]
00401CF5  |.  FF15 A8314000 call    dword ptr [<&MFC71.#310_ATL::CStringT<char,StrTraitMFC>;  MFC71.7C173199
00401CFB  |.  8D4C24 14     lea     ecx, dword ptr [esp+14]
00401CFF  |.  FF15 A8314000 call    dword ptr [<&MFC71.#310_ATL::CStringT<char,StrTraitMFC>;  MFC71.7C173199
00401D05  |.  C64424 40 04  mov     byte ptr [esp+40], 4
;获取CPU制造商信息信息
00401D0A  |.  33C0          xor     eax, eax
00401D0C  |.  0FA2          cpuid
00401D0E  |.  895C24 24     mov     dword ptr [esp+24], ebx
00401D12  |.  895424 28     mov     dword ptr [esp+28], edx
00401D16  |.  894C24 2C     mov     dword ptr [esp+2C], ecx
00401D1A  |.  8D4C24 24     lea     ecx, dword ptr [esp+24]
00401D1E  |.  51            push    ecx
00401D1F  |.  8D5424 1C     lea     edx, dword ptr [esp+1C]
;将CPU制造商信息以字符串的形式存储与一个CString对象中
00401D23  |.  68 BC394000   push    004039BC                                               ;  ASCII "%s-"
00401D28  |.  52            push    edx
00401D29  |.  FF15 DC314000 call    dword ptr [<&MFC71.#2322_ATL::CStringT<char,StrTraitMF>;  MFC71.7C146A9D
00401D2F  |.  83C4 0C       add     esp, 0C
;获取CPU序列号的高两个WORD
00401D32  |.  B8 01000000   mov     eax, 1
00401D37  |.  33D2          xor     edx, edx
00401D39  |.  0FA2          cpuid
00401D3B  |.  895424 1C     mov     dword ptr [esp+1C], edx
00401D3F  |.  894424 20     mov     dword ptr [esp+20], eax
00401D43  |.  8B4424 20     mov     eax, dword ptr [esp+20]
00401D47  |.  8B4C24 1C     mov     ecx, dword ptr [esp+1C]
00401D4B  |.  50            push    eax
00401D4C  |.  51            push    ecx
00401D4D  |.  8D5424 18     lea     edx, dword ptr [esp+18]
;将CPU序列号转为十六进制,再将其格式化为字符串存入一个CString中
00401D51  |.  68 B0394000   push    004039B0                                               ;  ASCII "%08X%08X"
00401D56  |.  52            push    edx
00401D57  |.  FF15 DC314000 call    dword ptr [<&MFC71.#2322_ATL::CStringT<char,StrTraitMF>;  MFC71.7C146A9D
00401D5D  |.  83C4 10       add     esp, 10
;获取CPU序列号的4个WORD
00401D60  |.  B8 03000000   mov     eax, 3
00401D65  |.  33C9          xor     ecx, ecx
00401D67  |.  33D2          xor     edx, edx
00401D69  |.  0FA2          cpuid
00401D6B  |.  895424 1C     mov     dword ptr [esp+1C], edx
00401D6F  |.  894C24 20     mov     dword ptr [esp+20], ecx
00401D73  |.  8B4424 20     mov     eax, dword ptr [esp+20]
00401D77  |.  8B4C24 1C     mov     ecx, dword ptr [esp+1C]
00401D7B  |.  50            push    eax
00401D7C  |.  51            push    ecx
00401D7D  |.  8D5424 1C     lea     edx, dword ptr [esp+1C]
;将这4个WORD转为16进制,并格式化为字符串存入一个CString中
00401D81  |.  68 B0394000   push    004039B0                                               ;  ASCII "%08X%08X"
00401D86  |.  52            push    edx
00401D87  |.  FF15 DC314000 call    dword ptr [<&MFC71.#2322_ATL::CStringT<char,StrTraitMF>;  MFC71.7C146A9D
00401D8D  |.  8D4424 24     lea     eax, dword ptr [esp+24]
00401D91  |.  50            push    eax
00401D92  |.  8D4C24 24     lea     ecx, dword ptr [esp+24]
00401D96  |.  51            push    ecx
00401D97  |.  8D5424 34     lea     edx, dword ptr [esp+34]
00401D9B  |.  52            push    edx
;这里将CPU序列号的两个字符串连成一个字符串
00401D9C  |.  E8 3FFEFFFF   call    00401BE0

    接下来通过00401ADC   .  E8 CFFDFFFF   call    004018B0来对获取的CPU序列号进行计算,这个函数的实现过程如下:
[AppleScript] 纯文本查看 复制代码
00401903  |> /33F6          /xor     esi, esi
;要获取的元素下标
00401905  |> |8D0437        |/lea     eax, dword ptr [edi+esi]
00401908  |. |50            ||push    eax
;源字符串的存放位置
00401909  |. |8D4C24 58     ||lea     ecx, dword ptr [esp+58]
;这里调用的是GetAt函数,来获取相应位置的字符
0040190D  |. |FF15 D4314000 ||call    dword ptr [<&MFC71.#2451_ATL::CSimpleStringT<char,1>>;  MFC71.7C1894E7
00401913  |. |8D4C24 0C     ||lea     ecx, dword ptr [esp+C]
00401917  |. |50            ||push    eax
00401918  |. |FF15 D0314000 ||call    dword ptr [<&MFC71.#782_ATL::CStringT<char,StrTraitM>;  MFC71.7C18B1DF
0040191E  |. |8D4C24 0C     ||lea     ecx, dword ptr [esp+C]
00401922  |. |51            ||push    ecx
00401923  |. |8BCD          ||mov     ecx, ebp
;将字符存入CString对象中
00401925  |. |FF15 CC314000 ||call    dword ptr [<&MFC71.#907_ATL::CStringT<char,StrTraitM>;  MFC71.7C14E599
0040192B  |. |46            ||inc     esi
;每个CString对象存放8个字符
0040192C  |. |83FE 08       ||cmp     esi, 8
0040192F  |.^|7C D4         |\jl      short 00401905
00401931  |. |83C7 08       |add     edi, 8
00401934  |. |83C5 04       |add     ebp, 4
00401937  |. |83FF 20       |cmp     edi, 20
0040193A  |.^\7C C7         \jl      short 00401903
	接下来把CString中的字符串转为数字,代码如下:
00401942  |> /6A 10         /push    10
00401944  |. |51            |push    ecx
00401945  |. |8BCC          |mov     ecx, esp
00401947  |. |896424 18     |mov     dword ptr [esp+18], esp
0040194B  |. |57            |push    edi
0040194C  |. |FF15 C8314000 |call    dword ptr [<&MFC71.#297_ATL::CStringT<char,StrTraitMFC_D>;  MFC71.7C14E575
;字符串转换成数字函数
00401952  |. |E8 79FDFFFF   |call    004016D0
00401957  |. |83C4 08       |add     esp, 8
;保存转换结果
0040195A  |. |8944F4 24     |mov     dword ptr [esp+esi*8+24], eax
0040195E  |. |8954F4 28     |mov     dword ptr [esp+esi*8+28], edx
00401962  |. |46            |inc     esi
00401963  |. |83C7 04       |add     edi, 4
00401966  |. |83FE 04       |cmp     esi, 4
00401969  |.^\7C D7         \jl      short 00401942

    字符串转换为数字的函数代码如下:
[AppleScript] 纯文本查看 复制代码
;获取要转换的字符串首
004016EE  |.  8D4C24 3C     lea     ecx, dword ptr [esp+3C]
004016F2  |.  896C24 34     mov     dword ptr [esp+34], ebp
;获取要转换的字符串长度
004016F6  |.  FF15 C4314000 call    dword ptr [<&MFC71.#2902_ATL::CSimpleStringT<char,1>::GetLength>]   ;  MFC71.7C146AB0
;如果字符串长度小于等于0,则结束转换
004016FC  |.  3BC5          cmp     eax, ebp
;保存要转换的次数
004016FE  |.  894424 18     mov     dword ptr [esp+18], eax
00401702  |.  896C24 1C     mov     dword ptr [esp+1C], ebp
00401706  |.  896C24 20     mov     dword ptr [esp+20], ebp
0040170A  |.  896C24 10     mov     dword ptr [esp+10], ebp
0040170E  |.  0F8E 0E010000 jle     00401822
00401714  |.  8B7C24 18     mov     edi, dword ptr [esp+18]
00401718  |.  8D58 FF       lea     ebx, dword ptr [eax-1]
0040171B  |.  895C24 14     mov     dword ptr [esp+14], ebx
0040171F  |.  90            nop
00401720  |> /8B4424 40     /mov     eax, dword ptr [esp+40]
00401724  |. |50            |push    eax
00401725  |. |55            |push    ebp
;获取要转换的字符串地址
00401726  |. |8D4C24 44     |lea     ecx, dword ptr [esp+44]
;获取要转换的字符
0040172A  |. |FF15 C0314000 |call    dword ptr [<&MFC71.#865_ATL::CSimpleStr>;  MFC71.7C1894E7
00401730  |. |50            |push    eax
;判断该字符是否为0~f之一,如果是则返回1,否则返回0
00401731  |. |E8 1AFFFFFF   |call    00401650
00401736  |. |83C4 08       |add     esp, 8
;如果返回0则说明该字符并非0~f之一,则直接结束
00401739  |. |85C0          |test    eax, eax
0040173B  |. |8D4C24 3C     |lea     ecx, dword ptr [esp+3C]
0040173F  |. |0F84 02010000 |je      00401847
00401745  |. |55            |push    ebp
;获取要检测的字符
00401746  |. |FF15 C0314000 |call    dword ptr [<&MFC71.#865_ATL::CSimpleStr>;  MFC71.7C1894E7
0040174C  |. |0FBEC8        |movsx   ecx, al
0040174F  |. |51            |push    ecx                                     ; /c
;用isdugit函数来检测该字符是否为数字, 如果为数字则返回1,否则返回0
00401750  |. |FF15 F4324000 |call    dword ptr [<&MSVCR71.isdigit>]          ; \isdigit
00401756  |. |83C4 04       |add     esp, 4
;如果是字符,则进入字符处理
00401759  |. |85C0          |test    eax, eax
0040175B  |. |8D4C24 3C     |lea     ecx, dword ptr [esp+3C]
0040175F  |. |55            |push    ebp
00401760  |. |74 0E         |je      short 00401770
;获取字符
00401762  |. |FF15 C0314000 |call    dword ptr [<&MFC71.#865_ATL::CSimpleStr>;  MFC71.7C1894E7
00401768  |. |0FBEF8        |movsx   edi, al
;如果是数字,则直接用它的ascii码减去字符0的ascii码,然后跳到下面的处理部分
0040176B  |. |83EF 30       |sub     edi, 30
0040176E  |. |EB 47         |jmp     short 004017B7
;获取字符
00401770  |> |FF15 C0314000 |call    dword ptr [<&MFC71.#865_ATL::CSimpleStr>;  MFC71.7C1894E7
00401776  |. |0FBEC0        |movsx   eax, al
;用该字符啊ascii码减去大写字母A的ascii码
00401779  |. |83C0 BF       |add     eax, -41                                ;  Switch (cases 41..66)
;大于f则不进行字符转数字的处理
0040177C  |. |83F8 25       |cmp     eax, 25
0040177F  |. |77 36         |ja      short 004017B7
00401781  |. |0FB690 841840>|movzx   edx, byte ptr [eax+401884]
;这里用个switch语句来将字母转为相应的数字
00401788  |. |FF2495 681840>|jmp     dword ptr [edx*4+401868]
;字母A或a则转为0x0A
0040178F  |> |BF 0A000000   |mov     edi, 0A                                 ;  Cases 41 ('A'),61 ('a') of switch 00401779
00401794  |. |EB 21         |jmp     short 004017B7
;字母B或b则转为0x0B
00401796  |> |BF 0B000000   |mov     edi, 0B                                 ;  Cases 42 ('B'),62 ('b') of switch 00401779
0040179B  |. |EB 1A         |jmp     short 004017B7
;字母C或c则转为0x0C
0040179D  |> |BF 0C000000   |mov     edi, 0C                                 ;  Cases 43 ('C'),63 ('c') of switch 00401779
004017A2  |. |EB 13         |jmp     short 004017B7
;字母D或d则转为0x0D
004017A4  |> |BF 0D000000   |mov     edi, 0D                                 ;  Cases 44 ('D'),64 ('d') of switch 00401779
004017A9  |. |EB 0C         |jmp     short 004017B7
;字母E或e则转为0x0E
004017AB  |> |BF 0E000000   |mov     edi, 0E                                 ;  Cases 45 ('E'),65 ('e') of switch 00401779
004017B0  |. |EB 05         |jmp     short 004017B7
;字母F或f则转为0x0F
004017B2  |> |BF 0F000000   |mov     edi, 0F                                 ;  Cases 46 ('F'),66 ('f') of switch 00401779
004017B7  |> |33F6          |xor     esi, esi                                ;  Default case of switch 00401779
;ebx保存的是剩余为转换的字符个数,如果小于等于0,则说明字符已转换完毕
004017B9  |. |85DB          |test    ebx, ebx
004017BB  |. |B9 01000000   |mov     ecx, 1
004017C0  |. |7E 2A         |jle     short 004017EC
;eax存放的值为0x10
004017C2  |. |8B4424 40     |mov     eax, dword ptr [esp+40]
004017C6  |. |99            |cdq
004017C7  |. |8BE8          |mov     ebp, eax
004017C9  |. |895424 28     |mov     dword ptr [esp+28], edx
004017CD  |. |8D49 00       |lea     ecx, dword ptr [ecx]
;下面这个循环用来确定字符在转换化后数字所处的位置
004017D0  |> |8B4424 28     |/mov     eax, dword ptr [esp+28]
004017D4  |. |56            ||push    esi
004017D5  |. |51            ||push    ecx
004017D6  |. |50            ||push    eax
004017D7  |. |55            ||push    ebp
004017D8  |. |E8 530A0000   ||call    00402230
004017DD  |. |4B            ||dec     ebx
004017DE  |. |8BC8          ||mov     ecx, eax
004017E0  |. |8BF2          ||mov     esi, edx
004017E2  |.^|75 EC         |\jnz     short 004017D0
004017E4  |. |8B6C24 10     |mov     ebp, dword ptr [esp+10]
004017E8  |. |8B5C24 14     |mov     ebx, dword ptr [esp+14]
004017EC  |> |56            |push    esi
004017ED  |. |8BC7          |mov     eax, edi
004017EF  |. |99            |cdq
004017F0  |. |51            |push    ecx
004017F1  |. |52            |push    edx
004017F2  |. |50            |push    eax
;这里实现的是用字符串转化后的数字乘以它所处的位置,从而得到一个整数的某一位
004017F3  |. |E8 380A0000   |call    00402230
004017F8  |. |8B4C24 1C     |mov     ecx, dword ptr [esp+1C]
004017FC  |. |8B7424 20     |mov     esi, dword ptr [esp+20]
00401800  |. |03C8          |add     ecx, eax
00401802  |. |8B4424 18     |mov     eax, dword ptr [esp+18]
00401806  |. |13F2          |adc     esi, edx
00401808  |. |45            |inc     ebp
00401809  |. |4B            |dec     ebx
;判断转换是否结束
0040180A  |. |3BE8          |cmp     ebp, eax
;保存转换结果
0040180C  |. |894C24 1C     |mov     dword ptr [esp+1C], ecx
00401810  |. |897424 20     |mov     dword ptr [esp+20], esi
00401814  |. |896C24 10     |mov     dword ptr [esp+10], ebp
00401818  |. |895C24 14     |mov     dword ptr [esp+14], ebx
0040181C  |.^\0F8C FEFEFFFF \jl      00401720

    所有的字符串都转为数字之后,接下来就对它进行计算。转换的结果保存在一个长度为8的int型数组中,下面是计算部分的代码:
[AppleScript] 纯文本查看 复制代码
0040196B  |.  8B4C24 40     mov     ecx, dword ptr [esp+40]
0040196F  |.  8B7C24 28     mov     edi, dword ptr [esp+28]
00401973  |.  8B5424 3C     mov     edx, dword ptr [esp+3C]
00401977  |.  8B7424 24     mov     esi, dword ptr [esp+24]
0040197B  |.  8B6C24 38     mov     ebp, dword ptr [esp+38]
0040197F  |.  8B4424 34     mov     eax, dword ptr [esp+34]
00401983  |.  33F9          xor     edi, ecx
00401985  |.  8B4C24 30     mov     ecx, dword ptr [esp+30]
00401989  |.  33F2          xor     esi, edx
0040198B  |.  8B5424 2C     mov     edx, dword ptr [esp+2C]
0040198F  |.  33FD          xor     edi, ebp
00401991  |.  33F0          xor     esi, eax
00401993  |.  33F9          xor     edi, ecx
00401995  |.  8D4C24 0C     lea     ecx, dword ptr [esp+C]
00401999  |.  33F2          xor     esi, edx

    计算出结果后,将所得结果与用户名前三字节的计算结果相或,代码如下:
[AppleScript] 纯文本查看 复制代码
;edx保存的是所得结果的高32位,eax保存的是所得结果的低32位,edi保存的是用户名前三字节结算结果的高32位,esi保存的是计算所得结果的低32位
00401AE1   .  0BFA          or      edi, edx
00401AE3   .  0BF0          or      esi, eax

    接下来将所得结果转化为字符串,代码如下:
[AppleScript] 纯文本查看 复制代码
00401AE5   .  57            push    edi
00401AE6   .  56            push    esi
00401AE7   .  8D4424 1C     lea     eax, dword ptr [esp+1C]
00401AEB   .  68 A4394000   push    004039A4                                                       ;  ASCII "%x"
00401AF0   .  50            push    eax
00401AF1   .  FF15 DC314000 call    dword ptr [<&MFC71.#2322_ATL::CStringT<char,StrTraitMFC_DLL<ch>;  MFC71.7C146A9D
00401AF7   .  8B4C24 34     mov     ecx, dword ptr [esp+34]
00401AFB   .  83C4 14       add     esp, 14

    然后调用reg.dll中的加密函数,代码如下:
[AppleScript] 纯文本查看 复制代码
00401AFE   .  68 98394000   push    00403998                                                       ; /ProcNameOrOrdinal = "GetMD5Str"
00401B03   .  51            push    ecx                                                            ; |hModule
00401B04   .  FF15 38304000 call    dword ptr [<&KERNEL32.GetProcAddress>]                         ; \GetProcAddress
00401B0A   .  8D4C24 10     lea     ecx, dword ptr [esp+10]
00401B0E   .  8BF0          mov     esi, eax
00401B10   .  FF15 9C314000 call    dword ptr [<&MFC71.#876_ATL::CSimpleStringT<char,1>::operator >;  MFC71.7C158BCD
00401B16   .  50            push    eax
;调用reg.dll中的GetMD5Str函数
00401B17   .  FFD6          call    esi

    然后将所得结果存入一个CString对象中,并对这个字符串进行计算,计算方式与对CPU序列号的计算一样,代码如下:
[AppleScript] 纯文本查看 复制代码
00401B19   .  50            push    eax
00401B1A   .  8D4C24 18     lea     ecx, dword ptr [esp+18]
00401B1E   .  FF15 D8314000 call    dword ptr [<&MFC71.#784_ATL::CStringT<char,StrTraitM>;  MFC71.7C14FF74
00401B24   .  51            push    ecx
00401B25   .  8D5424 18     lea     edx, dword ptr [esp+18]
00401B29   .  8BCC          mov     ecx, esp
00401B2B   .  896424 20     mov     dword ptr [esp+20], esp
00401B2F   .  52            push    edx
00401B30   .  FF15 C8314000 call    dword ptr [<&MFC71.#297_ATL::CStringT<char,StrTraitM>;  MFC71.7C14E575
;对所生成的MD5字符串进行计算
00401B36   .  E8 75FDFFFF   call    004018B0

    通过这次计算后,得到的结果就是这个CM所需要的序列号了,接下来就是序列号的比较,代码如下:
[AppleScript] 纯文本查看 复制代码
;这里获取用户输入的序列号
00401B3B   .  8B4B 78       mov     ecx, dword ptr [ebx+78]
00401B3E   .  83C4 04       add     esp, 4
;eax中保存的是通过计算后所得的结果,如果用户输入的序列号与所得结果相等,则说明序列号是正确的,否则不正确
00401B41   .  3BC1          cmp     eax, ecx
00401B43   .  75 0F         jnz     short 00401B54
00401B45   .  3B53 7C       cmp     edx, dword ptr [ebx+7C]
00401B48   .  75 0A         jnz     short 00401B54

    分析终于完成了,长呼一口气,接下来就是注册机的代码了
[AppleScript] 纯文本查看 复制代码
/**
 * @brief 序列号生成函数
 * @per 修改历史
 * @code
 *	作者		修改时间		函数描述			版本号
 * -------	  ------------	  --------------	 ----------
 * IceAmber	   2013-04-14		计算序列号		v1.0
 * @endcode
 */
void CCRACKME4KeygenDlg::OnBnClickedGenerate()
{
	// TODO: 在此添加控件通知处理程序代码
	int iNameCaculate = 0;
	int iCpuCaculate = 0;
	int iResult = 0;
	EightInt iSerial = {0};
	HMODULE lHandle = 0;
	CString strDigest;
	typedef char* (__stdcall *LPFNGETMD5)(CString);
	LPFNGETMD5 lpfnRegister = NULL;
	char *cDigestAddr;

	//加载DLL
	lHandle = LoadLibrary("Reg.dll");
	if(NULL != lHandle)
	{
		iNameCaculate = DealName();
		//获取CPUID
		iCpuCaculate = GetCPUIDFunction();

		iResult = iNameCaculate | iCpuCaculate;
		strDigest.Format("%x",iResult);
		lpfnRegister = (LPFNGETMD5)GetProcAddress(lHandle, "GetMD5Str");

		if(NULL != lpfnRegister)
		{
			cDigestAddr = (*lpfnRegister)(strDigest);
			strDigest.Format("%s",cDigestAddr);
			iSerial = DealString(strDigest);
			strDigest.Format("%d", iSerial.lEax);
		}
	}
	SetDlgItemText(IDC_SERIAL, strDigest);
	FreeLibrary(lHandle);
}

/**
 * @brief 用户名计算函数
 * @per 修改历史
 * @code
 *	作者		修改时间		函数描述			版本号
 * -------	  ------------	  --------------	 ----------
 * IceAmber	   2013-04-14     用户名计算函数		v1.0
 * @endcode
 */
EightInt CCRACKME4KeygenDlg::NameCaculete(int iParam1, int iParam2, int iParam3, int iParam4)
{
	EightInt lResult = {0};
	int iEaxReg, iEcxReg;
	iEaxReg = iParam2;
	iEcxReg = iParam4;
	if(0 == (iEaxReg | iEcxReg))
	{
		iEcxReg = iParam3;
		iEaxReg = iParam1;
		__asm
		{
			mov		eax, iEaxReg
			mov		ecx, iEcxReg
			mul		ecx
			mov		lResult.lEax, eax
			mov		lResult.lEdx, edx
		}
	}
	
	return lResult;
}

/**
 * @brief 用户名处理函数
 * @per 修改历史
 * @code
 *	作者		修改时间		函数描述				版本号
 * -------	  ------------	  -------------------	  ----------
 * IceAmber	   2013-04-14    计算用户名前三个字母	    v1.0
 * @endcode
 */
int CCRACKME4KeygenDlg::DealName(void)
{
	int i = 0;
	EightInt lRet = {0};
	int iEdi = 0;
	int iEsi = 2;	//用来处理用户名前三个字母的密钥
	int iEdx = 0;
	int iEax = 0;
	int iLenth = 0;	
	CString strName; //用户名
	int iResult = 0;	//用户名计算结果

	//获取用户名
	GetDlgItemText(IDC_NAME, strName);
	
	iLenth = strName.GetLength(); //获取用户名长度
	
	for(i = 0; i < iLenth / 2; i ++)
	{
		if('0' == strName[i])	//用户名不能有字符'0'
		{
			return 0;
		}
		iEax = (int)strName[i];
		//获取前三个字母的计算结果
		lRet = NameCaculete(iEax, iEdx, iEsi, iEdi);
		iEax = (int)lRet.lEax;
		iEdx = (int)lRet.lEdx;
		
		iEsi = iEax;
		iEdi = iEdx;
	}

	if(6 != iLenth)			//用户名长度不为6则退出
	{
		return 0;
	}
	//因为edx的值为0
	iResult = iEax;

	return iResult;
}

/**
 * @brief 获取CPUID函数
 * @per 修改历史
 * @code
 *	作者		修改时间		函数描述		版本号
 * -------	  ------------	  -------------	  ----------
 * IceAmber	   2013-04-14       获取CPUID	    v1.0
 * @endcode
 */
int CCRACKME4KeygenDlg::GetCPUIDFunction(void)
{
	char cCpuID[16];
	int iRecieve1, iRecieve2;
	EightInt ret = {0};
	int iResult = 0;
	CString strCpuInfo1, strCpuInfo2, strCpuInfo3;

	__asm
	{
		mov		eax, 0
		cpuid
		mov		dword ptr cCpuID, ebx
		mov		dword ptr cCpuID[4], edx
		mov		dword ptr cCpuID[8], ecx
		mov		dword ptr cCpuID[12], 0
	}

	__asm
	{	
		mov		eax, 1
		xor		edx, edx
		cpuid
		mov		dword ptr iRecieve1, edx
		mov		dword ptr iRecieve2, eax
	}

	strCpuInfo2.Format("%08X%08X", iRecieve1, iRecieve2);

	__asm
	{
		mov		eax, 3
		xor		ecx, ecx
		xor		edx, edx
		cpuid
		mov		dword ptr iRecieve1, edx
		mov		dword ptr iRecieve2, ecx
	}

	strCpuInfo3.Format("%08X%08X", iRecieve1, iRecieve2);

	strCpuInfo = strCpuInfo2 + strCpuInfo3;

	ret = DealString(strCpuInfo);

	iResult = ret.lEax;

	return iResult;
}

/**
 * @brief 字符串处理函数
 * @per 修改历史
 * @code
 *	作者		修改时间		函数描述		版本号
 * -------	  ------------	  -------------	  ----------
 * IceAmber	   2013-04-14       处理字符串	    v1.0
 * @endcode
 */
EightInt CCRACKME4KeygenDlg::DealString(CString strParam)
{
	int i = 0;
	int j = 0;
	EightInt iRecieve[4] = {0};
	EightInt iResult = {0};
	char cNumber;
	char cStr[9];
	CString str[4];

	//进行处理的字符串长度为32个字符
	for (i = 0; i < 4; i ++)
	{
		for (j = 0; j < 8 ; j++)
		{
			cStr[j] = strParam[i*8+j];
		}
		cStr[j] = '\0';
		str[i].Format("%8s", cStr);
	}

	for(i = 0; i < 4; i ++)
	{
		iRecieve[i] = CharChangeToNumber(str[i], 8, 16);
	}
	iRecieve[0].lEdx ^= iRecieve[3].lEdx;
	iRecieve[0].lEax ^= iRecieve[3].lEax;
	iRecieve[0].lEdx ^= iRecieve[2].lEdx;
	iRecieve[0].lEax ^= iRecieve[2].lEax;
	iRecieve[0].lEdx ^= iRecieve[1].lEdx;
	iRecieve[0].lEax ^= iRecieve[1].lEax;

	iResult = iRecieve[0];

	return iResult;
}

/**
 * @brief 将字符转成数字
 * @param [in] str 要判断的字符串
 * @param [in] iStrLen 字符串的长度
 * @param [in] iJudgeTimes 字符的判断次数
 * @per 修改历史
 * @code
 *	作者		修改时间		函数描述		版本号
 * -------	  ------------	  -------------	  ----------
 * IceAmber	   2013-04-21       处理字符串	    v1.0
 * @endcode
 */
EightInt CCRACKME4KeygenDlg::CharChangeToNumber(CString str, int iStrLen, int iJudgeTimes)
{
	int i = 0;
	int j = 0;
	int iCounter = 0;
	int iTimes = 0;
	int iChange = 0;
	int iRegister = 0;
	int iEcx = 0;
	int iEsi = 0;	//用来处理用户名前三个字母的密钥
	int iEdx = 0;
	int iEax = 0;
	int iRet = 0;
	EightInt iResult = {0};
	EightInt eightint = {0};

	iCounter = iStrLen;
	if(iStrLen <= 0)
	{
		return iResult;
	}

	for(i = 0; i < iStrLen; i ++)
	{

		iRet = CharJudge(iJudgeTimes, str[i]);
		if(0 == iRet)
		{
			iResult.lEax = 0;
			iResult.lEdx = 0;
			break;
		}
		else
		{
			iChange = (int)str[i];
			iRet = isdigit((int)str[i]);
			if(0 == iRet)
			{
				//传入的是字母
				iChange -= 65;
				if(iChange <= 37)
				{
					if(iChange >= 32)
					{
						//小写字母
						iChange -= 32;
					}
					switch (iChange)
					{
					case 0:
						iRegister = 0x0a;
						break;
					case 1:
						iRegister = 0x0b;
						break;
					case 2:
						iRegister = 0x0c;
						break;
					case 3:
						iRegister = 0x0d;
						break;
					case 4:
						iRegister = 0x0e;
						break;
					case 5:
						iRegister = 0x0f;
						break;
					default:
						break;
					}
				}
			}
			else
			{
				iRegister = iChange - 0x30;
			}
			iEcx = 1;
			if(iCounter > 0)
			{
				for(j = 0; j < iCounter - 1; j ++)
				{
					//获取前三个字母的计算结果
					eightint = NameCaculete(iJudgeTimes, 0, iEcx, iEsi);
					
					iEcx = (int)eightint.lEax;
					iEsi = (int)eightint.lEdx;
				}
			}

			eightint = NameCaculete(iRegister, 0, iEcx, iEsi);
			iResult.lEax += eightint.lEax;
			iResult.lEdx += eightint.lEdx;
			iCounter --;
		}
	}

	return iResult;
}

/**
 * @brief 判断该字符是否为0~f
 * @param [in] iJudgeNum 要判断的次数
 * @param [in] cJudgeChar 要判断的字符
 * @per 修改历史
 * @code
 *	作者		修改时间		函数描述		版本号
 * -------	  ------------	  -------------	  ----------
 * IceAmber	   2013-04-21       处理字符串	    v1.0
 * @endcode
 */
int CCRACKME4KeygenDlg::CharJudge(int iJudgeNum, char cJudgeChar)
{
	int i = 0;
	int iResult = 0;
	char sJudgeStr[] = "00112233445566778899aAbBcCdDeEfF";

	//如果计算的次数小于2或大于16,则返回0
	if(iJudgeNum < 2 || iJudgeNum > 16)
	{
		return 0;
	}

	for(i = 0; i < iJudgeNum; i ++)
	{
		if((cJudgeChar == sJudgeStr[i * 2]) || (cJudgeChar == sJudgeStr[i * 2 + 1]))
		{
			//如果输入的字符为0~f之间则返回1
			iResult = 1;
			break;
		}
	}

	return iResult;
}

这个CrackMe+注册机的分析就到此结束了,欢迎指教,让大家共同进步。

评分

参与人数 4i币 +40 收起 理由
KingSKY + 10 支持原创
ilx + 10 支持原创
土豆 + 10 支持个。
Free_小东 + 10 顶一个

查看全部评分

回复

使用道具 举报

发表于 2013-10-22 23:19:02 | 显示全部楼层
小白菜看不懂。。。

点评

入群要发帖,我等不会入侵的,只能发这些了  详情 回复 发表于 2013-10-22 23:25
回复 支持 反对

使用道具 举报

 楼主| 发表于 2013-10-22 23:25:34 | 显示全部楼层
mojitingliu 发表于 2013-10-22 23:19
小白菜看不懂。。。

入群要发帖,我等不会入侵的,只能发这些了
回复 支持 反对

使用道具 举报

发表于 2013-10-22 23:57:16 | 显示全部楼层
PL。支持一个。   学习了。

点评

只会逆向,其他的还需大家指点  详情 回复 发表于 2013-10-23 00:05
回复 支持 反对

使用道具 举报

 楼主| 发表于 2013-10-23 00:05:49 | 显示全部楼层
土豆 发表于 2013-10-22 23:57
PL。支持一个。   学习了。

只会逆向,其他的还需大家指点
回复 支持 反对

使用道具 举报

头像被屏蔽
发表于 2013-10-23 00:54:54 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复 支持 反对

使用道具 举报

发表于 2013-10-23 10:12:49 | 显示全部楼层
虽然我完全看不懂。。。。
回复 支持 反对

使用道具 举报

发表于 2013-10-23 21:22:29 | 显示全部楼层
顶,虽然看不懂,楼主发这么多也不容易啊
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

旗下站点

邮箱系统

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2025-5-1 19:39 , Processed in 0.067505 second(s), 19 queries , Gzip On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部