Android Hook实用教程:逆向必备

Android Hook 实用教程

环境准备

1. 安装 Frida

1
2
3
4
5
# 直接安装 frida-tools
pip install frida-tools

# 查看版本
frida --version

如果无法直接安装,使用虚拟环境方式:

1
2
3
4
5
6
7
8
9
10
11
12
# 创建虚拟环境
python3 -m venv ~/frida-venv

# 激活虚拟环境
source ~/frida-venv/bin/activate

# 在虚拟环境中安装frida
pip install frida frida-tools

# 验证安装
frida --version
frida-ps

每次使用前需要激活虚拟环境:

1
2
3
4
5
6
7
# 激活虚拟环境
source ~/frida-venv/bin/activate

# 然后可以使用frida命令
frida --version
frida-ps
# ... 其他frida命令

2. 设备准备

对于Root设备:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 下载对应架构的frida-server
#查看手机的架构版本
adb shell getprop ro.product.cpu.abi
# ARM64: https://github.com/frida/frida/releases
wget https://github.com/frida/frida/releases/download/17.1.0/frida-server-17.1.0-android-arm64.xz

# 解压并推送到设备
xz -d frida-server-17.1.0-android-arm64.xz
adb push frida-server-17.1.0-android-arm64 /data/local/tmp/frida-server
adb shell chmod 755 /data/local/tmp/frida-server

# 启动frida-server
adb shell /data/local/tmp/frida-server &

# 真机启动问题解决方案:
# 使用nohup后台启动,避免终端断开导致进程结束
adb shell "su -c 'nohup /data/local/tmp/frida-server >/dev/null 2>&1 &'"

# 检查frida-server是否启动成功
adb shell "ps | grep frida"

# 如果看到frida-server进程则启动成功
# 输出示例:root 12345 1 0 15:30:40 ? 00:00:01 /data/local/tmp/frida-server

对于非Root设备:
使用 Frida-gadget 方式,需要重打包APK或使用免root框架。

基础Hook示例

1. Hook 方法调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// hook_method.js
Java.perform(function() {
var targetClass = Java.use("com.example.MainActivity");

// Hook 方法
targetClass.checkPassword.implementation = function(password) {
console.log("[+] checkPassword called with: " + password);

// 调用原方法
var result = this.checkPassword(password);
console.log("[+] Original result: " + result);

// 修改返回值
return true;
};
});

使用方法:

1
frida -U -f com.example.app -l hook_method.js --no-pause

2. Hook 构造函数

1
2
3
4
5
6
7
8
9
Java.perform(function() {
var User = Java.use("com.example.User");

// Hook 构造函数
User.$init.overload('java.lang.String', 'int').implementation = function(name, age) {
console.log("[+] User constructor called: " + name + ", " + age);
return this.$init(name, age);
};
});

3. Hook 静态方法

1
2
3
4
5
6
7
8
9
10
Java.perform(function() {
var Utils = Java.use("com.example.Utils");

Utils.encrypt.implementation = function(data) {
console.log("[+] Utils.encrypt called with: " + data);
var result = this.encrypt(data);
console.log("[+] Encrypted result: " + result);
return result;
};
});

常用Hook场景

1. 绕过SSL Pinning

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// ssl_bypass.js
Java.perform(function() {
// Hook X509TrustManager
var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');
var SSLContext = Java.use('javax.net.ssl.SSLContext');

var TrustManager = Java.registerClass({
name: 'com.frida.TrustManager',
implements: [X509TrustManager],
methods: {
checkClientTrusted: function(chain, authType) {},
checkServerTrusted: function(chain, authType) {},
getAcceptedIssuers: function() {
return [];
}
}
});

var TrustManagers = [TrustManager.$new()];
var SSLContextInit = SSLContext.init.overload('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom');

SSLContextInit.implementation = function(keyManager, trustManager, secureRandom) {
console.log('[+] SSL Context bypassed');
SSLContextInit.call(this, keyManager, TrustManagers, secureRandom);
};
});

2. Hook 加密算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// crypto_hook.js
Java.perform(function() {
// Hook AES加密
var Cipher = Java.use('javax.crypto.Cipher');

Cipher.doFinal.overload('[B').implementation = function(input) {
console.log('[+] AES Input: ' + bytesToHex(input));
var result = this.doFinal(input);
console.log('[+] AES Output: ' + bytesToHex(result));
return result;
};

// Hook Base64
var Base64 = Java.use('android.util.Base64');
Base64.encodeToString.overload('[B', 'int').implementation = function(input, flags) {
var result = this.encodeToString(input, flags);
console.log('[+] Base64 encode: ' + bytesToHex(input) + ' -> ' + result);
return result;
};
});

function bytesToHex(bytes) {
var hex = '';
for (var i = 0; i < bytes.length; i++) {
hex += ('0' + (bytes[i] & 0xFF).toString(16)).slice(-2);
}
return hex;
}

3. Hook SharedPreferences

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// sharedprefs_hook.js
Java.perform(function() {
var SharedPreferences = Java.use('android.content.SharedPreferences');
var Editor = Java.use('android.content.SharedPreferences$Editor');

// Hook getString
SharedPreferences.getString.overload('java.lang.String', 'java.lang.String').implementation = function(key, defValue) {
var result = this.getString(key, defValue);
console.log('[+] SharedPreferences getString: ' + key + ' = ' + result);
return result;
};

// Hook putString
Editor.putString.implementation = function(key, value) {
console.log('[+] SharedPreferences putString: ' + key + ' = ' + value);
return this.putString(key, value);
};
});

4. Hook 网络请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// network_hook.js
Java.perform(function() {
// Hook OkHttp
var OkHttpClient = Java.use('okhttp3.OkHttpClient');
var Request = Java.use('okhttp3.Request');

OkHttpClient.newCall.implementation = function(request) {
console.log('[+] OkHttp Request URL: ' + request.url().toString());
console.log('[+] OkHttp Request Method: ' + request.method());

var headers = request.headers();
var headerNames = headers.names();
var iterator = headerNames.iterator();

while (iterator.hasNext()) {
var name = iterator.next();
console.log('[+] Header: ' + name + ': ' + headers.get(name));
}

return this.newCall(request);
};
});

高级技巧

1. 动态调用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Java.perform(function() {
// 获取类实例
Java.choose("com.example.MainActivity", {
onMatch: function(instance) {
console.log("[+] Found instance: " + instance);

// 调用实例方法
var result = instance.someMethod("test");
console.log("[+] Method result: " + result);
},
onComplete: function() {
console.log("[+] Search completed");
}
});
});

2. 修改字段值

1
2
3
4
5
6
7
8
9
10
11
12
Java.perform(function() {
var targetClass = Java.use("com.example.Config");

Java.choose("com.example.Config", {
onMatch: function(instance) {
console.log("[+] Original debug flag: " + instance.isDebug.value);
instance.isDebug.value = true;
console.log("[+] Modified debug flag: " + instance.isDebug.value);
},
onComplete: function() {}
});
});

3. 枚举所有方法

1
2
3
4
5
6
7
8
Java.perform(function() {
var targetClass = Java.use("com.example.MainActivity");
var methods = targetClass.class.getDeclaredMethods();

for (var i = 0; i < methods.length; i++) {
console.log("[+] Method: " + methods[i].toString());
}
});

实用脚本集合

1. 通用Hook脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// universal_hook.js
Java.perform(function() {
// Hook所有类的指定方法
function hookMethod(className, methodName) {
try {
var targetClass = Java.use(className);
var overloadCount = targetClass[methodName].overloads.length;

for (var i = 0; i < overloadCount; i++) {
targetClass[methodName].overloads[i].implementation = function() {
console.log('[+] ' + className + '.' + methodName + ' called');
console.log('[+] Arguments: ' + JSON.stringify(arguments));

var result = this[methodName].apply(this, arguments);
console.log('[+] Return: ' + result);
return result;
};
}
} catch (e) {
console.log('[-] Error hooking ' + className + '.' + methodName + ': ' + e);
}
}

// 使用示例
hookMethod('com.example.Utils', 'encrypt');
hookMethod('com.example.Auth', 'login');
});

2. 内存dump脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// memory_dump.js
Java.perform(function() {
var File = Java.use('java.io.File');
var FileOutputStream = Java.use('java.io.FileOutputStream');

function dumpMemory(address, size, filename) {
var buffer = Memory.readByteArray(address, size);
var file = File.$new('/sdcard/' + filename);
var fos = FileOutputStream.$new(file);
fos.write(buffer);
fos.close();
console.log('[+] Memory dumped to: ' + filename);
}
});

调试技巧

1. 日志输出

1
2
3
4
5
6
7
// 使用console.log
console.log('[+] This is info');
console.warn('[!] This is warning');
console.error('[-] This is error');

// 使用send发送到Python脚本
send({type: 'log', message: 'Custom log'});

2. 异常处理

1
2
3
4
5
6
7
Java.perform(function() {
try {
var targetClass = Java.use("com.example.NonExistentClass");
} catch (e) {
console.log('[-] Class not found: ' + e);
}
});

3. 延迟Hook

1
2
3
4
5
6
Java.perform(function() {
setTimeout(function() {
console.log('[+] Delayed hook execution');
// Hook代码
}, 2000);
});

常见问题解决

1. ClassNotFoundException

1
2
3
4
5
6
// 等待类加载
Java.perform(function() {
Java.use("com.example.App").someMethod.implementation = function() {
// Hook代码
};
});

2. 方法重载

1
2
3
4
// 指定具体重载
targetClass.someMethod.overload('java.lang.String', 'int').implementation = function(str, num) {
// Hook代码
};

3. 混淆代码处理

1
2
// 使用完整类名
var obfuscatedClass = Java.use("a.b.c");

总结

Frida是Android逆向分析的强大工具,通过以上示例可以快速上手常见的Hook场景。关键点:

  1. 熟练掌握Java.perform和Java.use
  2. 理解方法重载的处理方式
  3. 掌握常用的Hook模式和技巧
  4. 善用调试和异常处理

更多高级用法需要结合具体的逆向目标进行实践。

常用adb操作命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# 查看已连接设备
adb devices

# 连接远程设备(WiFi调试)
adb connect 192.168.1.100:5555

# 推送文件到设备
adb push <本地文件> <设备路径>

# 从设备拉取文件到本地
adb pull <设备文件> <本地路径>

# 进入设备shell
adb shell

# 查看进程列表
adb shell ps | grep <包名>

# 杀死进程
adb shell am force-stop <包名>

# 安装/卸载应用
adb install <apk路径>
adb uninstall <包名>

# 端口转发(本地端口转设备端口)
adb forward tcp:27042 tcp:27042
adb forward tcp:27043 tcp:27043

# 端口转发的作用和原理:
# 1. Frida默认监听27042端口,需要将PC端口转发到设备端口
# 2. 27042是frida-server的默认监听端口
# 3. 27043通常用于额外的frida服务或多实例场景
# 4. 转发后PC端的Frida客户端可以通过本地端口连接设备上的frida-server

# 查看当前端口转发列表
adb forward --list

# 移除特定端口转发
adb forward --remove tcp:27042

# 移除所有端口转发
adb forward --remove-all

# 查看logcat日志
adb logcat

# 仅查看指定包名日志
adb logcat | grep <包名>

# 重启设备
adb reboot

# 获取root权限(需设备支持)
adb root

# 重启adb服务
adb kill-server
adb start-server

# 查看所有设备
adb devices

# 指定设备执行命令(如获取CPU架构)
adb -s emulator-5554 shell getprop ro.product.cpu.abi

adb shell getprop ro.product.cpu.abi

这些命令是Android逆向和调试过程中最常用的,建议熟练掌握。

多设备操作

当有多台设备/模拟器同时连接时,使用 -s <序列号> 指定目标设备:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 查看所有设备序列号
adb devices

# 指定设备安装应用
adb -s <序列号> install <apk路径>

# 指定设备进入shell
adb -s <序列号> shell

# 指定设备推送文件
adb -s <序列号> push <本地文件> <设备路径>

# 指定设备拉取文件
adb -s <序列号> pull <设备文件> <本地路径>

多设备场景下,所有adb命令都可以加-s参数精确控制目标设备。

支付宝打赏 微信打赏

如果文章对你有帮助,欢迎点击上方按钮打赏作者,更多功能请访问博客站



Android Hook实用教程:逆向必备
https://blog.fxcxy.com/2025/06/03/Android—Hook使用教程-逆向必备/
作者
spatacus
发布于
2025年6月3日
许可协议