冰琥珀 发表于 2013-10-22 22:51:03

一个简单的CrackMe分析

    这是一个简单的CrackMe分析,大牛勿笑,欢迎指教。
    这个CrackMe没有关键字符串提醒,没有敏感函数的调用,常规的查找字符串和对敏感函数下断点都行不通,只能另想办法。用IDA打开软件,在函数列表中看到一个函数UpDateData,于是眼前一亮。因为在MFC中,这个函数是用来更新控件数据的,所以就猜想这个CrackMe是通过这个函数来获取用户名和序列号,找到这个函数的调用位置,然后用OD加载CrackMe,找到UpDateData函数的调用位置,并下断点。按下F9运行,输入用户名和序列号,点击“确定”后,程序停在下面这个位置:
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,然后取出用户名,代码如下:
;获取用户名和序列号
00401A09   .E8 1E070000   call    <jmp.&MFC71.#6236_CWnd::UpdateData>
;调用LoadLibraryA来加载Reg.dll
00401A0E   .68 A8394000   push004039A8; /FileName = "Reg.dll"
00401A13   .FF15 34304000 call    dword ptr [<&KERNEL32.LoadLibraryA>]               
; \LoadLibraryA
00401A19   .68 70394000   push    00403970
00401A1E   .8D4C24 18   lea   ecx, dword ptr
;保存Reg.dll的句柄
00401A22   .894424 24   mov   dword ptr , eax
;取出用户名
00401A6A   .FF15 9C314000 call    dword ptr [<&MFC71.#876_ATL::CSimpleStringT<char,1>;MFC71.7C158BCD

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

   然后取出用户名的前三个字节,通过一些算法来对其进行计算,代码如下:
00401A7F   .33ED          xor   ebp, ebp
;取出用户名的一个字节
00401A81   >8A442C 24   mov   al, byte ptr
;判断其是否为字符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这个函数里都做了些什么,代码如下:
;获取第二个参数
00402230/$8B4424 08   mov   eax, dword ptr
;获取第四个参数
00402234|.8B4C24 10   mov   ecx, dword ptr
;用第二个参数和第四个参数进行或运算
00402238|.0BC8          or      ecx, eax
;获取第三个参数
0040223A|.8B4C24 0C   mov   ecx, dword ptr
;如果或的结果部位0则跳转
0040223E|.75 09         jnz   short 00402249
;获取第一个参数
00402240|.8B4424 04   mov   eax, dword ptr
;第一个参数与第三个参数相乘,结果低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
;第一个参数乘以第四个参数,结果存与eax
00402252|.F76424 14   mul   dword ptr
;将所得的积与前面所得的积相加
00402256|.03D8          add   ebx, eax
;取出第一个参数
00402258|.8B4424 08   mov   eax, dword ptr
;ecx存放的是第三个参数,这里将第一个参数与第三个参数相乘
0040225C|.F7E1          mul   ecx
;将所得结果与前面的和相加
0040225E|.03D3          add   edx, ebx
00402260|.5B            pop   ebx
00402261\.C2 1000       retn    10

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

    通过上面的两个判断可知,用户名的长度只能为6个字节。接下来是通过00401AC1   .E8 BA010000   call    00401C80这个函数来获取CPU信息的函数,这个函数代码如下:
;实例化4个CString对象
00401CE1|.FF15 A8314000 call    dword ptr [<&MFC71.#310_ATL::CStringT<char,StrTraitMFC>;MFC71.7C173199
00401CE7|.8D4C24 0C   lea   ecx, dword ptr
00401CEB|.FF15 A8314000 call    dword ptr [<&MFC71.#310_ATL::CStringT<char,StrTraitMFC>;MFC71.7C173199
00401CF1|.8D4C24 10   lea   ecx, dword ptr
00401CF5|.FF15 A8314000 call    dword ptr [<&MFC71.#310_ATL::CStringT<char,StrTraitMFC>;MFC71.7C173199
00401CFB|.8D4C24 14   lea   ecx, dword ptr
00401CFF|.FF15 A8314000 call    dword ptr [<&MFC71.#310_ATL::CStringT<char,StrTraitMFC>;MFC71.7C173199
00401D05|.C64424 40 04mov   byte ptr , 4
;获取CPU制造商信息信息
00401D0A|.33C0          xor   eax, eax
00401D0C|.0FA2          cpuid
00401D0E|.895C24 24   mov   dword ptr , ebx
00401D12|.895424 28   mov   dword ptr , edx
00401D16|.894C24 2C   mov   dword ptr , ecx
00401D1A|.8D4C24 24   lea   ecx, dword ptr
00401D1E|.51            push    ecx
00401D1F|.8D5424 1C   lea   edx, dword ptr
;将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 , edx
00401D3F|.894424 20   mov   dword ptr , eax
00401D43|.8B4424 20   mov   eax, dword ptr
00401D47|.8B4C24 1C   mov   ecx, dword ptr
00401D4B|.50            push    eax
00401D4C|.51            push    ecx
00401D4D|.8D5424 18   lea   edx, dword ptr
;将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 , edx
00401D6F|.894C24 20   mov   dword ptr , ecx
00401D73|.8B4424 20   mov   eax, dword ptr
00401D77|.8B4C24 1C   mov   ecx, dword ptr
00401D7B|.50            push    eax
00401D7C|.51            push    ecx
00401D7D|.8D5424 1C   lea   edx, dword ptr
;将这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
00401D91|.50            push    eax
00401D92|.8D4C24 24   lea   ecx, dword ptr
00401D96|.51            push    ecx
00401D97|.8D5424 34   lea   edx, dword ptr
00401D9B|.52            push    edx
;这里将CPU序列号的两个字符串连成一个字符串
00401D9C|.E8 3FFEFFFF   call    00401BE0

    接下来通过00401ADC   .E8 CFFDFFFF   call    004018B0来对获取的CPU序列号进行计算,这个函数的实现过程如下:
00401903|> /33F6          /xor   esi, esi
;要获取的元素下标
00401905|> |8D0437      |/lea   eax, dword ptr
00401908|. |50            ||push    eax
;源字符串的存放位置
00401909|. |8D4C24 58   ||lea   ecx, dword ptr
;这里调用的是GetAt函数,来获取相应位置的字符
0040190D|. |FF15 D4314000 ||call    dword ptr [<&MFC71.#2451_ATL::CSimpleStringT<char,1>>;MFC71.7C1894E7
00401913|. |8D4C24 0C   ||lea   ecx, dword ptr
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
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
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 , eax
0040195E|. |8954F4 28   |mov   dword ptr , edx
00401962|. |46            |inc   esi
00401963|. |83C7 04       |add   edi, 4
00401966|. |83FE 04       |cmp   esi, 4
00401969|.^\7C D7         \jl      short 00401942

    字符串转换为数字的函数代码如下:
;获取要转换的字符串首
004016EE|.8D4C24 3C   lea   ecx, dword ptr
004016F2|.896C24 34   mov   dword ptr , 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 , eax
00401702|.896C24 1C   mov   dword ptr , ebp
00401706|.896C24 20   mov   dword ptr , ebp
0040170A|.896C24 10   mov   dword ptr , ebp
0040170E|.0F8E 0E010000 jle   00401822
00401714|.8B7C24 18   mov   edi, dword ptr
00401718|.8D58 FF       lea   ebx, dword ptr
0040171B|.895C24 14   mov   dword ptr , ebx
0040171F|.90            nop
00401720|> /8B4424 40   /mov   eax, dword ptr
00401724|. |50            |push    eax
00401725|. |55            |push    ebp
;获取要转换的字符串地址
00401726|. |8D4C24 44   |lea   ecx, dword ptr
;获取要转换的字符
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
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
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
;这里用个switch语句来将字母转为相应的数字
00401788|. |FF2495 681840>|jmp   dword ptr
;字母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
004017C6|. |99            |cdq
004017C7|. |8BE8          |mov   ebp, eax
004017C9|. |895424 28   |mov   dword ptr , edx
004017CD|. |8D49 00       |lea   ecx, dword ptr
;下面这个循环用来确定字符在转换化后数字所处的位置
004017D0|> |8B4424 28   |/mov   eax, dword ptr
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
004017E8|. |8B5C24 14   |mov   ebx, dword ptr
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
004017FC|. |8B7424 20   |mov   esi, dword ptr
00401800|. |03C8          |add   ecx, eax
00401802|. |8B4424 18   |mov   eax, dword ptr
00401806|. |13F2          |adc   esi, edx
00401808|. |45            |inc   ebp
00401809|. |4B            |dec   ebx
;判断转换是否结束
0040180A|. |3BE8          |cmp   ebp, eax
;保存转换结果
0040180C|. |894C24 1C   |mov   dword ptr , ecx
00401810|. |897424 20   |mov   dword ptr , esi
00401814|. |896C24 10   |mov   dword ptr , ebp
00401818|. |895C24 14   |mov   dword ptr , ebx
0040181C|.^\0F8C FEFEFFFF \jl      00401720

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

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

    接下来将所得结果转化为字符串,代码如下:
00401AE5   .57            push    edi
00401AE6   .56            push    esi
00401AE7   .8D4424 1C   lea   eax, dword ptr
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
00401AFB   .83C4 14       add   esp, 14

    然后调用reg.dll中的加密函数,代码如下:
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
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序列号的计算一样,代码如下:
00401B19   .50            push    eax
00401B1A   .8D4C24 18   lea   ecx, dword ptr
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
00401B29   .8BCC          mov   ecx, esp
00401B2B   .896424 20   mov   dword ptr , 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所需要的序列号了,接下来就是序列号的比较,代码如下:
;这里获取用户输入的序列号
00401B3B   .8B4B 78       mov   ecx, dword ptr
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
00401B48   .75 0A         jnz   short 00401B54

    分析终于完成了,长呼一口气,接下来就是注册机的代码了
/**
* @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)        //用户名不能有字符'0'
                {
                        return 0;
                }
                iEax = (int)strName;
                //获取前三个字母的计算结果
                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;
        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, edx
                mov                dword ptr cCpuID, ecx
                mov                dword ptr cCpuID, 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 = {0};
        EightInt iResult = {0};
        char cNumber;
        char cStr;
        CString str;

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

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

        iResult = iRecieve;

        return iResult;
}

/**
* @brief 将字符转成数字
* @param str 要判断的字符串
* @param iStrLen 字符串的长度
* @param 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);
                if(0 == iRet)
                {
                        iResult.lEax = 0;
                        iResult.lEdx = 0;
                        break;
                }
                else
                {
                        iChange = (int)str;
                        iRet = isdigit((int)str);
                        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 iJudgeNum 要判断的次数
* @param 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) || (cJudgeChar == sJudgeStr))
                {
                        //如果输入的字符为0~f之间则返回1
                        iResult = 1;
                        break;
                }
        }

        return iResult;
}

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

mojitingliu 发表于 2013-10-22 23:19:02

小白菜看不懂。。。:curse:

冰琥珀 发表于 2013-10-22 23:25:34

mojitingliu 发表于 2013-10-22 23:19 static/image/common/back.gif
小白菜看不懂。。。

入群要发帖,我等不会入侵的,只能发这些了

土豆 发表于 2013-10-22 23:57:16

PL。支持一个。   学习了。

冰琥珀 发表于 2013-10-23 00:05:49

土豆 发表于 2013-10-22 23:57 static/image/common/back.gif
PL。支持一个。   学习了。

只会逆向,其他的还需大家指点

lainiang55 发表于 2013-10-23 00:54:54

zhoujian017 发表于 2013-10-23 10:12:49

虽然我完全看不懂。。。。

孤卫 发表于 2013-10-23 21:22:29

顶,虽然看不懂,楼主发这么多也不容易啊
页: [1]
查看完整版本: 一个简单的CrackMe分析