CrackProof (三)Windows 分析

[!Error] 本贴仅研究壳子本身,不涉及游戏内容,请勿在游戏内进行作弊行为! 0x0 《内核级反作弊》 《请输入文本》 0x1 日志 参考了别的佬的文档 gist.github.com https://gist.github.com/WitherOrNot/0c2974be82393265aa5...
CrackProof (三)Windows 分析
CrackProof (三)Windows 分析

[!Error]
本贴仅研究壳子本身,不涉及游戏内容,请勿在游戏内进行作弊行为!

0x0

《内核级反作弊》

PixPin2026-04-2001-01-53
《请输入文本》
PixPin2026-04-2001-02-53

0x1 日志

参考了别的佬的文档

gist.github.com https://gist.github.com/WitherOrNot/0c2974be82393265aa5fb8f82be1f0a1 htpec.md
# Notes

These are very disorganized because I wrote these as I reversed various bits and pieces. If I'm wrong about something, please let me know so I can forget to update this.

## Logging

For me this was the Achilles' heel, Crackproof creates a debug log if a certain folder in `%temp%` is present. The folder name is 12 hex characters long, and different between executables. The easiest way to find it is to break on/hook `CreateFileW`.

Once you create it, any Crackproofed modules will spit out logs as they unpack. Lines contain status codes, indicating roughly what the unpacker is doing at a given time (see below). Lines with additional debug information are also included, sometimes they can be very handy.
此文件已被截断。 显示原始文件

对kernelbase.CreateFileW下断不难发现壳子在检查存放日志的文件夹是否存在,并且还会检查文件夹创建时间是否超过2天,否则不保存日志

PixPin2026-04-2005-47-56

创建文件夹以后正常启动一次游戏可以抓出以下的日志:

C:\priconner\sample\PrincessConnectReDive.exe
 -CF -C2 -E2 -CC -T12 -RC3 -GWH -DA -DD -PP2 -EDD -I -EL5 -CK2 -CD1 -CD2 -CD3 -CD4 -CD5 -EVS -DE -DEP1 -DC -PD -NCP -NCP2 -NC -NE -NCC2 -NRC -NDA3 -NDDE -NEL -NEL2 -NEL3 -NEL4 -NELA -NERR -NWEB -NGMD
2026/04/18 23:56:10.197
 00000001`40000000
200
 000 00001000 00000000 ._
410
510
520
540
560
2026/04/18 23:56:12.083
561
C00
 001 01A06658 01A037AB
C01
 001 0003 0003 0000 0000
C02
 001 Htsysm1B4001
 002 1 2 PrincessConnectRedive64_.sys
C03
 002 BCAB405E 36950000 03E8
 003 PrincessConnectRedive64_.sys
 004 0F000
C04
 005 000005A0 000005E8 000005E0
 003 03 00007FFC`21A9F7D0 ntdll.dll!LdrLoadDll
A09
A0F
A08
 009 1
A07
 002 01 00
5D0
552
570
 00C 005F
 00D 0000 0004 0000 0002 0002 0000 0000
 010 00000001`400AB760
590
5B0
5B1
598
5A0
A06  
5C0
5E1
5E2
610
640
655
6E1
800
 001 00000001`400BB000
810
820
840
 002 00000001`40052010 00000001`400B8D50 00000001`400B8C20
 001 04F0
6E2
E20
 014 003D20 1000 00000000`00000000
E40
 00A 00000001`400C6800 000001A0
 00B 0004 0002 0002 0001 0001 0000 0000
E52
E53
E54
E55
E59
660
280
2026/04/18 23:56:13.295
000-000-000
日志 解释 00000001`40000000 主exe的imagebase 200 壳子流程开始 000 00001000 00000000 ._ 映射外置的_RDATA段 410 开始反调试 510 检查某块内存的CRC32 520 检查NtQueryInformationProcess 540 检查是否存在VMWare后面 560 代码段解密初始化 561 选择加载子模块(新版本似乎已弃用 C00 检查Windows版本 001 01A06658 01A037AB 0x01A0+26200 = 01A06658,代表 Windows 11 25H2;0x01A0+14251 = 01A037AB,代表 Windows 10 Insider Preview Build 14251;也就是至少Windows 10才能运行 C01 检查 BCD 启动参数 001 0003 0003 0000 0000 意义不明 日志 解释 C02 安装驱动服务 001 Htsysm1B4001 驱动设备名称 002 1 2 PrincessConnectRedive64_.sys 1代表SERVICE_KERNEL_DRIVER,2代表SERVICE_AUTO_START,最后是驱动名称,在C:\Windows\System32文件夹下面

PixPin2026-04-2006-08-21

日志 解释 C03 NtLoadDriver 002 BCAB405E 36950000 03E8 意义不明 意义不明 驱动IOCTL返回值 003 PrincessConnectRedive64_.sys 驱动文件名 004 0F000 SizeOfImage 日志 解释 C04 安装hook(? 005 000005A0 000005E8 000005E0 意义不明 意义不明 意义不明 003 PrincessConnectRedive64_.sys 驱动文件名 03 03 00007FFC`21A9F7D0 ntdll.dll!LdrLoadDll LdrLoadDll所在地址 日志 解释 A09 反虚拟机,检测注册表路径里面是否以下面的字符串开头:Virtual,VMware,Bochs,VBOX,VRTUAL,Microsoft Hyper-V,Parallels(PD都检测是何意味) A0F 检查dll注入:AppInit_DLLs,User32.dll,apphelp.dll,ShimEng.dll(不检查dxgi,winhttp,version?????) A08 从磁盘拷贝干净的 ntdll/kernel32 代码到内存(hyperdbg:人尔女子) A07 杀注入线程

注册表

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SystemInformation\SystemProductName
HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\BIOS\SystemProductName
HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\SystemBiosVersion
日志 解释 5D0 解密前置流程,handler为0x14005B694 552 解密前置流程,handler为0x140059AB5 570 解密前置流程,handler为0x14005A122 590 解密前置流程,handler为0x14005A5DC 5B0 解密前置流程,handler为0x14005A3E4 5B1 解密前置流程,handler为0x14005A728 598 解密前置流程,handler为0x14005AA0E 5A0 解密前置流程,handler为0x14005AAB3 A06 意义不明 5C0 运行时恢复模块的入口点(? 5E1 解密前置流程,handler为0x14005B483 5E2 解密前置流程,handler为0x14005B523 610 解密PE头 640 解密各个段 655 PE重定位 6E1 疑似收尾工作,似乎在重新加密部分text段 E55 检测openark驱动的设备名,检测到就自杀 E91 检测CE驱动的设备名,检测到就自杀 日志 解释 046-019-01F 内核驱动回调被禁用 048-012-002 检测到hyper-v虚拟机 601-000-008 检测到CE

PixPin2026-04-2006-28-18

0x2 动态解密

这部分仅针对公主连结Re:Dive DMM,Cygames就喜欢搞这种看似有用实际上完全没用的东西(笑
别的exe上面的crackproof没有这个功能,例如学园偶像大师DMM的内存就是正常的

PixPin2026-04-2007-29-02

00007FFA5E889000为例,CE可以看到这里的内存属性为PAGE_NOACCESS

PixPin2026-04-2006-30-08
当CPU试图执行里面的指令就会触发异常

EXCEPTION_DEBUG_INFO:
           dwFirstChance: 1
           ExceptionCode: C0000005 (EXCEPTION_ACCESS_VIOLATION)
          ExceptionFlags: 00000000
        ExceptionAddress: gameassembly.00007FFA5E889000
        NumberParameters: 2
ExceptionInformation[00]: 0000000000000008 DEP Violation
ExceptionInformation[01]: gameassembly.00007FFA5E889000 Inaccessible Address

这里会调用ntdll.KiUserExceptionDispatcher处理异常

ntdll.KiUserExceptionDispatcher 被壳 inline hook

00007FFB685E5AE0  E9 2B051000        jmp 00007FFB686E6010

跳到 ntdll .reloc 里面:

00007FFB686E6010  FF25 F2FFFFFF      jmp qword ptr [00007FFB686E6008]

[7FFB686E6008] 再跳到壳里面

→ 7FF6AD977C00:ntdll.KiUserExceptionDispatcher hook dispatcher

7FF6AD977C00  8B0D 72210000      mov ecx, dword ptr [7FF6AD979D78] // 栈偏移
7FF6AD977C06  48 03 CC           add rcx, rsp // rcx = rsp + offset 得到一个与当前异常恢复相关的结构指针,后续按 EXCEPTION_RECORD 布局读取。
7FF6AD977C09  48 83 EC 20        sub rsp, 20h
7FF6AD977C0D  E8 AEDEFFFF        call 7FF6AD975AC0 // 从异常记录里取 fault address
7FF6AD977C12  85 C0              test eax, eax
7FF6AD977C14  74 15              jz 7FF6AD977C2B
7FF6AD977C16  E8 F5DFFFFF        call 7FF6AD975C10 // 处理异常
7FF6AD977C1B  85 C0              test eax, eax
7FF6AD977C1D  74 0B              jz 7FF6AD977C2A
7FF6AD977C1F  48 83 C4 20        add rsp, 20h
7FF6AD977C23  48 8B CC           mov rcx, rsp
7FF6AD977C26  33 D2              xor edx, edx
7FF6AD977C28  FF D0              call rax // 返回KiUserExceptionDispatcher
7FF6AD977C2A  CC                 int3
7FF6AD977C2B  E8 B0DFFFFF        call 7FF6AD975BE0
7FF6AD977C30  48 83 C4 20        add rsp, 20h
7FF6AD977C34  FF E0              jmp rax // 返回KiUserExceptionDispatcher

→ 7FF6AD975AC0:从异常记录取 ExceptionAddress / ExceptionInformation[1]

7FF6AD975B0C  48 8B 77 28        mov rsi, qword ptr [rdi+28h]
7FF6AD975B10  48 8B 7F 10        mov rdi, qword ptr [rdi+10h]
...
7FF6AD975B41  8B CE              mov ecx, esi
7FF6AD975B43  E8 98FDFFFF        call 7FF6AD9758E0
...
7FF6AD975BA4  48 8B D7           mov rdx, rdi
7FF6AD975BA7  48 8B CE           mov rcx, rsi
7FF6AD975BAA  E8 D1FEFFFF        call 7FF6AD975A80

对应 EXCEPTION_RECORD 布局

ExceptionRecord + 0x10 = ExceptionAddress
ExceptionRecord + 0x28 = ExceptionInformation[1]

→ 7FF6AD975A80:7FF6AD975910的wapper

→ 7FF6AD975910:定位 fault address 所属保护区域和页

7FF6AD975942  48 8D 44 24 30     lea rax, [rsp+30h]
7FF6AD975947  4C 8D 4C 24 68     lea r9,  [rsp+68h]
7FF6AD97594C  4C 8D 44 24 60     lea r8,  [rsp+60h]
7FF6AD975951  49 8B D4           mov rdx, r12
7FF6AD975954  48 8B CF           mov rcx, rdi
7FF6AD975957  48 89 44 24 20     mov [rsp+20h], rax
7FF6AD97595C  C7 44 24 60 0...   mov dword ptr [rsp+60h], 0
7FF6AD975964  C7 44 24 68 0...   mov dword ptr [rsp+68h], 0
7FF6AD97596C  C7 44 24 30 0...   mov dword ptr [rsp+30h], 0
7FF6AD975974  E8 F7F6FFFF        call 7FF6AD975070            // 查找 fault address 属于哪个受保护区域
7FF6AD975979  48 8B 0D ...       mov rcx, qword ptr [7FF6AD979D60]
7FF6AD975980  0F BA 69 04 1B     bt dword ptr [rcx+4], 1Bh   // 打印日志的开关
7FF6AD975985  48 8B D8           mov rbx, rax
7FF6AD975988  73 4F              jnb 7FF6AD9759D9
7FF6AD9759D9  48 85 DB           test rbx, rbx               // 匹配到的区域描述符
7FF6AD9759DC  74 4B              jz 7FF6AD975A29
7FF6AD9759DE  83 7C 24 60 00     cmp dword ptr [rsp+60h], 0  // 是是否需要解密
7FF6AD9759E3  74 44              jz 7FF6AD975A29
7FF6AD9759E5  FF15 ...           call qword ptr [<&GetTickCount>]
7FF6AD9759EB  8905 ...           mov dword ptr [7FF6AD979D50], eax
...
7FF6AD975A19  2B 7B 08           sub edi, dword ptr [rbx+8]
7FF6AD975A1C  48 8B CB           mov rcx, rbx
7FF6AD975A1F  C1 EF 0C           shr edi, 0Ch
7FF6AD975A22  8B D7              mov edx, edi                // edi = (fault_address - region_base) >> 12 得到 page index
7FF6AD975A24  E8 67F1FFFF        call 7FF6AD974B90           // page handler

→ 7FF6AD975070:查找区域和页描述符

7FF6AD9750AA  44 8B 1D ...       mov r11d, dword ptr [7FF6AD979D6C] // region count
7FF6AD9750C6  48 8D 0D ...       lea rcx, [7FF6AD978D50]            // region table

loop:
7FF6AD9750D0  48 8B 11           mov rdx, qword ptr [rcx]
7FF6AD9750D3  4C 8B 42 08        mov r8, qword ptr [rdx+8]          // region base
7FF6AD9750D7  49 3B D8           cmp rbx, r8                        // fault < base?
7FF6AD9750DA  72 0C              jb next
7FF6AD9750DC  48 63 42 10        movsxd rax, dword ptr [rdx+10h]    // region size
7FF6AD9750E0  49 03 C0           add rax, r8
7FF6AD9750E3  48 3B D8           cmp rbx, rax                       // fault < base+size?
7FF6AD9750E6  72 39              jb found

找到所属区域后:

7FF6AD97514D  2B 5A 08           sub ebx, dword ptr [rdx+8]
7FF6AD975150  C1 EB 0C           shr ebx, 0Ch                       // page index
7FF6AD975153  3B 5A 24           cmp ebx, dword ptr [rdx+24h]       // page count
7FF6AD975158  48 63 42 20        movsxd rax, dword ptr [rdx+20h]    // page table offset
7FF6AD97515C  8B CB              mov ecx, ebx
7FF6AD97515E  48 C1 E1 04        shl rcx, 4                         // page_desc size = 0x10
7FF6AD975162  48 03 C8           add rcx, rax
7FF6AD975165  48 03 4A 08        add rcx, qword ptr [rdx+8]         // page_desc VA
7FF6AD975169  8B 01              mov eax, dword ptr [rcx]
7FF6AD97516B  0F BA E0 18        bt eax, 18h
7FF6AD975171  C1 E8 10           shr eax, 10h
7FF6AD975174  83 E0 01           and eax, 1
7FF6AD975177  89 45 00           mov dword ptr [rbp], eax

推断结构体:

struct region_desc {
    uint32_t flags;       // +0x04 也有全局/区域标志
    uint64_t base;        // +0x08
    uint32_t size;        // +0x10
    ...
    uint32_t page_off;    // +0x20
    uint32_t page_count;  // +0x24
    HANDLE   mapping;     // +0x28
    uint32_t decrypt_cnt; // +0x34
};

struct page_desc {
    uint32_t flags;       // +0x00
    uint32_t key_part;    // +0x04
    uint32_t last_tick;   // +0x08
    uint16_t hit_count;   // +0x0C
    uint16_t checksum;    // +0x0E
};

→ 7FF6AD974B90: 解密准备工作

7FF6AD974BC1  BF 00000100        mov edi, 10000h
...
7FF6AD974BEE  48 8B 4B 28        mov rcx, qword ptr [rbx+28h]
7FF6AD974BF2  41 8D 50 06        lea edx, [r8+6]
7FF6AD974BF6  FF15 ...           call qword ptr [<&MapViewOfFile>]
7FF6AD974BFC  48 63 7B 20        movsxd rdi, dword ptr [rbx+20h]
...
7FF6AD974C08  49 8B E8           mov r12, rax                      // mapped view

通过 MapViewOfFile 映射待处理页的数据,保存在r12

7FF6AD974C0F  48 C1 E1 04        shl rcx, 4
7FF6AD974C13  48 03 F9           add rdi, rcx
7FF6AD974C16  48 03 7B 08        add rdi, qword ptr [rbx+8]

page_desc = region_base + page_table_offset + page_index * 0x10

7FF6AD974C22  8B05 ...           mov eax, dword ptr [7FF6AD979D50]
7FF6AD974C28  81 27 FFFF FEFF    and dword ptr [rdi], 0FFFEFFFFh
7FF6AD974C2E  66 83 47 0C 01     add word ptr [rdi+0Ch], 1
7FF6AD974C33  89 47 08           mov dword ptr [rdi+8], eax
7FF6AD974C36  83 43 34 01        add dword ptr [rbx+34h], 1

意义不明

7FF6AD974C3A  8B C5              mov eax, ebp
7FF6AD974C3C  41 2B C6           sub eax, r14d
7FF6AD974C3F  BA 00100000        mov edx, 1000h
7FF6AD974C44  48 63 F0           movsxd rsi, eax
7FF6AD974C47  8B 43 08           mov eax, dword ptr [rbx+8]
7FF6AD974C4A  44 8D 44 05 00     lea r8d, [rbp+rax]
7FF6AD974C4F  49 03 F4           add rsi, r12
7FF6AD974C52  44 33 47 04        xor r8d, dword ptr [rdi+4]
7FF6AD974C56  48 8B CE           mov rcx, rsi
7FF6AD974C59  E8 52FBFFFF        call 7FF6AD9747B0

key = (page_va_related_value + region_base_low32) ^ page_desc->key_part

7FF6AD974C96  0F BA 27 14        bt dword ptr [rdi], 14h
7FF6AD974C9A  73 48              jnb 7FF6AD974CE4
7FF6AD974C9C  45 8B C5           mov r8d, r13d
7FF6AD974C9F  BA 00100000        mov edx, 1000h
7FF6AD974CA4  48 8B CE           mov rcx, rsi
7FF6AD974CA7  E8 84FBFFFF        call 7FF6AD974830

二次解密(?

7FF6AD974CE4  48 63 CD           movsxd rcx, ebp
7FF6AD974CE7  4C 8D 4C 24 68     lea r9, [rsp+68h]
7FF6AD974CEC  BA 00100000        mov edx, 1000h
7FF6AD974CF1  48 03 4B 08        add rcx, qword ptr [rbx+8]
7FF6AD974CF5  41 B8 20000000     mov r8d, 20h
7FF6AD974CFB  C7 44 24 68 0...   mov dword ptr [rsp+68h], 0
7FF6AD974D03  FF15 ...           call qword ptr [<&VirtualProtect>]

把page权限改成可执行,然后清理现场
VirtualProtect(region_base + page_index_or_offset, 0x1000, PAGE_EXECUTE_READ, &oldProtect)

→ 7FF6AD9747B0: 主解密函数

7FF6AD9747BF  44 8B D8           mov ebx, r8d
7FF6AD9747C2  8B F2              mov esi, edx
7FF6AD9747C4  48 8B F9           mov rdi, rcx
...
7FF6AD9747DC  C1 EE 02           shr esi, 2
7FF6AD9747DF  41 C1 E0 10        shl r8d, 10h
7FF6AD9747E3  33 D2              xor edx, edx
7FF6AD9747E5  44 33 C3           xor r8d, ebx
7FF6AD9747EA  45 8B C8           mov r9d, r8d

loop:
7FF6AD9747F0  8B 0F              mov ecx, dword ptr [rdi]
7FF6AD9747F2  44 03 C2           add r8d, edx
7FF6AD9747F5  83 C2 01           add edx, 1
7FF6AD9747F8  41 C1 C0 03        rol r8d, 3
7FF6AD9747FC  8B C1              mov eax, ecx
7FF6AD9747FE  48 83 C7 04        add rdi, 4
7FF6AD974802  41 33 C1           xor eax, r9d
7FF6AD974805  44 8B C9           mov r9d, ecx
7FF6AD974808  41 33 C0           xor eax, r8d
7FF6AD97480B  3B D6              cmp edx, esi
7FF6AD97480D  89 47 FC           mov dword ptr [rdi-4], eax
7FF6AD974810  7C DE              jl loop
void decrypt(uint32_t *buf, uint32_t size, uint32_t key)
{
    uint32_t count = size >> 2;
    uint32_t state = (key << 16) ^ key;
    uint32_t prev = state;

    for (uint32_t i = 0; i < count; i++) {
        uint32_t enc = buf[i];
        state = rol32(state + i, 3);
        buf[i] = enc ^ prev ^ state;
        prev = enc;
    }
}

0x3 过保护

停用ProcessObCallback PREProcessObCallback POST内核回调就能随意attach/inject/dump了,想干啥就干啥(笑

0x4

前置解密就不研究了,隔着在里面block套block,解密出来一个block跳进去跑,再解密一个block出来再跳进去跑,还会把上一个block加密回去,各种间接跳转用的飞起,trace日志随随便便就1GB了,给我逆红温了,压力codex这货也看不明白 :tieba_006:

0xF 总结

非常垃圾的内核反作弊,完全过时的技术,只能看到吃老本的技术,看不到一点创新:
不检查PatchGuard,不检查Driver Signature Enforcement,不检查dll劫持,不检查堆栈回调,不过滤日志,混淆力度约等于0,隔壁ACE都在VT猛猛发力了,这玩意还在ring3吃老本
大名鼎鼎的capcom.sys(Htsysm72FB)驱动任意读写内核也是crackproof的《杰作》,允许任何进程运行带有未认证IOCTL的内核模式shellcode,内核里面还敢这样玩也是无敌了

089c358d7c0734ed54bb41593bfeba63.jpeg

1 个帖子 - 1 位参与者

阅读完整话题

来源: linux.do查看原文