Frida的一些脚本
2024-09-13 23:14:47

这篇只是记录,方便逆向的时候直接开启CV工程。

脚本框架

1
2
3
4
//1.js
Java.perform(function (){
//加下面的组件
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import frida, sys
def on_message(message, data): # 该函数可在frida中使用send(a); 输出python类型内容
if message['type'] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)
# java的代码
# package com.sdfglb.forjack;
# public class NativeLib {
# public native String forJackMore(String str, HashMap<String, String> hashMap);
# }
f = open("1.js",'rb')
jscode = f.read().decode('utf-8')
device = frida.get_usb_device()
pid = device.spawn("com.baidu.app.xx71")
process = device.attach(pid)
script = process.create_script(jscode)
script.load()
device.resume(pid)
sys.stdin.read()

类型

Java Type (Java中参数类型) Frida Type (frida脚本中参数类型)
int int
byte byte
short short
long long
float float
double double
char char
(比如String、List) .(比如java.lang.String、java.util.List)
int[] [I
byte[] [B
short[] [S
long[] [J
float[] [F
double[] [D
char[] [C
[](比如String[]) L.; (比如 [Ljava.lang.String;)

输出堆栈

1
2
3
4
5
6
7
8
var Exception = Java.use('java.lang.Exception');
var stackTrace = Exception.$new().getStackTrace();
var stackTraceString = '';
for (var i = 0; i < stackTrace.length; i++) {
stackTraceString += '\t' + stackTrace[i].toString() + '\n';
}
console.log(stackTraceString);

输出Activity

1
2
3
4
5
6
7
8
var Activity = Java.use('android.app.Activity');

Activity.onCreate.overload("android.os.Bundle").implementation = function(savedInstanceState) {
// 在这里可以添加您的 Hook 逻辑
console.log('Activity.onCreate() is hooked!');
// 执行原始的 onCreate() 方法
this.onCreate(savedInstanceState);
};

WebView LoadUrl

1
2
3
4
5
6
7
8
9
10
11
12
var WebView = Java.use('android.webkit.WebView');
WebView.loadUrl.overload('java.lang.String', 'java.util.Map').implementation = function(url, additionalHttpHeaders) {
console.log("loadUrl called with URL: " + url);
console.log("Additional headers: " + additionalHttpHeaders);
this.loadUrl(url, additionalHttpHeaders);
}

WebView.loadUrl.overload('java.lang.String').implementation = function(url, ) {
console.log("loadUrl called with URL: " + url);

this.loadUrl(url);
}

奇怪的方法名称

1
2
3
4
5
6
7
//com.lt.app.WebActivity.ʻ(java.lang.String,boolean)
//转成utf-8
WebActivity = Java.use("com.lt.app.WebActivity");
WebActivity["\u02BB"].overload('java.lang.String', 'java.util.Map').implementation = function (str, map) {
console.log(`WebActivity.ʻ is called: str=${str}, map=${map}`);
this["\u02BB"](str, map);
};

SharedPreferences

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
// hook getString() 方法
var SharedPreferences = Java.use('android.app.SharedPreferencesImpl');


SharedPreferences.getString.implementation = function(key, defValue) {
console.log('SharedPreferences.getString() is called, key:', key, ' defValue:', defValue);
var value = this.getString(key, defValue);
console.log('SharedPreferences.getString() is returning:', value);
return value;
};

// hook getString() 方法
var EditorImpl = Java.use('android.app.SharedPreferencesImpl$EditorImpl');


EditorImpl.putString.implementation = function(key, defValue) {
console.log('SharedPreferences.putString() is called, key:', key, ' defValue:', defValue);

var value = this.putString(key, defValue);
console.log('SharedPreferences.putString() is returning:', value);
return value;
};

// hook edit方法
SharedPreferences.edit.implementation = function() {
console.log('调用 SharedPreferences.edit() 方法');
console.log('edit path:', this.mFile.value);
var editor = this.edit();
return editor;
};

Hook loadLibrary

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    const System = Java.use('java.lang.System');
const Runtime = Java.use('java.lang.Runtime');
const SystemLoad_2 = System.loadLibrary.overload('java.lang.String');
const VMStack = Java.use('dalvik.system.VMStack');

SystemLoad_2.implementation = function(library) {
try {
console.log("Loading dynamic library 1=> " + library);
const loaded = Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), library);
// console.log("Loading dynamic library2 => " + library);
return loaded;


} catch(ex) {
console.log(ex);
}
};

Hook linker

通过hook log函数来dump

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
68
69
70
71
72
73
74
75
76
77
78
79
function hook_constructor() {
if (Process.pointerSize == 4) {
var linker = Process.findModuleByName("linker");
} else {
var linker = Process.findModuleByName("linker64");
}
console.log(`find linker`, linker.base);
//一、获取函数的地址
var isLogOpen = false;
var addr_call_function =null;
var addr_g_ld_debug_verbosity = null;
var addr_dl__Z10linker_logiPKcz = null;
// var addr_async_safe_format_log = null;
if (linker) {
//console.log("found linker");
var symbols = linker.enumerateSymbols();
for (var i = 0; i < symbols.length; i++) {
var name = symbols[i].name;
if (name.indexOf("call_constructors") >= 0){
addr_call_function = symbols[i].address;
console.log("call_function",JSON.stringify(symbols[i]));
}
else if(name.indexOf("g_ld_debug_verbosity") >=0){
addr_g_ld_debug_verbosity = symbols[i].address;
}
else if(name.indexOf("_dl__Z10linker_logiPKcz") >= 0 && name.indexOf("va_list") < 0){
addr_dl__Z10linker_logiPKcz = symbols[i].address;
console.log("log",JSON.stringify(symbols[i]));
}

}
//二、打开log函数的等级
if(addr_call_function){
Interceptor.attach(addr_call_function, {
onEnter: function(args){
if(!isLogOpen)
{
console.log("open log")
ptr(addr_g_ld_debug_verbosity).writeInt(2);
isLogOpen = true;
}
},
onLeave: function(retval){
// console.log("close log")
// ptr(addr_g_ld_debug_verbosity).writeInt(0);
}

})
}
//三、判断log的日志字符串情况来输出
if(addr_dl__Z10linker_logiPKcz){
Interceptor.attach(addr_dl__Z10linker_logiPKcz, {
onEnter: function(args){
// this.tag = ptr(args[1]).readCString()
this.fmt = ptr(args[1]).readCString()
// console.log(this.fmt);
//所有的init_array func and init func
if(this.fmt.indexOf("c-tor") >= 0 && this.fmt.indexOf('Done') >= 0){
this.func_type = ptr(args[2]).readCString();
this.so_path = ptr(args[4]).readCString();
console.log(this.so_path, this.func_type);
}
//start init array
else if(this.fmt.indexOf("Calling") >= 0 && this.fmt.indexOf("%zd)") >= 0){
this.func_type = ptr(args[2]).readCString();
this.count = (args[3]).toInt32();
console.log(this.func_type,"started size " , this.count);
}
// init_array结束
else if(this.fmt.indexOf("Done calling %s for '%s'") >= 0){
this.func_type = ptr(args[2]).readCString();
console.log(this.func_type, "finished");
}
},
onLeave: function(retval){}
});
}
}
}

Dump so

1
2
3
4
5
6
7
8
9
10
11
//dump code
var libbaiduprotect = Process.findModuleByName("libbaiduprotect.so");
console.log(libbaiduprotect.base, libbaiduprotect.size);
Memory.protect(ptr(libbaiduprotect.base), libbaiduprotect.size, 'rwx');
var dumpFilePath = "/data/data/com.moutai.mall/files/dump.so"; // 替换为保存.so文件转储数据的文件路径
var dumpFile = new File(dumpFilePath, "wb");
var soData = Memory.readByteArray(libbaiduprotect.base, libbaiduprotect.size);
dumpFile.write(soData);
dumpFile.flush();
dumpFile.close();
console.log((libbaiduprotect.base.add(0xE155C)))

通过主动调用来解密字符串(baidujiagu)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var decodelist = [
"1F9A8***0F892D685939526D",
"1FA88***4F25DC8975A33B550346DE887FA531521346FE887FA531441352F18D70A735130B08D3803E82204E0E07DADC5DBB354A0646D1867FB67B6F131BD48976EA1856061FDCC87DB03A5B483AC99578BF33072E40EB",
"1FB68***CAD6059A10DC32C3",
"1FC48***29B16CA41B46FCCE65D26EA51140F6C975D24EA51140F6DF75C6249C",
"1FD28***2E0C218B92E17D7E",
"1FE08***4C21048D5373F3960042068C5975F9911042268C5975F98710564CB5"]

for(let i in decodelist){
var str = decodelist[i].substring(8)
var funcaddr = parseInt(decodelist[i].substring(0, 5), 16)

var n_addr_so = Module.findBaseAddress("libbaiduprotect.so");
var n_funcaddr = funcaddr + parseInt(n_addr_so, 16);
var call_func = new NativeFunction(ptr(n_funcaddr), 'pointer', ['pointer']);

var str_arg = Memory.allocUtf8String(str);
var p_str_ret = call_func(str_arg);
var str_ret = Memory.readCString(p_str_ret);

console.log(str_ret)
}

调用RegisterNatives

因为存在两个RegisterNatives,所以需不需要取决于实际情况

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
function hook_RegisterNatives() {
var symbols = Module.enumerateSymbolsSync("libart.so");
var addrRegisterNatives = null;
for (var i = 0; i < symbols.length; i++) {
var symbol = symbols[i];

//_ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi
if (symbol.name.indexOf("art") >= 0 &&
symbol.name.indexOf("JNI") >= 0 &&
symbol.name.indexOf("RegisterNatives") >= 0 &&
symbol.name.indexOf("CheckJNI") < 0 &&
symbol.name.indexOf("URegisterNatives")) {
addrRegisterNatives = symbol.address;
console.log("RegisterNatives is at ", symbol.address, symbol.name);
//注意点,这个break是不是需要取决于实际情况
break;
}
}

if (addrRegisterNatives != null) {
Interceptor.attach(addrRegisterNatives, {
onEnter: function (args) {
console.log("[RegisterNatives] method_count:", args[3]);
var env = args[0];
var java_class = args[1];
var class_name = Java.vm.tryGetEnv().getClassName(java_class);
//console.log(class_name);

var methods_ptr = ptr(args[2]);

var method_count = parseInt(args[3]);
for (var i = 0; i < method_count; i++) {
var name_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3));
var sig_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize));
var fnPtr_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2));

var name = Memory.readCString(name_ptr);
var sig = Memory.readCString(sig_ptr);
var find_module = Process.findModuleByAddress(fnPtr_ptr);
console.log("[RegisterNatives] java_class:", class_name, "name:", name, "sig:", sig, "fnPtr:", fnPtr_ptr, "module_name:", find_module.name, "module_base:", find_module.base, "offset:", ptr(fnPtr_ptr).sub(find_module.base));

}
}
});
}
}

输出数组

1
2
var arrays = Java.use('java.util.Arrays')
console.log('sortResults : ' + arrays.toString(results.toArray()));