[逆向工程]160个CrackMe入门实战之ajj1.2解析(五)
一.CKme002.exe功能
当"注册"按钮变为"注册了"则表明注册成功

以下是逆向步骤:
执行固定操作序列:程序会持续检查你是否遵循了特定的交互逻辑。你需要按顺序完成以下动作:
-
将
ok.txt文件放入 X:\ajj.126.c0m\j\o\j\o\ok.txt路径下,解锁Edit2` 输入框。默认是X盘符,本次为方便修改内存为D盘。ok.txt的文本内容为shell#写入文本 20 61 6A 6A D0 B4 B5 C4 43 4B 6D 65 D5 E6 C0 C3 21 FF FF 会在桌面生成一个ok.txt文件,将该文件拷贝到D:\ajj.126.c0m\j\o\j\o\ok.txt 该路径下 $bytes = 0x20,0x61,0x6A,0x6A,0xD0,0xB4,0xB5,0xC4,0x43,0x4B,0x6D,0x65,0xD5,0xE6,0xC0,0xC3,0x21,0xFF,0xFF; [System.IO.File]::WriteAllBytes("$env:USERPROFILE\Desktop\ok.txt", $bytes) -
右键点击 程序界面的"注册"按钮 5次 ,当图片框中空白时左键双击,解除
Edit2输入框的禁用状态。 -
在用户名处输入
ajj。 -
在
Edit2框中输入1_345,78,然后左键双击该输入框的任意位置。 -
当图片显示为"性相近"时,将鼠标从程序窗口的右下角移入窗口内。
-
当图片显示为"性本善"时,将鼠标从程序窗口的左下角移入窗口内。
-
完成上述所有步骤后,
label3会显示一个介于 0 到 3 之间的数字,将它记录下来 -
注册机源码如下,先编译为crackme_solver.exe可执行程序,运行注册机输入数字
c++// crackme_solver.cpp // 编译方式:在 VSCode 中打开终端,执行: // g++ crackme_solver.cpp -o crackme_solver.exe // 或者使用 MSVC: // cl crackme_solver.cpp #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> // 图片结构体 struct Image { char szname[7]; // 图片名称,最多6个汉字+结束符 int nNumber; // 编号(1~4) int nLef; // 左键点击对应的系数 int nRight; // 右键点击对应的系数 }; int main() { // 1. 初始化图片数组(与原始代码完全一致) Image aImage[4] = {0}; sprintf_s(aImage[0].szname, sizeof(aImage[0].szname), "人之初"); aImage[0].nNumber = 1; aImage[0].nLef = 0x2; // 2 aImage[0].nRight = 0x11; // 17 sprintf_s(aImage[1].szname, sizeof(aImage[1].szname), "性本善"); aImage[1].nNumber = 2; aImage[1].nLef = 0x3; // 3 aImage[1].nRight = 0x13; // 19 sprintf_s(aImage[2].szname, sizeof(aImage[2].szname), "性相近"); aImage[2].nNumber = 3; aImage[2].nLef = 0x5; // 5 aImage[2].nRight = 0x17; // 23 sprintf_s(aImage[3].szname, sizeof(aImage[3].szname), "习相远"); aImage[3].nNumber = 4; aImage[3].nLef = 0x7; // 7 aImage[3].nRight = 0x1B; // 27 // 2. 获取用户输入 int input; std::cout << "请输入前期步骤得到的数值 (1, 2, 3 或其他): "; std::cin >> input; // 3. 数值映射(与原始代码 switch 完全一致) int n; switch (input) { case 1: n = 0x3D; break; // 61 case 2: n = 0x34; break; // 52 case 3: n = 0xDF; break; // 223 default: n = 0x41; break; // 65 } std::cout << "映射后的目标数值 n = " << n << std::endl; // 4. 暴力求解最优组合 int nRes = 112; // 最小点击次数初始值 int nIL = 0, nIR = 0; // 左键图片下标、右键图片下标 int nJ = 0, nK = 0; // 右键点击次数 j, 左键点击次数 k // 遍历所有图片组合 for (int iL = 0; iL < 4; ++iL) { // 左键图片(系数 nLef) for (int iR = 0; iR < 4; ++iR) { // 右键图片(系数 nRight) // j 范围 1~14(右键次数,原始代码从1开始) for (int j = 1; j <= 14; ++j) { // k 范围 0~112(左键次数,原始代码从0开始) for (int k = 0; k <= 112; ++k) { int value = aImage[iR].nRight * j + aImage[iL].nLef * k; if (value == n && (j + k) < nRes) { nIL = iL; nIR = iR; nJ = j; nK = k; nRes = j + k; } } } } } // 5. 输出结果 if (nRes == 112) { std::cout << "未找到任何有效组合,请检查输入或算法边界!" << std::endl; } else { char szKey[512]; sprintf_s(szKey, sizeof(szKey), "在"%s"图片时左键点击图片%d次\r\n\r\n在"%s"图片时右键点击图片%d次\r\n\r\n即可注册成功!", aImage[nIL].szname, nK, // 注意:左键次数是 k aImage[nIR].szname, nJ); // 右键次数是 j std::cout << szKey << std::endl; } // 暂停,方便查看结果(可选) system("pause"); return 0; } -
当图是"习相远"时,左键点击一次,右键点击两次,注册成功
需要注意的点如下:
| 检查项 | 变量/地址 | 核心条件 | 逆向得出的操作要求 |
|---|---|---|---|
| 禁用点击注册 | [ebx+31C] |
其值不能为 0x3E7 |
绝对不能点击"注册"按钮 |
| 解锁第一个控件 | [ebx+304] |
其值不能为 0xC34 |
程序启动时,会检查固定路径下是否存在特定文件 (ok.txt) |
| 解锁第二个控件 | [ebx+308] |
其值不能为 0x230D |
需要右键点击 "注册"按钮5次 ,然后双击空白区域 |
| 鼠标移动与点击 | [ebx+310] |
其值必须为 0xF94 |
这是一个多步的、依赖上下文(如图片ID和鼠标坐标)的验证 |
| 用户名验证 | [ebx+314] |
- | 用户名必须为 ajj |
| 另一个数据输入 | [ebx+318] |
- | 这对应于 Edit2 输入框的内容必须为 1_345,78 |
注册成功截图:

二.开始破解
2.1 Exeinfo查壳
32位程序,upx壳,未有明显语言标识

2.1.1 脱壳
既然有壳并且是upx壳,那么接下来可以用UPX 脱壳 工具进行脱壳操作

脱壳后重新命名程序为CKme002new.exe
再次查壳发现是Delphi语言编写,壳已经去掉了
注意:脱壳这一步可以不操作,因为现在一些的动态调试工具可以无视UPX壳,根据实际情况操作

2.2 x32dbg动态分析
2.2.1 查找核心逻辑
动态分析前先观察运行程序,运行程序有"注册"按钮,那么可以用E2A2工具查看"注册"按钮地址下断点

dbg在004474C0地址处下断点 输入注册名,点击"注册"按钮,运行至004474C0断点处
shift+f8 单步运行后发现代码处有ret ,相当于直接返回了,说明按正常思路找"注册",的思路是错误的,第一处就是不能点击注册 ,那么在xdbg中继续查找发现有"注册了"字符串。依次在dbg反汇编窗口往上查看代码,发现有疑似段首代码
根据一般段首规则可猜测出入口地址代码
push ebx
mov ebx,eax
发现该代码地址为004473E4
004473E4在E2A2中是Timer2 事件入口地址

shell
004473E4 | 53 | push ebx | ebx:&"<珺"
004473E5 | 8BD8 | mov ebx,eax | ebx:&"<珺", eax:&"<珺"
004473E7 | 81BB 04030000 340C0000 | cmp dword ptr ds:[ebx+304],C34 |304=C34相等FormCreate读文件相关
004473F1 | 0F84 88000000 | je ckme002.44747F |
004473F7 | 81BB 08030000 0D230000 | cmp dword ptr ds:[ebx+308],230D |308不能等于230D Button1MouseDown相关
00447401 | 74 7C | je ckme002.44747F |
00447403 | 81BB 10030000 940F0000 | cmp dword ptr ds:[ebx+310],F94 |310 FormMouseMove Edit2双击图片框开启相关
0044740D | 75 70 | jne ckme002.44747F |
0044740F | 8B83 18030000 | mov eax,dword ptr ds:[ebx+318] | 318鼠标左键右键点不同图片累加不同的值,初始值为0
00447415 | 3B83 14030000 | cmp eax,dword ptr ds:[ebx+314] | 314 页面显示数字 1 2 3相关
0044741B | 75 62 | jne ckme002.44747F |
0044741D | 81BB 1C030000 E7030000 | cmp dword ptr ds:[ebx+31C],3E7 |
00447427 | 74 56 | je ckme002.44747F |
00447429 | 33D2 | xor edx,edx | edx:&"<珺"
0044742B | 8B83 D8020000 | mov eax,dword ptr ds:[ebx+2D8] | eax:&"<珺"
00447431 | 8B08 | mov ecx,dword ptr ds:[eax] | [eax]:"<珺"
00447433 | FF51 5C | call dword ptr ds:[ecx+5C] |
00447436 | 33D2 | xor edx,edx | edx:&"<珺"
00447438 | 8B83 DC020000 | mov eax,dword ptr ds:[ebx+2DC] | eax:&"<珺"
0044743E | 8B08 | mov ecx,dword ptr ds:[eax] | [eax]:"<珺"
00447440 | FF51 5C | call dword ptr ds:[ecx+5C] |
00447443 | 33D2 | xor edx,edx | edx:&"<珺"
00447445 | 8B83 E0020000 | mov eax,dword ptr ds:[ebx+2E0] | eax:&"<珺"
0044744B | 8B08 | mov ecx,dword ptr ds:[eax] | [eax]:"<珺"
0044744D | FF51 5C | call dword ptr ds:[ecx+5C] |
00447450 | 33D2 | xor edx,edx | edx:&"<珺"
00447452 | 8B83 E4020000 | mov eax,dword ptr ds:[ebx+2E4] | eax:&"<珺"
00447458 | 8B08 | mov ecx,dword ptr ds:[eax] | [eax]:"<珺"
0044745A | FF51 5C | call dword ptr ds:[ecx+5C] |
0044745D | A1 A8984400 | mov eax,dword ptr ds:[4498A8] | eax:&"<珺"
00447462 | 83C0 70 | add eax,70 | eax:&"<珺"
00447465 | BA 8C744400 | mov edx,ckme002.44748C | edx:&"<珺", 44748C:"厉害厉害真厉害!佩服佩服真佩服!!"
0044746A | E8 EDC4FBFF | call ckme002.40395C |
0044746F | BA B8744400 | mov edx,ckme002.4474B8 | edx:&"<珺", 4474B8:"注册了"
从Timer2 事件中可以看到有不同的五个判断,这5个判断和内存中的值有关 分别是[ebx+304]、[ebx+308]、[ebx+310]、[ebx+318]、[ebx+314]、[ebx+31C] 这几个内存地址中的值和不同的常量进行判断,有一个判断不通过则注册不了
2.2.2 判断层分析
接下来逐层分析这五个判断所用到值,顺序依次为
ebx+31C
ebx+304
ebx+308
ebx+310
ebx+314
ebx+318
其中31C 304 308 310 314 318 这些为常量,在dbg中可以使用常量跟踪法,查看那些地方都对这些值做了操作
dbg中鼠标指定这行代码0044741D | 81BB 1C030000 E7030000 | cmp dword ptr ds:[ebx+31C],3E7
2.2.2.1.右键查找引用 常数31C搜寻

查询到两处

查看代码
shell
004474C0 | C780 1C030000 E7030000 | mov dword ptr ds:[eax+31C],3E7 |
004474CA | C3 | ret |
004474C0 是Button1Click事件,也就是"注册"事件,点击"注册"按钮后会直接退出,所以"注册"按钮是第一层障碍,不需要去点击
2.2.2.2 右键查找引用 常数304搜寻
shell
地址=00446D8F
反汇编=mov dword ptr ds:[ebx+304],C34
地址=00446DB8
反汇编=mov dword ptr ds:[ebx+304],C34
地址=004473E7
反汇编=cmp dword ptr ds:[ebx+304],C34
目的是cmp dword ptr ds:[ebx+304],C34这个比较不能相等,如果相等则je ckme002.44747F 注册会失败
如果mov dword ptr ds:[ebx+304],C34和mov dword ptr ds:[ebx+304],C34这两处赋值的地方跳过,则会绕过je ckme002.44747F 判断,接着走下面的判断了
排查mov dword ptr ds:[ebx+304],C34这个赋值产生的条件,继续浏览代码发现,发现段首
push ebp
mov ebp,esp 地址00446C1C为 FormCreate 事件函数入口地址
shell
00446C1C | 55 | push ebp |
00446C1D | 8BEC | mov ebp,esp |
00446C1F | 81C4 30FEFFFF | add esp,FFFFFE30 |
00446C25 | 53 | push ebx | ebx:&"<珺"
00446C26 | 33C9 | xor ecx,ecx |
00446C28 | 894D FC | mov dword ptr ss:[ebp-4],ecx |
00446C2B | 8BD8 | mov ebx,eax | ebx:&"<珺", eax:&"<珺"
00446C2D | 33C0 | xor eax,eax | eax:&"<珺"
00446C2F | 55 | push ebp |
00446C30 | 68 D86D4400 | push ckme002.446DD8 |
00446C35 | 64:FF30 | push dword ptr fs:[eax] |
00446C38 | 64:8920 | mov dword ptr fs:[eax],esp |
00446C3B | 33D2 | xor edx,edx | edx:&"<珺"
00446C3D | 8B83 D8020000 | mov eax,dword ptr ds:[ebx+2D8] | eax:&"<珺"
00446C43 | E8 5CD3FDFF | call ckme002.423FA4 |
00446C48 | 33D2 | xor edx,edx | edx:&"<珺"
00446C4A | 8B83 DC020000 | mov eax,dword ptr ds:[ebx+2DC] | eax:&"<珺"
00446C50 | E8 4FD3FDFF | call ckme002.423FA4 |
00446C55 | 33D2 | xor edx,edx | edx:&"<珺"
00446C57 | 8B83 E0020000 | mov eax,dword ptr ds:[ebx+2E0] | eax:&"<珺"
00446C5D | E8 42D3FDFF | call ckme002.423FA4 |
00446C62 | 33D2 | xor edx,edx | edx:&"<珺"
00446C64 | 8B83 E4020000 | mov eax,dword ptr ds:[ebx+2E4] | eax:&"<珺"
00446C6A | E8 35D3FDFF | call ckme002.423FA4 |
00446C6F | 33D2 | xor edx,edx | edx:&"<珺"
00446C71 | 8B83 D8020000 | mov eax,dword ptr ds:[ebx+2D8] | eax:&"<珺"
00446C77 | E8 28D3FDFF | call ckme002.423FA4 |
00446C7C | 33D2 | xor edx,edx | edx:&"<珺"
00446C7E | 8B83 FC020000 | mov eax,dword ptr ds:[ebx+2FC] | eax:&"<珺"
00446C84 | E8 1BD3FDFF | call ckme002.423FA4 |
00446C89 | 33D2 | xor edx,edx | edx:&"<珺"
00446C8B | 8B83 F0020000 | mov eax,dword ptr ds:[ebx+2F0] | eax:&"<珺"
00446C91 | 8B08 | mov ecx,dword ptr ds:[eax] | [eax]:"<珺"
00446C93 | FF51 5C | call dword ptr ds:[ecx+5C] |
00446C96 | 33D2 | xor edx,edx | edx:&"<珺"
00446C98 | 8B83 F0020000 | mov eax,dword ptr ds:[ebx+2F0] | eax:&"<珺"
00446C9E | E8 01D3FDFF | call ckme002.423FA4 |
00446CA3 | BA 01000000 | mov edx,1 | edx:&"<珺"
00446CA8 | 8B83 D8020000 | mov eax,dword ptr ds:[ebx+2D8] | eax:&"<珺"
00446CAE | E8 19CCFDFF | call ckme002.4238CC |
00446CB3 | BA C0FEFFFF | mov edx,FFFFFEC0 | edx:&"<珺"
00446CB8 | 8B83 D8020000 | mov eax,dword ptr ds:[ebx+2D8] | eax:&"<珺"
00446CBE | E8 E9CBFDFF | call ckme002.4238AC |
00446CC3 | BA 01000000 | mov edx,1 | edx:&"<珺"
00446CC8 | 8B83 DC020000 | mov eax,dword ptr ds:[ebx+2DC] | eax:&"<珺"
00446CCE | E8 F9CBFDFF | call ckme002.4238CC |
00446CD3 | BA 7E000000 | mov edx,7E | edx:&"<珺", 7E:'~'
00446CD8 | 8B83 DC020000 | mov eax,dword ptr ds:[ebx+2DC] | eax:&"<珺"
00446CDE | E8 C9CBFDFF | call ckme002.4238AC |
00446CE3 | BA 01000000 | mov edx,1 | edx:&"<珺"
00446CE8 | 8B83 E0020000 | mov eax,dword ptr ds:[ebx+2E0] | eax:&"<珺"
00446CEE | E8 B9CBFDFF | call ckme002.4238AC |
00446CF3 | BA 7A000000 | mov edx,7A | edx:&"<珺", 7A:'z'
00446CF8 | 8B83 E0020000 | mov eax,dword ptr ds:[ebx+2E0] | eax:&"<珺"
00446CFE | E8 C9CBFDFF | call ckme002.4238CC |
00446D03 | BA 01000000 | mov edx,1 | edx:&"<珺"
00446D08 | 8B83 E4020000 | mov eax,dword ptr ds:[ebx+2E4] | eax:&"<珺"
00446D0E | E8 99CBFDFF | call ckme002.4238AC |
00446D13 | BA C0FEFFFF | mov edx,FFFFFEC0 | edx:&"<珺"
00446D18 | 8B83 E4020000 | mov eax,dword ptr ds:[ebx+2E4] | eax:&"<珺"
00446D1E | E8 A9CBFDFF | call ckme002.4238CC |
00446D23 | C783 08030000 8E020000 | mov dword ptr ds:[ebx+308],28E |
00446D2D | C783 0C030000 09000000 | mov dword ptr ds:[ebx+30C],9 | 09:'\t'
00446D37 | C783 14030000 0B000000 | mov dword ptr ds:[ebx+314],B | 0B:'\v'
00446D41 | 33C0 | xor eax,eax | eax:&"<珺"
00446D43 | 8983 18030000 | mov dword ptr ds:[ebx+318],eax | eax:&"<珺"
00446D49 | BA EC6D4400 | mov edx,ckme002.446DEC | 原本作者要求的路径 "X:\\ajj.126.c0m\\j\\o\\j\\o\\ok.txt"
00446D4E | 8D85 30FEFFFF | lea eax,dword ptr ss:[ebp-1D0] | [ebp-1D0]:"ヲA"
00446D54 | E8 EDE7FBFF | call ckme002.405546 |
00446D59 | 8D85 30FEFFFF | lea eax,dword ptr ss:[ebp-1D0] | [ebp-1D0]:"ヲA"
00446D5F | E8 07EAFBFF | call ckme002.40576B |
00446D64 | E8 8BBAFBFF | call ckme002.4027F4 |
00446D69 | 85C0 | test eax,eax | 判断文件路径内容等是否正确
00446D6B | 75 4B | jne ckme002.446DB8 |
00446D6D | 8D55 FC | lea edx,dword ptr ss:[ebp-4] |
00446D70 | 8D85 30FEFFFF | lea eax,dword ptr ss:[ebp-1D0] | [ebp-1D0]:"ヲA"
00446D76 | E8 5DD1FBFF | call ckme002.403ED8 |
00446D7B | E8 44BAFBFF | call ckme002.4027C4 |
00446D80 | 8B45 FC | mov eax,dword ptr ss:[ebp-4] |从实际路径得到的字符串
00446D83 | BA 146E4400 | mov edx,ckme002.446E14 | 实际的字符串
00446D88 | E8 0BCFFBFF | call ckme002.403C98 |比较字符串
00446D8D | 74 0A | je ckme002.446D99 | 是必须要相等则跳过赋值,路径能找到,必须查找到字符串实际内容
00446D8F | C783 04030000 340C0000 | mov dword ptr ds:[ebx+304],C34 |要越过这一步赋值
00446D99 | 8D85 30FEFFFF | lea eax,dword ptr ss:[ebp-1D0] | [ebp-1D0]:"ヲA"
shell
00446D54 | E8 EDE7FBFF | call ckme003.405546 |
这行代码是跳转到405546地址,具体功能为
0040557A | 85D2 | test edx,edx | 检查传入的字符串指针(即文件路径 "D:\\ajj.126.c0m\\j\\o\\j\\o\\ok.txt")是否为 NULL
0040557C | 74 1B | je ckme003.405599 |
作者原本文件路径是X盘符,所以为了方便并且符合不为null将盘符修改为D盘
446DEC:"X:\ajj.126.c0m\j\o\j\o\ok.txt" 修改盘符
X 58 --->D 44 保证字符串长度一致 鼠标双击修改 修改为D盘 打补丁保存为CKme003.exe


DarkDe打开FormCreate 事件代码静态分析,发现字符串为' ajj写的CKme真烂! ' ,需注意前后都有不可见字符,那么需要跟代码在内存中查找前后空格十六进制值

跟踪代码找到 ajj写的CKme真烂! 对应的十六进制: 20 61 6A 6A D0 B4 B5 C4 43 4B 6D 65 D5 E6 C0 C3 21 FF FF
20 代表空格字符
FF FF 代表 ÿ ÿ (带分音符的小写 y)
需要用命令行将生成ok.txt文件以及将以上内容写入该文件,windows下Po'werShell执行以下命令,生成后将桌面生成的ok.txt复制到固定路径下:
shell
#写入文本 20 61 6A 6A D0 B4 B5 C4 43 4B 6D 65 D5 E6 C0 C3 21 FF FF 会在桌面生成一个ok.txt文件,将该文件拷贝到D:\ajj.126.c0m\j\o\j\o\ok.txt 该路径下
$bytes = 0x20,0x61,0x6A,0x6A,0xD0,0xB4,0xB5,0xC4,0x43,0x4B,0x6D,0x65,0xD5,0xE6,0xC0,0xC3,0x21,0xFF,0xFF; [System.IO.File]::WriteAllBytes("$env:USERPROFILE\Desktop\ok.txt", $bytes)

2.2.2.3 右键查找引用 常数308搜寻
shell
地址=00446D23
反汇编=mov dword ptr ds:[ebx+308],28E FormCreate 事件赋初值
地址=00446FA7
反汇编=mov edx,dword ptr ds:[eax+308]
地址=00446FBA
反汇编=add dword ptr ds:[eax+308],3
地址=00446FCB
反汇编=mov dword ptr ds:[eax+308],230D
地址=00446FDC
反汇编=cmp dword ptr ds:[eax+308],29D
地址=004473F7
反汇编=cmp dword ptr ds:[ebx+308],230D
从Timer2事件看,308不能等于230D,如果相等直接跳转到44747F 程序直接ret
shell
004473F7 | 81BB 08030000 0D230000 | cmp dword ptr ds:[ebx+308],230D | 308
00447401 | 74 7C | je ckme003.44747F |
Button1MouseDown 事件入口地址 注册按钮,需注意:
MouseDown 事件,其函数原型大致是 ,由此可判断是鼠标右键还是左键
shell
procedure TForm1.Button1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
其中 Button 参数是一个枚举类型:
mbLeft = 0(左键)mbRight = 1(右键)mbMiddle = 2(中键)
这个 Button 参数正是通过 cl 寄存器传入的。因此:
- 左键点击 →
cl = 0 - 右键点击 →
cl = 1
shell
00446FA4 | 55 | push ebp | Button1MouseDown 事件入口地址 注册按钮
00446FA5 | 8BEC | mov ebp,esp |
00446FA7 | 8B90 08030000 | mov edx,dword ptr ds:[eax+308] |
00446FAD | 81FA 0D230000 | cmp edx,230D |
00446FB3 | 74 20 | je ckme003.446FD5 |
00446FB5 | 80F9 01 | cmp cl,1 | cl 要等于1 代表是鼠标右键点击 ECX低8位
00446FB8 | 75 09 | jne ckme003.446FC3 |
00446FBA | 8380 08030000 03 | add dword ptr ds:[eax+308],3 | 点击一次加3 直至29D 0x29D减去0x28E再除以0x3最后结果等于5 也就是点击5次
00446FC1 | EB 12 | jmp ckme003.446FD5 |
00446FC3 | 81FA 94020000 | cmp edx,294 |
00446FC9 | 7D 0A | jge ckme003.446FD5 |
00446FCB | C780 08030000 0D230000 | mov dword ptr ds:[eax+308],230D |
00446FD5 | 5D | pop ebp |
00446FD6 | C2 0C00 | ret C |
00446FD9 | 8D40 00 | lea eax,dword ptr ds:[eax] | eax:&"<珺", [eax]:"<珺"
00446FDC | 81B8 08030000 9D020000 | cmp dword ptr ds:[eax+308],29D | 图片框左键双击 图片不能点击 双击事件的处理函数名通常包含 DblClick 这个是Panel1DblClick事件入口地址Panel1DblClick事件就可以解禁 Edit2控件 308值等于29D时
00446FE6 | 75 0D | jne ckme003.446FF5 | 308不等于29D就跳转
00446FE8 | B2 01 | mov dl,1 |
00446FEA | 8B80 F0020000 | mov eax,dword ptr ds:[eax+2F0] | 双击图片框启用Edit2 2F0是控件Edit2的ID
00446FF0 | 8B08 | mov ecx,dword ptr ds:[eax] | [eax]:"<珺"
00446FF2 | FF51 5C | call dword ptr ds:[ecx+5C] |启用Edit2
00446FF5 | C3 | ret |
从寄存器中也可判断位右键,右键点击"注册":

2.2.2.4 右键查找引用 常数310搜寻
310值,必须等于0xF94
相关事件函数 FormMouseMove、Edit2DblClick
要令地址 [310] 最终被赋值为 0xF94,必须依次通过三重判断:
- **控件 ID 为
0xE20(图片"性相近")**时,鼠标右下角移动(FormMouseMove的 XY 坐标均足够大)→ 将[310]暂赋值为0x10。 - **控件 ID 为
0x2DC(图片"性本善")**时,鼠标左下角移动(X 小、Y 大)。 [30C]不能等于初始值0x9。[30C]的赋值条件:Edit2字符串长度为 8,且第 2 位为_、第 6 位为,(如1_345,78符合);- 用户名长度须为 3 的整数倍;
- 在
Edit2上左键双击 ,此时[30C]的值由磁盘剩余空间决定。
以上三重条件全部满足后,[310] 最终被赋值为 0xF94。
此外,若用户名为 "ajj",则显示隐藏控件 ID=0x2FC(Label3),其显示内容为 [30C] 的值。
shell
004470EC | 55 | push ebp | FormMouseMove事件入口 鼠标移动
004470ED | 8BEC | mov ebp,esp |
004470EF | 6A 00 | push 0 |
004470F1 | 6A 00 | push 0 |
004470F3 | 53 | push ebx | ebx:&"<珺"
004470F4 | 8BD8 | mov ebx,eax | ebx:&"<珺", eax:&"<珺"
004470F6 | 8B55 08 | mov edx,dword ptr ss:[ebp+8] | y坐标
004470F9 | 8B45 0C | mov eax,dword ptr ss:[ebp+C] | x坐标
004470FC | 33C9 | xor ecx,ecx | ecx:"<FD"
004470FE | 55 | push ebp |
004470FF | 68 17724400 | push <ckme003.sub_447217> |
00447104 | 64:FF31 | push dword ptr fs:[ecx] |
00447107 | 64:8921 | mov dword ptr fs:[ecx],esp |
0044710A | 8B8B E0020000 | mov ecx,dword ptr ds:[ebx+2E0] | image3控件地址
00447110 | 8079 47 01 | cmp byte ptr ds:[ecx+47],1 |
00447114 | 75 19 | jne ckme003.44712F |
00447116 | 3D E2000000 | cmp eax,E2 | eax:&"<珺"
0044711B | 7E 12 | jle ckme003.44712F | X小于E2则跳
0044711D | 81FA 2C010000 | cmp edx,12C | y小于12c则跳
00447123 | 7E 0A | jle ckme003.44712F |
00447125 | C783 10030000 10000000 | mov dword ptr ds:[ebx+310],10 |
0044712F | 8B8B DC020000 | mov ecx,dword ptr ds:[ebx+2DC] | image2控件
00447135 | 8079 47 01 | cmp byte ptr ds:[ecx+47],1 |
00447139 | 75 6C | jne ckme003.4471A7 | 不是image2则跳
0044713B | 83F8 17 | cmp eax,17 | eax:&"<珺"
0044713E | 7D 67 | jge ckme003.4471A7 | x大于17则跳
00447140 | 81FA 2C010000 | cmp edx,12C | edx:"h4D"
00447146 | 7E 5F | jle ckme003.4471A7 | y小于12c则跳
00447148 | 83BB 10030000 10 | cmp dword ptr ds:[ebx+310],10 |
0044714F | 75 56 | jne ckme003.4471A7 | 满足第一步则跳
00447151 | 83BB 0C030000 09 | cmp dword ptr ds:[ebx+30C],9 | 30c值初始值为9则跳
00447158 | 74 4D | je ckme003.4471A7 |
0044715A | C783 10030000 940F0000 | mov dword ptr ds:[ebx+310],F94 | 最终目标
shell
00446FF8 | 55 | push ebp | Edit2双击事件入口地址
00446FF9 | 8BEC | mov ebp,esp |
00446FFB | 33C9 | xor ecx,ecx | ecx:"<FD"
00446FFD | 51 | push ecx | ecx:"<FD"
00446FFE | 51 | push ecx | ecx:"<FD"
00446FFF | 51 | push ecx | ecx:"<FD"
00447000 | 51 | push ecx | ecx:"<FD"
00447001 | 51 | push ecx | ecx:"<FD"
00447002 | 53 | push ebx | ebx:&"<珺"
00447003 | 8BD8 | mov ebx,eax | ebx:&"<珺", eax:&"<珺"
00447005 | 33C0 | xor eax,eax | eax:&"<珺"
00447007 | 55 | push ebp |
00447008 | 68 DF704400 | push <ckme003.sub_4470DF> |
0044700D | 64:FF30 | push dword ptr fs:[eax] |
00447010 | 64:8920 | mov dword ptr fs:[eax],esp |
00447013 | 8D55 FC | lea edx,dword ptr ss:[ebp-4] | [ebp-04]:"h4D"
00447016 | 8B83 F0020000 | mov eax,dword ptr ds:[ebx+2F0] | Edit2控件ID
0044701C | E8 6BD0FDFF | call <ckme003.sub_42408C> |
00447021 | 8B45 FC | mov eax,dword ptr ss:[ebp-4] | [ebp-04]:"h4D"
00447024 | E8 5FCBFBFF | call <ckme003.sub_403B88> |
00447029 | 83F8 08 | cmp eax,8 | 8个字符强制要求
0044702C | 0F85 92000000 | jne ckme003.4470C4 |
00447032 | 8D55 F8 | lea edx,dword ptr ss:[ebp-8] |
00447035 | 8B83 F0020000 | mov eax,dword ptr ds:[ebx+2F0] | eax:&"<珺", [ebx+2F0]:&"<珺"
0044703B | E8 4CD0FDFF | call <ckme003.sub_42408C> |
00447040 | 8B45 F8 | mov eax,dword ptr ss:[ebp-8] |
00447043 | 8078 01 5F | cmp byte ptr ds:[eax+1],5F | eax+01:"iD", 5F:'_' 第二个字符是_
00447047 | 75 7B | jne ckme003.4470C4 |
00447049 | 8D55 F4 | lea edx,dword ptr ss:[ebp-C] |
0044704C | 8B83 F0020000 | mov eax,dword ptr ds:[ebx+2F0] | eax:&"<珺", [ebx+2F0]:&"<珺"
00447052 | E8 35D0FDFF | call <ckme003.sub_42408C> |
00447057 | 8B45 F4 | mov eax,dword ptr ss:[ebp-C] |
0044705A | 8078 05 2C | cmp byte ptr ds:[eax+5],2C | 2C:',' 第六个字符,
0044705E | 75 64 | jne ckme003.4470C4 |
00447060 | 8D55 F0 | lea edx,dword ptr ss:[ebp-10] |
00447063 | 8B83 E8020000 | mov eax,dword ptr ds:[ebx+2E8] | 第一个输入框
00447069 | E8 1ED0FDFF | call <ckme003.sub_42408C> |
0044706E | 8B45 F0 | mov eax,dword ptr ss:[ebp-10] |
00447071 | E8 12CBFBFF | call <ckme003.sub_403B88> | eax = 用户名长度
00447076 | 83C0 03 | add eax,3 | eax = eax +3
00447079 | B9 03000000 | mov ecx,3 | ecx:"<FD"
0044707E | 99 | cdq |
0044707F | F7F9 | idiv ecx | 取模运算eax = eax % 3
00447081 | 85D2 | test edx,edx | 不等于0则跳转
00447083 | 75 3F | jne ckme003.4470C4 |
00447085 | 6A 00 | push 0 |
00447087 | 6A 04 | push 4 |
00447089 | 8D55 EC | lea edx,dword ptr ss:[ebp-14] |
0044708C | 8B83 E8020000 | mov eax,dword ptr ds:[ebx+2E8] | eax:&"<珺", [ebx+2E8]:&"<珺"
00447092 | E8 F5CFFDFF | call <ckme003.sub_42408C> |
00447097 | 8B45 EC | mov eax,dword ptr ss:[ebp-14] |
0044709A | E8 E9CAFBFF | call <ckme003.sub_403B88> |
0044709F | 99 | cdq |
004470A0 | 52 | push edx | edx:"h4D"
004470A1 | 50 | push eax | eax:&"<珺"
004470A2 | 33C0 | xor eax,eax | eax:&"<珺"
004470A4 | E8 E70EFCFF | call <ckme003.sub_407F90> | 磁盘余量的函数值当随机数
004470A9 | 030424 | add eax,dword ptr ss:[esp] | [esp]:"h4D"
004470AC | 135424 04 | adc edx,dword ptr ss:[esp+4] | [esp+04]:sub_444774+12
004470B0 | 83C4 08 | add esp,8 |
004470B3 | 83C0 02 | add eax,2 | eax:&"<珺"
004470B6 | 83D2 00 | adc edx,0 | edx:"h4D"
004470B9 | E8 47ECFBFF | call <ckme003.sub_405D05> | 随机数hash 最终算出30c的值 只有0 1 2 3 四种可能
2.2.2.5 右键查找引用 常数314和318搜寻
314的值等于318的值
image1~4MouseDown 事件函数
[314] 已在上一流程中根据 [30C] 赋值:
30C = 0/1/2/3时,314 = 0x41 / 0x3D / 0x34 / 0xDF。
[318] 通过点击第 1~4 幅图片(区分左/右键)累加不同数值得到。
最终条件 :[318] 必须等于 [314]。
shell
00447179 | C783 14030000 41000000 | mov dword ptr ds:[ebx+314],41 | 鼠标移动事件 依据30c值给314赋值
00447183 | EB 22 | jmp ckme003.4471A7 |
00447185 | C783 14030000 3D000000 | mov dword ptr ds:[ebx+314],3D | 鼠标移动事件 依据30c值给314赋值
0044718F | EB 16 | jmp ckme003.4471A7 |
00447191 | C783 14030000 34000000 | mov dword ptr ds:[ebx+314],34 | 鼠标移动事件 依据30c值给314赋值
0044719B | EB 0A | jmp ckme003.4471A7 |
0044719D | C783 14030000 DF000000 | mov dword ptr ds:[ebx+314],DF | 鼠标移动事件 依据30c值给314赋值
004471A7 | 81BB 10030000 940F0000 | cmp dword ptr ds:[ebx+310],F94 |
004471B1 | 75 46 | jne ckme003.4471F9 |
004471B3 | 8D55 FC | lea edx,dword ptr ss:[ebp-4] | [ebp-04]:"h4D"
004471B6 | 8B83 E8020000 | mov eax,dword ptr ds:[ebx+2E8] | eax:&"<珺", [ebx+2E8]:&"<珺"
004471BC | E8 CBCEFDFF | call <ckme003.sub_42408C> |
004471C1 | 8B45 FC | mov eax,dword ptr ss:[ebp-4] | [ebp-04]:"h4D"
004471C4 | BA 30724400 | mov edx,ckme003.447230 | edx:"h4D", 447230:"ajj" 用户名是ajj 显示隐藏的label3 30c的值显示
004471C9 | E8 CACAFBFF | call <ckme003.sub_403C98> |
004471CE | 75 29 | jne ckme003.4471F9 |
004471D0 | B2 01 | mov dl,1 |
004471D2 | 8B83 FC020000 | mov eax,dword ptr ds:[ebx+2FC] | label3 控件id
004471D8 | E8 C7CDFDFF | call <ckme003.sub_423FA4> |
004471DD | 8D55 F8 | lea edx,dword ptr ss:[ebp-8] |
004471E0 | 8B83 0C030000 | mov eax,dword ptr ds:[ebx+30C] | eax:&"<珺"
004471E6 | E8 AD0AFCFF | call <ckme003.sub_407C98> |
004471EB | 8B55 F8 | mov edx,dword ptr ss:[ebp-8] |
004471EE | 8B83 FC020000 | mov eax,dword ptr ds:[ebx+2FC] | label3 控件id
三.总结
整个验证流程涉及多重条件嵌套:控件图片触发、鼠标坐标范围、字符串格式与长度、磁盘剩余空间、以及多幅图片的左右键点击累加。
如果爆破会简单很多,目的是学习,更多细节还需慢慢补充,期待和大家一起进步!