吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2534|回复: 55
收起左侧

[Android 原创] 加固的 Unity 儿童教育(**巴士学汉字) APP:脱壳、签名校验三连、热更 DLL 硬刚

  [复制链接]
Cencrack 发表于 2026-8-16 19:56
本帖最后由 Cencrack 于 2026-8-17 08:02 编辑

【逆向原创】折腾一周多,扒了个加固的 Unity 儿童教育 APP:脱壳、签名校验三连、热更 DLL 硬刚,全过程记录

目标:**巴士学汉字(com.sinyee.babybus.homeland 9.91.22.31)
难度:★★★★☆(加固 + IL2CPP + HybridCLR 热更 + 自研安全层 + 反调试,五件套齐活)
声明:全程只在自己的测试设备上折腾,仅供技术学习研究,请勿用于任何商业用途。文中不提供成品下载。


潜水好几年了,一直都是白嫖各位大佬的帖子,这回自己折腾了个硬骨头,从头到尾踩了无数坑,感觉不写下来对不起自己熬的这几个通宵。

先交代下背景。家里娃天天用这个 APP 学汉字,用着用着我职业病就犯了:这玩意是 Unity 的,还带着加固,内部到底怎么架构的?会员弹窗那些逻辑写在哪儿?能不能自己改改?就这么入的坑,没想到一折腾就是一个多星期。

废话不多说,直接上干货。

一、摸底:先搞清楚对面是什么货色

老规矩,jadx 先开一刀。好家伙,六千多个类,翻了半天全是各种 SDK:腾讯的、友盟的、exoplayer……游戏逻辑一行都没有。这种结构基本可以断定是 Unity IL2CPP,Java 层就是个空壳。

再翻 lib 目录,libjiagu_64.so 躺在那儿——加固了。用 MT 管理器看了下特征,360 的。这玩意不处理掉,后面改什么都会被签名校验拦住。

然后是关键一步,翻 assets。在 assets/app-launch/ 下面发现一个 27MB 的 hotfix.dll.bytes,拖进 010 editor 一看,这 TM 就是个明文 PE!.NET 程序集的头清清楚楚。

搞过 Unity 的应该知道这意味着什么——HybridCLR 热更。也就是说这游戏的大部分 C# 业务逻辑根本没编译进 libil2cpp.so,而是以一个可随时下发的 DLL 形式存在,游戏跑起来再加载。而且这个 DLL 没有加密,直接能反编译。

当时我就意识到,主战场找到了。后面证明这个判断救了我至少一周时间。

顺手把目标也定了:免登录、去掉各种会员拦截弹窗、学习设置那些 VIP 门禁放开。先说清楚一点,真正的会员权益在服务端(内容下载、账号体系都是服务器说了算),客户端能做的是把 UI 层的门禁全放开。这个预期要先摆正,不然后面会失望。

二、第一波翻车:反调试教我做人

本来想按老套路来:frida 挂上去 hook 一波看看调用链。结果 game 一启动就自杀,logcat 里捞到最后两行日志:

BabyBusSafe AntiDebug checkIsPortUsing 27042 true

好家伙,直接检测 frida 默认端口。这还算客气的,后来发现它还扫描 /data/local/tmp 下的可执行文件、检测 frida 进程特征,命中任何一条直接 raise(SIGKILL) 自毁。

跟它斗了几个回合,总结出活命姿势:

adb push fs-arm /data/local/zz1          # 改个不起眼的名字,别放 /data/local/tmp
adb shell su -c "chmod 755 /data/local/zz1"
adb shell su -c "nohup /data/local/zz1 -l 127.0.0.1:27100 >/dev/null 2>&1 &"
adb forward tcp:27100 tcp:27100

这样确实能续命做侦察。但还有个特别阴的坑要提醒大家:它连 23946 端口都查——这是 IDA android_server 的默认端口!我有次调试完没杀 android_server 进程,第二天测试怎么都跑不起来,一度怀疑自己 APK 改坏了,查了半天是这玩意在后台 LISTEN 着被扫到了。排查方法是把 /proc/net/tcp 里的 LISTEN 端口全捞一遍,按 inode 去各进程的 fd 里找占用者,见一个杀一个:

# 按 inode 找端口占用者的思路(写成 sh push 上去跑,别在 PowerShell 里内嵌,转义能把你逼疯)
# 1) grep ' 0A ' /proc/net/tcp   →  拿到 LISTEN 端口的十六进制端口号和 inode
#    比如 5D8A:0000000000000000000000000000000000000000:0A ... socket:[123456]
#    0x5D8A = 23946,就是 android_server
# 2) 遍历 /proc/*/fd/ 找 "socket:[123456]" → 锁定 pid
# 3) su -c 'kill -9 <pid>',杀完复查一遍,有 re-bind 现象,inode 会变

结论先给:frida 只能用来定位逻辑,交付千万别指望它。成品要能在任何机器上裸跑,所有修改都得静态打进去。

三、弯路:改本地配置文件,被服务器教做人(之二)

中间还走过一条弯路,记录一下给后来的兄弟避坑。

这游戏用 MMKV 存本地配置,里面有个 global_config_V2,存着各种弹窗开关、免费次数限制。我用 python 解了 MMKV 格式(注意它的 CRC 在独立的 .crc 文件里,改了主文件必须同步更新 CRC,不然判定损坏直接清空),把弹窗开关全置 false,免费次数改大。

CRC 这块顺手把代码放出来,格式是 [size:u32][magic=0x07ffffff][条目...],条目是 varint 长度 + key + varint 长度 + value,.crc 文件的 offset 0 是主 CRC、offset 28 是 actualSize、offset 36 是备份 CRC:

import zlib, struct

def fix_crc(main_path, crc_path):
    main = bytearray(open(main_path, 'rb').read())
    size = struct.unpack('<I', main[:4])[0]
    # 主 CRC = crc32(主文件 [4 : 4+size]),注意跳过开头的 size 字段
    crc = zlib.crc32(bytes(main[4:4+size])) & 0xffffffff
    crcf = bytearray(open(crc_path, 'rb').read())
    crcf[0:4]  = struct.pack('<I', crc)     # 主 CRC
    crcf[36:40] = struct.pack('<I', crc)    # 备份 CRC(0x24 处)
    open(crc_path, 'wb').write(crcf)

本地验证,生效了。重启,没了。

抓包一看,得,这游戏每次启动都从 api-config.babybus.com 拉一遍配置覆盖本地。想保住修改得 iptables 把这个 IP DROP 掉——但普通用户设备哪来的 root?这条路走到头了。

教训:改本地存档之前,先确认服务器会不会回写。不会回写的存档才是好存档。

四、正戏:从明文 DLL 到成品,中间隔着三座大山

思路其实很直:把 hotfix.dll.bytes 拖出来,dnSpy(或者自己写工具)改 IL,塞回 APK,重签名,收工。

第一枪打出去就哑火了:改完 DLL 回填重签,装上一跑,黑屏闪退。

logcat 一看,JNI_OnLoad 返回 -1,壳把进程杀了。360 加固校验 APK 签名,重签之后签名变了,壳直接不干了。

这就是本文真正的题目了:怎么跨过去。

4.1 先试了硬刚壳,阵亡

用 IDA 分析壳 SO(OLLVM 平坦化,1118 个函数,看得眼睛瞎掉),把签名校验链摸了个大概:

  • JNI_OnLoad 里调 getPackageInfo 拿签名 DER
  • 签名 DER 参与派生 DEX 解密密钥(一个 80 字节的密钥流)
  • 然后解密壳内加密的 DEX

我试了各种 patch:NOP 掉校验分支、在数据段伪造原始签名指针……最好的一次成绩是进程活了但黑屏——因为签名错了,派生出来的 DEX 解密密钥也错了,解出来一堆乱码。

这才想明白:DEX 密钥和签名是绑死的。重签之后密钥必然错,除非你能同时伪造签名获取和密钥派生两条链,还得在 OLLVM 混淆里抠对每一处校验。这条路投入产出比太低了,果断放弃。

修壳不如脱壳。

4.2 脱壳重建

脱壳工具试了一圈,最后 BlackDex 搞定(frida-dexdump 之类的会被前面说的反调试拦)。在云手机上跑 BlackDex,dump 出来 31 个 DEX。

脱出来的 DEX 有个特点:大部分类是好的,但有一部分方法被"抽取"了——方法声明还在,实现变成了 native。这是抽取式壳的常规操作,运行时才回填。这类方法得靠自己重写(后面细说)。

重建流程:

  1. apktool 反编原包(保留资源和 manifest)
  2. 删掉壳的一切痕迹(壳 so、stub dex、appComponentFactory 等)
  3. 脱壳 DEX 排好序回填 classesN.dex
  4. manifest 里把加固的 Application 换回真实入口
  5. 重签

到这一步,APK 能装了,能进启动页——然后死在加载 Unity 的路上。logcat 满屏的报错,三个大坑排着队,一个一个填。

4.3 第一座山:PSK 校验(本文技术含量最高的一段)

死因日志:

BabybusSecurity: Can not extract PSK Server secured
AppStartupster.GetLaunchProcedure() JSON parse error

大概逻辑是:游戏有一层自研安全(libbbc.so),用某个 PSK 当密钥解密服务器下发的启动流程数据,解不出来就异常,最后走到 UnityPlayer.kill() 自杀。

问题来了:这个 PSK 是哪来的?

上 IDA 硬啃 libbbc.so(也是混淆的,但比壳好啃)。跟了半天,找到一条链:

  1. 有个函数解析 APK 自己的 v2 签名块(搜 APK Sig Block 42 魔法字符串定位的,这里卡了小半天)
  2. 从签名块里把签名证书 DER 抠出来
  3. 对证书 DER 做 SHA-256
  4. 32 字节的哈希逐字节转 hex,得到 64 字符的 PSK 字符串

也就是说:PSK = hex(SHA256(v2签名块的证书 DER)),和签名死绑。我重签了名,证书变了,PSK 就变了,服务端数据解密自然失败。

一开始我还想确认输入到底是整张证书还是公钥,光看反汇编吃不准。上 frida(用前面说的活命姿势)hook 了 SHA256 的入口,把输入 dump 出来:

// hook 通用 SHA256 函数(sub_D2784),dump 每次调用的输入
var sha = base.add(0xD2784);
Interceptor.attach(sha, {
    onEnter: function (args) {
        var len = args[1].toInt32();
        var head = args[0].readByteArray(Math.min(len, 48));
        send('[SHA256_IN] len=' + len);
        send(hexdump(head));   // frida 17.x 没有 Memory.readByteArray,用 ptr.readByteArray
    }
});

输出一贴出来真相大白:

[SHA256_IN] len=855
  00000000  30 82 03 53 30 82 02 3b a0 03 02 01 02 02 08 0e  0..S0..;........
  00000010  7e a4 46 46 0f 18 06 30 0d 06 09 2a 86 48 86 f7  ~.FF...0...*.H..
  ...

开头 30 82 03 53,标准证书 ASN.1 SEQUENCE 头,总长 0x357,跟我重签证书的 DER 长度严丝合缝——石锤,输入就是整张证书 DER,不是公钥。

(顺带说个 JNI 的坑:hook 里要调 Java 的 native 函数表时,JNI 表槽位存的是函数指针,NativeFunction(tbl.add(off)) 会把槽位地址当代码执行直接 access violation,必须先 .readPointer() 取出真函数地址。我在这上面浪费了一个小时。)

解法很暴力也很有效:既然 PSK 是从证书算出来的,那我在 so 里塞一张原版证书,让计算函数永远读这张

具体操作:从原版 APK 提取证书 DER,写进 libbbc.so 的只读代码段 padding(我塞在 0x5bf40),然后把 PSK 生成函数里两处给 SHA256 传参的指令序列 patch 成 ADRP+ADD 指向固定证书+MOVZ 传长度

patch 完写了个自检,把前后指令都 capstone 反汇编出来对了一眼:

libbbc.so sub_F90A4 内(PSK 生成器,两处 SHA256 传参序列之一):
  adrp x8, #0x1c9000
  add  x8, x8, #0xe70           ; 原来指向动态解析的签名数据 → 改成指向 0x5bf40 的固定证书
  mov  x0, x8                   ; SHA256 输入指针,编码见下面坑二
  movz w1, #0x253               ; 输入长度写死成证书 DER 长度

顺手把编码自检的输出也贴上,这个环节救了我一命:

>>> capstone_disasm(bytes.fromhex('e00308aa'))
'mov x0, x8'     # 正确编码 0xAA0803E0
>>> capstone_disasm(bytes.fromhex('e00300aa'))
'mov x0, x0'     # ← 我第一版手编的,纯 no-op!就差一个寄存器位

这里有两个血泪坑,说给你们听:

  • 坑一:证书千万别写进 RW 数据段/.bss!我第一版就写错了地方,运行时那段内存被游戏全局变量覆盖成指针数组,等于白做。frida 上去一看运行时字节全变了才发现。一定要找 R-X 段的 padding。
  • 坑二:arm64 手工编码,mov x0, x8 我编成了 0xAA0003E0,实际上是 0xAA0803E0——前者是 mov x0,x0,纯 no-op。就这一个 bit 的差别,白跑一整轮测试。手工编码的指令必须 capstone 反汇编回验,血的教训。

patch 完,PSK 错误消失,server data 正常解密。

4.4 第二座山:完整性校验白名单

PSK 过了,还是被杀。又一层:游戏 C# 逻辑里有个 IntegrityCheck,从 IL2CPP 的 global-metadata.dat 里读一组硬编码的渠道签名证书 MD5 白名单(六七个渠道包各一个),当前签名不在白名单里就 kill。

这个好办,metadata 是二进制但字符串明文,直接把六个旧 MD5 等长替换成我现在签名证书的 MD5,收工。

(IL2CPP 自身还有个 metadata checksum 校验,这个在之前折腾的时候已经绕了,这里不展开。)

4.5 第三座山:被抽取的方法体

过了前两关,游戏能进大厅了,点某个功能闪退。一看崩溃栈,UnsatisfiedLinkError——某个 Activity 的 onCreate 被壳抽成了 native,脱壳后没人回填,调用就炸。

处理办法:baksmali 反编译对应 dex,把这个方法重写成 Java 逻辑(super.onCreate() + 把它原本该调的初始化补上)。原本的逻辑怎么确定?全库 smali 搜这个类的调用点,交叉佐证。这种抽取损伤脱壳后都要人工过一遍。

到这里,一个能跑的脱壳重打包版本终于落地了。从硬刚壳失败到这会儿,大概花掉了我一半的时间。

五、回到主战场:热更 DLL 的 IL patch

架子能跑了,改业务就轮到开篇说的那个明文热更 DLL 了。

这里我要吹一下自己踩的第三个大坑。一开始我用 dnfile 直接解析这 27MB 的 DLL,直接卡死——13 万个方法,dnfile 默认全表解析,BlobHeap 读取的时候有个尾部切片操作是 O(N²) 的,内存干到 1.3GB,CPU 跑几十秒出不来。我一度以为是死循环,faulthandler dump 了栈才知道是算法爆炸,卡在 stream.py 的切片上。

解决办法:clr_lazy_load=True 加上只读 raw struct 字段,2 秒索引完毕。13 万方法全建索引,类名方法名随便查:

import dnfile

pe = dnfile.dnPE('hotfix.dll.bytes', clr_lazy_load=True)  # 关键参数!
T = pe.net.mdtables
# 用 TypeDef.MethodList_Index 的区间法建 method -> type 映射
# 然后遍历 MethodDef 读 struct.Rva / struct.Name_StringIndex
# 千万别触发 full_loader 的慢路径,一碰就是几分钟
def find(cls, meth):
    for idx, tname, mname, rva in methods:
        if tname.endswith(cls) and mname == meth:
            print('%s::%s rva=0x%x' % (tname, mname, rva))

然后就是愉快的(并不) IL patch 时间。

门禁类方法,比如 IsLoginAccountIsVipIsMember 这些,全部 patch 成恒 true。拿 IsVip 举例,patch 前反汇编出来是这样:

// GameProxy::IsVip  (rva=0x935d4, tiny 头方法,共 37 条指令,节选)
ldarg.0
call     instance class StorageAccountDataProxy GameProxy::get_accountDataProxy()
brtrue.s  IL_0005
ldc.i4.0
ret
IL_0005:
...

patch 后就剩俩字节:

ldc.i4.1    // 0x17
ret         // 0x2A

具体替换的时候是字节级 in-place 写回(新旧方法体一样长,PE 布局不动),tiny 头重算 code_size:(new_size << 2) | 0x02

拦截类方法,比如弹登录窗的 ShowLogin,直接 ret 置空。

这里有几个细节必须说一下,都是实测踩出来的:

  1. 方法头的 tiny/fat 格式。tiny 头是 (code_size << 2) | 0x02,我第一版漏了 |0x02,生成了非法头。判别方法看第一个字节的低两位:&0x3 == 2 是 tiny,== 3 是 fat。别用 b0 < 0xff 这种想当然的判断,会把 fat 头误判成 tiny。
  2. 改完必须全量回验。我写完 patch 会用 dnfile 把 13 万个方法全部重新解析一遍,0 bad 才算过。再抽查 patch 点前后的邻接方法反汇编正常,防止写坏了邻居家。
  3. 栈平衡。有个验证码判断的逻辑,我把条件跳转 bne.un(5 字节)NOP 掉,结果点击全被判错。后来才反应过来:那个跳转前面压了两个值在栈上,光 NOP 跳转,栈上俩值没消费,后面全乱了。正确姿势是补两个 pop(0x26)再 NOP 填充。
  4. 设备的 DLL 缓存不随重装刷新!这个坑极其隐蔽:游戏会把 APK 里的热更 DLL 拷到 /sdcard/Android/data/<pkg>/files/app-launch/_cached/ 缓存起来,只有版本号变化或缓存缺失才重新拷。我改了 APK 里的 DLL 装上去,游戏用的还是旧缓存,一度以为 patch 写错了。改完必须覆盖缓存文件(注意 chown 回游戏用户的 uid 和 chmod 660)或者干脆 pm clear。

改哪些方法也有讲究。别去挨个找弹窗,去找源头判断——比如所有 VIP 拦截最终都汇聚到几个 IsVip/IsMember,改源头一处顶六十多处调用点。搜方法引用的时候拿通知字符串搜(比如 "appcmd_open_login"),触发链一目了然。

对了,这游戏的家长验证是两套独立系统:Java 层一个 NumberVerifyActivity(smali 里方法头插 const/4 v0,0x1; return v0 完事),Unity 热更层又一个 Widget(IL patch),都得处理,改一边没用。

六、临门一脚:把热更锁死,不然全白干

折腾到这,成品功能都正常了。但我心里总不踏实:这游戏是热更架构啊,哪天官方推个新版本热更 DLL,把我 patch 过的 DLL 顶掉,一切归零。

热更管理逻辑不在热更 DLL 里(它自己不能管理自己),在 AOT 层的 libil2cpp.so。用 Il2CppDumper 出 dump.cs,搜 CheckUpdate 相关,定位到 MainSubAppCtrl.CheckUpdateFromServer——一个 async 状态机。

然后干了一件我觉得还挺优雅的事:拿 capstone 反汇编它的 MoveNext,把所有 BL 调用目标用 script.json 的地址表翻译成方法名(27 万个方法,写个 bisect 查询),调用链瞬间可读:

# 翻译 BL 调用的核心逻辑,就这么几行
import bisect
addrs = sorted(int(str(m['Address'])) for m in sj['ScriptMethod'])   # 注意是十进制!
names = {int(str(m['Address'])): m['Name'] for m in sj['ScriptMethod']}
def resolve(a):
    if a in names: return names[a]
    i = bisect.bisect_right(addrs, a) - 1
    return names[addrs[i]] + '+0x%x' % (a - addrs[i])

翻译出来的输出(节选,一眼看穿逻辑):

0x02d77e08  bl -> System.Version$$.ctor
0x02d77e28  bl -> System.Version$$.ctor
0x02d77e38  bl -> System.Version$$op_LessThanOrEqual
0x02d77e3c  tbz w0, #0, #0x2d77fa0     ; false 跳"已是最新"路径
...
0x02d78150  bl -> App.Main.MainSubAppUpgrader$$.ctor   ; true 就走这里,开下载
0x02d78358  bl -> AsyncTaskMethodBuilder$$SetResult

版本比较全包就这一处,其他入口都是转发。那还说什么,patch 它:

BL op_LessThanOrEqual  (0x94C62D9B)
        ↓ 4 字节替换
MOVZ W0, #0            (0x52800000)  ← 恒 false

patch 完的指令流,capstone 回验:

0x02d77e38  mov w0, #0
0x02d77e3c  tbz w0, #0, #0x2d77fa0   ; w0 恒 0,必跳

一条指令,比较结果永远是"本地最新",游戏永远走原生的无更新分支——注意我说的是原生分支,游戏没更新的时候天天走的路,不是我编的路径,所以流程完整性有保证。

这里特别提醒:别跳过整个检查函数。async 方法直接 patch 成 return 的话,Task 没完成,上游 await 永久挂起,游戏卡死在启动里。也别想着 iptables 屏蔽热更域名,普通用户设备没 root,而且会误伤正常内容下载。

还有两个 Il2CppDumper 的坑顺手记一下:script.json 里的地址是十进制(我当成十六进制解析,方法全对不上,愣了半天);dump.cs 里的 RVA 不是文件偏移,同一行的 Offset 字段才是(差 0x1000)。

七、成品收尾

最后几步收尾工作,别嫌琐碎,都是交付质量:

  • 去调试桩。分析过程中往 smali 里插了一堆 logcat 桩,正式版得去掉。我的做法是留着插桩前的基线 dex,逐类 diff 回滚(diff 之前要做 label 归一化,不然跳转标签编号差异能淹死你)。
  • 砍 ABI。我只 patch 了 arm64 的 so,32 位的 so 没有 PSK patch,32 位设备装上必闪退。干脆把 armeabi-v7a 整个删了——装不上报"不兼容",好过装上黑屏。顺手 APK 从 264MB 瘦到 216MB。
  • 签名统一。全程一个自建 keystore 签的,后面所有校验补丁(PSK、白名单)都绑着这个证书,将来出新版必须同 keystore 签才能覆盖安装。
  • 验证环境用真 ARM。我在 x86 模拟器上跑,遇到 houdini 转译层随机 SIGSEGV,原版 APK 都崩——差点让我把环境问题当成 APK 问题,冤死。后来全换云手机(ARM 真机同理)。另外云手机 adb install 有个 incremental 假 Success 的坑,加 --no-incremental 就好。

八、总结

整个过程复盘下来,我觉得最有价值的是这几个判断:

  1. 先花时间搞清架构再动手。发现热更 DLL 是明文的那一刻,主战场就定了,后面所有精力没浪费在错的方向上。
  2. 修壳不如脱壳。签名绑定 DEX 密钥的壳,硬刚校验链是地狱难度,脱壳重建直接让整个壳消失。
  3. 重签之后要过几关,心里要有地图:壳校验(脱壳解决)→ 自研 PSK(塞原证书 patch 计算函数)→ metadata 白名单(等长替换)→ 抽取损伤(手工重写)。一层一层排,每层都有明确的死因日志可以定位。
  4. frida 是侦察兵不是主力,反调试面前,静态 patch 才是能交付的东西。
  5. 改完热更相关的资产,记得设备缓存。这个坑能坑你一整天。

用到的工具:jadx、IDA 8.3、Il2CppDumper、BlackDex、apktool、baksmali/smali、uber-apk-signer、dnfile + dncil(python 处理 .NET DLL 神器)、capstone、frida(侦察用)。PSK 那段的 python 脚本核心逻辑文中都写了,感兴趣的可以自己组合。

最后的最后,老生常谈:本研究仅出于个人学习目的,尊重开发者的劳动成果,请勿将文中技术用于商业用途或传播成品。宝宝巴士的内容本身做得还行,大家该支持还是支持。

折腾愉快。有问题欢迎留言讨论,看到会回。

(惯例:潜水员第一次发长帖,各位大佬轻喷,能混个免费评分就更好了)




附件内容:
附件脚本说明(配合帖子各章节使用)
====================================

通用性排序,前面几个拿到别的应用基本直接能跑:

1. sig_psk_calc.py
   解析 APK v2 签名块("APK Sig Block 42"),提取证书 DER/公钥,
   计算 SHA256/MD5 候选对比。对应帖子 4.3 节。
   用途:确认"签名派生密钥"类校验到底吃的哪个字段。
   依赖:无(纯标准库)。改脚本里的 APK 路径即可。

2. capstone_bl_translate.py
   capstone 反汇编 libil2cpp.so 指定函数,BL 调用目标用
   Il2CppDumper 的 script.json 翻译成 C# 方法名。对应帖子第六节。
   依赖:pip install capstone
   用法:python capstone_bl_translate.py <so> <script.json> <起始VA> <长度>
   注意:RVA 不是文件偏移(差 0x1000,脚本里可调);
        script.json 的 Address 是十进制。

3. hotfix_meta.py
   .NET 大程序集(10万+方法)快速索引。对应帖子第五节的 O(N&#178;) 坑。
   dnfile 必须 clr_lazy_load=True,否则 BlobHeap 尾部切片能跑几分钟。
   改 DEFAULT_PATH 指向你的 DLL。

4. dump_method_il.py
   依赖 hotfix_meta.py + dnfile + dncil(pip install dnfile dncil)
   用法:python dump_method_il.py <方法名> / <类名> <方法名> / --class <类名>

5. patch_hotfix_v3.py
   IL patch 参考实现:dncil opcode 解析、tiny/fat 方法头、in-place 字节替换。
   里面的 patch 清单是本案例的(IsVip 置 true / bne.un 保栈平衡等),
   换你自己的目标时改清单,框架逻辑通用。
   依赖:dnfile dncil + hotfix_meta.py
   &#9888;&#65039; 改完务必全量回验(dnfile 重解析全部方法 0 bad)+ 邻接方法抽查。

6. find_inode.sh
   反调试端口排查:按 /proc/net/tcp 的 LISTEN inode 找占用进程。
   push 到设备 su 下跑。对应帖子第二节。
   (默认查 23946,其他端口改脚本里的 :5D8A 十六进制端口号)

环境说明:python 3.x;除 sig_psk_calc/find_inode 外都需要
pip install dnfile dncil capstone


分享脚本附件.rar (10.92 KB, 下载次数: 102)

免费评分

参与人数 19威望 +1 吾爱币 +40 热心值 +15 收起 理由
zzfxll + 1 + 1 我很赞同!
keith230 + 1 + 1 热心回复!
xiaohezi123 + 1 我很赞同!
emptynullnill + 1 + 1 用心讨论,共获提升!
zlnxlzht + 1 + 1 我很赞同!
pojie521008 + 1 + 1 我很赞同!
随便起 + 1 + 1 谢谢@Thanks!
wapj20260721 + 1 谢谢@Thanks!
yiqiudxv + 1 我很赞同!
tongzhi + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
Tonyha7 + 2 + 1 用心讨论,共获提升!
zx55211 + 1 + 1 我很赞同!
iamPorter + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
dph5199278 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
mihok + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
and11 + 1 + 1 我很赞同!
helian147 + 1 + 1 谢谢@Thanks!
qtfreet00 + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
xiaobai + 2 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

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

 楼主| Cencrack 发表于 2026-8-19 11:35
本帖最后由 Cencrack 于 2026-8-19 11:37 编辑

----仅供技术学习研究,请勿用于任何商业用途。文中不提供成品下载。----
各位朋友我是不会发成品的,别留邮箱了。这纯技术分享!
spiff 发表于 2026-8-17 10:51
2441910474 发表于 2026-8-17 11:04
melody001 发表于 2026-8-17 11:06
有点溜啊,支持一下
bfbzfbz 发表于 2026-8-17 11:36
除了安装包以外的,都看不懂啊
wentao520 发表于 2026-8-17 12:39
谢谢分享,非常不错的软件
zengwei3098 发表于 2026-8-17 13:15
还是非常厉害了
Vincent168 发表于 2026-8-17 13:39
感谢分享666
zhouqiwen314 发表于 2026-8-17 13:44

感谢分享666
gesuo1987 发表于 2026-8-17 14:34
牛逼啊6666
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

GMT+8, 2026-9-8 04:55

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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