ykrishhh/android-reverse-engineering
GitHub: ykrishhh/android-reverse-engineering
一份涵盖 APK 分析、Smali、Frida 及 Xposed 的 Android 逆向工程完整实战指南与代码工具包。
Stars: 0 | Forks: 0
# Android 逆向工程工具包
[](https://github.com/ykrishhh/android-reverse-engineering)
[](https://github.com/ykrishhh/android-reverse-engineering/network/members)
[](https://opensource.org/licenses/MIT)
[](https://www.android.com/)
[](https://frida.re)
[](https://github.com/rovo89/Xposed)
[](https://discord.gg/android-re)
这是我刚开始接触 Android 应用逆向时希望能拥有的所有东西。包含实用的工作流、可直接复制粘贴的代码片段,以及真正好用的工具配置。
## 目录
- [环境配置与工具](#setup--tools)
- [APK 分析工作流](#apk-analysis-workflow)
- [静态分析](#static-analysis)
- [Smali 基础](#smali-basics)
- [Frida 动态插桩](#frida-dynamic-instrumentation)
- [Xposed 框架](#xposed-framework)
- [Kernel Hooking 概念](#kernel-hooking-concepts)
- [DEX 文件格式](#dex-file-format)
- [Native 库分析](#native-library-analysis)
- [反调试绕过](#anti-debug-bypasses)
- [练习应用](#practice-apps)
- [资源](#resources)
- [免责声明](#disclaimer)
## 环境配置与工具
在任何平台上进行 Android 逆向工程所需的基础工具。
### 核心工具安装
```
# --- Linux / macOS ---
# jadx — Dex 到 Java 反编译器
brew install jadx # macOS
sudo apt install jadx # Debian/Ubuntu
# apktool — APK 资源解码器和重打包工具
brew install apktool # macOS
sudo apt install apktool # Debian/Ubuntu
# Android SDK (adb, dexdump 等)
# 从 https://developer.android.com/studio 下载
# Frida
pip install frida-tools frida
# dex2jar
# 从 https://github.com/pxb1988/dex2jar/releases 下载
# --- Termux (Android) ---
pkg install jadx apktool dex2jar
pip install frida-tools frida objection
```
### 推荐工具列表
| 工具 | 用途 | 安装方式 |
|------|---------|---------|
| **jadx** | 将 DEX 反编译为可读的 Java 源码 | `brew install jadx` |
| **apktool** | 解码/重构 APK 资源、Smali | `brew install apktool` |
| **Frida** | 动态插桩和 hooking | `pip install frida-tools` |
| **Xposed Framework** | 无需修改 APK 的系统级 hooking | [GitHub](https://github.com/rovo89/Xposed) |
| **Objection** | 基于 Frida 的移动安全工具包 | `pip install objection` |
| **dex2jar** | 将 DEX 转换为 JAR 以便进行 Java 分析 | [GitHub](https://github.com/pxb1988/dex2jar) |
| **JEB Pro** | 商业 Android 反编译工具 (AST, Smali, Java) | [网站](https://www.pnfsoftware.com/) |
| **Ghidra** | 免费的逆向工程套件 — 反编译 native .so 库 | [GitHub](https://github.com/NationalSecurityAgency/ghidra) |
| **IDA Pro** | 用于 native code 的工业级标准反汇编器 | [Hex-Rays](https://hex-rays.com/ida-pro/) |
| **Burp Suite** | 用于 API 拦截的 HTTP/HTTPS 代理 | [PortSwigger](https://portswigger.net/burp) |
## APK 分析工作流
我针对每个 APK 遵循的逐步流程。每次都很有效。
```
APK File
│
├─→ 1. RENAME to .zip, EXTRACT
│ └─→ AndroidManifest.xml, classes.dex, lib/, res/
│
├─→ 2. DECOMPILE with jadx
│ └─→ Readable Java source code
│
├─→ 3. DECODE RESOURCES with apktool
│ └─→ Smali code, decoded XML, layouts
│
├─→ 4. ANALYZE with dex2jar + JD-GUI
│ └─→ Browse decompiled JAR interactively
│
├─→ 5. HOOK at runtime with Frida
│ └─→ Bypass SSL pinning, dump data, trace calls
│
└─→ 6. PATCH Smali and REBUILD
└─→ apktool b modified_apk/ -o patched.apk
```
### 快速开始命令
```
# 反编译为 Java 源代码
jadx -d output/ target.apk
# 解码为 Smali + 资源
apktool d target.apk -o decoded/
# 将 DEX 转换为 JAR
d2j-dex2jar target.apk -o target-d2j.jar
# 重新构建修改后的 APK
apktool b decoded/ -o patched.apk
# 对重新构建的 APK 进行签名
apksigner sign --ks my-key.keystore patched.apk
# 安装到设备
adb install patched.apk
```
## 静态分析
在不运行的情况下阅读和理解代码。
### Manifest 分析
```
# 解码 AndroidManifest.xml
apktool d target.apk
cat decoded/AndroidManifest.xml
```
需要检查的关键项:
- **权限**:`uses-permission` 声明能暴露应用的功能权限
- **导出组件**:`android:exported="true"` — 可被其他应用访问
- **Intent 过滤器**:定义哪些 intent 会触发组件
- **Debuggable 标志**:`android:debuggable="true"` 启用调试附加
- **网络安全配置**:明文流量规则
### jadx 深入探索
```
# 带资源反编译
jadx --deobf -d output/ target.apk
# 在所有 class 中搜索特定字符串
grep -r "api_key\|secret\|password\|token" output/sources/
# 查找硬编码 URL
grep -r "http://" output/sources/ | grep -v "https://"
```
### 隐藏 API 枚举
```
# 列出所有导出的 activities, services, receivers
aapt dump badging target.apk | grep -E "activity|service|receiver"
# 完整的 permission 转储
aapt dump permissions target.apk
```
## Smali 基础
Smali 是 Android Dalvik/ART 虚拟机的汇编语言。学习 Smali 是“我能看懂代码”和“我能修改代码”之间的分水岭。
### Smali 语法参考
```
# Class 声明
.class public Lcom/example/MyClass;
.super Ljava/lang/Object;
.source "MyClass.java"
# Method 签名
# method_name(param1_type, param2_type)return_type
.method public login(Ljava/lang/String;Ljava/lang/String;)Z
.locals 4
# .locals defines register count (v0..v3 + p0..p1)
# p0 = "this" (for non-static methods)
# p1, p2 = method parameters
# String assignment
const-string v0, "admin"
# Method invocation
invoke-virtual {p1, v0}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
move-result v1
# Conditional branch
if-eqz v1, :fail
# Return true
const/4 v0, 0x1
return v0
:fail
const/4 v0, 0x0
return v0
.end method
```
### 常见 Smali 指令
| 指令 | 含义 |
|-------------|---------|
| `const-string v0, "text"` | 将字符串加载到寄存器 |
| `const/4 v0, 0x1` | 加载整型常量 |
| `invoke-virtual {v0, v1}, L...;->method(L...;)V` | 调用实例方法 |
| `invoke-static {v0}, L...;->method()V` | 调用静态方法 |
| `move-result v0` | 将返回值移动到寄存器 |
| `return v0` | 从方法返回值 |
| `return-void` | 返回 void |
| `if-eqz v0, :label` | 寄存器等于零时跳转 |
| `if-nez v0, :label` | 寄存器不等于零时跳转 |
| `goto :label` | 无条件跳转 |
| `iget-object v0, p0, L...;->field:L...;` | 读取对象字段 |
| `iput v0, p0, L...;->field:I` | 写入整型字段 |
### 实战:绕过登录检查(Smali 补丁)
```
# BEFORE (原始登录 method):
.method public checkCredentials(Ljava/lang/String;)Z
.locals 2
invoke-virtual {p0, p1}, Lcom/app/Auth;->validate(Ljava/lang/String;)Z
move-result v0
return v0
.end method
# AFTER (已 patch — 始终返回 true):
.method public checkCredentials(Ljava/lang/String;)Z
.locals 2
const/4 v0, 0x1
return v0
.end method
```
## Frida 动态插桩
Frida 将 JavaScript 注入到运行中的进程,实现实时的 hooking 和追踪。在几乎每次任务中,这都是我首选的工具。
### 环境配置
```
pip install frida-tools frida
# 验证服务器是否在设备上运行
adb push frida-server--android- /data/local/tmp/
adb shell "chmod 755 /data/local/tmp/frida-server"
adb shell "/data/local/tmp/frida-server &"
```
### SSL Pinning 绕过
```
// ssl_bypass.js — Bypass SSL certificate pinning
Java.perform(function() {
var TrustManagerImpl = Java.use("com.android.org.conscrypt.TrustManagerImpl");
TrustManagerImpl.verifyChain.implementation = function(untrustedChain, trustAnchorChain, host, clientAuth, ocspData, tlsSctData) {
console.log("[+] SSL pinning bypassed for: " + host);
return untrustedChain;
};
var SSLContext = Java.use("javax.net.ssl.SSLContext");
SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom").implementation = function(km, tm, sr) {
console.log("[+] SSLContext initialized with custom TrustManager");
this.init(km, tm, sr);
};
// OkHttp3 pinning
try {
var CertificatePinner = Java.use("okhttp3.CertificatePinner");
CertificatePinner.check.overload("java.lang.String", "java.util.List").implementation = function(hostname, peerCertificates) {
console.log("[+] OkHttp3 pinning bypassed for: " + hostname);
};
} catch(e) {}
});
```
```
frida -U -l ssl_bypass.js -f com.target.app
```
### 方法追踪
```
// trace_methods.js — Log all calls to a class
Java.perform(function() {
var TargetClass = Java.use("com.target.app.AuthManager");
var methods = TargetClass.class.getDeclaredMethods();
methods.forEach(function(method) {
var methodName = method.getName();
TargetClass[methodName].overloads.forEach(function(overload) {
overload.implementation = function() {
console.log("[*] " + methodName + "(" + Array.from(arguments).join(", ") + ")");
var result = this[methodName].apply(this, arguments);
console.log("[*] " + methodName + " returned: " + result);
return result;
};
});
});
});
```
### 字符串解密
```
// decrypt_strings.js — Hook encryption methods and log plaintext
Java.perform(function() {
var CryptoUtils = Java.use("com.target.app.CryptoUtils");
CryptoUtils.decrypt.overload("java.lang.String").implementation = function(encrypted) {
var result = this.decrypt(encrypted);
console.log("[DECRYPT] " + encrypted + " -> " + result);
return result;
};
});
```
### Root 检测绕过
```
// root_bypass.js — Circumvent common root checks
Java.perform(function() {
var RootDetection = Java.use("com.target.app.Security");
// Bypass isRooted()
RootDetection.isRooted.implementation = function() {
console.log("[+] isRooted() forced false");
return false;
};
// Bypass SafetyNet / Play Integrity
try {
var SafetyNet = Java.use("com.google.android.gms.safetynet.SafetyNetApi");
SafetyNet.attest.overload().implementation = function() {
console.log("[+] SafetyNet attest bypassed");
return null;
};
} catch(e) {}
});
```
### Objection — 让 Frida 更简单
```
pip install objection
# 启动 objection 会话
objection -g com.target.app explore
# 绕过 SSL pinning
android sslpinning disable
# Root 检测绕过
android root disable
# 列出 activities
android hooking list activities
# 监控文件访问
android hooking watch file_access
```
## Xposed 框架
Xposed 通过在框架级别修改 Android 运行时 (ART) 来实现系统级的 hooking。LSPosed 才是你想要的 —— 原版的 Xposed 基本上已经死了。
### 安装(已 Root 的设备)
```
# 安装 LSPosed (现代化的 Xposed fork)
# 从 https://github.com/LSPosed/LSPosed/releases 下载
# 通过 recovery 或 adb 安装
adb push LSPosed-v1.9.2.zip /sdcard/
# 通过 TWRP recovery 刷入
# 或者使用 Magisk Manager 安装 Zygisk + LSPosed 模块
```
### 示例 Xposed 模块
```
// Example: Intercept WebView URL loading
package com.example.xposedhook;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
public class WebViewHook implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) {
if (!lpparam.packageName.equals("com.target.app")) return;
// Hook WebView.loadUrl()
XposedHelpers.findAndHookMethod(
"android.webkit.WebView",
lpparam.classLoader,
"loadUrl",
String.class,
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
String url = (String) param.args[0];
XposedBridge.log("[WebView] Loading: " + url);
// Redirect to our proxy
if (url.contains("target.com")) {
param.args[0] = url.replace("target.com", "attacker-proxy.com");
}
}
}
);
}
}
```
### Hook SharedPreferences
```
// Intercept SharedPreferences reads
XposedHelpers.findAndHookMethod(
"android.app.SharedPreferencesImpl",
lpparam.classLoader,
"getString",
String.class, String.class,
new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) {
String key = (String) param.args[0];
String value = (String) param.getResult();
XposedBridge.log("[SharedPrefs] " + key + " = " + value);
}
}
);
```
## Kernel Hooking 概念
适用于当用户态 hooking 不足以满足需求时的深度系统级插桩。大多数人不需要用到它,但当你需要时,它是无可替代的。
### Linux Kernel Tracepoints
```
// Example: Using kprobes to trace system calls
#include
#include
static int handler_pre(struct kprobe *p, struct pt_regs *regs) {
printk(KERN_INFO "[TRACE] openat called with arg: %s\n",
(char *)regs->regs[1]);
return 0;
}
static struct kprobe kp = {
.symbol_name = "do_sys_openat2",
.pre_handler = handler_pre,
};
static int __init trace_init(void) {
return register_kprobe(&kp);
}
static void __exit trace_exit(void) {
unregister_kprobe(&kp);
}
```
### eBPF (Extended Berkeley Packet Filter)
```
// eBPF program to trace file opens (BCC syntax)
#include
#include
struct event_t {
u32 pid;
char filename[256];
};
BPF_PERF_OUTPUT(events);
int trace_open(struct pt_regs *ctx) {
struct event_t event = {};
event.pid = bpf_get_current_pid_tgid() >> 32;
bpf_probe_read_user_str(&event.filename, sizeof(event.filename),
(void *)PT_REGS_PARM1(ctx));
events.perf_submit(ctx, &event, sizeof(event));
return 0;
}
```
### 概念总结
| 层级 | 技术 | 工具 |
|-------|-----------|------|
| 用户态 | 函数 hooking、JNI 拦截 | Frida, Xposed |
| 框架层 | ART 方法替换 | LSPosed 模块 |
| Native (ELF) | GOT/PLT 补丁、inline hook | Frida Gadget, Substrate |
| Kernel | kprobes, tracepoints, eBPF | bcc, bpftrace |
| 硬件层 | JTAG/SWD、内存转储 | OpenOCD, JTAGulator |
## DEX 文件格式
理解 Dalvik Executable 格式对于高级补丁操作至关重要。大多数人会跳过这一步,但之后就会吃大亏。
### DEX 结构概览
```
DEX File
├── Header (magic, checksum, SHA-1, file size)
├── String IDs (all string literals)
├── Type IDs (class/interface references)
├── Proto IDs (method prototypes)
├── Field IDs (field references)
├── Method IDs (method references)
├── Class Definitions (one per class)
│ ├── class_idx
│ ├── access_flags
│ ├── superclass_idx
│ ├── source_file_idx
│ ├── class_data
│ │ ├── static_fields
│ │ ├── instance_fields
│ │ ├── direct_methods
│ │ └── virtual_methods
│ └── code_item (bytecode)
├── Data Section (annotations, etc.)
└── Map List (section offsets)
```
### 使用 Python 解析 DEX
```
#!/usr/bin/env python3
"""Minimal DEX header parser"""
import struct
def parse_dex_header(filepath):
with open(filepath, 'rb') as f:
magic = f.read(8)
checksum = struct.unpack('
由 ykrishhh 维护 — 安全研究员与开发者
标签:Android逆向工程, Docker支持, Frida, Xposed, 云安全监控, 目录枚举, 移动安全, 静态分析