本文环境
- Google Nexus/Pixel
- iPhone 6sp
Android
环境
1
2
3
4
5
6
7
8
9
10
11
# 用 magiskfrida 之类的插件代替
export PATH=~/Android/Sdk/platform-tools/:$PATH
adb push frida-server-12.4.8-android-arm64 /data/local/tmp/fserver
# adb shell su -c "mount -o rw,remount /system"
# adb shell su -c "mv /data/local/tmp/fserver /system/bin/fserver"
# adb shell su -c "chmod 777 /system/bin/fserver"
# adb shell su -c "mount -o ro,remount /system"
adb shell su -c "mv /data/local/tmp/fserver /sbin/fserver"
adb shell su -c "chmod 777 /sbin/fserver"
adb shell su -c "fserver --help"
-
pip 安装指定版本 (virtualenv)
1 2 3 4 5
cd ~/frida virtualenv --no-site-packages frida-12.0.7 source ~/frida/frida-12.0.7/bin/activate pip3 install frida==12.0.7 pip3 install frida-tools==1.1.0
vscode 里添加
1 2 3 4 5 6
"python.venvPath": "F:\\path\\to\\Frida", "python.venvFolders": [ "envs", ".pyenv", ".direnv" ],
即可在 vscode 中选择 venv 中的解释器.
虚拟机堆栈
1
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
相当于 java 的
1
android.util.Log.getStackTraceString(new java.lang.Throwable())
注入 Dex
- 先用 sdk - build-tools 中的 dx 制作 dex, push 到手机里
- 注入代码
1 2 3 4 5 6 7
var currentApplication = Java.use("android.app.ActivityThread").currentApplication(); var context = currentApplication.getApplicationContext(); var pkgName = context.getPackageName(); var dexPath = "/data/local/tmp/guava.dex"; Java.openClassFile(dexPath).load(); console.log("inject " + dexPath + " to " + pkgName + " successfully!") console.log(Java.use("com.google.common.collect.Maps")); //
Non-ASCII
比如说
1
2
3
int ֏(int x) {
return x + 100;
}
甚至有一些不可视, 所以可以先编码打印出来, 再用编码后的字符串去 hook.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Java.perform(
function x() {
var targetClass = "com.example.hooktest.MainActivity";
var hookCls = Java.use(targetClass);
var methods = hookCls.class.getDeclaredMethods();
for (var i in methods) {
console.log(methods[i].toString());
console.log(encodeURIComponent(methods[i].toString().replace(/^.*?\.([^\s\.\(\)]+)\(.*?$/, "$1")));
}
hookCls[decodeURIComponent("%D6%8F")]
.implementation = function (x) {
console.log("original call: fun(" + x + ")");
var result = this[decodeURIComponent("%D6%8F")](900);
return result;
}
}
)
TracerPid
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
console.log("anti_fgets");
var fgetsPtr = Module.findExportByName('libc.so', 'fgets');
var fgets = new NativeFunction(fgetsPtr, 'pointer', ['pointer', 'int', 'pointer']);
Interceptor.replace(fgetsPtr, new NativeCallback(function (buffer, size, fp) {
var retval = fgets(buffer, size, fp)
var bufstr = Memory.readUtf8String(buffer)
if (bufstr.indexOf('TracerPid:') > -1) {
Memory.writeUtf8String(buffer, 'TracerPid:\t0')
// console.log('tracerpid replaced: ' + bufstr)
}
if (bufstr.indexOf(':' + 27042..toString(16).toUpperCase()) > -1) {
Memory.writeUtf8String(buffer, '')
console.log('27042 replaced: ' + bufstr)
}
if (bufstr.indexOf('frida') > -1) { // frida
Memory.writeUtf8String(buffer, '')
console.log('frida replaced: ' + bufstr)
}
return retval
}, 'pointer', ['pointer', 'int', 'pointer']))
System.loadLibrary
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
const SDK_INT = Java.use('android.os.Build$VERSION').SDK_INT.value
const System = Java.use('java.lang.System')
const Runtime = Java.use('java.lang.Runtime')
const VMStack = Java.use('dalvik.system.VMStack')
System.loadLibrary.implementation = function (library: string) {
try {
console.log('System.loadLibrary("' + library + '")')
if (SDK_INT > 23) {
return Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), library)
} else {
return Runtime.getRuntime().loadLibrary(library, VMStack.getCallingClassLoader())
}
} catch (e) {
console.warn(e)
}
}
System.load.implementation = function (library: string) {
try {
console.log('System.load("' + library + '")')
if (SDK_INT > 23) {
return Runtime.getRuntime().load0(VMStack.getCallingClassLoader(), library)
} else {
return Runtime.getRuntime().load(library, VMStack.getCallingClassLoader())
}
} catch (e) {
console.warn(e)
}
};
也可以:
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
function readStdString(str) {
const isTiny = (str.readU8() & 1) === 0
if (isTiny) {
return str.add(1).readUtf8String()
}
return str.add(2 * Process.pointerSize).readPointer().readUtf8String()
}
var mod_art = Process.findModuleByName("libart.so")
if (mod_art) {
for (var exp of mod_art.enumerateExports()) {
if (exp.name.indexOf("LoadNativeLibrary") != -1) {
console.log(exp.name, exp.address)
Interceptor.attach(exp.address, {
onEnter: function (args) {
this.pathName = readStdString(args[2])
console.log("[*] [LoadNativeLibrary] in pathName =", this.pathName)
},
onLeave: function (retval) {
console.log("[*] [LoadNativeLibrary] out pathName =", this.pathName)
}
})
break
}
}
}
var mod_dvm = Process.findModuleByName("libdvm.so")
if (mod_dvm) {
for (var exp of mod_dvm.enumerateExports()) {
if (exp.name.indexOf("dvmLoadNativeCode") != -1) {
console.log(exp.name, exp.address)
// bool dvmLoadNativeCode(const char * pathName, void * classLoader, char ** detail)
Interceptor.attach(exp.address, {
onEnter: function (args) {
this.pathName = args[0].readUtf8String()
console.log("[*] [dvmLoadNativeCode] in pathName =", this.pathName)
},
onLeave: function (retval) {
console.log("[*] [dvmLoadNativeCode] out pathName =", this.pathName)
}
})
break
}
}
}
SDK_INT
1
const SDK_INT = Java.use('android.os.Build$VERSION').SDK_INT.value;
dump so
1
2
// Process.enumerateModules();
var fd = new File("/sdcard/Android/data/com.example/files/libxx.so","wb"); fd.write(new NativePointer(0x94300000).readByteArray(900368));fd.close();
找 interface 的实现 (估计有谬误, 有空再找一下对应的 Java 代码翻译一下)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Java.enumerateLoadedClasses(
{
"onMatch": function (className) {
if (className.indexOf("com.example.hooktest.") < 0) {
return
}
var hookCls = Java.use(className)
var interFaces = hookCls.class.getGenericInterfaces()
if (interFaces.length > 0) {
console.log(className)
for (var i in interFaces) {
console.log("\t", interFaces[i])
}
var methods = hookCls.class.getDeclaredMethods()
for (var i in methods) {
console.log(methods[i].toString(), "\t", encodeURIComponent(methods[i].toString().replace(/^.*?\.([^\s\.\(\)]+)\(.*?$/, "$1")))
}
}
},
"onComplete": function () { }
}
)
打印 il2cpp 中返回的 c# string
第三个四字节是长度, 第四个四字节开始存放 utf-16 字符串.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// public string DecryptString(string stringId); // RVA: 0x3D270 Offset: 0x3D270
function attach_DecryptString(){
var func = Module.getBaseAddress("libil2cpp.so").add(0x3D270)
console.log('func addr: ' + func)
Interceptor.attach(func, {
onEnter: function (args) {
},
onLeave: function (retval) {
print_dotnet_string("onLeave", retval)
}
}
)
}
1
2
3
4
5
6
7
function print_dotnet_string(tag, dotnet_string) {
console.log(JSON.stringify({
tag: tag,
len: dotnet_string.add(8).readU32(),
data: dotnet_string.add(12).readUtf16String(-1)
}))
}