在开始之前我要简单的说下,我是个小白但是我还是要写博客,这个闪退分析是不完全的,由于我的逆向技术不算精所以闪退问题还没有解决,我有什么问题大佬可以指点出来
一、引言:一个让人抓狂的问题,执行了某改设备id.sh文件
- 由于之前打王者开挂被标记了黑设备,后果就是打两把娱乐还是排位都会禁赛
- 重装、清缓存、重启手机,全都没用。
- 在小破站搜索了相关内容,执行了sh文件重新更新了下文件
- 该文件如下
二、初步尝试:常规手段无效
- 这是改了后游戏出现的现象,游戏更新后卡在98.5%,立马闪退
- 重装游戏(没用)
- 清除缓存和数据(没用)
- 重启手机(没用)
- 怀疑是系统问题,但其他 App 都正常
三、引入工具:adb logcat 登场
- adb logcat 是个强大的adb命令可以查看安卓各种日志
- 什么是
adb logcat(一句话解释:Android 的系统日志工具) - 为什么要用它(闪退时系统会留下日志,日志里藏着真相)
参考代码如下:
adb logcat -v threadtime *:E > D:\log.log+
前面的代码是清除之前的日志信息,回车执行后就打开游戏抓取日志,等待游戏再次闪退后,按Ctrl+c停止抓取,
四、日志分析:
4.1 日志太多了,怎么下手?
打开日志信息一大堆调用的信息,我们按照一些关键词tencent,AndroidRuntime搜索,内容还是太多我也看不懂,思考了下决定将关键的信息复制给ai分析
回到终端用关键词过滤命令过滤出我们要的,因为是闪退我们看AndroidRuntime,Tencent,sgame,backtrace,pc
之类的关键词
- 用
findstr过滤关键词:AndroidRuntime,Tencent,sgame,backtrace,pc - 命令示例:
命令参考如下
findstr /I "tencent AndroidRuntime" D:\log.log- 继续深度挖掘用
backtrace找堆栈
findstr /I "backtrace pc #" D:\log.log
将所有的内容复制给ai分析
ai的分析如下:
"我直接帮你把关键点抓出来了,你这个日志已经很明显了,不是普通闪退,是被检测/初始化崩溃。
🚨 核心崩溃位置(最重要)
你这里:
E CRASH : pid: 1172, tid: 1581, name: UnityMain #00 pc ... libtersafe.so #01 pc ... libtersafe.so (JNI_OnLoad+120)👉 结论一:
崩溃发生在libtersafe.so
🧠 libtersafe 是什么?
这是腾讯的:
👉反作弊 / 安全检测模块(TenSafe / Tersafe)
❗ 为什么是98.5%闪退?
你这个阶段:
✔ 游戏资源基本加载完成
✔ Unity 初始化完成
✔开始加载安全模块(反作弊)
👉 所以:
98% ≈ 反作弊初始化阶段
🔥 直接结论(重点看这里)
你的闪退原因不是普通问题,而是:
👉 反作弊初始化失败 → 直接崩溃
📊 日志里的“异常信号”
我帮你逐条解释:
1️⃣ 文件缺失
File not found: so_load_order.txt File not found: so_cdn_url.txt👉 说明:
- 游戏资源/环境不完整
- 或被改过 / 非官方环境
2️⃣ 权限异常(很多)
MediaProvider: SecurityException com.tencent.tmgp.sgame has no access to content://media/...👉 说明:
- Android 读存储被限制
- 权限不正常 / 沙盒异常
3️⃣ 最关键:直接崩在 tersafe
libtersafe.so (JNI_OnLoad)👉 这个函数是:
反作弊加载入口
💥 在这里崩 = 检测直接失败
🧨 常见触发原因(你对照一下)
这个情况一般不是“运气不好”,基本是下面之一:
🚫 1. Root / 模拟器 / 虚拟环境
- 模拟器(雷电、夜神、MuMu)
- root 手机
- Magisk
👉 ✅ 最常见原因(80%)
🚫 2. 有注入 / hook / 工具
比如你这种学习环境很可能:
- frida
- GameGuardian
- 自己写的读内存工具
- /proc/pid/mem
- 注入so
👉 tersafe会直接检测 → 崩
🚫 3. APK 或资源被改过
- 修改版游戏
- 解包重打包
- 替换so
- 缺文件(你日志里已经有)
🚫 4. 系统权限/环境异常
- 存储权限没给
- 系统限制访问
- ROM问题(特别是国产魔改)
🧠 你这个情况我帮你判断(结合你的背景)
你之前说过你在做:
👉 内存读取 / hook / 逆向 / 安全研究
所以我直接告诉你:
👉99% 是被 tersafe 检测到了
而且是:
👉加载阶段直接 kill(不是封号,是直接不让进)
🛠️ 解决思路(分两种)
✅ 普通玩家解决(最简单)
如果你只是想玩:
✔ 方法1(推荐)
- 卸载游戏
- 去官方重新下载
- 不要用修改版
✔ 方法2
- 给全部权限(存储)
- 清缓存
✔ 方法3
- 换真机(不要模拟器)
⚠️ 你这种(研究环境)
我就直接说本质:
👉 你不是“闪退”
👉 你是“被安全模块拦了”
🔬 想继续研究你要知道:
关键点在:
libtersafe.so JNI_OnLoad👉 也就是:
- 初始化时检测环境
- 一旦异常 → 直接 crash
📍 你现在卡的点
不是 Unity,不是资源
👉 是:
反作弊初始化不过
🧭 给你一个正确研究方向(不涉及违规,只讲思路)
你要分析:
1️⃣ 什么时候加载 tersafe
2️⃣ JNI_OnLoad 做了什么检测
3️⃣ 崩溃是主动触发还是异常
💬 总结一句话
👉 你这个不是bug:
是游戏检测到你当前环境“不安全”,直接在98%加载反作弊时把你干掉了"
回忆:改过设备 ID
- 看到
libtersafe突然想起——之前封号后用工具改过 Android ID - 安全组件会检测设备指纹(Android ID、OAID、UUID 等)
- 改 ID 后,新旧指纹不匹配,安全模块触发自我保护,主动闪退
我不能百分百保证是改过id导致的,因为也有可能是我的环境导致的,或者资源包不全等导致的,解决这个问题需要用辩证思维的方式解决,因此我们先检查一下我的手机环境,先用了hunter,luna
这下破案了,是我的环境问题,不过我还是不甘心
五、问题定性
- 结论:闪退不是因为游戏代码本身有 Bug,而是腾讯安全组件
libtersafe.so检测到设备指纹不一致后主动触发崩溃,并且我的环境本身就有问题 - 证据链:
- 崩溃点始终在
libtersafe.so(安全库) SIGSEGV+SI_TKILL组合表明是主动自毁(不是偶然崩溃)- 回忆确实改过 ID,与安全检测逻辑吻合
- 崩溃点始终在
- 解决方案:
- 1.刷机(刷入原厂的包)
- 2.改变环境再次试试会不会闪退(最终失败)
- 3.用ida逆向(担心技术不够)
六、总结
- 安全组件(如
libtersafe)的检测维度比想象中更严格 - 如何分析堆栈定位崩溃点
- 如何用
adb logcat抓取崩溃日志
最后我还是勇敢的试试用ida分析下libsafe.so模块
我通过字符串搜索找到JNI_load入口
一下是我转成的伪c代码,大佬可以来看看
__int64 __fastcall JNI_OnLoad(__int64 *a1, __int64 a2) { __int64 v4; // x0 __int64 v5; // x0 unsigned int (__fastcall *v6)(__int64 *, __int64); // x8 unsigned int n65540; // w19 __int64 v8; // x20 __int64 v9; // x0 __int64 v10; // x0 __int64 v11; // x0 __int64 v12; // x0 __int64 v13; // x0 __int64 v14; // x0 __int64 v16; // x8 __int64 v17; // x21 __int64 v18; // x0 __int64 v19; // x0 __int64 v20; // x22 __int64 v21; // x0 __int64 v22; // x21 __int64 v23; // x0 __int64 v24; // x0 __int64 v25; // x22 int v26; // w23 int v27; // w23 __int64 v28; // [xsp+18h] [xbp+18h] BYREF v4 = sub_2D0D30(); sub_2D0D94(v4, a1); if ( !g_AllTssExportFunc ) { v16 = *a1; v28 = 0; if ( !(*(unsigned int (__fastcall **)(__int64 *, __int64 *, __int64))(v16 + 48))(a1, &v28, 65540) ) { v17 = v28; if ( v28 ) { v18 = sub_4EAC74(3164); v19 = (*(__int64 (__fastcall **)(__int64, __int64))(*(_QWORD *)v17 + 48LL))(v17, v18); if ( !v19 || (v20 = v19, (*(unsigned __int8 (__fastcall **)(__int64))(*(_QWORD *)v17 + 1824LL))(v17)) || (v26 = (*(__int64 (__fastcall **)(__int64, __int64, char **, __int64))(*(_QWORD *)v17 + 1720LL))( v17, v20, off_5220B8, 3), (*(void (__fastcall **)(__int64, __int64))(*(_QWORD *)v17 + 184LL))(v17, v20), v26 < 0) || (v21 = (*(__int64 (__fastcall **)(__int64))(*(_QWORD *)v17 + 1824LL))(v17), (_BYTE)v21) )// "init" { v21 = (*(__int64 (__fastcall **)(__int64))(*(_QWORD *)v17 + 136LL))(v17); } v22 = v28; if ( v28 ) { v23 = sub_2D0D28(v21); v24 = (*(__int64 (__fastcall **)(__int64, __int64))(*(_QWORD *)v22 + 48LL))(v22, v23); if ( !v24 || (v25 = v24, (*(unsigned __int8 (__fastcall **)(__int64))(*(_QWORD *)v22 + 1824LL))(v22)) || (v27 = (*(__int64 (__fastcall **)(__int64, __int64, __int64 *, __int64))(*(_QWORD *)v22 + 1720LL))( v22, v25, &qword_55E910, 11), (*(void (__fastcall **)(__int64, __int64))(*(_QWORD *)v22 + 184LL))(v22, v25), v27 < 0) || (*(unsigned __int8 (__fastcall **)(__int64))(*(_QWORD *)v22 + 1824LL))(v22) ) { (*(void (__fastcall **)(__int64))(*(_QWORD *)v22 + 136LL))(v22); } } } } } v5 = sub_1D4334(0); if ( qword_55E8E0 && (v6 = *(unsigned int (__fastcall **)(__int64 *, __int64))(qword_55E8E0 + 112)) != 0 ) { return v6(a1, a2); } else { v8 = sub_22D820(v5); v9 = sub_4E82F8(49827); v10 = sub_22DA98(v8, v9, 1, 1); if ( (v10 & 1) != 0 ) { v11 = ((__int64 (*)(void))sub_2D15F0)(); v12 = sub_2D1628(v11, a1); v13 = sub_2D15F0(v12); v10 = sub_2D1738(v13); } n65540 = 65540; v14 = sub_1D94E8(v10); sub_1DA020(v14); } return n65540; }