Windows逆向学习之CrackMe
字数 1362 2025-08-24 16:48:07
Windows逆向工程之CrackMe分析教程
免责声明
本教程仅用于学习和研究目的,严禁用于商业用途和非法用途。所有分析内容均基于技术研究角度,不鼓励任何形式的软件破解行为。
第一部分:KeygenMe分析
1.1 基本信息
- 程序类型:32位GUI程序
- 编程语言:TASM/MASM
- 加壳情况:无壳
1.2 程序功能
- 需要输入Name和Serial
- 未输入时提示"Give me more material hehe!!"
- 输入错误时提示错误信息
1.3 逆向分析
1.3.1 关键API
GetDlgItemTextA:获取文本框输入内容lstrlen:计算字符串长度MessageBoxA:显示提示信息
1.3.2 注册算法流程
- 检查Name和Serial是否已输入
- 计算Name的算法结果:
- 对Name的每个字符进行以下计算:
EBX = 字符的ASCII值EBX = EBX * EBX(字符ASCII值的平方)ESI += EBXEBX = 字符的ASCII值EBX = EBX >> 1(字符ASCII值除以2)EBX += 3EBX = EBX * 字符的ASCII值EBX -= 字符的ASCII值ESI += EBXESI += ESI(ESI值乘以2)
- 对Name的每个字符进行以下计算:
- 将计算结果与输入的Serial前4字节比较
1.3.3 注册机实现
#include <iostream>
#include <sstream>
#include <iomanip>
#include <string>
#include <algorithm>
#include <cmath>
#include <Windows.h>
std::string generateSequentialName(int length, unsigned long long index);
DWORD name_To_Serialnum(std::string& name);
std::string serialnum_To_Serial(DWORD num);
int main() {
// 生成Name和对应Serial的代码
// ...
}
DWORD name_To_Serialnum(std::string& name) {
DWORD num = 0;
for (auto c : name) {
num += c * c;
int temp = c;
temp /= 2;
temp += 3;
temp *= c;
temp -= c;
num += temp;
num += num;
}
return num;
}
std::string serialnum_To_Serial(DWORD num) {
// 将计算结果转换为可显示的Serial
// ...
}
1.3.4 破解方法
修改0040133C处的跳转指令JNZ SHORT KeygenMe.00401353为NOP指令,绕过验证。
第二部分:crackme_0006分析
2.1 基本信息
- 程序类型:32位GUI程序
- 编程语言:TASM/MASM
- 加壳情况:无壳
2.2 关键API
GetDlgItemTextA:获取文本框输入GetVolumeInformationA:获取卷序列号信息lstrcmpA:字符串比较
2.3 注册算法分析
2.3.1 卷序列号计算
- 获取C盘和D盘的卷序列号
- 计算步骤:
- 将两个序列号相加
- 结果左移5位(相当于乘以32)
- 循环左移0x0D位
- 计算两个序列号的平方和的平方根(勾股定理)
2.3.2 Name算法
Name-Step1算法:
00401036 /$ 55 PUSH EBP
00401037 |. 8BEC MOV EBP,ESP
00401039 |. 8B75 08 MOV ESI,DWORD PTR SS:[EBP+8] ; Name地址
0040103C |. FC CLD ; 正向处理字符串
0040103D |. 33D2 XOR EDX,EDX
0040103F |. B8 01000000 MOV EAX,1 ; EAX初始为1
00401044 |> 0FB60E /MOVZX ECX,BYTE PTR DS:[ESI] ; 取字符
00401047 |. 46 |INC ESI ; 指针后移
00401048 |. 0BC9 |OR ECX,ECX ; 检查是否结束
0040104A |. 74 06 |JE SHORT CrackMe_.00401052
0040104C |. F7E1 |MUL ECX ; EAX *= ECX
0040104E |. 03C2 |ADD EAX,EDX ; 加上高位
00401050 |.^ EB F2 \JMP SHORT CrackMe_.00401044
Name-Step2算法:
- 将Step1结果循环左移1位
- 与卷序列号计算结果进行OR运算
- 与0x0FFFFFFF进行AND运算
Name-Step3算法:
- 使用字符串"071362de9f8ab45c"作为转换表
- 将Step2结果反复除以16取余数,用余数索引转换表
- 将结果除以4,直到结果为0
2.3.3 校验
比较生成的Serial与输入的Serial,使用lstrcmpA函数。
2.3.4 注册机实现
#include <iostream>
#include <string>
#include <Windows.h>
#include <cstdint>
#include <cmath>
// 获取卷序列号信息
DWORD GetVolumeInformationNumber() {
// 实现获取C盘和D盘序列号并计算的代码
// ...
}
// 循环左移
DWORD RotateLeft(DWORD value, BYTE count) {
const BYTE num_bits = sizeof(value) * 8;
count %= num_bits;
return (value << count) | (value >> (num_bits - count));
}
// 计算平方和的平方根
DWORD CalculateHypotenuse(DWORD a, DWORD b) {
double a_float = static_cast<double>(a);
double b_float = static_cast<double>(b);
double sum_of_squares = (a_float * a_float) + (b_float * b_float);
return static_cast<DWORD>(std::round(std::sqrt(sum_of_squares)));
}
// Name-Step1计算
DWORD NameStep1(const std::string& name) {
unsigned long long num = 1;
unsigned long num_low32 = 0;
unsigned long num_high32 = 0;
for (auto& c : name) {
num *= c;
num_low32 = num & 0xFFFFFFFF;
num_high32 = num >> 32;
num_low32 += num_high32;
}
return num_low32;
}
// Name-Step2计算
DWORD NameStep2(DWORD num1, DWORD volume_information_number) {
DWORD num2 = RotateLeft(num1, 0x1);
num2 |= volume_information_number;
num2 &= 0x0FFFFFFF;
return num2;
}
// Name-Step3计算
std::string NameStep3(DWORD num2) {
std::string check_chars("071362de9f8ab45c");
std::string serial;
int num = num2;
while (true) {
DWORD remainder = num % 0x10;
serial.push_back(check_chars[remainder]);
num /= 0x04;
if (num == 0) {
break;
}
}
return serial;
}
2.3.5 破解方法
修改004012D5处的跳转指令JNZ SHORT CrackMe_.004012EF为NOP指令,绕过验证。
总结
本教程详细分析了两个CrackMe程序的逆向过程和算法实现,包括:
- KeygenMe的简单乘法算法
- crackme_0006的复杂算法,涉及:
- 卷序列号处理
- 乘法累加
- 位操作
- 数学计算
- 查表转换
通过理解这些算法,可以更好地掌握Windows逆向工程技术,提高软件安全分析能力。