zbnysjwsnd8 发表于 2017-8-24 13:54

160个CrackMe之156 算法分析

本帖最后由 zbnysjwsnd8 于 2017-8-30 20:48 编辑

翻了一圈 好像论坛里还没有人成功破解这个CM 刚好今天看了一下 于是就有了这篇文章。
我并不是大神 就是一个菜鸟 如果有错误还希望各位大佬们指出来。

0x0 KeyFile的检测CM首先会读取 crack.dat 如果这个文件不存在则失败
00401170|.A1 6CCA4000   mov eax,dword ptr ds:
00401175|.50            push eax                                           ; /Arg4 = 0019FE60
00401176|.68 81000000   push 0x81                                          ; |Arg3 = 00000081
0040117B|.68 3EB44000   push thecodin.0040B43E                           ; |Arg2 = 0040B43E ASCII "crack.dat"
00401180|.8D95 D0FEFFFF lea edx,                            ; |
00401186|.52            push edx                                           ; |Arg1 = 0019FE74
00401187|.E8 705A0000   call thecodin.fstreambase::open                  ; \fstreambase::open
0040118C|.83C4 10       add esp,0x10
0040118F|.8B8D D0FEFFFF mov ecx,
00401195|.F641 0C 86    test byte ptr ds:,0x86
00401199|.74 6B         je short thecodin.00401206                         ;如果不存在则失败
0040119B|.6A 00         push 0x0                                           ; /Arg3 = 00000000
0040119D|.68 48B44000   push thecodin.0040B448                           ; |Arg2 = 0040B448 ASCII "Kein Dat file vorhanden......"
004011A2|.56            push esi                                           ; |Arg1 = 0040D830
004011A3|.E8 34670000   call thecodin.ostream::outstr                      ; \ostream::outstr
004011A8|.83C4 0C       add esp,0xC
004011AB|.56            push esi                                           ; /Arg1 = 0040D830
004011AC|.E8 DB510000   call thecodin.endl                                 ; \endl
004011B1|.59            pop ecx                                          ;0019FE60
004011B2|.6A 00         push 0x0                                           ; /Arg3 = 00000000
004011B4|.68 66B44000   push thecodin.0040B466                           ; |Arg2 = 0040B466 ASCII "Press a Key....."
004011B9|.56            push esi                                           ; |Arg1 = 0040D830
004011BA|.E8 1D670000   call thecodin.ostream::outstr                      ; \ostream::outstr
004011BF|.83C4 0C       add esp,0xC
004011C2|.56            push esi                                           ; /Arg1 = 0040D830
004011C3|.E8 C4510000   call thecodin.endl                                 ; \endl
004011C8|.59            pop ecx                                          ;0019FE60
004011C9|.E8 2A0A0000   call thecodin.getch
004011CE|.33C0          xor eax,eax
004011D0|.50            push eax
004011D1|.6A 02         push 0x2                                           ; /Arg2 = 00000002
004011D3|.8D95 D0FEFFFF lea edx,                            ; |
004011D9|.52            push edx                                           ; |Arg1 = 0019FE74
004011DA|.E8 0D5B0000   call thecodin.fstream::~fstream                  ; \fstream::~fstream
004011DF|.83C4 08       add esp,0x8
004011E2|.6A 02         push 0x2                                           ; /Arg2 = 00000002
004011E4|.8D8D 68FFFFFF lea ecx,                        ; |
004011EA|.51            push ecx                                           ; |Arg1 = E6898150
004011EB|.E8 FC5A0000   call thecodin.fstream::~fstream                  ; \fstream::~fstream
004011F0|.83C4 08       add esp,0x8
004011F3|.58            pop eax                                          ;0019FE60
004011F4|.8B95 B0FEFFFF mov edx,
004011FA|.64:8915 00000>mov dword ptr fs:,edx
00401201|.E9 E7010000   jmp thecodin.004013ED                              ;返回
00401206|>6A 00         push 0x0                                           ; /Arg3 = 00000000
00401208|.68 77B44000   push thecodin.0040B477                           ; |Arg2 = 0040B477 ASCII "SuCCeSS."
0040120D|.56            push esi                                           ; |Arg1 = 0040D830
0040120E|.E8 C9660000   call thecodin.ostream::outstr                      ; \ostream::outstr
00401213|.83C4 0C       add esp,0xC
00401216|.56            push esi                                           ; /Arg1 = 0040D830
00401217|.E8 70510000   call thecodin.endl                                 ; \endl
0040121C|.59            pop ecx                                          ;0019FE60
0040121D|.8D8D 98FEFFFF lea ecx,                            ;读文件(crack.dat)
00401223|.51            push ecx                                           ; /Arg2 = E6898150
00401224|.8D85 14FFFFFF lea eax,                         ; |
0040122A|.50            push eax                                           ; |Arg1 = 0019FE60
0040122B|.E8 6C620000   call thecodin.istream::operator >>               ; \istream::operator
00401230|.83C4 08       add esp,0x8
00401233|.3BBD 98FEFFFF cmp edi,                            ;如果文件的内容(整数型)和edi(0xE6898150)不相等则失败
00401239|.74 6D         je short thecodin.004012A8                         ;如果相等则转移
0040123B|.6A 00         push 0x0                                           ; /Arg3 = 00000000
0040123D|.68 80B44000   push thecodin.0040B480                           ; |Arg2 = 0040B480 ASCII "
DON'T TOUCH MY FILE THATS ILLEGAL."
00401242|.56            push esi                                           ; |Arg1 = 0040D830
00401243|.E8 94660000   call thecodin.ostream::outstr                      ; \ostream::outstr
00401248|.83C4 0C       add esp,0xC
0040124B|.6A 00         push 0x0                                           ; /Arg3 = 00000000
0040124D|.68 A4B44000   push thecodin.0040B4A4                           ; |Arg2 = 0040B4A4 ASCII "
FILE KORRUPTED DON'T PATCH........"
00401252|.56            push esi                                           ; |Arg1 = 0040D830
00401253|.E8 84660000   call thecodin.ostream::outstr                      ; \ostream::outstr
00401258|.83C4 0C       add esp,0xC
0040125B|.6A 00         push 0x0                                           ; /Arg3 = 00000000
0040125D|.68 C8B44000   push thecodin.0040B4C8                           ; |Arg2 = 0040B4C8 ASCII "
Press a Key...."
00401262|.56            push esi                                           ; |Arg1 = 0040D830
00401263|.E8 74660000   call thecodin.ostream::outstr                      ; \ostream::outstr
00401268|.83C4 0C       add esp,0xC
0040126B|.E8 88090000   call thecodin.getch
00401270|.33C0          xor eax,eax
00401272|.50            push eax
00401273|.6A 02         push 0x2                                           ; /Arg2 = 00000002
00401275|.8D95 D0FEFFFF lea edx,                            ; |
0040127B|.52            push edx                                           ; |Arg1 = 0019FE74
0040127C|.E8 6B5A0000   call thecodin.fstream::~fstream                  ; \fstream::~fstream
00401281|.83C4 08       add esp,0x8
00401284|.6A 02         push 0x2                                           ; /Arg2 = 00000002
00401286|.8D8D 68FFFFFF lea ecx,                        ; |
0040128C|.51            push ecx                                           ; |Arg1 = E6898150
0040128D|.E8 5A5A0000   call thecodin.fstream::~fstream                  ; \fstream::~fstream
00401292|.83C4 08       add esp,0x8
00401295|.58            pop eax                                          ;0019FE60
00401296|.8B95 B0FEFFFF mov edx,
0040129C|.64:8915 00000>mov dword ptr fs:,edx
004012A3|.E9 45010000   jmp thecodin.004013ED

新建一个crack.dat 内容是0xE6898150的十进制,即可过掉这步验证

第一步验证过掉后CM的截图:



0x1 Name Organization Serial的输入及Serial的检测上一步验证成功以后 CM要求用户输入Name Organization Serial这三个信息 其中Serial必须为整数,否则失败。
004012CA|> \6A 00         push 0x0                                           ; /Arg3 = 00000000
004012CC|.68 D9B44000   push thecodin.0040B4D9                           ; |Arg2 = 0040B4D9 ASCII "Name:->"
004012D1|.56            push esi                                           ; |Arg1 = 0040D830
004012D2|.E8 05660000   call thecodin.ostream::outstr                      ; \ostream::outstr
004012D7|.83C4 0C       add esp,0xC
004012DA|.FFB5 A4FEFFFF push                                  ; /Arg2 = 02023628
004012E0|.68 E4D74000   push offset thecodin.cin                           ; |Arg1 = 0040D7E4
004012E5|.E8 FE5D0000   call thecodin.istream::operator >>               ; \istream::operator
004012EA|.83C4 08       add esp,0x8
004012ED|.6A 00         push 0x0                                           ; /Arg3 = 00000000
004012EF|.68 E3B44000   push thecodin.0040B4E3                           ; |Arg2 = 0040B4E3 ASCII "Orga:->"
004012F4|.56            push esi                                           ; |Arg1 = 0040D830
004012F5|.E8 E2650000   call thecodin.ostream::outstr                      ; \ostream::outstr
004012FA|.83C4 0C       add esp,0xC
004012FD|.FFB5 A0FEFFFF push                                   ; /Arg2 = 02023638
00401303|.68 E4D74000   push offset thecodin.cin                           ; |Arg1 = 0040D7E4
00401308|.E8 DB5D0000   call thecodin.istream::operator >>               ; \istream::operator
0040130D|.83C4 08       add esp,0x8
00401310|.6A 00         push 0x0                                           ; /Arg3 = 00000000
00401312|.68 EDB44000   push thecodin.0040B4ED                           ; |Arg2 = 0040B4ED ASCII "Serial:->"
00401317|.56            push esi                                           ; |Arg1 = 0040D830
00401318|.E8 BF650000   call thecodin.ostream::outstr                      ; \ostream::outstr
0040131D|.83C4 0C       add esp,0xC
00401320|.8D8D 9CFEFFFF lea ecx,
00401326|.51            push ecx                                           ; /Arg2 = 0040D830
00401327|.68 E4D74000   push offset thecodin.cin                           ; |Arg1 = 0040D7E4
0040132C|.E8 6B610000   call thecodin.istream::operator >>               ; \istream::operator
00401331|.83C4 08       add esp,0x8
00401334|.A1 E4D74000   mov eax,dword ptr ds:
00401339|.F640 0C 86    test byte ptr ds:,0x86
0040133D|.74 5B         je short thecodin.0040139A                         ;如果输入的是整数则转移
0040133F|.6A 00         push 0x0                                           ; /Arg3 = 00000000
00401341|.68 F7B44000   push thecodin.0040B4F7                           ; |Arg2 = 0040B4F7 ASCII "Eingabe Fehler !!"
00401346|.56            push esi                                           ; |Arg1 = 0040D830
00401347|.E8 90650000   call thecodin.ostream::outstr                      ; \ostream::outstr
0040134C|.83C4 0C       add esp,0xC
0040134F|.6A 00         push 0x0                                           ; /Arg3 = 00000000
00401351|.68 09B54000   push thecodin.0040B509                           ; |Arg2 = 0040B509 ASCII "
Press a key."
00401356|.56            push esi                                           ; |Arg1 = 0040D830
00401357|.E8 80650000   call thecodin.ostream::outstr                      ; \ostream::outstr
0040135C|.83C4 0C       add esp,0xC
0040135F|.E8 94080000   call thecodin.getch
00401364|.83C8 FF       or eax,0xFFFFFFFF
00401367|.50            push eax                                           ;thecodin.cout
00401368|.6A 02         push 0x2                                           ; /Arg2 = 00000002
0040136A|.8D95 D0FEFFFF lea edx,                            ; |
00401370|.52            push edx                                           ; |Arg1 = 0040D838
00401371|.E8 76590000   call thecodin.fstream::~fstream                  ; \fstream::~fstream
00401376|.83C4 08       add esp,0x8
00401379|.6A 02         push 0x2                                           ; /Arg2 = 00000002
0040137B|.8D8D 68FFFFFF lea ecx,                        ; |
00401381|.51            push ecx                                           ; |Arg1 = 0040D830
00401382|.E8 65590000   call thecodin.fstream::~fstream                  ; \fstream::~fstream
00401387|.83C4 08       add esp,0x8
0040138A|.58            pop eax                                          ;thecodin.cout
0040138B|.8B95 B0FEFFFF mov edx,
00401391|.64:8915 00000>mov dword ptr fs:,edx                           ;thecodin.0040D838
00401398|.EB 53         jmp short thecodin.004013ED                        ;返回
然后,CM将Name作为第一个参数 Organization作为第二个参数 Serial作为第三个参数来调用0x004013F4这个函数

0x2 分析函数:0x004013F4CM首先计算Name的长度
00401403|.8B7D 08       mov edi,
00401406|.33C0          xor eax,eax
00401408|.8945 FC       mov ,eax
0040140B|.57            push edi                                           ; /s = Name
0040140C|.E8 0F0B0000   call thecodin.strlen                               ; \strlen
00401411|.59            pop ecx                                          ;02023628
00401412|.8945 F4       mov ,eax                           ;Name长度
然后获取以time(0)的返回值为随机数种子 获取一个随机数r(无符号整数)
00401443|.6A 00         push 0x0                                           ; /timer = NULL
00401445|.E8 1A980000   call thecodin.time                                 ; \time
0040144A|.59            pop ecx                                          ;02023628
0040144B|.50            push eax                                           ; /seed = 0x6
0040144C|.E8 3B6E0000   call thecodin.srand                              ; \srand
00401451|.59            pop ecx                                          ;02023628
00401452|.E8 4D6E0000   call thecodin.rand                                 ; [获取一个随机数 记为r
然后将r mod 10的结果保存
00401457|.B9 0A000000   mov ecx,0xA
0040145C|.99            cdq
0040145D|.F7F9          idiv ecx
0040145F|.8955 F8       mov ,edx                              ;r mod 10
然后就开始用Name和Organization来计算Serial(_Serial的初始值是0)
00401462|. /EB 14         jmp short thecodin.00401478
00401464|> |0FBE06      /movsx eax,byte ptr ds:             ;Organization
00401467|. |0FBE17      |movsx edx,byte ptr ds:             ;Name
0040146A|. |F7EA          |imul edx
0040146C|. |0145 FC       |add ,eax
0040146F|. |3B5D F4       |cmp ebx,
00401472|. |75 02         |jnz short thecodin.00401476
00401474|. |33DB          |xor ebx,ebx
00401476|> |46            |inc esi
00401477|. |47            |inc edi
00401478|> \56            |push esi                              ; /s = Organization
00401479|.E8 A20A0000   |call thecodin.strlen                  ; \strlen
0040147E|.59            |pop ecx
0040147F|.85C0          |test eax,eax                            ;eax是Orgaization的长度
00401481|.^ 75 E1         \jnz short thecodin.00401464

这里需要注意一下:Orgaization的长度要和Name的长度相等;观察一下Name和Serial在内存中存放的顺序 可以得知Name的长度不能超过16个字节。
最后开始校验用户输入的Serial
00401483|.33DB          xor ebx,ebx
00401485|.8B45 10       mov eax,
00401488|.F76D F8       imul
0040148B|.8B55 FC       mov edx,
0040148E|.0FAF55 F8   imul edx,
00401492|.3BC2          cmp eax,edx                                        ;相等则成功 不相等则失败
00401494|.75 43         jnz short thecodin.004014D9

0x3 计算注册码

由这段代码可以得知:当usernum(用户输入的serial) * dummy == serial(CM计算的serial) * dummy(均为无符号乘法)时 就会成功。其中dummy = r mod 10.因为dummy可能为0 所以应分为两种情况讨论:当dummy为0时:
usernum * dummy == serial * dummy总是成立的 因此这时一定会成功
当dummy不为0时:
即usernum == serial时才能成功.
如果想让用户在任何情况下都能注册成功 我们直接考虑第二种情况即可,即dummy不为0的时候。这样就可以写出一个注册机:
运行效果如图所示:
最后附上一组可用的Name Organization和Serial以及CM的注册成功图片和CM源文件(包括KeyFile)Name:_KaQqi
Organization:_52pj_
Serial:48875CM成功图片:CM源文件以及KeyFile:

KaQqi 发表于 2017-8-24 14:11

膜拜星斗

标题改一下,160cm之156 算法分析

魔弑神 发表于 2017-8-24 15:39

大神 厉害,
160个cm在哪里下载呢

winooxx 发表于 2017-8-25 11:48

好文,分析到位!感谢分享

云飞扬1 发表于 2017-8-25 17:16

求一份1-156的算法解析,
我是渣渣,想学习学习

whb122 发表于 2017-8-26 15:06

这是干嘛用的?
页: [1]
查看完整版本: 160个CrackMe之156 算法分析