安卓逆向入门-Native层入门

入门

动态注册方法:

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
#include <jni.h>
#include <string>
#include "aes_utils.h"
#include "tools.h"
#include "junk.h"
#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
JNIEXPORT jstring JNICALL method02(JNIEnv *env, jclass jcls, jstring str_) {
if (str_ == nullptr)
return nullptr;
const char *str = env->GetStringUTFChars(str_, JNI_FALSE);
char *result = AES_128_CBC_PKCS5_Decrypt(str);
env->ReleaseStringUTFChars(str_, str);
jstring jResult = getJString(env, result);
free(result);
return jResult;
}
static JNINativeMethod method_table[] = {
// {"method01", "(Ljava/lang/String;)Ljava/lang/String;", (void *) method01},
{"decrypt", "(Ljava/lang/String;)Ljava/lang/String;", (void *) method02},
// (参数1类型标示;参数2类型标示;参数3类型标示...)返回值类型标示
};
static int registerMethods(JNIEnv *env, const char *className,JNINativeMethod *gMethods, int numMethods) {
jclass clazz = env->FindClass(className);
if (clazz == nullptr)
return JNI_FALSE;
if (env->RegisterNatives(clazz, gMethods, numMethods) < 0)
return JNI_FALSE;
return JNI_TRUE;
}
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jstring JNICALL Java_com_roysue_easyso1_MainActivity_method01(JNIEnv *env, jclass jcls, jstring str_) {
if (str_ == nullptr) return nullptr;
const char *str = env->GetStringUTFChars(str_, JNI_FALSE);
char *result = AES_128_CBC_PKCS5_Encrypt(str);
env->ReleaseStringUTFChars(str_, str);
jstring jResult = getJString(env, result);
free(result);
return jResult;
}
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
_JUNK_FUN_0
JNIEnv *env = nullptr;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK)
return JNI_ERR;
assert(env != nullptr);
// 注册native方法
if (!registerMethods(env, "com/roysue/easyso1/MainActivity", method_table,NELEM(method_table)))
return JNI_ERR;
return JNI_VERSION_1_6;
}
#ifdef __cplusplus
}
#endif

加载完SO文件后,ELF首先运行.init.init_array,再找JNI_Onload运行。有重写JNI_Onload的实现则自动运行,没有则系统自动生成一个。SO文件被卸载时JNI_OnUnload被运行。

使用RegisterNatives进行动态注册:

1
2
3
4
5
6
jint RegisterNatives(jclass clazz,const JNINativeMethod* methods,jint nMethods);
typedef struct{
const char* name;
const char* signature;
void* fnPtr;
}JNINativeMethod;

下面Frida脚本可Hook该SO库的加载过程,并输出method01decrypt函数的注册:

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
var ENV = null;
var JCLZ = null;
var method01addr = null;
var method02addr = null;
var method02 = null;
var addrNewStringUTF = null;
var NewStringUTF = null;
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("NewStringUTF") >= 0 && symbol.name.indexOf("CheckJNI") < 0) {
addrNewStringUTF = symbol.address;
console.log("NewStringUTF is at ", symbol.address, symbol.name);
NewStringUTF = new NativeFunction(addrNewStringUTF,'pointer',['pointer','pointer'])
}
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);
}
}
if (addrRegisterNatives != null) {
Interceptor.attach(addrRegisterNatives, {
onEnter: function (args) {
console.log("[RegisterNatives] method_count:", args[3]);
var env = args[0];
ENV = args[0];
var java_class = args[1];
JCLZ = 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));
if(name.indexOf("method01")>=0){
// method01addr = fnPtr_ptr;
continue;
}else if (name.indexOf("decrypt")>=0){
method02addr = fnPtr_ptr;
method02 = new NativeFunction(method02addr,'pointer',['pointer','pointer','pointer']);
method01addr = Module.findExportByName("libroysue.so", "Java_com_roysue_easyso1_MainActivity_method01")
}else{
continue;
}
}
}
});
}
}
function invokemethod01(contents){
console.log("method01_addr is =>",method01addr)
var method01 = new NativeFunction(method01addr,'pointer',['pointer','pointer','pointer']);
var NewStringUTF = new NativeFunction(addrNewStringUTF,'pointer',['pointer','pointer'])
var result = null;
Java.perform(function(){
console.log("Java.vm.getEnv()",Java.vm.getEnv())
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 invokemethod02(contents){
var result = null;
Java.perform(function(){
var JSTRING = NewStringUTF(Java.vm.getEnv(),Memory.allocUtf8String(contents))
result = method02(Java.vm.getEnv(),JSTRING,JSTRING);
result = Java.vm.getEnv().getStringUtfChars(result, null).readCString();
})
return result;
}
rpc.exports = {
invoke1:invokemethod01,
invoke2:invokemethod02
};
setImmediate(hook_RegisterNatives);

下面脚本自动Hook ART中关键符号,如GetStringUTFCharsNewStringUTFFindClassGetMethodID等,输出传参和返回值:

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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/*
GetFieldID is at 0xe39b87c5 _ZN3art3JNI10GetFieldIDEP7_JNIEnvP7_jclassPKcS6_
GetMethodID is at 0xe39a1a19 _ZN3art3JNI11GetMethodIDEP7_JNIEnvP7_jclassPKcS6_
NewStringUTF is at 0xe39cff25 _ZN3art3JNI12NewStringUTFEP7_JNIEnvPKc
RegisterNatives is at 0xe39e08fd _ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi
GetStaticFieldID is at 0xe39c9635 _ZN3art3JNI16GetStaticFieldIDEP7_JNIEnvP7_jclassPKcS6_
GetStaticMethodID is at 0xe39be0ed _ZN3art3JNI17GetStaticMethodIDEP7_JNIEnvP7_jclassPKcS6_
GetStringUTFChars is at 0xe39d06e5 _ZN3art3JNI17GetStringUTFCharsEP7_JNIEnvP8_jstringPh
FindClass is at 0xe399ae5d _ZN3art3JNI9FindClassEP7_JNIEnvPKc
*/
function hook_libart() {
var symbols = Module.enumerateSymbolsSync("libart.so");
var addrGetStringUTFChars = null;
var addrNewStringUTF = null;
var addrFindClass = null;
var addrGetMethodID = null;
var addrGetStaticMethodID = null;
var addrGetFieldID = null;
var addrGetStaticFieldID = null;
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("CheckJNI") < 0) {
if (symbol.name.indexOf("GetStringUTFChars") >= 0) {
addrGetStringUTFChars = symbol.address;
console.log("GetStringUTFChars is at ", symbol.address, symbol.name);
} else if (symbol.name.indexOf("NewStringUTF") >= 0) {
addrNewStringUTF = symbol.address;
console.log("NewStringUTF is at ", symbol.address, symbol.name);
} else if (symbol.name.indexOf("FindClass") >= 0) {
addrFindClass = symbol.address;
console.log("FindClass is at ", symbol.address, symbol.name);
} else if (symbol.name.indexOf("GetMethodID") >= 0) {
addrGetMethodID = symbol.address;
console.log("GetMethodID is at ", symbol.address, symbol.name);
} else if (symbol.name.indexOf("GetStaticMethodID") >= 0) {
addrGetStaticMethodID = symbol.address;
console.log("GetStaticMethodID is at ", symbol.address, symbol.name);
} else if (symbol.name.indexOf("GetFieldID") >= 0) {
addrGetFieldID = symbol.address;
console.log("GetFieldID is at ", symbol.address, symbol.name);
} else if (symbol.name.indexOf("GetStaticFieldID") >= 0) {
addrGetStaticFieldID = symbol.address;
console.log("GetStaticFieldID is at ", symbol.address, symbol.name);
} else if (symbol.name.indexOf("RegisterNatives") >= 0) {
addrRegisterNatives = symbol.address;
console.log("RegisterNatives is at ", symbol.address, symbol.name);
}
}
}
if (addrGetStringUTFChars != null) {
Interceptor.attach(addrGetStringUTFChars, {
onEnter: function (args) { },
onLeave: function (retval) {
if (retval != null) {
var bytes = Memory.readCString(retval);
console.log("[GetStringUTFChars] result:" + bytes);
console.log('CCCryptorCreate called from:\n' + Thread.backtrace(this.context, Backtracer.FUZZY).map(DebugSymbol.fromAddress).join('\n') + '\n');
}
}
});
}
if (addrNewStringUTF != null) {
Interceptor.attach(addrNewStringUTF, {
onEnter: function (args) {
if (args[1] != null) {
var string = Memory.readCString(args[1]);
console.log("[NewStringUTF] bytes:" + string);
}
},
onLeave: function (retval) { }
});
}
if (addrFindClass != null) {
Interceptor.attach(addrFindClass, {
onEnter: function (args) {
if (args[1] != null) {
var name = Memory.readCString(args[1]);
console.log("[FindClass] name:" + name);
}
},
onLeave: function (retval) { }
});
}
if (addrGetMethodID != null) {
Interceptor.attach(addrGetMethodID, {
onEnter: function (args) {
if (args[2] != null) {
var name = Memory.readCString(args[2]);
if (args[3] != null) {
var sig = Memory.readCString(args[3]);
console.log("[GetMethodID] name:" + name + ", sig:" + sig);
} else {
console.log("[GetMethodID] name:" + name);
}
}
},
onLeave: function (retval) { }
});
}
if (addrGetStaticMethodID != null) {
Interceptor.attach(addrGetStaticMethodID, {
onEnter: function (args) {
if (args[2] != null) {
var name = Memory.readCString(args[2]);
if (args[3] != null) {
var sig = Memory.readCString(args[3]);
console.log("[GetStaticMethodID] name:" + name + ", sig:" + sig);
} else {
console.log("[GetStaticMethodID] name:" + name);
}
}
},
onLeave: function (retval) { }
});
}
if (addrGetFieldID != null) {
Interceptor.attach(addrGetFieldID, {
onEnter: function (args) {
if (args[2] != null) {
var name = Memory.readCString(args[2]);
if (args[3] != null) {
var sig = Memory.readCString(args[3]);
console.log("[GetFieldID] name:" + name + ", sig:" + sig);
} else {
console.log("[GetFieldID] name:" + name);
}
}
},
onLeave: function (retval) { }
});
}
if (addrGetStaticFieldID != null) {
Interceptor.attach(addrGetStaticFieldID, {
onEnter: function (args) {
if (args[2] != null) {
var name = Memory.readCString(args[2]);
if (args[3] != null) {
var sig = Memory.readCString(args[3]);
console.log("[GetStaticFieldID] name:" + name + ", sig:" + sig);
} else {
console.log("[GetStaticFieldID] name:" + name);
}
}
},
onLeave: function (retval) { }
});
}
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);
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));
}
},
onLeave: function (retval) { }
});
}
}
setImmediate(hook_libart);

也可用jnitrace工具:

1
jnitrace -l libroysue.so com.xxx.xxx