Android Hook 实用教程
环境准备
1. 安装 Frida
1 2 3 4 5
| 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
pip install frida frida-tools
frida --version frida-ps
|
每次使用前需要激活虚拟环境:
1 2 3 4 5 6 7
| source ~/frida-venv/bin/activate
frida --version frida-ps
|
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
|
adb shell getprop ro.product.cpu.abi
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
adb shell /data/local/tmp/frida-server &
adb shell "su -c 'nohup /data/local/tmp/frida-server >/dev/null 2>&1 &'"
adb shell "ps | grep frida"
|
对于非Root设备:
使用 Frida-gadget 方式,需要重打包APK或使用免root框架。
基础Hook示例
1. Hook 方法调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| Java.perform(function() { var targetClass = Java.use("com.example.MainActivity"); 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"); 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
| Java.perform(function() { 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
| Java.perform(function() { 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; }; 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
| Java.perform(function() { var SharedPreferences = Java.use('android.content.SharedPreferences'); var Editor = Java.use('android.content.SharedPreferences$Editor'); 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; }; 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
| Java.perform(function() { 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
| Java.perform(function() { 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
| 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('[+] This is info'); console.warn('[!] This is warning'); console.error('[-] This is error');
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'); }, 2000); });
|
常见问题解决
1. ClassNotFoundException
1 2 3 4 5 6
| Java.perform(function() { Java.use("com.example.App").someMethod.implementation = function() { }; });
|
2. 方法重载
1 2 3 4
| targetClass.someMethod.overload('java.lang.String', 'int').implementation = function(str, num) { };
|
3. 混淆代码处理
1 2
| var obfuscatedClass = Java.use("a.b.c");
|
总结
Frida是Android逆向分析的强大工具,通过以上示例可以快速上手常见的Hook场景。关键点:
- 熟练掌握Java.perform和Java.use
- 理解方法重载的处理方式
- 掌握常用的Hook模式和技巧
- 善用调试和异常处理
更多高级用法需要结合具体的逆向目标进行实践。
常用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
adb connect 192.168.1.100:5555
adb push <本地文件> <设备路径>
adb pull <设备文件> <本地路径>
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
adb forward --list
adb forward --remove tcp:27042
adb forward --remove-all
adb logcat
adb logcat | grep <包名>
adb reboot
adb root
adb kill-server adb start-server
adb devices
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路径>
adb -s <序列号> shell
adb -s <序列号> push <本地文件> <设备路径>
adb -s <序列号> pull <设备文件> <本地路径>
|
多设备场景下,所有adb命令都可以加-s参数精确控制目标设备。