吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1412|回复: 9
上一主题 下一主题
收起左侧

[CrackMe] 自制C语言中高等逆向难度CrackMe

  [复制链接]
跳转到指定楼层
楼主
AfanX 发表于 2026-8-20 11:46 回帖奖励
CM是什么?Crackme是什么?这是什么东西?楼主发的什么?
他们都是一些公开给别人尝试破解的小程序,制作 Crackme 的人可能是程序员,想测试一下自己的软件保护技术,也可能是一位 Cracker,想挑战一下其它 Cracker 的破解实力,也可能是一些正在学习破解的人,自己编一些小程序给自己破解,KeyGenMe是要求别人做出它的 keygen (序号产生器), ReverseMe 要求别人把它的算法做出逆向分析, UnpackMe 是要求别人把它成功脱壳,本版块禁止回复非技术无关水贴。

本帖最后由 AfanX 于 2026-8-20 11:50 编辑

一、程序说明

这是一个练手用的 Crackme,基于 Windows / MinGW-w64 编译,64 位控制台程序。

程序运行后会依次要求输入:

[1/2] Password:  <你的输入>
[2/2] Serial :   <你的输入>

只有当 口令(Password) 和 序列号(Serial) 都正确时,才会输出 [+] Access granted.。

目标(三选一,难度递增):

  1. 入门目标:逆向出正确的 Serial 校验算法,任意给定一组合法的 Password,算出对应的 Serial。
  2. 进阶目标:逆向出 正确的 Password 原文。
  3. 终极目标:写出一份完整的 keygen(注册机),能对任意输入生成合法 Serial(或直接吐出正确口令)。

二、说明与要求

  • 本程序没有联网、没有加壳、没有虚拟机保护,就是纯代码层面的对抗。
  • 拿到正确口令/序列号后,请回帖说明你的 分析思路、走了哪些弯路、卡在了哪里,方便大家一起学习。
  • 建议别急着爆破,这个 CM 的设计目标是「提高攻击成本」

三、致破解者的话

这个 CM 不追求「不可破解」,也没有谁能做到绝对不可逆——它只是想看看,面对一层层嵌套的反逆向手段时,你会选择硬扛、绕过,还是另辟蹊径。

祝各位玩得开心,期待看到你们的思路。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册[Register]

x

免费评分

参与人数 1吾爱币 +1 收起 理由
jaffa + 1 谢谢@Thanks!

查看全部评分

发帖前要善用【论坛搜索】功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
xieyulin 发表于 2026-8-20 13:20
人都无聊到这个程度了

免费评分

参与人数 1吾爱币 -15 违规 +1 收起 理由
RS水果 -15 + 1 警告:CM区禁止回复『与主题无关非技术内容』,违者重罚!

查看全部评分

推荐
addtool 发表于 2026-8-23 01:59
本帖最后由 addtool 于 2026-8-23 02:02 编辑

这个crackme还是很强。不过确实已经完成。只不过是AI小朋友代劳的。总耗时两个小时。
注册机就不上传了,结果已经出来。楼主还是很棒的。感觉很难。

创建时间: 2026-08-22 23:48

crackme.exe 逆向分析与破解报告

一、任务目标与完成度

目标 状态 说明
入门: 逆向 Serial 校验算法, 生成合法 Serial ✅ 完成 算法完全还原, 运行时验证 Access granted
进阶: 逆向正确 Password 原文 ✅ 完成 ifgkenc (AVX2 爆破命中, 实测 Access granted)
终极: 完整 keygen ✅ 完成 program/keygen.py (Serial 任意构造 + 口令校验复刻)

最终凭证: Password: ifgkenc / Serial: DEAD-BEEF-CAFE-792C(及任意构造值)

二、样本概览

属性 值
文件 crackme.exe, 65,648 字节, x64 PE
编译器 MinGW GCC 16.1.0 (-g -O2)
保护特征 /4 /19 /31 /45 /57 /70 /81 递增编号区块(仿 VMProtect 布局), TLS 回调, 自校验
致命弱点 COFF 符号表未剥离(1619 个符号), 全部函数名/地址泄露

关键函数地址 (ImageBase 0x140000000)

函数 VA 功能
main 0x140003400 主流程
decrypt_str 0x140001490 字符串解密器
obf_hash 0x140001650 防爆破哈希
verify_password 0x140001b40 口令校验
verify_serial 0x140001c80 序列号校验
anti_init 0x140001970 反调试初始化
anti_collect_flags 0x140001a80 读 g_anti_secret 全局标志
anti_runtime_check 0x140001a90 运行时反调试(IsDebuggerPresent+PEB+rdtsc)
code_self_check 0x140001780 .text CRC32+FNV-1a 自校验
fake_fail_path 0x140001710 假失败路径(打印误导信息, exit 0xDEAD)
g_anti_secret 0x140008148 反调试状态全局变量

三、核心算法还原

3.1 decrypt_str (字符串解密)

  • 密钥流: key[i] = ((i*7 - 251*floor(i*7*0x828cbfbf/2^39)) + 0x5a) ^ r14 - (i&15), 其中 r14 每字节递增 0x13
  • 分叉: 每字节调用 anti_collect_flags() 取 g_anti_secret:
    • g == 0x9e3779b9 → 走正常路径
    • 其他值 → 走混淆分支(key 重算后 ^0xa5), 解出乱码
  • 终止: 密文字节 == key 字节(明文为 0)或 i==255

解密出的全部明文:

符号 明文
STR_BANNER (0x140005220) \n=== SUPER GUARDED CRACKME ===\n[Reverse me if you can]\n
STR_PROMPT_PW (0x140005198) [1/2] Password:
STR_PROMPT_SN (0x140005110) [2/2] Serial :
STR_SUCCESS (0x140005088) [+] Access granted. This is just a demo crackme.\n
STR_FAIL (0x140005000) [-] Wrong. Try harder (and check your debugger).\n
盐常量 (0x140005440) n0t_th3_r3al_s4lt ("not the real salt")

3.2 obf_hash (防爆破哈希, 4096 轮)

obf_hash(buf, len, rounds=0x1000):
  state=0x811c9dc5(FNV basis); k=0x9e3779b9(golden)
  循环 rounds 轮:
    r10=0
    对每个字节 b:
      e = rol((b ^ state), 5) * k
      state = e
      k = k*0x19660d + 0x3c6ef35f        # LCG 演进
      state = ((e>>13)^e)*0x85ebca6b + r10
      r10 += 0x1f
    state ^= state>>16
  返回 state

每轮重新扫描全串, len*rounds 次迭代 → 单次调用即 4 万次运算, 有意拖慢爆破。

3.3 verify_password (口令校验)

h1 = obf_hash(input, strlen(input), 4096)
h2 = obf_hash("n0t_th3_r3al_s4lt", 18, 4096) = 0x1cdb78dc
g  = anti_collect_flags()   ; 正常运行 = 0x9e3779b9, 被调试 = 0xdeadbeef
target = h2 ^ g ^ 0x680d202a
通过条件: h1 == target

运行时实测 target = 0xeae1214f (内存探针读 verify_password 内 esi 寄存器确认, 与静态推导一致):

场景 target
正常运行 (g=0x9e3779b9) 0xeae1214f ← 有效口令的目标
被调试 (g=0xdeadbeef) 0xaa7be619 (陷阱值, 无对应口令)

3.4 verify_serial (序列号校验) — 与口令无关, 可独立构造

格式: XXXX-XXXX-XXXX-XXXX (4 组 hex, '-' 在第 4/9/14 位, 总长 19)

A,B,C,D = 四组 hex 值 (每组 16 位)
约束1: D == ((3*A + 7*B + 13*C) ^ 0x5a5a) & 0xffff
约束2: A/B/C 均非零
约束3: A != B, B != C, A != C
满足则返回 0 (通过)

构造方法: 任意选三个互异非零的 A/B/C, D 由线性公式直接推出。示例合法序列号(运行时验证通过):
DEAD-BEEF-CAFE-792C / 1111-2222-3333-E1E2 / 1234-5678-9ABC-352A

四、反调试机制与绕过

检测项 实现 结果处理
IsDebuggerPresent / CheckRemoteDebuggerPresent anti_init 开头 g_anti_secret=0xdeadbeef
PEB.BeingDebugged (gs:[0x60]+2) anti_init / anti_runtime_check 同上
PEB.NtGlobalFlag & 0x70 (gs:[0x60]+0xBC) anti_init 同上
FindWindowA 窗口类检测 dbg_window 同上
OutputDebugStringA 异常探测 anti_init 同上
rdtsc/cpuid 时序检测 ×8 次 anti_runtime_check 尾部 返回非0
.text CRC32+FNV 自校验 (baseline 双哈希) code_self_check 不匹配→0xdeadbeef
假失败路径 fake_fail_path 打印 "Debugger detected? No problem..." 后 exit 0xDEAD
死代码混淆 main 中大量 GetTickCount 伪分支 不影响逻辑

关键设计: g_anti_secret 的值同时影响字符串解密分叉与口令 target——被调试时解密乱码且口令永远错误。

绕过方式 (crackme_nofail.exe):

  • anti_init @0x140001970 → mov dword ptr [0x140008148], 0x9e3779b9; xor eax,eax; ret(直接置正常标志并返回成功)
  • anti_runtime_check / anti_self_check / code_self_check → xor eax,eax; ret

验证结果 (文件重定向 stdin, 真实控制台输出):

=== SUPER GUARDED CRACKME ===
[Reverse me if you can]
[1/2] Password:
[2/2] Serial :
[+] Access granted. This is just a demo crackme.

五、Password 原文求解过程

有效口令需满足 obf_hash(pw, 4096) == 0xeae1214f (pw 为 1~64 字节可打印 ASCII)。

为什么原文无法"算出": Serial 是线性方程(正向构造), Password 是单向哈希(原像攻击)。算法全知也只能验证不能反推。原文不在二进制中(.rdata 全偏移解密扫描/编号区块/DWARF/COFF 符号表均穷尽, 仅发现作者嘲讽彩蛋: DWARF 文件名 "gY0uKgg0=L" = "got you caught")。

最终方案: AVX2 八路并行爆破 (program/brute_avx2.c):

  • 单条 SIMD 指令并行推进 8 个候选的独立状态链, 吞吐为标量 8 倍; 20 线程共 160 路
  • 以 salt 哈希 0x1cdb78dc 对拍自检(mask=ff 全命中)
  • 排除空间: ≤4 字符全可打印、5 字符小写+数字、6 字符小写(标量版)、500 万字典规则变体
  • 在 7 位小写字母空间(26^7≈80亿)命中

结果: ifgkenc — obf_hash = eae1214f 与 target 精确匹配; crackme_nofail.exe(校验逻辑原样, 仅绕反调试) 实测输出 [+] Access granted.

注[中]: 该串亦可能只是 80 亿空间中的一个碰撞而非作者原文, 但对校验逻辑完全等效——verify_password 只认哈希。

六、防范能力评估

防护手段 强度 失效原因
仿 VMProtect 区块命名 ★☆☆☆☆ 仅视觉迷惑, 无实际虚拟化
COFF 符号保留(-g 未 strip) 致命 函数名/地址全泄露, 逆向难度骤降
字符串加密 ★★★☆☆ 解密器随程序自带, 且密钥流可静态模拟
4096 轮防爆破哈希 ★★★★☆ 单向性强, 是本样本最有效的防线
Serial 与 Password 解耦设计缺陷 致命 Serial 可独立构造, 攻击者无需口令即可过 serial 关
多层反调试 ★★★☆☆ 单点 patch anti_init 即全局失效; 且管道/重定向环境误触发(健壮性差)
.text 自校验 ★★☆☆☆ baseline 首次调用才记录, patch 后首次运行不触发对比
假失败路径误导 ★★☆☆☆ 打印"继续执行"实则退出, 易识别

总体评价[中]: 对脚本小子有一定拖延效果(字符串加密+反调试+慢哈希), 但符号泄露与 Serial/Password 解耦两个致命伤使其在熟练分析者面前约 1 小时内全面失守。

七、文件索引

文件 说明
test/peinfo.py PE 头解析器
test/coffsymdump.py COFF 符号表导出(函数地址)
test/disfunc.py 按符号独立反汇编各函数(capstone)
test/simulate.py 全算法精确复刻(decrypt_str/obfhash/verify*)
test/testdec.py 字符串常量批量解密演示
test/makepatch_fixed.py 生成 crackme_nofail.exe(仅绕反调试)
test/maketestserial.py 生成 crackme_test_serial.exe(口令绕过+serial原样)
test/runfile.py 文件 stdin 方式运行(规避 CRT 管道缓冲问题)
test/readtarget2.py 内存探针读取运行时 target(决定性验证)
program/keygen.py 完整注册机(Serial 构造器+算法复刻)
program/brute_avx2.c/.exe AVX2 八路并行爆破器(160路, 含自检)
program/brute_mt.c 标量 20 线程爆破器
program/brute_dict.c 字典+规则攻击(123词×4变换×1万后缀)
crackme_nofail.exe 反调试绕过版(verify 原样)
crackme_test_serial.exe 口令校验绕过+serial 原样(Access granted 演示版)

免费评分

参与人数 1热心值 +1 收起 理由
jaffa + 1 谢谢@Thanks!

查看全部评分

3#
qq465881818 发表于 2026-8-20 14:44
密码哈希原像问题,没有简单数学公式直接算出明文,只能暴力 / 哈希碰撞。


[C] 纯文本查看 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <intrin.h>

static uint32_t g_anti_flags = 0;
static uint32_t g_self_crc = 0;
static uint32_t g_self_fnv = 0;
static unsigned char g_decrypt_buf[256];

static const unsigned char enc_banner[] = {
    0x50,0x4c,0x71,0x6e,0x16,0x4e,0xa5,0x57,0x47,0x7b,0x34,0x2c,0x6b,0x74,0xfa,0x8b,
    0xbf,0xd5,0xac,0xf0,0xc4,0x1c,0x13,0x0c,0x8f,0x8c,0xd4,0x36,0xc3,0xc8,0x02,0x34,
    0x08,0x54,0xba,0xb6,0x81,0xab,0xd0,0xae,0xea,0x09,0xb1,0xdb,0xad,0xe0,0xc4,0xa9,
    0x4a,0x34,0x6a,0x6b,0x7d,0x65,0xdf,0xee,0xc7,0xcc,0xb1,0x92,0x6b,0x80,0x7d,0xa6,
    0xdf,0xf4,0xc9,0xca,0x33,0x58,0x75,0x0e,0xfc,0xff,0x0a,0xd5,0xd0,0xbb,0x3e,0x41,
    0x64,0x97,0xb2,0x7d,0x88,0xf3,0xd6,0xa9,0x3c,0x3f,0x6a,0x15,0x30,0xfb,0xfe,0xe1,
    0x12,0x43,0x75,0x23,0xd4,0x85,0x3b,0x68,0x97,0xc7,0xf8,0xa9,0x5d,0x66,0x33,0x40,
    0x29,0xe2,0xef,0x04,0x05,0x2e,0x5b,0x78,0x51,0x3a,0xa7,0xdc,0xed,0x86,0x73,0x80,
    0x69,
};
static const unsigned char enc_passwd_prompt[] = {
    0x01,0x40,0x63,0x61,0x6b,0x3d,0xa0,0x66,0x71,0x5a,0x63,0x04,0x4c,0x51,0x92,0xef,
    0xfa,0x91,0x8c,0xb3,0x96,0x5d,0x50,0x47,0xc2,0xc9,0xf4,0x0b,0xfe,0xf5,0x08,0x6f,
    0x5a,0x31,0xcc,0xd3,0xf3,0xd8,0xb5,0x8e,0x87,0x6c,0x91,0xb2,0xcb,0xc0,0xbd,0xc6,
    0x3f,0x14,0x09,0x0a,0x13,0x38,0xd5,0xee,0xc7,0xcc,0xb1,0x92,0x6b,0x80,0x7d,0xa6,
    0xdf,0xf4,0xc9,0xca,0x33,0x58,0x75,0x0e,0xfc,0xff,0x0a,0xd5,0xd0,0xbb,0x3e,0x41,
    0x64,0x97,0xb2,0x7d,0x88,0xf3,0xd6,0xa9,0x3c,0x3f,0x6a,0x15,0x30,0xfb,0xfe,0xe1,
    0x12,0x43,0x75,0x23,0xd4,0x85,0x3b,0x68,0x97,0xc7,0xf8,0xa9,0x5d,0x66,0x33,0x40,
    0x29,0xe2,0xef,0x04,0x05,0x2e,0x5b,0x78,0x51,0x3a,0xa7,0xdc,0xed,0x86,0x73,0x80,
    0x69,
};
static const unsigned char enc_serial_prompt[] = {
    0x01,0x43,0x63,0x61,0x6b,0x3d,0xa3,0x62,0x70,0x40,0x75,0x07,0x1e,0x0f,0x88,0xcf,
    0xfa,0x91,0x8c,0xb3,0x96,0x5d,0x50,0x47,0xc2,0xc9,0xf4,0x0b,0xfe,0xf5,0x08,0x6f,
    0x5a,0x31,0xcc,0xd3,0xf3,0xd8,0xb5,0x8e,0x87,0x6c,0x91,0xb2,0xcb,0xc0,0xbd,0xc6,
    0x3f,0x14,0x09,0x0a,0x13,0x38,0xd5,0xee,0xc7,0xcc,0xb1,0x92,0x6b,0x80,0x7d,0xa6,
    0xdf,0xf4,0xc9,0xca,0x33,0x58,0x75,0x0e,0xfc,0xff,0x0a,0xd5,0xd0,0xbb,0x3e,0x41,
    0x64,0x97,0xb2,0x7d,0x88,0xf3,0xd6,0xa9,0x3c,0x3f,0x6a,0x15,0x30,0xfb,0xfe,0xe1,
    0x12,0x43,0x75,0x23,0xd4,0x85,0x3b,0x68,0x97,0xc7,0xf8,0xa9,0x5d,0x66,0x33,0x40,
    0x29,0xe2,0xef,0x04,0x05,0x2e,0x5b,0x78,0x51,0x3a,0xa7,0xdc,0xed,0x86,0x73,0x80,
    0x69,
};
static const unsigned char enc_success[] = {
    0x01,0x5a,0x11,0x73,0x77,0x7e,0x93,0x62,0x71,0x5a,0x34,0x0c,0x4c,0x54,0xc6,0xbb,
    0x9f,0xf5,0xa2,0x93,0xc2,0x35,0x39,0x34,0xe2,0xa0,0x87,0x2b,0x94,0x80,0x7b,0x1b,
    0x7a,0x50,0xec,0xb7,0x96,0xb5,0xda,0xae,0xe4,0x1e,0xf0,0xd1,0xa0,0xad,0xd8,0xe8,
    0x35,0x14,0x09,0x0a,0x13,0x38,0xd5,0xee,0xc7,0xcc,0xb1,0x92,0x6b,0x80,0x7d,0xa6,
    0xdf,0xf4,0xc9,0xca,0x33,0x58,0x75,0x0e,0xfc,0xff,0x0a,0xd5,0xd0,0xbb,0x3e,0x41,
    0x64,0x97,0xb2,0x7d,0x88,0xf3,0xd6,0xa9,0x3c,0x3f,0x6a,0x15,0x30,0xfb,0xfe,0xe1,
    0x12,0x43,0x75,0x23,0xd4,0x85,0x3b,0x68,0x97,0xc7,0xf8,0xa9,0x5d,0x66,0x33,0x40,
    0x29,0xe2,0xef,0x04,0x05,0x2e,0x5b,0x78,0x51,0x3a,0xa7,0xdc,0xed,0x86,0x73,0x80,
    0x69,
};
static const unsigned char enc_failure[] = {
    0x01,0x5c,0x11,0x73,0x61,0x6f,0x9f,0x69,0x65,0x07,0x34,0x3f,0x4c,0x4c,0x88,0xa7,
    0x9b,0xe3,0xe8,0xd6,0xe4,0x7d,0x78,0x26,0xac,0xad,0xd4,0x68,0x96,0x90,0x6b,0x04,
    0x7a,0x48,0xa3,0xa6,0x81,0xf8,0xd1,0xeb,0xe5,0x19,0xf6,0xd5,0xae,0xb2,0x94,0xe8,
    0x35,0x14,0x09,0x0a,0x13,0x38,0xd5,0xee,0xc7,0xcc,0xb1,0x92,0x6b,0x80,0x7d,0xa6,
    0xdf,0xf4,0xc9,0xca,0x33,0x58,0x75,0x0e,0xfc,0xff,0x0a,0xd5,0xd0,0xbb,0x3e,0x41,
    0x64,0x97,0xb2,0x7d,0x88,0xf3,0xd6,0xa9,0x3c,0x3f,0x6a,0x15,0x30,0xfb,0xfe,0xe1,
    0x12,0x43,0x75,0x23,0xd4,0x85,0x3b,0x68,0x97,0xc7,0xf8,0xa9,0x5d,0x66,0x33,0x40,
    0x29,0xe2,0xef,0x04,0x05,0x2e,0x5b,0x78,0x51,0x3a,0xa7,0xdc,0xed,0x86,0x73,0x80,
    0x69,
};
static const unsigned char enc_expected_passwd[] = {
    0x34,0x41,0x38,0x0c,0x42,0x75,0xc3,0x58,0x70,0x1a,0x75,0x07,0x61,0x46,0x9c,0xa3,
    0x8e,0x91,0x8c,0xb3,0x96,0x5d,0x50,0x47,0xc2,0xc9,0xf4,0x0b,0xfe,0xf5,0x08,0x6f,
    0x5a,0x31,0xcc,0xd3,0xf3,0xd8,0xb5,0x8e,0x87,0x6c,0x91,0xb2,0xcb,0xc0,0xbd,0xc6,
    0x3f,0x14,0x09,0x0a,0x13,0x38,0xd5,0xee,0xc7,0xcc,0xb1,0x92,0x6b,0x80,0x7d,0xa6,
    0xdf,0xf4,0xc9,0xca,0x33,0x58,0x75,0x0e,0xfc,0xff,0x0a,0xd5,0xd0,0xbb,0x3e,0x41,
    0x64,0x97,0xb2,0x7d,0x88,0xf3,0xd6,0xa9,0x3c,0x3f,0x6a,0x15,0x30,0xfb,0xfe,0xe1,
    0x12,0x43,0x75,0x23,0xd4,0x85,0x3b,0x68,0x97,0xc7,0xf8,0xa9,0x5d,0x66,0x33,0x40,
    0x29,0xe2,0xef,0x04,0x05,0x2e,0x5b,0x78,0x51,0x3a,0xa7,0xdc,0xed,0x86,0x73,0x80,
    0x69,
};
static const char* const dbg_window_classes[] = {
    "OLLYDBG", "OllyDbg", "IDA -", "Immunity Debugger",
    "WinDbgFrameClass", "IDA64", "Qt5QWindowIcon",
    "ImmunityDebugger", "ObsidianGUI", "x64dbg", NULL,
};

static uint32_t obf_hash(const char* s, size_t len, uint32_t iters)
{
    uint32_t ecx, edx, r10;
    const unsigned char* p, * end;
    if (iters == 0)
        return 0x811c9dc5;
    if (len == 0)
        return (iters & 1u) ? 0x811c1cd9 : 0x811c9dc5;
    ecx = 0x9e3779b9u;
    edx = 0x811c9dc5u;
    for (uint32_t i = 0; i < iters; i++) {
        p = (const unsigned char*)s;
        end = p + len;
        r10 = 0;
        do {
            uint32_t eax = *p;
            eax ^= edx;
            eax = (eax << 5) | (eax >> 27);
            eax = eax * ecx;
            edx = eax;
            ecx = ecx * 0x19660d;
            p++;
            edx >>= 13;
            edx ^= eax;
            edx = edx * 0x85ebca6b;
            ecx = ecx + 0x3c6ef35f;
            edx = edx + r10;
            r10 = r10 + 0x1f;
        } while (p != end);
        edx ^= edx >> 16;
    }
    return edx;
}

static unsigned char* decrypt_str(const unsigned char* in)
{
    uint32_t r14 = 0;
    size_t i;
    for (i = 0; i < 0xff; i++) {
        uint32_t ebx = 7u * static_cast<uint32_t>(i);
        ebx = ebx - static_cast<uint32_t>(((uint64_t)ebx * 0x828cbfbfu) >> 39) * 251u;
        ebx = ebx + 0x5a;
        ebx ^= r14;
        ebx = ebx - (static_cast<uint32_t>(i) & 0xf);
        if (g_anti_flags != 0x9e3779b9u) {
            uint32_t eax = g_anti_flags * 0x9e3779b9u;
            uint32_t edx = eax >> 13;
            uint32_t ecx = static_cast<uint32_t>(i) & 7;
            eax ^= edx;
            edx = eax >> ecx;
            ecx = static_cast<uint32_t>(i) >> 1;
            edx ^= ebx;
            ecx &= 7;
            eax >>= ecx;
            edx ^= eax;
            ebx = edx ^ 0xffffffa5u;
        }
        uint8_t k = static_cast<uint8_t>(ebx);
        uint8_t c = in[i];
        g_decrypt_buf[i] = static_cast<uint8_t>(c ^ k);
        if (c == k) {
            g_decrypt_buf[i] = 0;
            return g_decrypt_buf;
        }
        r14 += 0x13;
    }
    g_decrypt_buf[0xff] = 0;
    return g_decrypt_buf;
}

static void print_decrypted(const unsigned char* enc)
{
    const char* s = reinterpret_cast<const char*>(decrypt_str(enc));
    fputs(s, stdout);
    fflush(stdout);
}

static void print_prompt(const unsigned char* enc)
{
    const char* s = reinterpret_cast<const char*>(decrypt_str(enc));
    fputs(s, stdout);
    fputc('\n', stdout);
    fflush(stdout);
}

static void strip_newline(char* s)
{
    size_t n = strlen(s);
    while (n > 0 && (s[n - 1] == '\r' || s[n - 1] == '\n'))
        s[--n] = '\0';
}

static uint32_t anti_collect_flags(void)
{
    return g_anti_flags;
}

static int dbg_window(void)
{
    for (size_t i = 0; dbg_window_classes[i]; i++)
        if (FindWindowA(dbg_window_classes[i], NULL))
            return 1;
    return 0;
}

static int code_self_check(void)
{
    HMODULE base = GetModuleHandleA(NULL);
    if (base == NULL)
        return 1;
    uint8_t* p = reinterpret_cast<uint8_t*>(base);
    if (*reinterpret_cast<uint16_t*>(p) != 0x5a4d)
        return 1;
    LONG e_lfanew = *reinterpret_cast<LONG*>(p + 0x3c);
    uint8_t* nt = p + e_lfanew;
    if (*reinterpret_cast<uint32_t*>(nt) != 0x4550)
        return 1;
    uint16_t nsec = *reinterpret_cast<uint16_t*>(nt + 6);
    if (nsec == 0)
        return 1;
    uint16_t opt_size = *reinterpret_cast<uint16_t*>(nt + 0x14);
    uint8_t* sect = nt + 0x18 + opt_size;
    uint8_t* end = sect + (nsec - 1) * 40 + 40;
    uint8_t* text_va = NULL;
    DWORD text_size = 0;
    for (; sect < end; sect += 40) {
        if (*reinterpret_cast<uint32_t*>(sect) == 0x7865742e && *(sect + 4) == 't') {
            text_size = *reinterpret_cast<DWORD*>(sect + 0x10);
            text_va = p + *reinterpret_cast<DWORD*>(sect + 0x0c);
            break;
        }
    }
    if (text_va == NULL || text_size == 0)
        return 1;
    uint32_t crc = 0xffffffff;
    for (DWORD i = 0; i < text_size; i++) {
        crc ^= text_va[i];
        for (int b = 0; b < 8; b++) {
            uint32_t lsb = crc & 1U;
            crc >>= 1;
            if (lsb != 0)
                crc ^= 0xedb88320U;
        }
    }
    crc = ~crc;
    uint32_t fnv = 0x811c9dc5u;
    for (DWORD i = 0; i < text_size; i++) {
        fnv ^= text_va[i];
        fnv *= 0x01000193u;
    }
    if (g_self_crc == 0) {
        g_self_crc = crc;
        g_self_fnv = fnv;
        return 0;
    }
    if (g_self_crc == crc && g_self_fnv == fnv)
        return 0;
    g_anti_flags = 0xdeadbeef;
    return 1;
}

static int anti_init(void)
{
    int is_dbg = IsDebuggerPresent();
    BOOL remote = FALSE;
    HANDLE h = GetCurrentProcess();
    CheckRemoteDebuggerPresent(h, &remote);
    uint8_t being_debugged = *(reinterpret_cast<uint8_t*>(__readgsqword(0x60) + 2));
    uint32_t nt_global_flag = *(reinterpret_cast<uint32_t*>(__readgsqword(0x60) + 0xbc));
    DWORD lasterr = 0;
    int window_found = 0;
    if (being_debugged) {
        SetLastError(0xdeadbeef);
        OutputDebugStringA("crackme_anti_debug_probe");
        (void)GetLastError();
        (void)dbg_window();
        goto fail;
    }
    SetLastError(0xdeadbeef);
    OutputDebugStringA("crackme_anti_debug_probe");
    lasterr = GetLastError();
    window_found = dbg_window();
    if (remote)               goto fail;
    if (nt_global_flag & 0x70) goto fail;
    if (lasterr == 0)         goto fail;
    if (window_found)         goto fail;
    if (code_self_check() != 0) goto fail;
    g_anti_flags = 0x9e3779b9u;
    return 0;
fail:
    (void)code_self_check();
    g_anti_flags = 0xdeadbeefu;
    return 1;
}

static int anti_runtime_check(void)
{
    if (IsDebuggerPresent())
        return 1;
    uint8_t  being_debugged = *(reinterpret_cast<uint8_t*>(__readgsqword(0x60) + 2));
    uint32_t nt_global_flag = *(reinterpret_cast<uint32_t*>(__readgsqword(0x60) + 0xbc));
    if (being_debugged || (nt_global_flag & 0x70))
        return 1;
    SetLastError(0xdeadbeef);
    OutputDebugStringA("crackme_anti_debug_probe");
    if (GetLastError() == 0)
        return 1;
    if (dbg_window())
        return 1;
    for (int i = 0; i < 8; i++) {
        unsigned long long t0 = __rdtsc();
        int cpuid_buf[4] = { 0 };
        __cpuid(cpuid_buf, 0);
        (void)__rdtsc();
        (void)t0;
    }
    return 0;
}

static int verify_password(const char* input)
{
    if (input == NULL)
        return 1;
    size_t len = strlen(input);
    if (len - 1 > 0x3f)
        return 1;
    uint32_t h_in = obf_hash(input, len, 0x1000);
    const char* expected = reinterpret_cast<const char*>(decrypt_str(enc_expected_passwd));
    uint32_t h_exp = obf_hash(expected, strlen(expected), 0x1000);
    uint32_t target = h_exp ^ g_anti_flags ^ 0x680d202au;
    return h_in != target;
}

static int verify_serial(const char* s)
{
    if (s == NULL)
        return 1;
    if (strlen(s) != 19)
        return 1;
    if (s[4] != '-' || s[9] != '-' || s[14] != '-')
        return 1;
    uint32_t g[4] = { 0, 0, 0, 0 };
    const char* p = s;
    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4; j++) {
            char c = *p++;
            int v;
            if (c >= '0' && c <= '9')
                v = c - '0';
            else if (c >= 'A' && c <= 'F')
                v = c - 'A' + 10;
            else if (c >= 'a' && c <= 'f')
                v = c - 'a' + 10;
            else
                return 1;
            g[i] = (g[i] << 4) | (v & 0xf);
        }
        if (i < 3)
            p++;
    }
    uint32_t g0 = g[0], g1 = g[1], g2 = g[2], g3 = g[3];
    uint32_t checksum = ((g0 * 3 + g1 * 7 + g2 * 13) ^ 0x5a5au) & 0xffffu;
    if (g3 != checksum)
        return 1;
    if (g0 == 0 || g1 == 0 || g2 == 0)
        return 1;
    if (g0 == g1 || g1 == g2 || g0 == g2)
        return 1;
    return 0;
}

static void fake_fail_path(void)
{
    fputs("\n[+] Debugger detected? No problem, continuing...\n", stdout);
    volatile uint64_t acc = 0;
    for (int i = 0; i < 1000; i++)
        acc += GetTickCount();
    (void)acc;
}

int main(void)
{
    char passwd[0x80] = { 0 };
    char serial[0x80] = { 0 };
    char p2[0x80] = { 0 };
    char s2[0x80] = { 0 };
    if (anti_init() != 0) {
        fake_fail_path();
        return 0xdead;
    }
    print_decrypted(enc_banner);
    if (anti_runtime_check()) {
        fake_fail_path();
        return 0xbeef;
    }
    print_prompt(enc_passwd_prompt);
    if (fgets(passwd, sizeof passwd, stdin) == NULL)
        goto fail;
    strip_newline(passwd);
    print_prompt(enc_serial_prompt);
    if (fgets(serial, sizeof serial, stdin) == NULL)
        goto fail;
    strip_newline(serial);
    strncpy(p2, passwd, 0x7f); p2[0x7f] = '\0';
    strncpy(s2, serial, 0x7f); s2[0x7f] = '\0';
    if (anti_runtime_check()) {
        fake_fail_path();
        return 0xbeef;
    }
    if (verify_password(p2) != 0)
        goto fail;
    if (anti_runtime_check()) {
        fake_fail_path();
        return 0xbeef;
    }
    if (verify_serial(s2) != 0)
        goto fail;
    if (anti_runtime_check()) {
        fake_fail_path();
        return 0xbeef;
    }
    print_decrypted(enc_success);
    if (anti_runtime_check()) {
        fake_fail_path();
        return 0xbeef;
    }
    print_decrypted(enc_failure);
    (void)code_self_check();
    if (anti_runtime_check()) {
        fake_fail_path();
        return 0xbeef;
    }
    SecureZeroMemory(p2, sizeof p2);
    SecureZeroMemory(s2, sizeof s2);
    SecureZeroMemory(passwd, sizeof passwd);
    SecureZeroMemory(serial, sizeof serial);
    return 0;
fail:
    if (anti_runtime_check()) {
        fake_fail_path();
        return 0xbeef;
    }
    print_decrypted(enc_failure);
    (void)code_self_check();
    (void)anti_runtime_check();
    SecureZeroMemory(p2, sizeof p2);
    SecureZeroMemory(s2, sizeof s2);
    SecureZeroMemory(passwd, sizeof passwd);
    SecureZeroMemory(serial, sizeof serial);
    return 0;
}

static void pre_main_guard(void)
{
    uint8_t being_debugged = *(reinterpret_cast<uint8_t*>(__readgsqword(0x60) + 2));
    if (IsDebuggerPresent())
        goto kill;
    if (being_debugged)
        goto kill;
    return;
kill:
    TerminateProcess(GetCurrentProcess(), 0xdeadbeefu);
}
4#
qwertyuioplm7 发表于 2026-8-20 19:25
吾爱被ai占领了

免费评分

参与人数 1吾爱币 -20 违规 +1 收起 理由
bian96 -20 + 1 警告:CM区禁止回复『与主题无关非技术内容』,违者重罚!

查看全部评分

5#
wyesheng 发表于 2026-8-21 00:04
这位楼主大大是大佬嘛?正好想着多学习一下呢。就是想请教下,如果用了加壳的,还能算出来吗?
6#
kevinyou816 发表于 2026-8-21 08:08
任何一个形式最终转化一种结果

免费评分

参与人数 1吾爱币 -20 违规 +1 收起 理由
bian96 -20 + 1 警告:CM区禁止回复『与主题无关非技术内容』,违者重罚!

查看全部评分

7#
xiaoyu7771111 发表于 2026-8-21 17:25
调试信息忘记去掉了?
8#
GDExecW 发表于 2026-8-21 18:17
我用 IDA 给程序 Patch 了,可能是编译器的特性吧,函数名称都保留了,因此比较好爆破,但是这看着像哈希算法,如果硬要还原的话还是有些难度。

思路就是将 verify_serial 和 verify_password 恒返回 0。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册[Register]

x
9#
Haswell 发表于 2026-8-22 10:10

调试信息忘记去掉了?

免费评分

参与人数 1吾爱币 -100 违规 +1 收起 理由
bian96 -100 + 1 警告:CM区禁止回复『与主题无关非技术内容』,违者重罚!

查看全部评分

您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-10-5 14:34

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表