疑问:
mexcrk1是Delphi写的硬编码, 虽然是硬编码, 但是觉得它的写法好奇怪, 可读性和之前比起来差多了, 所以决定跟踪一下:
reference string 易得到编码是"Benadryl", 输入这个正确编码跟踪过程:(虽然列表里面有GetWindowTextA, 但是断不下来, 估计Delphi有自己的函数).
我的问题是: 分析Delphi 程序的话, 它的calld 都是call xx.1234567的, 而不是 call xx.funname的, 即无法显示调用函数的名字或者调用的根本就是自己封装的函数, 这个时候怎么知道这个函数的功能并知道该不该跟进呢?
以下是两个CM的代码分析:
mexcrk1 是安同学教程第16章课后作业
004038D0 /$ 53 PUSH EBX
004038D1 |. 56 PUSH ESI
004038D2 |. 57 PUSH EDI ;保护寄存器
004038D3 |. 89C6 MOV ESI,EAX ; 装入我们输入的序列号地址
004038D5 |. 89D7 MOV EDI,EDX ; 装入正确序列号地址
004038D7 |. 39D0 CMP EAX,EDX ; 为什么要比对这两个不可能相等的地址?
004038D9 |. 0F84 8F000000 JE CRACK1.0040396E
004038DF |. 85F6 TEST ESI,ESI
004038E1 |. 74 68 JE SHORT CRACK1.0040394B
004038E3 |. 85FF TEST EDI,EDI ; 两个TEST测试字符串是否空, 为何连自己设置的编码也要检查?
004038E5 |. 74 6B JE SHORT CRACK1.00403952
004038E7 |. 8B46 FC MOV EAX,DWORD PTR DS:[ESI-4] ; 序列号地址-4的位置是序列长度
004038EA |. 8B57 FC MOV EDX,DWORD PTR DS:[EDI-4] ; 同上
004038ED |. 29D0 SUB EAX,EDX
004038EF |. 77 02 JA SHORT CRACK1.004038F3
004038F1 |. 01C2 ADD EDX,EAX
004038F3 |> 52 PUSH EDX ; 相当于min函数, 最终EDX会是比较小的那个
004038F4 |. C1EA 02 SHR EDX,2 ; EDX/4, 之后要以4个字符为单位比较
004038F7 |. 74 26 JE SHORT CRACK1.0040391F ; 不足四则跳不知道跳哪里
004038F9 |> 8B0E /MOV ECX,DWORD PTR DS:[ESI]
004038FB |. 8B1F |MOV EBX,DWORD PTR DS:[EDI] ; 装入两个编码的前四字节
004038FD |. 39D9 |CMP ECX,EBX ;比较
004038FF |. 75 58 |JNZ SHORT CRACK1.00403959
00403901 |. 4A |DEC EDX ; 减去1, 其实是减去四个字节
00403902 |. 74 15 |JE SHORT CRACK1.00403919 ; 空了跳
00403904 |. 8B4E 04 |MOV ECX,DWORD PTR DS:[ESI+4]
00403907 |. 8B5F 04 |MOV EBX,DWORD PTR DS:[EDI+4]
0040390A |. 39D9 |CMP ECX,EBX ;这三句再比较四个字节
0040390C |. 75 4B |JNZ SHORT CRACK1.00403959
0040390E |. 83C6 08 |ADD ESI,8
00403911 |. 83C7 08 |ADD EDI,8 ;移动指针
00403914 |. 4A |DEC EDX
00403915 |.^ 75 E2 \JNZ SHORT CRACK1.004038F9
; 这一段是比较硬编码的, 每次比较8个字节
剩下这些东西各种跳转完全不知所以然…
00403917 |. EB 06 JMP SHORT CRACK1.0040391F
00403919 |> 83C6 04 ADD ESI,4
0040391C |. 83C7 04 ADD EDI,4
0040391F |> 5A POP EDX
00403920 |. 83E2 03 AND EDX,3
00403923 |. 74 22 JE SHORT CRACK1.00403947
00403925 |. 8B0E MOV ECX,DWORD PTR DS:[ESI]
00403927 |. 8B1F MOV EBX,DWORD PTR DS:[EDI]
00403929 |. 38D9 CMP CL,BL
0040392B |. 75 41 JNZ SHORT CRACK1.0040396E
0040392D |. 4A DEC EDX
0040392E |. 74 17 JE SHORT CRACK1.00403947
00403930 |. 38FD CMP CH,BH
00403932 |. 75 3A JNZ SHORT CRACK1.0040396E
00403934 |. 4A DEC EDX
00403935 |. 74 10 JE SHORT CRACK1.00403947
00403937 |. 81E3 0000FF00 AND EBX,0FF0000
0040393D |. 81E1 0000FF00 AND ECX,0FF0000
00403943 |. 39D9 CMP ECX,EBX
00403945 |. 75 27 JNZ SHORT CRACK1.0040396E
00403947 |> 01C0 ADD EAX,EAX
00403949 |. EB 23 JMP SHORT CRACK1.0040396E
0040394B |> 8B57 FC MOV EDX,DWORD PTR DS:[EDI-4]
0040394E |. 29D0 SUB EAX,EDX
00403950 |. EB 1C JMP SHORT CRACK1.0040396E
00403952 |> 8B46 FC MOV EAX,DWORD PTR DS:[ESI-4]
00403955 |. 29D0 SUB EAX,EDX
00403957 |. EB 15 JMP SHORT CRACK1.0040396E
00403959 |> 5A POP EDX
0040395A |. 38D9 CMP CL,BL
0040395C |. 75 10 JNZ SHORT CRACK1.0040396E
0040395E |. 38FD CMP CH,BH
00403960 |. 75 0C JNZ SHORT CRACK1.0040396E
00403962 |. C1E9 10 SHR ECX,10
00403965 |. C1EB 10 SHR EBX,10
00403968 |. 38D9 CMP CL,BL
0040396A |. 75 02 JNZ SHORT CRACK1.0040396E
0040396C |. 38FD CMP CH,BH
0040396E |> 5F POP EDI ;直接跳到这里就Thank you mate it 了.
0040396F |. 5E POP ESI
00403970 |. 5B POP EBX
00403971 \. C3 RETN
从这个函数奇葩的比对方式, 我觉得我跟踪的…应该是一个封装的函数…比较操作看起来挺通用的, 大概是CompareText…真是痛苦…
Cruehead 是16章的例题, 教程只分析了一部分(坛子里有前辈也发过了, 不要脸地再发一次)
(参考了安于此生的做法)
照例是name+serial, 发现函数列表中有GetDlgItemTextA, 下断, 得到name位于0x40218E, serial 位于0x40217E, 对name的地址下内存断点, 运行, 来到:
00401383 |> /8A06 /MOV AL,BYTE PTR DS:[ESI]; name的地址
00401385 |. |84C0 |TEST AL,AL
00401387 |. |74 13 |JE SHORT CRACKME.0040139C ; 处理结束跳走到循环出口
00401389 |. |3C 41 |CMP AL,41 ; 小于'A'(一般是数字)则跳往失败提示
0040138B |72 1F JB SHORT CRACKME.004013AC
0040138D |. |3C 5A |CMP AL,5A ;大于'Z' 跳转到转换大写的函数, 否则继续循环
0040138F |. |73 03 |JNB SHORT CRACKME.00401394
00401391 |. |46 |INC ESI
00401392 |.^ EB EF |JMP SHORT CRACKME.00401383
00401394 |> |E8 39000000 |CALL CRACKME.004013D2 ; 转换大写的函数, 略过
00401399 |. |46 |INC ESI
0040139A |.^\EB E7 \JMP SHORT CRACKME.00401383
0040139C |> 5E POP ESI ; 循环出口
0040139D |. E8 20000000 CALL CRACKME.004013C2
接下来会遇到一个retn, 来到另一个部分
004013C2 /$ 33FF XOR EDI,EDI
004013C4 |. 33DB XOR EBX,EBX
004013C6 |> 8A1E /MOV BL,BYTE PTR DS:[ESI]
004013C8 |. 84DB |TEST BL,BL
004013CA |. 74 05 |JE SHORT CRACKME.004013D1
004013CC |. 03FB |ADD EDI,EBX
004013CE |. 46 |INC ESI
004013CF |.^ EB F5 \JMP SHORT CRACKME.004013C6
004013D1 \> C3 RETN
以上这段将name的每一个元素累加起来到EDI, 即是∑24_i^n▒name[i] , retrn 到
004013A2 |. 81F7 78560000 XOR EDI,5678; 将累加和 与 5678 异或
004013A8 |. 8BC7 MOV EAX,EDI
004013AA |. EB 15 JMP SHORT CRACKME.004013C1 ;跳到处理serial 的地方
接下来有几个跳转, 小心跳过后来到:
004013D8 /$ 33C0 XOR EAX,EAX
004013DA |. 33FF XOR EDI,EDI
004013DC |. 33DB XOR EBX,EBX
004013DE |. 8B7424 04 MOV ESI,DWORD PTR SS:[ESP+4]
004013E2 |> B0 0A /MOV AL,0A
004013E4 |. 8A1E |MOV BL,BYTE PTR DS:[ESI] ; ESI是Serial的地址
004013E6 |. 84DB |TEST BL,BL
004013E8 |. 74 0B |JE SHORT CRACKME.004013F5
004013EA |. 80EB 30 |SUB BL,30
004013ED |. 0FAFF8 |IMUL EDI,EAX
004013F0 |. 03FB |ADD EDI,EBX
004013F2 |. 46 |INC ESI
004013F3 |.^ EB ED \JMP SHORT CRACKME.004013E2
004013F5 |> 81F7 34120000 XOR EDI,1234
004013FB |. 8BDF MOV EBX,EDI
004013FD \. C3 RETN
这一段将Serial的每一位 - 30, 加在 EDI上, 之后让 EDI *= 0x0A,
也就是将Serial - 30连乘, ∏24_i^n▒〖(serial[i]−30)〗
得到的值再异或1234, 再 retn,到了最后的代码:
EAX 是 name 运算得到的, EBX是 Serial运算得到的, 相等则提示成功, 分析完毕..
0040123D . 83C4 04 ADD ESP,4
00401240 . 58 POP EAX
00401241 . 3BC3 CMP EAX,EBX
00401243 . 74 07 JE SHORT CRACKME.0040124C
00401245 . E8 18010000 CALL CRACKME.00401362
接下来把算法逆向, 由name推出Serial…竟然..想了很久..
代码:
#include <stdio.h>
#include <string.h>
int main()
{
char name[128] = {0}, serial[128] = {0};
int i, j;
printf("name:");
scanf("%s",name);
getchar();
j = 0;
for (i = 0; i < strlen(name); i++)
{
if (name[i] < 'A') return -1;
if (name[i] >= 'Z') name[i] -= 0x20;
j += name[i];
}
j = (j ^ 0x5678) ^ 0x1234;
printf("serial: %d\n",j);
getchar();
return 0;
}
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课