Windows逆向学习之CrackMe
Aking9 发表于 河南 历史精选 2480浏览 · 2024-07-08 02:03

声明

本文章分享两个在学习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盘的pVolumeSerialNumberA833742F

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)

附件:
0 条评论
某人
表情
可输入 255

没有评论