一个简单的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+注册机的分析就到此结束了,欢迎指教,让大家共同进步。 小白菜看不懂。。。:curse: mojitingliu 发表于 2013-10-22 23:19 static/image/common/back.gif
小白菜看不懂。。。
入群要发帖,我等不会入侵的,只能发这些了 PL。支持一个。 学习了。 土豆 发表于 2013-10-22 23:57 static/image/common/back.gif
PL。支持一个。 学习了。
只会逆向,其他的还需大家指点 虽然我完全看不懂。。。。 顶,虽然看不懂,楼主发这么多也不容易啊
页:
[1]