ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

安卓Gamex木马源码分析:动态加载与行为链复现指南

安卓Gamex木马源码分析:动态加载与行为链复现指南 简介这份资源是面向Android安全初学者与逆向分析人员的Gamex木马分析报告包围绕一款伪装成合法应用、具备自启动、隐藏图标、数据窃取与广告注入等行为的Android恶意软件展开帮助读者理解其源码结构、混淆手法与解密思路。压缩包共7个文件约626KB包含py解密脚本、java源码片段、doc分析文档、txt说明、rar样本及apk原版程序覆盖从样本到代码的完整分析链路。已有127人学习。读者可借助解密脚本与源码对照掌握变量函数名混淆、代码跳跃、动态加载等对抗静态分析的技术并了解样本解密与后门行为还原的基本方法同时结合报告中的防范建议形成从检测到防御的完整认知适合作为移动安全入门与逆向实践的参考材料。1. 从一份“Gamex木马分析报告”说起安卓源码里到底藏着什么拿到一份名为“安卓Android源码——Gamex木马分析报告.zip”的材料多数人的第一反应是这到底是一份源码还是一份分析文档答案通常是两者都有——里面既有被标记为恶意的安卓工程代码也有配套的行为记录、流量抓包和静态特征说明。它解决的核心问题很具体当你怀疑某个安卓应用是 Gamex 家族木马或者想搞清楚这类木马在源码层面怎么落地你需要一条从样本到结论的完整链路。适合谁看做移动端安全审计的工程师、负责应用上架前扫描的测试人员以及想理解安卓恶意代码常见结构的技术负责人。这份材料不是给纯逆向新手看的它默认你能读懂 Java/Kotlin 和 Smali能跑 adb 和基础抓包。下面我按“先立住概念再动手复现最后讲坑”的顺序把这类分析报告怎么读、怎么用、怎么自己产出一份讲清楚。2. Gamex木马在安卓源码里的典型结构与行为链2.1 为什么它常伪装成正常工具类应用Gamex 这类木马在源码层面很少一上来就暴露恶意逻辑。常见做法是主 Activity 只做正常界面把真正的行为藏在动态加载的 dex、反射调用或远程配置里。你打开工程目录看到的是一个能编译、能跑出界面的普通应用甚至还有正常的权限申请。但仔细看AndroidManifest.xml会发现多出一些不常见的receiver和service它们监听开机、网络变化或包安装事件。这些组件本身不违法组合起来却能在用户无感知时拉起后台任务。分析报告的价值就在于把这些分散的点连成行为链入口伪装 → 权限申请 → 后台拉起 → 远程取指令 → 执行敏感操作。读源码时不要只盯MainActivity先看清单文件里所有 exported 组件和权限组合这是最快定位可疑点的路径。2.2 动态加载与反射木马最爱的藏身方式Gamex 家族常见的一个特征是使用DexClassLoader或PathClassLoader在运行时加载外部 dex。源码里你会看到类似这样的片段// 从应用私有目录或 assets 中释放 dex再动态加载 File dexFile new File(getDir(dex, MODE_PRIVATE), payload.dex); DexClassLoader loader new DexClassLoader( dexFile.getAbsolutePath(), // 待加载的 dex 路径 getDir(odex, MODE_PRIVATE).getAbsolutePath(), // 优化后输出目录 null, // 本地库搜索路径通常为空 getClassLoader() // 父加载器 ); Class? clazz loader.loadClass(com.hidden.Payload); Method method clazz.getMethod(run, Context.class); method.invoke(clazz.newInstance(), this);这段代码的逻辑说明先把加密或伪装成资源文件的 dex 释放到私有目录再用DexClassLoader加载最后通过反射调用目标方法。参数上要关注三个点——dexFile的来源assets、raw 还是网络下载、odex目录是否可写、父加载器传的是谁。如果父加载器是getClassLoader()说明它想复用宿主已加载的类如果传 null隔离性更强也更可疑。分析报告里通常会记录释放文件的哈希和加载后的类名这是你判断家族归属的关键证据。2.3 权限与后台服务行为链的起点静态看权限不能只看数量要看组合。Gamex 类样本常申请RECEIVE_BOOT_COMPLETED、INTERNET、READ_PHONE_STATE、SYSTEM_ALERT_WINDOW等。单独看都正常合在一起就能实现开机自启、联网取指令、读取设备标识、弹窗覆盖。源码里对应的Service往往在onStartCommand里启动一个线程做轮询// 后台服务中轮询远程配置间隔由服务端下发 public int onStartCommand(Intent intent, int flags, int startId) { new Thread(() - { while (true) { try { String cmd fetchRemoteCommand(); // 网络请求返回指令 if (cmd ! null) execute(cmd); // 反射执行具体动作 Thread.sleep(60_000); // 默认 60 秒可被服务端覆盖 } catch (Exception e) { // 静默失败避免日志暴露 } } }).start(); return START_STICKY; // 被杀后尝试重启 }逻辑说明START_STICKY让系统在资源允许时重启服务Thread.sleep的间隔通常由远程配置控制分析报告里会记录实际抓到的间隔值。参数上fetchRemoteCommand的 URL 和加密方式是最有价值的 IOC。如果你在源码里看到硬编码域名先别急着下结论很多样本会用 DGA 或配置下发硬编码的只是备用。2.4 用静态分析工具快速定位可疑调用拿到工程后我一般先跑一遍基础静态扫描再人工看关键类。常用组合是apktool反编译 grep关键词 jadx看 Java 层。命令示例# 反编译 apk输出 smali 和资源 apktool d sample.apk -o sample_out # 在 smali 中搜索动态加载和反射相关调用 grep -r DexClassLoader\|PathClassLoader\|loadClass\|invoke sample_out/smali --include*.smali -l # 用 jadx 导出 Java 源码便于阅读 jadx -d sample_java sample.apk逻辑说明apktool保留资源文件方便看 assets 里有没有藏 dexgrep快速缩小范围-l只列文件名避免刷屏jadx把 smali 还原成近似 Java降低阅读成本。参数上-d指定输出目录--include限定文件类型。注意不是所有样本都能被 jadx 完美还原遇到混淆严重的回到 smali 逐行看反而更可靠。分析报告里通常会附上关键类的 smali 片段你可以对照自己的反编译结果验证。3. 复现一份 Gamex 分析报告从样本到结论的完整步骤3.1 环境准备与样本隔离做恶意代码分析第一原则是隔离。我一般用一台不联网或仅内网隔离的虚拟机装好 Android SDK、adb、Python 3 和常用分析工具。样本不要放在宿主机共享目录里直接执行先算哈希存档# 记录样本哈希便于后续比对和报告引用 sha256sum sample.apk sample.sha256 md5sum sample.apk sample.sha256 # 查看 apk 基本信息不安装 aapt dump badging sample.apk | head -40逻辑说明sha256sum和md5sum是报告里必须写的标识aapt dump badging能快速看到包名、版本、权限和启动 Activity不用装就能判断是否值得深入。参数上head -40只是控制输出长度实际报告里权限列表要完整记录。注意不要在联网真机上直接安装来路不明的样本哪怕只是“看一眼界面”。3.2 静态提取权限、组件与硬编码字符串静态阶段的目标是把所有可检索的 IOC 挖出来。除了前面说的权限和组件还要重点看strings.xml、assets和res/raw。很多样本把远程地址拆成多段拼接或者用 Base64 存在资源里。可以写个小脚本批量解码import base64, re, os # 遍历反编译输出目录找疑似 Base64 字符串并尝试解码 pattern re.compile(r[A-Za-z0-9/]{20,}{0,2}) for root, _, files in os.walk(sample_out): for f in files: if f.endswith((.xml, .smali, .json)): path os.path.join(root, f) text open(path, encodingutf-8, errorsignore).read() for match in pattern.findall(text): try: decoded base64.b64decode(match).decode(utf-8) if http in decoded or api in decoded: print(f{path}: {decoded}) except Exception: pass逻辑说明正则匹配长度 20 以上的 Base64 候选解码后只打印含http或api的结果减少噪音。参数上{20,}是经验值太短容易误报太长会漏掉分段拼接。这个脚本不能保证全命中但能帮你快速筛出可疑 URL。分析报告里通常会把解码后的域名和路径单独列一张 IOC 表。3.3 动态验证用模拟器观察真实行为静态看完必须动态跑一遍验证。我一般用 Android 模拟器无 Google Play 镜像装好样本后开adb logcat和抓包。关键命令# 清空日志后启动目标 Activity观察崩溃和敏感调用 adb logcat -c adb shell am start -n com.example.gamex/.MainActivity # 过滤关键日志标签 adb logcat | grep -i DexClassLoader\|http\|socket\|exec # 查看应用私有目录下释放的文件 adb shell run-as com.example.gamex ls -R /data/data/com.example.gamex/逻辑说明logcat -c先清空避免旧日志干扰am start直接拉起主界面grep过滤动态加载和网络关键词run-as在 debuggable 应用上可以查看私有目录确认是否有释放的 dex 或 so。参数上包名要换成实际样本的。注意如果应用不是 debuggablerun-as会失败这时需要 root 模拟器或改用其他方式。动态阶段重点记录释放了哪些文件、连了哪些 IP/域名、有没有执行 shell 命令。3.4 把发现整理成可复现的报告结构一份能被人复现的分析报告结构比文采重要。我通常按这个顺序写样本标识哈希、包名、版本→ 静态特征权限、组件、硬编码 IOC→ 动态行为释放文件、网络请求、敏感 API 调用→ 家族归属判断与已知 Gamex 特征的比对→ 处置建议查杀规则、流量封禁。每一步都要附上原始证据比如 smali 片段、logcat 输出、抓包截图。不要只写“发现可疑”要写“在哪个文件第几行调用了什么参数是什么”。这样别人拿到你的报告能按同样步骤跑出同样结果。4. 分析 Gamex 类样本时最容易翻车的几个点4.1 只跑静态就下结论漏掉远程加载现象静态看源码里没有明显恶意行为权限也正常于是判断为“误报”。原因Gamex 的核心 payload 往往在运行时从网络下载静态工程里只有加载器。解决必须动态跑一遍抓包看有没有下载 dex 或 so 的请求同时检查DexClassLoader的路径参数如果是网络 URL 或 assets 里的加密文件静态看不到完整逻辑。4.2 在联网真机上直接安装样本现象安装后手机出现弹窗、扣费或数据异常。原因样本在真机联网环境下会立即拉取指令并执行。解决始终在隔离模拟器或专用测试机上分析模拟器不登录真实账号不插 SIM 卡。如果必须看联网行为用抓包工具限制出口只放行分析用的代理。4.3 忽略资源文件里的加密 payload现象反编译后 Java 代码很干净找不到恶意逻辑。原因payload 被加密存在assets或res/raw里运行时解密释放。解决检查 assets 下所有非图片、非配置文件的体积和熵值用binwalk或file看文件类型对可疑文件尝试常见异或或 Base64 解码。分析报告里通常会记录释放文件的哈希你可以用哈希反查家族。4.4 动态分析时没关自动更新和网络时间同步现象样本行为不稳定有时跑出恶意行为有时没有。原因远程配置可能依赖时间、地区或网络环境模拟器时间不对或网络不通会导致指令不下发。解决固定模拟器时间确保网络可达在隔离环境内多次运行取并集。不要因为一次没跑出行为就否定样本。4.5 报告里只写结论不写证据链现象别人看了报告不知道怎么复现或者质疑结论。原因缺少原始文件路径、行号、命令输出和抓包记录。解决每个结论后面附上证据来源比如“见smali/com/example/Payload.smali第 42 行”或“抓包记录capture.pcap第 15 号流”。这样即使别人环境不同也能按图索骥。5. 进阶用脚本批量提取 Gamex 家族特征并做相似度比对当你手头不止一个样本而是十几个疑似 Gamex 的 apk 时逐个手工分析效率太低。我一般会写一个批量提取脚本把每个样本的关键特征向量化再做相似度比对。特征包括权限集合、动态加载类名、硬编码域名、释放文件哈希、服务名。下面是一个简化版import os, re, hashlib, json from androguard.core.apk import APK # 需安装 androguard def extract_features(apk_path): apk APK(apk_path) feats { package: apk.get_package(), permissions: sorted(apk.get_permissions()), activities: sorted(apk.get_activities()), services: sorted(apk.get_services()), sha256: hashlib.sha256(open(apk_path, rb).read()).hexdigest() } # 从反编译 smali 中提取动态加载类名需提前 apktool 反编译 smali_dir apk_path.replace(.apk, _out/smali) loaders set() if os.path.isdir(smali_dir): for root, _, files in os.walk(smali_dir): for f in files: if f.endswith(.smali): text open(os.path.join(root, f), encodingutf-8, errorsignore).read() for m in re.findall(rL([\w/];), text): if Payload in m or Loader in m: loaders.add(m) feats[loader_classes] sorted(loaders) return feats # 批量处理目录下所有 apk输出 JSON 便于比对 results [] for f in os.listdir(samples): if f.endswith(.apk): results.append(extract_features(os.path.join(samples, f))) json.dump(results, open(features.json, w), indent2)逻辑说明androguard负责解析 apk 元数据权限和组件列表直接可比loader_classes从 smali 里正则抓取含Payload或Loader的类描述符这是 Gamex 家族的强特征。参数上samples目录放待分析 apk每个 apk 需要先apktool d反编译到同名_out目录。输出features.json后你可以用简单的 Jaccard 相似度比较权限集合和 loader 类集合快速聚类。比对时注意权限集合的相似度高不一定同源因为很多正常应用也申请类似权限但loader_classes和释放文件哈希的组合相似度高基本可以判定同家族。我一般会设一个阈值权限 Jaccard 0.8 且 loader 类有交集就标为“疑似同家族”再人工确认。这套方法不能替代深度分析但能把几十个样本缩小到几个重点省下大量时间。最后说个血泪经验分析这类样本最忌讳的是“想当然”。看到DexClassLoader就说是木马看到权限多就说是恶意都不靠谱。真正有用的做法是固定一套流程——哈希存档、静态提取、动态验证、证据链整理——然后严格执行。我习惯在每个样本分析完后把关键命令和输出存成一个notes.md下次遇到类似样本直接翻记录比重新跑一遍快得多。希望帮到你。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进