jni接口比较(Jnitrace/Frida/ExAndroidNativeEmu/Unidbg)
zsk Lv4
  • 它们是如何处理/实现jni几百个接口函数的?

  • 它们能hook/主动调用JNI接口函数么?

  • 它们是如何找到jni(动)静态注册的Native函数的?

  • 它们能hook动(静)态注册的Native函数么?

  • 它们能主动调用动(静)态注册的Native函数么?

  • 首先定义:

    • Java层的Native函数
    • env、vm → jni接口函数
    • jni静态函数、jni动态函数
    • 纯native函数
      Frida jnitrace ExAndroidNativeEmu Unidbg
      可以跑完整的apk么? × ×
      可以hook Java么?查看参调返 x x ×
      可以hook JNI接口函数么?查看参调返
      可以主动调用JNI接口函数么?获取返回值 x
      如何找到静态绑定的Native函数? dlopen/dlsym dlsym Module Module
      如何找到动态绑定的Native函数? hook RegisterNatives hook RegisterNatives hook RegisterNatives hook RegisterNatives
      如何hook(动)静态注册的Native函数? x
      如何主动调用(动)静态注册的Native函数? x
  • Frida中JNI接口的实现细节

https://github.com/frida/frida-java-bridge/blob/master/lib/env.js#L362
linux动态库函数dlopen与dlsym使用
https://blog.csdn.net/lanhuazui10/article/details/117849995

frida hook java

1
2
3
4
5
6
7
8
9
10
function hookJava(){
Java.perform(function(){
Java.use("com.roysue.easyso1.MainActivity").method01.implementation = function(str){
console.log("str:", str)
var result = this.method01(str);
console.log("result:", result);
return result;
}
})
}

frida invoke java

1
2
3
4
5
6
function invokeJava(){
Java.perform(function(){
var result = Java.use("com.roysue.easyso1.MainActivity").decrypt("82e8edd5b05654bf0fedcdfc1c9b4b0f");
console.log("result:", result)
})
}

frida hook jni

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
function find_RegisterNatives(params) {
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) {
addrRegisterNatives = symbol.address;
console.log("RegisterNatives is at ", symbol.address, symbol.name);
hook_RegisterNatives(addrRegisterNatives)
}
}
}
function hook_RegisterNatives(addrRegisterNatives) {
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, " fnOffset:", ptr(fnPtr_ptr).sub(find_module.base), " callee:", DebugSymbol.fromAddress(this.returnAddress));
}
}
});
}
}
setImmediate(find_RegisterNatives);

frida hook 纯native静态,动态函数(dlopen)

动态函数初始化只执行一次,需要hook dlopen(来判断so是否加载)

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_Native_JNi(){
Java.perform(function(){
var method01 = Module.findExportByName("libroysue.so", "Java_com_roysue_easyso1_MainActivity_method01");
var method02 = Module.findExportByName("libroysue.so", "_Z8method02P7_JNIEnvP7_jclassP8_jstring");
console.log("method01 address:", method01)
console.log("method02 address:", method02)
if(method01){
Interceptor.attach(method01, {
onEnter: function(args){
// env->GetStringUTFChars(str_, JNI_FALSE)
console.log("method01 args[2] ==> ", Java.vm.getEnv().getStringUtfChars(args[2], null).readCString())
},
onLeave: function(retval){
console.log("method01 retval ==> ", Java.vm.getEnv().getStringUtfChars(retval, null).readCString())
}
})
}
if(method02){
Interceptor.attach(method02, {
onEnter: function(args){
console.log("method02 args[2] ==> ", Java.vm.getEnv().getStringUtfChars(args[2], null).readCString())
},
onLeave: function(retval){
console.log("method02 retval ==> ", Java.vm.getEnv().getStringUtfChars(retval, null).readCString())
}
})
}
})
}
function hook_dlopen(module_name, fun) {
//安卓高版本需要同时hook android_dlopen_ext
var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");
if (android_dlopen_ext) {
Interceptor.attach(android_dlopen_ext, {
onEnter: function (args) {
var pathptr = args[0];
if (pathptr) {
this.path = (pathptr).readCString();
if (this.path.indexOf(module_name) >= 0) {
this.canhook = true;
console.log("android_dlopen_ext:", this.path);
}
}
},
onLeave: function (retval) {
if (this.canhook) {
fun();
}
}
});
}
var dlopen = Module.findExportByName(null, "dlopen");
if (dlopen) {
Interceptor.attach(dlopen, {
onEnter: function (args) {
var pathptr = args[0];
if (pathptr) {
this.path = (pathptr).readCString();
console.log("dlopen:", this.path)
if (this.path.indexOf(module_name) >= 0) {
this.canhook = true;
console.log("dlopen:", this.path);
}
}
},
onLeave: function (retval) {
if (this.canhook) {
fun();
}
}
});
}
console.log("android_dlopen_ext:", android_dlopen_ext, "dlopen:", dlopen);
}

function main(){
// hookJava()
hook_dlopen("libroysue.so", hook_Native_JNi)
}

frida hook 纯native静态,动态函数(libart,RegisterNatives)

通过hook libart下的RegisterNatives来hook动态函数,动态函数加载后,静态函数已经加载了
.init .init_array, so feature - dlopen dlsym
jni JNI_Onload = 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
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
function find_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) {
addrRegisterNatives = symbol.address;
console.log("RegisterNatives is at ", symbol.address, symbol.name);
hook_RegisterNatives(addrRegisterNatives)
}
}

}
function hook_RegisterNatives(addrRegisterNatives) {

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, " fnOffset:", ptr(fnPtr_ptr).sub(find_module.base), " callee:", DebugSymbol.fromAddress(this.returnAddress));
if (name.indexOf("decrypt") >= 0){
var method02addr = fnPtr_ptr;
var method01addr = Module.findExportByName("libroysue.so", "Java_com_roysue_easyso1_MainActivity_method01");
console.log("method01 address:", method02addr)
console.log("method02 address:", method01addr)
if(method01addr){
Interceptor.attach(method01addr, {
onEnter: function(args){
// env->GetStringUTFChars(str_, JNI_FALSE)
console.log("method01 args[2] ==> ", Java.vm.getEnv().getStringUtfChars(args[2], null).readCString())
},
onLeave: function(retval){
console.log("method01 retval ==> ", Java.vm.getEnv().getStringUtfChars(retval, null).readCString())
}
})
}
if(method02addr){
Interceptor.attach(method02addr, {
onEnter: function(args){
console.log("method02 args[2] ==> ", Java.vm.getEnv().getStringUtfChars(args[2], null).readCString())
},
onLeave: function(retval){
console.log("method02 retval ==> ", Java.vm.getEnv().getStringUtfChars(retval, null).readCString())
}
})
}
}
}
}
});
}
}

frida invoke 纯native(动态,静态)(基于dlopen)

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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
var method01_addr = null;
var method02_addr = null;
var addrNewStringUTF = null;
function hook_dlopen(module_name, fun) {
//安卓高版本需要同时hook android_dlopen_ext
var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");
if (android_dlopen_ext) {
Interceptor.attach(android_dlopen_ext, {
onEnter: function (args) {
var pathptr = args[0];
if (pathptr) {
this.path = (pathptr).readCString();
if (this.path.indexOf(module_name) >= 0) {
this.canhook = true;
console.log("android_dlopen_ext:", this.path);
}
}
},
onLeave: function (retval) {
if (this.canhook) {
fun();
}
}
});
}
var dlopen = Module.findExportByName(null, "dlopen");
if (dlopen) {
Interceptor.attach(dlopen, {
onEnter: function (args) {
var pathptr = args[0];
if (pathptr) {
this.path = (pathptr).readCString();
// console.log("dlopen:", this.path)
if (this.path.indexOf(module_name) >= 0) {
this.canhook = true;
// console.log("dlopen:", this.path);
}
}
},
onLeave: function (retval) {
if (this.canhook) {
fun();
}
}
});
}
console.log("android_dlopen_ext:", android_dlopen_ext, "dlopen:", dlopen);
}
function hook_Native_JNi(){
Java.perform(function(){
method01_addr = Module.findExportByName("libroysue.so", "Java_com_roysue_easyso1_MainActivity_method01");
method02_addr = Module.findExportByName("libroysue.so", "_Z8method02P7_JNIEnvP7_jclassP8_jstring");
addrNewStringUTF = Module.findExportByName("libroysue.so", "_ZN7_JNIEnv12NewStringUTFEPKc");
console.log("method01 address:", method01_addr)
console.log("method02 address:", method02_addr)
console.log("addrNewStringUTF address:", addrNewStringUTF)
})
}

function invoke_method01(contents){
var method01_addr = Module.findExportByName("libroysue.so", "Java_com_roysue_easyso1_MainActivity_method01")
if(method01_addr && addrNewStringUTF){
console.log("method01_addr is =>",method01_addr)
var method01 = new NativeFunction(method01_addr, 'pointer',['pointer','pointer','pointer'])
var NewStringUTF = new NativeFunction(addrNewStringUTF, "pointer", ["pointer", "pointer"]);
var result = null;
Java.perform(function(){
var Jstring = Java.vm.getEnv().newStringUtf(contents)
// var Jstring = NewStringUTF(Java.vm.getEnv(), Memory.allocUtf8String(contents));
result = method01(Java.vm.getEnv(), Jstring, Jstring)
console.log("result is =>",result)
console.log("result is ", Java.vm.getEnv().getStringUtfChars(result, null).readCString())
result = Java.vm.getEnv().getStringUtfChars(result, null).readCString();
})
return result;
}
}
function invoke_method02(contents){
if(method02_addr && addrNewStringUTF){
console.log("method02_addr is =>", method02_addr)
var method02 = new NativeFunction(method02_addr, 'pointer',['pointer','pointer','pointer'])
var NewStringUTF = new NativeFunction(addrNewStringUTF,'pointer',['pointer','pointer'])
var result = null;
Java.perform(function(){
var Jstring = Java.vm.getEnv().newStringUtf(contents)
// var Jstring = NewStringUTF(Java.vm.getEnv(), Memory.allocUtf8String(contents));
result = method02(Java.vm.getEnv(), Jstring, Jstring)
console.log("result is =>",result)
console.log("result is ", Java.vm.getEnv().getStringUtfChars(result, null).readCString())
result = Java.vm.getEnv().getStringUtfChars(result, null).readCString();
})
return result;
}
}

rpc.exports = {
invoke1:invoke_method01,
invoke2:invoke_method02
}
function main(){
hook_dlopen("libroysue.so", hook_Native_JNi)
}

setImmediate(main)

frida invoke 纯native(动态,静态)(基于RegisterNatives)

通过RegisterNatives获取到jni函数地址,进行调用

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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
var addrNewStringUTF = null;
var method02addr = null;
function find_RegisterNatives() {
var symbols = Module.enumerateSymbolsSync("libart.so");
var addrRegisterNatives = null;
for (var i = 0; i < symbols.length; i++) {
var symbol = symbols[i];

if (symbol.name.indexOf("art") >= 0 &&
symbol.name.indexOf("JNI") >= 0 &&
symbol.name.indexOf("NewStringUTF") >= 0 &&
symbol.name.indexOf("CheckJNI") < 0) {
addrNewStringUTF = symbol.address;
console.log("NewStringUTF is at ", symbol.address, symbol.name);
}
//_ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi
if (symbol.name.indexOf("art") >= 0 &&
symbol.name.indexOf("JNI") >= 0 &&
symbol.name.indexOf("RegisterNatives") >= 0 &&
symbol.name.indexOf("CheckJNI") < 0) {
addrRegisterNatives = symbol.address;
console.log("RegisterNatives is at ", symbol.address, symbol.name);
hook_RegisterNatives(addrRegisterNatives)
}
}

}
function hook_RegisterNatives(addrRegisterNatives) {

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, " fnOffset:", ptr(fnPtr_ptr).sub(find_module.base), " callee:", DebugSymbol.fromAddress(this.returnAddress));
if (name.indexOf("decrypt") >= 0){
method02addr = fnPtr_ptr;
var method01addr = Module.findExportByName("libroysue.so", "Java_com_roysue_easyso1_MainActivity_method01");
console.log("method01 address:", method02addr)
console.log("method02 address:", method01addr)
// if(method01addr){
// Interceptor.attach(method01addr, {
// onEnter: function(args){
// // env->GetStringUTFChars(str_, JNI_FALSE)
// console.log("method01 args[2] ==> ", Java.vm.getEnv().getStringUtfChars(args[2], null).readCString())
// },
// onLeave: function(retval){
// console.log("method01 retval ==> ", Java.vm.getEnv().getStringUtfChars(retval, null).readCString())
// }
// })
// }
if(method02addr){
Interceptor.attach(method02addr, {
onEnter: function(args){
console.log("method02 args[2] ==> ", Java.vm.getEnv().getStringUtfChars(args[2], null).readCString())
},
onLeave: function(retval){
console.log("method02 retval ==> ", Java.vm.getEnv().getStringUtfChars(retval, null).readCString())
}
})
}
}
}
}
});
}
}

function invoke_method01(contents){
var method01_addr = Module.findExportByName("libroysue.so", "Java_com_roysue_easyso1_MainActivity_method01")
if(method01_addr && addrNewStringUTF){
console.log("method01_addr is =>",method01_addr)
var method01 = new NativeFunction(method01_addr, 'pointer',['pointer','pointer','pointer'])
var NewStringUTF = new NativeFunction(addrNewStringUTF, "pointer", ["pointer", "pointer"]);
var result = null;
Java.perform(function(){
var Jstring = NewStringUTF(Java.vm.getEnv(), Memory.allocUtf8String(contents));
result = method01(Java.vm.getEnv(), Jstring, Jstring)
console.log("result is =>",result)
console.log("result is ", Java.vm.getEnv().getStringUtfChars(result, null).readCString())
result = Java.vm.getEnv().getStringUtfChars(result, null).readCString();
})
return result;
}
}
function invoke_method02(contents){
if(method02addr && addrNewStringUTF){
console.log("method02_addr is =>", method02addr)
var method02 = new NativeFunction(method02addr, 'pointer',['pointer','pointer','pointer'])
var NewStringUTF = new NativeFunction(addrNewStringUTF,'pointer',['pointer','pointer'])
var result = null;
Java.perform(function(){
var Jstring = NewStringUTF(Java.vm.getEnv(), Memory.allocUtf8String(contents));
result = method02(Java.vm.getEnv(), Jstring, Jstring)
console.log("result is =>",result)
console.log("result is ", Java.vm.getEnv().getStringUtfChars(result, null).readCString())
result = Java.vm.getEnv().getStringUtfChars(result, null).readCString();
})
return result;
}
}

rpc.exports = {
invoke1:invoke_method01,
invoke2:invoke_method02
}
function main(){
// hookJava()
// hook_dlopen("libroysue.so", hook_Native_JNi)
find_RegisterNatives("libroysue.so")
}

setImmediate(main)

注:

可以获取jni的NewStringUTF的地址,然后创建函数自动调用。或者直接使用Java.vm.getEnv().newStringUtf(contents)
作用:将java的string转jni的Jsting

 评论