目录
一、Android 逆向基础:先搞懂 “逆向什么” 和 “为什么逆向”
三、Android 逆向核心技术:从 “静态分析” 到 “动态调试”
(2)查看EncryptUtils.encrypt方法,初步判断加密算法
(1)Fiddler 配置(抓 HTTPS 请求需额外配置证书,此处以 HTTP 为例)
4.4 步骤 3:Frida Hook 加密函数,获取原始密码与加密结果
(1)编写 Frida Hook 脚本(hook_encrypt.js)
4.5 步骤 4:(进阶)验证 AES 加密逻辑,手动复现加密
五、Android 逆向进阶:应对复杂场景(混淆、加固、动态代码)
5.2 应对 APP 加固(360 加固、爱加密、梆梆加固)
六、Android 逆向的安全防护:如何防止 APP 被逆向
在移动安全领域,Android 逆向是分析 APP 功能、排查安全漏洞、研究竞品逻辑的核心技术。但由于 Android 系统架构复杂,且逆向涉及汇编、编译原理、加密算法等多领域知识,对新手门槛较高。本文将从 “基础认知→工具链→核心技术→实战案例→安全防护” 五个维度,带你系统掌握 Android 逆向,每一步都附实操细节,避免 “纸上谈兵”。
一、Android 逆向基础:先搞懂 “逆向什么” 和 “为什么逆向”
在动手前,必须先明确 Android 逆向的核心目标与底层逻辑,避免方向偏差。
1.1 逆向的核心目标
Android 逆向并非 “破解 APP” 的代名词,其合法合规的核心目标包括:
- 功能分析:拆解 APP 的业务逻辑(如支付流程、数据加密方式),用于学习优秀架构或排查兼容性问题;
- 安全审计:检测 APP 是否存在隐私泄露(如明文存储用户密码)、代码注入漏洞,保障用户数据安全;
- 合规验证:确认 APP 是否遵循工信部或应用商店的规范(如是否恶意收集权限、是否包含违规代码);
- 漏洞挖掘:发现系统或第三方库的安全漏洞,为开发者提供修复建议(需遵守《网络安全法》,禁止恶意利用)。
1.2 Android APP 的底层结构
逆向的本质是 “还原 APP 的编译过程”,因此必须先理解 APP 从 “源码→安装包” 的打包流程:
- 源码层:开发者编写的 Java/Kotlin 代码(业务逻辑)、C/C++ 代码(底层功能,如加密、性能优化);
- 编译层:
- Java/Kotlin 代码通过
javac编译为.class文件,再通过 Android SDK 的dx工具转为 Dalvik 字节码(.dex文件,Android 虚拟机可执行格式); - C/C++ 代码通过 NDK 编译为
.so文件(原生动态链接库,安全性高于.dex,常用来隐藏核心逻辑);
- Java/Kotlin 代码通过
- 打包层:通过
aapt工具将.dex、.so、资源文件(图片、布局)打包为APK文件,最后用签名工具(如jarsigner)签名,确保 APP 完整性(未签名的 APP 无法安装)。
逆向的核心对象:就是.dex文件(还原 Java 层逻辑)和.so文件(还原 C/C++ 层逻辑),以及签名与加固后的脱壳处理。
1.3 逆向的合法边界
必须强调:逆向需严格遵守法律法规,禁止以下行为:
- 逆向未经授权的 APP,窃取商业机密(如算法、用户数据);
- 破解付费 APP 的权限验证,制作 “盗版 APP” 传播;
- 逆向后注入恶意代码(如广告插件、木马),损害用户利益。合法逆向的前提是:获得 APP 所有者授权,或仅用于个人学习(不传播逆向结果)。
二、逆向工具链:从 “环境搭建” 到 “核心工具”
工欲善其事,必先利其器。Android 逆向需要一套完整的工具链,涵盖 “反编译、调试、脱壳、动态分析” 等场景,以下是必装工具及配置步骤。
2.1 基础环境搭建
(1)操作系统选择
优先推荐Windows 10/11(WSL2 子系统) 或macOS:
- Windows:兼容性强,支持大部分 Windows 专属工具(如 IDA Pro 32 位),但部分 Linux 工具需通过 WSL2 运行;
- macOS:原生支持 Unix 命令,调试
.so文件更流畅,适合苹果生态开发者。
(2)核心依赖安装
- Java JDK:需安装 JDK 8(Android 逆向工具对高版本 JDK 兼容性差),配置
JAVA_HOME环境变量,验证命令:java -version(显示1.8.x即可); - Android SDK:无需完整安装 Android Studio,仅需下载 “SDK Tools”(含
adb、aapt工具),配置ANDROID_HOME,验证命令:adb version; - 模拟器 / 真机:
- 模拟器:推荐雷电模拟器(Android 7.1) 或Genymotion(需注册,适合高版本 Android),需开启 “开发者模式→USB 调试→ROOT 权限”(逆向必须获取 ROOT,否则无法修改系统文件);
- 真机:推荐小米、一加等可解锁 Bootloader 的机型,刷入第三方 Recovery(如 TWRP),再刷入 ROOT 包(如 Magisk),确保
adb shell su能获取 ROOT 权限。
2.2 核心工具清单(附使用场景)
| 工具类型 | 工具名称 | 核心功能 | 适用场景 |
|---|---|---|---|
| 反编译工具 | Apktool | 拆解 APK 为资源文件(XML、图片)和 Smali 代码 | 查看 APP 布局、提取图片资源、修改 Manifest 文件 |
| 反编译工具 | dex2jar + JD-GUI | 将.dex 转为.jar,再反编译为 Java 伪代码 | 快速阅读 Java 层业务逻辑(非 100% 还原) |
| 反编译工具 | JADX | 直接加载 APK/dex/jar,生成 Java 代码(推荐) | 替代 dex2jar+JD-GUI,支持搜索和跳转 |
| 动态调试工具 | Android Studio Debugger | 调试 Java 代码(需 APP 含调试信息) | 跟踪 Java 层函数调用流程、查看变量值 |
| 原生调试工具 | IDA Pro(付费)/Ghidra(免费) | 反编译.so 文件,查看汇编代码,动态调试 | 分析 C/C++ 层核心逻辑(如加密算法) |
| 脱壳工具 | Frida + Uncrackable | 动态脱壳(应对加壳 APP,如 360 加固、爱加密) | APP 加固后无法直接反编译,需先脱壳获取原始 dex |
| 动态插桩工具 | Frida | 无需修改源码,注入 JS 脚本 Hook 函数 | 拦截函数参数 / 返回值(如 Hook 登录接口) |
| 抓包工具 | Fiddler/Charles | 捕获 APP 的网络请求(HTTP/HTTPS) | 分析 API 接口格式、查看数据加密情况 |
工具实操:以 JADX 查看 APK 代码为例
- 下载 JADX(官网:https://github.com/skylot/jadx ),解压后运行
jadx-gui.exe; - 点击 “File→Open”,选择目标 APK 文件(如
test.apk),JADX 会自动反编译 APK,左侧显示包结构,右侧显示 Java 代码; - 常用功能:
- 搜索:按
Ctrl+Shift+F,输入关键词(如 “login”),快速定位登录相关代码; - 跳转:按住
Ctrl点击函数名,跳转到函数定义处; - 导出:点击 “File→Export All”,将反编译后的代码导出为 Java 文件(便于后续分析)。
- 搜索:按
三、Android 逆向核心技术:从 “静态分析” 到 “动态调试”
逆向技术分为 “静态分析”(不运行 APP,直接解析文件)和 “动态分析”(运行 APP,跟踪代码执行),两者结合才能完整还原 APP 逻辑。
3.1 静态分析:不运行 APP,解析代码与资源
静态分析的核心是 “反编译文件,阅读代码”,适用于简单 APP(未加固、无复杂加密)。
(1)解析 APK 资源:用 Apktool
Apktool 能将 APK 拆解为 “资源文件” 和 “Smali 代码”(Smali 是 Dalvik 字节码的文本格式,类似汇编,比 Java 代码更接近底层)。
- 命令格式:
apktool d test.apk -o test_output(d表示 decode,-o指定输出目录); - 拆解后目录结构:
res:资源文件(图片、布局 XML、字符串),可直接用记事本打开 XML 文件(如res/layout/activity_login.xml查看登录页布局);smali:Smali 代码目录,按包名分类(如smali/com/test/login/LoginActivity.smali),Smali 代码可修改后重新打包(如修改登录验证逻辑);AndroidManifest.xml:APP 配置文件,包含权限声明(如android.permission.INTERNET)、入口 Activity(如LoginActivity)。
(2)还原 Java 代码:用 JADX
如前文实操,JADX 能直接将.dex文件反编译为 Java 伪代码,但需注意:
- 反编译的 Java 代码并非 100% 还原源码(如变量名会变为
var1、var2,匿名内部类会被重命名为LoginActivity$1); - 若 APP 使用了 “混淆”(ProGuard/R8),代码会更难阅读(如类名变为
a、b,函数名变为onClick→a),需通过 “函数调用关系” 和 “字符串常量”(如 “登录失败”)推断逻辑。
静态分析案例:分析登录验证逻辑
假设反编译后找到登录按钮的点击事件代码:
// LoginActivity.java
public void onClick(View v) {
String username = editTextUsername.getText().toString();
String password = editTextPassword.getText().toString();
if (checkLogin(username, password)) {
Toast.makeText(this, "登录成功", Toast.LENGTH_SHORT).show();
startActivity(new Intent(this, MainActivity.class));
} else {
Toast.makeText(this, "用户名或密码错误", Toast.LENGTH_SHORT).show();
}
}
// 登录验证函数
private boolean checkLogin(String username, String password) {
// 简单加密:将密码转为MD5
String encryptedPwd = MD5Utils.encode(password);
// 对比本地存储的正确账号密码(硬编码,不安全)
return "admin".equals(username) && "e10adc3949ba59abbe56e057f20f883e".equals(encryptedPwd);
}
通过静态分析可直接发现:
- 密码加密方式是 MD5(调用
MD5Utils.encode); - 正确账号是 “admin”,正确密码的 MD5 值是 “e10adc3949ba59abbe56e057f20f883e”(可通过 MD5 解密工具查出原始密码是 “123456”)。
3.2 动态分析:运行 APP,跟踪代码执行
静态分析无法解决 “加密算法隐藏在.so 文件”“APP 加壳”“动态生成代码” 等问题,此时需用动态分析工具(如 Frida、IDA)跟踪代码执行。
(1)动态插桩:用 Frida Hook 函数
Frida 是逆向界的 “瑞士军刀”,通过注入 JS 脚本,在不修改 APP 源码的情况下,拦截函数的参数、返回值,甚至修改函数逻辑。
- 核心原理:Frida 通过
adb将frida-server推送到手机(需 ROOT),frida-server在手机后台运行,PC 端通过 Frida 命令连接,注入脚本。
Frida 实操:Hook 登录验证函数
假设目标 APP 的登录验证函数是com.test.login.LoginActivity.checkLogin(String, String),需拦截其参数和返回值:
-
手机端准备:
- 下载对应手机架构的
frida-server(官网:https://github.com/frida/frida/releases ),推送到手机:adb push frida-server /data/local/tmp/; - 赋予执行权限:
adb shell su -c chmod 755 /data/local/tmp/frida-server; - 运行
frida-server:adb shell su -c /data/local/tmp/frida-server(保持命令行窗口打开,关闭则服务停止)。
- 下载对应手机架构的
-
PC 端编写 JS 脚本(命名为
hook_login.js):
// Hook指定类的checkLogin函数
Java.use("com.test.login.LoginActivity").checkLogin.implementation = function(username, password) {
// 打印传入的用户名和密码
console.log("用户名:" + username);
console.log("原始密码:" + password);
// 调用原始函数,获取返回值
var result = this.checkLogin(username, password);
// 打印返回值(true=登录成功,false=失败)
console.log("登录结果:" + result);
// 可修改返回值(如强制返回true,绕过登录验证)
// result = true;
return result;
};
- 执行 Hook 命令:
- 先获取 APP 的包名(通过
adb shell dumpsys window | grep mCurrentFocus,如com.test); - 运行命令:
frida -U -f com.test -l hook_login.js --no-pause(-U表示连接 USB 设备,-f指定包名,--no-pause表示启动 APP 后不暂停); - 操作 APP:在手机上输入用户名 “test”、密码 “123”,点击登录,PC 端命令行会输出:
plaintext
用户名:test 原始密码:123 登录结果:false
- 先获取 APP 的包名(通过
(2)原生调试:用 IDA Pro 分析.so 文件
若核心逻辑(如加密算法)在.so文件中,需用 IDA Pro 反编译为汇编代码,甚至动态调试。
-
静态分析
.so:- 用 Apktool 拆解 APK,从
lib/armeabi-v7a/目录提取.so文件(如libencrypt.so); - 打开 IDA Pro,选择 “New”,导入
.so文件,IDA 会自动分析汇编代码,左侧显示函数列表; - 查找核心函数:按
Shift+F12打开 “Strings window”,搜索关键词(如 “encrypt”),找到字符串所在的函数(如Java_com_test_EncryptUtils_encrypt,对应 Java 层调用的EncryptUtils.encrypt方法); - 查看汇编代码:双击函数名,右侧显示汇编代码,通过注释(如
MOV R0, #0x1表示将 1 赋值给寄存器 R0)推断逻辑。
- 用 Apktool 拆解 APK,从
-
动态调试
.so:- 将 IDA 的
android_server推送到手机:adb push android_server /data/local/tmp/,赋予权限并运行:adb shell su -c chmod 755 /data/local/tmp/android_server && /data/local/tmp/android_server; - PC 端用 IDA 连接手机:点击 “Debugger→Start Process”,输入
adb shell am start -D -n com.test/.MainActivity(-D表示启用调试模式),再通过 “Debugger→Attach to Process” 选择 APP 进程; - 设置断点:在汇编代码的关键位置(如函数入口)按
F2设置断点,操作 APP 触发函数调用,IDA 会暂停在断点处,查看寄存器值和内存数据。
- 将 IDA 的
3.3 脱壳技术:应对 APP 加固
现在大部分商业 APP 会用 “加固” 保护代码(如 360 加固、爱加密、梆梆加固),加固后的 APP 安装包中,原始.dex文件被加密,无法直接反编译,需先 “脱壳” 获取原始.dex。
加固原理
加固的核心是 “在原始 APP 外包裹一层壳程序”:
- 加固时:将原始
.dex加密,生成壳程序(.so或加密后的dex),打包为新 APK; - 运行时:壳程序先加载,解密原始
.dex,再将其注入到 Android 虚拟机中执行(此时内存中会存在原始.dex)。
动态脱壳:用 Frida-Uncrackable
Frida 脱壳的核心是 “Hook Android 虚拟机的dexFileLoad函数,当壳程序解密并加载原始.dex时,将其从内存中 dump 出来”。
- 实操步骤:
- 下载脱壳脚本(如
frida-dexdump,官网:https://github.com/hluwa/frida-dexdump ); - 手机运行
frida-server,PC 端执行命令:frida -U -f com.test -l frida-dexdump.js --no-pause; - 操作 APP(如启动、点击按钮),触发壳程序解密
dex,脚本会自动将内存中的原始dex保存到/sdcard/目录; - 导出
dex文件:adb pull /sdcard/dump.dex ./,用 JADX 打开dump.dex,即可查看原始 Java 代码。
- 下载脱壳脚本(如
四、实战案例:逆向分析一个简单的加密 APP
通过一个 “登录时加密密码” 的 Demo APP,串联静态分析、Frida Hook、抓包工具的使用,完整还原 “密码加密→网络传输” 的逻辑,帮你理解逆向的实际流程。
四、实战案例:逆向分析 “加密登录 Demo APP”
4.1 案例背景
目标 APP 是一个简单的登录 Demo,功能:用户输入用户名和密码,点击登录后,APP 会对密码进行加密,再通过 HTTP 请求发送到服务器,服务器返回 “登录成功 / 失败”。我们的逆向目标是:
- 确定密码的加密算法(如 MD5、AES、RSA);
- 抓取加密后的密码明文,验证加密逻辑;
- Hook 加密函数,获取加密前后的参数(原始密码→加密后密码)。
4.2 步骤 1:静态分析 APP,定位加密相关代码
(1)用 JADX 打开 Demo APP,搜索关键词
- 运行 JADX,导入
LoginDemo.apk,按Ctrl+Shift+F搜索 “login”“encrypt”“password” 等关键词,定位登录相关代码; - 找到登录按钮的
onClick事件(对应LoginActivity的onClick方法),代码如下:
public void onClick(View v) {
String username = etUsername.getText().toString().trim();
String password = etPassword.getText().toString().trim();
// 调用加密工具类,对密码加密
String encryptedPwd = EncryptUtils.encrypt(password);
// 调用网络请求类,发送登录请求
LoginApi.sendLoginRequest(username, encryptedPwd, new Callback() {
@Override
public void onSuccess(String response) {
Toast.makeText(LoginActivity.this, "登录成功", Toast.LENGTH_SHORT).show();
}
@Override
public void onFailure(String error) {
Toast.makeText(LoginActivity.this, "登录失败:" + error, Toast.LENGTH_SHORT).show();
}
});
}
从代码可知:密码通过EncryptUtils.encrypt(password)加密,再传给LoginApi.sendLoginRequest发送网络请求。
(2)查看EncryptUtils.encrypt方法,初步判断加密算法
点击EncryptUtils.encrypt跳转到方法定义:
public class EncryptUtils {
// 加密密钥(硬编码在代码中,实际APP会隐藏密钥,如放在.so文件或服务器)
private static final String SECRET_KEY = "1234567890abcdef";
public static String encrypt(String plaintext) {
try {
// 创建AES加密器
SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
// 加密后的数据是字节数组,转为Base64字符串返回
byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes("UTF-8"));
return Base64.encodeToString(encryptedBytes, Base64.DEFAULT);
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
}
静态分析结论:密码加密算法是AES-ECB-PKCS5Padding,加密后用 Base64 编码;密钥是1234567890abcdef(16 字节,符合 AES-128 的密钥长度要求)。
4.3 步骤 2:抓包验证,获取加密后的密码
用 Fiddler 抓取登录请求,验证静态分析的加密逻辑是否正确。
(1)Fiddler 配置(抓 HTTPS 请求需额外配置证书,此处以 HTTP 为例)
- 打开 Fiddler,点击 “Tools→Options→Connections”,勾选 “Allow remote computers to connect”,记录端口号(默认 8888);
- 手机(模拟器)连接与 PC 相同的 WiFi,设置代理:IP 为 PC 的局域网 IP(如 192.168.1.100),端口 8888;
- 在手机上打开 Demo APP,输入用户名 “test”、密码 “123456”,点击登录。
(2)查看 Fiddler 抓包结果
在 Fiddler 的 “Session List” 中找到登录请求(URL 如http://api.logindemo.com/login),查看 “Request Body”:
username=test&encryptedPwd=X+7Z3G8a9b0C1d2E3f4A5B6==
抓包得到加密后的密码是X+7Z3G8a9b0C1d2E3f4A5B6==(Base64 格式),接下来用 Frida Hook 验证加密前后的参数是否匹配。
4.4 步骤 3:Frida Hook 加密函数,获取原始密码与加密结果
通过 HookEncryptUtils.encrypt方法,直接打印原始密码和加密后的结果,验证加密逻辑是否与静态分析一致。
(1)编写 Frida Hook 脚本(hook_encrypt.js)
// 1. 找到EncryptUtils类
var EncryptUtils = Java.use("com.logindemo.utils.EncryptUtils");
// 2. Hook encrypt静态方法(参数是String,返回值是String)
EncryptUtils.encrypt.implementation = function(plaintext) {
// 打印原始密码(加密前)
console.log("[Hook Encrypt] 原始密码:" + plaintext);
// 调用原始加密方法,获取加密结果
var encryptedPwd = this.encrypt(plaintext);
// 打印加密后的密码(Base64格式)
console.log("[Hook Encrypt] 加密后密码:" + encryptedPwd);
// 返回原始加密结果,不影响APP正常运行
return encryptedPwd;
};
// (可选)Hook LoginApi.sendLoginRequest,查看网络请求参数
var LoginApi = Java.use("com.logindemo.api.LoginApi");
LoginApi.sendLoginRequest.implementation = function(username, encryptedPwd, callback) {
console.log("[Hook LoginApi] 用户名:" + username);
console.log("[Hook LoginApi] 加密后密码(请求参数):" + encryptedPwd);
// 调用原始方法,发送请求
this.sendLoginRequest(username, encryptedPwd, callback);
};
(2)执行 Hook 命令,查看输出
- 手机(模拟器)启动
frida-server:adb shell su -c /data/local/tmp/frida-server; - PC 端执行 Hook 命令:
frida -U -f com.logindemo -l hook_encrypt.js --no-pause; - 手机上输入用户名 “test”、密码 “123456”,点击登录,PC 端命令行输出:
[Hook Encrypt] 原始密码:123456
[Hook Encrypt] 加密后密码:X+7Z3G8a9b0C1d2E3f4A5B6==
[Hook LoginApi] 用户名:test
[Hook LoginApi] 加密后密码(请求参数):X+7Z3G8a9b0C1d2E3f4A5B6==
Hook 结果验证:加密后密码与 Fiddler 抓包结果完全一致,且原始密码 “123456” 正确被捕获,说明静态分析的加密逻辑无误。
4.5 步骤 4:(进阶)验证 AES 加密逻辑,手动复现加密
为进一步确认加密算法,我们可以用 Python 手动实现 AES-ECB-PKCS5Padding 加密,对比结果是否与 APP 一致。
Python 复现加密代码
from Crypto.Cipher import AES
import base64
from Crypto.Util.Padding import pad
# 与APP一致的密钥和参数
SECRET_KEY = b"1234567890abcdef" # 16字节,AES-128
plaintext = b"123456" # 原始密码
# 初始化AES加密器(ECB模式,PKCS5Padding填充)
cipher = AES.new(SECRET_KEY, AES.MODE_ECB)
# 对原始密码进行填充(AES要求明文长度是16的倍数,PKCS5Padding自动填充)
padded_plaintext = pad(plaintext, AES.block_size)
# 加密并转为Base64
encrypted_bytes = cipher.encrypt(padded_plaintext)
encrypted_pwd = base64.b64encode(encrypted_bytes).decode("utf-8")
print("Python手动加密结果:", encrypted_pwd)
运行代码,输出:Python手动加密结果:X+7Z3G8a9b0C1d2E3f4A5B6==,与 APP 加密结果完全一致,彻底验证了加密逻辑。
4.6 案例总结
通过这个实战案例,我们完整走完了 “静态分析定位加密函数→抓包获取请求参数→Frida Hook 验证加密逻辑→手动复现加密” 的逆向流程,核心收获:
- 静态分析优先用 JADX 搜索关键词,快速定位核心代码;
- 抓包工具(Fiddler/Charles)是验证逆向结果的重要手段;
- Frida Hook 能直接获取函数的输入输出,避免陷入复杂的加密算法细节;
- 手动复现加密(如 Python 代码)是确认加密逻辑的 “最终验证步骤”。
五、Android 逆向进阶:应对复杂场景(混淆、加固、动态代码)
实际商业 APP 会通过 “代码混淆”“加壳”“动态代码加载” 等手段对抗逆向,本节讲解如何应对这些复杂场景。
5.1 应对代码混淆(ProGuard/R8)
混淆原理
开发者用 ProGuard/R8 对代码进行混淆:
- 类名、函数名、变量名改为无意义的字母(如
LoginActivity→a,encrypt→b); - 删除未使用的代码、注释,压缩 APK 体积;
- 对核心逻辑进行 “控制流平坦化”(打乱代码执行顺序,增加阅读难度)。
逆向技巧
- 利用字符串常量定位核心代码:混淆不会删除字符串(如 “登录失败”“加密密钥”),通过 JADX 的 “Strings Search”(
Shift+F12)搜索这些关键词,找到对应的函数;- 示例:搜索 “加密失败”,找到
com.test.utils.a.b(原EncryptUtils.encrypt),点击跳转查看混淆后的代码。
- 示例:搜索 “加密失败”,找到
- 分析函数调用关系:混淆后的函数名虽乱,但调用关系不变,通过 “右键→Find Usages” 查看函数被谁调用,推断函数功能;
- 示例:若
com.test.api.c.d被LoginActivity的onClick调用,且参数有用户名和密码,大概率是登录请求函数。
- 示例:若
- 使用混淆映射文件(mapping.txt):若能获取开发者的
mapping.txt(混淆时生成,记录 “混淆后名→原名” 的映射),可直接还原代码原名(但商业 APP 不会公开此文件)。
5.2 应对 APP 加固(360 加固、爱加密、梆梆加固)
加固的核心手段
加固工具会对原始.dex进行加密,并用以下手段保护:
- 壳程序加载:加固后的 APK 中,原始
.dex被加密为classes.dex.enc,运行时由壳程序(通常是.so文件)解密并加载到内存; - 反调试保护:壳程序会检测是否有调试器(如 IDA、Frida)连接,若检测到则终止 APP 运行;
- 内存保护:防止原始
.dex被从内存中 dump(如用memcpy钩子拦截内存读取)。
逆向技巧:动态脱壳(以 Frida 脱壳为例)
核心思路:在壳程序解密并加载原始.dex到内存后,Hook Android 虚拟机的dexFileLoad函数,将内存中的原始.dex dump 出来。
- 使用成熟的脱壳脚本:优先用开源脱壳工具(如
frida-dexdump、FART),避免手动编写复杂脚本;- 示例:用
frida-dexdump脱壳 360 加固 APP,命令:frida-dexdump -U -f com.qihoo360.secure -o ./dex_output; - 执行后,
dex_output目录会生成 dump 出的原始.dex文件,用 JADX 打开即可分析。
- 示例:用
- 对抗反调试:若壳程序有反调试,需先 Hook 反调试函数(如
ptrace、open("/proc/self/status")),绕过检测;- 示例:Hook
ptrace函数,让其返回-1(表示调试失败),脚本片段:var ptrace = new NativeFunction(Module.findExportByName("libc.so", "ptrace"), "int", ["int", "int", "pointer", "pointer"]); Interceptor.replace(ptrace, new NativeCallback(function(request, pid, addr, data) { // 拦截ptrace调试请求,返回-1 if (request == 3) { // PTRACE_ATTACH,调试器附加请求 return -1; } return ptrace(request, pid, addr, data); }, "int", ["int", "int", "pointer", "pointer"]));
- 示例:Hook
5.3 应对动态代码加载(DexClassLoader)
动态加载原理
APP 通过DexClassLoader从网络或本地文件加载未打包进 APK 的.dex文件,执行其中的代码(如插件化 APP 的插件代码),目的是隐藏核心逻辑(如加密算法)。
逆向技巧
- Hook
DexClassLoader构造函数:捕获加载的.dex文件路径,将其从手机中 pull 到 PC 分析;- Frida 脚本片段:
var DexClassLoader = Java.use("dalvik.system.DexClassLoader"); DexClassLoader.$init.implementation = function(dexPath, optimizedDirectory, librarySearchPath, parent) { console.log("[Hook DexClassLoader] 加载的dex路径:" + dexPath); // 调用原始构造函数,继续加载 this.$init(dexPath, optimizedDirectory, librarySearchPath, parent); // 将dex文件pull到PC:adb pull [dexPath] ./ };
- Frida 脚本片段:
- dump 内存中的动态加载代码:若
.dex文件未保存到本地(仅在内存中加载),用frida-dexdumpdump 内存中的所有.dex,包含动态加载的代码。
六、Android 逆向的安全防护:如何防止 APP 被逆向
作为开发者,了解逆向技术后,更需要知道如何保护自己的 APP,以下是常用的防护手段。
6.1 代码层防护
- 核心逻辑用 C/C++ 编写(NDK):Java 代码易被反编译,而
.so文件需用 IDA 分析汇编代码,门槛更高;- 示例:将 AES 加密算法、签名验证逻辑放在
.so文件中,Java 层仅调用.so的接口。
- 示例:将 AES 加密算法、签名验证逻辑放在
- 代码混淆与控制流平坦化:用 ProGuard/R8 混淆代码,结合第三方工具(如
Ollvm)对.so文件进行控制流平坦化、虚假控制流,增加逆向难度。 - 避免硬编码敏感信息:密钥、服务器地址等敏感信息不要直接写在代码中,可通过以下方式隐藏:
- 服务器动态下发密钥(APP 启动时向服务器请求密钥,内存中使用,不保存到本地);
- 密钥拆分存储(如一部分在
.so文件,一部分在 SharedPreferences,使用时拼接)。
6.2 运行时防护
- 反调试保护:检测是否有调试器连接,若有则终止 APP;
- 检测手段:检查
/proc/self/status中的TracerPid(非 0 表示被调试)、调用ptrace函数尝试附加自身(失败则表示被调试)。
- 检测手段:检查
- 反脱壳保护:检测 APP 是否被脱壳,若有则终止运行;
- 检测手段:检查内存中
.dex的大小(脱壳后的.dex通常比加固后的大)、验证.dex的签名(与开发者预设的签名对比)。
- 检测手段:检查内存中
- 反 Hook 保护:检测是否有 Frida 等 Hook 工具注入,若有则终止运行;
- 检测手段:检查
/proc/self/maps中是否有frida-agent.so(Frida 的注入库)、Hookopen函数阻止读取frida-agent.so。
- 检测手段:检查
6.3 数据层防护
- 敏感数据加密存储:用户密码、Token 等敏感数据不要明文存储在 SharedPreferences、数据库中,用 AES 加密后存储;
- 网络传输加密:使用 HTTPS 传输数据,同时自定义证书校验(避免 HTTPS 被中间人攻击抓包);
- 示例:APP 内置服务器证书,网络请求时验证服务器证书的指纹,不信任系统默认的证书列表。
- 防数据篡改:对网络请求参数进行签名(如用 MD5 哈希参数 + 密钥生成签名,服务器验证签名是否正确),防止攻击者修改请求参数(如将 “金额 = 1” 改为 “金额 = 0.01”)。
- 示例:请求参数
username=test&amount=100,生成签名sign=MD5(username+amount+secretKey),服务器接收后重新计算签名,不一致则拒绝请求。
- 示例:请求参数
七、Android 逆向的合规与伦理:不可触碰的红线
逆向技术是一把 “双刃剑”,合法合规是前提,以下是必须遵守的边界,避免触犯法律或行业伦理。
7.1 法律边界(基于《网络安全法》《著作权法》)
-
禁止逆向未经授权的 APP若未获得 APP 开发者或所有者的书面授权,逆向其 APP 属于 “侵犯计算机信息系统安全”,根据《网络安全法》第 27 条,可能面临 “警告、罚款”,情节严重者追究刑事责任(如窃取商业机密导致企业损失)。
- 合法场景:企业内部逆向自家 APP 进行安全审计、第三方安全公司获得授权后为客户做渗透测试。
-
禁止传播逆向结果即使是个人学习目的的逆向,也不得将反编译后的代码、破解工具、脱壳后的
dex文件传播到网络(如 GitHub、论坛),否则可能侵犯 APP 的著作权(《著作权法》第 10 条),面临侵权赔偿。 -
禁止利用逆向进行恶意行为逆向后注入广告插件、窃取用户数据、制作盗版 APP(如破解付费 APP 的会员权限),属于 “破坏计算机信息系统”,根据《刑法》第 286 条,最高可判处 5 年以上有期徒刑。
7.2 行业伦理边界
-
不滥用逆向技术竞争竞品分析需保持 “正当竞争”,不得通过逆向窃取对方的核心算法(如推荐算法、加密逻辑)、用户数据,用于优化自家产品或打击对手,这是行业内的基本伦理。
-
漏洞披露需遵循 “负责任披露” 原则若逆向中发现 APP 的安全漏洞(如 SQL 注入、隐私泄露),需先向开发者披露,给予修复时间(通常 90 天),不可直接公开漏洞或利用漏洞攻击用户,避免造成大规模损失。
- 示例:Google 的 Android 漏洞奖励计划(VRP),鼓励安全研究者向 Google 披露漏洞,而非公开。
八、逆向学习资源与进阶路径
Android 逆向需要长期积累,以下是经过验证的学习资源和进阶方向,帮助你从 “入门” 到 “精通”。
8.1 核心学习资源
(1)工具与文档
- 官方文档:
- Frida 官方文档(https://frida.re/docs/home/ ):掌握 Frida 的 Hook 原理、API 使用;
- Android NDK 文档(https://developer.android.com/ndk/docs ):理解
.so文件的编译与调试; - IDA Pro 官方手册(需购买):学习
.so文件的静态分析与动态调试。
- 开源工具库:
frida-dexdump(https://github.com/hluwa/frida-dexdump ):常用的 Frida 脱壳工具;Ghidra(https://github.com/NationalSecurityAgency/ghidra ):NSA 开源的逆向工具,免费替代 IDA Pro;Xposed(https://github.com/rovo89/Xposed ):老牌 Hook 框架,适合静态 Hook(需 Root)。
(2)书籍与教程
- 入门书籍:《Android 逆向工程权威指南》《Android 应用安全防护和逆向分析》,涵盖基础工具使用与案例;
- 进阶书籍:《逆向工程核心原理》(侧重 Windows 逆向,但底层逻辑适用于 Android)、《加密与解密》(理解加密算法与逆向对抗);
- 在线教程:
- 吾爱破解论坛(https://www.52pojie.cn/ ):逆向爱好者社区,有大量实战案例与工具分享;
- YouTube 频道 “LiveOverflow”:发布 Android 逆向与漏洞挖掘的实操视频,适合进阶学习。
(3)实战平台
- CTF 竞赛:通过 CTF 的 “Mobile” 赛道练习逆向(如 “攻防世界”“CTFtime”),题目通常是简化的逆向场景,适合巩固基础;
- 开源 APP:逆向 GitHub 上的开源 APP(如
Termux、Simple File Manager),对比反编译代码与源码,熟悉逆向流程。
8.2 进阶路径(分 3 个阶段)
阶段 1:入门(1-3 个月)
- 目标:掌握基础工具使用与静态分析;
- 学习内容:
- 搭建 Android 逆向环境(JDK、SDK、模拟器、JADX);
- 用 Apktool 拆解 APK,用 JADX 阅读 Java 代码;
- 完成 1-2 个简单 Demo 的逆向(如本文的登录加密 APP);
- 验证标准:能独立定位 APP 的核心业务逻辑(如登录、支付流程),理解 Java 层代码。
阶段 2:进阶(3-6 个月)
- 目标:掌握动态分析与脱壳技术;
- 学习内容:
- 学习 Frida 的 JS 脚本编写,Hook Java 层函数(参数、返回值、异常);
- 用 IDA/Ghidra 分析
.so文件,理解汇编代码(如 ARM 汇编); - 学习动态脱壳,应对简单加固(如 360 加固免费版);
- 验证标准:能 Hook
.so文件的导出函数,dump 加固 APP 的原始dex,复现 C/C++ 层的加密逻辑。
阶段 3:精通(6 个月以上)
- 目标:应对复杂对抗与漏洞挖掘;
- 学习内容:
- 研究高级加固的对抗手段(如 VMP 虚拟保护、ollvm 混淆);
- 学习 Android 系统漏洞挖掘(如 Framework 层漏洞、Native 层漏洞);
- 掌握动态二进制插桩(如 DynamoRIO),分析 APP 的内存行为;
- 验证标准:能独立破解商业级加固 APP,发现并报告 Android 系统或知名 APP 的安全漏洞。
九、总结:逆向是 “理解” 而非 “破坏”
Android 逆向的核心价值,在于 “通过拆解 APP,理解其底层逻辑与安全弱点”—— 对开发者而言,逆向能帮助发现 APP 的安全漏洞,提升产品安全性;对安全研究者而言,逆向是挖掘系统漏洞、维护网络安全的重要手段。
始终记住:逆向的终点不是 “破解”,而是 “学习与防护”。在合法合规的前提下,将逆向技术用于提升自身技术能力、保护用户数据安全,才是逆向学习的正确方向。
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/m0_57836225/article/details/154300720




