1. 打开密钥文件
00401028 | push C0000000 ; dwCreationDisposition = CREATE_NEW
0040102D | push cruehead.3.4020D7 ; lpFileName = "CRACKME3.KEY"
00401032 | call <JMP.&CreateFileA> ; 创建/打开文件
00401037 | cmp eax,FFFFFFFF ; 检查文件句柄是否有效
0040103A | jne 401043 ; 有效则继续,否则跳转到错误处理
2. 读取18字节到缓冲区
00401048 | mov eax,12 ; 要读取的字节数 = 0x12(18)
0040104D | mov ebx,402008 ; 缓冲区地址
00401054 | push 4021A0 ; lpNumberOfBytesRead 接收实际读取字节数
00401059 | push eax ; nNumberOfBytesToRead = 18
0040105A | push ebx ; lpBuffer = 402008
0040105B | push dword ptr ds:[4020F5] ; hFile 文件句柄
00401061 | call <JMP.&ReadFile> ; 读取文件
3. 检查读取字节数是否为18
00401066 | cmp dword ptr ds:[4021A0],12 ; 比较实际读取字节数是否为18
0040106D | jne 401037 ; 如果不是18,跳转到错误处理(显示错误消息)
4. 调用sub_401311处理前14字节
0040106F | push 402008 ; 传入缓冲区地址作为参数
00401074 | call <cruehead.3.sub_401311> ; 调用处理函数
sub_401311函数的详细汇编代码:
00401311 | xor ecx,ecx ; 清空计数器ECX
00401313 | xor eax,eax ; 清空EAX
00401315 | mov esi,dword ptr ss:[esp+4] ; 获取参数(缓冲区地址)
00401319 | mov bl,41 ; BL = 0x41 (起始XOR值)
0040131B | mov al,byte ptr ds:[esi] ; 读取缓冲区的一个字节到AL
0040131D | xor al,bl ; AL = AL XOR BL
0040131F | mov byte ptr ds:[esi],al ; 将结果存回缓冲区
00401321 | inc esi ; 指向下一个字节
00401322 | inc bl ; BL递增(0x41→0x42→...)
00401324 | add dword ptr ds:[4020F9],eax ; 累加结果到[4020F9](变量S)
0040132A | cmp al,0 ; 检查结果是否为0
0040132C | je 401335 ; 如果为0则提前结束
0040132E | inc cl ; 字节计数器递增
00401330 | cmp bl,4F ; 检查BL是否达到0x4F
00401333 | jne 40131B ; 循环处理下一个字节
00401335 | mov dword ptr ds:[402149],ecx ; 保存处理的字节数
0040133B | ret ; 返回
5. 将S与0x12345678异或
00401079 | xor dword ptr ds:[4020F9],12345678 ; S = S XOR 0x12345678
6. 调用sub_40133C读取后4字节作为T
0040108B | call <cruehead.3.sub_40133C> ; 调用函数获取T值
sub_40133C函数的详细汇编代码:
0040133C | mov esi,dword ptr ss:[esp+4] ; 获取参数(缓冲区地址)
00401340 | add esi,0E ; ESI = 缓冲区地址 + 0x0E(14)
00401343 | mov eax,dword ptr ds:[esi] ; 读取4字节到EAX(这就是T值)
00401345 | ret ; 返回EAX=T
7. 比较T和S
00401090 | add esp,4 ; 清理栈
00401093 | cmp eax,dword ptr ds:[4020F9] ; 比较EAX(T)和[4020F9](S)
00401099 | sete al ; 如果相等,AL=1,否则AL=0
0040109C | push eax ; 保存结果
0040109D | test al,al ; 测试AL
0040109F | je 401037 ; 如果AL=0(不相等),跳转到错误处理
完整的验证流程控制流
; 成功路径:
00401093 | cmp eax,[4020F9] ; 比较T和S
00401099 | sete al ; 相等则AL=1
0040109C | push eax
0040109D | test al,al ; 测试结果
0040109F | je 401037 ; 不相等则跳转到错误
004010A1 | push 40210E ; 成功消息地址
004010A6 | call sub_401346 ; 显示成功消息
004010AB | add esp,4
; ... 继续执行程序
; 错误路径:
00401037 | push 40210E ; 错误消息地址
0040103C | call sub_4012F5 ; 显示错误消息
00401041 | jmp 4010AE ; 跳转到程序退出
生成"CRACKME3.KEY"文件
•#include <stdio.h>
•#include <stdlib.h>
•#include <string.h>
•#include <windows.h>
int main()
• {
• char* username="test123";
• char* filename="CRACKME3.KEY";
• FILE* file;
• unsigned char key[18];
• unsigned int checksum = 0;
• int i;
•
•
• // 检查用户名长度并处理`
• int name_len = strlen(username);
• if (name_len > 14)
• {
• printf("用户名超过14字节,将被截断\n");
• name_len = 14;
• }
•
• // 复制用户名到密钥缓冲区
• memset(key, 0, sizeof(key));
• memcpy(key, username, name_len);
•
• // 如果用户名不足14字节,用空格填充
• for (i = name_len; i < 14; i++)
• {
• key[i] = ' ';
• }
•
• // 计算校验和`
• for (i = 0; i < 14; i++)
• {
• unsigned char xor_val = 0x41 + i; // 从0x41递增到0x4E
• unsigned char result = key[i] ^ xor_val;
• checksum += result;
• }
•
• // 最终校验值 = 校验和 XOR 0x12345678
• unsigned int final_checksum = checksum ^ 0x12345678;
•
• // 将校验值写入密钥的最后4字节(小端序)
• key[14] = (final_checksum >> 0) & 0xFF; // 最低字节
• key[15] = (final_checksum >> 8) & 0xFF;
• key[16] = (final_checksum >> 16) & 0xFF;
• key[17] = (final_checksum >> 24) & 0xFF; // 最高字节
•
• // 创建密钥文件
• file = fopen(filename, "wb");
• if (file == NULL)
• {
• printf("错误: 无法创建文件 %s\n", filename);
• return FALSE;
• }
•
• // 写入18字节密钥
• fwrite(key, 1, 18, file);
• fclose(file);
•
• printf("文件创建完成");
• return 0;
•
• }
把"CRACKME3.KEY"文件复制到程序目录下。
![]() |
|---|
