声明
本文章分享两个在学习Windows逆向过程中遇到的两个难度中上的CrackMe
免责声明:
本文章中发布的一切破解补丁、注册机和注册信息及软件的解密分析文章仅限用于学习和研究目的;不用于其他任何目的,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关.
KeygenMe
信息收集
PEID查看程序基本信息:
-
程序类型:32位 GUI
-
编程语言:TASM/MASM
- 壳信息:无壳
程序基本功能信息:
当Name/Serial其中一个未输入时,则会提示如下信息
当输入错误的Name/Serial
时,则会提示以下信息
此程序算法简单,但是写出注册机比价麻烦。⭐⭐⭐
ReCrackMe
程序导入OllyDbg
Ctrl + N
,查看程序所调用函数,有我们熟悉的APIGetDlgItemTextA
,设置断点BPX GetDlgItemTextA
F9运行,输入ReCrackMe/R123456e
,点击check,程序中断在0x004012BD
我们向下逐步分析,得到该程序的注册流程
004012B1 |. 6A 1A PUSH 1A ; /Count = 1A (26.)
004012B3 |. 68 38304000 PUSH KeygenMe.00403038 ; |Buffer = KeygenMe.00403038
004012B8 |. 6A 6A PUSH 6A ; |ControlID = 6A (106.)
004012BA |. FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hWnd
004012BD |. E8 08010000 CALL <JMP.&USER32.GetDlgItemTextA> ; \GetDlgItemTextA
004012C2 |. 83F8 00 CMP EAX,0 ; 检验是否输入Name
004012C5 |. 74 18 JE SHORT KeygenMe.004012DF
004012C7 |. 6A 1A PUSH 1A ; /Count = 1A (26.)
004012C9 |. 68 38314000 PUSH KeygenMe.00403138 ; |Buffer = KeygenMe.00403138
004012CE |. 6A 6B PUSH 6B ; |ControlID = 6B (107.)
004012D0 |. FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hWnd
004012D3 |. E8 F2000000 CALL <JMP.&USER32.GetDlgItemTextA> ; \GetDlgItemTextA
004012D8 |. 83F8 00 CMP EAX,0 ; 检验是否输入Serial
004012DB |. 74 02 JE SHORT KeygenMe.004012DF
004012DD |. EB 17 JMP SHORT KeygenMe.004012F6
004012DF |> 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
004012E1 |. 68 62344000 PUSH KeygenMe.00403462 ; |Title = "KeyGen lena151 "
004012E6 |. 68 00304000 PUSH KeygenMe.00403000 ; |Text = " Give me more material hehe!!"
004012EB |. 6A 00 PUSH 0 ; |hOwner = NULL
004012ED |. E8 FC000000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA
004012F2 |. C9 LEAVE
004012F3 |. C2 1000 RETN 10
004012F6 |> 68 38304000 PUSH KeygenMe.00403038 ; /String = ""
004012FB |. E8 30010000 CALL <JMP.&kernel32.lstrlen> ; \lstrlenA
00401300 |. 33F6 XOR ESI,ESI
00401302 |. 8BC8 MOV ECX,EAX ; 将Namme赋值给ECX
00401304 |. B8 01000000 MOV EAX,1 ; EAX 赋值为 0x1
00401309 |> 8B15 38304000 /MOV EDX,DWORD PTR DS:[403038] ; 将Name的前四个字节赋值给EDX
0040130F |. 8A90 37304000 |MOV DL,BYTE PTR DS:[EAX+403037] ; 将Name 的单字节赋值给DL
00401315 |. 81E2 FF000000 |AND EDX,0FF ; 前四个字节值 与 0x0FF 相与, 即将EDX至存储单个字符的值
0040131B |. 8BDA |MOV EBX,EDX ; EDX 赋值给 EBX
0040131D |. 0FAFDA |IMUL EBX,EDX ; EBX 与 EDX 相乘
00401320 |. 03F3 |ADD ESI,EBX ; ESI + EBX
00401322 |. 8BDA |MOV EBX,EDX ; EDX赋值给 EBX
00401324 |. D1FB |SAR EBX,1 ; EBX向右移位1位 即除以2的1次方
00401326 |. 83C3 03 |ADD EBX,3 ; EBX + 3
00401329 |. 0FAFDA |IMUL EBX,EDX ; EBX * EDX
0040132C |. 2BDA |SUB EBX,EDX ; EBX - EDX
0040132E |. 03F3 |ADD ESI,EBX ; ESI + EBX
00401330 |. 03F6 |ADD ESI,ESI ; ESI + ESI
00401332 |. 40 |INC EAX
00401333 |. 49 |DEC ECX
00401334 |.^ 75 D3 \JNZ SHORT KeygenMe.00401309
00401336 |. 3B35 38314000 CMP ESI,DWORD PTR DS:[403138] ; 将计算的而结果与 Serial前四个字节想比较
0040133C |. 75 15 JNZ SHORT KeygenMe.00401353
- 首先,程序会检查
Name/Serial
的输入情况(最多输入25个字符) - 其次,对Name进行算法
- 最后,将算法的结果与输入的Serial的前四个字符的字节值做对比(注意,是小端对比)
注册机
这里注注册机的难点在于计算出Serial,但是要给他转换为字符。而且有的输入的Name没有对应的Serial,因此该注册及最好是爆破注册机
#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() {
int name_length;
std::cout << "Please enter the length of the Name to be generated (1-25): ";
std::cin >> name_length;
if (name_length < 1 || name_length > 25) {
std::cerr << "Invalid length. Please enter a value between 1 and 25." << std::endl;
return 1;
}
unsigned long long totalCombinations = pow(62, name_length);
for (unsigned long long i = 0; i < totalCombinations; ++i) {
std::string register_Name = generateSequentialName(name_length, i);
DWORD serial_hexnum = name_To_Serialnum(register_Name);
std::string register_serial = serialnum_To_Serial(serial_hexnum);
if(!register_serial.empty())
std::cout << register_Name << " To " << register_serial << std::endl;
}
return 0;
}
std::string generateSequentialName(int length, unsigned long long index) {
const std::string chars("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
std::string name(length, '0');
unsigned long long base = chars.size();
for (int i = 0; i < length; ++i) {
name[length - i - 1] = chars[index % base];
index /= base;
}
return name;
}
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) {
const std::string chars("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
std::ostringstream oss;
oss << std::hex<< num;
std::string hexStr = oss.str();
std::string result;
for (size_t i = 0; i < hexStr.length(); i += 2) {
std::string byteString = hexStr.substr(i, 2);
char byte = (char)strtol(byteString.c_str(), nullptr, 16);
if (chars.find(byte) == std::string::npos)
return "";
result.push_back(byte);
}
// 去掉前导的0字符
result.erase(result.find_last_not_of('\0') + 1);
//逆向输出
std::reverse(result.begin(), result.end());
return result;
}
Crack
我们对最后比较的地方进行修改0040133C |. /75 15 JNZ SHORT KeygenMe.00401353
,将其使用NOP填充
crackme_0006
信息收集
PEID查看程序基本信息:
-
程序类型:32位 GUI
-
编程语言:TASM/MASM
- 壳信息:无壳
程序基本功能信息:
此程序经过尝试,只有输入正确的Name/Serial,才会弹出注册信息
此程序算法比较复杂,⭐⭐⭐⭐
ReCrackMe
程序导入OllyDbg
首先我们查看程序调用的API
-
GetDlgItemTextA
:接受文本框输入的内容https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-getdlgitemtexta
-
GetVolumeInformationA
:检索与指定根目录关联的文件系统和卷的相关信息。https://learn.microsoft.com/zh-cn/windows/win32/api/fileapi/nf-fileapi-getvolumeinformationa
我们使用combar插件对这两个设置函数断点
-
BPX GetDlgItemTextA
-
BPX GetVolumeInformationA
我们输入ReCrackMe/R123456e
,点击check后,程序中断在0x004010F9
我们分析汇编代码,根据OllyDbg给出的注释信息,我们得知GetVolumeInformationA
在这里是用来获取C盘的pVolumeSerialNumber
我们点击段首004010C2 /$ 55 PUSH EBP
,我们得知有两处调用此函数段
0x004011D5
0x004011F4
我们Ctrl + F9
,执行到函数末尾,再点击F8,单步执行返回
函数返回至0x004011DA
,我们得知C盘的pVolumeSerialNumber
是A833742F
PS:这是我这台电脑的C盘的卷序列号,这里面的注释是我已经分析后的注释
卷序列号计算
我们继续向下分析,这里面调用函数如下
-
CALL 00401098
:移位函数004011DD |. 6A 05 PUSH 5 004011DF |. FF75 FC PUSH DWORD PTR SS:[EBP-4] ; C盘卷序列信息 004011E2 |. E8 B1FEFFFF CALL CrackMe_.00401098 ; C盘序列号信息参数向左移动5位 即×2的5次方
00401098 /$ 55 PUSH EBP ; EAX参数向左移动n位 即×2的n次方
00401099 |. 8BEC MOV EBP,ESP
0040109B |. 53 PUSH EBX
0040109C |. 56 PUSH ESI
0040109D |. 57 PUSH EDI
0040109E |. 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8]
004010A1 |. 8D55 0C LEA EDX,DWORD PTR SS:[EBP+C]
004010A4 |. 8A0A MOV CL,BYTE PTR DS:[EDX]
004010A6 |. D3E0 SHL EAX,CL ; EAX向左移动CL位
004010A8 |. 5F POP EDI
004010A9 |. 5E POP ESI
004010AA |. 5B POP EBX
004010AB |. C9 LEAVE
004010AC \. C2 0800 RETN 8
-
CALL 00401081
:循环移位函数004011E7 |. 6A 0D PUSH 0D 004011E9 |. 50 PUSH EAX 004011EA |. E8 92FEFFFF CALL CrackMe_.00401081 ; 处理后的C盘序列号信息 循环向左移动 0x0D位
00401081 /$ 55 PUSH EBP ; 将EAX的值 循环左移
00401082 |. 8BEC MOV EBP,ESP
00401084 |. 53 PUSH EBX
00401085 |. 56 PUSH ESI
00401086 |. 57 PUSH EDI
00401087 |. 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8] ; 将[EBP+8] 赋值给EAX
0040108A |. 8D55 0C LEA EDX,DWORD PTR SS:[EBP+C] ; 将EBP+C 地址赋值给 EDX
0040108D |. 8A0A MOV CL,BYTE PTR DS:[EDX] ; 将[EBP+C] 赋值给CL
0040108F |. D3C0 ROL EAX,CL ; 将EAX中的数值 向左 循环移动CL位
00401091 |. 5F POP EDI
00401092 |. 5E POP ESI
00401093 |. 5B POP EBX
00401094 |. C9 LEAVE
00401095 \. C2 0800 RETN 8
-
CALL 004010AF
:两数相加函数(舍弃进位)004011FC |. FF75 F8 PUSH DWORD PTR SS:[EBP-8] ; D盘序列号信息 004011FF |. FF75 FC PUSH DWORD PTR SS:[EBP-4] ; C盘序列号信息 00401202 |. E8 A8FEFFFF CALL CrackMe_.004010AF ; C盘和D盘序列号信息相加
004010AF /$ 55 PUSH EBP ; 相加 004010B0 |. 8BEC MOV EBP,ESP 004010B2 |. 53 PUSH EBX 004010B3 |. 57 PUSH EDI 004010B4 |. 56 PUSH ESI 004010B5 |. 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8] 004010B8 |. 0345 0C ADD EAX,DWORD PTR SS:[EBP+C] 004010BB |. 5E POP ESI 004010BC |. 5F POP EDI 004010BD |. 5B POP EBX 004010BE |. C9 LEAVE 004010BF \. C2 0800 RETN 8
-
CALL 00401056
00401217 |. FF75 F8 PUSH DWORD PTR SS:[EBP-8] ; /Arg2 0040121A |. FF75 FC PUSH DWORD PTR SS:[EBP-4] ; |Arg1 0040121D |. E8 34FEFFFF CALL CrackMe_.00401056 ; \将两个参数转换为浮点数,并将每个参数的平方数相机啊,在计算其平方根
- 两个无符号整数转换为有符号浮点数
- 两个浮点数的平方相加 (即
a^2 + b^2 = c^2
) - 相加后的值开平方
00401056 /$ 55 PUSH EBP ; 将两个参数转换为浮点数,并将每个参数的平方数相机啊,在计算其平方根
00401057 |. 8BEC MOV EBP,ESP
00401059 |. 83C4 FC ADD ESP,-4
0040105C |. 53 PUSH EBX
0040105D |. 56 PUSH ESI
0040105E |. 57 PUSH EDI
0040105F |. 9B WAIT
00401060 |. DBE3 FINIT
00401062 |. DB45 08 FILD DWORD PTR SS:[EBP+8] ; 将[EBP+8]整数放到浮点数栈顶
00401065 |. D9C0 FLD ST ; 将计算器ST的值复制到栈顶
00401067 |. DEC9 FMULP ST(1),ST ; ST(1) * ST结果存储与ST(1) 并将ST出栈
00401069 |. DB45 0C FILD DWORD PTR SS:[EBP+C] ; 将[EBP+C]整数放在栈顶
0040106C |. D9C0 FLD ST ; 将ST内容复制到浮点数栈顶
0040106E |. DEC9 FMULP ST(1),ST ; ST(1) * ST结果存储与ST(1) 并将ST出栈
00401070 |. DEC1 FADDP ST(1),ST ; 将两个相乘结果相加
00401072 |. D9FA FSQRT ; 计算相加的平方根
00401074 |. DB5D FC FISTP DWORD PTR SS:[EBP-4] ; 将ST转换为DWORD整数,存储在EBP-4
00401077 |. 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4]
0040107A |. 5F POP EDI
0040107B |. 5E POP ESI
0040107C |. 5B POP EBX
0040107D |. C9 LEAVE
0040107E \. C2 0800 RETN 8
Name
算法
我们继续向下分析,来到第一个调用GetDlgItemTextA
处,我们可以看到,在第二个GetDlgItemTextA
之间,有很多指令,这就是Name的算法
首先会检查输入的Name长度是否大于等于4,后续的处理算法,我将其分为了三步。
Name-Step1
算法
Name-Step1
只调用了CALL 00401036
00401249 |> \68 04314000 PUSH CrackMe_.00403104
0040124E |. E8 E3FDFFFF CALL CrackMe_.00401036 ; Name-Step1
该函数内容如下
00401036 /$ 55 PUSH EBP
00401037 |. 8BEC MOV EBP,ESP
00401039 |. 8B75 08 MOV ESI,DWORD PTR SS:[EBP+8] ; 将Name的地址赋值给ESI
0040103C |. FC CLD ; DF置0 即从低地址向高地址方向处理字符串,即正向
0040103D |. 33D2 XOR EDX,EDX ; 将EAX赋值为1
0040103F |. B8 01000000 MOV EAX,1 ; 将EAX赋值为1
00401044 |> 0FB60E /MOVZX ECX,BYTE PTR DS:[ESI] ; 将Name的单字节赋值给ECX,即单个字符
00401047 |. 46 |INC ESI ; ESI 自增1,即遍历Name
00401048 |. 0BC9 |OR ECX,ECX ; ECX与ECX异或,即检验是否到Name的末尾
0040104A |. 74 06 |JE SHORT CrackMe_.00401052
0040104C |. F7E1 |MUL ECX ; EAX *= ECX,将低32位赋值给EAX,高32位赋值给EDX
0040104E |. 03C2 |ADD EAX,EDX ; EAX += EDX,低32位 + 高32位
00401050 |.^ EB F2 \JMP SHORT CrackMe_.00401044
00401052 |> C9 LEAVE
00401053 \. C2 0400 RETN 4
Name-Step2
算法
Name-Step2
算法调用一个函数,但有其他处理
00401253 |. 6A 01 PUSH 1
00401255 |. 50 PUSH EAX
00401256 |. E8 26FEFFFF CALL CrackMe_.00401081 ; Name-Step2 将Step1生成的的数值向左 循环移动1位
0040125B |. 0B45 F0 OR EAX,DWORD PTR SS:[EBP-10] ; 将生成的数值与 0x5ED8D5A9 或运算
0040125E |. 25 FFFFFF0F AND EAX,0FFFFFFF ; 再将其与 0x0FFFFFFFF 与运算
00401263 |. 8945 EC MOV DWORD PTR SS:[EBP-14],EAX ; 将生成的地址复制到 EBP-14
CALL 00401081
,前面我们已经知道这是循环移位函数
00401081 /$ 55 PUSH EBP ; 将EAX的值 循环左移
00401082 |. 8BEC MOV EBP,ESP
00401084 |. 53 PUSH EBX
00401085 |. 56 PUSH ESI
00401086 |. 57 PUSH EDI
00401087 |. 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8] ; 将[EBP+8] 赋值给EAX
0040108A |. 8D55 0C LEA EDX,DWORD PTR SS:[EBP+C] ; 将EBP+C 地址赋值给 EDX
0040108D |. 8A0A MOV CL,BYTE PTR DS:[EDX] ; 将[EBP+C] 赋值给CL
0040108F |. D3C0 ROL EAX,CL ; 将EAX中的数值 向左 循环移动CL位
00401091 |. 5F POP EDI
00401092 |. 5E POP ESI
00401093 |. 5B POP EBX
00401094 |. C9 LEAVE
00401095 \. C2 0800 RETN 8
Name-Step3
算法
Name-Step3
算法生成对应的Serial
0040126A |. 8D35 00304000 LEA ESI,DWORD PTR DS:[403000] ; 将地址0x00403000 赋值给ESI
00401270 |. 8B45 EC MOV EAX,DWORD PTR SS:[EBP-14] ; 将生成的值 赋值给EAX
00401273 |> 8945 EC /MOV DWORD PTR SS:[EBP-14],EAX
00401276 |. 6A 10 |PUSH 10 ; /Arg2 = 00000010
00401278 |. 50 |PUSH EAX ; |Arg1
00401279 |. E8 82FDFFFF |CALL CrackMe_.00401000 ; \获取EAX的除以0x10的余数
0040127E |. 8BC8 |MOV ECX,EAX ; 将余数 赋值给ECX
00401280 |. 8D3D 73204000 |LEA EDI,DWORD PTR DS:[402073] ; 将0x00402073 赋值给EDI
00401286 |. 8A0439 |MOV AL,BYTE PTR DS:[ECX+EDI] ; 将0x402073存储的字符串的 第ECX位置单字节赋值给AL
00401289 |. 8806 |MOV BYTE PTR DS:[ESI],AL ; 将获取到的单字符赋值给ESI
0040128B |. 8B45 EC |MOV EAX,DWORD PTR SS:[EBP-14] ; 将Step3生成的值 赋值给 EAX
0040128E |. 6A 04 |PUSH 4 ; /Arg2 = 00000004
00401290 |. 50 |PUSH EAX ; |Arg1
00401291 |. E8 86FDFFFF |CALL CrackMe_.0040101C ; \EAX除以0x04 获取其商
00401296 |. 8945 EC |MOV DWORD PTR SS:[EBP-14],EAX
00401299 |. 0BC0 |OR EAX,EAX ; EAX与EAX 或运算,检验是否为0
0040129B |. 74 04 |JE SHORT CrackMe_.004012A1
0040129D |. 46 |INC ESI
0040129E |. 47 |INC EDI
0040129F |.^ EB D2 \JMP SHORT CrackMe_.00401273
校验
当算法结束后,接受输入的Serial,将其与真-Serial对比
这里使用lstcmpA
函数进行对比。
注册机
通过前面分析,我们已经知道该程序算法的流程,写出注册机
#include <iostream>
#include <string>
#include <Windows.h>
#include <cstdint>
#include <cmath>
DWORD GetVolumeInformationNumber();
DWORD RotateLeft(DWORD value, BYTE count);
DWORD CalculateHypotenuse(DWORD a, DWORD b);
std::string GetName();
DWORD NameStep1(const std::string& name);
DWORD NameStep2(DWORD num1, DWORD volume_information_number);
std::string NameStep3(DWORD num2);
int main() {
// Get volume information number
DWORD volume_information_number = GetVolumeInformationNumber();
// Get name from user
std::string user_name = GetName();
// Perform Name-Step1 calculation
DWORD name_step1_number = NameStep1(user_name);
// Perform Name-Step2 calculation
DWORD name_step2_number = NameStep2(name_step1_number, volume_information_number);
// Perform Name-Step3 calculation
std::string serial_number = NameStep3(name_step2_number);
// Output the serial number
std::cout << "Your serial number is: " << serial_number << std::endl;
return 0;
}
DWORD GetVolumeInformationNumber() {
char volume_name_buffer_c[0x80] = { 0 };
char volume_name_buffer_d[0x80] = { 0 };
DWORD volume_serial_number_c = 0;
DWORD volume_serial_number_d = 0;
DWORD max_filename_length = 0;
DWORD file_system_flags = 0;
char file_system_name_buffer_c[0x80] = { 0 };
char file_system_name_buffer_d[0x80] = { 0 };
DWORD volume_name_size = sizeof(volume_name_buffer_c);
DWORD file_system_name_size = sizeof(file_system_name_buffer_c);
// Get volume information for "C:\"
if (GetVolumeInformationA("C:\\", volume_name_buffer_c, volume_name_size, &volume_serial_number_c, &max_filename_length, &file_system_flags, file_system_name_buffer_c, file_system_name_size)) {
std::cout << "Volume Serial Number of C: " << std::hex << volume_serial_number_c << std::endl;
}
else {
std::cerr << "Failed to get volume information for C:\\" << std::endl;
}
// Get volume information for "D:\"
if (GetVolumeInformationA("D:\\", volume_name_buffer_d, volume_name_size, &volume_serial_number_d, &max_filename_length, &file_system_flags, file_system_name_buffer_d, file_system_name_size)) {
std::cout << "Volume Serial Number of D: " << std::hex << volume_serial_number_d << std::endl;
}
else {
std::cerr << "Failed to get volume information for D:\\" << std::endl;
}
DWORD volume_serial_number_sum = volume_serial_number_c + volume_serial_number_d;
DWORD volume_serial_number_sum_shifted = volume_serial_number_sum << 5;
DWORD volume_serial_number_sum_rolled = RotateLeft(volume_serial_number_sum_shifted, 0x0D);
DWORD volume_serial_number_sum_hypotenuse = CalculateHypotenuse(volume_serial_number_c, volume_serial_number_d);
return volume_serial_number_sum_hypotenuse;
}
DWORD RotateLeft(DWORD value, BYTE count) {
const BYTE num_bits = sizeof(value) * 8; // 32 bits
count %= num_bits; // Prevent shift count from exceeding 32
return (value << count) | (value >> (num_bits - count));
}
DWORD CalculateHypotenuse(DWORD a, DWORD b) {
// Convert parameters to signed integers
int32_t a_signed = static_cast<int32_t>(a);
int32_t b_signed = static_cast<int32_t>(b);
// Convert signed integers to floating point numbers
double a_float = static_cast<double>(a_signed);
double b_float = static_cast<double>(b_signed);
// Calculate sum of squares
double sum_of_squares = (a_float * a_float) + (b_float * b_float);
// Calculate square root of sum of squares
double hypotenuse = std::sqrt(sum_of_squares);
// Convert result to integer and return
return static_cast<DWORD>(std::round(hypotenuse)); // Round to nearest integer
}
std::string GetName() {
std::string name;
std::cout << "Please enter your name: ";
std::getline(std::cin, name);
while (name.length() < 4) {
std::cout << "The name must contain at least 4 characters! Please re-enter: ";
std::getline(std::cin, name);
}
return name;
}
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;
}
DWORD NameStep2(DWORD num1, DWORD volume_information_number) {
DWORD num2 = RotateLeft(num1, 0x1);
num2 |= volume_information_number;
num2 &= 0x0FFFFFFF;
return num2;
}
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;
}
Crack
根据前面分析,我们可以得知,该程序主要是通过lstrcmpA
函数结果来控制跳转
004012D5 |. /75 18 JNZ SHORT CrackMe_.004012EF
因此我们可以将跳转给NOP
,让其顺序执行即可
PS:这里需要输入Name/Serial
,且Name ≥4
(没有对其crack)
-
附件.zip 下载
没有评论