[逆向工程]160个CrackMe入门实战之aLoNg3x.2解析(七)

[逆向工程]160个CrackMe入门实战之aLoNg3x.2解析(七)

一.aLoNg3x.2功能

作者说不要打补丁,找出用户名和注册码,隐藏下面的按钮,算成功

还是用补丁方式,验证是否逆向成功

二.查壳

无壳,32位程序,Delphi语言

DeDe查看事件及控件相关信息:

事件:

00442B98 取消按钮事件

00442CC8 关于按钮事件

00442F28 注册按钮事件

004430BC 重试按钮事件

控件ID:

2E8 重试按钮控件ID

2E4 取消控件ID

2DC 注册码控件ID

2D8 输入名控件ID

2CC 注册控件ID

三.逆向分析

3.1 00442F28 注册按钮事件

还是使用nop爆破方法分析事件,注册按钮入口地址00442F28处下断点

根据DeDe以及dbg分析以下是爆破点注释

00442F9F处nop填充

00442FC0处nop填充

00442F9F处nop填充

00442FCF处mov dl,1-----修改为mov dl,0

修改后将该文件打补丁为3.exe

备注:源程序中对注册码也做了一些简单校验,本次爆破输入时全部为数字

shell 复制代码
00442F28 | 55                       | push ebp                                | 注册按钮入口
00442F29 | 8BEC                     | mov ebp,esp                             |
00442F2B | 83C4 F8                  | add esp,FFFFFFF8                        |
00442F2E | 53                       | push ebx                                |
00442F2F | 56                       | push esi                                | esi:"U嬱兡舾(3D"
00442F30 | 33C9                     | xor ecx,ecx                             | ecx:"U嬱兡舾(3D"
00442F32 | 894D F8                  | mov dword ptr ss:[ebp-8],ecx            |
00442F35 | 8BD8                     | mov ebx,eax                             |
00442F37 | 33C0                     | xor eax,eax                             |
00442F39 | 55                       | push ebp                                |
00442F3A | 68 22304400              | push 3.443022                           |
00442F3F | 64:FF30                  | push dword ptr fs:[eax]                 |
00442F42 | 64:8920                  | mov dword ptr fs:[eax],esp              |
00442F45 | 8D55 F8                  | lea edx,dword ptr ss:[ebp-8]            |
00442F48 | 8B83 DC020000            | mov eax,dword ptr ds:[ebx+2DC]          |
00442F4E | E8 ED02FEFF              | call 3.423240                           |
00442F53 | 8B45 F8                  | mov eax,dword ptr ss:[ebp-8]            |
00442F56 | 8D55 FC                  | lea edx,dword ptr ss:[ebp-4]            | [ebp-04]:BaseThreadInitThunk
00442F59 | E8 FAF9FBFF              | call 3.402958                           |
00442F5E | 8BF0                     | mov esi,eax                             | esi:"U嬱兡舾(3D"
00442F60 | 837D FC 00               | cmp dword ptr ss:[ebp-4],0              | [ebp-04]:BaseThreadInitThunk
00442F64 | 74 37                    | je 3.442F9D                             |
00442F66 | B8 38304400              | mov eax,3.443038                        | 443038:"You MUST insert a valid Long Integer Value in the Code Editor... Thank you :)"
00442F6B | E8 00F6FFFF              | call 3.442570                           |
00442F70 | 8D55 F8                  | lea edx,dword ptr ss:[ebp-8]            |
00442F73 | 8B83 DC020000            | mov eax,dword ptr ds:[ebx+2DC]          |
00442F79 | E8 C202FEFF              | call 3.423240                           |
00442F7E | 8B45 F8                  | mov eax,dword ptr ss:[ebp-8]            |
00442F81 | E8 06FBFFFF              | call 3.442A8C                           |
00442F86 | A3 30584400              | mov dword ptr ds:[445830],eax           |
00442F8B | BA 90304400              | mov edx,3.443090                        | edx:"U嬱兡舾(3D"
00442F90 | 8B83 DC020000            | mov eax,dword ptr ds:[ebx+2DC]          | 2DC 注册码控件ID
00442F96 | E8 D502FEFF              | call 3.423270                           |
00442F9B | EB 6F                    | jmp 3.44300C                            |
00442F9D | 85F6                     | test esi,esi                            | esi:"U嬱兡舾(3D"
00442F9F | 90                       | nop                                     | 跳过隐藏注册按钮,显示Again按钮
00442FA0 | 90                       | nop                                     |
00442FA1 | 8D55 F8                  | lea edx,dword ptr ss:[ebp-8]            |
00442FA4 | 8B83 D8020000            | mov eax,dword ptr ds:[ebx+2D8]          | 2D8 输入名字控件id
00442FAA | E8 9102FEFF              | call 3.423240                           |
00442FAF | 8B4D F8                  | mov ecx,dword ptr ss:[ebp-8]            |
00442FB2 | 8BD6                     | mov edx,esi                             | edx:"U嬱兡舾(3D", esi:"U嬱兡舾(3D"
00442FB4 | A1 30584400              | mov eax,dword ptr ds:[445830]           |
00442FB9 | E8 EAF9FFFF              | call 3.4429A8                           |
00442FBE | 84C0                     | test al,al                              |
00442FC0 | 90                       | nop                                     | 第三处判断 跳过隐藏注册按钮 显示重试按钮
00442FC1 | 90                       | nop                                     |
00442FC2 | 33D2                     | xor edx,edx                             | edx:"U嬱兡舾(3D"
00442FC4 | 8B83 CC020000            | mov eax,dword ptr ds:[ebx+2CC]          | 2CC 注册控件ID
00442FCA | E8 6101FEFF              | call 3.423130                           |
00442FCF | B2 00                    | mov dl,0                                | 修改常量1 为0 
00442FD1 | 8B83 E8020000            | mov eax,dword ptr ds:[ebx+2E8]          | 2E8 重试按钮控件ID
00442FD7 | E8 5401FEFF              | call 3.423130                           |
00442FDC | 33D2                     | xor edx,edx                             | 2D8  输入名控件ID

其中修改常量方式如下:ctrl+e 之前01修改为00

3.2 代码分析过程

shell 复制代码
004429A8 | 55                       | push ebp                                | 关键call
004429A9 | 8BEC                     | mov ebp,esp                             |
004429AB | 83C4 F4                  | add esp,FFFFFFF4                        |
004429AE | 53                       | push ebx                                |
004429AF | 56                       | push esi                                |
004429B0 | 57                       | push edi                                |
004429B1 | 894D F8                  | mov dword ptr ss:[ebp-8],ecx            |  用户名入栈
004429B4 | 8955 FC                  | mov dword ptr ss:[ebp-4],edx            |
004429B7 | 8BF8                     | mov edi,eax                             |
004429B9 | 8B45 F8                  | mov eax,dword ptr ss:[ebp-8]            | 从栈中取用户名
004429BC | E8 2712FCFF              | call along3x.2.403BE8                   |
004429C1 | 33C0                     | xor eax,eax                             |
004429C3 | 55                       | push ebp                                |
004429C4 | 68 7A2A4400              | push along3x.2.442A7A                   |
004429C9 | 64:FF30                  | push dword ptr fs:[eax]                 |
004429CC | 64:8920                  | mov dword ptr fs:[eax],esp              |
004429CF | 8B45 F8                  | mov eax,dword ptr ss:[ebp-8]            | [ebp-08]:"11111"
004429D2 | E8 5D10FCFF              | call along3x.2.403A34                   | 跟进去 取用户名长度 返回eax
004429D7 | 83F8 04                  | cmp eax,4                               |
004429DA | 0F8E 82000000            | jle along3x.2.442A62                    | 小于等于4则跳转,那么输入用户名必须大于4
004429E0 | 33DB                     | xor ebx,ebx                             |
004429E2 | 8B45 F8                  | mov eax,dword ptr ss:[ebp-8]            | [ebp-08]:"11111"
004429E5 | E8 4A10FCFF              | call along3x.2.403A34                   |
004429EA | 85C0                     | test eax,eax                            |
004429EC | 7E 38                    | jle along3x.2.442A26                    |
004429EE | 8945 F4                  | mov dword ptr ss:[ebp-C],eax            | 用户名长度入栈
004429F1 | BE 01000000              | mov esi,1                               | 循环起始
004429F6 | 8B45 F8                  | mov eax,dword ptr ss:[ebp-8]            | [ebp-08]:"11111"
004429F9 | E8 3610FCFF              | call along3x.2.403A34                   |
004429FE | 83F8 01                  | cmp eax,1                               |
00442A01 | 7C 1D                    | jl along3x.2.442A20                     | 小于则跳转,如果不跳转 则eax也就是用户名长度要大于1
00442A03 | 8B55 F8                  | mov edx,dword ptr ss:[ebp-8]            | [ebp-08]:"11111"
00442A06 | 0FB65432 FF              | movzx edx,byte ptr ds:[edx+esi-1]       | 例如用户名输入为11111则从11111 中取1 显示31也就是第一个字符的ASCII值
00442A0B | 8B4D F8                  | mov ecx,dword ptr ss:[ebp-8]            | ecx等于用户名值
00442A0E | 0FB64C01 FF              | movzx ecx,byte ptr ds:[ecx+eax-1]       | 每次循环最后一个字符16进制ASCII值
00442A13 | 0FAFD1                   | imul edx,ecx                            | edx=edx*ecx 第一位和最后一位相乘
00442A16 | 0FAFD7                   | imul edx,edi                            | edx=edx*edi
00442A19 | 03DA                     | add ebx,edx                             | ebx=ebx+edx
00442A1B | 48                       | dec eax                                 | eax递减
00442A1C | 85C0                     | test eax,eax                            | eax是否为0
00442A1E | 75 E3                    | jne along3x.2.442A03                    |
00442A20 | 46                       | inc esi                                 |
00442A21 | FF4D F4                  | dec dword ptr ss:[ebp-C]                |
00442A24 | 75 D0                    | jne along3x.2.4429F6                    | 循环结果
00442A26 | 8BC3                     | mov eax,ebx                             |
00442A28 | 99                       | cdq                                     | 将 eax 中的有符号数符号扩展为 64 位,结果保存在 edx:eax 中(edx 存放高位,eax 存放低位)
00442A29 | 33C2                     | xor eax,edx                             |
00442A2B | 2BC2                     | sub eax,edx                             |
00442A2D | B9 2A2C0A00              | mov ecx,A2C2A                           |
00442A32 | 99                       | cdq                                     |
00442A33 | F7F9                     | idiv ecx                                | eax / ecx eax=(商),edx=(余数)
00442A35 | 8BDA                     | mov ebx,edx                             |
00442A37 | 8B45 FC                  | mov eax,dword ptr ss:[ebp-4]            |
00442A3A | B9 59000000              | mov ecx,59                              | 59:'Y'
00442A3F | 99                       | cdq                                     |
00442A40 | F7F9                     | idiv ecx                                | ecx/59  商eax  余数 edx
00442A42 | 8BC8                     | mov ecx,eax                             |
00442A44 | 8B45 FC                  | mov eax,dword ptr ss:[ebp-4]            |
00442A47 | BE 50000000              | mov esi,50                              | 50:'P'
00442A4C | 99                       | cdq                                     |
00442A4D | F7FE                     | idiv esi                                | esi/50 商eax 余数edx
00442A4F | 03CA                     | add ecx,edx                             |
00442A51 | 41                       | inc ecx                                 | ecx = ecx+1
00442A52 | 894D FC                  | mov dword ptr ss:[ebp-4],ecx            |
00442A55 | 3B5D FC                  | cmp ebx,dword ptr ss:[ebp-4]            | ebx和注册码比较
00442A58 | 75 04                    | jne along3x.2.442A5E                    | 不相等则直接跳转了
00442A5A | B3 01                    | mov bl,1                                | bl等于1正确
3.2.1 隐藏条件(必须先触发)
  • 原因 :程序内部有一个全局变量 [0x445830],初始值为 0
    如果这个值是 0,所有算法计算结果都是 0,永远无法注册成功。
  • 触发方法
    Codice(注册码)输入框 中输入 至少 6 个英文字母 (例如 AAAAAA),然后点击 Register 按钮。
    此时会弹出错误提示(因为纯字母不是有效注册码),但程序内部调用 call 00442A8C[0x445830] 赋值为 0x1686(十进制 5766)。
    这一步只需做一次,之后整个程序运行期间该值保持有效。
3.2.2 隐藏 Register 按钮(第一次成功)
  1. 输入用户名 :在 Name 框中输入任意 长度大于4 的字符串(例如 admin)。
  2. 输入正确注册码 :在 Codice 框中输入根据用户名计算出的纯数字注册码(由注册机生成)。
  3. 点击 Register
    • 程序用 0x1686 和用户名经过双层循环算出 nTemp
    • 验证 注册码 / 89 + 注册码 % 80 + 1 == nTemp
    • 验证通过后,Register 按钮隐藏 ,同时 Again(重试)按钮出现

此时你看到的界面:Register 按钮消失,多了 Again 按钮。

3.2.3 隐藏 Again 按钮(第二次成功)

为了完全破解,还需要把 Again 按钮也隐藏掉。方法类似:

  1. 再次输入假注册码
    Codice 框中再次输入 至少6个英文字母 (例如 AAAAAA),点击 Again 按钮。
    会弹出错误提示(Again 按钮不会消失,但内部状态被重置)。
  2. 输入正确注册码
    Codice 框中重新输入刚才的纯数字正确注册码 ,再次点击 Again 按钮。
  3. 验证通过后,Again 按钮隐藏,最终界面只剩下 Logo 和 Cancella(取消)按钮。
3.2.4 Cancella(取消)按钮的彩蛋

作者在取消按钮的事件(call 00442B30)中隐藏了一个特殊分支:

  • 普通情况:点击 Cancella 会把 Codice 输入框的内容清零。

  • 特殊情况

    Name 框的内容Codice 框的内容 完全相等 (字符串相同)时,点击 Cancella 会弹出两个信息框(而不是清空注册码)。

    注意:Codice 框通常要求纯数字,所以要想触发这个彩蛋,用户名也必须是一个纯数字字符串(例如 12345)。此时点击 Cancella 会弹出两个提示框,算是作者留下的一个"惊喜"。

四、总结操作流程(完整版)

步骤 操作 结果
前置 在 Codice 框输入 AAAAAA,点击 Register 弹出错误,但内部 [0x445830] = 0x1686
1 输入用户名(>4位)和正确纯数字注册码,点击 Register Register 按钮隐藏,Again 按钮出现
2 在 Codice 框再次输入 AAAAAA,点击 Again 弹出错误(Again 按钮还在)
3 在 Codice 框重新输入正确纯数字注册码,点击 Again Again 按钮隐藏,破解完成
彩蛋 用户名和 Codice 框输入相同数字(如 12345),点击 Cancella 弹出两个消息框(不清零)

五、注册机核心公式

正确注册码(纯数字整数)serial 满足:

复制代码
serial / 89 + serial % 80 + 1 == nTemp

其中 nTemp = abs( sum ) % 666666
sum = Σ (0x1686 * username[j] * username[i]),双层循环 i=1...len, j=len...1。

用 C++ 注册机即可根据任意用户名(长度>4)计算出唯一正确的 serial

注册机源码如下:

c++ 复制代码
#include <iostream>
#include <string>
#include <cctype>
#include <limits>

// 检查字符串是否全为数字
bool isAllDigits(const std::string& str) {
    for (char c : str) {
        if (!std::isdigit(static_cast<unsigned char>(c))) return false;
    }
    return true;
}

int main() {
    std::string username, fakeSerial;
    int lenx = 0, leny = 0;

    // 输入用户名(长度至少5)
    while (lenx < 5) {
        std::cout << "请输入用户名(5位以上): ";
        std::getline(std::cin, username);
        lenx = username.length();
        if (lenx < 5) {
            std::cout << "用户名太短,请重新输入。\n";
        }
    }

    // 输入错误注册码(不能全数字,长度至少6)
    while (leny < 6) {
        std::cout << "请输入错误注册码(不能全数字6位以上): ";
        std::getline(std::cin, fakeSerial);
        leny = fakeSerial.length();
        if (leny < 6) {
            std::cout << "长度不足6,请重新输入。\n";
            continue;
        }
        if (isAllDigits(fakeSerial)) {
            std::cout << "不能全为数字,请重新输入。\n";
            leny = 0; // 重置,继续循环
        }
    }

    // 计算隐藏值 edi
    int edi = 0x37B;  // 891
    for (size_t i = 1; i < fakeSerial.length(); ++i) {
        unsigned char cur = static_cast<unsigned char>(fakeSerial[i]);
        unsigned char prev = static_cast<unsigned char>(fakeSerial[i - 1]);
        edi += (cur % 0x11 + 1) * prev;
    }
    edi %= 0x7148;  // 29000
    std::cout << "EDI为: " << edi << std::endl;

    // 根据用户名和 edi 计算 ebx
    long long ebx = 0;
    size_t len = username.length();
    for (size_t i = 0; i < len; ++i) {
        for (size_t j = len; j > 0; --j) {
            unsigned char ch_i = static_cast<unsigned char>(username[i]);
            unsigned char ch_j = static_cast<unsigned char>(username[j - 1]);
            ebx += static_cast<long long>(ch_i) * ch_j * edi;
        }
    }
    ebx %= 0xA2C2A;  // 666666
    std::cout << "EBX为: " << ebx << std::endl;

    ebx -= 1;
    int code = 0;
    // 原C代码中的近似求解
    code = static_cast<int>(ebx * 0x59);          // 取最大值
    code = code - (code % 0x50) + 1;              // 调整满足 %0x50 条件
    std::cout << "正确注册码为: " << code << "\n\n";

    // 枚举所有可能的解
    std::cout << "所有可能的注册码:\n";
    int start = static_cast<int>((ebx - 0x50) * 0x59);
    int end   = static_cast<int>(ebx * 0x59);
    for (int i = start; i < end; ++i) {
        if (i / 0x59 + i % 0x50 == ebx) {
            std::cout << i << std::endl;
        }
    }

    std::cout << "\n按回车键退出...";
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    std::cin.get();
    return 0;
}
相关推荐
曼岛_9 小时前
[逆向工程]160个CrackMe入门实战之Andrnalin.1解析(八)
逆向工程
Pure_White_Sword2 天前
[NSSRound#6 Team]void(V1)
网络安全·ctf·reverse·逆向工程
Pure_White_Sword7 天前
[广东省大学生攻防大赛 2022]pyre
网络安全·ctf·reverse·逆向工程
Hello.Reader19 天前
编译器的六大阶段详解以一行赋值语句为例
编译原理·逆向工程
阿昭L19 天前
浅析64位Windows的SEH机制
windows·逆向工程·seh
阿昭L20 天前
静态反调试技术
逆向工程·反调试
胡耀超1 个月前
Web Crawling 网络爬虫全景:技术体系、反爬对抗与全链路成本分析
前端·爬虫·python·网络爬虫·数据采集·逆向工程·反爬虫
阿昭L1 个月前
CodePatch hook api
hook·逆向工程
阿昭L1 个月前
《逆向工程核心原理》使用调试函数hook api的实验在64位Windows11上的复现
hook·逆向工程