|
1598| 9
|
[Android 原创] 某环逆向 TOP3:反作弊与反调试 |
发表于 2026-9-26 17:49
自退。 命令串 协议。 不走 libc。 文件检测不调 libc 的 这一步用的工具: 三、网易易盾(
|
| 它走哪 | 在哪堵 | 本样本 |
|---|---|---|
| Java API | 改返回值 | 图灵盾 / DFGA |
libc / syscall() |
hook libc | libsecsdk.so、libturingmfa.so |
内联 svc |
patch svc |
libthemis.so(2 条 openat) |
| 自带 syscall 引擎 | hook 该引擎 | libtprt.so 的 tp_syscall_imp |
另一条是堵漏斗,不堵点。ACE 的汇总通道:
libtprt 检测 ──(ioctl/gp*ioctl)──> Java AceManager ──> h.trace.qq.com
堵 libtprt.so 的 ioctl,或在 Java 层把 AceApplication.initialize 改返回 0。
分三层写:
AceApplication.initialize→0,图灵盾 / DFGA / Alipay / Bugly / Debug)。/proc/self/{maps,status} 洗白(TracerPid→0)、prctl/ptrace→0、dl_iterate_phdr 过滤注入模块、syscall() 拦截。libtprt.so 的 tp_syscall_imp,patch 内联 svc(libtprt.so 的 0x2603c、libthemis.so 的 0x33d38 / 0x356a8)。注入用 spawn(-f) + --no-pause,frida-server / gadget 改名。自检看 /proc/self/maps 有没有 frida、TracerPid 是不是 0。
这套方案只在模拟里过过——frida_stable.js 里只有 libthemis.so 那条(prctl)验过会被绕过,Java 层和 libturingmfa.so 都没验,真机也没上。
这一步用的工具:frida-tools(frida -U -f ... -l frida_stable.js -l frida_svc_patch.js),配 node 校验 .js。
5 个 dex、所有带 SSL 的库、libUnreal.so 全流式搜 sha256/<base64> 形式的 pin,一个都没有。dex 里的 sha256/ 是 okhttp 格式串,libmxcore.so 的 verify 回调只打日志。所以抓包不用清 pin,直接 hook SSL_write/SSL_read。
SSL 栈分布:libmxcore.so、libprimekit.so、libtapsdkcore.so、libliteavsdk.so(BoringSSL)、libUnreal.so(UE 自带 OpenSSL)。
这一步用的工具:strings + 全库流式搜(scripts/scan_all_ssl.py / find_realpins.py)。
账号和支付全在虎鱼 laohu SDK(com.laohu.sdk.LaohuPlatform),纯 Java,不加载 so:
LaohuPlatform(Facade) → c.a / c.b / c.c
→ loginForGame → ActivityLogin / 三方(QQ/微信/微博/TapTap) → pay
完美 oneSdk(oneAppId=1289)域名加密,只有 dfga_domain.json 明文(clientlog.wanmei.com)。UE 侧只有标准桥 com.epicgames.unreal.NativeCalls(13 个 native,无业务),业务桥是 OneGlobalJNI。虎鱼包扫 kycgm / loadLibrary / RegisterNatives 全是 0,说明国密 libkycgm.so 的 GmCipher 调用方在 UE 层。
这一步用的工具:jadx 反编译 dex(scripts/find_login_classes.py 定位),unzip 出 dex。
retoc 解包后 31,880 个文件:
.uasset 27300 / .ubulk 4500 / .umap 71 / .ushaderbytecode 9。HT/Content/):Characters 13843、Blueprints 5595、FX 3087、UI 2237、sequence_pack 2195、DataTable 867、WwiseAudio 276、Text 137。DataTable(数值)、Text(文案)、Blueprints(逻辑)最有用。UE5 资产要配 .usmap 才能读属性名。
这一步用的工具:unzip 取 OBB 里的 .utoc/.ucas,retoc -a <key> unpack 解包。
三家工具各管一段。环境和 JNI 都得自己补,现成的跑不起来。
环境。 Python 要 capstone / pyelftools / unicorn / pycryptodome / loguru(见 requirements.txt)。Qiling 得源码装——pip 上那个 qiling 是空壳,用 pip install D:\qiling\qiling-master;rootfs 取 arm64_android,路径不能含中文(异环 会触发 latin-1 报错)。unidbg 用 JDK + Maven。IDA 是 Pro 9.4 的 idat.exe。Frida 配 frida-tools + Node。
IDA(静态破混淆)。 反编译 libtprt.so 的平坦化函数,出可读 C:
idat.exe -A -S"scripts/ida_libtprt.py" libtprt.so
脚本模式跑,不开 GUI。判据:F5 后状态机(mov wState,#imm + cmp/b.ge)还原成可读 C。产物 reports/ida_libtprt.txt。
Qiling(纯原生拟真)。 想把 libtprt.so 的 JNI_OnLoad 跑起来,先补三步:
br 就跳进未映射页崩掉。得解析 .rela.plt(libmxcore.so 有 512 条)给每个导入打桩、回填 GOT、hook 桩做分发(scripts/qiling_harness.py)。tp_syscall_imp(0x146be0),看它读了哪些文件。跑通后能看到它读了 /proc/<pid>/cmdline(查可疑进程/改名)之类。边界:Qiling 是纯原生,JNIEnv 全被 stub,只到"外壳"。
unidbg(真 JNIEnv)。 纯原生跑不出 JNI 语义,得靠 unidbg 的 DalvikVM。但它自带的 JNI 实现不全,要在 MyJni.java 里补 4 处:
getClassLoader 返回 BaseDexClassLoader 对象(不是 ClassLoader);getObjectField 收到的字段名是完整签名,要解析成裸名再匹配;DexFile.mFileName;String.getBytes("GB2312")。补完,rL 和 ClassLoader→DEX 链才走得通(VM base = 0x12000000);也正是在 unidbg 里看到 libtprt.so 在 0x2603c 自退。
字符串枚举(黑盒转白盒)。 IDA 反编译里找到取串分发器 sub_111740(id) = off_163750[id % 100]();再用 unidbg m.callFunction(base + 0x111740, id) 枚举 id 0..2000,dump 出 101 条明文(检测点 + 命令通道)。
Frida(三层保稳)。
frida -U -f com.hottagames.yh.laohu --no-pause -l frida_stable.js -l frida_svc_patch.js
自检看 /proc/self/maps 有没有 frida、TracerPid 是不是 0。
脚本清单。 ida_libtprt.py(IDA 反编译)、tprt_deflat.py(找平坦化分发器)、tprt_strxref.py / tprt_syscall_xref.py(取串 / svc 引用)、qiling_harness.py / qiling_tprt_sim.py(Qiling 补环境)、unidbg/TprtProbe.java / SecsdkProbe.java / MyJni.java(unidbg 探针)、secsdk_*.py / search_sepdata*.py(易盾 VMP)、themis_svc_list.py / frida_themis_svc.js(themis svc)、frida_stable.js / frida_svc_patch.js(Frida)。
分工与边界。 IDA 破混淆 → Qiling 看纯原生行为(读文件 / 命令)→ unidbg 走真 JNI(协议 / VM)。三家都只到"外壳和监督行为"层,平坦化核心没打开。
libtprt.so(ACE):读 /proc/{cmdline,status,maps,fd}、查调试器 / 模拟器 / ADB / 注入痕迹、通过 ioctl 命令通道收发、必要时让进程自退。检测全走自带 tp_syscall_imp,绕 libc。推测:那条 ioctl 通道能远程下发策略(魔数 0x87920101 看着像固定协议头)。libsecsdk.so(易盾):把业务方法编译成自研 VM 字节码、运行时解释(DEX-VMP)。推测:它保护的正是 OAID 设备标识获取链(106 个 rL 调用全是 miitmdid)。libthemis.so:读文件(2 处内联 openat)、prctl 反调试。除此之外很干净。libturingmfa.so(图灵盾):按导入表推断是套完整环境扫描——开文件、popen 跑 shell、正则扫、读传感器指纹、socket 上报。具体扫什么没验。libUnreal.so:装载并解资源(Oodle + AES)、跑游戏逻辑。libmxcore.so:IM 消息收发(自带 OpenSSL + curl)。libkycgm.so:国密 SM2 + SM4-CBC。h.trace.qq.com / clientlog.wanmei.com):收客户端上报做风控判定。这块完全没碰,纯属常识推测。带"推测 / 推断"的都不是实证,只是从字符串表、导入表、调用点推出来的能力范围。
| 结论 | 文件 | 偏移 / 符号 | 函数 |
|---|---|---|---|
| ACE 取串混淆 | libtprt.so |
— | sub_111740(id) → off_163750[id % 100] |
| ACE raw svc 引擎 | libtprt.so |
0x146be0 | tp_syscall_imp |
| ACE 命令分发 | libtprt.so |
0x136d7c | ioctl;构建 0x136f24 / 解析 0x137018 |
| ACE native 方法 | libtprt.so |
init 0x25ae0 / ioctl 0x136d7c / gp6 0x26058 / gp7 0x25ea4 | initialize/ioctl/gp6ioctl/gp7ioctl |
| ACE 自退 | libtprt.so |
0x2603c | 内联 svc #0(exit_group) |
| 易盾 rL 桥 | libsecsdk.so |
0xaddc | rL;worker 0xfcfc |
| 易盾解释器 | libsecsdk.so |
0x30374 / 主循环 0x307b8 | InterpretInternal |
| 易盾 opcode 表 | libsecsdk.so |
0x48b30(256×int32) | handler 0x30c00–0x34458 |
| 易盾 SepData 解析器 | libsecsdk.so |
0xc1c8(out,buf,len) | 读器 0x12968、写字节码 0xc2fc |
| 易盾 VM 上下文 | libsecsdk.so |
单例 0x64d30 / vtable 0x60c78 / lookup 0xc80c | vtable[4]=lookup(id) |
| 易盾串解密 | libsecsdk.so |
0xa868(init_array[0]) | ((K-c)^K) |
| themis 反调试 | libthemis.so |
0x71f0c / 0xc8a60 | prctl(PR_SET_DUMPABLE=21) |
| themis 内联 openat | libthemis.so |
0x33d38 / 0x356a8 | 内联 svc #0(x8=56) |
| Unreal AES(OpenSSL) | libUnreal.so |
0x267EC98 / 0x267EF30 | AES_encrypt / AES_decrypt |
| Unreal AES(类) | libUnreal.so |
0xABFA760 / 0xABFA9A8 / 0xABFA8E0 | SetKey(16B) / Encrypt / Decrypt(ECB) |
| Unreal Oodle | libUnreal.so |
0xB587284 / 0xB588538 | OodleLZ_Decompress / Compress 包装 |
| Unreal IoStore 密钥 | libUnreal.so |
常量 0xcdbc40 | key-provider 0x9a93d08(注册 0x26a26e8 / 触发 0x9aae9e0) |
| Unreal IoStore 解密 | libUnreal.so |
包装 0x3AC7CA8 / 取key 0x3AC7E14 / AES 0x264DA0C | — |
| mxcore SSL_write 包装 | libmxcore.so |
— | sub_46AA74 → 写 sub_489DBC |
| 国密 GmCipher | libkycgm.so |
0x4a80 / 0x4ca8 / 0x4f6c | sm2Encrypt / sm4CbcEncrypt / sm4CbcDecrypt |
| IM JNI 面 | libmxcore_javasupport.so |
JNI_OnLoad 0x11e58 | 140 个 Java_..._pwim_* |
顺带交代两个我自己踩过的坑。一是 libturingmfa.so 我最初把它标成了"JNI 名混淆",其实那是 libthemis.so 的特征,后来改了。二是早期找 libtprt.so 解密器时,被 disasm_range.py 里写死的 SO=libsecsdk.so 带偏过,报告 77 才更正——那之后我不再用那个脚本的旧结论。
没搞定的还有两处:libsecsdk.so 的 SepData 只在内存里,得真机动态抓;服务端风控(h.trace.qq.com / clientlog.wanmei.com)完全是另一摊,我没碰。
脚本、.so、unidbg 探针、依赖清单都在这儿:
https://github.com/Machao147258-max/yh-top3
so/ 反作弊相关库(libtprt / libsecsdk / libthemis / libturingmfa / libDfga_Catch / libclient / libmxcore)
scripts/ 对抗脚本(.py / .js)
unidbg/ unidbg 探针(.java)
requirements.txt / README.md
本仓库不含 AES 密钥(已在脚本里抹掉)。
发帖前要善用【论坛搜索】功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。
发表于 2026-9-27 09:41
| ||
发表于 2026-9-27 12:42
| ||
发表于 2026-9-27 18:18
| ||
发表于 2026-9-27 20:58
| ||
发表于 2026-9-28 10:20
| ||
RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )
GMT+8, 2026-11-11 07:29
Powered by Discuz!
Copyright © 2001-2020, Tencent Cloud.