前几天在玩CrackMe 一直想找个共享软件练手
e-PDF To HTML Converter
看了这贴 就拿来分析一下 http://bbs.pediy.com/showthread.php?t=112427
见笑了。。。
此软件无壳 直接拉进OD
在GetWindowTextA断下 来到调用代码段
0043C169 . 68 C8000000 push 0C8 ; /Count = C8 (200.)
0043C16E . 68 3C3B4E00 push 004E3B3C ; |Buffer = pdf2html.004E3B3C
0043C173 . 68 FB030000 push 3FB ; |ControlID = 3FB (1019.)
0043C178 . 8B45 08 mov eax, dword ptr [ebp+8] ; |
0043C17B . 50 push eax ; |hWnd
0043C17C . FF15 0C654A00 call dword ptr [<&USER32.GetDlgItemTe>; \GetDlgItemTextA
0043C182 . 68 3C3B4E00 push 004E3B3C ; key压栈
0043C187 . E8 EBF5FFFF call 0043B777; 注册码验证CALL跟进
0043B777 /$ 55 push ebp
0043B778 |. 8BEC mov ebp, esp
0043B77A |. 83EC 3C sub esp, 3C
0043B77D |. 56 push esi
0043B77E |. 8B45 08 mov eax, dword ptr [ebp+8]
0043B781 |. 8A08 mov cl, byte ptr [eax] ; 取第一个key 放入局部变量
0043B783 |. 884D F4 mov byte ptr [ebp-C], cl
0043B786 |. C645 F5 00 mov byte ptr [ebp-B], 0 ; 后面一字节赋值为0 在后面的call中这个0很重要
0043B78A |. 8B55 08 mov edx, dword ptr [ebp+8]
0043B78D |. 8A42 01 mov al, byte ptr [edx+1] ; 取第二个key 放入局部变量
0043B790 |. 8845 E8 mov byte ptr [ebp-18], al
0043B793 |. C645 E9 00 mov byte ptr [ebp-17], 0 ; 同上
0043B797 |. 8B4D 08 mov ecx, dword ptr [ebp+8]
0043B79A |. 8A51 0E mov dl, byte ptr [ecx+E] ; 取第15个key 放入局部变量
0043B79D |. 8855 DC mov byte ptr [ebp-24], dl
0043B7A0 |. C645 DD 00 mov byte ptr [ebp-23], 0 ; 同上
0043B7A4 |. 8B45 08 mov eax, dword ptr [ebp+8]
0043B7A7 |. 8A48 0F mov cl, byte ptr [eax+F] ; 取第16个key 放入局部变量
0043B7AA |. 884D D0 mov byte ptr [ebp-30], cl
0043B7AD |. C645 D1 00 mov byte ptr [ebp-2F], 0 ; 同上
0043B7B1 |. 8B55 08 mov edx, dword ptr [ebp+8]
0043B7B4 |. 8A42 02 mov al, byte ptr [edx+2] ; 取第3个key 放入局部变量
0043B7B7 |. 8845 C4 mov byte ptr [ebp-3C], al
0043B7BA |. C645 C5 00 mov byte ptr [ebp-3B], 0 ; 同上
0043B7BE |. 0FBE4D C4 movsx ecx, byte ptr [ebp-3C] ; 取出第三个key
0043B7C2 |. 83F9 24 cmp ecx, 24 ; 比较第三个key是否为 '$'
0043B7C5 |. 74 04 je short 0043B7CB ; 相等的话进入关键验证段
0043B7C7 |. 33C0 xor eax, eax ; 不相等返回0
0043B7C9 |. EB 67 jmp short 0043B832
0043B7CB |> 8D55 F4 lea edx, dword ptr [ebp-C] ; 取出第一个key的地址
0043B7CE |. 52 push edx ; 压栈
0043B7CF |. E8 BCC80300 call 00478090 ; 关键验证call跟入
00478090 /$ FF7424 04 push dword ptr [esp+4];压入先前存放了key字符的局部变量地址
00478094 |. E8 6CFFFFFF call 00478005 ; 傀儡函数 再次跟进
00478099 |. 59 pop ecx
0047809A \. C3 retn
00478005 /$ 53 push ebx ; 先说明下 前面局部变量只是对连续两个字节赋值(key和0) 故这里一定最多只能得到第2个数0
00478006 |. 55 push ebp
00478007 |. 56 push esi
00478008 |. 57 push edi
00478009 |. 8B7C24 14 mov edi, dword ptr [esp+14] ; 把原先存放key字符局部变量的地址取出
0047800D |> 833D 481C4E00>/cmp dword ptr [4E1C48], 1 ; 此比较的作用未知 [4E1C48]始终为1 对后面无影响
00478014 |. 7E 0F |jle short 00478025 ; 跳
00478016 |. 0FB607 |movzx eax, byte ptr [edi]
00478019 |. 6A 08 |push 8
0047801B |. 50 |push eax
0047801C |. E8 635B0000 |call 0047DB84 ; 未知call 因为[4E1C48]始终为1 故此CALL并不会被调用
00478021 |. 59 |pop ecx
00478022 |. 59 |pop ecx
00478023 |. EB 0F |jmp short 00478034
00478025 |> 0FB607 |movzx eax, byte ptr [edi] ; 取key字符
00478028 |. 8B0D 3C1A4E00 |mov ecx, dword ptr [4E1A3C] ; 取出一个数组的地址 数组内容见下面
0047802E |. 8A0441 |mov al, byte ptr [ecx+eax*2] ; 取出数组中的内容 ecx[key * 2]
00478031 |. 83E0 08 |and eax, 8 ; 与8(1000)位与
00478034 |> 85C0 |test eax, eax ; 测试0
00478036 |. 74 03 |je short 0047803B ; 为0则跳出
00478038 |. 47 |inc edi ; 地址偏移1字节
00478039 |.^ EB D2 \jmp short 0047800D
0047803B |> 0FB637 movzx esi, byte ptr [edi] ; 取key字符
0047803E |. 47 inc edi ; 见前面说明
0047803F |. 83FE 2D cmp esi, 2D
00478042 |. 8BEE mov ebp, esi
00478044 |. 74 05 je short 0047804B
00478046 |. 83FE 2B cmp esi, 2B
00478049 |. 75 04 jnz short 0047804F
0047804B |> 0FB637 movzx esi, byte ptr [edi]
0047804E |. 47 inc edi ; 见前面说明
0047804F |> 33DB xor ebx, ebx ; 前面比较如果与2D和2B不相等的话 则到这里 ebx清零
00478051 |> 833D 481C4E00>/cmp dword ptr [4E1C48], 1 ; 相似代码段 同前面
00478058 |. 7E 0C |jle short 00478066
0047805A |. 6A 04 |push 4
0047805C |. 56 |push esi
0047805D |. E8 225B0000 |call 0047DB84 ; 相似代码段 同前面
00478062 |. 59 |pop ecx
00478063 |. 59 |pop ecx
00478064 |. EB 0B |jmp short 00478071
00478066 |> A1 3C1A4E00 |mov eax, dword ptr [4E1A3C] ; 相似代码段 同前面
0047806B |. 8A0470 |mov al, byte ptr [eax+esi*2]
0047806E |. 83E0 04 |and eax, 4 ; 取出数组中的数与4(100)位与
00478071 |> 85C0 |test eax, eax
00478073 |. 74 0D |je short 00478082 ; 为0则跳出
00478075 |. 8D049B |lea eax, dword ptr [ebx+ebx*4] ; eax = ebx * 5
00478078 |. 8D5C46 D0 |lea ebx, dword ptr [esi+eax*2-30] ; ebx = esi + ebx * 5 * 2 - 30h
0047807C |. 0FB637 |movzx esi, byte ptr [edi]
0047807F |. 47 |inc edi
00478080 |.^ EB CF \jmp short 00478051
00478082 |> 83FD 2D cmp ebp, 2D
00478085 |. 8BC3 mov eax, ebx ; ebx为返回值
00478087 |. 75 02 jnz short 0047808B ; 如果ebp(前面ebp被esi赋值)不是2D则不对返回值neg
00478089 |. F7D8 neg eax
0047808B |> 5F pop edi
0047808C |. 5E pop esi
0047808D |. 5D pop ebp
0047808E |. 5B pop ebx
0047808F \. C3 retn
看到这里不懂也没关系 后面对返回值的处理会详细解释
上面代码用到的数组
仔细观察的话 如果不想让取出的数与4或8位与后不为0的话 只能找84h或48h
004E1A46 20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 . . . . . . . .
004E1A56 20 00 28 00 28 00 28 00 28 00 28 00 20 00 20 00 .(.(.(.(.(. . .
004E1A66 20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 . . . . . . . .
004E1A76 20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 . . . . . . . .
004E1A86 48 00 10 00 10 00 10 00 10 00 10 00 10 00 10 00 H........
004E1A96 10 00 10 00 10 00 10 00 10 00 10 00 10 00 10 00 ........
004E1AA6 84 00 84 00 84 00 84 00 84 00 84 00 84 00 84 00 ????????
004E1AB6 84 00 84 00 10 00 10 00 10 00 10 00 10 00 10 00 ??......
004E1AC6 10 00 81 00 81 00 81 00 81 00 81 00 81 00 01 00 .??????.
004E1AD6 01 00 01 00 01 00 01 00 01 00 01 00 01 00 01 00 ........
004E1AE6 01 00 01 00 01 00 01 00 01 00 01 00 01 00 01 00 ........
004E1AF6 01 00 01 00 01 00 10 00 10 00 10 00 10 00 10 00 ........
004E1B06 10 00 82 00 82 00 82 00 82 00 82 00 82 00 02 00 .??????.
004E1B16 02 00 02 00 02 00 02 00 02 00 02 00 02 00 02 00 ........
004E1B26 02 00 02 00 02 00 02 00 02 00 02 00 02 00 02 00 ........
004E1B36 02 00 02 00 02 00 10 00 10 00 10 00 10 00 20 00 ....... .
出来后
0043B7D4 |. 83C4 04 add esp, 4
0043B7D7 |. 8BF0 mov esi, eax ; 把返回值放入esi
0043B7D9 |. 8D45 D0 lea eax, dword ptr [ebp-30] ; 取存了第16个key的局部变量地址
0043B7DC |. 50 push eax ; 压栈
0043B7DD |. E8 AEC80300 call 00478090 ; 再次相同的CALL
0043B7E2 |. 83C4 04 add esp, 4
0043B7E5 |. 03F0 add esi, eax ; 这次把返回值和esi相加了。。
0043B7E7 |. 83FE 0A cmp esi, 0A ; 关键比较 两次返回值加起来要与0xA相等 这样的话结合上面的分析 我们必须构造出使两次返回值的和为0xA的字符
对数组的观察发现 里面大部分的数与4或8进行and操作的话 都会使返回值为零
故我第一次想让第一次的返回值为0 第二次的返回值为0xA 但是对前面几个操作进行反推发现并不能使返回值为0xA
要让返回值为0xA 那么ebx需为0xA
再对 [esi+eax*2-30] 反推得esi(esi为key字符)必须为0x3A(eax为0是因为前面的ebx被清零了 eax被ebx赋值为0)
但是如果esi为0x3A的话 就无法通过前面与取出的数组元素的比较(取出的数跟4或8 进行and操作后都为0) 那么就无法进行lea [esi+eax*2-30]
观察数组 结合计算:004E1A46(数组基地址) + 0x3A * 2 = 004E1ABA 发现此地址旁边刚好就是0x84 这个数和4and操作不会为0
其实那么就不跳转 能lea [esi+eax*2-30]了
那么让esi再小一点 取出数组[eax+esi*2] 就能取到0x84了
我构造出了esi为0x35 即key字符'5' 这样取到0x84 让返回值也成了0x5 两次相加也就是0xA
0043B7EA |. 75 23 jnz short 0043B80F ; 不相等的话就验证失败
0043B7EC |. 8D4D E8 lea ecx, dword ptr [ebp-18] ; 完全同上
0043B7EF |. 51 push ecx
0043B7F0 |. E8 9BC80300 call 00478090 ; 完全同上
0043B7F5 |. 83C4 04 add esp, 4
0043B7F8 |. 8BF0 mov esi, eax ; 完全同上
0043B7FA |. 8D55 DC lea edx, dword ptr [ebp-24]
0043B7FD |. 52 push edx
0043B7FE |. E8 8DC80300 call 00478090 ; 完全同上
0043B803 |. 83C4 04 add esp, 4
0043B806 |. 03F0 add esi, eax ; 完全同上
0043B808 |. 83FE 0A cmp esi, 0A
0043B80B |. 75 02 jnz short 0043B80F
0043B80D |. EB 04 jmp short 0043B813
0043B80F |> 33C0 xor eax, eax
0043B811 EB 1F jmp short 0043B832
0043B813 |> 8B45 08 mov eax, dword ptr [ebp+8]
0043B816 |. 8A48 03 mov cl, byte ptr [eax+3] ; 取第四个key
0043B819 |. 884D C4 mov byte ptr [ebp-3C], cl
0043B81C |. C645 C5 00 mov byte ptr [ebp-3B], 0
0043B820 |. 0FBE55 C4 movsx edx, byte ptr [ebp-3C]
0043B824 |. 83FA 2F cmp edx, 2F ; 第四个key为0x2f
0043B827 |. 74 04 je short 0043B82D
0043B829 |. 33C0 xor eax, eax
0043B82B EB 05 jmp short 0043B832
0043B82D |> B8 01000000 mov eax, 1 ; 成功验证的返回值为1
0043B832 |> 5E pop esi
0043B833 |. 8BE5 mov esp, ebp
0043B835 |. 5D pop ebp
0043B836 \. C3 retn
这样的话这个key的第1, 2,15,16个key可以是'5'(ascii 0x35) 第四个key是'/'(ascii 0x2f)
第3个key是'$'(ascii 0x24)
例子: 55$/777777777755 这样就能过验证了
分析结束。我表达能力不好 有些东西很难说清 见笑了。。
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!