吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 949|回复: 21
上一主题 下一主题
收起左侧

[CrackMe] 一个.Net的Check Me!

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

本帖最后由 gsyifan 于 2026-7-6 14:16 编辑

欢迎来测试:
一:得到密码
二:还原源码

三:分析算法


无论是否可以破解,都欢迎讨论,如果成功破解,还有其它几种保护方式,欢迎尝试。

程序:
解压密码:52pojie


保护选项:




成功截图:

本帖子中包含更多资源

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

x

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

推荐
凉游浅笔深画眉 发表于 2026-7-7 14:53

丢给AI一键就分析出来了啊。




CheckMe 验证算法与破解思路

样本:CheckMe.exe(TWProtector 加壳)  


1. 程序概览

CheckMe 是一个 TWProtector 保护下的 .NET 控制台 crackme。表面入口 Program.Main 并非真实逻辑,而是:

  1. 打印 TWProtector ASCII 横幅  
  2. 调用 EntryPointDecryptor.Initialize 注入 AES 密钥/IV/密文  
  3. 调用 EntryPointDecryptor.Execute 解密并动态执行真实入口 IL  
  4. 真实逻辑再调用 VerifyPassword 校验用户输入

2. 验证算法

2.1 总入口:VerifyPassword(string input)

bool VerifyPassword(string input)
{
    if (input.Length < 10) return false;
    if (!CheckPrefix(input)) return false;
    if (!CheckMiddle(input)) return false;
    if (!CheckHash(input)) return false;
    return true;
}

校验顺序:长度 → 前缀 → 中段 → 哈希


2.2 前缀校验:CheckPrefix

静态数据:

static readonly int[] _keyEnc = { 115, 119, 113, 148, 146, 152, 138, 137, 155, 151 };
const int prefixKey = 31;  // 0x1F

解码算法:

string DecodePrefix(int[] data, int k)
{
    char[] buf = new char[data.Length];
    for (int i = 0; i < data.Length; i++)
        buf[i] = (char)((data[i] - k - i) & 0xFF);
    return new string(buf);
}

校验:

bool CheckPrefix(string input) =>
    input.StartsWith(DecodePrefix(_keyEnc, 31));

解码结果: TWProtecto(10 字符)


本帖子中包含更多资源

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

x

免费评分

参与人数 2吾爱币 +1 热心值 +2 收起 理由
vipcrack + 1 谢谢@Thanks!
gsyifan + 1 + 1 我很赞同!

查看全部评分

推荐
heartingrass 发表于 2026-7-30 18:32
本帖最后由 heartingrass 于 2026-7-30 21:29 编辑

原贴地址:https://www.52pojie.cn/forum.php?mod=viewthread&tid=2115655

代码分析(壳体分析)

程序执行入口

static <Module>()
{
    //验证主要壳方法是否被修改或优化
    \u200D\u200E\u200B\u200E\u200E\u200C.\u200F\u200E\u200F\u200F\u200B();
    //....
 \u200B\u200C\u200F\u200E\u200F\u200E\u200C\u200E.\u200C\u200E\u200E\u200C\u200D\u200B\u200E\u200D();
    // .....
        \u200B\u200E\u200F\u200D.\u200C\u200D\u200E\u200C();
    //.....
        \u200F\u200E\u200B\u200F\u200E\u200F.\u200D\u200F\u200C\u200D();
    //.....
        <Module>.\u200E\u200F\u200D\u200D\u200C\u200E();
}

验证主要壳方法是否被修改或优化

// Token: 0x02000011 RID: 17
[NullableContext(1)]
[Nullable(0)]
internal static class \u200D\u200E\u200B\u200E\u200E\u200C
{
        // Token: 0x0600003E RID: 62 RVA: 0x00009D30 File Offset: 0x00007F30
        public static void \u200F\u200E\u200F\u200F\u200B()

方法主要功能是:

  • 找到资源"nkyplgcbjbbp":通过判断资源数据的前四个字节为 DE AD BE EF,长度为 0x0000024C

  • 解密资源:key 为 第4至7字节,即 0x4D9B2B3D,调用方法 0x06000040 解密第8至末尾的字节。

  • 解密后资源结构(第8至末尾):总数(4字节)、方法体Token(4)及字节摘要(4字节)、…………

    0000 EFBEADDE 4D9B2B3D 00000048 0600001F ....=+.MH.......
    0010 C88A8C26 06000020 4EC67CE4 06000021 &... ....|.N!...
    0020 48899709 06000022 5459B784 06000001 ...H".....YT....
    0030 14F08ACB 06000002 124CC62B 06000003 ........+.L.....
    0040 D75CCA79 06000004 45003C9F 06000005 y.\......<.E....
    0050 D5ED6309 06000006 578311A7 06000007 .c.........W....
    0060 4F37EEAF 06000008 C11D05FC 06000009 ..7O............
    0070 D2966F4B 0600000A 6456825D 0600000B Ko......].Vd....
    0080 76590DE3 0600000C 243A1F0C 0600000D ..Yv......:$....
    0090 D4E2C0F5 0600000E CAB721D9 0600000F .........!......
    00A0 F5DC050E 06000010 81BC7EB5 06000011 .........~......
    00B0 DB894762 06000012 54AC4009 06000013 bG.......@.T....
    …………

    总共校验 0x48 个,而方法表里有 0x4B 个。只有3个不需要校验 0x06000024  0x06000026  0x06000027 。
    这三个方法正好没有方法体。

        // Token: 0x06000024 RID: 36
        [DllImport("kernel32.dll", EntryPoint = "IsDebuggerPresent")]
        private static extern bool \u200D\u200B\u200D\u200E\u200C\u200C\u200F();
        // Token: 0x06000026 RID: 38
        [NullableContext(1)]
        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, EntryPoint = "GetModuleHandle")]
        private static extern IntPtr \u200F\u200C\u200B\u200C\u200B\u200E\u200E\u200E(string lpModuleName);
    
        // Token: 0x06000027 RID: 39
        [DllImport("kernel32.dll", EntryPoint = "VirtualProtect")]
        private unsafe static extern bool \u200B\u200C\u200C\u200B\u200E(byte* lpAddress, int dwSize, uint flNewProtect, out uint lpflOldProtect);
  • 验证壳方法是否被修改:读取资源里的相应方法的方法体,与元数据的方法体是否一致,不一致,则调用 Environment.FailFast(null); 终止程序。

    提取数据

    // Token: 0x02000010 RID: 16
    [NullableContext(1)]
    [Nullable(0)]
    internal static class \u200B\u200C\u200F\u200E\u200F\u200E\u200C\u200E
    {
        // Token: 0x06000028 RID: 40 RVA: 0x000048E8 File Offset: 0x000048E8
        public static void \u200C\u200E\u200E\u200C\u200D\u200B\u200E\u200D()

    方法主要功能:

  • 找到资源"uwdchypjzvdi":通过判断资源数据的前四个字节为 CA FE BA BE,长度为 0x0000CF4F

  • 解密资源:key 为 第4至7字节,即 0x8090B238,调用方法 0x0600003C 解密第8至末尾的字节。

  • 解密后资源结构(第8至末尾):总数(4字节)、方法体Token(4)及字节摘要(4字节)、…………

    0000 CA FE BA BE 38 B2 90 80 1C 00 00 00 01 00 00 06 ....8...........
    0010 07 00 00 00 00 00 00 00 00 00 BE AC 00 00 20 87 .............. .
    0020 33 B6 4D 20 87 34 B6 4D 61 8D 18 00 00 01 25 20 3.M .4.Ma.....% 
    0030 05 98 71 74 20 05 98 71 74 61 20 25 60 05 84 20 ..qt ..qta %`.. 
    0040 70 60 05 84 61 9C 25 20 85 92 7F 0F 20 84 92 7F p`..a.% .... ...
    0050 0F 61 20 38 0E 2D 7D 20 BC 0E 2D 7D 61 9C 25 20 .a 8.-} ..-}a.% 
    0060 75 46 F8 6C 20 77 46 F8 6C 61 20 5B 85 92 4D 20 uF.l wF.la [..M 
    0070 12 85 92 4D 61 9C 25 20 89 AB BD CE 20 8A AB BD ...Ma.% .... ...
    0080 CE 61 20 BF AC 34 26 20 85 AC 34 26 61 9C 25 20 .a ..4& ..4&a.% 
    0090 E6 74 B6 32 20 E2 74 B6 32 61 20 FB F2 33 24 20 .t.2 .t.2a ..3$ 
    00A0 DC F2 33 24 61 9C 25 20 48 C9 60 8D 20 4D C9 60 ..3$a.% H.`. M.`
    00B0 8D 61 20 29 80 3F 22 20 83 80 3F 22 61 9C 25 20 .a ).?" ..?"a.% 
    magic CA FE BA BE
    key   38 B2 90 80
    cnt   1C 00 00 00
    --------------------------------------------------------------------
    最终分析结构:
    00 00 00 00 token
    00 00 00 00 未知(某种标记)  
    00 00 ....  局部变量签名长度、签名数据
    00 00 00 00 异常处理块长度、异常处理块信息
    00 00 00 00 方法体长度、方法体字节码
    00 00       字符串
    .......
    --------------------------------------------------------------------
    方法体:起始地址为 0xC
    01 00 00 06 token 
    07 00 00 00 未知1,读的时候跳过了,+ 8
    00 00 未知2
    00 00 00 00 未知3
    BE AC 00 00 方法体:0000020C00060300000000000000000067000000
    地址计算:C + 8 + 未知2 + 2 + 未知3 + 4 + 0x0000ACBE + 4 = 0x0000ACDC
    在地址 0x0000ACDC 处,读取2个字节(未知4), 0x0000ACDC + 2 = 0x0000ACDE
    在地址 0x0000ACDE 处,此时就是下一个循环,这个一定是 token
    未知2:如果不为0,则后面紧接着存储数据,
    未知3:如果不为0,则后面紧接着存储数据,
    未知4:如果不为0,则开始循环读取
    --------------------------------------------------------------------
    方法体:起始地址为 0x0000ACDE
    02 00 00 06 token 
    03 00 00 00 未知1,读的时候跳过了,+ 8
    00 00 未知2
    00 00 00 00 未知3
    67 00 00 00 方法体:000003000006030000
    地址计算:0x0000ACDE + 8 + 未知2 + 2 + 未知3 + 4 + 0x00000067 + 4 = 0x0000AD57
    在地址 0x0000AD57 处,读取2个字节(未知4), 0x0000AD57 + 2 = 0x0000AD59
    在地址 0x0000AD59 处,此时就是下一个循环,这个一定是 token
    --------------------------------------------------------------------
    方法体:起始地址为 0x0000AD59
    03 00 00 06 token 
    03 00 00 00 未知1,读的时候跳过了,+ 8
    03 00 未知2:07 01 0E 疑似局部变量签名
    00 00 00 00 未知3
    1E 00 00 00 方法体:000003000006030000
    地址计算: 0x0000AD59 + 8 + 3 + 2 + 未知3 + 4 + 0x0000001E + 4 = 0x0000AD8C
    在地址 0x0000AD8C 处,读取2个字节(未知4), 0x0000AD8C + 2 = 0x0000AD8E
    在地址 0x0000AD8E 处,此时就是下一个循环,这个一定是 token
    --------------------------------------------------------------------
    经过测试,在字节码跳转完了之后,读取的2个字节均为0。
  • 存储结构:方法体的解密信息。

    array:资源库解密之后的数据 "uwdchypjzvdi"
加密方法token列表
                array3        {uint[0x0000001C]}        uint[]
                [0]        0x06000001        uint
                [1]        0x06000002        uint
                [2]        0x06000003        uint
                [3]        0x06000004        uint
                [4]        0x06000005        uint
                [5]        0x06000006        uint
                [6]        0x06000007        uint
                [7]        0x06000008        uint
                [8]        0x06000009        uint
                [9]        0x0600000B        uint
                [10]        0x0600000C        uint
                [11]        0x0600000D        uint
                [12]        0x0600000E        uint
                [13]        0x0600000F        uint
                [14]        0x06000010        uint
                [15]        0x06000011        uint
                [16]        0x06000012        uint
                [17]        0x06000013        uint
                [18]        0x06000015        uint
                [19]        0x06000016        uint
                [20]        0x06000017        uint
                [21]        0x06000018        uint
                [22]        0x06000019        uint
                [23]        0x0600001A        uint
                [24]        0x0600001B        uint
                [25]        0x0600001C        uint
                [26]        0x0600001D        uint
                [27]        0x0600001E        uint
//加密方法的解密信息偏移地址
                array4        {int[0x0000001C]}        int[]
                [0]        0x0000000C        int
                [1]        0x0000ACDE        int
                [2]        0x0000AD59        int
                [3]        0x0000AD8E        int
                [4]        0x0000ADDE        int
                [5]        0x0000AE12        int
                [6]        0x0000AE74        int
                [7]        0x0000AEE2        int
                [8]        0x0000AF4B        int
                [9]        0x0000AFA2        int
                [10]        0x0000AFCA        int
                [11]        0x0000B4AA        int
                [12]        0x0000B689        int
                [13]        0x0000BB32        int
                [14]        0x0000C396        int
                [15]        0x0000C442        int
                [16]        0x0000C466        int
                [17]        0x0000C740        int
                [18]        0x0000C7D4        int
                [19]        0x0000C96C        int
                [20]        0x0000CD2F        int
                [21]        0x0000CE6E        int
                [22]        0x0000CE9B        int
                [23]        0x0000CEC8        int
                [24]        0x0000CEE3        int
                [25]        0x0000CEFE        int
                [26]        0x0000CF19        int
                [27]        0x0000CF34        int
                num3        0x0000001C        int

分析:此处执行之后,记录的数据会作为DynamicMethod方法还原之用

.... ConfuserEx 标准 AntiDump 保护逻辑,抹除 PE 导出表、DOS/NT 头、导入表关键标志,阻止工具内存转储分析

调试时,直接跳过执行。

// Token: 0x0200000F RID: 15
internal static class \u200B\u200E\u200F\u200D
{
        // Token: 0x06000025 RID: 37 RVA: 0x00003FE0 File Offset: 0x00003FE0
        public unsafe static void \u200C\u200D\u200E\u200C()
        {

反调试

调试时,直接跳过执行。

// Token: 0x0200000E RID: 14
internal static class \u200F\u200E\u200B\u200F\u200E\u200F
{
        // Token: 0x06000023 RID: 35 RVA: 0x00003F78 File Offset: 0x00003F78
        public static void \u200D\u200F\u200C\u200D()
        {
                bool isAttached = Debugger.IsAttached;
                if (isAttached)
                {
                        Environment.Exit(1255763124 ^ -1255763125);
                }
                try
                {
                        bool flag = \u200F\u200E\u200B\u200F\u200E\u200F.\u200D\u200B\u200D\u200E\u200C\u200C\u200F();
                        if (flag)
                        {
                                Environment.Exit(-309533851 ^ 309533850);
                        }
                }
                catch
                {
                }
        }

        // Token: 0x06000024 RID: 36
        [DllImport("kernel32.dll", EntryPoint = "IsDebuggerPresent")]
        private static extern bool \u200D\u200B\u200D\u200E\u200C\u200C\u200F();
}

解密一大堆数据:与字符串解密有关系,是解密的#US字符串。

挂接字符串解密函数

// Token: 0x02000001 RID: 1
internal class <Module>
{
        // Token: 0x0600001F RID: 31 RVA: 0x000020A0 File Offset: 0x000020A0
        private static void \u200E\u200F\u200D\u200D\u200C\u200E()
        {
                uint num = 464U;
                uint num2 = 4144958357U;
                uint[] array = new uint[]
                {

Program静态构造方法

// CheckMe.Program
// Token: 0x0600000A RID: 10 RVA: 0x000039C0 File Offset: 0x000039C0
// Note: this type is marked as 'beforefieldinit'.
static Program()
{
        int[] array = new int[21753686 ^ 21753692];
        RuntimeHelpers.InitializeArray(array, fieldof(<PrivateImplementationDetails>.8242ECB75666623F6918BF63D14BA58912CDE2A4A82417B8FAD722CD12DB26C0).FieldHandle);
        Program._keyEnc = array;
        byte[] array2 = new byte[-890174908 ^ -890174911];
        RuntimeHelpers.InitializeArray(array2, fieldof(<PrivateImplementationDetails>.AAE6C1A0CDEC645F6331A2757B98D69D5F22101FA2A8ABA06F52228F36A2EA26).FieldHandle);
        Program._midEnc = array2;
}
//还原
static Program()
{
    int[] array = new int[0xA];
    RuntimeHelpers.InitializeArray(array, fieldof(<PrivateImplementationDetails>.8242ECB75666623F6918BF63D14BA58912CDE2A4A82417B8FAD722CD12DB26C0).FieldHandle);
    Program._keyEnc = array;
    byte[] array2 = new byte[0x5];
    RuntimeHelpers.InitializeArray(array2, fieldof(<PrivateImplementationDetails>.AAE6C1A0CDEC645F6331A2757B98D69D5F22101FA2A8ABA06F52228F36A2EA26).FieldHandle);
    Program._midEnc = array2;
}
直接定位读取
_keyEnc = [0x73, 0x77, 0x71, 0x94, 0x92, 0x98, 0x8A, 0x89, 0x9B, 0x97]
_midEnc = [0x5A, 0x55, 0x59, 0x4B, 0x51]
000250 73 00 00 00 77 00 00 00 71 00 00 00 94 00 00 00 s...w...q.......
000260 92 00 00 00 98 00 00 00 8A 00 00 00 89 00 00 00 ................
000270 9B 00 00 00 97 00 00 00 5A 55 59 4B 51 00 00 00 ........ZUYKQ...

DynamicMethod实现方法

全局类存储待解密信息

  • private static byte[] byte_0;解密资源数据,存放方法体解密信息
  • private static uint[] uint_0;加密方法体token列表
  • private static int[] int_0;加密方法体解密数据的偏移地址列表
  • private static DynamicMethod[] dynamicMethod_0;存储初次构造DM
  • private static object object_0;未知
        // Token: 0x0600002A RID: 42 RVA: 0x0000583C File Offset: 0x00003A3C
        private static DynamicMethod \u200C\u200F\u200D\u200C\u200F(uint token, int pos)
        {

沙发
 楼主| gsyifan 发表于 2026-7-5 06:23 |楼主
4#
吾爱廖小雨 发表于 2026-7-7 20:55
本帖最后由 吾爱廖小雨 于 2026-7-7 20:59 编辑

D师傅大概3分钟找到了密码

然后登录成功

╔══════════════════════════════════════════════════════════════════╗
║               CheckMe.exe  逆向分析报告                          ║
║               TWProtector .NET 程序破解                          ║
╚══════════════════════════════════════════════════════════════════╝

┌─── 基本档案 ────────────────────────────────────────────────────┐
│                                                                 │
│  □ 文件名    CheckMe.exe                                        │
│  □ 大小      107,520 字节                                       │
│  □ 平台      .NET Framework v4.0.30319                          │
│  □ 保护壳    TWProtector(自定义加壳混淆器)                       │
│  □ 密码      TWProtector5I                                      │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
第一章  程序架构
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  CheckMe.exe
  │
  ├── EntryPointDecryptor      入口解密器 (AES-CBC)
  │
  ├── DynamicExecutor          自定义 IL 虚拟机 ★ 核心引擎
  │   ├── IlDesc               方法描述 (返回值/参数/局部变量)
  │   ├── InstrDesc            指令描述 (OpCode + 操作数)
  │   ├── LocalDesc            局部变量定义
  │   └── EhDesc               异常处理表 (try/catch/finally)
  │
  ├── CheckMe.Program          密码验证逻辑 ★ 分析目标
  │   ├── VerifyPassword()     主校验
  │   ├── CheckPrefix()        前缀校验
  │   ├── CheckMiddle()        中间校验
  │   ├── CheckHash()          哈希校验
  │   ├── DecodePrefix()       解码函数
  │   ├── DecodeMiddle()       解码函数
  │   ├── ComputeHash()        哈希函数
  │   └── PrintFlag()          成功输出
  │
  └── 混淆类型(Unicode 中文名) TWProtector 运行时辅助


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
第二章  混淆机制
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  本程序采用三层混淆保护:

  ┌──────────────────────────────────────────────────────────┐
  │ ① 方法体加密                                              │
  │    所有方法的 IL 代码被 AES-CBC 加密,分两块存入嵌入资源:    │
  │      · uwdchypjzvdi  (53,071 字节) — 方法 IL 数据         │
  │      · nkyplgcbjbbp  (588 字节)   — 字符串 / 配置数据     │
  │                                                          │
  │ ② 自定义 IL 虚拟机                                         │
  │    DynamicExecutor 解析加密 IL,使用自定义指令格式执行:      │
  │      [OpCode(int16)] [OperandType(byte)] [Operand(...)]   │
  │    支持 15 种操作数类型,模拟完整 .NET IL 语义               │
  │                                                          │
  │ ③ 符号混淆                                                │
  │    类型名和方法名替换为 Unicode 中文字符(??、???? 等)       │
  │    反编译工具无法正常显示和解析                               │
  └──────────────────────────────────────────────────────────┘


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
第三章  密码结构
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  静态数据(存储在 Program 类中):

    _keyEnc = { 115, 119, 113, 148, 146, 152, 138, 137, 155, 151 }
    _midEnc = {  90,  85,  89,  75,  81 }
    _xorKey = 60

  ★ 密码 = TWProtector5I  (共 13 字符)

    ┌──────┬───────┬───────┬───────┬───────┬──────┐
    │  T   │   W   │   P   │   r   │   o   │  t   │  ← 位置 0~5
    │  84  │  87   │  80   │  114  │  111  │ 116  │
    ├──────┴───────┴───────┴───────┴───────┴──────┤
    │          CheckPrefix() 验证                  │
    │  encoded = ascii + (31 + i)               │
    │  比对 _keyEnc[0..5] = 115,119,113,148,146,152│
    └─────────────────────────────────────────────┘

    ┌──────┬───────┬───────┬───────┬──────┐
    │  e   │   c   │   t   │   o   │  r   │  ← 位置 6~10
    │ 101  │  99   │  116  │  111  │ 114  │
    ├──────┴───────┴───────┴───────┴──────┤
    │       CheckMiddle() 验证              │
    │  Substring(6,5) == DecodeMiddle()    │
    │  解码 _midEnc → "ector"              │
    └──────────────────────────────────────┘

    ┌──────┬──────┐
    │  5   │  I   │  ← 位置 11~12
    │  53  │  73  │
    ├──────┴──────┤
    │ CheckHash() │
    │ 验证哈希后缀  │
    └─────────────┘


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
第四章  核心算法详解
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  4.1  ComputeHash — 自定义哈希函数

    H("")  =  7807

    H(s + c)  =  H(s) × 31  ⊕  c     (⊕ = XOR)

    这是 djb2 哈希的变体,用 XOR 代替了加法。
    32 位无符号算术,自然溢出。

    ┌──────────────────────────────────────┐
    │  H("")            = 0x00001E7F       │
    │  H("a")           = 0x0003B100       │
    │  H("ab")          = 0x00726F62       │
    │  H("abc")         = 0x0DDB7CBD       │
    │  H("TWProtector") = 0x57DC4AD6       │
    └──────────────────────────────────────┘

  4.2  DecodePrefix — 递减键解码

    result  =  (char)( data - (k + i) )

    解码 _keyEnc (k=60) → "7:3URWHFWR"
    (注:CheckPrefix 内部使用不同基准 31 编码,非此函数)


  4.3  DecodeMiddle — 序列 XOR 解码

    result  =  (char)( data ⊕ key_sequence )

    key_sequence = { k+3, k-6, k-15, k-24, k-25 }
                 = {  63,  54,   45,   36,   35 }   (k=60)

    解码 _midEnc → "ector"


  4.4  VerifyPassword — 综合验证

    bool VerifyPassword(string input)
    {
        return CheckPrefix(input)
            && CheckMiddle(input)
            && CheckHash(input);
    }


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
第五章  还原源码
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  以下为 CheckMe.Program 类完整还原(C#):

───────────────────────────────────────────────────────────────
using System;

namespace CheckMe
{
    class Program
    {
        static int[]  _keyEnc = { 115, 119, 113, 148, 146,
                                  152, 138, 137, 155, 151 };
        static byte[] _midEnc = { 90, 85, 89, 75, 81 };
        static int    _xorKey = 60;

        static void Main(string[] args)
        {
            Console.WriteLine(" TWProtector - 程序保护测试\n");
            Console.Write("请输入密码: ");
            string input = Console.ReadLine();

            if (VerifyPassword(input))
            {
                Console.WriteLine("破解成功!");
                PrintFlag();
            }
            else
                Console.WriteLine("破解失败!");
        }

        static bool VerifyPassword(string s)
            => CheckPrefix(s) && CheckMiddle(s) && CheckHash(s);

        static bool CheckPrefix(string s)
        {
            for (int i = 0; i < 6; i++)
                if ((int)s + (31 + i) != _keyEnc)
                    return false;
            return true;
        }

        static bool CheckMiddle(string s)
            => s.Substring(6, 5) == DecodeMiddle(_midEnc, _xorKey);

        static bool CheckHash(string s)
            => ComputeHash(s) == /* expected */ 0xCA7C4C68;

        static string DecodePrefix(int[] d, int k)
        {
            char[] r = new char[d.Length];
            for (int i = 0; i < d.Length; i++)
                r = (char)(d - (k + i));
            return new string(r);
        }

        static string DecodeMiddle(byte[] d, int k)
        {
            int[] key = { k+3, k-6, k-15, k-24, k-25 };
            char[] r = new char[d.Length];
            for (int i = 0; i < d.Length; i++)
                r = (char)(d ^ key);
            return new string(r);
        }

        static int ComputeHash(string s)
        {
            uint h = 7807;
            foreach (char c in s)
                h = (h * 31) ^ (uint)c;
            return (int)h;
        }

        static void PrintFlag()
        {
            Console.WriteLine(@"
  +==================================+
  |       CONGRATULATIONS!           |
  |   You have cracked CheckMe       |
  +==================================+");
        }
    }
}
───────────────────────────────────────────────────────────────


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
第六章  破解思路
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  步骤 1: 直接调用反射拿到编码数据

    _keyEnc  = [115, 119, 113, 148, 146, 152, 138, 137, 155, 151]
    _midEnc  = [90, 85, 89, 75, 81]
    _xorKey  = 60

  步骤 2: 调用 DecodeMiddle 解码中间部分

    DecodeMiddle(_midEnc, 60) → "ector"

  步骤 3: 确定密码中间位置

    反复传入不同长度的字符串给 CheckMiddle,观察异常
    → Substring(6, 5)  → 密码需 ≥ 11 字符
    → 位置 6~10 为 "ector"

  步骤 4: 确定密码前缀

    遍历所有位置测试 CheckPrefix
    → CheckPrefix 验证位置 0~5
    → 编码公式: input + (31 + i) = _keyEnc
    → 前 6 字符 = "TWProt"

  步骤 5: 暴力破解哈希后缀

    foreach c1, c2 in 可打印字符:
        pwd = "TWProtector" + c1 + c2
        if VerifyPassword(pwd) → 找到!

    → 后缀 = "5I"

  最终密码:  TWProtector5I  &#10003;


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
                         END
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

本帖子中包含更多资源

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

x
5#
tfrist 发表于 2026-7-8 07:03
你们这些人 人家楼主为了考你们 不是考AI
6#
 楼主| gsyifan 发表于 2026-7-8 09:32 |楼主
tfrist 发表于 2026-7-8 07:03
你们这些人 人家楼主为了考你们 不是考AI

没事的,AI时代,就要研究如何应对新的技术和方式,我会继续做下一个保护供大神们测试。
7#
cc329533757 发表于 2026-7-8 12:47
凉游浅笔深画眉 发表于 2026-7-7 14:53
丢给AI一键就分析出来了啊。

用的是哪个ai啊?
8#
adld 发表于 2026-7-12 13:48
以目前AI的能力来说,搞定NET程序,以当前的市面上的强壳,搞定几乎只要用时间,都是降维打击。
我的收费软件让朋友来处理,我上的VMP【VMProtect Lite 3.10.4 Build 2668】,防反射、VMP函数虚拟化、核心函数虚拟化+变异。
最后还是被AI干了,这破的是真干净,和正常有卡密一样,我都好多暗桩,还是被补了。或者被JITHOOK移除。

9#
 楼主| gsyifan 发表于 2026-7-12 22:58 |楼主
adld 发表于 2026-7-12 13:48
以目前AI的能力来说,搞定NET程序,以当前的市面上的强壳,搞定几乎只要用时间,都是降维打击。
我的收费 ...

请关注此贴,我会在随后分享一个新的Check Me!
10#
SoftCracker 发表于 2026-7-13 01:17
adld 发表于 2026-7-12 13:48
以目前AI的能力来说,搞定NET程序,以当前的市面上的强壳,搞定几乎只要用时间,都是降维打击。
我的收费 ...

不可能吧?被脱壳反虚拟化了?把你的文件上传上来(最好把你加壳的文件和AI处理后上的文件都上传上来),我看你是不是没启用虚拟化
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

GMT+8, 2026-8-22 15:48

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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