一次游戏安全SO静态分析记录:腾讯ACE与FairGuard加固强度对比

最近用GPT-5.6 Sol分析了几份Android SO,感受比较明显:AI已经可以承担不少二进制静态分析中的重复工作。把ELF头、节区、符号、字符串、重定位和函数展开信息交给它批量整理,再回到工具输出逐项核对,效率比手工来回翻结果高很多。

这次我选择了两款手游中的安全模块,分别来自FairGuard和腾讯ACE。目的很直接:在相同分析口径下,看看两个SO向常规工具暴露了多少结构与语义信息,以及分析者能否快速建立代码地图。

下面记录完整的样本信息、复现命令、指标口径和量化结果。

一、测试对象与分析环境

项目 FairGuard 腾讯ACE
游戏 《闪烁之光》 《火影忍者》
游戏版本 4.4.8 1.79.79.9
测试文件 libFairGuard.so libtersafe.so
SHA-256 e34dd793cb58f360fc235e0375486a9a116154d664724ce5e688a46c611bf4fe 517f1e3891e7a7c5dd8254bedcb09a5fe1d2acb0147cec757efb3a06ec5b2a10
架构 AArch64 AArch64
文件大小 5.16 MiB 5.52 MiB
ELF类型 64位共享库 64位共享库
Strip

解压安装包后,可以在lib/arm64-v8a目录中找到对应模块。两份样本架构一致、体积接近,并且都已经Strip,适合放在同一套静态指标下观察。

AI辅助部分使用GPT-5.6 Sol,推理强度设置为"极高",基础指令为:

分析两个SO文件的加固强度并进行量化对比。

这里让模型负责整理检查项、批量统计数据和发现异常,再用ELF工具输出复核关键结论。常用基础命令如下:

bash 复制代码
sha256sum libFairGuard.so libtersafe.so
file libFairGuard.so libtersafe.so

readelf -hW -lW -SW -sW -rW -dW libFairGuard.so
readelf -hW -lW -SW -sW -rW -dW libtersafe.so

readelf --debug-dump=frames libFairGuard.so
readelf --debug-dump=frames libtersafe.so

strings -a -n 8 libFairGuard.so
strings -a -n 8 libtersafe.so

这些命令可以复核ELF头、程序段、节区、动态符号、重定位、动态项、字符串和FDE记录。节区覆盖率、无节区区域、分档字符串数量以及局部熵值,则需要在这些结果的基础上继续用脚本统计。

本次采用的几个指标口径如下:

  • 节区表覆盖文件比例:有文件内容的节区所描述的文件范围,占整个文件的比例。
  • 最大无节区描述区域:文件偏移范围内,没有被节区表描述的最大连续区间。
  • 可打印字符串:连续可打印ASCII内容,按长度≥8、≥16和≥32分别统计。
  • FDE记录.eh_frame中能够正常解析的Frame Description Entry。
  • 局部熵值:将RX段按64 KiB分块,统计熵值≥7.8和熵值<1的块。

二、先看ELF结构:工具能否直接找到代码主体

指标 FairGuard 腾讯ACE
节区数量 21 28
程序段数量 6 9
节区表覆盖文件比例 6.94% 99.96%
最大无节区描述区域 4,805,144字节 1,799字节
最大无节区区域占比 88.80% 0.03%
声明的.text大小 16字节 3,590,944字节
.text零字节比例 100% 10.10%
RX段中可执行节区覆盖率 0.0039% 65.74%

FairGuard样本最明显的特征是.text只有16字节,而且内容全部为零。它的ELF入口为0xfe00,落在节区表没有描述的区域。整个文件最大的无节区区域达到4,805,144字节,占比88.8%。

对IDA、Ghidra、radare2一类工具来说,标准节区原本是建立初始代码地图的重要依据。遇到这种布局后,只按节区表分析会漏掉主体内容,需要转向程序段、入口地址和运行时装载逻辑继续识别。

腾讯ACE样本的节区表覆盖率为99.96%,.text.rodata.eh_frame、重定位表和版本表均能正常定位。常规工具导入后,可以直接依据标准ELF结构开始分析。

这一组数据也是两个样本拉开差距最大的地方:FairGuard优先破坏了工具赖以建立结构的静态地图,腾讯ACE则保留了更标准的共享库布局。

三、动态符号:符号很多,未必能提供有效信息

指标 FairGuard 腾讯ACE
动态符号总数 1,028 288
空名称符号 1,002(97.47%) 1(0.35%)
可读符号比例 2.24% 99.65%
可读导出接口 0 71
唯一导入函数名 4 216
4字节伪函数条目 999 2
不落在声明节区的符号 1,007 0
readelf一致性警告 10条 0

单看数量,FairGuard拥有1,028个动态符号,比腾讯ACE更多。继续检查会发现,其中1,002个没有名称,999个函数条目的长度固定为4字节,还有1,007个符号地址与声明节区对不上。

它另外保留了3个非空导出名称,但内容属于不可读字节序列,因此可读导出接口统计为0。readelf还给出了10条本地符号顺序异常。这样的符号表很难充当有效的分析导航,更接近干扰信息。

腾讯ACE样本中的288个动态符号结构正常,可读符号比例达到99.65%,其中可直接识别71个导出接口,例如:

  • JNI_OnLoad
  • tss_sdk_init
  • tss_sdk_encryptpacket
  • tss_sdk_decryptpacket
  • tss_sdk_ischeatpacket
  • tss_recv_sec_signature

部分名称属于SDK需要公开的接口,但它们同样会成为静态分析中的路标。分析者可以从初始化、数据收发或加解密接口进入,再沿交叉引用向内部追踪。

四、字符串:还能看到多少功能语义

短字符串容易受到高熵数据随机组合的影响,因此这里重点统计8字节以上的连续可打印ASCII内容。

指标 FairGuard 腾讯ACE
长度≥8的字符串 427 4,224
长度≥16的字符串 27 1,624
长度≥32的字符串 12 370
.rodata可打印字符串 6 10,880
源码路径 0 6
明文JNI_OnLoad 未发现 可见

腾讯ACE中长度达到16字符的字符串有1,624条,约为FairGuard的60倍。样本里还可以看到NDK版本、Build ID和部分指向.cpp、LLVM源码的编译路径。

FairGuard主要保留依赖库名称及GCC 4.9编译痕迹,没有提取到能够直接指向反调试、Hook或签名校验等功能的明文关键词。只从字符串入手,很难快速还原模块内部的功能分布。

字符串保护看起来只是"少暴露一些文字",实际会影响整个分析节奏。错误提示、日志、文件路径、接口名称和协议字段经常被用来给未知函数命名;这些线索收敛以后,后续交叉引用也会失去一批明确锚点。

五、重定位、初始化入口与函数边界

指标 FairGuard 腾讯ACE
重定位总数 93 25,380
INIT/FINI目标 4 66
位于标准.text的INIT/FINI目标 0 66
位于无节区区域的INIT/FINI目标 4 0
可恢复FDE记录 0 16,286
.eh_frame零字节比例 100% 32.51%
.gcc_except_table零字节比例 100% 38.33%

FairGuard的4个构造与析构目标全部位于无节区描述区域。.eh_frame.gcc_except_table虽然保留了节区名称,内容却全部为零,readelf只能识别一个终止符,无法恢复有效FDE记录。

腾讯ACE可以解析出16,286条FDE记录。即使这些记录不带函数名称,IDA、Ghidra等工具仍然可以利用它们识别函数边界和栈展开关系。

重定位总数会受到代码规模、编译器和链接方式影响,单项数值不适合直接判定强弱。这里将它和节区布局、符号表、初始化入口、FDE一起观察,可以看到腾讯ACE保留了更多标准化交叉引用信息;FairGuard则让多个静态入口同时失效。

六、为什么整体熵值不能直接代表加固强度

指标 FairGuard 腾讯ACE
整体信息熵 4.626 6.456
文件零字节占比 54.43% 21.89%
RX段中熵≥7.8的64 KiB块 35/83,42.17% 4/84,4.76%
RX段中熵<1的64 KiB块 42/83,50.60% 0/84

如果只比较整体熵,腾讯ACE的6.456高于FairGuard的4.626,很容易把这个结果直接理解成腾讯ACE的加密或压缩程度更高。但FairGuard文件中有54.43%的零字节,大量零填充会显著拉低整体熵。

换成64 KiB分块后,FairGuard的RX段里有35个高熵块,同时存在42个低熵零块。结合88.8%的无节区区域,这种"高熵载荷与大块零区并存"的分布更接近加密数据、隐藏载荷或自定义布局。

腾讯ACE的整体数据分布更连续,RX段中仅有4个分块达到7.8以上。由此看,熵值要和文件布局、零字节比例及分块位置一起解释,单一平均值很容易掩盖真实结构。

七、基础ELF安全属性

属性 FairGuard 腾讯ACE
NX Stack 通过 通过
GNU RELRO 通过 通过
BIND_NOW 通过 通过
Full RELRO 通过 通过
TEXTREL
RWX加载段

这一部分双方表现基本一致:都开启了NX、GNU RELRO、BIND_NOW和Full RELRO,也没有TEXTREL或RWX加载段。基础编译和链接安全属性都比较完整。

八、静态抗分析强度量化

业内没有统一的SO加固百分制。为了汇总本次观察结果,我按"常规静态工具能够直接利用的信息量"设计了一个百分制模型。总分方便快速对照,具体判断仍以原始指标为准。

评分维度 权重 FairGuard 腾讯ACE
ELF结构与节区隐藏 25 23 8
符号与接口信息隐藏 20 18 11
字符串与构建信息保护 15 14 8
初始化、重定位及FDE隐藏 15 14 7
数据不透明度与局部熵特征 15 13 9
基础ELF安全属性 10 10 10
总分 100 92 53

libFairGuard.so在这套静态指标下得到92分。它的代码主体没有通过常规节区完整呈现,动态符号中存在大量无名、定长及越界条目,字符串、重定位和函数展开信息也明显收敛。常规工具很难直接恢复有效代码地图。

libtersafe.so得到53分。该样本同样完成Strip,并具备完整的基础ELF安全属性;它保留了较标准的共享库布局,符号、接口、字符串、重定位和FDE信息可以被工具较完整地提取,初始结构恢复相对直接。

九、这次分析留下的几个检查思路

做完这轮对比,我觉得判断SO静态强度时,下面几项值得形成固定检查顺序:

  1. 先看程序段和节区表是否一致。 .text大小正常,并不代表全部可执行内容都被节区完整描述;入口和构造函数落点同样重要。
  2. 统计有效符号,不只看符号总数。 空名称比例、固定长度函数、异常地址和可读导出数量,更能反映符号表是否可用。
  3. 字符串要分长度并结合所在节区。 单纯运行一次strings很容易被随机短字符串干扰。
  4. 把FDE当成函数边界线索。 经过Strip的SO仍可能从.eh_frame恢复出大量函数范围。
  5. 熵值必须分块看。 整体均值会被零填充拉低,也会掩盖局部高熵区域。
  6. 最后再做量化评分。 原始数据负责提供证据,分数只用于汇总差异。

小结

GPT-5.6 Sol在这次分析中的价值,主要体现在统计和整理:它能快速把分散在多条命令中的数据归到同一套指标下,也能提醒我关注符号地址、节区覆盖率和局部熵分布等异常点。涉及关键结论时,仍要回到readelfstrings和脚本统计结果逐项确认,这样输出才方便复现。

回到两个样本,双方的基础ELF安全配置相近,真正拉开静态分析难度的是结构和语义信息的暴露程度。FairGuard在节区隐藏、符号干扰、字符串收敛和函数边界隐藏方面更彻底;腾讯ACE保留了较多标准ELF结构及可读接口,常规工具更容易建立初始分析框架。

这也说明,只确认SO有没有Strip远远不够。把ELF布局、符号、字符串、重定位、初始化入口和FDE放到一起检查,才能更接近常用逆向工具实际面对的分析阻力。

相关推荐
传奇开心果编程1 小时前
【Jetpack Compose基础语法学与练】第9课 SideEffect副作用 + LaunchedEffect,处理异步任务和生命周期
android·学习·ui·kotlin·android jetpack
2401_833269303 小时前
RecyclerView多布局详解
android
v_34836087624 小时前
Android_8 zygote启动分析
android
消失的旧时光-19434 小时前
Android 系统层扫盲 11:JNI 到底是什么?Java / Kotlin 是怎么调用 C/C++ 的?
android·binder·zygote·system_server·aops
mmsx4 小时前
Android 地图几万要素卡成幻灯片?顶点缓冲批上传与显存复用
android·opengl·地图·栅格
北京自在科技15 小时前
谷歌 Find Hub 迎来新版本更新
android·安卓
古法安卓21 小时前
Android-休眠唤醒后onLocationChanged没有数据问题排查
android·java·android studio
Htr_1 天前
Creem 2.0 使用指南:面向 AI 构建时代的资金平台
android·数据库·人工智能·ui·photoshop
wardenlzr1 天前
车机看门狗(Watchdog)为何会让整机硬重启
android·优化·车机