Android逆向实战:通过smali/baksmali动态注入DEX代码的完整指南

张开发
2026/4/17 5:27:17 15 分钟阅读

分享文章

Android逆向实战:通过smali/baksmali动态注入DEX代码的完整指南
1. 为什么需要动态注入DEX代码在Android开发中我们经常会遇到需要修改第三方APK或者遗留系统的情况。比如接手一个老项目发现某个功能需要调整但源码已经丢失或者需要对某个APK进行功能扩展但没有开发文档。这时候动态注入DEX代码就成了解决问题的利器。我遇到过这样一个实际案例客户需要在一个已经上线的APK中添加埋点统计功能但这个APK是五年前开发的开发团队早已解散源码也无从查找。通过动态注入DEX代码我们成功在不影响原有功能的情况下实现了埋点统计的添加。动态注入的核心原理是利用Android的DEX文件格式特性。DEX是Android平台的可执行文件包含了应用的字节码。通过反编译DEX文件我们可以获取到smali代码相当于Java字节码的汇编语言修改后再重新编译回DEX文件。2. 准备工作与环境搭建2.1 工具下载与配置首先需要准备以下工具smali/baksmali工具包建议从GitHub官方仓库下载最新版本Java运行环境需要JDK 8或以上版本APKTool用于解包和打包APK文件文本编辑器推荐使用VS Code或Notepad我建议下载fat-release版本的工具包这个版本包含了所有依赖开箱即用。下载后解压到一个固定目录比如我习惯放在D:\AndroidTools\smali目录下。2.2 基础环境检查在开始前先确认Java环境配置正确。打开命令行输入java -version应该能看到类似这样的输出java version 1.8.0_301 Java(TM) SE Runtime Environment (build 1.8.0_301-b09) Java HotSpot(TM) 64-Bit Server VM (build 25.301-b09, mixed mode)如果提示java不是内部或外部命令需要先配置Java环境变量。具体方法这里不赘述网上有很多教程。3. 反编译DEX文件3.1 提取目标DEX文件首先需要从APK中提取DEX文件。可以使用APKTool解包APKapktool d target.apk -o output_dir解包后在output_dir目录下会看到classes.dex文件如果是多DEX应用可能会有classes2.dex等。3.2 使用baksmali反编译拿到DEX文件后使用baksmali进行反编译。命令格式如下java -jar baksmali-3.0.9-fat-release.jar disassemble classes.dex -o output_smali这里有几个实用参数--api指定目标Android API级别比如--api 33对应Android 13--use-locals使用局部变量名而不是寄存器名代码更易读反编译完成后output_smali目录下会生成对应的.smali文件这些就是我们可以直接编辑的代码。4. 分析与修改smali代码4.1 理解smali语法基础smali代码看起来可能有点吓人但其实掌握几个关键点就能上手寄存器表示v0、v1等是局部变量寄存器p0、p1等是参数寄存器方法调用格式Lpackage/name/ClassName;-methodName(LparamType;)ReturnType常见指令invoke-virtual调用实例方法invoke-static调用静态方法const-string定义字符串常量return-void无返回值返回举个例子Java代码Log.d(TAG, Hello World);对应的smali代码const-string v0, TAG const-string v1, Hello World invoke-static {v0, v1}, Landroid/util/Log;-d(Ljava/lang/String;Ljava/lang/String;)I4.2 定位目标方法要修改代码首先需要找到目标方法。我通常的做法是根据功能推测可能的关键类名使用grep或文本编辑器搜索关键字符串分析调用关系确定目标方法比如要修改一个广告展示逻辑可以搜索ad、showAd等关键词。找到目标方法后先完整阅读方法逻辑理解其工作原理再修改。4.3 代码注入实战假设我们要在一个方法开头插入日志打印可以这样操作原始方法.method public showAd()V .registers 3 ... .end method修改后.method public showAd()V .registers 5 # 注意寄存器数量要增加 const-string v3, AdDebug const-string v4, showAd called invoke-static {v3, v4}, Landroid/util/Log;-d(Ljava/lang/String;Ljava/lang/String;)I ...原方法代码... .end method几个注意事项修改.registers声明确保足够寄存器使用新代码不要占用方法参数寄存器p0、p1等保持堆栈平衡每个指令调用前后堆栈状态要一致5. 回编译与测试5.1 使用smali重新编译修改完成后使用smali工具重新编译java -jar smali-3.0.9-fat-release.jar assemble output_smali -o new_classes.dex同样可以使用--api参数指定目标API级别。5.2 替换DEX文件将生成的new_classes.dex替换原APK中的classes.dex。如果是多DEX应用需要注意保持文件名一致classes.dex、classes2.dex等。然后使用APKTool重新打包apktool b output_dir -o modified.apk5.3 签名与安装修改后的APK需要重新签名才能安装。可以使用Android SDK的apksignerapksigner sign --ks my-release-key.keystore modified.apk安装测试时建议先卸载原应用再安装修改版。可以使用adb命令adb install -r modified.apk6. 常见问题与调试技巧6.1 反编译失败处理如果遇到反编译失败可以尝试使用最新版本的smali/baksmali添加--allow-odex参数处理odex文件尝试不同的API级别参数6.2 回编译错误排查回编译常见错误包括寄存器数量不足增加.registers声明类型不匹配检查方法签名是否正确指令参数错误确认指令使用方式6.3 运行时崩溃调试如果修改后应用崩溃可以通过logcat查看错误日志adb logcat -s AndroidRuntime重点关注ClassNotFoundException、NoSuchMethodError等异常这通常说明smali代码有误。7. 高级技巧与最佳实践7.1 动态注入复杂逻辑对于复杂逻辑注入建议先在Java中编写完整代码使用javac编译后用dx工具生成DEX反编译这个DEX获取smali代码将需要的部分移植到目标smali中7.2 保持代码可维护性为了方便后续维护添加详细注释说明修改内容保持代码风格一致记录修改的类和方法7.3 安全注意事项进行代码注入时要注意不要违反软件许可协议只修改自己有权限修改的代码测试要充分避免引入新问题在实际项目中我通常会先在一个测试APK上验证修改方案确认无误后再应用到正式APK。同时会保留完整的修改记录方便后续追溯。

更多文章