安卓相关逆向解析笔记

加壳与脱壳

Art脱壳原理

源码分析

InMemoryDexClassLoader源码分析

  • 1、static jobject CreateSingleDexFileCookie(JNIEnv* env, std::unique_ptr<MemMap> data)
  • 2、static const DexFile* CreateDexFile(JNIEnv* env, std::unique_ptr<MemMap> dex_mem_map)
  • 3、DexFile::Open(location,
  • 4、OpenCommon(map->Begin()
  • 5、DexFile::DexFile(const uint8_t* base,

DexClassLoader加载dex源码流程分析

  • 1、OpenAndReadMagic(filename, &magic, error_msg);
  • 2、DexFile::OpenCommon(const uint8_t* base,
  • 3、DexFile::DexFile(const uint8_t* base,

Art下实现函数抽取dex的难点在于dex2oat

如果dex2oat对抽取的dex进行编译生成了oat文件,那么动态修改的dex中的smali指令流也不会生效

解决方案

  • dex指令抽取早于dex2oat
  • 干掉dex2oat的过程

加壳厂商大多数干掉dex2oat
如果在ART下禁用掉dex2oat?

1
2
3
4
5
6
// 调用dex2oat进行编译
GenerateOatFileNoChecks->
Dex2Oat->
Exec->
ExecAndReturnCode->
execv-> 或 execve->

如果在这个流程中任意一个阶段打断, dex2oat就不会成功
接下来就hookexecve的流程来干掉dex2oat

其中github上有一个项目TurboDex来达到了Hook dex2oat达到了第一次加载的时候快速加载完App
如果不干掉dex2oat第一次就会启动编译, 会非常耗时的, 所以干掉dex2oat就会提高加载dex的效率了


Art函数抽取实现方案

首先利用外部工具对dex的code_item代码指令给nop掉, 然后在从loadmethod中恢复这些指令

第一、抽取指令流程

  • 1、解析原始dex文件格式,保存所有方法的代码结构体信息。
  • 2、通过传入需要置空指令的方法和类名,检索到其代码结构体信息。
  • 3、通过方法的代码结构体信息获取指令个数和偏移地址,构造空指令集,然后覆盖原始指令。
  • 4、重新计算dex文件的checksum和signature信息,回写到头部信息中。

第二、指令还原流程

  • 1、native层hook系统函数dexFindClass,获取类结构体信息。
  • 2、获取类中所有的方法信息,通过指定方法名进行过滤,获取该方法的代码结构体信息。
  • 3、获取该方法被抽取的指令集,修改方法对应的内存地址为可读属性,直接进行指令还原。
    在这个过程中,抽取指令需要借助编写的工具,当然工具可以进行深入优化。可以留给你们进行完善。对于还原指令中,如何保存抽取指令并
    没有介绍,这部分内容可以后续完善优化,比如我们可以将抽取的指令再做一次加密保存到程序中的某个地方,在指令还原的时候去这个文件
    进行读取解密即可。

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
#include <jni.h>
#include <string>
#include <unistd.h>
#include <android/log.h>
#include <fcntl.h>
#include <asm/fcntl.h>
#include <sys/mman.h>
#include <dlfcn.h>
//import c header
extern "C" {
#include "hook/dlfcn/dlfcn_compat.h"
#include "hook/include/inlineHook.h"
}
typedef unsigned char byte;
#define TAG "SecondShell"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
struct DexFile {
// Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
// The class we are a part of.
uint32_t declaring_class_;
// Access flags; low 16 bits are defined by spec.
void *begin;
/* Dex file fields. The defining dex file is available via declaring_class_->dex_cache_ */
// Offset to the CodeItem.
uint32_t size;
};
struct ArtMethod {
// Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
// The class we are a part of.
uint32_t declaring_class_;
// Access flags; low 16 bits are defined by spec.
uint32_t access_flags_;
/* Dex file fields. The defining dex file is available via declaring_class_->dex_cache_ */
// Offset to the CodeItem.
uint32_t dex_code_item_offset_;
// Index into method_ids of the dex file associated with this method.
uint32_t dex_method_index_;
};

void* *(*oriexecve)(const char *__file, char *const *__argv, char *const *__envp);

void* *myexecve(const char *__file, char *const *__argv, char *const *__envp) {
LOGD("process:%d,enter execve:%s", getpid(), __file);
if (strstr(__file, "dex2oat")) {
return NULL;
} else {
return oriexecve(__file, __argv, __envp);
}


}

//void ClassLinker::LoadMethod(Thread* self, const DexFile& dex_file, const ClassDataItemIterator& it,Handle<mirror::Class> klass, ArtMethod* dst)
void *(*oriloadmethod)(void *, void *, void *, void *, void *);

void *myloadmethod(void *a, void *b, void *c, void *d, void *e) {
LOGD("process:%d,before run loadmethod:", getpid());
struct ArtMethod *artmethod = (struct ArtMethod *) e;
struct DexFile *dexfile = (struct DexFile *) b;
LOGD("process:%d,enter loadmethod:dexfilebegin:%p,size:%d", getpid(), dexfile->begin,
dexfile->size);//0,57344
char dexfilepath[100] = {0};
sprintf(dexfilepath, "/sdcard/%d_%d.dex", dexfile->size, getpid());
int fd = open(dexfilepath, O_CREAT | O_RDWR, 0666);
if (fd > 0) {
write(fd, dexfile->begin, dexfile->size);
close(fd);
}

void *result = oriloadmethod(a, b, c, d, e);
LOGD("process:%d,enter loadmethod:code_offset:%d,idx:%d", getpid(),
artmethod->dex_code_item_offset_, artmethod->dex_method_index_);

byte *code_item_addr = static_cast<byte *>(dexfile->begin) + artmethod->dex_code_item_offset_;
LOGD("process:%d,enter loadmethod:dexfilebegin:%p,size:%d,beforedumpcodeitem:%p", getpid(),
dexfile->begin, dexfile->size, code_item_addr);


if (artmethod->dex_method_index_ == 15203) {//TestClass.testFunc->methodidx
LOGD("process:%d,enter loadmethod:dexfilebegin:%p,size:%d,start repire method", getpid(),
dexfile->begin, dexfile->size);
byte *code_item_addr = (byte *) dexfile->begin + artmethod->dex_code_item_offset_;
LOGD("process:%d,enter loadmethod:dexfilebegin:%p,size:%d,beforedumpcodeitem:%p", getpid(),
dexfile->begin, dexfile->size, code_item_addr);

int result = mprotect(dexfile->begin, dexfile->size, PROT_WRITE);
byte *code_item_start = static_cast<byte *>(code_item_addr) + 16;
LOGD("process:%d,enter loadmethod:dexfilebegin:%p,size:%d,code_item_start:%p", getpid(),
dexfile->begin, dexfile->size, code_item_start);
byte inst[16] = {0x1a, 0x00, 0xed, 0x34, 0x1a, 0x01, 0x43, 0x32, 0x71, 0x20, 0x91, 0x05,
0x10, 0x00, 0x0e, 0x00};
for (int i = 0; i < sizeof(inst); i++) {
code_item_start[i] = inst[i];
}
//2343->i am from com.kanxue.test02.TestClass.testFunc
code_item_start[2] = 0x43;//34ed->kanxue
code_item_start[3] = 0x23;
memset(dexfilepath, 0, 100);
sprintf(dexfilepath, "/sdcard/%d_%d.dex_15203_2", dexfile->size, getpid());
fd = open(dexfilepath, O_CREAT | O_RDWR, 0666);
if (fd > 0) {
write(fd, dexfile->begin, dexfile->size);
close(fd);
}
}
LOGD("process:%d,after loadmethod:code_offset:%d,idx:%d", getpid(),
artmethod->dex_code_item_offset_, artmethod->dex_method_index_);//0,57344
return result;

}

void hooklibc() {
LOGD("go into hooklibc");
//7.0 命名空间限制
void *libc_addr = dlopen_compat("libc.so", RTLD_NOW);
void *execve_addr = dlsym_compat(libc_addr, "execve");
if (execve_addr != NULL) {
if (ELE7EN_OK == registerInlineHook((uint32_t) execve_addr, (uint32_t) myexecve,
(uint32_t **) &oriexecve)) {
if (ELE7EN_OK == inlineHook((uint32_t) execve_addr)) {
LOGD("inlineHook execve success");
} else {
LOGD("inlineHook execve failure");
}
}
}
}

void hookART() {
LOGD("go into hookART");
void *libart_addr = dlopen_compat("/system/lib/libart.so", RTLD_NOW);
if (libart_addr != NULL) {
void *loadmethod_addr = dlsym_compat(libart_addr,
"_ZN3art11ClassLinker10LoadMethodERKNS_7DexFileERKNS_21ClassDataItemIteratorENS_6HandleINS_6mirror5ClassEEEPNS_9ArtMethodE");
if (loadmethod_addr != NULL) {
if (ELE7EN_OK == registerInlineHook((uint32_t) loadmethod_addr, (uint32_t) myloadmethod,
(uint32_t **) &oriloadmethod)) {
if (ELE7EN_OK == inlineHook((uint32_t) loadmethod_addr)) {
LOGD("inlineHook loadmethod success");
} else {
LOGD("inlineHook loadmethod failure");
}
}
}
}


}

extern "C" JNIEXPORT jstring JNICALL
Java_com_kanxue_secondshell_180_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "ART SecondShell";
return env->NewStringUTF(hello.c_str());
}
extern "C" JNIEXPORT void JNICALL
Java_com_kanxue_secondshell_180_MainActivity_SecondShell(
JNIEnv *env,
jobject /* this */) {
hooklibc();
hookART();
return;
}


NDK开发

JNI:Java Native Interface的缩写,通常翻译为JAVA本地接口。从Java1.1开始,Java Native Interface(JNI)标准成为java平台的一部分,它允许Java代码和其他语言写的代
码进行交互。JNI一开始是为本地已编译语言,尤其是 C和C++而设计的,但是它并不妨碍你使用其他语言,只要调用约定受支持就可以了。

JNI不是Android特有,windows、linux等凡是有JVM的地方都支持JNI。Android的Dalvik/ART虚拟机同样支持JNI标准。
通过JNI,便可以打通Android里的两个世界:JAVA世界和Native世界。
可以说,JNI是Java和Native世界的桥梁。而背后的一切都由Dalvik/ART虚拟机来驱动。

Java语言的特点

特性:简单、面向对象、分布式、编译和解释性、健壮性、跨平台性(Write Once,Run Anywhere)、多线程、动态性等等
但是:性能较低、易于逆向分析等问题
因此,如何能够提高java程序的性能以及如何能够实现对海量已有C/C++代码库的复用问题?


NDK性能分析

java函数运行模式:

  • 1、纯解释模式下执行;
  • 2、JIT模式
  • 3、经过dex2oat编译后在quick模式下运行
    注意: Android 7.0(代号 Nougat,简称 N)开始结合使用 AOT、即时 (JIT) 编译和配置文件引导型编译。

因此一个java函数可能运行在解释模式、JIT或者quick模式。

分别比较相同代码逻辑的java函数和JNI函数的时间代价
java函数运行模式:

  • 1、在4.4以前的dalvik下运行或者在ART下利用hook禁用掉dex2oat过程强制让其运行在解释模式;
  • 2、使用Android6.0测试java函数在quick模式(运行dex2oat编译以后的汇编代码)

JNI函数实现:

  • 1、和java函数相同的逻辑,纯C/C++实现;
  • 2、和java函数相同的逻辑,经过JNI提供的接口频繁调用java函数;

直观感受使用JNI开发的性能提升和代码安全性比较
分别用java和jni实现一个简单的耗时函数,进行执行效率的比较

1
2
3
4
5
6
7
8
这里特别注明一下 

从Android7.0开始, 结合使用AOT、即时编译(JIT)编译和配置文件引导型编译, 所有这些编译模式的组合均可配置

1: 最初安装应用不进行任何AOT编译, 应用前几次运行时, 系统会对其进行解译, 并对经常执行的方法进行JIT编译
2: 当设备闲置和充电时, 编译守护进程会运行, 以便根据在应用前几次运行期间生成的配置文件对常用代码进行AOT编译
3: 下一次重启应用时将会使用配置文件引导型代码, 并避免在运行时对已经编译过的方法进行JIT编译,
在应用后续运行期间进行了JIT编译的方法将会被添加到配置文件中, 然后编译守护进程将会对这些方法进行AOT编译

Art包含一个编译器(dex2oat)工具和一个启动Zygote而加载运行时(libart.so), dex2oat工具接受一个apk文件, 并生成一个或多个编译工程文件, 然后运行时加载这些文件
文件的个数、扩展名和名称会因版本而异

  • .vdex: 包含Apk未压缩的dex代码,另外还有加速验证速度的元数据
  • .odex: 包含Apk中已经过AOT编译的方法代码
  • .art(optional): 包含Apk中列出的某些字符串和类的ART内部表示, 用于加快应用启动速度




NDK

NDK 即Native Development Kit,因此称为“NDK”。

  • NDK是一系列工具的集合。
    NDK提供了工具链,帮助开发者快速开发以及调试C(或C++)的动态库。
  • NDK提供了一份稳定、功能有限的API库。
    不同于linux的glibc,Android采用的是Google Bionic Libc,大部分api是一致的。
  • 一些重要逻辑、算法可以采用C/C++、甚至是汇编的形式通过NDK的工具链最终编译生成动态库,最后通过JNI完成和Dalvik/ART虚拟机环境中的Java代码的交互。

使用NDK开发的so不再具有跨平台特性,需要编译提供不同平台支持ABI:ApplicationBinary Interface


首先利用外部工具对dex的code_item代码指令给nop掉, 然后在从loadmethod中恢复这些指令


JNI函数特征一览

extern “C”:由于C++支持重载,C++类中的函数会有name mangling,后面会详细介绍为什么这里必须要有extern “C”

jni函数的参数问题:JNIEnv和jobject以及JNIEnv和jclass
JNICALL:空宏
JNIEXPORT:__attribute__ ((visibility ("default"))),代表当前函数符号需要导出,与之对应为hidden隐藏符号信息

AndroidStudio默认生成的函数名很长:如Java_com_example_jni_1enc_Jni_stringFromJNI,以下划线分割

NDK开发就是C/C++甚至是汇编结合JNI提供的一系列丰富的api来开发!

JNI基础数据

4种基本引用类型、8种基本类型

其中基本引用类型为:jobject, jclass, jstring, jthrowable
数组引用类型为:jintArray, jbyteArray, jshortArray, jlongArray, jfloatArray, jdoubleArray, jcharArray, jbooleanArray, jobjectArray

JNI中的其他类型: 成员域ID和方法ID

成员域ID、方法方法ID

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct_jfieldId;
typedef struct_jfieldID* jfieldID;
struct _jmethodID;
typedef struct _jmethodID* jmethodID;
typedef union jvalue{
jboolean z;
jbyte b;
jchar c;
jshort s;
jint i;
jlong j;
jfloat f;
jdouble d;
jobject l;
} jvalue;

类描述符

在Java代码中的java.lang.String类的类描述符就是java/lang/String
其实在实践中, 我发现可以直接用该类型的域描述符取代, 也是成功的

例如: jclass intArrCls = env->FindClass("java/lang/String");
等同于: jclass intArrCls = env->FindClass("Ljava/lang/String");

数组类型的描述符则为 [+其类型的域描述符]
例如:

1
2
3
int[] -> [I
float[] -> [F
String[] -> [Ljava/lang/String

域描述符

Field Descriptor Java Language Type
Z boolean
B byte
C char
S short
I int
J long
F float
D double

函数描述符

Java方法 JNI函数签名
String test(); ()Ljava/lang/String;
int f(int i, Object object); (I,Ljava/lang/object)V;
void set(byte[] bytes) ([B)v


String Api

处理返回类型是String,或者参数是String, c和c++与Java交互的过程

jni中的字符串操作

jstring NewStringUTF(const char* bytes)
函数使用给定的C字符串创建一个新的JNI字符串(jstring),不能为构造的java.lang.String分配足够的内存,NewStringUTF会抛出一个OutOfMemoryError异常,并返回一个NULL

const char* GetStringUTFChars(jstring string, jboolean* isCopy)
函数可用于从给定的Java的jstring创建新的C字符串(char *)
如果无法分配内存,则该函数返回NULL。 检查NULL是一个好习惯。不要忘记检查。因为该函数需要为新诞生的UTF-8字符串分配内存,这个操作有可能因为内存太少而失败。
失败时GetStringUTFChars会返回NULL,并抛出一个OutOfMemoryError异常,在不使用GetStringUTFChars()返回的字符串时,需要来释放内存和引用以便可以对其进行垃圾回收
因此之后应始终调用ReleaseStringUTFChars()

jsize GetStringUTFLength(jstring string)用于获取jstring的长度


JNI_OnLoad和JNI_OnUnload

执行时机:当So被加载的时候,比如Java代码里面出现System.loadLibrary或System.load(绝对路径)就会执行到JNI_OnLoad,它会取so导出符号表,然后调用完成

返回值返回的是JNI的类型
第一个参数就是Javavm指针

1
2
3
4
5
6
7
8
9
10
11
12
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
int result = 0;
__android_log_print(4, "kanxue->jni", "JNI_OnLoad(JavaVM *vm, void *reserved)->%p", vm);
__android_log_print(4, "kanxue->jni", "jni->%s", "JNI_OnLoad is called");
JNIEnv *env = nullptr;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) == JNI_OK) {
__android_log_print(4, "kanxue->jni", "jni->%s",
"vm->GetEnv((void**)&env,JNI_VERSION_1_6) success");
}
result = JNI_VERSION_1_6;
return result;
}


JavaVM与JNIEnv

JavaVM是虚拟机在JNI层的代表, 一个进程只有一个JavaVM, 所有线程共用一个JavaVM

  • JNIInvokeInterface_结构封装和JVM相关功能函数,如销毁JVM,获得当前线程的Java执行环境
  • 在C和C++中JavaVM的定义有所不同, 在C中JavaVM是JNIInovkeInterface类型指针, 而在C++中又对JNIInovkeInterface进行了一次封装
  • 推荐使用C++来编写JNI代码,比C中少了一个参数

JNIEnv表示Java调用native语言的环境, 是一个封装了大量JNI方法的指针
JNIEnv只是在创建它的线程生效, 不能跨线程传递, 不同线程的JNIEnv彼此独立
native环境中创建的线程, 如果需要访问JNI, 必须要调用AttachCurrentThread关联, 并使用DetachCurrentThread解除链接

两种代码风格(C\C++)
JavaVM和JNIEnv在C语言环境中和C++环境中调用是有区别的,主要表现在:

  • C风格: (*env)->NewStringUTF(env, "HelloWorld");
  • C++风格: (*env)->NewStringUTF("HelloWorld");

JavaVM的获取

  • 1:在JNI_onLoad中作为参数获得,如下
    JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)该函数由Art负责自动化查找和传入参数进行调用
  • 2:通过JNIEnv的GetJavaVM函数获取,如下
    JavaVm * thisJvm = nullptr;
    env->GetJavaVM(&thisJvm);

结构代码情况

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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
#if defined(__cplusplus)
typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;
#else
typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;
#endif


struct _JNIEnv;
struct _JavaVM;



struct _JavaVM {
const struct JNIInvokeInterface* functions;

#if defined(__cplusplus)
jint DestroyJavaVM()
{ return functions->DestroyJavaVM(this); }
jint AttachCurrentThread(JNIEnv** p_env, void* thr_args)
{ return functions->AttachCurrentThread(this, p_env, thr_args); }
jint DetachCurrentThread()
{ return functions->DetachCurrentThread(this); }
jint GetEnv(void** env, jint version)
{ return functions->GetEnv(this, env, version); }
jint AttachCurrentThreadAsDaemon(JNIEnv** p_env, void* thr_args)
{ return functions->AttachCurrentThreadAsDaemon(this, p_env, thr_args); }
#endif /*__cplusplus*/
};


struct _JNIEnv {
/* do not rename this; it does not seem to be entirely opaque */
const struct JNINativeInterface* functions;

#if defined(__cplusplus)

jint GetVersion()
{ return functions->GetVersion(this); }

jclass DefineClass(const char *name, jobject loader, const jbyte* buf,
jsize bufLen)
{ return functions->DefineClass(this, name, loader, buf, bufLen); }

jclass FindClass(const char* name)
{ return functions->FindClass(this, name); }

jmethodID FromReflectedMethod(jobject method)
{ return functions->FromReflectedMethod(this, method); }

jfieldID FromReflectedField(jobject field)
{ return functions->FromReflectedField(this, field); }

jobject ToReflectedMethod(jclass cls, jmethodID methodID, jboolean isStatic)
{ return functions->ToReflectedMethod(this, cls, methodID, isStatic); }

jclass GetSuperclass(jclass clazz)
{ return functions->GetSuperclass(this, clazz); }

jboolean IsAssignableFrom(jclass clazz1, jclass clazz2)
{ return functions->IsAssignableFrom(this, clazz1, clazz2); }

jobject ToReflectedField(jclass cls, jfieldID fieldID, jboolean isStatic)
{ return functions->ToReflectedField(this, cls, fieldID, isStatic); }

jint Throw(jthrowable obj)
{ return functions->Throw(this, obj); }

jint ThrowNew(jclass clazz, const char* message)
{ return functions->ThrowNew(this, clazz, message); }

jthrowable ExceptionOccurred()
{ return functions->ExceptionOccurred(this); }

void ExceptionDescribe()
{ functions->ExceptionDescribe(this); }

void ExceptionClear()
{ functions->ExceptionClear(this); }

void FatalError(const char* msg)
{ functions->FatalError(this, msg); }

jint PushLocalFrame(jint capacity)
{ return functions->PushLocalFrame(this, capacity); }

jobject PopLocalFrame(jobject result)
{ return functions->PopLocalFrame(this, result); }

jobject NewGlobalRef(jobject obj)
{ return functions->NewGlobalRef(this, obj); }

void DeleteGlobalRef(jobject globalRef)
{ functions->DeleteGlobalRef(this, globalRef); }

void DeleteLocalRef(jobject localRef)
{ functions->DeleteLocalRef(this, localRef); }

jboolean IsSameObject(jobject ref1, jobject ref2)
{ return functions->IsSameObject(this, ref1, ref2); }

jobject NewLocalRef(jobject ref)
{ return functions->NewLocalRef(this, ref); }

jint EnsureLocalCapacity(jint capacity)
{ return functions->EnsureLocalCapacity(this, capacity); }

jobject AllocObject(jclass clazz)
{ return functions->AllocObject(this, clazz); }

jobject NewObject(jclass clazz, jmethodID methodID, ...)
{
va_list args;
va_start(args, methodID);
jobject result = functions->NewObjectV(this, clazz, methodID, args);
va_end(args);
return result;
}

jobject NewObjectV(jclass clazz, jmethodID methodID, va_list args)
{ return functions->NewObjectV(this, clazz, methodID, args); }

jobject NewObjectA(jclass clazz, jmethodID methodID, const jvalue* args)
{ return functions->NewObjectA(this, clazz, methodID, args); }

jclass GetObjectClass(jobject obj)
{ return functions->GetObjectClass(this, obj); }

jboolean IsInstanceOf(jobject obj, jclass clazz)
{ return functions->IsInstanceOf(this, obj, clazz); }

jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)
{ return functions->GetMethodID(this, clazz, name, sig); }

#define CALL_TYPE_METHOD(_jtype, _jname) \
_jtype Call##_jname##Method(jobject obj, jmethodID methodID, ...) \
{ \
_jtype result; \
va_list args; \
va_start(args, methodID); \
result = functions->Call##_jname##MethodV(this, obj, methodID, \
args); \
va_end(args); \
return result; \
}
#define CALL_TYPE_METHODV(_jtype, _jname) \
_jtype Call##_jname##MethodV(jobject obj, jmethodID methodID, \
va_list args) \
{ return functions->Call##_jname##MethodV(this, obj, methodID, args); }
#define CALL_TYPE_METHODA(_jtype, _jname) \
_jtype Call##_jname##MethodA(jobject obj, jmethodID methodID, \
const jvalue* args) \
{ return functions->Call##_jname##MethodA(this, obj, methodID, args); }

#define CALL_TYPE(_jtype, _jname) \
CALL_TYPE_METHOD(_jtype, _jname) \
CALL_TYPE_METHODV(_jtype, _jname) \
CALL_TYPE_METHODA(_jtype, _jname)

CALL_TYPE(jobject, Object)
CALL_TYPE(jboolean, Boolean)
CALL_TYPE(jbyte, Byte)
CALL_TYPE(jchar, Char)
CALL_TYPE(jshort, Short)
CALL_TYPE(jint, Int)
CALL_TYPE(jlong, Long)
CALL_TYPE(jfloat, Float)
CALL_TYPE(jdouble, Double)

void CallVoidMethod(jobject obj, jmethodID methodID, ...)
{
va_list args;
va_start(args, methodID);
functions->CallVoidMethodV(this, obj, methodID, args);
va_end(args);
}
void CallVoidMethodV(jobject obj, jmethodID methodID, va_list args)
{ functions->CallVoidMethodV(this, obj, methodID, args); }
void CallVoidMethodA(jobject obj, jmethodID methodID, const jvalue* args)
{ functions->CallVoidMethodA(this, obj, methodID, args); }

#define CALL_NONVIRT_TYPE_METHOD(_jtype, _jname) \
_jtype CallNonvirtual##_jname##Method(jobject obj, jclass clazz, \
jmethodID methodID, ...) \
{ \
_jtype result; \
va_list args; \
va_start(args, methodID); \
result = functions->CallNonvirtual##_jname##MethodV(this, obj, \
clazz, methodID, args); \
va_end(args); \
return result; \
}
#define CALL_NONVIRT_TYPE_METHODV(_jtype, _jname) \
_jtype CallNonvirtual##_jname##MethodV(jobject obj, jclass clazz, \
jmethodID methodID, va_list args) \
{ return functions->CallNonvirtual##_jname##MethodV(this, obj, clazz, \
methodID, args); }
#define CALL_NONVIRT_TYPE_METHODA(_jtype, _jname) \
_jtype CallNonvirtual##_jname##MethodA(jobject obj, jclass clazz, \
jmethodID methodID, const jvalue* args) \
{ return functions->CallNonvirtual##_jname##MethodA(this, obj, clazz, \
methodID, args); }

#define CALL_NONVIRT_TYPE(_jtype, _jname) \
CALL_NONVIRT_TYPE_METHOD(_jtype, _jname) \
CALL_NONVIRT_TYPE_METHODV(_jtype, _jname) \
CALL_NONVIRT_TYPE_METHODA(_jtype, _jname)

CALL_NONVIRT_TYPE(jobject, Object)
CALL_NONVIRT_TYPE(jboolean, Boolean)
CALL_NONVIRT_TYPE(jbyte, Byte)
CALL_NONVIRT_TYPE(jchar, Char)
CALL_NONVIRT_TYPE(jshort, Short)
CALL_NONVIRT_TYPE(jint, Int)
CALL_NONVIRT_TYPE(jlong, Long)
CALL_NONVIRT_TYPE(jfloat, Float)
CALL_NONVIRT_TYPE(jdouble, Double)

void CallNonvirtualVoidMethod(jobject obj, jclass clazz,
jmethodID methodID, ...)
{
va_list args;
va_start(args, methodID);
functions->CallNonvirtualVoidMethodV(this, obj, clazz, methodID, args);
va_end(args);
}
void CallNonvirtualVoidMethodV(jobject obj, jclass clazz,
jmethodID methodID, va_list args)
{ functions->CallNonvirtualVoidMethodV(this, obj, clazz, methodID, args); }
void CallNonvirtualVoidMethodA(jobject obj, jclass clazz,
jmethodID methodID, const jvalue* args)
{ functions->CallNonvirtualVoidMethodA(this, obj, clazz, methodID, args); }

jfieldID GetFieldID(jclass clazz, const char* name, const char* sig)
{ return functions->GetFieldID(this, clazz, name, sig); }

jobject GetObjectField(jobject obj, jfieldID fieldID)
{ return functions->GetObjectField(this, obj, fieldID); }
jboolean GetBooleanField(jobject obj, jfieldID fieldID)
{ return functions->GetBooleanField(this, obj, fieldID); }
jbyte GetByteField(jobject obj, jfieldID fieldID)
{ return functions->GetByteField(this, obj, fieldID); }
jchar GetCharField(jobject obj, jfieldID fieldID)
{ return functions->GetCharField(this, obj, fieldID); }
jshort GetShortField(jobject obj, jfieldID fieldID)
{ return functions->GetShortField(this, obj, fieldID); }
jint GetIntField(jobject obj, jfieldID fieldID)
{ return functions->GetIntField(this, obj, fieldID); }
jlong GetLongField(jobject obj, jfieldID fieldID)
{ return functions->GetLongField(this, obj, fieldID); }
jfloat GetFloatField(jobject obj, jfieldID fieldID)
{ return functions->GetFloatField(this, obj, fieldID); }
jdouble GetDoubleField(jobject obj, jfieldID fieldID)
{ return functions->GetDoubleField(this, obj, fieldID); }

void SetObjectField(jobject obj, jfieldID fieldID, jobject value)
{ functions->SetObjectField(this, obj, fieldID, value); }
void SetBooleanField(jobject obj, jfieldID fieldID, jboolean value)
{ functions->SetBooleanField(this, obj, fieldID, value); }
void SetByteField(jobject obj, jfieldID fieldID, jbyte value)
{ functions->SetByteField(this, obj, fieldID, value); }
void SetCharField(jobject obj, jfieldID fieldID, jchar value)
{ functions->SetCharField(this, obj, fieldID, value); }
void SetShortField(jobject obj, jfieldID fieldID, jshort value)
{ functions->SetShortField(this, obj, fieldID, value); }
void SetIntField(jobject obj, jfieldID fieldID, jint value)
{ functions->SetIntField(this, obj, fieldID, value); }
void SetLongField(jobject obj, jfieldID fieldID, jlong value)
{ functions->SetLongField(this, obj, fieldID, value); }
void SetFloatField(jobject obj, jfieldID fieldID, jfloat value)
{ functions->SetFloatField(this, obj, fieldID, value); }
void SetDoubleField(jobject obj, jfieldID fieldID, jdouble value)
{ functions->SetDoubleField(this, obj, fieldID, value); }

jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* sig)
{ return functions->GetStaticMethodID(this, clazz, name, sig); }

#define CALL_STATIC_TYPE_METHOD(_jtype, _jname) \
_jtype CallStatic##_jname##Method(jclass clazz, jmethodID methodID, \
...) \
{ \
_jtype result; \
va_list args; \
va_start(args, methodID); \
result = functions->CallStatic##_jname##MethodV(this, clazz, \
methodID, args); \
va_end(args); \
return result; \
}
#define CALL_STATIC_TYPE_METHODV(_jtype, _jname) \
_jtype CallStatic##_jname##MethodV(jclass clazz, jmethodID methodID, \
va_list args) \
{ return functions->CallStatic##_jname##MethodV(this, clazz, methodID, \
args); }
#define CALL_STATIC_TYPE_METHODA(_jtype, _jname) \
_jtype CallStatic##_jname##MethodA(jclass clazz, jmethodID methodID, \
const jvalue* args) \
{ return functions->CallStatic##_jname##MethodA(this, clazz, methodID, \
args); }

#define CALL_STATIC_TYPE(_jtype, _jname) \
CALL_STATIC_TYPE_METHOD(_jtype, _jname) \
CALL_STATIC_TYPE_METHODV(_jtype, _jname) \
CALL_STATIC_TYPE_METHODA(_jtype, _jname)

CALL_STATIC_TYPE(jobject, Object)
CALL_STATIC_TYPE(jboolean, Boolean)
CALL_STATIC_TYPE(jbyte, Byte)
CALL_STATIC_TYPE(jchar, Char)
CALL_STATIC_TYPE(jshort, Short)
CALL_STATIC_TYPE(jint, Int)
CALL_STATIC_TYPE(jlong, Long)
CALL_STATIC_TYPE(jfloat, Float)
CALL_STATIC_TYPE(jdouble, Double)

void CallStaticVoidMethod(jclass clazz, jmethodID methodID, ...)
{
va_list args;
va_start(args, methodID);
functions->CallStaticVoidMethodV(this, clazz, methodID, args);
va_end(args);
}
void CallStaticVoidMethodV(jclass clazz, jmethodID methodID, va_list args)
{ functions->CallStaticVoidMethodV(this, clazz, methodID, args); }
void CallStaticVoidMethodA(jclass clazz, jmethodID methodID, const jvalue* args)
{ functions->CallStaticVoidMethodA(this, clazz, methodID, args); }

jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* sig)
{ return functions->GetStaticFieldID(this, clazz, name, sig); }

jobject GetStaticObjectField(jclass clazz, jfieldID fieldID)
{ return functions->GetStaticObjectField(this, clazz, fieldID); }
jboolean GetStaticBooleanField(jclass clazz, jfieldID fieldID)
{ return functions->GetStaticBooleanField(this, clazz, fieldID); }
jbyte GetStaticByteField(jclass clazz, jfieldID fieldID)
{ return functions->GetStaticByteField(this, clazz, fieldID); }
jchar GetStaticCharField(jclass clazz, jfieldID fieldID)
{ return functions->GetStaticCharField(this, clazz, fieldID); }
jshort GetStaticShortField(jclass clazz, jfieldID fieldID)
{ return functions->GetStaticShortField(this, clazz, fieldID); }
jint GetStaticIntField(jclass clazz, jfieldID fieldID)
{ return functions->GetStaticIntField(this, clazz, fieldID); }
jlong GetStaticLongField(jclass clazz, jfieldID fieldID)
{ return functions->GetStaticLongField(this, clazz, fieldID); }
jfloat GetStaticFloatField(jclass clazz, jfieldID fieldID)
{ return functions->GetStaticFloatField(this, clazz, fieldID); }
jdouble GetStaticDoubleField(jclass clazz, jfieldID fieldID)
{ return functions->GetStaticDoubleField(this, clazz, fieldID); }

void SetStaticObjectField(jclass clazz, jfieldID fieldID, jobject value)
{ functions->SetStaticObjectField(this, clazz, fieldID, value); }
void SetStaticBooleanField(jclass clazz, jfieldID fieldID, jboolean value)
{ functions->SetStaticBooleanField(this, clazz, fieldID, value); }
void SetStaticByteField(jclass clazz, jfieldID fieldID, jbyte value)
{ functions->SetStaticByteField(this, clazz, fieldID, value); }
void SetStaticCharField(jclass clazz, jfieldID fieldID, jchar value)
{ functions->SetStaticCharField(this, clazz, fieldID, value); }
void SetStaticShortField(jclass clazz, jfieldID fieldID, jshort value)
{ functions->SetStaticShortField(this, clazz, fieldID, value); }
void SetStaticIntField(jclass clazz, jfieldID fieldID, jint value)
{ functions->SetStaticIntField(this, clazz, fieldID, value); }
void SetStaticLongField(jclass clazz, jfieldID fieldID, jlong value)
{ functions->SetStaticLongField(this, clazz, fieldID, value); }
void SetStaticFloatField(jclass clazz, jfieldID fieldID, jfloat value)
{ functions->SetStaticFloatField(this, clazz, fieldID, value); }
void SetStaticDoubleField(jclass clazz, jfieldID fieldID, jdouble value)
{ functions->SetStaticDoubleField(this, clazz, fieldID, value); }

jstring NewString(const jchar* unicodeChars, jsize len)
{ return functions->NewString(this, unicodeChars, len); }

jsize GetStringLength(jstring string)
{ return functions->GetStringLength(this, string); }

const jchar* GetStringChars(jstring string, jboolean* isCopy)
{ return functions->GetStringChars(this, string, isCopy); }

void ReleaseStringChars(jstring string, const jchar* chars)
{ functions->ReleaseStringChars(this, string, chars); }

jstring NewStringUTF(const char* bytes)
{ return functions->NewStringUTF(this, bytes); }

jsize GetStringUTFLength(jstring string)
{ return functions->GetStringUTFLength(this, string); }

const char* GetStringUTFChars(jstring string, jboolean* isCopy)
{ return functions->GetStringUTFChars(this, string, isCopy); }

void ReleaseStringUTFChars(jstring string, const char* utf)
{ functions->ReleaseStringUTFChars(this, string, utf); }

jsize GetArrayLength(jarray array)
{ return functions->GetArrayLength(this, array); }

jobjectArray NewObjectArray(jsize length, jclass elementClass,
jobject initialElement)
{ return functions->NewObjectArray(this, length, elementClass,
initialElement); }

jobject GetObjectArrayElement(jobjectArray array, jsize index)
{ return functions->GetObjectArrayElement(this, array, index); }

void SetObjectArrayElement(jobjectArray array, jsize index, jobject value)
{ functions->SetObjectArrayElement(this, array, index, value); }

jbooleanArray NewBooleanArray(jsize length)
{ return functions->NewBooleanArray(this, length); }
jbyteArray NewByteArray(jsize length)
{ return functions->NewByteArray(this, length); }
jcharArray NewCharArray(jsize length)
{ return functions->NewCharArray(this, length); }
jshortArray NewShortArray(jsize length)
{ return functions->NewShortArray(this, length); }
jintArray NewIntArray(jsize length)
{ return functions->NewIntArray(this, length); }
jlongArray NewLongArray(jsize length)
{ return functions->NewLongArray(this, length); }
jfloatArray NewFloatArray(jsize length)
{ return functions->NewFloatArray(this, length); }
jdoubleArray NewDoubleArray(jsize length)
{ return functions->NewDoubleArray(this, length); }

jboolean* GetBooleanArrayElements(jbooleanArray array, jboolean* isCopy)
{ return functions->GetBooleanArrayElements(this, array, isCopy); }
jbyte* GetByteArrayElements(jbyteArray array, jboolean* isCopy)
{ return functions->GetByteArrayElements(this, array, isCopy); }
jchar* GetCharArrayElements(jcharArray array, jboolean* isCopy)
{ return functions->GetCharArrayElements(this, array, isCopy); }
jshort* GetShortArrayElements(jshortArray array, jboolean* isCopy)
{ return functions->GetShortArrayElements(this, array, isCopy); }
jint* GetIntArrayElements(jintArray array, jboolean* isCopy)
{ return functions->GetIntArrayElements(this, array, isCopy); }
jlong* GetLongArrayElements(jlongArray array, jboolean* isCopy)
{ return functions->GetLongArrayElements(this, array, isCopy); }
jfloat* GetFloatArrayElements(jfloatArray array, jboolean* isCopy)
{ return functions->GetFloatArrayElements(this, array, isCopy); }
jdouble* GetDoubleArrayElements(jdoubleArray array, jboolean* isCopy)
{ return functions->GetDoubleArrayElements(this, array, isCopy); }

void ReleaseBooleanArrayElements(jbooleanArray array, jboolean* elems,
jint mode)
{ functions->ReleaseBooleanArrayElements(this, array, elems, mode); }
void ReleaseByteArrayElements(jbyteArray array, jbyte* elems,
jint mode)
{ functions->ReleaseByteArrayElements(this, array, elems, mode); }
void ReleaseCharArrayElements(jcharArray array, jchar* elems,
jint mode)
{ functions->ReleaseCharArrayElements(this, array, elems, mode); }
void ReleaseShortArrayElements(jshortArray array, jshort* elems,
jint mode)
{ functions->ReleaseShortArrayElements(this, array, elems, mode); }
void ReleaseIntArrayElements(jintArray array, jint* elems,
jint mode)
{ functions->ReleaseIntArrayElements(this, array, elems, mode); }
void ReleaseLongArrayElements(jlongArray array, jlong* elems,
jint mode)
{ functions->ReleaseLongArrayElements(this, array, elems, mode); }
void ReleaseFloatArrayElements(jfloatArray array, jfloat* elems,
jint mode)
{ functions->ReleaseFloatArrayElements(this, array, elems, mode); }
void ReleaseDoubleArrayElements(jdoubleArray array, jdouble* elems,
jint mode)
{ functions->ReleaseDoubleArrayElements(this, array, elems, mode); }

void GetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len,
jboolean* buf)
{ functions->GetBooleanArrayRegion(this, array, start, len, buf); }
void GetByteArrayRegion(jbyteArray array, jsize start, jsize len,
jbyte* buf)
{ functions->GetByteArrayRegion(this, array, start, len, buf); }
void GetCharArrayRegion(jcharArray array, jsize start, jsize len,
jchar* buf)
{ functions->GetCharArrayRegion(this, array, start, len, buf); }
void GetShortArrayRegion(jshortArray array, jsize start, jsize len,
jshort* buf)
{ functions->GetShortArrayRegion(this, array, start, len, buf); }
void GetIntArrayRegion(jintArray array, jsize start, jsize len,
jint* buf)
{ functions->GetIntArrayRegion(this, array, start, len, buf); }
void GetLongArrayRegion(jlongArray array, jsize start, jsize len,
jlong* buf)
{ functions->GetLongArrayRegion(this, array, start, len, buf); }
void GetFloatArrayRegion(jfloatArray array, jsize start, jsize len,
jfloat* buf)
{ functions->GetFloatArrayRegion(this, array, start, len, buf); }
void GetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len,
jdouble* buf)
{ functions->GetDoubleArrayRegion(this, array, start, len, buf); }

void SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len,
const jboolean* buf)
{ functions->SetBooleanArrayRegion(this, array, start, len, buf); }
void SetByteArrayRegion(jbyteArray array, jsize start, jsize len,
const jbyte* buf)
{ functions->SetByteArrayRegion(this, array, start, len, buf); }
void SetCharArrayRegion(jcharArray array, jsize start, jsize len,
const jchar* buf)
{ functions->SetCharArrayRegion(this, array, start, len, buf); }
void SetShortArrayRegion(jshortArray array, jsize start, jsize len,
const jshort* buf)
{ functions->SetShortArrayRegion(this, array, start, len, buf); }
void SetIntArrayRegion(jintArray array, jsize start, jsize len,
const jint* buf)
{ functions->SetIntArrayRegion(this, array, start, len, buf); }
void SetLongArrayRegion(jlongArray array, jsize start, jsize len,
const jlong* buf)
{ functions->SetLongArrayRegion(this, array, start, len, buf); }
void SetFloatArrayRegion(jfloatArray array, jsize start, jsize len,
const jfloat* buf)
{ functions->SetFloatArrayRegion(this, array, start, len, buf); }
void SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len,
const jdouble* buf)
{ functions->SetDoubleArrayRegion(this, array, start, len, buf); }

jint RegisterNatives(jclass clazz, const JNINativeMethod* methods,
jint nMethods)
{ return functions->RegisterNatives(this, clazz, methods, nMethods); }

jint UnregisterNatives(jclass clazz)
{ return functions->UnregisterNatives(this, clazz); }

jint MonitorEnter(jobject obj)
{ return functions->MonitorEnter(this, obj); }

jint MonitorExit(jobject obj)
{ return functions->MonitorExit(this, obj); }

jint GetJavaVM(JavaVM** vm)
{ return functions->GetJavaVM(this, vm); }

void GetStringRegion(jstring str, jsize start, jsize len, jchar* buf)
{ functions->GetStringRegion(this, str, start, len, buf); }

void GetStringUTFRegion(jstring str, jsize start, jsize len, char* buf)
{ return functions->GetStringUTFRegion(this, str, start, len, buf); }

void* GetPrimitiveArrayCritical(jarray array, jboolean* isCopy)
{ return functions->GetPrimitiveArrayCritical(this, array, isCopy); }

void ReleasePrimitiveArrayCritical(jarray array, void* carray, jint mode)
{ functions->ReleasePrimitiveArrayCritical(this, array, carray, mode); }

const jchar* GetStringCritical(jstring string, jboolean* isCopy)
{ return functions->GetStringCritical(this, string, isCopy); }

void ReleaseStringCritical(jstring string, const jchar* carray)
{ functions->ReleaseStringCritical(this, string, carray); }

jweak NewWeakGlobalRef(jobject obj)
{ return functions->NewWeakGlobalRef(this, obj); }

void DeleteWeakGlobalRef(jweak obj)
{ functions->DeleteWeakGlobalRef(this, obj); }

jboolean ExceptionCheck()
{ return functions->ExceptionCheck(this); }

jobject NewDirectByteBuffer(void* address, jlong capacity)
{ return functions->NewDirectByteBuffer(this, address, capacity); }

void* GetDirectBufferAddress(jobject buf)
{ return functions->GetDirectBufferAddress(this, buf); }

jlong GetDirectBufferCapacity(jobject buf)
{ return functions->GetDirectBufferCapacity(this, buf); }

/* added in JNI 1.6 */
jobjectRefType GetObjectRefType(jobject obj)
{ return functions->GetObjectRefType(this, obj); }
#endif /*__cplusplus*/
};

经常使用到的GetEnvmain主线程使用,AttachCurrentThread新建线程使用
而JniEnv提供了特别多的接口使用

测试案例

Java

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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
package com.kanxue.reflectiontest;

import android.util.Log;

public class Test {

public String flag = null;

public Test() {

flag = "Test()";
}

public Test(String arg) {

flag = "Test(String arg)";
}

public Test(String arg, int arg2) {

flag = "Test(String arg,int arg2)";
}

public static String publicStaticField = "i am a publicStaticField";
public String publicField = "i am a publicField";
private static String privateStaticField = "i am a privateStaticField";
private String privateField = "i am a privateField";

public static void publicStaticFunc() {
Log.i("kanxue", "i am from publicStaticFunc");
}

public void publicFunc() {
Log.i("kanxue", "i am from publicFunc");
}

private static void privateStaticFunc() {
Log.i("kanxue", "i am from privateStaticFunc");
}

private static void privateFunc() {
Log.i("kanxue", "i am from privateStaticFunc");
}

}



package com.kanxue.reflectiontest;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class MainActivity extends AppCompatActivity {

// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
//System.load("");
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Example of a call to a native method
TextView tv = findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
//testField();
//testMethod();
}

public void testField() {
Test a_obj = new Test();
Test b_obj = new Test("test");
Test c_obj = new Test("test", 2);
Class testClazz = null;
try {
testClazz = MainActivity.class.getClassLoader().loadClass("com.kanxue.reflectiontest.Test");
Log.i("kanxue", "ClassLoader.loadClass->" + testClazz);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Class testClazz2;
try {
testClazz2 = Class.forName("com.kanxue.reflectiontest.Test");
Log.i("kanxue", "Class.forName->" + testClazz2);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Class testClazz3 = Test.class;
Log.i("kanxue", "Test.class->" + testClazz);
try {
Field publicStaticField_field = testClazz3.getDeclaredField("publicStaticField");
Log.i("kanxue", "getDeclaredField->" + publicStaticField_field);
String content = (String) publicStaticField_field.get(null);
Log.i("kanxue", "publicStaticField->" + content);
content = (String) publicStaticField_field.get(a_obj);
Log.i("kanxue", "publicStaticField->a_obj" + content);

} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
//privateField
try {
Field privateStaticField_field = testClazz3.getDeclaredField("privateStaticField");
Log.i("kanxue", "getDeclaredField->" + privateStaticField_field);
privateStaticField_field.setAccessible(true);
privateStaticField_field.set(null, "modified");
String content = (String) privateStaticField_field.get(null);

Log.i("kanxue", "privateStaticField_field->" + content);

} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
try {
Field privateField_field = testClazz3.getField("privateField");
Log.i("kanxue", "getField->" + privateField_field);
} catch (Error | NoSuchFieldException e) {
e.printStackTrace();
}
Field[] fields = testClazz3.getDeclaredFields();
for (Field i : fields) {
Log.i("kanxue", "getDeclaredFields->" + i);
}

Field[] fields_1 = testClazz3.getFields();
for (Field i : fields_1) {
Log.i("kanxue", "getFields->" + i);
}


}

public void testMethod() {
Class testClazz = Test.class;
Method publicStaticFunc_method = null;
try {
publicStaticFunc_method = testClazz.getDeclaredMethod("publicStaticFunc");
publicStaticFunc_method.invoke(null);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
Log.i("kanxue", "getDeclaredMethod->" + publicStaticFunc_method);

Method privateStaticFunc_method = null;
try {
privateStaticFunc_method = testClazz.getDeclaredMethod("privateStaticFunc");
privateStaticFunc_method.setAccessible(true);
privateStaticFunc_method.invoke(null);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
Log.i("kanxue", "getDeclaredMethod->" + privateStaticFunc_method);

Method[] methods = testClazz.getDeclaredMethods();
for (Method i : methods) {
Log.i("kanxue", "getDeclaredMethods->" + i);
}
Method[] methods_1 = testClazz.getMethods();
for (Method i : methods_1) {
Log.i("kanxue", "getMethods->" + i);
}
Constructor[] constructors=testClazz.getDeclaredConstructors();
for (Constructor i : constructors) {
Log.i("kanxue", "getDeclaredConstructors->" + i);
}
try {
Constructor constructor=testClazz.getDeclaredConstructor(String.class,int.class);
Object testobj=constructor.newInstance("test",666);
Field privateField_field=testClazz.getDeclaredField("privateField");
privateField_field.setAccessible(true);
String privateField_String= (String) privateField_field.get(testobj);
Log.i("kanxue","privateField->"+privateField_String);


Log.i("kanxue", "getDeclaredConstructor->" + constructor);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}

}

/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();
public native String stringFromJNIC();
public native String getFields();
}

Cpp

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
#include <jni.h>
#include <string>
#include <android/log.h>
#include <pthread.h>
#include <thread>

JavaVM *globalVM = nullptr;
extern "C" JNIEXPORT jstring JNICALL
Java_com_kanxue_reflectiontest_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
JavaVM *thisvm = nullptr;
env->GetJavaVM(&thisvm);

__android_log_print(4, "kanxue->jni", "env->GetJavaVM(&thisvm)->%p", thisvm);
__android_log_print(4, "kanxue->jni",
"Java_com_kanxue_reflectiontest_MainActivity_stringFromJNI->%p", env);

/* jint GetVersion()
{ return functions->GetVersion(this); }

jclass DefineClass(const char *name, jobject loader, const jbyte* buf,
jsize bufLen)
{ return functions->DefineClass(this, name, loader, buf, bufLen); }

jclass FindClass(const char* name)
{ return functions->FindClass(this, name); }*/

std::string hello = "Hello from C++";
//publicStaticField
jclass TestJclass = env->FindClass("com/kanxue/reflectiontest/Test");
//jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* sig)
jfieldID publicStaticField_jfieldID = env->GetStaticFieldID(TestJclass, "publicStaticField",
"Ljava/lang/String;");
//jobject GetStaticObjectField(jclass clazz, jfieldID fieldID)
jstring publicStaticField_content = (jstring) env->GetStaticObjectField(TestJclass,
publicStaticField_jfieldID);
const char *content_ptr = env->GetStringUTFChars(publicStaticField_content, nullptr);
__android_log_print(4, "kanxue->jni", "jni->%s", content_ptr);

return env->NewStringUTF(hello.c_str());
}
extern "C" JNIEXPORT jstring JNICALL
Java_com_kanxue_reflectiontest_MainActivity_getFields(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
}

void *threadtest(void *args) {

for (int i = 0; i < 10; i++) {
__android_log_print(4, "kanxue->jni", "jni->%s,%d", "I am from threadtest", i);
}
JNIEnv *threadenv = nullptr;
if (globalVM->GetEnv((void **) &threadenv, JNI_VERSION_1_6) == JNI_OK) {
__android_log_print(4, "kanxue->jni", "jni->%s",
"globalVM->GetEnv((void**)&threadenv,JNI_VERSION_1_6)==JNI_OK success");


} else {
__android_log_print(4, "kanxue->jni", "jni->%s",
"globalVM->GetEnv((void**)&threadenv,JNI_VERSION_1_6)==JNI_OK failed");
}
int result = globalVM->AttachCurrentThread(&threadenv, nullptr);
if (result == JNI_OK) {
__android_log_print(4, "kanxue->jni", "jni->%s",
" globalVM->AttachCurrentThread(&threadenv, nullptr)==JNI_OK success");
jstring jstring1 = threadenv->NewStringUTF("threadtest jstring");
const char *content = threadenv->GetStringUTFChars(jstring1, nullptr);
__android_log_print(4, "kanxue->jni", "jni->%s", content);
threadenv->ReleaseStringUTFChars(jstring1, content);

} else {
__android_log_print(4, "kanxue->jni", "jni->%s",
" globalVM->AttachCurrentThread(&threadenv, nullptr)==JNI_OK failed");
}
//publicStaticField
jclass TestJclass = threadenv->FindClass("com/kanxue/reflectiontest/Test");
threadenv->ExceptionDescribe();
threadenv->ExceptionClear();
if (TestJclass == nullptr) {
__android_log_print(4, "kanxue->jni", "jni->%s",
"TestJclass is nullptr");
} else {
__android_log_print(4, "kanxue->jni", "jni->%s",
"TestJclass is not nullptr");
//jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* sig)
jfieldID publicStaticField_jfieldID = threadenv->GetStaticFieldID(TestJclass,
"publicStaticField",
"Ljava/lang/String;");
//jobject GetStaticObjectField(jclass clazz, jfieldID fieldID)
jstring publicStaticField_content = (jstring) threadenv->GetStaticObjectField(TestJclass,
publicStaticField_jfieldID);
const char *content_ptr = threadenv->GetStringUTFChars(publicStaticField_content, nullptr);
__android_log_print(4, "kanxue->jni", "jni->%s", content_ptr);

};

//globalVM->AttachCurrentThread()

globalVM->DetachCurrentThread();

pthread_exit(0);

}

JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
globalVM = vm;
__android_log_print(4, "kanxue->jni", "JNI_OnLoad(JavaVM *vm, void *reserved)->%p", vm);
__android_log_print(4, "kanxue->jni", "jni->%s", "JNI_OnLoad is called");
jint result = 0;
JNIEnv *env = nullptr;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) == JNI_OK) {
__android_log_print(4, "kanxue->jni", "jni->%s",
"vm->GetEnv((void**)&env,JNI_VERSION_1_6) success");
}

//publicStaticField
jclass TestJclass = env->FindClass("com/kanxue/reflectiontest/Test");
//jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* sig)
jfieldID publicStaticField_jfieldID = env->GetStaticFieldID(TestJclass, "publicStaticField",
"Ljava/lang/String;");
//jobject GetStaticObjectField(jclass clazz, jfieldID fieldID)
jstring publicStaticField_content = (jstring) env->GetStaticObjectField(TestJclass,
publicStaticField_jfieldID);
const char *content_ptr = env->GetStringUTFChars(publicStaticField_content, nullptr);
__android_log_print(4, "kanxue->jni", "jni->%s", content_ptr);


__android_log_print(4, "kanxue->jni", "GetEnv((void**)&env,JNI_VERSION_1_6)->%p", env);
//int pthread_create(pthread_t* __pthread_ptr, pthread_attr_t const* __attr, void* (*__start_routine)(void*), void*);
pthread_t thread;
//pthread_create(&thread, nullptr,threadtest, nullptr);
pthread_create(&thread, nullptr, threadtest, nullptr);
pthread_join(thread, nullptr);

/* jint DestroyJavaVM()
{ return functions->DestroyJavaVM(this); }
jint AttachCurrentThread(JNIEnv** p_env, void* thr_args)
{ return functions->AttachCurrentThread(this, p_env, thr_args); }
jint DetachCurrentThread()
{ return functions->DetachCurrentThread(this); }
jint GetEnv(void** env, jint version)
{ return functions->GetEnv(this, env, version); }
jint AttachCurrentThreadAsDaemon(JNIEnv** p_env, void* thr_args)
{ return functions->AttachCurrentThreadAsDaemon(this, p_env, thr_args); }*/
result = JNI_VERSION_1_6;
return result;


}




Java反射与NDK开发

利用Java反射可以获取所需私有成员或是方法
而对于public类型的成员变量和方法或属性都可以使用反射来进行访问

Java反射的相关类

类名 用途
Class类 代表类的实体,在运行的Java应用程序中表示类和接口
Field类 代表类的成员变量
Method类 代表类的方法
Constructor类 代表类的构造方法

JNI新建对象和访问Java属性

MainActivity.Java

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
173
174
175
176
177
178
179
180
181
182
183
package com.kanxue.reflectiontest;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class MainActivity extends AppCompatActivity {

// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
//System.load("");
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Example of a call to a native method
TextView tv = findViewById(R.id.sample_text);
//tv.setText(stringFromJNI());
//getStaticField();
Test testobj=new Test("i am from java");
getNonStaticField(testobj);
int[] intarray=testobj.intarray;
for(int i=0;i<intarray.length;i++){
Log.i("kanxue->java",i+"--"+intarray[i]);
}

//newObject();
//testField();
//testMethod();
}

public void testField() {
Test a_obj = new Test();
Test b_obj = new Test("test");
Test c_obj = new Test("test", 2);
Class testClazz = null;
try {
testClazz = MainActivity.class.getClassLoader().loadClass("com.kanxue.reflectiontest.Test");
Log.i("kanxue", "ClassLoader.loadClass->" + testClazz);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Class testClazz2;
try {
testClazz2 = Class.forName("com.kanxue.reflectiontest.Test");
Log.i("kanxue", "Class.forName->" + testClazz2);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Class testClazz3 = Test.class;
Log.i("kanxue", "Test.class->" + testClazz);
try {
Field publicStaticField_field = testClazz3.getDeclaredField("publicStaticField");
Log.i("kanxue", "getDeclaredField->" + publicStaticField_field);
String content = (String) publicStaticField_field.get(null);
Log.i("kanxue", "publicStaticField->" + content);
content = (String) publicStaticField_field.get(a_obj);
Log.i("kanxue", "publicStaticField->a_obj" + content);

} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
//privateField
try {
Field privateStaticField_field = testClazz3.getDeclaredField("privateStaticField");
Log.i("kanxue", "getDeclaredField->" + privateStaticField_field);
privateStaticField_field.setAccessible(true);
privateStaticField_field.set(null, "modified");
String content = (String) privateStaticField_field.get(null);

Log.i("kanxue", "privateStaticField_field->" + content);

} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
try {
Field privateField_field = testClazz3.getField("privateField");
Log.i("kanxue", "getField->" + privateField_field);
} catch (Error | NoSuchFieldException e) {
e.printStackTrace();
}
Field[] fields = testClazz3.getDeclaredFields();
for (Field i : fields) {
Log.i("kanxue", "getDeclaredFields->" + i);
}

Field[] fields_1 = testClazz3.getFields();
for (Field i : fields_1) {
Log.i("kanxue", "getFields->" + i);
}


}

public void testMethod() {
Class testClazz = Test.class;
Method publicStaticFunc_method = null;
try {
publicStaticFunc_method = testClazz.getDeclaredMethod("publicStaticFunc");
publicStaticFunc_method.invoke(null);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
Log.i("kanxue", "getDeclaredMethod->" + publicStaticFunc_method);

Method privateStaticFunc_method = null;
try {
privateStaticFunc_method = testClazz.getDeclaredMethod("privateStaticFunc");
privateStaticFunc_method.setAccessible(true);
privateStaticFunc_method.invoke(null);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
Log.i("kanxue", "getDeclaredMethod->" + privateStaticFunc_method);

Method[] methods = testClazz.getDeclaredMethods();
for (Method i : methods) {
Log.i("kanxue", "getDeclaredMethods->" + i);
}
Method[] methods_1 = testClazz.getMethods();
for (Method i : methods_1) {
Log.i("kanxue", "getMethods->" + i);
}
Constructor[] constructors=testClazz.getDeclaredConstructors();
for (Constructor i : constructors) {
Log.i("kanxue", "getDeclaredConstructors->" + i);
}
try {
Constructor constructor=testClazz.getDeclaredConstructor(String.class,int.class);
Object testobj=constructor.newInstance("test",666);
Field privateField_field=testClazz.getDeclaredField("privateField");
privateField_field.setAccessible(true);
String privateField_String= (String) privateField_field.get(testobj);
Log.i("kanxue","privateField->"+privateField_String);


Log.i("kanxue", "getDeclaredConstructor->" + constructor);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}

}

/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();
public native String stringFromJNIC();
public native void newObject();
public native void getStaticField();
public native void getNonStaticField(Object m);
}

Test.java

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
package com.kanxue.reflectiontest;

import android.util.Log;

public class Test {

public String flag = null;
public int[] intarray = null;

public Test() {

flag = "Test()";
intarray = new int[10];
for (int i = 0; i < 10; i++) {
intarray[i] = i;

}
}

public Test(String arg) {

flag = "Test(String arg)";
intarray = new int[10];
for (int i = 0; i < 10; i++) {
intarray[i] = i;

}
}

public Test(String arg, int arg2) {

flag = "Test(String arg,int arg2)";
intarray = new int[10];
for (int i = 0; i < 10; i++) {
intarray[i] = i;

}
}

public static String publicStaticField = "i am a publicStaticField";
public static int publicStaticField_int = 100;
public String publicField = "i am a publicField";
private static String privateStaticField = "i am a privateStaticField";
private String privateField = "i am a privateField";
private int privateField_int = 200;

public static void publicStaticFunc() {
Log.i("kanxue", "i am from publicStaticFunc");
}

public void publicFunc() {
Log.i("kanxue", "i am from publicFunc");
}

private static void privateStaticFunc() {
Log.i("kanxue", "i am from privateStaticFunc");
}

private static void privateFunc() {
Log.i("kanxue", "i am from privateStaticFunc");
}

}

native-lib.cpp

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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
#include <jni.h>
#include <string>
#include <android/log.h>
#include <pthread.h>
#include <thread>

JavaVM *globalVM = nullptr;
extern "C" JNIEXPORT jstring JNICALL
Java_com_kanxue_reflectiontest_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
JavaVM *thisvm = nullptr;
env->GetJavaVM(&thisvm);

__android_log_print(4, "kanxue->jni", "env->GetJavaVM(&thisvm)->%p", thisvm);
__android_log_print(4, "kanxue->jni",
"Java_com_kanxue_reflectiontest_MainActivity_stringFromJNI->%p", env);

/* jint GetVersion()
{ return functions->GetVersion(this); }

jclass DefineClass(const char *name, jobject loader, const jbyte* buf,
jsize bufLen)
{ return functions->DefineClass(this, name, loader, buf, bufLen); }

jclass FindClass(const char* name)
{ return functions->FindClass(this, name); }*/

std::string hello = "Hello from C++";
//publicStaticField
jclass TestJclass = env->FindClass("com/kanxue/reflectiontest/Test");
//jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* sig)
jfieldID publicStaticField_jfieldID = env->GetStaticFieldID(TestJclass, "publicStaticField",
"Ljava/lang/String;");
//jobject GetStaticObjectField(jclass clazz, jfieldID fieldID)
jstring publicStaticField_content = (jstring) env->GetStaticObjectField(TestJclass,
publicStaticField_jfieldID);
const char *content_ptr = env->GetStringUTFChars(publicStaticField_content, nullptr);
__android_log_print(4, "kanxue->jni", "jni->%s", content_ptr);

return env->NewStringUTF(hello.c_str());
}

extern "C" JNIEXPORT void JNICALL
Java_com_kanxue_reflectiontest_MainActivity_newObject(
JNIEnv *env,
jobject /* this */) {
//NewObject
jclass TestJclass = env->FindClass("com/kanxue/reflectiontest/Test");
//public Test(String arg)
// jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)
jmethodID con_mid = env->GetMethodID(TestJclass, "<init>", "(Ljava/lang/String;)V");
jstring arg = env->NewStringUTF("I am From Jni");
//jobject NewObject(jclass clazz, jmethodID methodID, ...)
jobject testobj = env->NewObject(TestJclass, con_mid, arg);
if (testobj != nullptr) {
__android_log_print(4, "kanxue->jni", "jni->%s", "NewObject success!");
}

//AllocObject
//jclass clazz_str = env->FindClass("java/lang/String");
//jmethodID methodID_str = env->GetMethodID(clazz_str ,"<init>", "()V");
jobject testobj2 = env->AllocObject(TestJclass);
jstring arg1 = env->NewStringUTF("I am From Jni->AllocObject");
env->CallNonvirtualVoidMethod(testobj2, TestJclass, con_mid, arg1);
if (testobj2 != nullptr) {

__android_log_print(4, "kanxue->jni", "jni->%s", "AllocObject success!");
}

}
extern "C" JNIEXPORT void JNICALL
Java_com_kanxue_reflectiontest_MainActivity_getStaticField(
JNIEnv *env,
jobject /* this */) {
//private static
jclass TestJclass = env->FindClass("com/kanxue/reflectiontest/Test");
// public static String publicStaticField = "i am a publicStaticField";
// jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* sig)
jfieldID publicStaticField_fid = env->GetStaticFieldID(TestJclass, "publicStaticField",
"Ljava/lang/String;");
/*jstring publicStaticField_obj = static_cast<jstring>(env->GetStaticObjectField(TestJclass,
publicStaticField_fid));
const char *publicStaticField_content = env->GetStringUTFChars(publicStaticField_obj, nullptr);
__android_log_print(4, "kanxue->jni", "publicStaticField_obj->%s", publicStaticField_content);*/

jstring setjstring = env->NewStringUTF("modified by jni");
// void SetStaticObjectField(jclass clazz, jfieldID fieldID, jobject value)
env->SetStaticObjectField(TestJclass, publicStaticField_fid, setjstring);
jstring publicStaticField_obj = static_cast<jstring>(env->GetStaticObjectField(TestJclass,
publicStaticField_fid));
const char *publicStaticField_content = env->GetStringUTFChars(publicStaticField_obj, nullptr);
__android_log_print(4, "kanxue->jni", "publicStaticField_obj->%s", publicStaticField_content);

//private static String privateStaticField = "i am a privateStaticField";
jfieldID privateStaticField_fid = env->GetStaticFieldID(TestJclass, "privateStaticField",
"Ljava/lang/String;");
jstring privateStaticField_obj = static_cast<jstring>(env->GetStaticObjectField(TestJclass,
privateStaticField_fid));
const char *privateStaticField_content = env->GetStringUTFChars(privateStaticField_obj,
nullptr);
__android_log_print(4, "kanxue->jni", "privateStaticField_obj->%s", privateStaticField_content);

// public static int publicStaticField_int = 100;

jfieldID publicStaticField_int_fid = env->GetStaticFieldID(TestJclass, "publicStaticField_int",
"I");


env->SetStaticIntField(TestJclass, publicStaticField_int_fid, 200);

jint publicStaticField_int_value = env->GetStaticIntField(TestJclass,
publicStaticField_int_fid);

__android_log_print(4, "kanxue->jni", "publicStaticField_int_value->%d",
publicStaticField_int_value);

/* jobject GetStaticObjectField(jclass clazz, jfieldID fieldID)
{ return functions->GetStaticObjectField(this, clazz, fieldID); }


jboolean GetStaticBooleanField(jclass clazz, jfieldID fieldID)
{ return functions->GetStaticBooleanField(this, clazz, fieldID); }
jbyte GetStaticByteField(jclass clazz, jfieldID fieldID)
{ return functions->GetStaticByteField(this, clazz, fieldID); }
jchar GetStaticCharField(jclass clazz, jfieldID fieldID)
{ return functions->GetStaticCharField(this, clazz, fieldID); }
jshort GetStaticShortField(jclass clazz, jfieldID fieldID)
{ return functions->GetStaticShortField(this, clazz, fieldID); }
jint GetStaticIntField(jclass clazz, jfieldID fieldID)
{ return functions->GetStaticIntField(this, clazz, fieldID); }
jlong GetStaticLongField(jclass clazz, jfieldID fieldID)
{ return functions->GetStaticLongField(this, clazz, fieldID); }
jfloat GetStaticFloatField(jclass clazz, jfieldID fieldID)
{ return functions->GetStaticFloatField(this, clazz, fieldID); }
jdouble GetStaticDoubleField(jclass clazz, jfieldID fieldID)
{ return functions->GetStaticDoubleField(this, clazz, fieldID); }*/



/* void SetStaticObjectField(jclass clazz, jfieldID fieldID, jobject value)
{ functions->SetStaticObjectField(this, clazz, fieldID, value); }
void SetStaticBooleanField(jclass clazz, jfieldID fieldID, jboolean value)
{ functions->SetStaticBooleanField(this, clazz, fieldID, value); }
void SetStaticByteField(jclass clazz, jfieldID fieldID, jbyte value)
{ functions->SetStaticByteField(this, clazz, fieldID, value); }
void SetStaticCharField(jclass clazz, jfieldID fieldID, jchar value)
{ functions->SetStaticCharField(this, clazz, fieldID, value); }
void SetStaticShortField(jclass clazz, jfieldID fieldID, jshort value)
{ functions->SetStaticShortField(this, clazz, fieldID, value); }
void SetStaticIntField(jclass clazz, jfieldID fieldID, jint value)
{ functions->SetStaticIntField(this, clazz, fieldID, value); }
void SetStaticLongField(jclass clazz, jfieldID fieldID, jlong value)
{ functions->SetStaticLongField(this, clazz, fieldID, value); }
void SetStaticFloatField(jclass clazz, jfieldID fieldID, jfloat value)
{ functions->SetStaticFloatField(this, clazz, fieldID, value); }
void SetStaticDoubleField(jclass clazz, jfieldID fieldID, jdouble value)
{ functions->SetStaticDoubleField(this, clazz, fieldID, value); }*/


}
// public native void getNonStaticField(Object m);
extern "C" JNIEXPORT void JNICALL
Java_com_kanxue_reflectiontest_MainActivity_getNonStaticField(
JNIEnv *env,
jobject obj, jobject testobj) {

// private String privateField = "i am a privateField";

jclass TestJclass = env->FindClass("com/kanxue/reflectiontest/Test");
jfieldID privateField_fid = env->GetFieldID(TestJclass, "privateField", "Ljava/lang/String;");
// jobject GetObjectField(jobject obj, jfieldID fieldID)
jstring privateField_obj = static_cast<jstring>(env->GetObjectField(testobj, privateField_fid));
/* const char *privateField_obj_content = env->GetStringUTFChars(privateField_obj,
nullptr);
__android_log_print(4, "kanxue->jni", "privateField_obj->%s", privateField_obj_content);*/

//
jstring newString = env->NewStringUTF("Modified by jni");
env->SetObjectField(testobj, privateField_fid, newString);


privateField_obj = static_cast<jstring>(env->GetObjectField(testobj, privateField_fid));
const char *privateField_obj_content = env->GetStringUTFChars(privateField_obj,
nullptr);
__android_log_print(4, "kanxue->jni", "privateField_obj->%s", privateField_obj_content);

/* void SetObjectField(jobject obj, jfieldID fieldID, jobject value)
{ functions->SetObjectField(this, obj, fieldID, value); }
void SetBooleanField(jobject obj, jfieldID fieldID, jboolean value)
{ functions->SetBooleanField(this, obj, fieldID, value); }
void SetByteField(jobject obj, jfieldID fieldID, jbyte value)
{ functions->SetByteField(this, obj, fieldID, value); }
void SetCharField(jobject obj, jfieldID fieldID, jchar value)
{ functions->SetCharField(this, obj, fieldID, value); }
void SetShortField(jobject obj, jfieldID fieldID, jshort value)
{ functions->SetShortField(this, obj, fieldID, value); }
void SetIntField(jobject obj, jfieldID fieldID, jint value)
{ functions->SetIntField(this, obj, fieldID, value); }
void SetLongField(jobject obj, jfieldID fieldID, jlong value)
{ functions->SetLongField(this, obj, fieldID, value); }
void SetFloatField(jobject obj, jfieldID fieldID, jfloat value)
{ functions->SetFloatField(this, obj, fieldID, value); }
void SetDoubleField(jobject obj, jfieldID fieldID, jdouble value)
{ functions->SetDoubleField(this, obj, fieldID, value); }*/

// private int privateField_int = 200;

jfieldID privateField_int_fid = env->GetFieldID(TestJclass, "privateField_int", "I");
/* jint privateField_int_value = env->GetIntField(testobj, privateField_int_fid);
__android_log_print(4, "kanxue->jni", "privateField_int_value->%d", privateField_int_value);*/

env->SetIntField(testobj, privateField_int_fid, 300);
jint privateField_int_value = env->GetIntField(testobj, privateField_int_fid);
__android_log_print(4, "kanxue->jni", "privateField_int_value->%d", privateField_int_value);

// public int[] intarray = null;
jfieldID intarray_fid=env->GetFieldID(TestJclass,"intarray","[I");
jintArray intarray_obj= static_cast<jintArray>(env->GetObjectField(testobj, intarray_fid));

int arraylength=env->GetArrayLength(intarray_obj);

__android_log_print(4, "kanxue->jni", "arraylength->%d", arraylength);

/* int* array=env->GetIntArrayElements(intarray_obj, nullptr);
for(int i=0;i<arraylength;i++){

__android_log_print(4, "kanxue->jni", "array[%d]->%d", i,array[i]);
}*/
// void SetIntArrayRegion(jintArray array, jsize start, jsize len,
// const jint* buf)
jint jni_array[arraylength];
for(int j=0;j<arraylength;j++){

jni_array[j]=10-j;
}
const jint* ptr=jni_array;
env->SetIntArrayRegion(intarray_obj,0,arraylength,ptr);

int* array=env->GetIntArrayElements(intarray_obj, nullptr);
for(int i=0;i<arraylength;i++){

__android_log_print(4, "kanxue->jni", "array[%d]->%d", i,array[i]);
}
//env->SetIntArrayRegion()


}

JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
globalVM = vm;
__android_log_print(4, "kanxue->jni", "JNI_OnLoad(JavaVM *vm, void *reserved)->%p", vm);
__android_log_print(4, "kanxue->jni", "jni->%s", "JNI_OnLoad is called");
jint result = 0;
JNIEnv *env = nullptr;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) == JNI_OK) {
__android_log_print(4, "kanxue->jni", "jni->%s",
"vm->GetEnv((void**)&env,JNI_VERSION_1_6) success");
}
result = JNI_VERSION_1_6;
return result;


}

test.c

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
//
// Created by tom on 3/25/20.
//

#include <stdio.h>
#include <jni.h>
#include <android/log.h>
/*void* reserved0;
void* reserved1;
void* reserved2;
void* reserved3;

jint (*GetVersion)(JNIEnv *);

jclass (*DefineClass)(JNIEnv*, const char*, jobject, const jbyte*,
jsize);
jclass (*FindClass)(JNIEnv*, const char*);

jmethodID (*FromReflectedMethod)(JNIEnv*, jobject);
jfieldID (*FromReflectedField)(JNIEnv*, jobject);
*//* spec doesn't show jboolean parameter *//*
jobject (*ToReflectedMethod)(JNIEnv*, jclass, jmethodID, jboolean);

jclass (*GetSuperclass)(JNIEnv*, jclass);*/
JNIEXPORT jstring JNICALL
Java_com_kanxue_reflectiontest_MainActivity_stringFromJNIC(
JNIEnv *env,
jobject thisobj/* this */) {
/* jint GetVersion()
{ return functions->GetVersion(this); }

jclass DefineClass(const char *name, jobject loader, const jbyte* buf,
jsize bufLen)
{ return functions->DefineClass(this, name, loader, buf, bufLen); }

jclass FindClass(const char* name)
{ return functions->FindClass(this, name); }*/

const char* hello = "Hello from C";
//publicStaticField
jclass TestJclass = (*env)->FindClass(env,"com/kanxue/reflectiontest/Test");
//jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* sig)
jfieldID publicStaticField_jfieldID = (*env)->GetStaticFieldID(env,TestJclass, "publicStaticField",
"Ljava/lang/String;");
//jobject GetStaticObjectField(jclass clazz, jfieldID fieldID)
jstring publicStaticField_content = (jstring) (*env)->GetStaticObjectField(env,TestJclass,
publicStaticField_jfieldID);
const char *content_ptr = (*env)->GetStringUTFChars(env,publicStaticField_content, NULL);
__android_log_print(4, "kanxue->jni", "jni->%s", content_ptr);

return (*env)->NewStringUTF(env,hello);
}




JNI访问Java类函数

MainActivity.java

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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
package com.kanxue.reflectiontest;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.widget.TextView;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class MainActivity extends AppCompatActivity {

// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");

}

/* @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i("kanxue","onCreate is Called!");
setContentView(R.layout.activity_main);
TextView tv = findViewById(R.id.sample_text);
Test testobj = (Test) callInit();
Log.i("kanxue", testobj.flag);
}


//
// Example of a call to a native method
//TextView tv = findViewById(R.id.sample_text);
/* Test testobj = (Test) callInit();
Log.i("kanxue", testobj.flag);*/
//callStaticFunc();
//callNonStaticFunc();
//tv.setText(stringFromJNI());
//getStaticField();

/*
Test testobj=new Test("i am from java");
getNonStaticField(testobj);
int[] intarray=testobj.intarray;
for(int i=0;i<intarray.length;i++){
Log.i("kanxue->java",i+"--"+intarray[i]);
}
*/

//newObject();
//testField();
//testMethod();
// }

public void testField() {
Test a_obj = new Test();
Test b_obj = new Test("test");
Test c_obj = new Test("test", 2);
Class testClazz = null;
try {
testClazz = MainActivity.class.getClassLoader().loadClass("com.kanxue.reflectiontest.Test");
Log.i("kanxue", "ClassLoader.loadClass->" + testClazz);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Class testClazz2;
try {
testClazz2 = Class.forName("com.kanxue.reflectiontest.Test");
Log.i("kanxue", "Class.forName->" + testClazz2);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Class testClazz3 = Test.class;
Log.i("kanxue", "Test.class->" + testClazz);
try {
Field publicStaticField_field = testClazz3.getDeclaredField("publicStaticField");
Log.i("kanxue", "getDeclaredField->" + publicStaticField_field);
String content = (String) publicStaticField_field.get(null);
Log.i("kanxue", "publicStaticField->" + content);
content = (String) publicStaticField_field.get(a_obj);
Log.i("kanxue", "publicStaticField->a_obj" + content);

} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
//privateField
try {
Field privateStaticField_field = testClazz3.getDeclaredField("privateStaticField");
Log.i("kanxue", "getDeclaredField->" + privateStaticField_field);
privateStaticField_field.setAccessible(true);
privateStaticField_field.set(null, "modified");
String content = (String) privateStaticField_field.get(null);

Log.i("kanxue", "privateStaticField_field->" + content);

} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
try {
Field privateField_field = testClazz3.getField("privateField");
Log.i("kanxue", "getField->" + privateField_field);
} catch (Error | NoSuchFieldException e) {
e.printStackTrace();
}
Field[] fields = testClazz3.getDeclaredFields();
for (Field i : fields) {
Log.i("kanxue", "getDeclaredFields->" + i);
}

Field[] fields_1 = testClazz3.getFields();
for (Field i : fields_1) {
Log.i("kanxue", "getFields->" + i);
}


}

public void testMethod() {
Class testClazz = Test.class;
Method publicStaticFunc_method = null;
try {
publicStaticFunc_method = testClazz.getDeclaredMethod("publicStaticFunc");
publicStaticFunc_method.invoke(null);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
Log.i("kanxue", "getDeclaredMethod->" + publicStaticFunc_method);

Method privateStaticFunc_method = null;
try {
privateStaticFunc_method = testClazz.getDeclaredMethod("privateStaticFunc");
privateStaticFunc_method.setAccessible(true);
privateStaticFunc_method.invoke(null);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
Log.i("kanxue", "getDeclaredMethod->" + privateStaticFunc_method);

Method[] methods = testClazz.getDeclaredMethods();
for (Method i : methods) {
Log.i("kanxue", "getDeclaredMethods->" + i);
}
Method[] methods_1 = testClazz.getMethods();
for (Method i : methods_1) {
Log.i("kanxue", "getMethods->" + i);
}
Constructor[] constructors = testClazz.getDeclaredConstructors();
for (Constructor i : constructors) {
Log.i("kanxue", "getDeclaredConstructors->" + i);
}
try {
Constructor constructor = testClazz.getDeclaredConstructor(String.class, int.class);
Object testobj = constructor.newInstance("test", 666);
Field privateField_field = testClazz.getDeclaredField("privateField");
privateField_field.setAccessible(true);
String privateField_String = (String) privateField_field.get(testobj);
Log.i("kanxue", "privateField->" + privateField_String);


Log.i("kanxue", "getDeclaredConstructor->" + constructor);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}

}

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {

if (keyCode == KeyEvent.KEYCODE_BACK) {
System.exit(0);
}
return super.onKeyDown(keyCode, event);
}

/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/

protected native void onCreate(Bundle savedInstanceState);

public native String stringFromJNI();

public native String stringFromJNIC();

public native void newObject();

public native void getStaticField();

public native Object callInit();

public native void callStaticFunc();

public native void callNonStaticFunc();

public static native void getNonStaticField(Object m);

}

Test.java

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
package com.kanxue.reflectiontest;

import android.util.Log;

public class Test {

public String flag = null;
public int[] intarray = null;

public Test() {

flag = "Test()";
intarray = new int[10];
for (int i = 0; i < 10; i++) {
intarray[i] = i;

}
}

public Test(String arg) {

flag = "Test(String arg)";
intarray = new int[10];
for (int i = 0; i < 10; i++) {
intarray[i] = i;

}
}

public Test(String arg, int arg2) {

flag = "Test(String arg,int arg2)";
intarray = new int[10];
for (int i = 0; i < 10; i++) {
intarray[i] = i;

}
}

public static String publicStaticField = "i am a publicStaticField";
public static int publicStaticField_int = 100;
public String publicField = "i am a publicField";
private static String privateStaticField = "i am a privateStaticField";
private String privateField = "i am a privateField";
private int privateField_int = 200;

public static void publicStaticFunc() {
Log.i("kanxue", "i am from publicStaticFunc");
}
public static int publicStaticFunc_int(int a) {
Log.i("kanxue", "i am from publicStaticFunc_int");
return 100+a;
}
public static String publicStaticFunc_string(String arg) {
Log.i("kanxue", "i am from publicStaticFunc_int");
return "publicStaticFunc_string->"+arg;
}
public void publicFunc() {
Log.i("kanxue", "i am from publicFunc");
}


//add
private String privatetest(int a, String b) {

Log.i("kanxue","privatetest func is called:"+a+"---"+b);
return b + a;
}

private static void privateStaticFunc() {
Log.i("kanxue", "i am from privateStaticFunc");
}
private int[] privateFunc_array(int num) {
Log.i("kanxue", "i am from privateStaticFunc");

int array[]=new int[num];
for(int i=0;i<array.length;i++){
array[i]=i;
}
return array;
}
private static void privateFunc() {
Log.i("kanxue", "i am from privateStaticFunc");
}

}

native-lib.cpp

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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
#include <jni.h>
#include <string>
#include <android/log.h>
#include <pthread.h>
#include <thread>
//protected native void onCreate(Bundle savedInstanceState);
extern "C" JNIEXPORT void JNICALL
Java_com_kanxue_reflectiontest_MainActivity_onCreate(
JNIEnv *env,
jobject thiz, jobject bundle) {

/* super.onCreate(savedInstanceState);
Log.i("kanxue","onCreate is Called!");
setContentView(R.layout.activity_main);
TextView tv = findViewById(R.id.sample_text);
Test testobj = (Test) callInit();
Log.i("kanxue", testobj.flag);*/
jclass AppCompatActivity_jclass1 = env->FindClass("androidx/appcompat/app/AppCompatActivity");

jclass MainActivity_jclass1 = env->FindClass("com/kanxue/reflectiontest/MainActivity");

jclass MainActivity_jclass2 = env->GetObjectClass(thiz);

jclass AppCompatActivity_jclass2 = env->GetSuperclass(MainActivity_jclass2);

jmethodID superClassOnCreate_mid = env->GetMethodID(AppCompatActivity_jclass2, "onCreate",
"(Landroid/os/Bundle;)V");

env->CallNonvirtualVoidMethod(thiz, AppCompatActivity_jclass2, superClassOnCreate_mid, bundle);
jstring arg1 = env->NewStringUTF("kanxue");
jstring arg2 = env->NewStringUTF("native onCreate is called!");
jclass Logjclass = env->FindClass("android/util/Log");
jmethodID Log_i_mid = env->GetStaticMethodID(Logjclass, "i",
"(Ljava/lang/String;Ljava/lang/String;)I");

jint result0 = env->CallStaticIntMethod(Logjclass, Log_i_mid, arg1, arg2);
__android_log_print(4, "kanxue->jni",
"jint result0=env->CallStaticIntMethod(Logjclass,Log_i_mid,arg1,arg2);->%d",
result0);

}

extern "C" JNIEXPORT void JNICALL
Java_com_kanxue_reflectiontest_MainActivity_callNonStaticFunc(
JNIEnv *env,
jobject obj/* this */) {

jclass TestJclass = env->FindClass("com/kanxue/reflectiontest/Test");

jmethodID con_mid = env->GetMethodID(TestJclass, "<init>", "(Ljava/lang/String;I)V");

jstring arg0 = env->NewStringUTF("i am from callInit");

jobject testobj = env->NewObject(TestJclass, con_mid, arg0, 100);
jmethodID publicFunc_mid = env->GetMethodID(TestJclass, "publicFunc", "()V");
env->CallVoidMethod(testobj, publicFunc_mid);
jmethodID privatetest = env->GetMethodID(TestJclass, "privatetest",
"(ILjava/lang/String;)Ljava/lang/String;");

jstring arg1 = env->NewStringUTF("i am from jni");

jvalue args[2];
args[0].i = 200;
args[1].l = arg1;
jstring result_obj = static_cast<jstring>(env->CallObjectMethodA(testobj, privatetest, args));
const char *result_ptr = env->GetStringUTFChars(result_obj, nullptr);
__android_log_print(4, "kanxue->jni", "privatetest->%s", result_ptr);

jmethodID privateFunc_array_mid = env->GetMethodID(TestJclass, "privateFunc_array", "(I)[I");

jintArray array_obj = static_cast<jintArray>(env->CallObjectMethod(testobj,
privateFunc_array_mid, 100));

jint *array_ptr = env->GetIntArrayElements(array_obj, nullptr);
for (int i = 0; i < env->GetArrayLength(array_obj); i++) {
__android_log_print(4, "kanxue->jni", "array[%d]->%d", i, array_ptr[i]);
}


}
extern "C" JNIEXPORT void JNICALL
Java_com_kanxue_reflectiontest_MainActivity_callStaticFunc(
JNIEnv *env,
jobject /* this */) {
//public static void publicStaticFunc()

jclass TestJclass = env->FindClass("com/kanxue/reflectiontest/Test");

jmethodID publicStaticFunc_mid = env->GetStaticMethodID(TestJclass, "publicStaticFunc", "()V");

env->CallStaticVoidMethod(TestJclass, publicStaticFunc_mid);


//private static void privateStaticFunc()

jmethodID privateStaticFunc_mid = env->GetStaticMethodID(TestJclass, "privateStaticFunc",
"()V");

env->CallStaticVoidMethod(TestJclass, privateStaticFunc_mid);

/* public static int publicStaticFunc_int(int a) {
Log.i("kanxue", "i am from publicStaticFunc_int");
return 100+a;
}*/
jmethodID publicStaticFunc_int_mid = env->GetStaticMethodID(TestJclass, "publicStaticFunc_int",
"(I)I");

jint result_int = env->CallStaticIntMethod(TestJclass, publicStaticFunc_int_mid, 200);
__android_log_print(4, "kanxue->jni", "publicStaticFunc_int->%d", result_int);
/*public static String publicStaticFunc_string(String arg) {
Log.i("kanxue", "i am from publicStaticFunc_int");
return "publicStaticFunc_string->"+arg;
}*/
jmethodID publicStaticFunc_string_mid = env->GetStaticMethodID(TestJclass,
"publicStaticFunc_string",
"(Ljava/lang/String;)Ljava/lang/String;");

jstring arg_string = env->NewStringUTF("i am from jni");
jstring result_string = static_cast<jstring>(env->CallStaticObjectMethod(TestJclass,
publicStaticFunc_string_mid,
arg_string));

const char *content = env->GetStringUTFChars(result_string, nullptr);
__android_log_print(4, "kanxue->jni", "publicStaticFunc_string->%s", content);


}
extern "C" JNIEXPORT jobject JNICALL
Java_com_kanxue_reflectiontest_MainActivity_callInit(
JNIEnv *env,
jobject /* this */) {

jclass TestJclass = env->FindClass("com/kanxue/reflectiontest/Test");

jmethodID con_mid = env->GetMethodID(TestJclass, "<init>", "(Ljava/lang/String;I)V");

jstring arg0 = env->NewStringUTF("i am from callInit");

jobject obj = env->NewObject(TestJclass, con_mid, arg0, 100);
jmethodID privatetest_mid = env->GetMethodID(TestJclass, "privatetest",
"(ILjava/lang/String;)Ljava/lang/String;");
jstring arg2 = env->NewStringUTF("i am from jni-CallObjectMethodA");

return obj;

}
extern "C" JNIEXPORT jstring JNICALL
Java_com_kanxue_reflectiontest_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
JavaVM *thisvm = nullptr;
env->GetJavaVM(&thisvm);

__android_log_print(4, "kanxue->jni", "env->GetJavaVM(&thisvm)->%p", thisvm);
__android_log_print(4, "kanxue->jni",
"Java_com_kanxue_reflectiontest_MainActivity_stringFromJNI->%p", env);

std::string hello = "Hello from C++";
jclass TestJclass = env->FindClass("com/kanxue/reflectiontest/Test");
jfieldID publicStaticField_jfieldID = env->GetStaticFieldID(TestJclass, "publicStaticField",
"Ljava/lang/String;");
jstring publicStaticField_content = (jstring) env->GetStaticObjectField(TestJclass,
publicStaticField_jfieldID);
const char *content_ptr = env->GetStringUTFChars(publicStaticField_content, nullptr);
__android_log_print(4, "kanxue->jni", "jni->%s", content_ptr);

return env->NewStringUTF(hello.c_str());
}

void newObject(JNIEnv *env, jobject obj) {


}

extern "C" JNIEXPORT void JNICALL
Java_com_kanxue_reflectiontest_MainActivity_newObject(
JNIEnv *env,
jobject /* this */) {
//NewObject
jclass TestJclass = env->FindClass("com/kanxue/reflectiontest/Test");
jmethodID con_mid = env->GetMethodID(TestJclass, "<init>", "(Ljava/lang/String;)V");
jstring arg = env->NewStringUTF("I am From Jni");
jobject testobj = env->NewObject(TestJclass, con_mid, arg);
if (testobj != nullptr) {
__android_log_print(4, "kanxue->jni", "jni->%s", "NewObject success!");
}
//AllocObject
jobject testobj2 = env->AllocObject(TestJclass);
jstring arg1 = env->NewStringUTF("I am From Jni->AllocObject");
env->CallNonvirtualVoidMethod(testobj2, TestJclass, con_mid, arg1);
if (testobj2 != nullptr) {

__android_log_print(4, "kanxue->jni", "jni->%s", "AllocObject success!");
}
jmethodID privatetest_mid = env->GetMethodID(TestJclass, "privatetest",
"(ILjava/lang/String;)Ljava/lang/String;");
jstring arg2 = env->NewStringUTF("i am from jni");
jobject result = env->CallObjectMethod(testobj2, privatetest_mid, 100, arg2);


}


extern "C" JNIEXPORT void JNICALL
Java_com_kanxue_reflectiontest_MainActivity_getStaticField(
JNIEnv *env,
jobject /* this */) {
//private static
jclass TestJclass = env->FindClass("com/kanxue/reflectiontest/Test");
// public static String publicStaticField = "i am a publicStaticField";
// jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* sig)
jfieldID publicStaticField_fid = env->GetStaticFieldID(TestJclass, "publicStaticField",
"Ljava/lang/String;");
/*jstring publicStaticField_obj = static_cast<jstring>(env->GetStaticObjectField(TestJclass,
publicStaticField_fid));
const char *publicStaticField_content = env->GetStringUTFChars(publicStaticField_obj, nullptr);
__android_log_print(4, "kanxue->jni", "publicStaticField_obj->%s", publicStaticField_content);*/

jstring setjstring = env->NewStringUTF("modified by jni");
// void SetStaticObjectField(jclass clazz, jfieldID fieldID, jobject value)
env->SetStaticObjectField(TestJclass, publicStaticField_fid, setjstring);
jstring publicStaticField_obj = static_cast<jstring>(env->GetStaticObjectField(TestJclass,
publicStaticField_fid));
const char *publicStaticField_content = env->GetStringUTFChars(publicStaticField_obj, nullptr);
__android_log_print(4, "kanxue->jni", "publicStaticField_obj->%s", publicStaticField_content);

//private static String privateStaticField = "i am a privateStaticField";
jfieldID privateStaticField_fid = env->GetStaticFieldID(TestJclass, "privateStaticField",
"Ljava/lang/String;");
jstring privateStaticField_obj = static_cast<jstring>(env->GetStaticObjectField(TestJclass,
privateStaticField_fid));
const char *privateStaticField_content = env->GetStringUTFChars(privateStaticField_obj,
nullptr);
__android_log_print(4, "kanxue->jni", "privateStaticField_obj->%s", privateStaticField_content);

// public static int publicStaticField_int = 100;

jfieldID publicStaticField_int_fid = env->GetStaticFieldID(TestJclass, "publicStaticField_int",
"I");


env->SetStaticIntField(TestJclass, publicStaticField_int_fid, 200);

jint publicStaticField_int_value = env->GetStaticIntField(TestJclass,
publicStaticField_int_fid);

__android_log_print(4, "kanxue->jni", "publicStaticField_int_value->%d",
publicStaticField_int_value);


}
extern "C" JNIEXPORT void JNICALL
Java_com_kanxue_reflectiontest_MainActivity_getNonStaticField(
JNIEnv *env,
jclass obj, jobject testobj) {

// private String privateField = "i am a privateField";

jclass TestJclass = env->FindClass("com/kanxue/reflectiontest/Test");
jfieldID privateField_fid = env->GetFieldID(TestJclass, "privateField", "Ljava/lang/String;");
// jobject GetObjectField(jobject obj, jfieldID fieldID)
jstring privateField_obj = static_cast<jstring>(env->GetObjectField(testobj, privateField_fid));

jstring newString = env->NewStringUTF("Modified by jni");
env->SetObjectField(testobj, privateField_fid, newString);


privateField_obj = static_cast<jstring>(env->GetObjectField(testobj, privateField_fid));
const char *privateField_obj_content = env->GetStringUTFChars(privateField_obj,
nullptr);
__android_log_print(4, "kanxue->jni", "privateField_obj->%s", privateField_obj_content);
jfieldID privateField_int_fid = env->GetFieldID(TestJclass, "privateField_int", "I");
env->SetIntField(testobj, privateField_int_fid, 300);
jint privateField_int_value = env->GetIntField(testobj, privateField_int_fid);
__android_log_print(4, "kanxue->jni", "privateField_int_value->%d", privateField_int_value);

// public int[] intarray = null;
jfieldID intarray_fid = env->GetFieldID(TestJclass, "intarray", "[I");
jintArray intarray_obj = static_cast<jintArray>(env->GetObjectField(testobj, intarray_fid));

int arraylength = env->GetArrayLength(intarray_obj);

__android_log_print(4, "kanxue->jni", "arraylength->%d", arraylength);
jint jni_array[arraylength];
for (int j = 0; j < arraylength; j++) {

jni_array[j] = 10 - j;
}
const jint *ptr = jni_array;
env->SetIntArrayRegion(intarray_obj, 0, arraylength, ptr);

int *array = env->GetIntArrayElements(intarray_obj, nullptr);
for (int i = 0; i < arraylength; i++) {

__android_log_print(4, "kanxue->jni", "array[%d]->%d", i, array[i]);
}


}


JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
int result = 0;
__android_log_print(4, "kanxue->jni", "JNI_OnLoad(JavaVM *vm, void *reserved)->%p", vm);
__android_log_print(4, "kanxue->jni", "jni->%s", "JNI_OnLoad is called");
JNIEnv *env = nullptr;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) == JNI_OK) {
__android_log_print(4, "kanxue->jni", "jni->%s",
"vm->GetEnv((void**)&env,JNI_VERSION_1_6) success");
}
result = JNI_VERSION_1_6;
return result;


}

test.c

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
//
// Created by tom on 3/25/20.
//

#include <stdio.h>
#include <jni.h>
#include <android/log.h>
/*void* reserved0;
void* reserved1;
void* reserved2;
void* reserved3;

jint (*GetVersion)(JNIEnv *);

jclass (*DefineClass)(JNIEnv*, const char*, jobject, const jbyte*,
jsize);
jclass (*FindClass)(JNIEnv*, const char*);

jmethodID (*FromReflectedMethod)(JNIEnv*, jobject);
jfieldID (*FromReflectedField)(JNIEnv*, jobject);
*//* spec doesn't show jboolean parameter *//*
jobject (*ToReflectedMethod)(JNIEnv*, jclass, jmethodID, jboolean);

jclass (*GetSuperclass)(JNIEnv*, jclass);*/
JNIEXPORT jstring JNICALL
Java_com_kanxue_reflectiontest_MainActivity_stringFromJNIC(
JNIEnv *env,
jobject thisobj/* this */) {
/* jint GetVersion()
{ return functions->GetVersion(this); }

jclass DefineClass(const char *name, jobject loader, const jbyte* buf,
jsize bufLen)
{ return functions->DefineClass(this, name, loader, buf, bufLen); }

jclass FindClass(const char* name)
{ return functions->FindClass(this, name); }*/

const char* hello = "Hello from C";
//publicStaticField
jclass TestJclass = (*env)->FindClass(env,"com/kanxue/reflectiontest/Test");
//jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* sig)
jfieldID publicStaticField_jfieldID = (*env)->GetStaticFieldID(env,TestJclass, "publicStaticField",
"Ljava/lang/String;");
//jobject GetStaticObjectField(jclass clazz, jfieldID fieldID)
jstring publicStaticField_content = (jstring) (*env)->GetStaticObjectField(env,TestJclass,
publicStaticField_jfieldID);
const char *content_ptr = (*env)->GetStringUTFChars(env,publicStaticField_content, NULL);
__android_log_print(4, "kanxue->jni", "jni->%s", content_ptr);

return (*env)->NewStringUTF(env,hello);
}




全局引用、局部引用、弱全局引用

NDK内存管理:全局引用、局部引用、弱全局引用

Java当中的内存管理:透明的, 当新建类的实例时, 值需要在创建完这个类的实例之后, 拿着这个引用访问它的所有数据成员了(属性、方法)就可以了,事实上对于Java来说, 有一个垃圾回收器线程即GC线程负责将一些不再使用的对象回收

C/C++当中的内存管理,需要编码人员自己进行内存管理, 如在C++中new一个对象, 使用完了还要做一次delete操作, malloc一次同样也要调用free来释放相应的内存, 否则就会有内存泄漏

三种引用简介和区别

  • 局部引用:通过NewLocalRef和各种JNI接口创建(FindClass、NewOBject、GetOBjectClass和NewCharArray等)【哪怕用了全局变量指针也是局部引用】,会阻止GC回收所引用的对象, 局部引用只能在当前函数中使用, 函数返回后局部引用所引用的对象会被JVM自动释放, 或者调用DeleteLocalRef手动释放, 因此局部引用不能跨函数调用, 不能跨线程使用
  • 全局引用:调用NewGlobalRef基于局部引用创建,会阻GC回收所引用的对象, 全局引用可以跨函数、跨线程使用。Art不会自动释放, 必须调用DeleteGlobalRef手动释放DeleteGlobalRef(g_cls_string),否则会出现内存泄露

    1
    2
    3
    4
    5
    // 函数内创建出来
    jclass tmpjclass = env->FindClass("com/kanxue/reflectiontest/Test");
    testjclass = static_cast<jclass>(env->NewGlobalRef(tmpjclass));
    // 在其他函数或者其他线程内部手动释放掉
    env->DeleteGlobalRef(testjclass);
  • 弱全局引用: 调用NewWeakGLoablRef基于内部引用或全局引用创建,不会阻止GC回收所引用的对象, 可以跨方法、跨线程使用,但是与全局引用很重要不同的一点是, 弱引用不会组织GC回收它引用的对象, 但是引用也不会自动释放, 在ART认为应该回收它的时候(比如内存紧张的时候)进行回收而被释放, 或调用DeleteWeakGlobalRef手动释放

    1
    2
    3
    jclass tmpjclass = env->FindClass("com/kanxue/reflectiontest/Test");
    testjclass = static_cast<jclass>(env->NewWeakGlobalRef(tmpjclass));
    env->DeleteWeakGlobalRef(testjclass);

局部引用表

局部引用表并不是无限的, 一般在JNI函数调用结束后, art会释放这个引用
但是如果函数里面有大量的JNI循环调用Java函数, 那么因为局部引用太多而出现异常情况

1
2
3
4
5
6
7
for (int i = 0; i < 2048; i++) {
jstring content = env->NewStringUTF("test localreference");
__android_log_print(4, "kanxue->jni", "localreference number->%d",
i);
//如果想正常使用,每次使用完就必须delete掉
//env->DeleteLocalRef(content);
}

6.0的系统局部引用表最大512,会出现溢出的错误

局部引用的管理

  • 函数返回时自动释放
  • 手动调用DeleteLocalRef释放
  • 使用JNI提供的一系列函数来管理局部引用的生命周期

JNI提供了一些函数来管理局部引用的生命周期

  • EnsureLocalCapacity:如果需要创建更多的引用,可以通过调用EnsureLocalCapacity确保当前线程创建指定数量的局部引用,如果创建成功返回0,失败则抛出OutOfMemoryError异常
  • NewLocalRef
  • DeleteLocalRef
  • PushLocalFrame
  • PopLocalFrame
    Push和Pop是局部引用创建一个引用堆栈和销毁栈中所有引用,因此不需要在关注获取一个引用后再调用DeleteLocalRef来释放引用,在调用PopLocalFrame销毁当前frame中的所用引用前,如果第二个参数result不为空, 会由result生成一个新的局部引用,再把这个新生成的局部引用存储在上一个frame中

MainActivity.java

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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
package com.kanxue.reflectiontest;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.widget.TextView;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class MainActivity extends AppCompatActivity {

// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
//System.load("/data/data/xxxx/test.so");
Test testobj = new Test();
getNonStaticField(testobj);
//System.load("");

}

/* @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i("kanxue","onCreate is Called!");
setContentView(R.layout.activity_main);
TextView tv = findViewById(R.id.sample_text);
Test testobj = (Test) callInit();
Log.i("kanxue", testobj.flag);
}


//
// Example of a call to a native method
//TextView tv = findViewById(R.id.sample_text);
/* Test testobj = (Test) callInit();
Log.i("kanxue", testobj.flag);*/
//callStaticFunc();
//callNonStaticFunc();
//tv.setText(stringFromJNI());
//getStaticField();

/*
Test testobj=new Test("i am from java");
getNonStaticField(testobj);
int[] intarray=testobj.intarray;
for(int i=0;i<intarray.length;i++){
Log.i("kanxue->java",i+"--"+intarray[i]);
}
*/

//newObject();
//testField();
//testMethod();
// }

public void testField() {
Test a_obj = new Test();
Test b_obj = new Test("test");
Test c_obj = new Test("test", 2);
Class testClazz = null;
try {
testClazz = MainActivity.class.getClassLoader().loadClass("com.kanxue.reflectiontest.Test");
Log.i("kanxue", "ClassLoader.loadClass->" + testClazz);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Class testClazz2;
try {
testClazz2 = Class.forName("com.kanxue.reflectiontest.Test");
Log.i("kanxue", "Class.forName->" + testClazz2);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Class testClazz3 = Test.class;
Log.i("kanxue", "Test.class->" + testClazz);
try {
Field publicStaticField_field = testClazz3.getDeclaredField("publicStaticField");
Log.i("kanxue", "getDeclaredField->" + publicStaticField_field);
String content = (String) publicStaticField_field.get(null);
Log.i("kanxue", "publicStaticField->" + content);
content = (String) publicStaticField_field.get(a_obj);
Log.i("kanxue", "publicStaticField->a_obj" + content);

} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
//privateField
try {
Field privateStaticField_field = testClazz3.getDeclaredField("privateStaticField");
Log.i("kanxue", "getDeclaredField->" + privateStaticField_field);
privateStaticField_field.setAccessible(true);
privateStaticField_field.set(null, "modified");
String content = (String) privateStaticField_field.get(null);

Log.i("kanxue", "privateStaticField_field->" + content);

} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
try {
Field privateField_field = testClazz3.getField("privateField");
Log.i("kanxue", "getField->" + privateField_field);
} catch (Error | NoSuchFieldException e) {
e.printStackTrace();
}
Field[] fields = testClazz3.getDeclaredFields();
for (Field i : fields) {
Log.i("kanxue", "getDeclaredFields->" + i);
}

Field[] fields_1 = testClazz3.getFields();
for (Field i : fields_1) {
Log.i("kanxue", "getFields->" + i);
}


}

public void testMethod() {
Class testClazz = Test.class;
Method publicStaticFunc_method = null;
try {
publicStaticFunc_method = testClazz.getDeclaredMethod("publicStaticFunc");
publicStaticFunc_method.invoke(null);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
Log.i("kanxue", "getDeclaredMethod->" + publicStaticFunc_method);

Method privateStaticFunc_method = null;
try {
privateStaticFunc_method = testClazz.getDeclaredMethod("privateStaticFunc");
privateStaticFunc_method.setAccessible(true);
privateStaticFunc_method.invoke(null);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
Log.i("kanxue", "getDeclaredMethod->" + privateStaticFunc_method);

Method[] methods = testClazz.getDeclaredMethods();
for (Method i : methods) {
Log.i("kanxue", "getDeclaredMethods->" + i);
}
Method[] methods_1 = testClazz.getMethods();
for (Method i : methods_1) {
Log.i("kanxue", "getMethods->" + i);
}
Constructor[] constructors = testClazz.getDeclaredConstructors();
for (Constructor i : constructors) {
Log.i("kanxue", "getDeclaredConstructors->" + i);
}
try {
Constructor constructor = testClazz.getDeclaredConstructor(String.class, int.class);
Object testobj = constructor.newInstance("test", 666);
Field privateField_field = testClazz.getDeclaredField("privateField");
privateField_field.setAccessible(true);
String privateField_String = (String) privateField_field.get(testobj);
Log.i("kanxue", "privateField->" + privateField_String);


Log.i("kanxue", "getDeclaredConstructor->" + constructor);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}

}

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {

if (keyCode == KeyEvent.KEYCODE_BACK) {
System.exit(0);
}
return super.onKeyDown(keyCode, event);
}

/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/

protected native void onCreate(Bundle savedInstanceState);

public native String stringFromJNI();

public native String stringFromJNIC();

public native void newObject();

public native void getStaticField();

public native Object callInit();

public native void callStaticFunc();

public native void callNonStaticFunc();

public static native void getNonStaticField(Object m);

}

Test.java

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
package com.kanxue.reflectiontest;

import android.util.Log;

public class Test {

public String flag = null;
public int[] intarray = null;

public Test() {

flag = "Test()";
intarray = new int[10];
for (int i = 0; i < 10; i++) {
intarray[i] = i;

}
}

public Test(String arg) {

flag = "Test(String arg)";
intarray = new int[10];
for (int i = 0; i < 10; i++) {
intarray[i] = i;

}
}

public Test(String arg, int arg2) {

flag = "Test(String arg,int arg2)";
intarray = new int[10];
for (int i = 0; i < 10; i++) {
intarray[i] = i;

}
}

public static String publicStaticField = "i am a publicStaticField";
public static int publicStaticField_int = 100;
public String publicField = "i am a publicField";
private static String privateStaticField = "i am a privateStaticField";
private String privateField = "i am a privateField";
private int privateField_int = 200;

public static void publicStaticFunc() {
Log.i("kanxue", "i am from publicStaticFunc");
}
public static int publicStaticFunc_int(int a) {
Log.i("kanxue", "i am from publicStaticFunc_int");
return 100+a;
}
public static String publicStaticFunc_string(String arg) {
Log.i("kanxue", "i am from publicStaticFunc_int");
return "publicStaticFunc_string->"+arg;
}
public void publicFunc() {
Log.i("kanxue", "i am from publicFunc");
}


//add
private String privatetest(int a, String b) {

Log.i("kanxue","privatetest func is called:"+a+"---"+b);
return b + a;
}

private static void privateStaticFunc() {
Log.i("kanxue", "i am from privateStaticFunc");
}
private int[] privateFunc_array(int num) {
Log.i("kanxue", "i am from privateStaticFunc");

int array[]=new int[num];
for(int i=0;i<array.length;i++){
array[i]=i;
}
return array;
}
private static void privateFunc() {
Log.i("kanxue", "i am from privateStaticFunc");
}

}

native-lib.cpp

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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
#include <jni.h>
#include <string>
#include <android/log.h>
#include <pthread.h>
#include <thread>

JavaVM *globalVM = nullptr;
jclass testjclass = nullptr;

//protected native void onCreate(Bundle savedInstanceState);
__attribute__ ((visibility ("hidden"))) void onCreate(
JNIEnv *env,
jobject thiz, jobject bundle) {

/* super.onCreate(savedInstanceState);
Log.i("kanxue","onCreate is Called!");
setContentView(R.layout.activity_main);
TextView tv = findViewById(R.id.sample_text);
Test testobj = (Test) callInit();
Log.i("kanxue", testobj.flag);*/
jclass AppCompatActivity_jclass1 = env->FindClass("androidx/appcompat/app/AppCompatActivity");

jclass MainActivity_jclass1 = env->FindClass("com/kanxue/reflectiontest/MainActivity");

jclass MainActivity_jclass2 = env->GetObjectClass(thiz);

jclass AppCompatActivity_jclass2 = env->GetSuperclass(MainActivity_jclass2);

jmethodID superClassOnCreate_mid = env->GetMethodID(AppCompatActivity_jclass2, "onCreate",
"(Landroid/os/Bundle;)V");

env->CallNonvirtualVoidMethod(thiz, AppCompatActivity_jclass2, superClassOnCreate_mid, bundle);
jstring arg1 = env->NewStringUTF("kanxue");
jstring arg2 = env->NewStringUTF("native onCreate is called!");
jclass Logjclass = env->FindClass("android/util/Log");
jmethodID Log_i_mid = env->GetStaticMethodID(Logjclass, "i",
"(Ljava/lang/String;Ljava/lang/String;)I");

jint result0 = env->CallStaticIntMethod(Logjclass, Log_i_mid, arg1, arg2);
__android_log_print(4, "kanxue->jni",
"jint result0=env->CallStaticIntMethod(Logjclass,Log_i_mid,arg1,arg2);->%d",
result0);
jmethodID setContentView_mid = env->GetMethodID(MainActivity_jclass2, "setContentView", "(I)V");

jclass R_layoutjclass = env->FindClass("com/kanxue/reflectiontest/R$layout");
jfieldID activity_main_fieldid = env->GetStaticFieldID(R_layoutjclass, "activity_main", "I");
jint activity_main_value = env->GetStaticIntField(R_layoutjclass, activity_main_fieldid);

env->CallVoidMethod(thiz, setContentView_mid, activity_main_value);
jmethodID findViewById_mid = env->GetMethodID(MainActivity_jclass2, "findViewById",
"(I)Landroid/view/View;");

jclass R_idjclass = env->FindClass("com/kanxue/reflectiontest/R$id");
jfieldID sample_text_fieldid = env->GetStaticFieldID(R_idjclass, "sample_text", "I");
jint sample_text_value = env->GetStaticIntField(R_idjclass, sample_text_fieldid);

jobject tvobj = env->CallObjectMethod(thiz, findViewById_mid, sample_text_value);
jmethodID callInit_mid = env->GetMethodID(MainActivity_jclass2, "callInit",
"()Ljava/lang/Object;");
jobject testobj = env->CallObjectMethod(thiz, callInit_mid);
jfieldID flagjfield = env->GetFieldID(testjclass, "flag", "Ljava/lang/String;");
jstring flagvalue = static_cast<jstring>(env->GetObjectField(testobj, flagjfield));

jint result1 = env->CallStaticIntMethod(Logjclass, Log_i_mid, arg1, flagvalue);
__android_log_print(4, "kanxue->jni",
" jint result1=env->CallStaticIntMethod(Logjclass,Log_i_mid,arg1,flagvalue);->%d",
result1);

env->DeleteWeakGlobalRef(testjclass);

}

extern "C" JNIEXPORT void JNICALL
Java_com_kanxue_reflectiontest_MainActivity_callNonStaticFunc(
JNIEnv *env,
jobject obj/* this */) {

jclass TestJclass = env->FindClass("com/kanxue/reflectiontest/Test");

jmethodID con_mid = env->GetMethodID(TestJclass, "<init>", "(Ljava/lang/String;I)V");

jstring arg0 = env->NewStringUTF("i am from callInit");

jobject testobj = env->NewObject(TestJclass, con_mid, arg0, 100);
jmethodID publicFunc_mid = env->GetMethodID(TestJclass, "publicFunc", "()V");
env->CallVoidMethod(testobj, publicFunc_mid);
jmethodID privatetest = env->GetMethodID(TestJclass, "privatetest",
"(ILjava/lang/String;)Ljava/lang/String;");

jstring arg1 = env->NewStringUTF("i am from jni");

jvalue args[2];
args[0].i = 200;
args[1].l = arg1;
jstring result_obj = static_cast<jstring>(env->CallObjectMethodA(testobj, privatetest, args));
const char *result_ptr = env->GetStringUTFChars(result_obj, nullptr);
__android_log_print(4, "kanxue->jni", "privatetest->%s", result_ptr);

jmethodID privateFunc_array_mid = env->GetMethodID(TestJclass, "privateFunc_array", "(I)[I");

jintArray array_obj = static_cast<jintArray>(env->CallObjectMethod(testobj,
privateFunc_array_mid, 100));

jint *array_ptr = env->GetIntArrayElements(array_obj, nullptr);
for (int i = 0; i < env->GetArrayLength(array_obj); i++) {
__android_log_print(4, "kanxue->jni", "array[%d]->%d", i, array_ptr[i]);
}


}
extern "C" JNIEXPORT void JNICALL
Java_com_kanxue_reflectiontest_MainActivity_callStaticFunc(
JNIEnv *env,
jobject /* this */) {
//public static void publicStaticFunc()

jclass TestJclass = env->FindClass("com/kanxue/reflectiontest/Test");

jmethodID publicStaticFunc_mid = env->GetStaticMethodID(TestJclass, "publicStaticFunc", "()V");

env->CallStaticVoidMethod(TestJclass, publicStaticFunc_mid);


//private static void privateStaticFunc()

jmethodID privateStaticFunc_mid = env->GetStaticMethodID(TestJclass, "privateStaticFunc",
"()V");

env->CallStaticVoidMethod(TestJclass, privateStaticFunc_mid);

/* public static int publicStaticFunc_int(int a) {
Log.i("kanxue", "i am from publicStaticFunc_int");
return 100+a;
}*/
jmethodID publicStaticFunc_int_mid = env->GetStaticMethodID(TestJclass, "publicStaticFunc_int",
"(I)I");

jint result_int = env->CallStaticIntMethod(TestJclass, publicStaticFunc_int_mid, 200);
__android_log_print(4, "kanxue->jni", "publicStaticFunc_int->%d", result_int);
/*public static String publicStaticFunc_string(String arg) {
Log.i("kanxue", "i am from publicStaticFunc_int");
return "publicStaticFunc_string->"+arg;
}*/
jmethodID publicStaticFunc_string_mid = env->GetStaticMethodID(TestJclass,
"publicStaticFunc_string",
"(Ljava/lang/String;)Ljava/lang/String;");

jstring arg_string = env->NewStringUTF("i am from jni");
jstring result_string = static_cast<jstring>(env->CallStaticObjectMethod(TestJclass,
publicStaticFunc_string_mid,
arg_string));

const char *content = env->GetStringUTFChars(result_string, nullptr);
__android_log_print(4, "kanxue->jni", "publicStaticFunc_string->%s", content);


}
extern "C" JNIEXPORT jobject JNICALL
Java_com_kanxue_reflectiontest_MainActivity_callInit(
JNIEnv *env,
jobject /* this */) {

jclass TestJclass = env->FindClass("com/kanxue/reflectiontest/Test");

jmethodID con_mid = env->GetMethodID(TestJclass, "<init>", "(Ljava/lang/String;I)V");

jstring arg0 = env->NewStringUTF("i am from callInit");

jobject obj = env->NewObject(TestJclass, con_mid, arg0, 100);
jmethodID privatetest_mid = env->GetMethodID(TestJclass, "privatetest",
"(ILjava/lang/String;)Ljava/lang/String;");
jstring arg2 = env->NewStringUTF("i am from jni-CallObjectMethodA");

return obj;

}
extern "C" JNIEXPORT jstring JNICALL
Java_com_kanxue_reflectiontest_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
JavaVM *thisvm = nullptr;
env->GetJavaVM(&thisvm);

__android_log_print(4, "kanxue->jni", "env->GetJavaVM(&thisvm)->%p", thisvm);
__android_log_print(4, "kanxue->jni",
"Java_com_kanxue_reflectiontest_MainActivity_stringFromJNI->%p", env);

std::string hello = "Hello from C++";
jclass TestJclass = env->FindClass("com/kanxue/reflectiontest/Test");
jfieldID publicStaticField_jfieldID = env->GetStaticFieldID(TestJclass, "publicStaticField",
"Ljava/lang/String;");
jstring publicStaticField_content = (jstring) env->GetStaticObjectField(TestJclass,
publicStaticField_jfieldID);
const char *content_ptr = env->GetStringUTFChars(publicStaticField_content, nullptr);
__android_log_print(4, "kanxue->jni", "jni->%s", content_ptr);

return env->NewStringUTF(hello.c_str());
}

void newObject(JNIEnv *env, jobject obj) {


}

extern "C" JNIEXPORT void JNICALL
Java_com_kanxue_reflectiontest_MainActivity_newObject(
JNIEnv *env,
jobject /* this */) {
//NewObject
jclass TestJclass = env->FindClass("com/kanxue/reflectiontest/Test");
jmethodID con_mid = env->GetMethodID(TestJclass, "<init>", "(Ljava/lang/String;)V");
jstring arg = env->NewStringUTF("I am From Jni");
jobject testobj = env->NewObject(TestJclass, con_mid, arg);
if (testobj != nullptr) {
__android_log_print(4, "kanxue->jni", "jni->%s", "NewObject success!");
}
//AllocObject
jobject testobj2 = env->AllocObject(TestJclass);
jstring arg1 = env->NewStringUTF("I am From Jni->AllocObject");
env->CallNonvirtualVoidMethod(testobj2, TestJclass, con_mid, arg1);
if (testobj2 != nullptr) {

__android_log_print(4, "kanxue->jni", "jni->%s", "AllocObject success!");
}
jmethodID privatetest_mid = env->GetMethodID(TestJclass, "privatetest",
"(ILjava/lang/String;)Ljava/lang/String;");
jstring arg2 = env->NewStringUTF("i am from jni");
jobject result = env->CallObjectMethod(testobj2, privatetest_mid, 100, arg2);


}


extern "C" JNIEXPORT void JNICALL
Java_com_kanxue_reflectiontest_MainActivity_getStaticField(
JNIEnv *env,
jobject /* this */) {
//private static
jclass TestJclass = env->FindClass("com/kanxue/reflectiontest/Test");
// public static String publicStaticField = "i am a publicStaticField";
// jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* sig)
jfieldID publicStaticField_fid = env->GetStaticFieldID(TestJclass, "publicStaticField",
"Ljava/lang/String;");
/*jstring publicStaticField_obj = static_cast<jstring>(env->GetStaticObjectField(TestJclass,
publicStaticField_fid));
const char *publicStaticField_content = env->GetStringUTFChars(publicStaticField_obj, nullptr);
__android_log_print(4, "kanxue->jni", "publicStaticField_obj->%s", publicStaticField_content);*/

jstring setjstring = env->NewStringUTF("modified by jni");
// void SetStaticObjectField(jclass clazz, jfieldID fieldID, jobject value)
env->SetStaticObjectField(TestJclass, publicStaticField_fid, setjstring);
jstring publicStaticField_obj = static_cast<jstring>(env->GetStaticObjectField(TestJclass,
publicStaticField_fid));
const char *publicStaticField_content = env->GetStringUTFChars(publicStaticField_obj, nullptr);
__android_log_print(4, "kanxue->jni", "publicStaticField_obj->%s", publicStaticField_content);

//private static String privateStaticField = "i am a privateStaticField";
jfieldID privateStaticField_fid = env->GetStaticFieldID(TestJclass, "privateStaticField",
"Ljava/lang/String;");
jstring privateStaticField_obj = static_cast<jstring>(env->GetStaticObjectField(TestJclass,
privateStaticField_fid));
const char *privateStaticField_content = env->GetStringUTFChars(privateStaticField_obj,
nullptr);
__android_log_print(4, "kanxue->jni", "privateStaticField_obj->%s", privateStaticField_content);

// public static int publicStaticField_int = 100;

jfieldID publicStaticField_int_fid = env->GetStaticFieldID(TestJclass, "publicStaticField_int",
"I");


env->SetStaticIntField(TestJclass, publicStaticField_int_fid, 200);

jint publicStaticField_int_value = env->GetStaticIntField(TestJclass,
publicStaticField_int_fid);

__android_log_print(4, "kanxue->jni", "publicStaticField_int_value->%d",
publicStaticField_int_value);


}

__attribute__ ((visibility ("hidden"))) void cccc(
JNIEnv *env,
jclass obj, jobject testobj) {

// private String privateField = "i am a privateField";

jclass TestJclass = env->FindClass("com/kanxue/reflectiontest/Test");
jfieldID privateField_fid = env->GetFieldID(TestJclass, "privateField", "Ljava/lang/String;");
// jobject GetObjectField(jobject obj, jfieldID fieldID)
jstring privateField_obj = static_cast<jstring>(env->GetObjectField(testobj, privateField_fid));

jstring newString = env->NewStringUTF("Modified by jni");
env->SetObjectField(testobj, privateField_fid, newString);


privateField_obj = static_cast<jstring>(env->GetObjectField(testobj, privateField_fid));
const char *privateField_obj_content = env->GetStringUTFChars(privateField_obj,
nullptr);
__android_log_print(4, "kanxue->jni", "privateField_obj->%s", privateField_obj_content);
jfieldID privateField_int_fid = env->GetFieldID(TestJclass, "privateField_int", "I");
env->SetIntField(testobj, privateField_int_fid, 300);
jint privateField_int_value = env->GetIntField(testobj, privateField_int_fid);
__android_log_print(4, "kanxue->jni", "privateField_int_value->%d", privateField_int_value);

// public int[] intarray = null;
jfieldID intarray_fid = env->GetFieldID(TestJclass, "intarray", "[I");
jintArray intarray_obj = static_cast<jintArray>(env->GetObjectField(testobj, intarray_fid));

int arraylength = env->GetArrayLength(intarray_obj);

__android_log_print(4, "kanxue->jni", "arraylength->%d", arraylength);
jint jni_array[arraylength];
for (int j = 0; j < arraylength; j++) {

jni_array[j] = 10 - j;
}
const jint *ptr = jni_array;
env->SetIntArrayRegion(intarray_obj, 0, arraylength, ptr);

int *array = env->GetIntArrayElements(intarray_obj, nullptr);
for (int i = 0; i < arraylength; i++) {

__android_log_print(4, "kanxue->jni", "array[%d]->%d", i, array[i]);
}


}

__attribute__ ((visibility ("hidden"))) void dddd(
JNIEnv *env,
jclass obj, jobject testobj) {

// private String privateField = "i am a privateField";

jclass TestJclass = env->FindClass("com/kanxue/reflectiontest/Test");
jfieldID privateField_fid = env->GetFieldID(TestJclass, "privateField", "Ljava/lang/String;");
// jobject GetObjectField(jobject obj, jfieldID fieldID)
jstring privateField_obj = static_cast<jstring>(env->GetObjectField(testobj, privateField_fid));

jstring newString = env->NewStringUTF("Modified by jni");
env->SetObjectField(testobj, privateField_fid, newString);


privateField_obj = static_cast<jstring>(env->GetObjectField(testobj, privateField_fid));
const char *privateField_obj_content = env->GetStringUTFChars(privateField_obj,
nullptr);
__android_log_print(4, "kanxue->jni", "privateField_obj->%s", privateField_obj_content);
jfieldID privateField_int_fid = env->GetFieldID(TestJclass, "privateField_int", "I");
env->SetIntField(testobj, privateField_int_fid, 300);
jint privateField_int_value = env->GetIntField(testobj, privateField_int_fid);
__android_log_print(4, "kanxue->jni", "privateField_int_value->%d", privateField_int_value);

// public int[] intarray = null;
jfieldID intarray_fid = env->GetFieldID(TestJclass, "intarray", "[I");
jintArray intarray_obj = static_cast<jintArray>(env->GetObjectField(testobj, intarray_fid));

int arraylength = env->GetArrayLength(intarray_obj);

__android_log_print(4, "kanxue->jni", "arraylength->%d", arraylength);
jint jni_array[arraylength];
for (int j = 0; j < arraylength; j++) {

jni_array[j] = 10 - j;
}
const jint *ptr = jni_array;
env->SetIntArrayRegion(intarray_obj, 0, arraylength, ptr);

int *array = env->GetIntArrayElements(intarray_obj, nullptr);
for (int i = 0; i < arraylength; i++) {

__android_log_print(4, "kanxue->jni", "array[%d]->%d", i, array[i]);
}


}
jint testPushAndPopLocalFrame(JNIEnv *env) {
jobject result = nullptr;
if (env->PushLocalFrame(20) == 0) {
for (int i = 0; i < 18; i++) {
jstring tmp = env->NewStringUTF("kanxue");
}
jstring tmp1 = env->NewStringUTF("result1");
jstring tmp2 = env->NewStringUTF("result2");
result = env->PopLocalFrame(NULL);

} else {

//error
}

return 100;


}

JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
globalVM = vm;
int result = 0;
__android_log_print(4, "kanxue->jni", "JNI_OnLoad(JavaVM *vm, void *reserved)->%p", vm);
__android_log_print(4, "kanxue->jni", "jni->%s", "JNI_OnLoad is called");
JNIEnv *env = nullptr;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) == JNI_OK) {
__android_log_print(4, "kanxue->jni", "jni->%s",
"vm->GetEnv((void**)&env,JNI_VERSION_1_6) success");
}
/* protected native void onCreate(Bundle savedInstanceState);
public native String stringFromJNI();
public native String stringFromJNIC();
public native void newObject();
public native void getStaticField();
public native Object callInit();
public native void callStaticFunc();
public native void callNonStaticFunc();
public static native void getNonStaticField(Object m);*/



/*typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;*/
/*jint (*RegisterNatives)(jclass, const JNINativeMethod*,
jint);

*/

JNINativeMethod jniNativeMethod[] = {{"onCreate", "(Landroid/os/Bundle;)V", (void *) onCreate},
{"getNonStaticField", "(Ljava/lang/Object;)V", (void *) cccc}
};
jclass MainActivityjclass = env->FindClass("com/kanxue/reflectiontest/MainActivity");

env->RegisterNatives(MainActivityjclass, jniNativeMethod,
sizeof(jniNativeMethod) / sizeof(JNINativeMethod));

JNINativeMethod jniNativeMethod2[] = {{"onCreate", "(Landroid/os/Bundle;)V", (void *) onCreate},
{"getNonStaticField", "(Ljava/lang/Object;)V", (void *) dddd}
};
env->RegisterNatives(MainActivityjclass, jniNativeMethod2,
sizeof(jniNativeMethod2) / sizeof(JNINativeMethod));

jclass tmpjclass = env->FindClass("com/kanxue/reflectiontest/Test");
testjclass = static_cast<jclass>(env->NewWeakGlobalRef(tmpjclass));



/* for (int i = 0; i < 2048; i++) {

jstring content = env->NewStringUTF("test localreference");
__android_log_print(4, "kanxue->jni", "localreference number->%d",
i);
env->DeleteLocalRef(content);
}*/
/* int len = 10;
if (env->EnsureLocalCapacity(1024) == 0) {

for (int i = 0; i < len; i++) {

jstring content = env->NewStringUTF("test localreference");
__android_log_print(4, "kanxue->jni", "localreference number->%d",
i);
env->DeleteLocalRef(content);
}
}*/

/* __android_log_print(4, "kanxue->jni", "nullptr and NULL->%d,%d",
nullptr,NULL);
jstring resultjstring = testPushAndPop(env);
const char *content = env->GetStringUTFChars(resultjstring, nullptr);
__android_log_print(4, "kanxue->jni", "testPushAndPop->%s",
content);*/


/* jobject resultobj = testPushAndPopLocalFrame(env);
*

if (!env->IsSameObject(resultobj, NULL)) {

const char *content = env->GetStringUTFChars(static_cast<jstring>(resultobj), nullptr);
__android_log_print(4, "kanxue->jni", "testPushAndPopLocalFrame->%s",
content);
}*/


jint result_jint = testPushAndPopLocalFrame(env);
__android_log_print(4, "kanxue->jni", "testPushAndPopLocalFrame->%d",
result_jint);
result = JNI_VERSION_1_6;
return result;


}


Dalvik动态注册原理

静态注册和动态注册

1
2
3
4
5
6
7
8
9
10
11
extern "C" JNIEXPORT void JNICALL 
Java_......(JNIEnv *env, jobject this){
......
}

// 这类的函数都是冗长的能否用简介的函数签名来注册
// 那就需要用到动态注册

void test(JNIEnv *env, jobject this){
......
}

对于任意一个jni函数来说,在该函数被调用前,必须要完成java函数与so中地址的绑定
这个绑定过程可以是被动的, 即由Dalvik、ART虚拟机在调用前查找完成地址的绑定
也可以是主动即由App自己完成地址的绑定

静态注册

  • 1:对应函数名: Java+包名+类名+方法名
    其中使用下划线将每部分隔开,包名也使用下划线隔开, 如果名称中本来就包含下划线,将下划线加数字替换
1
2
3
4
5
6
7
例如(包名:com.afei.jnidemo 类名:MainActivity)
// Java Native method
public native String stringFromJNI();

// Jni method
JNIEXPORT jstring JNICALL
Java_com_afei_jnidemo_MainActivity_stringFromJNI(JNIEnv* env, jobject instance);
  • 2:优点: 对于静态注册来说,简单明了, 语义清晰
  • 3:缺点: 不够安全

必须遵从注册规则从而导致名称过长,由于保留了符号签名,很容易使用IDA等直接定位地址

动态注册

  • 1:定义

通过RegisterNatives方法手动完成native方法和so中的方法的绑定,这样虚拟机就可以通过这个函数映射关系直接找到相应的方法了

  • 2: 注册过程示例

假设有两个native方法如下

1
2
3
public native String stringFromJNI();

public static native int add(int a, int b);

通常我们在JNI_OnLoad方法中完成动态注册,事实上只需要在该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
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {

JNIEnv *env = nullptr;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) == JNI_OK) {
__android_log_print(4, "kanxue->jni", "jni->%s",
"vm->GetEnv((void**)&env,JNI_VERSION_1_6) success");
}


JNINativeMethod jniNativeMethod[] = {{"onCreate", "(Landroid/os/Bundle;)V", (void *) onCreate},
{"getNonStaticField", "(Ljava/lang/Object;)V", (void *) cccc}
};
jclass MainActivityjclass = env->FindClass("com/kanxue/reflectiontest/MainActivity");

env->RegisterNatives(MainActivityjclass, jniNativeMethod,
sizeof(jniNativeMethod) / sizeof(JNINativeMethod));

JNINativeMethod jniNativeMethod2[] = {{"onCreate", "(Landroid/os/Bundle;)V", (void *) onCreate},
{"getNonStaticField", "(Ljava/lang/Object;)V", (void *) dddd}
};
env->RegisterNatives(MainActivityjclass, jniNativeMethod2,
sizeof(jniNativeMethod2) / sizeof(JNINativeMethod));

return JNI_VERSION_1_6;
}

env->RegisterNatives的参数解析

  • 第一个参数, 要注册上的Java类
  • 第二个参数, 要动态注册的方法
  • 第三个参数, 要动态注册的方法数量

JNIEXPORT对应的就是__attribute__((visibility("default")))默认展现出符号名
就是IDA直接能够提取到当前函数的原来函数名称
如果想要隐藏的话__attribute__((visibility("hidden")))纠正这样的情况即可

Dalvik具体RegisterNatives实现的原理

dalvik/vm/Jni.cpp#RegisterNatives->dvmRegisterJNIMethod->dvmUserJNIBridge


Art动态注册原理

Art具体的RegisterNatives的实现

art/runtime/jni_internal.cc#RegisterNatives->RegisterNativeMethods->JNI::RegisterNativeMethods->ReportInvalidJNINativeMethod

Java类定义nativeMethod都是ArtMethod
->FindMethod(在Art虚拟机中找ArtMethod)

ClassLiner::LoadClassMembers

ArtMethod类的定义在art/runtime/art_method.h文件中




Frida

objection不写代码Hook、Trace、Stack

apt install liblzma-dev
apt-get install -y make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev

objection可以不用写代码直接用命令执行hook

  • objection与Frida版本匹配安装
    • frida 12.8.0
    • frida-tools 5.3.0
    • objection 1.8.4
  • objection内存漫游
    https://www.anquanke.com/post/id/197657
    • objection -g com.android.settings explore 注入应用
    • memory list modules 查看内存中加载的库
    • memory list exports libssl.so 查看库的导出函数
    • memory list exports libart.so –json /root/libart.json 将结果保存到json文件中
    • memory dump all from_base 提取整个(或部分)内存
    • memory search –string –offsets-only 搜索整个内存
    • android heap search instances com.android.settings.DisplaySettings 在堆上搜索实例
    • android heap execute 0x2526 getPreferenceScreenResId 调用实例的方法
    • android heap evaluate 0x2526 在实例上执行js代码,进入迷你编辑器
  • objection hook、trace、stack
    • android hooking list classes 列出内存中所有的类
    • android hooking list activities 列出内存中所有的activity
    • android intent launch_activity xxxx 进入某个activity
    • android hooking search classes display 内存中搜索所有的类
    • android hooking list class_methods com.android.settings.DisplaySettings 列出类的所有方法
    • android hooking watch class android.bluetooth.BluetoothDevice hook类的所有方法
    • android hooking watch class_method android.bluetooth.BluetoothDevice.getName –dump-args –dump-return –dump-backtrace hook方法的参数、返回值和调用栈
  • objection插件体系:WallBreaker
    https://github.com/hluwa/Wallbreaker
    plugin load /root/Desktop/Wallbreaker
    • plugin wallbreaker classdump –fullname com.vivo.push.sdk.LinkProxyClientActivity
  • objection + DexDump脱壳
    • 配合fart-dexdump进行objection的插件, 进行dex导出处理
    • plugin dexdump dump 接着寻找关键的Activity存在的dex文件 grep -ril “LoginActivity” *

objection主动调用,heap寻找实例,无法调用静态函数,只能在Frida来主动调用静态函数


Frida上手和逆向三段

  • Frida基本操作
    参数、返回值
    调用栈(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable"),$new()))
  • Frida精髓
    • 方法重载
      使用overload来选择方法重载确定的函数
    • 参数构造
      构造字符串参数Java.use(‘java.lang.String’).$new(“NIHAOJAVA”)
    • 动静态处理
      动态函数的意思就是需要找到instance实例, 再去调用
      静态函数则可以直接找到这个class调用
    • 主动调用
    • 忽略内部细节,直接返回结果
  • 逆向三段
    • hook、invoke、rpc
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
function main(){
Java.perform(function(){

Java.use("com.example.lesson4one.MainActivity").fun.overload('int', 'int').implementation = function(arg1,arg2){
var result = this.fun(arg1,arg2);
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
console.log("arg1,arg2,result",arg1,arg2,result)
return 800;
}


Java.use("com.example.lesson4one.MainActivity").fun.overload('java.lang.String').implementation = function (arg1 ){
var result = this.fun(Java.use('java.lang.String').$new("NIHAOJAVA"));
console.log("arg1,result",arg1,result)
return Java.use('java.lang.String').$new("NIHAOJS");
}


Java.choose("com.example.lesson4one.MainActivity",{
onMatch:function(instance){
console.log("found instance :",instance)
console.log("found instance :",instance.secret())
},onComplete:function(){}
})

var result = Java.use("com.example.lesson4one.MainActivity").secret2();
console.log(result);
})
}
setImmediate(main)


Frida构造数组、对象、Map和类参数

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
package com.r0ysue.a0526printout;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//TrafficLight.main();
//com.r0ysue.a0526printout.Juice.main();
//milk.main();


/*
Log.d("SimpleArray", "onCreate: SImpleArray");
char arr[][] = new char[4][]; // 创建一个4行的二维数组
arr[0] = new char[] { '春', '眠', '不', '觉', '晓' }; // 为每一行赋值
arr[1] = new char[] { '处', '处', '闻', '啼', '鸟' };
arr[2] = new char[] { '夜', '来', '风', '雨', '声' };
arr[3] = new char[] { '花', '落', '知', '多', '少' };
Log.d("SimpleArray", "-----横版-----");
for (int i = 0; i < 4; i++) { // 循环4行
Log.d("SimpleArraysToString", Arrays.toString(arr[i]));
Log.d("SimpleStringBytes", Arrays.toString (Arrays.toString (arr[i]).getBytes()));
for (int j = 0; j < 5; j++) { // 循环5列
Log.d("SimpleArray", Character.toString(arr[i][j])); // 输出数组中的元素
}
if (i % 2 == 0) {
Log.d("SimpleArray", ",");// 如果是一、三句,输出逗号
} else {
Log.d("SimpleArray", "。");// 如果是二、四句,输出句号
}
}
*/

Map<String, String> mapr0ysue = new HashMap<>(); // 创建Map集合对象
mapr0ysue.put("ISBN 978-7-5677-8742-1", "Android项目开发实战入门"); // 向Map集合中添加元素
mapr0ysue.put("ISBN 978-7-5677-8741-4", "C语言项目开发实战入门");
mapr0ysue.put("ISBN 978-7-5677-9097-1", "PHP项目开发实战入门");
mapr0ysue.put("ISBN 978-7-5677-8740-7", "Java项目开发实战入门");

//Log.d("5map", "key值toString"+mapr0ysue.toString());




Set<String> set = mapr0ysue.keySet(); // 构建Map集合中所有key的Set集合
Iterator<String> it = set.iterator(); // 创建Iterator迭代器
Log.d("5map", "key值:");
while (it.hasNext()) { // 遍历并输出Map集合中的key值
try {
Thread.sleep(5000);
Log.d("5map", it.next()+" ");
} catch (InterruptedException e) {
e.printStackTrace();
}
}

}
}

数组/字符串对象数组/gson/Java.array

1
2
3
4
5
6
7
8
9
Java.openClassFile("/data/local/tmp/r0gson.dex").load();
const gson = Java.use('com.r0ysue.gson.Gson');

Java.use("java.util.Arrays").toString.overload('[C').implementation = function (x){
var charArray = Java.array('char', [ '一','去','二','三','里' ]);
var result = this.toString(charArray);
console.log("x,result",gson.$new().toJson(charArray),result);
return Java.use('java.lang.String').$new(Java.array('char',['烟','村','四','五','家']));
}

对象/多态、强制Java.cast

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
public class Water { // 水 类
public static String flow(Water W) { // 水 的方法
// SomeSentence
Log.d("2Object", "water flow: I`m flowing");
return "water flow: I`m flowing";
}
public String still(Water W) { // 水 的方法
// SomeSentence
Log.d("2Object", "water still: still water runs deep!");
return "water still: still water runs deep!";
}
}
package com.r0ysue.a0526printout;
import android.util.Log;
import com.r0ysue.a0526printout.Water;
public class Juice extends Water { // 果汁 类 继承了水类
public String fillEnergy(){
Log.d("2Object", "Juice: i`m fillingEnergy!");
return "Juice: i`m fillingEnergy!";
}
public static void main() {
Water w1 = new Water();
flow(w1) ; //
Juice J = new Juice(); // 实例化果汁类对象
flow(J) ; // 调用水的方法 向上转型 J → W
Water w2 = new Juice();
((Juice) w2).fillEnergy();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var Waterhandle = null;
Java.choose("com.r0ysue.a0526printout.Water",{
onMatch:function(instance){
console.log("found instance:",instance);
console.log("water instance call still:",instance.still(instance));
Waterhandle = instance;
},onComplete:function(){console.log("search completed!")}
})
// 这是无法转换的, 无法把父类转换为子类
var JuiceHandle = Java.cast(Waterhandle,Java.use("com.r0ysue.a0526printout.Juice"));
console.log("Juice fillEnergy method :",JuiceHandle.fillEnergy());
var JuiceHandle = null ;
Java.choose("com.r0ysue.a0526printout.Juice",{
onMatch:function(instance){
console.log("found instance :",instance);
console.log("filling energy,",instance.fillEnergy());
JuiceHandle= instance;
},onComplete:function(){"Search Completed!"}
})
var WaterHandle = Java.cast(JuiceHandle ,Java.use("com.r0ysue.a0526printout.Water"));
console.log("Water invoke still ", WaterHandle.still(WaterHandle));

接口Interface、Java.register

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.r0ysue.a0526printout;
public interface liquid {
public String flow();
}
package com.r0ysue.a0526printout;
import android.util.Log;
public class milk implements liquid {
public String flow(){
Log.d("3interface", "flowing : interface ");
return "nihao";
};
public static void main() {
milk m = new milk();
m.flow();
}
}
1
2
3
4
5
6
7
8
9
10
11
var beer = Java.registerClass({
name: 'com.r0ysue.a0526printout.beer',
implements: [Java.use('com.r0ysue.a0526printout.liquid')],
methods: {
flow: function () {
console.log("look I`m beer!");
return "taste good!";
}
}
});
console.log("beer.flow:",beer.$new().flow());

枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.r0ysue.a0526printout;
import android.util.Log;
enum Signal {
GREEN, YELLOW, RED
}
public class TrafficLight {
public static Signal color = Signal.RED;
public static void main() {
Log.d("4enum", "enum "+ color.getClass().getName().toString());
switch (color) {
case RED:
color = Signal.GREEN;
break;
case YELLOW:
color = Signal.RED;
break;
case GREEN:
color = Signal.YELLOW;
break;
}
}
1
2
3
4
5
6
Java.choose("com.r0ysue.a0526printout.Signal",{
onMatch:function(instance){
console.log("found instance:",instance)
console.log("invoke getDeclaringClass",instance.getDeclaringClass())
},onComplete:function(){console.log("Search Completed!")}
})

泛型、List、Map、Set、迭代打印

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Map<String, String> mapr0ysue = new HashMap<>(); // 创建Map集合对象
mapr0ysue.put("ISBN 978-7-5677-8742-1", "Android项目开发实战入门"); // 向Map集合中添加元素
mapr0ysue.put("ISBN 978-7-5677-8741-4", "C语言项目开发实战入门");
mapr0ysue.put("ISBN 978-7-5677-9097-1", "PHP项目开发实战入门");
mapr0ysue.put("ISBN 978-7-5677-8740-7", "Java项目开发实战入门");
Set<String> set = mapr0ysue.keySet(); // 构建Map集合中所有key的Set集合
Iterator<String> it = set.iterator(); // 创建Iterator迭代器
Log.d("5map", "key值:");
while (it.hasNext()) { // 遍历并输出Map集合中的key值
try {
Thread.sleep(5000);
Log.d("5map", it.next()+" ");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
Java.choose("java.util.HashMap",{
onMatch:function(instance){
if(instance.toString().indexOf("ISBN")!=-1){
console.log("found HashMap",instance);
console.log("HashMap toString:",instance.toString());
}
},onComplete:function(){console.log("Search Completed!")}
})
Java.use("java.util.HashMap").put.implementation = function(x,y){
var result = this.put(x,y);
console.log("x,y,result",x,y,result)
return result;
}

重要思路:开发时如何打印、Frida中也是如何打印

hook的时机要控制好,比如App启动了,实例创建后马上销毁再hook也无济于事

non-ascii 方法名hook

https://api-caller.com/2019/03/30/frida-note/
比如说一些符号方法名,那就可以通过url编码, 之后再url解码, 就可以hook了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int ֏(int x) {
return x + 100;
}
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;
}
}
)


Frida综合情景案例

  • 调用静态函数和调用非静态函数
  • 设置成员变量

    1
    Java.use("com.example.androiddemo.Activity.FridaActivity3").static_bool_var.value = true ;
  • 内部类,枚举类的函数并hook, trace原型1

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var all_methods = InnerClass.class.getDeclaredMethods();
    //console.log(all_methods);
    var InnerClass = Java.use(class_name);
    var all_methods = InnerClass.class.getDeclaredMethods();
    for(var i = 0;i<all_methods.length;i++){
    var method = all_methods[i];
    //console.log(method.toString());
    var substring = method.toString().substr(method.toString().indexOf(class_name)+class_name.length+1);
    var finalMethodString = substring.substr(0,substring.indexOf("("));
    console.log(finalMethodString);
    InnerClass[finalMethodString].implementation = function(){return true};
    }
  • 查找接口,hook动态加载dex,补充一个找interface的实现"通杀"的方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    Java.enumerateClassLoaders({
    onMatch:function(loader){
    try {
    if(loader.findClass("com.example.androiddemo.Dynamic.DynamicCheck")){
    console.log("Succefully found loader!",loader);
    Java.classFactory.loader = loader;
    }
    } catch (error) {
    console.log("found error "+error)
    }
    },onComplete:function(){"enum completed!"}
    })
    Java.use("com.example.androiddemo.Dynamic.DynamicCheck").check.implementation = function(){return true};
  • 枚举class, trace原型2

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Java.perform(function(){
    Java.enumerateLoadedClasses({
    onMatch:function(name,handle){
    if(name.toString().indexOf("com.example.androiddemo.Activity.Frida6.Frida6")>=0){
    console.log("name",name)
    Java.use(name).check.implementation = function(){return true}
    }
    },onComplete(){}
    })
    })

找hook原则

  • 开发视角
  • hook点离数据越近越好
  • 换安卓版本,换frida版本试试
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
function main(){
Java.perform(function(){
Java.use("com.example.androiddemo.Activity.LoginActivity").a.overload('java.lang.String', 'java.lang.String').implementation = function(x,y){
var result = this.a(x,y);
console.log("x,y,result",x,y,result);
return result;
}

Java.use("com.example.androiddemo.Activity.FridaActivity1").a.implementation = function (x){
console.log("first challenge 1")
return "R4jSLLLLLLLLLLOrLE7/5B+Z6fsl65yj6BgC6YWz66gO6g2t65Pk6a+P65NK44NNROl0wNOLLLL=" ;
}
})
}


function second(){
Java.perform(function(){
Java.use("com.example.androiddemo.Activity.FridaActivity2").setStatic_bool_var();

Java.choose("com.example.androiddemo.Activity.FridaActivity2",{
onMatch:function(instance){
console.log("found instance :",instance);
instance.setBool_var()
},onComplete:function(){}
})
})
}

function third(){
Java.perform(function(){

Java.use("com.example.androiddemo.Activity.FridaActivity3").static_bool_var.value = true ;

Java.choose("com.example.androiddemo.Activity.FridaActivity3",{
onMatch:function(instance){
console.log("found instance :",instance);
instance.bool_var.value = true ;
instance._same_name_bool_var.value = true ;
},onComplete:function(){}
})

})
}

function forth(){
Java.perform(function(){
Java.use("com.example.androiddemo.Activity.FridaActivity4$InnerClasses").check1.implementation = function(){return true};
Java.use("com.example.androiddemo.Activity.FridaActivity4$InnerClasses").check2.implementation = function(){return true};
Java.use("com.example.androiddemo.Activity.FridaActivity4$InnerClasses").check3.implementation = function(){return true};
Java.use("com.example.androiddemo.Activity.FridaActivity4$InnerClasses").check4.implementation = function(){return true};
Java.use("com.example.androiddemo.Activity.FridaActivity4$InnerClasses").check5.implementation = function(){return true};
Java.use("com.example.androiddemo.Activity.FridaActivity4$InnerClasses").check6.implementation = function(){return true};
})
}
function forth2(){
Java.perform(function(){
var class_name = "com.example.androiddemo.Activity.FridaActivity4$InnerClasses" ;
var InnerClass = Java.use(class_name);
var all_methods = InnerClass.class.getDeclaredMethods();
//console.log(all_methods);
for(var i = 0;i<all_methods.length;i++){
var method = all_methods[i];
//console.log(method.toString());
var substring = method.toString().substr(method.toString().indexOf(class_name)+class_name.length+1);
var finalMethodString = substring.substr(0,substring.indexOf("("));
console.log(finalMethodString);
InnerClass[finalMethodString].implementation = function(){return true};
}
})
}
function fifth(){
Java.perform(function(){
Java.choose("com.example.androiddemo.Activity.FridaActivity5",{
onMatch:function(instance){
console.log("found instance getDynamicDexCheck :",instance.getDynamicDexCheck().$className);
},onComplete:function(){console.log("search complete!")}
})
Java.enumerateClassLoaders({
onMatch:function(loader){
try {
if(loader.findClass("com.example.androiddemo.Dynamic.DynamicCheck")){
console.log("Succefully found loader!",loader);
Java.classFactory.loader = loader;
}
} catch (error) {
console.log("found error "+error)

}
},onComplete:function(){"enum completed!"}
})
Java.use("com.example.androiddemo.Dynamic.DynamicCheck").check.implementation = function(){return true};
})
}

function sixth (){
Java.perform(function(){
Java.use("com.example.androiddemo.Activity.Frida6.Frida6Class0").check.implementation = function (){return true };
Java.use("com.example.androiddemo.Activity.Frida6.Frida6Class1").check.implementation = function (){return true };
Java.use("com.example.androiddemo.Activity.Frida6.Frida6Class2").check.implementation = function (){return true };
})

}
function sixth2(){
Java.perform(function(){
Java.enumerateLoadedClasses({
onMatch:function(name,handle){
if(name.toString().indexOf("com.example.androiddemo.Activity.Frida6.Frida6")>=0){
console.log("name",name)
Java.use(name).check.implementation = function(){return true}
}
},onComplete(){}
})
})
}

setImmediate(sixth2);




RPC远程调用概念和实例

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
import time
import frida

def my_message_handler(message, payload):
print(message)
print(payload)

# usb connection
device = frida.get_usb_device()

device = frida.get_device_manager().add_remote_device("192.168.1.102:9999")
# 注意frida-server的端口也是开在9999上,并且是同一个局域网互联
device = frida.
#pid = device.spawn("com.example.session4one")
#device.resume
#time.sleep(1)
#session = device.attach(pid)
session = device.attach("com.example.session4one")
while open("task.js") as f:
script = session.create_script(f.read())
script.on("message", my_message_handler)
script.load()

command = ""
while True:
command = input("Enter Command:")
if command == "1":
break
elif command == "2":
script.exports.infokefunc

逆向三段:第三段RPC

Remote Procedure Call 远程调用
利用这段可以进行多主机多手机多端口混连device = frida.get_device_manager().add_remote_device("192.168.1.102:9999")

远程调动,互联互通,动态修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Java.perform(function(){
Java.use("android.widget.TextView").setText.overload('java.lang.CharSequence').implementation = function(x){
var string_to_send_x = x.toString();
var string_to_recv;
send(string_to_send_x);
recv(function(received_json_objection){
string_to_recv = received_json_objection.my_data
console.log("string_to_recv:"+string_to_recv)
}).wait();
var javaStringToSend=Java.use('java.lang.String').$new(string_to_recv);

var result = this.setText(javaStringToSend);
//console.log("x.toString(),result",x.toString(),result);
return result;
}
})
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
import time
import frida
import base64

def my_message_handler(message,payload):
print(message)
print(payload)
if message["type"]=="send":
print(message["payload"])
data = message["payload"].split(":")[1].strip()
print('message:',message)
data = str(base64.b64decode(data))
print('data:',data)
usr,pw = data.split(":")
print('pw:',pw)
data = str(base64.b64encode(("admin"+":"+pw).encode()))
print("encode data:",data)
script.post({"my_data":data})
print("Modified data sent !")

device = frida.get_usb_device()

#pid = device.spawn(["com.example.lesson4one"])
#device.resume
#time.sleep(1)
#session = device.attach(pid)
session = device.attach("com.example.lesson7sec")
with open("lesson7sec.js") as f :
script = session.create_script(f.read())
script.on("message",my_message_handler)
script.load()
input()


Rpc公开外网

通过flask的web框架公布到公网集群中去

nps: 一款轻量级、高性能、功能强大的内网穿透代理服务器。支持tcp、udp、socks5、http等几乎所有流量转发,可用来访问内网网站、本地支付接口调试、ssh访问、远程桌面,内网dns解析、内网socks5代理等等……,并带有功能强大的web管理端。

先通过nps的server段架设到公网
再通过nps的client段架设到Android手机上
建立tcp隧道通信,直接连通android frida server的端口,映射公网的端口
这样python脚本的frida rpc可以直接操作android手机客户端的frida server

1
device = frida.get_device_manager().add_remote_device("公网ip:58888")

Zentracer是葫芦娃大佬写的Tracer工具,它基于Pyqt+Frida开发,短小精悍。

https://www.anquanke.com/post/id/196657

child-gating子进程管理器

解析原先DEXDump的源码以及Zentrace的源码


综合实战

  • spawn/attach 时机的选择
  • 各种主动调用/直接lu完
  • 各种hook及构造函数
  • 动态加载自己的dex
  • 约束求解/符号执行
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 main(){
Java.perform(function(){

Java.openClassFile("/data/local/tmp/classes.dex").load();
const reverseA = Java.use('com.example.lesson9.reverseA');
console.log("reverseA result:",reverseA.decode_P());
console.log("r_to_hex result:",reverseA.r_to_hex());

Java.use("java.lang.System").getProperty.overload('java.lang.String').implementation = function(x){
var result = this.getProperty(x);
console.log("x,result:",x,result);
return Java.use("java.lang.String").$new("Russia");
}
Java.use("java.lang.System").getenv.overload('java.lang.String').implementation = function(x){
var result = this.getenv(x);
console.log("x,result:",x,result);
return Java.use("java.lang.String").$new("RkxBR3s1N0VSTDFOR180UkNIM1J9Cg==");
}
/*
Java.use("com.tlamb96.kgbmessenger.b.a").$init.implementation = function(str0,str1,str2,b){
var result = this.$init(str0,str1,str2,b);
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
console.log("str0,str1,str2,b:",str0,str1,str2,b)
return result;
}
Java.use("com.tlamb96.kgbmessenger.MessengerActivity").a.implementation = function(x){
var result = this.a(x);
console.log("a:x,result:",x,result);
if(x=="aaaa"){
result= Java.use("java.lang.String").$new("V@]EAASB\u0012WZF\u0012e,a$7(&am2(3.\u0003");
}
return result;
}


Java.use("com.tlamb96.kgbmessenger.MessengerActivity").b.implementation = function(x){
var result = this.b(x);
console.log("b:x,result:",x,result);
if(x=="bbbb"){
result= Java.use("java.lang.String").$new("\u0000dslp}oQ\u0000 dks$|M\u0000h +AYQg\u0000P*!M$gQ\u0000");
}
return result;
}
*/
})
}
setImmediate(main)
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
package com.example.lesson9;

public class reverseA {
public static String decode_P(){
String p = "V@]EAASB\u0012WZF\u0012e,a$7(&am2(3.\u0003";
String result = a(p);
return result;
}


private static String a(String str) {
char[] charArray = str.toCharArray();
for (int i = 0; i < charArray.length / 2; i++) {
char c = charArray[i];
charArray[i] = (char) (charArray[(charArray.length - i) - 1] ^ 'A');
charArray[(charArray.length - i) - 1] = (char) (c ^ '2');
}
return new String(charArray);
}
public static String r_to_hex(){
String r = "\u0000dslp}oQ\u0000 dks$|M\u0000h +AYQg\u0000P*!M$gQ\u0000";

byte[] bytes = r.getBytes();
String result = "";
for(int i=0;i<bytes.length;i++){
result += String.format("%02x",bytes[i]);
}
return result;

}
}




Xposed

一般公司稳定性选用xposed, frida一般都用于逆向测试, xposed用来开发产品并发布

Xposed是一款可以不修改Apk情况下影响程序运行的框架
基于它可以制作许多功能强大的模块,且在功能不冲突的情况下同时运作

在这个框架下,我们可以编写并加载自己编写的插件App,实现对目标Apk的注入、拦截等

Xposed作者出自国外大名鼎鼎的Android论坛Xda, 由作者rovo89进行更新和维护
在8.1的betal版后目前迟迟未见更新

xposed历史悠久, 几乎伴随着Google对每一个Android版本的升级, 上至Android2时代, 到现在的Android8, 在没有AndroidStudio作为Android开发的ide时代

Xposed与edxposed安装

Xposed 原理

控制Zygote进程,通过替换/system/bin/app_process程序控制zygote进程,使得它在系统启动的过程中会加载Xposed Framework的一个jar文件即XposedBridge.jar,
从而完成对Zygote进程以及其创建Dalvik/Art虚拟机的劫持, 并且能够允许开发者独立的替代任何class, 例如framework本身, 系统UI又或者随意的一个App

Xposed的安装

4.4以下Android版本安装较为简单, 只需两步即可

  • 对需要安装Xposed的手机进行root
  • 下载并安装xposedinstaller, 之手授予其root权限, 进入app点击安装即可

很遗憾, 当前官网不再维护, 导致无法直接通过xposedinstaller下载补丁包

从Android5.0开始, 谷歌使用Art替换Dalvik, 所以xposed安装有点麻烦, 分为两个部分: xposed*.zipXposedInstaller.apk
zip文件是框架主体, 需要进入Recovery后刷入, apk文件是xposed模块管理应用, 主要用于下载、激活、是否启用模块等管理功能

  • 完成对手机的Root,并刷入recovery(比如twrp);
  • 下载对应的zip补丁包,进入recovery刷入;
  • 重启手机, 安装xposedinstaller并授予root权限即可

如果你买了华为、小米,并不是Google亲儿子手机, 虽然说rom可以搜到,并且rom是定制的,这会踩很多的坑
所以还是推荐pixel或者nexus手机

从Android9,10等新版本安装Edxposed
对于Xposed官方不支持的较新的Android版本可以先安装magisk, 接着安装riyu模块, 最后再进行Edxposed的安装即可

Xposed优秀插件

隐私保护:Xprivacy
微信相关模块: 红包助手、防撤回、自动化营销等
QQ相关模块: QQ抢红包、QQ防撤回、记录QQ账号密码模块

开启Debug的
幸运破解器: 应用内购破解, 去广告神器
绿色守护: 切断后台唤醒、推送的神器
抓包神器: justtrustme
等等其他优秀插件


Xposed插件

  • 拷贝XposedBridgeApi.jar到新建工程的libs目录
  • 修改app目录下的build.gradle文件,在AndroidManifest.xml中增加Xposed相关内容
  • 新建hook类, 编写hook代码
  • 新建assets文件夹,然后在assets目录下新建文件xposed_init,在里面写上hook类的完整路径

编写准备

  • 新建Android Studio工程,选择有无Activity皆可, 并将XposedBridge.jar拷贝到libs目录下, 然后双击App目录下的build.gradle文件, 将
1
2
3
4
5
compile fileTree(include:{'*.jar'}, dir:'libs')
替换为
provided fileTree(include:{'*.jar'}, dir:'libs')
由于provided比较老,用compileOnly也是比较合适的
compileOnly

如果使用compile,可以正常编译生成插件apk, 但是当安装到手机上后, xposed会报错无法正常工作

  • 修改AndroidManifest.xml文件,在Application标签下增加内容如下
1
2
3
4
5
6
7
<meta-data
android:name="xposedmodule" //是否配置为xposed插件,设置为true
android:value="true" />
<meta-data android:name="xposeddescription"
android:value="第一个Xposed插件"/> // 模块名称
<meta-data android:name="xposedminversion"
android:value="54" /> //最低版本号

插件编写

新建hook类,命名为XModule,并实现接口IXposedHookLoadPackage即可, 并实现里面关键方法handleLoadPackage(XC_LoadPackage.LoadPackageParam lpParam),该方法会在每个软件被启动的时候回调,所以一般需要通过目标包过滤,内容如下

1
2
3
4
5
6
7
8
public class XModule implementes IXposedHookLoadPackage{
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable{
if(loadPackageParam.packageName.equals("com.example.test")){
XposedBridge.log("XLZH" + loadPackageParam.packageName);
}
}
}

插件告知

新建assets目录,在其中新建文本xposed_init
里面内容为实现了IXposedHookLoadPackage接口的hook类的完整类名
可以有多个,每一个hook实现类一行

Hook构造函数并修改属性

  • 无参构造函数的hook
  • 有参构造函数的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
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
package com.kanxue.xposedhook01;

import android.util.Log;

public class Student {
String name = null;
String id = null;
int age = 0;

private String nickname = null;
private static String teacher = null;
/*01-09 04:53:39.748 20009-20009/com.kanxue.xposedhook01 I/Xposed: beforeHookedMethod publicfunc->arg0:publicfunc---arg1:500
01-09 04:53:39.748 20009-20009/com.kanxue.xposedhook01 I/Xposed: beforeHookedMethod11 privatefunc->arg0:privatefunc---arg1:300
01-09 04:53:39.748 20009-20009/com.kanxue.xposedhook01 I/Xposed: privatefunc is called!---privatefunc---300
01-09 04:53:39.748 20009-20009/com.kanxue.xposedhook01 I/Xposed: afterHookedMethod11 privatefunc->result:privatefunc---300
01-09 04:53:39.748 20009-20009/com.kanxue.xposedhook01 I/Xposed: publicfunc is called!---changedbyxposedjava---888
01-09 04:53:39.748 20009-20009/com.kanxue.xposedhook01 I/Xposed: beforeHookedMethod getpersonname->person
01-09 04:53:39.749 20009-20009/com.kanxue.xposedhook01 I/Xposed: afterHookedMethod getpersonname->person
01-09 04:53:39.749 20009-20009/com.kanxue.xposedhook01 I/Xposed: afterHookedMethod publicfunc->result:changedbyxposedjava---888---privatefunc---300---person
01-09 04:53:39.749 20009-20009/com.kanxue.xposedhook01 I/xposed: changedbyxposed->afterHookedMethod
*/
public class person {
public String name;
public String sex;
public String getpersonname(String arg0){
return arg0;
}
}

public String getNickname() {
return nickname;
}

public static String getTeacher() {
return teacher;
}

public Student() {
name = "default";
id = "default";
age = 100;
}

public Student(String name) {
this.name = name;
id = "default";
}

public Student(String name, String id) {
this.name = name;
this.id = id;

}

public Student(String name, String id, int age, String teachername, String nickname) {
this.name = name;
this.id = id;
this.age = age;
teacher = teachername;
this.nickname = nickname;

}

public static String publicstaticfunc(String arg1, int arg2) {
String result = privatestaticfunc("privatestaticfunc", 200);
Log.i("Xposed","publicstaticfunc is called!"+"---"+arg1+"---"+arg2);
return arg1 + "---" + arg2 + "---" + result;
}

private static String privatestaticfunc(String arg1, int arg2) {
Log.i("Xposed","privatestaticfunc is called!"+"---"+arg1+"---"+arg2);
return arg1 + "---" + arg2;
}

public String publicfunc(String arg1, int arg2) {
String result = privatefunc("privatefunc", 300);
Log.i("Xposed","publicfunc is called!"+"---"+arg1+"---"+arg2);
person tmp=new person();
String tmpresult=tmp.getpersonname("person");
return arg1 + "---" + arg2 + "---" + result+"---"+tmpresult;
}

private String privatefunc(String arg1, int arg2) {
Log.i("Xposed","privatefunc is called!"+"---"+arg1+"---"+arg2);
return arg1 + "---" + arg2;
}

}







Student astudent = new Student();
Student bstudent = new Student("xiaoming");
Student cstudent = new Student("xiaohua", "2020");
Student dstudent = new Student("xiaohong", "2010", 20, "teacher", "panda");

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
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
173
174
package com.kanxue.xposed01;

import android.util.Log;

import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;

public class HookConstructors implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
Log.i("Xposed01", loadPackageParam.packageName);
XposedBridge.log("Xposed01->app packagename" + loadPackageParam.packageName);
if (loadPackageParam.packageName.equals("com.kanxue.xposedhook01")) {
//XposedBridge.log("kanxue " + loadPackageParam.packageName);


/* public static de.robv.android.xposed.XC_MethodHook.Unhook findAndHookConstructor(java.lang.Class<?> clazz, java.lang.Object... parameterTypesAndCallback)
public static de.robv.android.xposed.XC_MethodHook.Unhook findAndHookConstructor(java.lang.String className, java.lang.ClassLoader classLoader, java.lang.Object... parameterTypesAndCallback)
*/
ClassLoader classLoader = loadPackageParam.classLoader;
Class StudentClass = classLoader.loadClass("com.kanxue.xposedhook01.Student");
XposedHelpers.findAndHookConstructor(StudentClass, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
XposedBridge.log("com.kanxue.xposedhook01.Student() is called!!beforeHookedMethod");
}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
XposedBridge.log("com.kanxue.xposedhook01.Student() is called!!afterHookedMethod");
}
});

// public Student(String name2) {
// this.name = name2;
// this.id = "default";
// }
// public java.lang.Object thisObject;
// public java.lang.Object[] args;
// private java.lang.Object result;
XposedHelpers.findAndHookConstructor(StudentClass, String.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
java.lang.Object[] argsobjarray = param.args;
String name = (String) argsobjarray[0];
XposedBridge.log("com.kanxue.xposedhook01.Student(String) is called!!beforeHookedMethod--" + name);
}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
XposedBridge.log("com.kanxue.xposedhook01.Student(String) is called!!afterHookedMethod");
}
});
// public Student(String name2, String id2) {
// this.name = name2;
// this.id = id2;
// }

XposedHelpers.findAndHookConstructor("com.kanxue.xposedhook01.Student", loadPackageParam.classLoader, String.class, String.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
java.lang.Object[] argsobjarray = param.args;
String name = (String) argsobjarray[0];
String id = (String) argsobjarray[1];
XposedBridge.log("com.kanxue.xposedhook01.Student(String,String) is called!!beforeHookedMethod--" + name + "---" + id);

}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
XposedBridge.log("com.kanxue.xposedhook01.Student(String,String) is called!!afterHookedMethod");
}
});


ClassLoader pathClassLoader= loadPackageParam.classLoader;

final Class stuClass=pathClassLoader.loadClass("com.kanxue.xposedhook01.Student");
XposedBridge.log("StudentClass->"+stuClass);

/* Field teacherField=stuClass.getDeclaredField("teacher");

//teacherField.setAccessible(true);

teacherField.set(null,"teacher666");


String teachername1= (String) teacherField.get(null);

XposedBridge.log("teacherField->"+teachername1);*/

//(java.lang.Class<?> clazz, java.lang.String fieldName, java.lang.Object value)


XposedHelpers.setStaticObjectField(stuClass,"teacher","teacher888");

String teachername2= (String) XposedHelpers.getStaticObjectField(stuClass,"teacher");


XposedBridge.log("XposedHelpers.getStaticObjectField->"+teachername2);


/*XposedHelpers.findAndHookConstructor(StudentClass, String.class, String.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
java.lang.Object[] argsobjarray = param.args;
String name = (String) argsobjarray[0];
String id = (String) argsobjarray[1];
XposedBridge.log("com.kanxue.xposedhook01.Student(String,String) is called!!beforeHookedMethod--" + name + "---" + id);

}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
XposedBridge.log("com.kanxue.xposedhook01.Student(String,String) is called!!afterHookedMethod");
}
});
*/
//public Student(String name2, String id2, int age2)
XposedHelpers.findAndHookConstructor(StudentClass, String.class, String.class, int.class,String.class,String.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
java.lang.Object[] argsobjarray = param.args;
String name = (String) argsobjarray[0];
String id = (String) argsobjarray[1];
int age = (int) (argsobjarray[2]);
argsobjarray[1] = "2050";
argsobjarray[2] = 100;

String teacher= (String) argsobjarray[3];
String nickname= (String) argsobjarray[4];


XposedBridge.log("com.kanxue.xposedhook01.Student(String,String) is called!!beforeHookedMethod--" + name + "---" + id + "--" + age+"---"+teacher+"---"+nickname);
}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);

Object thisobj = param.thisObject;


/* Field nicknameField=stuClass.getDeclaredField("nickname");
XposedBridge.log(stuClass+"--nicknameField->"+nicknameField);
nicknameField.setAccessible(true);
nicknameField.set(thisobj,"bear");*/

XposedHelpers.setObjectField(thisobj,"nickname","chick");




Object returnobj = param.getResult();
XposedBridge.log(thisobj + "---" + returnobj);
XposedBridge.log("com.kanxue.xposedhook01.Student(String,String,int) is called!!afterHookedMethod");
}
});
}

}
}

xposed不仅实现对App类构造函数Hook,也可以hook系统层面的框架

HookDexClassLoader

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
package com.kanxue.xposed01;

import android.util.Log;

import dalvik.system.DexClassLoader;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;

public class HookDexClassLoader implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
Log.i("HookDexClassLoader", loadPackageParam.packageName);
XposedBridge.log("HookDexClassLoader->app packagename" + loadPackageParam.packageName);
if (loadPackageParam.packageName.equals("com.kanxue.loaddex")) {

//public DexClassLoader(String dexPath, String optimizedDirectory,
// String librarySearchPath, ClassLoader parent) {
// super(dexPath, null, librarySearchPath, parent);
// }
XposedHelpers.findAndHookConstructor(DexClassLoader.class, String.class, String.class, String.class, ClassLoader.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Object array[]=param.args;
String dexpath= (String) array[0];
String optimizedDirectory= (String) array[1];
String librarySearchPath= (String) array[2];
XposedBridge.log("DexClassLoader beforeHookedMethod:"+dexpath+"---"+optimizedDirectory+"---"+librarySearchPath);

}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);

DexClassLoader dexClassLoader= (DexClassLoader) param.thisObject;
XposedBridge.log("DexClassLoader afterHookedMethod:"+dexClassLoader);
}
});

}

}
}

HookFlag

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
package com.kanxue.xposed01;

import android.util.Log;

import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;

public class HookFlag implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
Log.i("HookFlag", loadPackageParam.packageName);
XposedBridge.log("HookFlag->app packagename" + loadPackageParam.packageName);
if (loadPackageParam.packageName.equals("com.kanxue.xposedflag")) {
XposedBridge.log("HookFlag " + loadPackageParam.packageName);


ClassLoader classLoader=loadPackageParam.classLoader;

Class Flag1Class=classLoader.loadClass("com.kanxue.xposedflag.Flag1");
XposedHelpers.setStaticIntField(Flag1Class,"length",8);


XposedHelpers.findAndHookConstructor("com.kanxue.xposedflag.Flag2", classLoader, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
XposedBridge.log("go into com.kanxue.xposedflag.Flag2()->beforeHookedMethod");
}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
XposedBridge.log("leave com.kanxue.xposedflag.Flag2()->afterHookedMethod");

Object Flag2obj=param.thisObject;
XposedHelpers.setObjectField(Flag2obj,"flag","12345678");


}
});

}
}
}


Hook一般函数

  • HookJava一般函数
  • HookJava内部类&匿名函数中的函数

    1
    2
    寻找ClassParentName$ClassChildName
    直接拖到GDA寻找类名就可以了
  • HookJNI函数

应用代码

Student.java

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
package com.kanxue.xposedhook01;

import android.util.Log;

public class Student {
String name = null;
String id = null;
int age = 0;

private String nickname = null;
private static String teacher = null;
/*01-09 04:53:39.748 20009-20009/com.kanxue.xposedhook01 I/Xposed: beforeHookedMethod publicfunc->arg0:publicfunc---arg1:500
01-09 04:53:39.748 20009-20009/com.kanxue.xposedhook01 I/Xposed: beforeHookedMethod11 privatefunc->arg0:privatefunc---arg1:300
01-09 04:53:39.748 20009-20009/com.kanxue.xposedhook01 I/Xposed: privatefunc is called!---privatefunc---300
01-09 04:53:39.748 20009-20009/com.kanxue.xposedhook01 I/Xposed: afterHookedMethod11 privatefunc->result:privatefunc---300
01-09 04:53:39.748 20009-20009/com.kanxue.xposedhook01 I/Xposed: publicfunc is called!---changedbyxposedjava---888
01-09 04:53:39.748 20009-20009/com.kanxue.xposedhook01 I/Xposed: beforeHookedMethod getpersonname->person
01-09 04:53:39.749 20009-20009/com.kanxue.xposedhook01 I/Xposed: afterHookedMethod getpersonname->person
01-09 04:53:39.749 20009-20009/com.kanxue.xposedhook01 I/Xposed: afterHookedMethod publicfunc->result:changedbyxposedjava---888---privatefunc---300---person
01-09 04:53:39.749 20009-20009/com.kanxue.xposedhook01 I/xposed: changedbyxposed->afterHookedMethod
*/
public class person {
public String name;
public String sex;
public String getpersonname(String arg0){
return arg0;
}
}

public String getNickname() {
return nickname;
}

public static String getTeacher() {
return teacher;
}

public Student() {
name = "default";
id = "default";
age = 100;
}

public Student(String name) {
this.name = name;
id = "default";
}

public Student(String name, String id) {
this.name = name;
this.id = id;

}

public Student(String name, String id, int age, String teachername, String nickname) {
this.name = name;
this.id = id;
this.age = age;
teacher = teachername;
this.nickname = nickname;

}

public static String publicstaticfunc(String arg1, int arg2) {
String result = privatestaticfunc("privatestaticfunc", 200);
Log.i("Xposed","publicstaticfunc is called!"+"---"+arg1+"---"+arg2);
return arg1 + "---" + arg2 + "---" + result;
}

private static String privatestaticfunc(String arg1, int arg2) {
Log.i("Xposed","privatestaticfunc is called!"+"---"+arg1+"---"+arg2);
return arg1 + "---" + arg2;
}

public String publicfunc(String arg1, int arg2) {
String result = privatefunc("privatefunc", 300);
Log.i("Xposed","publicfunc is called!"+"---"+arg1+"---"+arg2);
person tmp=new person();
String tmpresult=tmp.getpersonname("person");
return arg1 + "---" + arg2 + "---" + result+"---"+tmpresult;
}

private String privatefunc(String arg1, int arg2) {
Log.i("Xposed","privatefunc is called!"+"---"+arg1+"---"+arg2);
return arg1 + "---" + arg2;
}

}

MainActivity

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
package com.kanxue.xposedhook01;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;

public class MainActivity extends AppCompatActivity {

public void printStudent(Student stu) {
Log.i("Xposed", stu.name + "--" + stu.id + "---" + stu.age + "---" + stu.getNickname() + "---" + stu.getTeacher());
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

Student astudent = new Student();
Student bstudent = new Student("xiaoming");
Student cstudent = new Student("xiaohua", "2020");
Student dstudent = new Student("xiaohong", "2010", 20, "teacher", "panda");


printStudent(astudent);
printStudent(bstudent);
printStudent(cstudent);
printStudent(dstudent);

// public static String publicstaticfunc(String arg1, int arg2) {
// return arg1 + "---" + arg2;
// }
//publicstaticfunc---100---privatestaticfunc---200
Log.i("xposed", Student.publicstaticfunc("publicstaticfunc", 100));

//
// private static String privatestaticfunc(String arg1, int arg2) {
// return arg1 + "---" + arg2;
// }
//publicfunc---500---privatefunc---300
Log.i("xposed", dstudent.publicfunc("publicfunc", 500));
//
// public String publicsfunc(String arg1, int arg2) {
// return arg1 + "---" + arg2;
// }

//
// private String privatefunc(String arg1, int arg2) {
// return arg1 + "---" + arg2;
// }


}
}

HookJavaFunction

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
173
174
175
176
177
178
179
180
181
182
183
184
185
package com.kanxue.xposed01;

import android.app.Application;
import android.util.Log;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;

public class HookJavaFunction implements IXposedHookLoadPackage {

@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
Log.i("Xposed01", loadPackageParam.packageName);
XposedBridge.log("HookJava->app packagename" + loadPackageParam.packageName);
if (loadPackageParam.packageName.equals("com.kanxue.xposedhook01")) {
XposedBridge.log("kanxue " + loadPackageParam.packageName);
/* public static de.robv.android.xposed.XC_MethodHook.Unhook findAndHookMethod(java.lang.Class<?> clazz, java.lang.String methodName, java.lang.Object... parameterTypesAndCallback) { *//* compiled code *//* }

public static de.robv.android.xposed.XC_MethodHook.Unhook findAndHookMethod(java.lang.String className, java.lang.ClassLoader classLoader, java.lang.String methodName, java.lang.Object... parameterTypesAndCallback) { *//* compiled code *//* }
*/
ClassLoader classLoader = loadPackageParam.classLoader;

XposedBridge.log("loadPackageParam.classLoader->" + classLoader);



Class StuClass = classLoader.loadClass("com.kanxue.xposedhook01.Student");


// public static String publicstaticfunc(String arg1, int arg2) {
// String result = privatestaticfunc("privatestaticfunc", 200);
// return arg1 + "---" + arg2 + "---" + result;
// }
//
XposedHelpers.findAndHookMethod(StuClass, "publicstaticfunc", String.class, int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Object[] objectarray=param.args;
String arg0= (String) objectarray[0];
int arg1= (int) objectarray[1];
objectarray[0]="changedbyxposedjava";
objectarray[1]=888;
XposedBridge.log("beforeHookedMethod publicstaticfunc->arg0:"+arg0+"---arg1:"+arg1);

}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);

String result= (String) param.getResult();
param.setResult("changedbyxposed->afterHookedMethod");
XposedBridge.log("afterHookedMethod publicstaticfunc->result:"+result);

}
});
XposedHelpers.findAndHookMethod(StuClass, "privatestaticfunc", String.class, int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Object[] objectarray = param.args;
String arg0 = (String) objectarray[0];
int arg1 = (int) objectarray[1];
objectarray[0] = "changedbyxposedjava";
objectarray[1] = 888;
XposedBridge.log("beforeHookedMethod privatestaticfunc->arg0:" + arg0 + "---arg1:" + arg1);

}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);

String result = (String) param.getResult();
param.setResult("changedbyxposed->afterHookedMethod");
XposedBridge.log("afterHookedMethod privatestaticfunc->result:" + result);

}
});
// private static String privatestaticfunc(String arg1, int arg2) {
// return arg1 + "---" + arg2;
// }
//
XposedHelpers.findAndHookMethod(StuClass, "publicfunc", String.class, int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Object[] objectarray = param.args;
String arg0 = (String) objectarray[0];
int arg1 = (int) objectarray[1];
objectarray[0] = "changedbyxposedjava";
objectarray[1] = 888;
XposedBridge.log("beforeHookedMethod publicfunc->arg0:" + arg0 + "---arg1:" + arg1);

}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);

String result = (String) param.getResult();
param.setResult("changedbyxposed->afterHookedMethod");
XposedBridge.log("afterHookedMethod publicfunc->result:" + result);

}
});
// public String publicfunc(String arg1, int arg2) {
// String result = privatefunc("privatefunc", 300);
// return arg1 + "---" + arg2 + "---" + result;
// }
//
///* XposedHelpers.findAndHookMethod(StuClass, "privatefunc", String.class, int.class, new XC_MethodHook() {
// @Override
// protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
// super.beforeHookedMethod(param);
// Object[] objectarray = param.args;
// String arg0 = (String) objectarray[0];
// int arg1 = (int) objectarray[1];
// XposedBridge.log("beforeHookedMethod privatefunc->arg0:" + arg0 + "---arg1:" + arg1);
//
// }
//
// @Override
// protected void afterHookedMethod(MethodHookParam param) throws Throwable {
// super.afterHookedMethod(param);
//
// String result = (String) param.getResult();
// XposedBridge.log("afterHookedMethod privatefunc->result:" + result);
//
// }
// });*/
// private String privatefunc(String arg1, int arg2) {
// return arg1 + "---" + arg2;
// }


XposedHelpers.findAndHookMethod("com.kanxue.xposedhook01.Student", loadPackageParam.classLoader, "privatefunc", String.class, int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Object[] objectarray = param.args;
String arg0 = (String) objectarray[0];
int arg1 = (int) objectarray[1];
XposedBridge.log("beforeHookedMethod11 privatefunc->arg0:" + arg0 + "---arg1:" + arg1);

}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);

String result = (String) param.getResult();
XposedBridge.log("afterHookedMethod11 privatefunc->result:" + result);

}
});

Class personClass = XposedHelpers.findClass("com.kanxue.xposedhook01.Student$person", loadPackageParam.classLoader);
XposedHelpers.findAndHookMethod(personClass, "getpersonname", String.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
XposedBridge.log("beforeHookedMethod getpersonname->" + param.args[0]);
}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
XposedBridge.log("afterHookedMethod getpersonname->" + param.getResult());
}
});
// public static java.lang.Class<?> findClass(java.lang.String className, java.lang.ClassLoader classLoader)
//Class StuClassByXposed=XposedHelpers.findClass("com.kanxue.xposedhook01.Student",classLoader);

}
}
}

HookJni

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
package com.kanxue.xposed01;

import android.os.Bundle;
import android.util.Log;

import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;

public class HookJni implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
Log.i("Xposed01", loadPackageParam.packageName);
XposedBridge.log("HookJava->app packagename" + loadPackageParam.packageName);
if (loadPackageParam.packageName.equals("com.kanxue.loaddex")) {

Class MainActivityClass=XposedHelpers.findClass("com.kanxue.loaddex.MainActivity",loadPackageParam.classLoader);

XposedHelpers.findAndHookMethod(MainActivityClass, "onCreate", Bundle.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
XposedBridge.log("beforeHookedMethod onCreate->"+param.args[0]);
}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
XposedBridge.log("afterHookedMethod onCreate->"+param.thisObject);
}
});
XposedHelpers.findAndHookMethod(MainActivityClass, "replaceClassloader", ClassLoader.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
XposedBridge.log("beforeHookedMethod replaceClassloader->Classloader:"+param.args[0]);
}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
XposedBridge.log("afterHookedMethod replaceClassloader->"+param.thisObject);
}
});

}

}
}


主动调用

  • 利用Java反射完成对类函数的调用
  • 使用Xposed的Api完成对类函数的调用

对于类静态函数直接调用
对于非静态函数,需要得到类的实例

HookActiveInvoke

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
package com.kanxue.xposed01;

import android.util.Log;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;

public class HookActiveInvoke implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
Log.i("XposedActiveInvoke", loadPackageParam.packageName);
XposedBridge.log("XposedActiveInvoke->app packagename" + loadPackageParam.packageName);
if (loadPackageParam.packageName.equals("com.kanxue.xposedhook01")) {
XposedBridge.log("kanxue " + loadPackageParam.packageName);

// public static String publicstaticfunc(String arg1, int arg2) {
// String result = privatestaticfunc("privatestaticfunc", 200);
// Log.i("Xposed","publicstaticfunc is called!");
// return arg1 + "---" + arg2 + "---" + result;
// }
//
ClassLoader classLoader = loadPackageParam.classLoader;
Class StuClass = classLoader.loadClass("com.kanxue.xposedhook01.Student");
Method publicstaticfunc_method = StuClass.getDeclaredMethod("publicstaticfunc", String.class, int.class);
publicstaticfunc_method.invoke(null, "InvokedByXposed", 100);


// public static java.lang.Object callMethod(java.lang.Object obj, java.lang.String methodName, java.lang.Object... args) { /* compiled code */ }
//
// public static java.lang.Object callMethod(java.lang.Object obj, java.lang.String methodName, java.lang.Class<?>[] parameterTypes, java.lang.Object... args) { /* compiled code */ }
//
// public static java.lang.Object callStaticMethod(java.lang.Class<?> clazz, java.lang.String methodName, java.lang.Object... args)
//
// public static java.lang.Object callStaticMethod(java.lang.Class<?> clazz, java.lang.String methodName, java.lang.Class<?>[] parameterTypes, java.lang.Object... args)

java.lang.Class<?>[] parameterTypes = {String.class, int.class};

XposedHelpers.callStaticMethod(StuClass, "publicstaticfunc", parameterTypes, "publicstaticfunc is called by XposedHelpers.callStaticMethod11", 100);

XposedHelpers.callStaticMethod(StuClass, "publicstaticfunc", "publicstaticfunc is called by XposedHelpers.callStaticMethod22", 200);

XposedHelpers.callStaticMethod(StuClass, "privatestaticfunc", parameterTypes, "privatestaticfunc is called by XposedHelpers.callStaticMethod11", 400);
XposedHelpers.callStaticMethod(StuClass, "privatestaticfunc", "privatestaticfunc is called by XposedHelpers.callStaticMethod22", 300);


Method privatestaticfunc_method = StuClass.getDeclaredMethod("privatestaticfunc", String.class, int.class);

privatestaticfunc_method.setAccessible(true);

String result = (String) privatestaticfunc_method.invoke(null, "privatestaticfuncIsInvokedByXposed", 200);
XposedBridge.log("privatestaticfuncIsInvokedByXposed->result:" + result);
// private static String privatestaticfunc(String arg1, int arg2) {
// Log.i("Xposed","privatestaticfunc is called!");
// return arg1 + "---" + arg2;
// }
// public static java.lang.Object callMethod(java.lang.Object obj, java.lang.String methodName, java.lang.Object... args) { /* compiled code */ }
//
// public static java.lang.Object callMethod(java.lang.Object obj, java.lang.String methodName, java.lang.Class<?>[] parameterTypes, java.lang.Object... args) { /* compiled code */ }


// public static java.lang.Object newInstance(java.lang.Class<?> clazz, java.lang.Object... args) { /* compiled code */ }
//
// public static java.lang.Object newInstance(java.lang.Class<?> clazz, java.lang.Class<?>[] parameterTypes, java.lang.Object... args) { /* compiled code */ }


Object StuObjByXposed = XposedHelpers.newInstance(StuClass, "StuObjByXposed.newInstance", "500");
String result1 = (String) XposedHelpers.callMethod(StuObjByXposed, "publicfunc", "publicfunc is called by XposedHelpers.callMethod", 125);
XposedBridge.log("publicfunc XposedHelpers.callMethod result->" + result1);

XposedHelpers.callMethod(StuObjByXposed, "privatefunc", "privatefunc is called by XposedHelpers.callMethod", 130);


Method publicfunc_method = StuClass.getDeclaredMethod("publicfunc", String.class, int.class);


// public Student(String name, String id) {
// this.name = name;
// this.id = id;
//
// }

// public Student(String name, String id)
Constructor StuCon = StuClass.getDeclaredConstructor(String.class, String.class);

Object StuObj = StuCon.newInstance("InstanceByXposed", "300");

publicfunc_method.invoke(StuObj, "publicfuncInvokedByXposed", 100);


// public String publicfunc(String arg1, int arg2) {
// String result = privatefunc("privatefunc", 300);
// Log.i("Xposed","publicfunc is called!"+"---"+arg1+"---"+arg2);
// person tmp=new person();
// String tmpresult=tmp.getpersonname("person");
// return arg1 + "---" + arg2 + "---" + result+"---"+tmpresult;
// }
//

Method privatefunc_method = StuClass.getDeclaredMethod("privatefunc", String.class, int.class);
privatefunc_method.setAccessible(true);
String privatefunc_result = (String) privatefunc_method.invoke(StuObj, "privatefuncInvokedByXposed", 200);
XposedBridge.log("privatefunc activeInvokeByXposed-->" + privatefunc_result);
// private String privatefunc(String arg1, int arg2) {
// Log.i("Xposed","privatefunc is called!"+"---"+arg1+"---"+arg2);
// return arg1 + "---" + arg2;
// }


//Student cstudent = new Student("xiaohua", "2020");
XposedHelpers.findAndHookConstructor(StuClass, String.class, String.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
XposedBridge.log("Student(String,String) is called beforeHookedMethod");
}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
XposedBridge.log("Student(String,String) is called afterHookedMethod");
Object cstudent=param.thisObject;
XposedHelpers.callMethod(cstudent,"publicfunc","publicfunc is called XposedHelpers.findAndHookConstructor",666);
XposedHelpers.callMethod(cstudent,"privatefunc","privatefunc is called XposedHelpers.findAndHookConstructor",888);
}
});


// public String getNickname() {
// return nickname;
// }

XposedHelpers.findAndHookMethod(StuClass, "getNickname", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Object obj=param.thisObject;
XposedHelpers.callMethod(obj,"publicfunc","beforeHookedMethod publicfunc is called XposedHelpers.callMethod",444);
XposedHelpers.callMethod(obj,"privatefunc","beforeHookedMethod privatefunc is called XposedHelpers.callMethod",333);

XposedBridge.log("getNickname is called beforeHookedMethod->"+obj);
}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Object obj=param.thisObject;
XposedHelpers.callMethod(obj,"publicfunc","afterHookedMethod publicfunc is called XposedHelpers.callMethod",222);
XposedHelpers.callMethod(obj,"privatefunc","afterHookedMethod privatefunc is called XposedHelpers.callMethod",111);

XposedBridge.log("getNickname is called afterHookedMethod->"+param.thisObject);
}
});

}
}
}


加壳App处理

HookJava

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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
package com.kanxue.xposed01;

import android.app.Application;
import android.os.Bundle;
import android.util.Log;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;

public class HookJava implements IXposedHookLoadPackage {
public static Field getClassField(ClassLoader classloader, String class_name,
String filedName) {

try {
Class obj_class = classloader.loadClass(class_name);//Class.forName(class_name);
Field field = obj_class.getDeclaredField(filedName);
field.setAccessible(true);
return field;
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;

}

public static Object getClassFieldObject(ClassLoader classloader, String class_name, Object obj,
String filedName) {

try {
Class obj_class = classloader.loadClass(class_name);//Class.forName(class_name);
Field field = obj_class.getDeclaredField(filedName);
field.setAccessible(true);
Object result = null;
result = field.get(obj);
return result;
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;

}

public static Object invokeStaticMethod(String class_name,
String method_name, Class[] pareTyple, Object[] pareVaules) {

try {
Class obj_class = Class.forName(class_name);
Method method = obj_class.getMethod(method_name, pareTyple);
return method.invoke(null, pareVaules);
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;

}

public static Object getFieldOjbect(String class_name, Object obj,
String filedName) {
try {
Class obj_class = Class.forName(class_name);
Field field = obj_class.getDeclaredField(filedName);
field.setAccessible(true);
return field.get(obj);
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NullPointerException e) {
e.printStackTrace();
}
return null;

}

public static ClassLoader getClassloader() {
ClassLoader resultClassloader = null;
Object currentActivityThread = invokeStaticMethod(
"android.app.ActivityThread", "currentActivityThread",
new Class[]{}, new Object[]{});
Object mBoundApplication = getFieldOjbect(
"android.app.ActivityThread", currentActivityThread,
"mBoundApplication");
Application mInitialApplication = (Application) getFieldOjbect("android.app.ActivityThread",
currentActivityThread, "mInitialApplication");
Object loadedApkInfo = getFieldOjbect(
"android.app.ActivityThread$AppBindData",
mBoundApplication, "info");
Application mApplication = (Application) getFieldOjbect("android.app.LoadedApk", loadedApkInfo, "mApplication");
resultClassloader = mApplication.getClassLoader();
return resultClassloader;
}

public void GetClassLoaderClasslist(ClassLoader classLoader) {
//private final DexPathList pathList;
//public static java.lang.Object getObjectField(java.lang.Object obj, java.lang.String fieldName)
XposedBridge.log("start dealwith classloader:" + classLoader);
Object pathListObj = XposedHelpers.getObjectField(classLoader, "pathList");
//private final Element[] dexElements;
Object[] dexElementsObj = (Object[]) XposedHelpers.getObjectField(pathListObj, "dexElements");
for (Object i : dexElementsObj) {
//private final DexFile dexFile;
Object dexFileObj = XposedHelpers.getObjectField(i, "dexFile");
//private Object mCookie;
Object mCookieObj = XposedHelpers.getObjectField(dexFileObj, "mCookie");
//private static native String[] getClassNameList(Object cookie);
// public static java.lang.Object callStaticMethod(java.lang.Class<?> clazz, java.lang.String methodName, java.lang.Object... args) { /* compiled code */ }
Class DexFileClass = XposedHelpers.findClass("dalvik.system.DexFile", classLoader);

String[] classlist = (String[]) XposedHelpers.callStaticMethod(DexFileClass, "getClassNameList", mCookieObj);
for (String classname : classlist) {
XposedBridge.log(dexFileObj + "---" + classname);
}
}
XposedBridge.log("end dealwith classloader:" + classLoader);

}

@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
Log.i("Xposed01", loadPackageParam.packageName);
XposedBridge.log("HookJava->app packagename" + loadPackageParam.packageName);
if (loadPackageParam.packageName.equals("com.kanxue.xposedhook01")) {
XposedBridge.log("kanxue " + loadPackageParam.packageName);
/* public static de.robv.android.xposed.XC_MethodHook.Unhook findAndHookMethod(java.lang.Class<?> clazz, java.lang.String methodName, java.lang.Object... parameterTypesAndCallback) { *//* compiled code *//* }

public static de.robv.android.xposed.XC_MethodHook.Unhook findAndHookMethod(java.lang.String className, java.lang.ClassLoader classLoader, java.lang.String methodName, java.lang.Object... parameterTypesAndCallback) { *//* compiled code *//* }
*/
ClassLoader classLoader = loadPackageParam.classLoader;

XposedBridge.log("loadPackageParam.classLoader->" + classLoader);
/*
01-09 05:26:25.678 29011-29011/? I/Xposed: loadPackageParam.classLoader->dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.kanxue.xposedhook01-1/base.apk"],nativeLibraryDirectories=[/data/app/com.kanxue.xposedhook01-1/lib/arm, /vendor/lib, /system/lib]]]
01-09 05:26:25.679 29011-29011/? I/Xposed: start dealwith classloader:dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.kanxue.xposedhook01-1/base.apk"],nativeLibraryDirectories=[/data/app/com.kanxue.xposedhook01-1/lib/arm, /vendor/lib, /system/lib]]]
01-09 05:26:25.681 29011-29011/? I/Xposed: /data/app/com.kanxue.xposedhook01-1/base.apk---com.qihoo.util.Configuration
01-09 05:26:25.681 29011-29011/? I/Xposed: /data/app/com.kanxue.xposedhook01-1/base.apk---com.qihoo.util.DtcLoader
01-09 05:26:25.681 29011-29011/? I/Xposed: /data/app/com.kanxue.xposedhook01-1/base.apk---com.qihoo.util.QHDialog
01-09 05:26:25.681 29011-29011/? I/Xposed: /data/app/com.kanxue.xposedhook01-1/base.apk---com.qihoo.util.ᵢˋ
01-09 05:26:25.681 29011-29011/? I/Xposed: /data/app/com.kanxue.xposedhook01-1/base.apk---com.qihoo.util.ᵢˎ
01-09 05:26:25.681 29011-29011/? I/Xposed: /data/app/com.kanxue.xposedhook01-1/base.apk---com.qihoo.util.ᵢˏ
01-09 05:26:25.681 29011-29011/? I/Xposed: /data/app/com.kanxue.xposedhook01-1/base.apk---com.stub.StubApp
01-09 05:26:25.681 29011-29011/? I/Xposed: end dealwith classloader:dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.kanxue.xposedhook01-1/base.apk"],nativeLibraryDirectories=[/data/app/com.kanxue.xposedhook01-1/lib/arm, /vendor/lib, /system/lib]]]
*/

GetClassLoaderClasslist(classLoader);

ClassLoader parent = classLoader.getParent();
while (parent != null) {
XposedBridge.log("parent->" + parent);
if (parent.toString().contains("BootClassLoader")) {

} else {
GetClassLoaderClasslist(parent);
}
parent = parent.getParent();
}

/*
* 01-09 05:05:34.284 24462-24462/? I/Xposed: loadPackageParam.classLoader->dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.kanxue.xposedhook01-1/base.apk"],nativeLibraryDirectories=[/data/app/com.kanxue.xposedhook01-1/lib/arm, /vendor/lib, /system/lib]]]
01-09 05:05:34.284 24462-24462/? I/Xposed: parent->dalvik.system.PathClassLoader[DexPathList[[dex file "/data/dalvik-cache/xposed_XResourcesSuperClass.dex", dex file "/data/dalvik-cache/xposed_XTypedArraySuperClass.dex"],nativeLibraryDirectories=[/vendor/lib, /system/lib]]]
01-09 05:05:34.284 24462-24462/? I/Xposed: parent->java.lang.BootClassLoader@6c4cdad
* */


Class StubAppClass=XposedHelpers.findClass("com.stub.StubApp",loadPackageParam.classLoader);
Method[] methods=StubAppClass.getDeclaredMethods();
for(Method i:methods){
XposedBridge.log("com.stub.StubApp->"+i);
}
XposedHelpers.findAndHookMethod("com.stub.StubApp", loadPackageParam.classLoader, "onCreate", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
XposedBridge.log("com.stub.StubApp->onCreate beforeHookedMethod");
}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
XposedBridge.log("com.stub.StubApp->onCreate afterHookedMethod");

ClassLoader finalClassLoader=getClassloader();
XposedBridge.log("finalClassLoader->" + finalClassLoader);
GetClassLoaderClasslist(finalClassLoader);
XposedHelpers.findAndHookMethod("com.kanxue.xposedhook01.Student", finalClassLoader, "privatefunc", String.class, int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Object[] objectarray = param.args;
String arg0 = (String) objectarray[0];
int arg1 = (int) objectarray[1];
XposedBridge.log("beforeHookedMethod11 privatefunc->arg0:" + arg0 + "---arg1:" + arg1);

}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);

String result = (String) param.getResult();
XposedBridge.log("afterHookedMethod11 privatefunc->result:" + result);

}
});

Class personClass = XposedHelpers.findClass("com.kanxue.xposedhook01.Student$person", finalClassLoader);
XposedHelpers.findAndHookMethod(personClass, "getpersonname", String.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
XposedBridge.log("beforeHookedMethod getpersonname->" + param.args[0]);
}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
XposedBridge.log("afterHookedMethod getpersonname->" + param.getResult());
}
});


}
});

/*Class StuClass = classLoader.loadClass("com.kanxue.xposedhook01.Student");*/


// public static String publicstaticfunc(String arg1, int arg2) {
// String result = privatestaticfunc("privatestaticfunc", 200);
// return arg1 + "---" + arg2 + "---" + result;
// }
//
/* XposedHelpers.findAndHookMethod(StuClass, "publicstaticfunc", String.class, int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Object[] objectarray=param.args;
String arg0= (String) objectarray[0];
int arg1= (int) objectarray[1];
objectarray[0]="changedbyxposedjava";
objectarray[1]=888;
XposedBridge.log("beforeHookedMethod publicstaticfunc->arg0:"+arg0+"---arg1:"+arg1);

}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);

String result= (String) param.getResult();
param.setResult("changedbyxposed->afterHookedMethod");
XposedBridge.log("afterHookedMethod publicstaticfunc->result:"+result);

}
});*/
/*XposedHelpers.findAndHookMethod(StuClass, "privatestaticfunc", String.class, int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Object[] objectarray = param.args;
String arg0 = (String) objectarray[0];
int arg1 = (int) objectarray[1];
objectarray[0] = "changedbyxposedjava";
objectarray[1] = 888;
XposedBridge.log("beforeHookedMethod privatestaticfunc->arg0:" + arg0 + "---arg1:" + arg1);

}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);

String result = (String) param.getResult();
param.setResult("changedbyxposed->afterHookedMethod");
XposedBridge.log("afterHookedMethod privatestaticfunc->result:" + result);

}
});*/
// private static String privatestaticfunc(String arg1, int arg2) {
// return arg1 + "---" + arg2;
// }
//
/*
XposedHelpers.findAndHookMethod(StuClass, "publicfunc", String.class, int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Object[] objectarray = param.args;
String arg0 = (String) objectarray[0];
int arg1 = (int) objectarray[1];
objectarray[0] = "changedbyxposedjava";
objectarray[1] = 888;
XposedBridge.log("beforeHookedMethod publicfunc->arg0:" + arg0 + "---arg1:" + arg1);

}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);

String result = (String) param.getResult();
param.setResult("changedbyxposed->afterHookedMethod");
XposedBridge.log("afterHookedMethod publicfunc->result:" + result);

}
});
*/
// public String publicfunc(String arg1, int arg2) {
// String result = privatefunc("privatefunc", 300);
// return arg1 + "---" + arg2 + "---" + result;
// }
//
/* XposedHelpers.findAndHookMethod(StuClass, "privatefunc", String.class, int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Object[] objectarray = param.args;
String arg0 = (String) objectarray[0];
int arg1 = (int) objectarray[1];
XposedBridge.log("beforeHookedMethod privatefunc->arg0:" + arg0 + "---arg1:" + arg1);

}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);

String result = (String) param.getResult();
XposedBridge.log("afterHookedMethod privatefunc->result:" + result);

}
});*/
// private String privatefunc(String arg1, int arg2) {
// return arg1 + "---" + arg2;
// }


/* XposedHelpers.findAndHookMethod("com.kanxue.xposedhook01.Student", loadPackageParam.classLoader, "privatefunc", String.class, int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Object[] objectarray = param.args;
String arg0 = (String) objectarray[0];
int arg1 = (int) objectarray[1];
XposedBridge.log("beforeHookedMethod11 privatefunc->arg0:" + arg0 + "---arg1:" + arg1);

}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);

String result = (String) param.getResult();
XposedBridge.log("afterHookedMethod11 privatefunc->result:" + result);

}
});

Class personClass = XposedHelpers.findClass("com.kanxue.xposedhook01.Student$person", loadPackageParam.classLoader);
XposedHelpers.findAndHookMethod(personClass, "getpersonname", String.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
XposedBridge.log("beforeHookedMethod getpersonname->" + param.args[0]);
}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
XposedBridge.log("afterHookedMethod getpersonname->" + param.getResult());
}
});
*/// public static java.lang.Class<?> findClass(java.lang.String className, java.lang.ClassLoader classLoader)
//Class StuClassByXposed=XposedHelpers.findClass("com.kanxue.xposedhook01.Student",classLoader);

}
}
}

之前Xposed的Hook代码只对一些未加壳的App
现在直接从loadPackageParam的classLoader找class是会出现classNotFound
如果加壳了的话, AdnroidManifest.xml中的application会出现android:name="com.stub.StubApp"
https://bbs.pediy.com/thread-252630.htm#msg_header_h2_1

Application的实现类StubApp的attachBaseContext和onCreate要做的事情就是如上图

所以我们要劫持StubApp的OnCreate之后它利用反射设置的ActivityThread里面的相关classLoader
这个classLoader才是加载应用载入的dex代码, 有这个classLoader我们就可以继续寻找应用代码的实例和hook相关功能

还有一点要说明, 可能这些加壳厂商自己定制了classLoader而不是使用了BaseClassLoader,所以这点可能有时候会有问题

HookBelo

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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
package com.kanxue.xposed01;

import android.app.Application;
import android.os.Bundle;
import android.util.Log;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;

public class HookLebo implements IXposedHookLoadPackage {
public static Field getClassField(ClassLoader classloader, String class_name,
String filedName) {

try {
Class obj_class = classloader.loadClass(class_name);//Class.forName(class_name);
Field field = obj_class.getDeclaredField(filedName);
field.setAccessible(true);
return field;
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;

}

public static Object getClassFieldObject(ClassLoader classloader, String class_name, Object obj,
String filedName) {

try {
Class obj_class = classloader.loadClass(class_name);//Class.forName(class_name);
Field field = obj_class.getDeclaredField(filedName);
field.setAccessible(true);
Object result = null;
result = field.get(obj);
return result;
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;

}

public static Object invokeStaticMethod(String class_name,
String method_name, Class[] pareTyple, Object[] pareVaules) {

try {
Class obj_class = Class.forName(class_name);
Method method = obj_class.getMethod(method_name, pareTyple);
return method.invoke(null, pareVaules);
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;

}

public static Object getFieldOjbect(String class_name, Object obj,
String filedName) {
try {
Class obj_class = Class.forName(class_name);
Field field = obj_class.getDeclaredField(filedName);
field.setAccessible(true);
return field.get(obj);
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NullPointerException e) {
e.printStackTrace();
}
return null;

}

public static ClassLoader getClassloader() {
ClassLoader resultClassloader = null;
Object currentActivityThread = invokeStaticMethod(
"android.app.ActivityThread", "currentActivityThread",
new Class[]{}, new Object[]{});
Object mBoundApplication = getFieldOjbect(
"android.app.ActivityThread", currentActivityThread,
"mBoundApplication");
Application mInitialApplication = (Application) getFieldOjbect("android.app.ActivityThread",
currentActivityThread, "mInitialApplication");
Object loadedApkInfo = getFieldOjbect(
"android.app.ActivityThread$AppBindData",
mBoundApplication, "info");
Application mApplication = (Application) getFieldOjbect("android.app.LoadedApk", loadedApkInfo, "mApplication");
resultClassloader = mApplication.getClassLoader();
return resultClassloader;
}

public void GetClassLoaderClasslist(ClassLoader classLoader) {
//private final DexPathList pathList;
//public static java.lang.Object getObjectField(java.lang.Object obj, java.lang.String fieldName)
XposedBridge.log("start dealwith classloader:" + classLoader);
Object pathListObj = XposedHelpers.getObjectField(classLoader, "pathList");
//private final Element[] dexElements;
Object[] dexElementsObj = (Object[]) XposedHelpers.getObjectField(pathListObj, "dexElements");
for (Object i : dexElementsObj) {
//private final DexFile dexFile;
Object dexFileObj = XposedHelpers.getObjectField(i, "dexFile");
//private Object mCookie;
Object mCookieObj = XposedHelpers.getObjectField(dexFileObj, "mCookie");
//private static native String[] getClassNameList(Object cookie);
// public static java.lang.Object callStaticMethod(java.lang.Class<?> clazz, java.lang.String methodName, java.lang.Object... args) { /* compiled code */ }
Class DexFileClass = XposedHelpers.findClass("dalvik.system.DexFile", classLoader);

String[] classlist = (String[]) XposedHelpers.callStaticMethod(DexFileClass, "getClassNameList", mCookieObj);
for (String classname : classlist) {
XposedBridge.log(dexFileObj + "---" + classname);
}
}
XposedBridge.log("end dealwith classloader:" + classLoader);

}

@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
Log.i("Xposed01", loadPackageParam.packageName);
XposedBridge.log("HookLebo->app packagename" + loadPackageParam.packageName);
if (loadPackageParam.packageName.equals("com.f0208.lebo")) {
XposedBridge.log("kanxue " + loadPackageParam.packageName);
Class StubAppClass=XposedHelpers.findClass("com.f0208.lebo.MyWrapperProxyApplication",loadPackageParam.classLoader);
Method[] methods=StubAppClass.getDeclaredMethods();
for(Method i:methods){
XposedBridge.log("com.f0208.lebo.MyWrapperProxyApplication->"+i);
}
XposedHelpers.findAndHookMethod("com.f0208.lebo.MyWrapperProxyApplication", loadPackageParam.classLoader, "onCreate", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
XposedBridge.log("com.f0208.lebo.MyWrapperProxyApplication->onCreate beforeHookedMethod");
}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
XposedBridge.log("com.f0208.lebo.MyWrapperProxyApplication->onCreate afterHookedMethod");

ClassLoader finalClassLoader=getClassloader();
XposedBridge.log("finalClassLoader->" + finalClassLoader);
XposedHelpers.findAndHookMethod("com.zw.lebo.MainActivity", finalClassLoader, "onCreate", Bundle.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
XposedBridge.log("beforeHookedMethod com.zw.lebo.MainActivity.onCreate");

}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
XposedBridge.log("afterHookedMethod com.zw.lebo.MainActivity.onCreate");

}
});



}
});


}
}
}

除此之外还有一种非加固壳的, 只是利用了DexClassLoader进行了热加载或者外部加载Dex文件
这种也可以Hook, DexClassLoader的构造函数, 在用新出的DexClassLoader实例进行loadClass或者查找DexFile文件内容


so中函数处理

对so库中某个函数进行hook
第一步用Xposed进行hook的是System.loadlibrary
如果能hook成功的话, 接下来就是直接再利用System.load加载我们的hook代码的so
然后把hook函数写在JNI_OnLoad以前的_init方法或者jni_init方法

System.load打破了原来的ClassLoader, 所以后期又推荐使用Runtime的load

1
2
3
public static void loadLibrary(String libName) {
Runtime.getRuntime().loadLibrary(libName, VMStack.getCallingClassLoader());
}

所以hook代码又变成了这样

1
2
XposedHelpers.findAndHookMethod("java.lang.Runtime", loadPackageParam.classLoader, "loadLibrary", 
......

MainActivity

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
package com.kanxue.hookso;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// Example of a call to a native method
TextView tv = findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
}

/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();
}

native-lib

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
#include <jni.h>
#include <string>
#include <android/log.h>
#include <dlfcn.h>
#include <unistd.h>
#include <fcntl.h>

extern "C" {
#include "include/inlineHook.h"
}
void *(*old_strstr)(char *, char *) = nullptr;

void *new_strstr(char *arg0, char *arg1) {
__android_log_print(4, "hookso", "strstr is called,arg1:%s,arg2:%s", arg0, arg1);
if (strcmp(arg1, "hookso") == 0) {
int a = 1;
return &a;
} else {
void *result = old_strstr(arg0, arg1);
return result;
};
}

void starthooklibc() {

void *libchandle = dlopen("libc.so", RTLD_NOW);
void *strstr_addr = dlsym(libchandle, "strstr");
if (registerInlineHook((uint32_t) strstr_addr, (uint32_t) new_strstr,
(uint32_t **) &old_strstr) !=
ELE7EN_OK) {
return;
}
if (inlineHook((uint32_t) strstr_addr) == ELE7EN_OK) {
__android_log_print(4, "hookso", "hook libc.so->strstr success!");
//return -1;
}

}

extern "C" void _init(void) {
__android_log_print(4,"kanxuetest","go into _init");
starthooklibc();
}
__attribute__ ((constructor(10), visibility ("hidden"))) void initarray_4(void) {
__android_log_print(4,"kanxuetest","go into initarray_4");
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = nullptr;
jint result = -1;
__android_log_print(4,"kanxuetest","go into JNI_OnLoad");
//starthooklibc();
if ((vm)->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
printf("err!!");
return JNI_ERR;
}
return JNI_VERSION_1_6;
}

extern "C" JNIEXPORT jstring JNICALL
Java_com_kanxue_hookso_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
__android_log_print(4,"kanxuetest","go into Java_com_kanxue_hookso_MainActivity_stringFromJNI");
std::string hello = "Hello from C++";
if(strstr("aaaaabbbbb","hookso")!= nullptr){
__android_log_print(4,"kanxuetest","find hookso");
}else{
__android_log_print(4,"kanxuetest","not find hookso");
}
return env->NewStringUTF(hello.c_str());
}

XposedHookSo

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
package com.kanxue.xposed01;

import android.util.Log;

import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;

public class XposedHookSo implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
Log.i("XposedHookSo", loadPackageParam.packageName);
XposedBridge.log("XposedHookSo->app packagename" + loadPackageParam.packageName);
if (loadPackageParam.packageName.equals("com.kanxue.xposedhookso")) {
XposedBridge.log("XposedHookSo " + loadPackageParam.packageName);


XposedHelpers.findAndHookMethod("java.lang.Runtime", loadPackageParam.classLoader, "loadLibrary", String.class,ClassLoader.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
String soname= (String) param.args[0];
XposedBridge.log("beforeHookedMethod Runtime.load("+soname+","+param.args[1]+")");
}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
String soname= (String) param.args[0];
XposedBridge.log("afterHookedMethod Runtime.load("+soname+")");
if(soname.contains("native-lib")){
System.load("/data/data/com.kanxue.xposedhookso/files/hookso.so");
}


}
});



}
}
}


so中64位函数处理

上面的hook框架inline hook在nexus5的机器上的Android 6.0是没有问题的
但是在android 8.0 以上的64位手机是有问题的;

主要是把build.gradle中的部分属性配置注释掉就会有64位的平台ndk(这里默认就是4种平台的)

1
2
3
ndk{
abiFilters 'armeabi-v7a'// 这里注释掉,就会生成4种平台
}

默认情况下,cmake会输出 4 种 ABI("armeabi-v7a" , "arm64-v8a", "x86", "x86_64")

64位的inline hook库, 有个EdXposed所使用的SandHook

native-lib

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
#include <jni.h>
#include <string>
#include "sandhook_native.h"
#include "utils/log.h"
#include <android/log.h>
#include <unistd.h>
#include <cassert>
#include <cstdlib>

#include "includes/elf.h"

class instance;

void *(*old_strstr)(char *, char *) = nullptr;

void *new_strstr(char *arg0, char *arg1) {
__android_log_print(4, "hooksoarm64", "strstr is called,arg1:%s,arg2:%s", arg0, arg1);
if (strcmp(arg1, "hookso") == 0) {
int a = 1;
return &a;
} else {
void *result = old_strstr(arg0, arg1);
return result;
};
}

void starthooklibc() {
if (sizeof(void *) == 8) {
const char *libcpath = "/system/lib64/libc.so";
old_strstr = reinterpret_cast<void *(*)(char *, char *)>(SandInlineHookSym(libcpath,
"strstr",
reinterpret_cast<void *>(new_strstr)));
} else {
const char *libcpath = "/system/lib/libc.so";
old_strstr = reinterpret_cast<void *(*)(char *, char *)>(SandInlineHookSym(libcpath,
"strstr",
reinterpret_cast<void *>(new_strstr)));
}

}

bool testhook(const char *content) {

if (strstr(content, "hookso") != nullptr) {
__android_log_print(4, "xposedhookso", "find hookso");
return true;
} else {
__android_log_print(4, "xposedhookso", "not find hookso");
}
return false;

}

int (*testhookfunctin)(const char *) = nullptr;

extern "C" void _init(void) {
LOGD("go into _init");
//starthooklibc();
}
extern "C" jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
LOGD("go into JNI_OnLoad");
starthooklibc();

return JNI_VERSION_1_6;
}
extern "C" JNIEXPORT jstring JNICALL
Java_com_kanxue_xposeehooksobysandhook_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}

这里有个坑点, sandHook必须早于starthooklibc, 所以starthooklibc必须不能出现在_init,而是放置到JNI_OnLoad


so中函数主动调用

这里的函数偏移都是从IDA中看到的

Ida的Edit->Segments->Rebase program填写ImageValue

thumb需要奇数 + 1

activecalltesthook

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
#include <jni.h>
#include <string>
#include "sandhook_native.h"
#include "utils/log.h"
#include <android/log.h>
#include <unistd.h>
#include <cassert>
#include <cstdlib>

#include "includes/elf.h"

class instance;

void *(*old_strstr)(char *, char *) = nullptr;

void *new_strstr(char *arg0, char *arg1) {
__android_log_print(4, "hooksoarm64", "strstr is called,arg1:%s,arg2:%s", arg0, arg1);
if (strcmp(arg1, "hookso") == 0) {
int a = 1;
return &a;
} else {
void *result = old_strstr(arg0, arg1);
return result;
};
}

void starthooklibc() {
if (sizeof(void *) == 8) {
const char *libcpath = "/system/lib64/libc.so";
old_strstr = reinterpret_cast<void *(*)(char *, char *)>(SandInlineHookSym(libcpath,
"strstr",
reinterpret_cast<void *>(new_strstr)));
} else {
const char *libcpath = "/system/lib/libc.so";
old_strstr = reinterpret_cast<void *(*)(char *, char *)>(SandInlineHookSym(libcpath,
"strstr",
reinterpret_cast<void *>(new_strstr)));
}

}

bool testhook(const char *content) {

if (strstr(content, "hookso") != nullptr) {
__android_log_print(4, "xposedhookso", "find hookso");
return true;
} else {
__android_log_print(4, "xposedhookso", "not find hookso");
}
return false;

}

int (*testhookfunctin)(const char *) = nullptr;

void activecalltesthook() {
typedef int (*testhook)(const char *a1);

testhook testhookfunction = nullptr;
//得到函数地址
/* extern "C"
EXPORT void* SandGetModuleBase(const char* so);*/

void *libnativebase = SandGetModuleBase("libnative-lib.so");
//thumb
if(sizeof(void*)==4){
unsigned long tmpaddr = (unsigned long) libnativebase + 0x8B4C + 1;
// 这个0x8B4C是从ide这边来看到的
void *testhookaddr = reinterpret_cast<void *>(tmpaddr);
testhookfunction = reinterpret_cast<testhook>(testhookaddr);
}else{
unsigned long tmpaddr = (unsigned long) libnativebase + 0xf67c;
void *testhookaddr = reinterpret_cast<void *>(tmpaddr);
testhookfunction = reinterpret_cast<testhook>(testhookaddr);
LOGD("libnative-lib.so base:%p,testfuncaddr:%p",libnativebase,(void*)tmpaddr);
}

int result1 = testhookfunction("aaaaahookso");

LOGD("testhookfunction(\"aaaaahookso\"); return:%d", result1);

int result2 = testhookfunction("aaaabbbbb");
LOGD("testhookfunction(\"aaaabbbbb\"); return:%d", result2);


}

extern "C" void _init(void) {
LOGD("go into _init");
//starthooklibc();
}
extern "C" jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
LOGD("go into JNI_OnLoad");
//starthooklibc();
activecalltesthook();

return JNI_VERSION_1_6;
}
extern "C" JNIEXPORT jstring JNICALL
Java_com_kanxue_xposeehooksobysandhook_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}


Xposed指纹检测及定制反检测

Xposed都有哪些指纹

  • Xposed插件的管理: XposedInstaller
  • Xposed对函数Hook的根本原理: Java函数变native
    hook住检测方法,使其检测失效掉
  • Xposed框架拥有的大量Api
  • Xposed框架的特定文件
    system/lib/libxposed_art.so
    system/xposed.prop
    system/framework/xposedBridge.jar
  • 等等

Github上检测Xposed的demo: XposedChecker

Xposed的组成

  • XposedInstaller:完整的AndroidStudio工程,用于管理Xposed框架的插件
  • Xposed: 对Zygote进程定制,能够实现对接下来任何一个App进程的hook
  • android_art: 用于支持对Java类函数hook而对Art源码进行的一系列定制
  • XposedBridge: Xposed的Java部分, 可单独作为AndroidStudio工程进行编译
  • XposedTools: 用于编译和打包Xposed框架

XposedChecker#MainActivity

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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
public class MainActivity extends AppCompatActivity {
private static final String[] CHECK_ITEM = {
"载入Xposed工具类",
"寻找特征动态链接库",
"代码堆栈寻找调起者",
"检测Xposed安装情况",
"判定系统方法调用钩子",
"检测虚拟Xposed环境",
"寻找Xposed运行库文件",
"内核查找Xposed链接库",
"环境变量特征字判断",
};
private static final String[] ROOT_STATUS = {"出错", "未发现Root", "发现Root"};

private ArrayList<Integer> status = new ArrayList<>();
private static final int ALL_ALLOW = 0777;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTitle("Xposed Checker");
try {
FutureTask futureTask = new FutureTask<>(new UnpackThread());
new Thread(futureTask).start();
futureTask.get();

futureTask = new FutureTask<>(new CheckThread());
new Thread(futureTask).start();
futureTask.get();
}
catch (Exception e) {
e.printStackTrace();
}
setContentView(R.layout.activity_main);
final TextView textView = (TextView) findViewById(R.id.a);
final ListView listView = (ListView) findViewById(R.id.b);
final TextView about = (TextView) findViewById(R.id.about);
final TextView donation = (TextView) findViewById(R.id.donation);
if (about != null) {
about.getPaint().setAntiAlias(true);
about.getPaint().setUnderlineText(true);
}
if (donation != null) {
donation.getPaint().setAntiAlias(true);
donation.getPaint().setUnderlineText(true);
}

if (listView != null) {
listView.setAdapter(new BaseAdapter() {
@Override
public int getCount() {
return CHECK_ITEM.length + 1;
}

@Override
public Object getItem(int position) {
return position;
}

@Override
public long getItemId(int position) {
return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
RelativeLayout layout = (RelativeLayout) LayoutInflater.from(MainActivity.this).inflate(R.layout.items, null);
TextView textView1 = (TextView) layout.findViewById(R.id.check_item);
TextView textView2 = (TextView) layout.findViewById(R.id.check_result);
int itemStatus = status.get(position);
boolean pass = itemStatus == 0;
if (position == CHECK_ITEM.length) {
textView1.setText(R.string.root_check);
textView2.setText(ROOT_STATUS[itemStatus + 1]);
} else {
textView1.setText(CHECK_ITEM[position]);
textView2.setText((pass ? getString(R.string.item_no_xposed) : getString(R.string.item_found_xposed)));
}
textView2.setTextColor(pass ? Color.GREEN : Color.RED);
return layout;
}
});
}

int checkCode = 0;
for (int i = 0; i < CHECK_ITEM.length; ++i) {
checkCode += status.get(i);
}
if (textView != null) {
if (checkCode > 0) {

textView.setTextColor(Color.RED);

textView.setText(String.format(getString(R.string.found_xposed), checkCode, CHECK_ITEM.length));
} else {
textView.setTextColor(Color.GREEN);
textView.setText(R.string.no_xposed);
}
}
}

private class CheckThread implements Callable<Void> {

@Override
public Void call() throws Exception {
status.clear();
for (int i = 0; i <= CHECK_ITEM.length; i++) {
Method method = MainActivity.class.getDeclaredMethod("check" + (i + 1));
method.setAccessible(true);
try {
status.add((int) method.invoke(MainActivity.this));
}
catch (Throwable e) {
status.add(0);
}
}
return null;
}
}

public void about(View view) {
new AlertDialog.Builder(this).setTitle("关于")
.setMessage(String.format(getString(R.string.about), Process.myPid()))
.setPositiveButton("我知道了", null)
.show();
}

public void donation(View view) {
new AlertDialog.Builder(this).setTitle("关于捐赠")
.setMessage(getString(R.string.donation))
.setPositiveButton("支付宝捐赠", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (!AlipayDonate.startAlipayClient(MainActivity.this, "fkx04282fwosw4vwrfnsr82")) {
Toast.makeText(MainActivity.this, "朋友你看起来大概是没有安装支付宝...", Toast.LENGTH_LONG).show();
}

}
})
.show();
}

@Keep
private int check1() {

return testClassLoader() || testUseClassDirectly() ? 1 : 0;
}

private boolean testClassLoader() {
try {
ClassLoader.getSystemClassLoader()
.loadClass("de.robv.android.xposed.XposedHelpers");

return true;
}
catch (ClassNotFoundException e) {
e.printStackTrace();
}
return false;
}

private boolean testUseClassDirectly() {
try {
XposedBridge.log("fuck wechat");
return true;
}
catch (Throwable e) {
e.printStackTrace();
}
return false;
}

@Keep
private int check2() {
return checkContains("XposedBridge") ? 1 : 0;
}

@Keep
private int check3() {
try {
throw new Exception();
}
catch (Exception e) {
StackTraceElement[] arrayOfStackTraceElement = e.getStackTrace();
for (StackTraceElement s : arrayOfStackTraceElement) {
if ("de.robv.android.xposed.XposedBridge".equals(s.getClassName())) {
return 1;
}
}
return 0;
}
}

@Keep
private int check4() {
try {
List<PackageInfo> list = getPackageManager().getInstalledPackages(0);
for (PackageInfo info : list) {
if ("de.robv.android.xposed.installer".equals(info.packageName)) {
return 1;
}
if ("io.va.exposed".equals(info.packageName)) {
return 1;
}
}
}
catch (Throwable ignored) {

}
return 0;
}

@Keep
private int check5() {
try {
Method method = Throwable.class.getDeclaredMethod("getStackTrace");
return Modifier.isNative(method.getModifiers()) ? 1 : 0;
}
catch (NoSuchMethodException e) {
e.printStackTrace();
}
return 0;
}

@Keep
private int check6() {
return System.getProperty("vxp") != null ? 1 : 0;
}


/**
* @param paramString check string
* @return whether check string is found in maps
*/
public static boolean checkContains(String paramString) {
try {
HashSet<String> localObject = new HashSet<>();
// 读取maps文件信息
BufferedReader localBufferedReader =
new BufferedReader(new FileReader("/proc/" + Process.myPid() + "/maps"));
while (true) {
String str = localBufferedReader.readLine();
if (str == null) {
break;
}
localObject.add(str.substring(str.lastIndexOf(" ") + 1));
}
//应用程序的链接库不可能是空,除非是高于7.0。。。
if (localObject.isEmpty() && Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
return true;
}
localBufferedReader.close();
for (String aLocalObject : localObject) {
if (aLocalObject.contains(paramString)) {
return true;
}
}
}
catch (Throwable ignored) {
}
return false;
}

@Keep
private int check7() {
CommandResult commandResult = Shell.run("ls /system/lib");
return commandResult.isSuccessful() ? commandResult.getStdout().contains("xposed") ? 1 : 0 : 0;
}

@Keep
private int check8() {
CommandResult commandResult = Shell.run(getFilesDir().getAbsolutePath() + "/checkman " + Process.myPid());
return commandResult.isSuccessful() ? 1 : 0;
}

@Keep
private int check9() {
return System.getenv("CLASSPATH").contains("XposedBridge") ? 1 : 0;
}

@Keep
private int check10() {
try {
return RootCheckerUtils.detect(this) ? 1 : 0;
}
catch (Throwable t) {
t.printStackTrace();
return -1;
}
}

private class UnpackThread implements Callable<Void> {

@Override
public Void call() throws Exception {
if (!new File(getFilesDir().getAbsolutePath() + "/checkman").exists()) {
InputStream inputStream = getAssets().open("checkman");
OutputStream outputStream = openFileOutput("checkman", MODE_PRIVATE);
int bit;
while ((bit = inputStream.read()) != -1) {
outputStream.write(bit);
}
}
setFilePermissions(getFilesDir(), ALL_ALLOW, -1, -1);
setFilePermissions(getFilesDir().getAbsolutePath() + "/checkman", ALL_ALLOW, -1, -1);
return null;
}

/**
* 修改文件权限
* setFilePermissions(file, 0777, -1, -1);
*/
boolean setFilePermissions(File file, int chmod, int uid, int gid) {
if (file != null) {
Class<?> fileUtils;
try {
fileUtils = Class.forName("android.os.FileUtils");
Method setPermissions = fileUtils.getMethod("setPermissions", File.class, int.class, int.class, int.class);
int result = (Integer) setPermissions.invoke(null, file, chmod, uid, gid);

return result == 0;
}
catch (Exception e) {
e.printStackTrace();
}

return false;
} else {
return false;
}
}

boolean setFilePermissions(String file, int chmod, int uid, int gid) {
return setFilePermissions(new File(file), chmod, uid, gid);
}
}
}


抓包全解

  • 抓包的核心原理: 抓包处在明文状态下的一切实际
  • Https抓包种类: Http框架Hook系统框架hook,中间人抓包(中间人把一段通信切割两段通信)
  • 搭建环境抓取Http: Charles、BurpSuite
  • Http的缺陷
    • 通信使用明文(不加密),内容可能会被窃听
    • 不验证通信方的身份, 因此有可能遭遇伪装
    • 无法证明报文的完整性, 所以有可能已遭篡改

frida_ssl_logger native的ssl日志

Https抓包详解

  • Http + SSL + 认证 + 完整性保护 = Https
  • 预共享密钥的非对称加密技术
  • Https通信完整流程
  • 中间人抓包核心原理
  • Charles、BurpSuite开启SSL抓包

抓包环境

虚拟机,网络需要从Nat设置成桥接模式, Nat是一个子网, Nat下的虚拟机可以访问手机, 手机无法访问虚拟机
所以虚拟机先设置成桥接模式

虚拟机->编辑->虚拟网络编辑器

虚拟网络编辑器中设置桥接模式, 已桥接连接到正在上网的网卡上

Hook方案

Hook之sslSocket抓包

Hook之Socket抓包

Hook之网卡和路由器抓包


Arm&C++汇编原理

  • Arm可执行程序生成过程
  • Arm汇编
  • Arm汇编指令集
  • thumb、AArch64

Arm可执行程序的生成过程

  • 预处理(Preprocessing)
  • 编译(Compilation)
  • 汇编(Assemble)
  • 链接(Linking)

首先环境准备,我们得准备一个android的Ndk, 拿android最新的ndk内部/root/Documents/android-ndk-r21e/toolchains/llvm/prebuilt/linux-x86_64/bin/clang, 作为主要的clang编译器

至此可以拿clang --target xxx hello.c -o hello进行各个平台target的编译操作

汇编语言组成结构

  • cpu
  • section段
    • .text
    • .section .rodata.str1.1,"aMS",%progbits,1
    • .section ".note.GNU-stack","",%progbits
  • 符号
    • 外部符号
      bl printf
  • 子程序
    .globl main
  • main
    .fnstart/.fnend
  • 标号
    字符串+冒号: main:这类型都是
  • 注释
    @
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
    .text
.syntax unified
.cpu arm7tdmi
.globl main @ -- Begin function main
.type main,%function
.code 32 @ @main
main:
.fnstart
push {r11, lr} @保存寄存器lr, r11到栈里面, lr记录函数的返回地址
mov r11, sp @把sp赋值给r11 寄存器寻址
sub sp, sp, #24 @sp=sp-24 @sub rm, rn, #24 rm=rn-24
mov r2, #0 @r2=0 立即数寻址
str r2, [r11, #-4] @把r2的值存到r11-4的地址上 基址寻址
str r0, [r11, #-8] @把r0的值存到r11-8的地址上 基址寻址
str r1, [sp, #12] @把r1的值存到sp-12的地址上 基址寻址
ldr r0, .LCPI0_0 @ldr r0, [pc, #40], 从pc+40读取内容存到r0, PC是当前指令的地址+两条指令长度 0x17a8,
.LPC0_0:
add r0, pc, r0 @r0=pc+r0
ldr r1, .LCPI0_1
.LPC0_1:
add r1, pc, r1
str r2, [sp, #8] @ 4-byte Spill
bl printf @子程序的调用, r0~r3传递参数, 大于4个参数的时候,多的参数用栈传参数, si单步步入,
@r0用作返回值
ldr r1, [sp, #8] @ 4-byte Reload
str r0, [sp, #4] @ 4-byte Spill
mov r0, r1 @r0返回值
mov sp, r11 @恢复栈
pop {r11, lr} @恢复r11, lr
bx lr @bx lr, 跳转到lr地址, arm -> thumb 或者 thumb -> arm
@ %bb.1:
.LCPI0_0:
.long .L.str-(.LPC0_0+8)
.LCPI0_1:
.long .L.str.1-(.LPC0_1+8)
.Lfunc_end0:
.size main, .Lfunc_end0-main
.fnend
@ -- End function
.type .L.str,%object @ @.str
.section .rodata.str1.1,"aMS",%progbits,1
.L.str:
.asciz "hello %s!\r\n"
.size .L.str, 12

.type .L.str.1,%object @ @.str.1
.L.str.1:
.asciz "arm"
.size .L.str.1, 7

.section ".note.GNU-stack","",%progbits

预处理(Preprocessing)

clang -E hello.c -o arm_hello.i
切换到具体的目标
clang -target arm-linux--android30 -E hello.c -o arm_hello.i

编译(Compilation)

clang -S arm_hello.i -o arm_hello.s
切换到具体的目标
clang -target arm-linux--android30 -S arm_hello.i -o arm_hello.s

汇编(Assemble)

clang -c arm_hello.s -o arm_hello.o(小写c)
切换具体的目标
clang -target arm-linux--android30 -c arm_hello.s -o arm_hello.o

链接(Linking)

clang x86_hello.o -o x86_hello
切换具体的目标
clang -target amr-linux--android30 x86_hello.o -o x86_hello


寄存器

R0~R7 未分组寄存器
R8~R12 是分组寄存器
R13~R14
R13 SP
R14 LR

R15 PC

CPSR

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$r0  : 0x0
$r1 : 0x0
$r2 : 0x0
$r3 : 0x0
$r4 : 0x0
$r5 : 0x0
$r6 : 0x0
$r7 : 0x0
$r8 : 0x0
$r9 : 0x0
$r10 : 0x0
$r11 : 0x0
$r12 : 0x0
$sp : 0xfffef9900x00000001
$lr : 0x0
$pc : 0xf773c3b8 → <__dl__start+0> mov r0, sp
$cpsr: [negative zero carry overflow interrupt fast thumb]


Arm汇编寻址

数据处理指令的寻址方式

  • 立即数(都要#符号)
    mov r0, #0 @立即数寻址
    mov r0, #123 @十进制数
    mov r0, #0x12 @十六进制数值
  • 寄存器
    mov r1, #0 @r1 = 0
    mov r0, r1 @r0 = r1
    add r0, r1, r2 @r0 = r1 + r2
  • 寄存器移位寻址
    mov r0, r1, LSL #4 @ r0 = r1 << 4
    • LSL 逻辑左移
    • LSR 逻辑右移
    • ROR 循环右移
    • ASR 算术右移
    • RRX 扩展的循环右移

Load Store寻址方式

ldr伪指令和mov是比较相似的。只不过mov指令限制了立即数的长度为8位,也就是不能超过512。而ldr伪指令没有这个限制。
如果使用ldr伪指令,后面跟的立即数没有超过8位,那么在实际汇编的时候该ldr伪指令会被转换为mov指令。

1
2
3
例ldr r0, 0x12345678
是把0x12345678这个地址中的值存放到r0中。
而mov不能干这个活,mov只能在寄存器之间移动数据,或者把立即数移动到寄存器中。
  • 立即数
  • 寄存器
    • ldr r0, [r2, r0, LSL #2] @ r0 = (r2 + (r0 << 2))
    • ldr r0, [r1] @r0=[r1]
    • ldr/ str
    • ldmia r3, [r4-r5] @从r3取个指给r4 , +4给r5, 再+4给r6
  • 基址变址
    • str r0,[r3, #4] @ r3+4 = r0
    • strb r0,[r3, #4] @存一个字节
  • 栈寻址
    • stmfd
    • ldmfd

Arm汇编指令

跳转指令

B 强制跳转
BL 带返回的跳转指令 往LR寄存器赋值一个地址,函数结束后回到LR的寄存器的返回地址
BLX 带返回+带状态切换(arm -> thumb, thumb->arm)
BX 带状态切换

数据处理指令

  • mov, add, sub, and, eor, orr, bic

    1
    2
    3
    4
    5
    6
    7
    mov r0,r1
    add r0, r1, r2 @r0 = r1 + r2
    sub r0, r1, r2 @r0 = r1 - r2
    and r0, r1, r2 @r0 = r1 & r2
    eor r0, r1, r2 @r0 = r1 ^ r2
    orr r0, r1, r2 @r0 = r1 | r2
    bic r0, r1, #0xf @0x12345678 -> 0x12345670
  • 乘法指令

    • MUL r0, r1, r2 @r0 = r1 * r2
    • MLA r0, r1, r2 @r0 = r1 * r2 + r3
    • SMULL r0, r1, r2, r3 @ r0 = (r2 * r3)的低32位, r1 = (r2 * r3)的高32位
    • SMLAL r0, r1, r2, r3 @ r0 = (r2 * r3)的低32位 + r0, r1 = (r2 * r3)的高32位 + r1
    • UMULL r0, r1, r2, r3 @ r0 = (r2 * r3)的低32位, r1 = (r2 * r3)的高32位
    • UMLAL r0, r1, r2, r3 @ r0 = (r2 * r3)的低32位 + r0, r1 = (r2 * r3)的高32位 + r1

内存访问指令

  • ldr 4字节读取
  • ldrb 1字节读取
  • ldrh 2字节读取
  • str 4字节写入
  • strb 1字节写入
  • strh 2字节写入

Arm汇编程序开发

print函数

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
    .text
.syntax unified
.cpu arm7tdmi
.globl main
.type main,%function
.code 32
main:
push {r4, r5, lr}

@arm_ls path

ldr r0, [r1, #4]
bl opendir
cmp r0, #0
beq .LABEL_EXIT
mov r4, r0
bl readdir
cmp r0, #0
beq .LABEL_CLOSE_DIR

ldr r5, .format_str_
.LABLE0:
add r5, pc, r5

.LOOP_READDIR:
@printf("%s\r\n", str)
add r1, r0, #0x13
mov r0, r5
bl printf
mov r0, r4
bl readdir
cmp r0, #0
bne .LOOP_READDIR



.LABEL_CLOSE_DIR:
mov r0, r4
bl closedir

.LABEL_EXIT:
mov r0, #0
pop {r4, r5, lr}
bx lr

.format_str_:
.long .format_str-(.LABLE0 + 8)

.format_str:
.asciz "%s\r\n"


Arm汇编指令集

Arm和Thumb的区别

1
2
3
4
5
thumb-> 16,32

描述新增
.code 16
.thumb_func

Arm与Aarch64的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
寄存器多了, x0是寄存器, w0是x0的低32位寄存器
64位有x0~x30, x0~x7传递参数, x0经常作为返回结果,

### x8当一个返回值大于x0返回的长度, 那么x8会作为x0的结果地址

x9~x15临时寄存器, x16~x17用来内部过程调用的寄存器
x18临时
x19~x28用来做备份
x29 用来做栈的寄存器 FP

### x30 LR返回地址寄存器

64位有专门的SP、PC、CPSR寄存器

还有arm和thumb的返回指是bx lr,但是aarch64都是ret直接返回

如何查看Arm官方文档

Arm汇编指令集

  • clang -target arm-linux-android30 -S hello.c -o hello.s

Thumb汇编指令集

  • clang -target arm-linux-android30 -S -mthumb hello.c -o hello.s

    Armv8汇编指令集

  • clang target aarch64-linux-android30 -S hello.c -o hello.s


C程序逆向分析

  • 数据类型
  • 运算符
  • 分支与跳转
  • 循环
  • 函数
  • 数组与指针
  • 结构体
  • 位操作

数据类型&运算符&分支&循环

char & shourt

  • strb:一个字节 byte
  • strh:两个字节 short
  • str: 四个字节
1
2
3
4
strb    r0, [r11, #-14]
mov r0, #16
strh r0, [r11, #-16]
mov r0, #32

float

1
2
3
4
5
6
7
8
9
10
11
12
mov r0, #26738688
orr r0, r0, #1073741824
str r0, [sp, #36]

@ 这里怎么算,先算指数部分(30~23), 比如19.0f
@ 19翻译二进制就是0001 0011
@ 19是正数,所以31位为0, 负数则为1(31)
@ 19.0 翻译一下就是10011 -> 1.0011 那就是向右边移动4位 则2^4, 值就是4 那么127 + 4代表的是131
@ 得到的指数位结果131就是 1000 0011
@ 小数部分的计算就是 0.0011 -> 0011那么(22~0)
@ 得到的结果就是0100 0001 1(30~23)001 1000 000 000 000 000(22~0)
@ 这样算出的结果就是 0x4198 0000

double

  • double d1 = 29.0;
  • double d2 = 39.0;
1
2
3
4
5
@ 0x403d0000 29.0 -> 1.1101 * 2^4 =  100 0000 0011 = 1023 + 4
@ double比float指数位长一点,(30~20)
@ 小数位1101(19~0)
@ 29是正数,所以31位为0, 负数则为1(31)
@ 结果就是 0100 0000 0011 1101 0000 0000 0000 0000

运算符

1
2
3
4
5
6
char ch3 = ch1 + ch2;
short s3 = s1 - s2;
int n3 = n1 * n2;
n3 = 0x1000;
// int n4 = n3 / 11;
int n5 = n3 % 7;
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
ldrb    r0, [r11, #-13]
ldrb r1, [r11, #-14]
add r0, r0, r1 @ 加法
strb r0, [sp, #15]
ldrh r0, [r11, #-16]
ldrh r1, [r11, #-18]
sub r0, r0, r1 @减法
strh r0, [sp, #12]
ldr r0, [r11, #-24]
ldr r1, [r11, #-28]
mul r2, r0, r1 @乘法




ldr r0, [sp, #8]
ldr r1, .LCPI0_1
smull r3, r12, r0, r1 @除法
add r0, r12, r0 @r0 = 0xffff f924 + 0x1000 = 0x1 0000 0924, 高位全部用1填充, 只有4个字节舍弃高位1, r0则为0x0000 0924
asr r1, r0 ,#2 @r1 = 0x0000 0924 >> 2 = 0x249
add r0, r1, r0, lsr #31 @r0 = (0x0000 0924 >> 31) + 0x249 = 0x249



@ smull r3, r12, r0, r1
@ r0 = 0x1000
@ r1 = 0x92492493
@ 这两个值的乘法为 0x924 9249 3000 超过了32位
@ 低位放到了r2, 高位放到了r3
@ r3 = 0x9249 3000
@ r12 = 0xffff f924

分支

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
if(n5 > 6){
printf("n5:%d\n", n5);
}

switch(n5){
case 1:
printf("1\n");
break;
case 2:
printf("2\n");
break;
case 3:
printf("3\n");
break;
default:
printf("default\n");
break;
}
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
cmp r0,#7           # if 分支
blt .LABEL_2
xxx

.LABEL_2
xxx



.LBBO_11
ldr r0, {sp, #4}
cmp r0, #1
str r0, {sp}
beq .LBBO_1
b .LBBO_5

.LBBO_12
ldr r0, {sp}
cmp r0, #2
beq .LBBO_2
b .LBBO_5

.LBBO_13
ldr r0, {sp}
cmp r0, #3
beq .LBBO_3
b .LBBO_5

hello.c

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
#include <stdio.h>


int main(int argc, char const *argv[])
{
char ch1 = 'a';
char ch2 = 'b';

short s1 = 0x10;
short s2 = 0x20;

int n1 = 0x100;
int n2 = 0x200;

float f1 = 9.0f;
float f2 = 19.0f;

double d1 = 29.0;
double d2 = 39.0;

char ch3 = ch1 + ch2;
short s3 = s1 - s2;
int n3 = n1 * n2;
n3 = 0x1000;
// int n4 = n3 / 11;
int n5 = n3 % 7;

//goto goto_label;
if (n5 < 6) {
printf("if n5:%d\r\n",n5);
} else {
printf("else n5:%d\r\n",n5);
}

switch (n5)
{
case 1:
printf("case 1\r\n");
break;
case 2:
printf("case 2\r\n");
break;
case 3:
printf("case 3\r\n");
break;
case 4:
printf("case 4\r\n");
break;

default:
printf("case default\r\n");
break;
}

goto_label:
printf("goto");

for (int i = 0; i < 10; i++)
{
printf("for i : %d\r\n", i);
}
int j = 10;
while (j)
{
printf("while j : %d\r\n", j);
j--;
}
int k = 0;
do
{
printf("do while k : %d\r\n", k--);

} while (k);


return 0;
}

hello.s

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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
    .text
.syntax unified
.eabi_attribute 67, "2.09" @ Tag_conformance
.cpu arm7tdmi
.eabi_attribute 6, 2 @ Tag_CPU_arch
.eabi_attribute 8, 1 @ Tag_ARM_ISA_use
.eabi_attribute 9, 1 @ Tag_THUMB_ISA_use
.eabi_attribute 34, 0 @ Tag_CPU_unaligned_access
.eabi_attribute 15, 1 @ Tag_ABI_PCS_RW_data
.eabi_attribute 16, 1 @ Tag_ABI_PCS_RO_data
.eabi_attribute 17, 2 @ Tag_ABI_PCS_GOT_use
.eabi_attribute 20, 1 @ Tag_ABI_FP_denormal
.eabi_attribute 21, 1 @ Tag_ABI_FP_exceptions
.eabi_attribute 23, 3 @ Tag_ABI_FP_number_model
.eabi_attribute 24, 1 @ Tag_ABI_align_needed
.eabi_attribute 25, 1 @ Tag_ABI_align_preserved
.eabi_attribute 38, 1 @ Tag_ABI_FP_16bit_format
.eabi_attribute 18, 4 @ Tag_ABI_PCS_wchar_t
.eabi_attribute 26, 2 @ Tag_ABI_enum_size
.eabi_attribute 14, 0 @ Tag_ABI_PCS_R9_use
.file "c_hello.c"
.globl main @ -- Begin function main
.p2align 2
.type main,%function
.code 32 @ @main
main:
.fnstart
@ %bb.0:
.save {r11, lr}
push {r11, lr}
.setfp r11, sp
mov r11, sp
.pad #72
sub sp, sp, #72
mov r2, #0
str r2, [r11, #-4]
str r0, [r11, #-8]
str r1, [r11, #-12]
mov r0, #97
strb r0, [r11, #-13]
mov r0, #98
strb r0, [r11, #-14]
mov r0, #16
strh r0, [r11, #-16]
mov r0, #32
strh r0, [r11, #-18]
mov r0, #256
str r0, [r11, #-24]
mov r0, #512
str r0, [r11, #-28]
mov r0, #17825792
orr r0, r0, #1073741824
str r0, [r11, #-32]
mov r0, #26738688
orr r0, r0, #1073741824
str r0, [sp, #36]
mov r0, #3997696
orr r0, r0, #1073741824
str r0, [sp, #28]
str r2, [sp, #24]
ldr r0, .LCPI0_0
str r0, [sp, #20]
str r2, [sp, #16]
ldrb r0, [r11, #-13]
ldrb r1, [r11, #-14]
add r0, r0, r1
strb r0, [sp, #15]
ldrh r0, [r11, #-16]
ldrh r1, [r11, #-18]
sub r0, r0, r1
strh r0, [sp, #12]
ldr r0, [r11, #-24]
ldr r1, [r11, #-28]
mul r2, r0, r1
str r2, [sp, #8]
mov r0, #4096
str r0, [sp, #8]
ldr r0, [sp, #8]
ldr r1, .LCPI0_1
smull r2, r3, r0, r1
add r1, r3, r0
asr r3, r1, #2
add r1, r3, r1, lsr #31
sub r1, r1, r1, lsl #3
add r0, r0, r1
str r0, [sp, #4]
ldr r0, [sp, #4]
cmp r0, #5
bgt .LBB0_2
b .LBB0_1
.LBB0_1:
ldr r1, [sp, #4]
ldr r0, .LCPI0_2
.LPC0_0:
add r0, pc, r0
bl printf
b .LBB0_3
.LBB0_2:
ldr r1, [sp, #4]
ldr r0, .LCPI0_3
.LPC0_1:
add r0, pc, r0
bl printf
b .LBB0_3
.LBB0_3:
ldr r0, [sp, #4]
sub r0, r0, #1
cmp r0, #3
str r0, [sp] @ 4-byte Spill
bhi .LBB0_10
@ %bb.4:
adr r0, .LJTI0_0
ldr r1, [sp] @ 4-byte Reload
ldr r2, [r0, r1, lsl #2]
add pc, r0, r2
@ %bb.5:
.p2align 2
.LJTI0_0:
.long .LBB0_6-.LJTI0_0
.long .LBB0_7-.LJTI0_0
.long .LBB0_8-.LJTI0_0
.long .LBB0_9-.LJTI0_0
.LBB0_6:
ldr r0, .LCPI0_4
.LPC0_2:
add r0, pc, r0
bl printf
b .LBB0_11
.LBB0_7:
ldr r0, .LCPI0_5
.LPC0_3:
add r0, pc, r0
bl printf
b .LBB0_11
.LBB0_8:
ldr r0, .LCPI0_6
.LPC0_4:
add r0, pc, r0
bl printf
b .LBB0_11
.LBB0_9:
ldr r0, .LCPI0_7
.LPC0_5:
add r0, pc, r0
bl printf
b .LBB0_11
.LBB0_10:
b .LBB0_11
.LBB0_11:
mov r0, #0
mov sp, r11
pop {r11, lr}
bx lr
.p2align 2
@ %bb.12:
.LCPI0_0:
.long 1078165504 @ 0x40438000
.LCPI0_1:
.long 2454267027 @ 0x92492493
.LCPI0_2:
.long .L.str-(.LPC0_0+8)
.LCPI0_3:
.long .L.str.1-(.LPC0_1+8)
.LCPI0_4:
.long .L.str.2-(.LPC0_2+8)
.LCPI0_5:
.long .L.str.3-(.LPC0_3+8)
.LCPI0_6:
.long .L.str.4-(.LPC0_4+8)
.LCPI0_7:
.long .L.str.5-(.LPC0_5+8)
.Lfunc_end0:
.size main, .Lfunc_end0-main
.cantunwind
.fnend
@ -- End function
.type .L.str,%object @ @.str
.section .rodata.str1.1,"aMS",%progbits,1
.L.str:
.asciz "if n5:%d\r\n"
.size .L.str, 11

.type .L.str.1,%object @ @.str.1
.L.str.1:
.asciz "else n5:%d\r\n"
.size .L.str.1, 13

.type .L.str.2,%object @ @.str.2
.L.str.2:
.asciz "case 1\r\n"
.size .L.str.2, 9

.type .L.str.3,%object @ @.str.3
.L.str.3:
.asciz "case 2\r\n"
.size .L.str.3, 9

.type .L.str.4,%object @ @.str.4
.L.str.4:
.asciz "case 3\r\n"
.size .L.str.4, 9

.type .L.str.5,%object @ @.str.5
.L.str.5:
.asciz "case 4\r\n"
.size .L.str.5, 9


.ident "Android (5900059 based on r365631c) clang version 9.0.8 (https://android.googlesource.com/toolchain/llvm-project 207d7abc1a2abf3ef8d4301736d6a7ebc224a290) (based on LLVM 9.0.8svn)"
.section ".note.GNU-stack","",%progbits


函数、结构体、数组、位操作

main.c

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
#include <stdio.h>


void func_arg_type(char ch, short s, int n, int f, int d, int n2, int n3) {
printf("%c %d %d %d %d\r\n", ch, s, n, f, d);
}

struct func
{
int n1;
int n2;
int n3;
char* lpstr;
};

int func_ret_int() {
struct func f = {0};
f.n1 = 1;
f.n2 = 2;
f.n3 = 3;
f.lpstr = "123";

return f.n3;
}

struct func func_ret_struct() {
struct func f = {0};
f.n1 = 1;
f.n2 = 2;
f.n3 = 3;
f.lpstr = "123";
return f;
}
int n1 = 333;

char global_array[100] = {0};

int main(int argc, char const *argv[])
{

// func_arg_type('a', 10, 1000, 123, 456, 200, 300);

// func_ret_struct();

// func_ret_int();

// struct func f = {0};
// f.n1 = 1;
// f.n2 = 2;
// f.n3 = 3;

// struct func* lp_f = &f;
// lp_f->n1 = 4;
// lp_f->n2 = 5;

// union union_u {
// int n1;
// int n2;
// short s3;
// };
// union union_u u = {0};
// u.n1 = 7;

// printf("%d\r\n", u.n2);

// static int n = 222;

// n = n1;

// int n_array[20] = {0};
// n_array[3] = 3;

// for (size_t i = 0; i < sizeof(n_array); i++)
// {
// n_array[i] = i + 3;
// *(n_array + i) = i + 6;
// }
// int* lp_array = n_array;
// for (size_t i = sizeof(n_array) - 1; i >= 0; i--)
// {
// *(lp_array - i) = i + 9;
// }

// static char static_array[100] = {0};

// for (size_t i = 0; i < sizeof(static_array); i++)
// {
// static_array[i] = i;
// }
// for (size_t i = 0; i < sizeof(global_array); i++)
// {
// global_array[i] = i;
// }

int n1 = 0x100;
int n2 = ~n1;
int n3 = n1 & n2;
int n4 = n1 ^ n3;
int n5 = n1 >> 1;
int n6 = n1 << 1;
int n7 = n1 | n2;
return 0;
}

函数

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
    .text
.syntax unified
.eabi_attribute 67, "2.09" @ Tag_conformance
.cpu arm7tdmi
.eabi_attribute 6, 2 @ Tag_CPU_arch
.eabi_attribute 8, 1 @ Tag_ARM_ISA_use
.eabi_attribute 9, 1 @ Tag_THUMB_ISA_use
.eabi_attribute 34, 0 @ Tag_CPU_unaligned_access
.eabi_attribute 15, 1 @ Tag_ABI_PCS_RW_data
.eabi_attribute 16, 1 @ Tag_ABI_PCS_RO_data
.eabi_attribute 17, 2 @ Tag_ABI_PCS_GOT_use
.eabi_attribute 20, 1 @ Tag_ABI_FP_denormal
.eabi_attribute 21, 1 @ Tag_ABI_FP_exceptions
.eabi_attribute 23, 3 @ Tag_ABI_FP_number_model
.eabi_attribute 24, 1 @ Tag_ABI_align_needed
.eabi_attribute 25, 1 @ Tag_ABI_align_preserved
.eabi_attribute 38, 1 @ Tag_ABI_FP_16bit_format
.eabi_attribute 18, 4 @ Tag_ABI_PCS_wchar_t
.eabi_attribute 26, 2 @ Tag_ABI_enum_size
.eabi_attribute 14, 0 @ Tag_ABI_PCS_R9_use
.file "func.c"
.globl func_arg_type @ -- Begin function func_arg_type
.p2align 2
.type func_arg_type,%function
.code 32 @ @func_arg_type
func_arg_type:
.fnstart
@ %bb.0:
.save {r4, r5, r6, r10, r11, lr}
push {r4, r5, r6, r10, r11, lr}
.setfp r11, sp, #16
add r11, sp, #16
.pad #64
sub sp, sp, #64
ldr r12, [r11, #12]
ldr lr, [r11, #8]
mov r4, r3
mov r5, r1
mov r6, r0
strb r0, [r11, #-17]
strh r1, [r11, #-20]
str r2, [r11, #-24]
str r3, [r11, #-28]
str r12, [sp, #44]
str lr, [sp, #40]
ldrb r1, [r11, #-17]
ldrsh r2, [r11, #-20]
ldr r3, [r11, #-24]
ldr r0, [r11, #-28]
str r4, [sp, #36] @ 4-byte Spill
str r5, [sp, #32] @ 4-byte Spill
str r6, [sp, #28] @ 4-byte Spill
str r1, [sp, #24] @ 4-byte Spill
str r2, [sp, #20] @ 4-byte Spill
str r3, [sp, #16] @ 4-byte Spill
bl __aeabi_f2d
ldr r2, [sp, #40]
ldr r3, [sp, #44]
mov r12, sp
str r3, [r12, #12]
str r2, [r12, #8]
str r1, [r12, #4]
str r0, [r12]
ldr r0, .LCPI0_0
.LPC0_0:
add r0, pc, r0
ldr r1, [sp, #24] @ 4-byte Reload
ldr r2, [sp, #20] @ 4-byte Reload
ldr r3, [sp, #16] @ 4-byte Reload
bl printf
sub sp, r11, #16
pop {r4, r5, r6, r10, r11, lr}
bx lr
.p2align 2
@ %bb.1:
.LCPI0_0:
.long .L.str-(.LPC0_0+8)
.Lfunc_end0:
.size func_arg_type, .Lfunc_end0-func_arg_type
.cantunwind
.fnend
@ -- End function
.globl main @ -- Begin function main
.p2align 2
.type main,%function
.code 32 @ @main
main:
.fnstart
@ %bb.0:
.save {r11, lr}
push {r11, lr}
.setfp r11, sp
mov r11, sp
.pad #24
sub sp, sp, #24
mov r2, #0
str r2, [r11, #-4]
str r0, [r11, #-8]
str r1, [sp, #12]
mov r0, sp
ldr r1, .LCPI1_0
str r1, [r0, #4]
ldr r1, .LCPI1_1
str r1, [r0] @[sp] double 0x4069070a3d70a3d7 -> double 200.22
mov r0, #97 @r0~r3 r0 = 'a'
mov r1, #10 @r1 = 10
mov r3, #1000
ldr r12, .LCPI1_2
str r2, [sp, #8] @ 4-byte Spill
mov r2, r3 @r2 = 1000;
mov r3, r12 @r3 = 0x42c83852
bl func_arg_type @ bl 调用函数, blx
ldr r0, [sp, #8] @ 4-byte Reload
mov sp, r11
pop {r11, lr}
bx lr
.p2align 2
@ %bb.1:
.LCPI1_0:
.long 1080624906 @ 0x4069070a
.LCPI1_1:
.long 1030792151 @ 0x3d70a3d7
.LCPI1_2:
.long 1120417874 @ 0x42c83852
.Lfunc_end1:
.size main, .Lfunc_end1-main
.cantunwind
.fnend
@ -- End function
.type .L.str,%object @ @.str
.section .rodata.str1.1,"aMS",%progbits,1
.L.str:
.asciz "%c %d %d %f %a\r\n"
.size .L.str, 17


.ident "Android (5900059 based on r365631c) clang version 9.0.8 (https://android.googlesource.com/toolchain/llvm-project 207d7abc1a2abf3ef8d4301736d6a7ebc224a290) (based on LLVM 9.0.8svn)"
.section ".note.GNU-stack","",%progbits

静态变量

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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
    .text
.syntax unified
.eabi_attribute 67, "2.09" @ Tag_conformance
.cpu arm7tdmi
.eabi_attribute 6, 2 @ Tag_CPU_arch
.eabi_attribute 8, 1 @ Tag_ARM_ISA_use
.eabi_attribute 9, 1 @ Tag_THUMB_ISA_use
.eabi_attribute 34, 0 @ Tag_CPU_unaligned_access
.eabi_attribute 15, 1 @ Tag_ABI_PCS_RW_data
.eabi_attribute 16, 1 @ Tag_ABI_PCS_RO_data
.eabi_attribute 17, 2 @ Tag_ABI_PCS_GOT_use
.eabi_attribute 20, 1 @ Tag_ABI_FP_denormal
.eabi_attribute 21, 1 @ Tag_ABI_FP_exceptions
.eabi_attribute 23, 3 @ Tag_ABI_FP_number_model
.eabi_attribute 24, 1 @ Tag_ABI_align_needed
.eabi_attribute 25, 1 @ Tag_ABI_align_preserved
.eabi_attribute 38, 1 @ Tag_ABI_FP_16bit_format
.eabi_attribute 18, 4 @ Tag_ABI_PCS_wchar_t
.eabi_attribute 26, 2 @ Tag_ABI_enum_size
.eabi_attribute 14, 0 @ Tag_ABI_PCS_R9_use
.file "func.c"
.globl func_arg_type @ -- Begin function func_arg_type
.p2align 2
.type func_arg_type,%function
.code 32 @ @func_arg_type
func_arg_type:
.fnstart
@ %bb.0:
.save {r4, r5, r6, r7, r8, r10, r11, lr}
push {r4, r5, r6, r7, r8, r10, r11, lr}
.setfp r11, sp, #24
add r11, sp, #24
.pad #40
sub sp, sp, #40
ldr r12, [r11, #16]
ldr lr, [r11, #12]
ldr r4, [r11, #8]
mov r5, r1
mov r6, r0
strb r0, [r11, #-25]
strh r1, [r11, #-28]
str r2, [sp, #32]
str r3, [sp, #28]
ldrb r1, [r11, #-25]
ldrsh r2, [r11, #-28]
ldr r3, [sp, #32]
ldr r0, [sp, #28]
ldr r7, [r11, #8]
mov r8, sp
str r7, [r8, #4]
str r0, [r8]
ldr r0, .LCPI0_0
.LPC0_0:
add r0, pc, r0
str r12, [sp, #24] @ 4-byte Spill
str lr, [sp, #20] @ 4-byte Spill
str r4, [sp, #16] @ 4-byte Spill
str r5, [sp, #12] @ 4-byte Spill
str r6, [sp, #8] @ 4-byte Spill
bl printf
sub sp, r11, #24
pop {r4, r5, r6, r7, r8, r10, r11, lr}
bx lr
.p2align 2
@ %bb.1:
.LCPI0_0:
.long .L.str-(.LPC0_0+8)
.Lfunc_end0:
.size func_arg_type, .Lfunc_end0-func_arg_type
.cantunwind
.fnend
@ -- End function
.globl func_ret_int @ -- Begin function func_ret_int
.p2align 2
.type func_ret_int,%function
.code 32 @ @func_ret_int
func_ret_int:
.fnstart
@ %bb.0:
.pad #16
sub sp, sp, #16
mov r0, #0
str r0, [sp, #12]
str r0, [sp, #8]
str r0, [sp, #4]
str r0, [sp]
mov r0, #1
str r0, [sp]
mov r0, #2
str r0, [sp, #4]
mov r0, #3
str r0, [sp, #8]
ldr r0, .LCPI1_0
.LPC1_0:
add r0, pc, r0
str r0, [sp, #12]
ldr r0, [sp, #8]
add sp, sp, #16
bx lr
.p2align 2
@ %bb.1:
.LCPI1_0:
.long .L.str.1-(.LPC1_0+8)
.Lfunc_end1:
.size func_ret_int, .Lfunc_end1-func_ret_int
.cantunwind
.fnend
@ -- End function
.globl func_ret_struct @ -- Begin function func_ret_struct
.p2align 2
.type func_ret_struct,%function
.code 32 @ @func_ret_struct
func_ret_struct:
.fnstart
@ %bb.0:
mov r1, #0
str r1, [r0, #12]
str r1, [r0, #8]
str r1, [r0, #4]
str r1, [r0]
mov r1, #1
str r1, [r0]
mov r1, #2
str r1, [r0, #4]
mov r1, #3
str r1, [r0, #8]
ldr r1, .LCPI2_0
.LPC2_0:
add r1, pc, r1
str r1, [r0, #12]
bx lr
.p2align 2
@ %bb.1:
.LCPI2_0:
.long .L.str.1-(.LPC2_0+8)
.Lfunc_end2:
.size func_ret_struct, .Lfunc_end2-func_ret_struct
.cantunwind
.fnend
@ -- End function
.globl main @ -- Begin function main
.p2align 2
.type main,%function
.code 32 @ @main
main:
.fnstart
@ %bb.0:
.pad #12
sub sp, sp, #12
mov r2, #0
str r2, [sp, #8]
str r0, [sp, #4]
str r1, [sp]
ldr r0, .LCPI3_0
.LPC3_0:
ldr r0, [pc, r0]
ldr r1, .LCPI3_1
.LPC3_1:
add r1, pc, r1
str r0, [r1]
ldr r0, .LCPI3_2
.LPC3_2:
ldr r0, [pc, r0]
add sp, sp, #12
bx lr
.p2align 2
@ %bb.1:
.LCPI3_0:
.long n1-(.LPC3_0+8)
.LCPI3_1:
.long main.n-(.LPC3_1+8)
.LCPI3_2:
.long main.n-(.LPC3_2+8)
.Lfunc_end3:
.size main, .Lfunc_end3-main
.cantunwind
.fnend
@ -- End function
.type .L.str,%object @ @.str
.section .rodata.str1.1,"aMS",%progbits,1
.L.str:
.asciz "%c %d %d %d %d\r\n"
.size .L.str, 17

.type .L.str.1,%object @ @.str.1
.L.str.1:
.asciz "123"
.size .L.str.1, 4
mder
.type n1,%object @ @n1
.data
.globl n1
.p2align 2
n1:
.long 333 @ 0x14d
.size n1, 4

.type main.n,%object @ @main.n
.p2align 2
main.n:
.long 222 @ 0xde
.size main.n, 4


.ident "Android (5900059 based on r365631c) clang version 9.0.8 (https://android.googlesource.com/toolchain/llvm-project 207d7abc1a2abf3ef8d4301736d6a7ebc224a290) (based on LLVM 9.0.8svn)"
.section ".note.GNU-stack","",%progbits

结构体

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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
    .text
.syntax unified
.eabi_attribute 67, "2.09" @ Tag_conformance
.cpu arm7tdmi
.eabi_attribute 6, 2 @ Tag_CPU_arch
.eabi_attribute 8, 1 @ Tag_ARM_ISA_use
.eabi_attribute 9, 1 @ Tag_THUMB_ISA_use
.eabi_attribute 34, 0 @ Tag_CPU_unaligned_access
.eabi_attribute 15, 1 @ Tag_ABI_PCS_RW_data
.eabi_attribute 16, 1 @ Tag_ABI_PCS_RO_data
.eabi_attribute 17, 2 @ Tag_ABI_PCS_GOT_use
.eabi_attribute 20, 1 @ Tag_ABI_FP_denormal
.eabi_attribute 21, 1 @ Tag_ABI_FP_exceptions
.eabi_attribute 23, 3 @ Tag_ABI_FP_number_model
.eabi_attribute 24, 1 @ Tag_ABI_align_needed
.eabi_attribute 25, 1 @ Tag_ABI_align_preserved
.eabi_attribute 38, 1 @ Tag_ABI_FP_16bit_format
.eabi_attribute 18, 4 @ Tag_ABI_PCS_wchar_t
.eabi_attribute 26, 2 @ Tag_ABI_enum_size
.eabi_attribute 14, 0 @ Tag_ABI_PCS_R9_use
.file "func.c"
.globl func_arg_type @ -- Begin function func_arg_type
.p2align 2
.type func_arg_type,%function
.code 32 @ @func_arg_type
func_arg_type:
.fnstart
@ %bb.0:
.save {r4, r5, r6, r7, r8, r10, r11, lr}
push {r4, r5, r6, r7, r8, r10, r11, lr}
.setfp r11, sp, #24
add r11, sp, #24
.pad #40
sub sp, sp, #40
ldr r12, [r11, #16]
ldr lr, [r11, #12]
ldr r4, [r11, #8]
mov r5, r1
mov r6, r0
strb r0, [r11, #-25]
strh r1, [r11, #-28]
str r2, [sp, #32]
str r3, [sp, #28]
ldrb r1, [r11, #-25]
ldrsh r2, [r11, #-28]
ldr r3, [sp, #32]
ldr r0, [sp, #28]
ldr r7, [r11, #8]
mov r8, sp
str r7, [r8, #4]
str r0, [r8]
ldr r0, .LCPI0_0
.LPC0_0:
add r0, pc, r0
str r12, [sp, #24] @ 4-byte Spill
str lr, [sp, #20] @ 4-byte Spill
str r4, [sp, #16] @ 4-byte Spill
str r5, [sp, #12] @ 4-byte Spill
str r6, [sp, #8] @ 4-byte Spill
bl printf
sub sp, r11, #24
pop {r4, r5, r6, r7, r8, r10, r11, lr}
bx lr
.p2align 2
@ %bb.1:
.LCPI0_0:
.long .L.str-(.LPC0_0+8)
.Lfunc_end0:
.size func_arg_type, .Lfunc_end0-func_arg_type
.cantunwind
.fnend
@ -- End function
.globl func_ret_int @ -- Begin function func_ret_int
.p2align 2
.type func_ret_int,%function
.code 32 @ @func_ret_int
func_ret_int:
.fnstart
@ %bb.0:
.pad #16
sub sp, sp, #16
mov r0, #0
str r0, [sp, #12]
str r0, [sp, #8]
str r0, [sp, #4]
str r0, [sp]
mov r0, #1
str r0, [sp]
mov r0, #2
str r0, [sp, #4]
mov r0, #3
str r0, [sp, #8] @ [sp, #8]=3 str
ldr r0, .LCPI1_0
.LPC1_0:
add r0, pc, r0
str r0, [sp, #12]
ldr r0, [sp, #8] @ r0 = [sp, #8] ldr
add sp, sp, #16
bx lr
.p2align 2
@ %bb.1:
.LCPI1_0:
.long .L.str.1-(.LPC1_0+8)
.Lfunc_end1:
.size func_ret_int, .Lfunc_end1-func_ret_int
.cantunwind
.fnend
@ -- End function
.globl func_ret_struct @ -- Begin function func_ret_struct
.p2align 2
.type func_ret_struct,%function
.code 32 @ @func_ret_struct
func_ret_struct:
.fnstart
@ %bb.0:
mov r1, #0
str r1, [r0, #12]
str r1, [r0, #8]
str r1, [r0, #4]
str r1, [r0]
mov r1, #1
str r1, [r0]
mov r1, #2
str r1, [r0, #4]
mov r1, #3
str r1, [r0, #8]
ldr r1, .LCPI2_0
.LPC2_0:
add r1, pc, r1
str r1, [r0, #12]
bx lr
.p2align 2
@ %bb.1:
.LCPI2_0:
.long .L.str.1-(.LPC2_0+8)
.Lfunc_end2:
.size func_ret_struct, .Lfunc_end2-func_ret_struct
.cantunwind
.fnend
@ -- End function
.globl main @ -- Begin function main
.p2align 2
.type main,%function
.code 32 @ @main
main:
.fnstart
@ %bb.0:
.save {r11, lr}
push {r11, lr}
.setfp r11, sp
mov r11, sp
.pad #48
sub sp, sp, #48
mov r2, #0
str r2, [r11, #-4]
str r0, [r11, #-8]
str r1, [r11, #-12]
mov r0, sp
mov r1, #300
str r1, [r0, #8]
mov r1, #200
str r1, [r0, #4]
mov r1, #456
str r1, [r0]
mov r0, #97
mov r1, #10
mov r3, #1000
mov r12, #123
str r2, [sp, #16] @ 4-byte Spill
mov r2, r3
mov r3, r12
bl func_arg_type
add r0, sp, #20
bl func_ret_struct
bl func_ret_int
ldr r1, [sp, #16] @ 4-byte Reload
str r0, [sp, #12] @ 4-byte Spill
mov r0, r1
mov sp, r11
pop {r11, lr}
bx lr
.Lfunc_end3:
.size main, .Lfunc_end3-main
.cantunwind
.fnend
@ -- End function
.type .L.str,%object @ @.str
.section .rodata.str1.1,"aMS",%progbits,1
.L.str:
.asciz "%c %d %d %d %d\r\n"
.size .L.str, 17

.type .L.str.1,%object @ @.str.1
.L.str.1:
.asciz "123"
.size .L.str.1, 4


.ident "Android (5900059 based on r365631c) clang version 9.0.8 (https://android.googlesource.com/toolchain/llvm-project 207d7abc1a2abf3ef8d4301736d6a7ebc224a290) (based on LLVM 9.0.8svn)"
.section ".note.GNU-stack","",%progbits

数组

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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
    .text
.syntax unified
.eabi_attribute 67, "2.09" @ Tag_conformance
.cpu arm7tdmi
.eabi_attribute 6, 2 @ Tag_CPU_arch
.eabi_attribute 8, 1 @ Tag_ARM_ISA_use
.eabi_attribute 9, 1 @ Tag_THUMB_ISA_use
.eabi_attribute 34, 0 @ Tag_CPU_unaligned_access
.eabi_attribute 15, 1 @ Tag_ABI_PCS_RW_data
.eabi_attribute 16, 1 @ Tag_ABI_PCS_RO_data
.eabi_attribute 17, 2 @ Tag_ABI_PCS_GOT_use
.eabi_attribute 20, 1 @ Tag_ABI_FP_denormal
.eabi_attribute 21, 1 @ Tag_ABI_FP_exceptions
.eabi_attribute 23, 3 @ Tag_ABI_FP_number_model
.eabi_attribute 24, 1 @ Tag_ABI_align_needed
.eabi_attribute 25, 1 @ Tag_ABI_align_preserved
.eabi_attribute 38, 1 @ Tag_ABI_FP_16bit_format
.eabi_attribute 18, 4 @ Tag_ABI_PCS_wchar_t
.eabi_attribute 26, 2 @ Tag_ABI_enum_size
.eabi_attribute 14, 0 @ Tag_ABI_PCS_R9_use
.file "func.c"
.globl func_arg_type @ -- Begin function func_arg_type
.p2align 2
.type func_arg_type,%function
.code 32 @ @func_arg_type
func_arg_type:
.fnstart
@ %bb.0:
.save {r4, r5, r6, r7, r8, r10, r11, lr}
push {r4, r5, r6, r7, r8, r10, r11, lr}
.setfp r11, sp, #24
add r11, sp, #24
.pad #40
sub sp, sp, #40
ldr r12, [r11, #16]
ldr lr, [r11, #12]
ldr r4, [r11, #8]
mov r5, r1
mov r6, r0
strb r0, [r11, #-25]
strh r1, [r11, #-28]
str r2, [sp, #32]
str r3, [sp, #28]
ldrb r1, [r11, #-25]
ldrsh r2, [r11, #-28]
ldr r3, [sp, #32]
ldr r0, [sp, #28]
ldr r7, [r11, #8]
mov r8, sp
str r7, [r8, #4]
str r0, [r8]
ldr r0, .LCPI0_0
.LPC0_0:
add r0, pc, r0
str r12, [sp, #24] @ 4-byte Spill
str lr, [sp, #20] @ 4-byte Spill
str r4, [sp, #16] @ 4-byte Spill
str r5, [sp, #12] @ 4-byte Spill
str r6, [sp, #8] @ 4-byte Spill
bl printf
sub sp, r11, #24
pop {r4, r5, r6, r7, r8, r10, r11, lr}
bx lr
.p2align 2
@ %bb.1:
.LCPI0_0:
.long .L.str-(.LPC0_0+8)
.Lfunc_end0:
.size func_arg_type, .Lfunc_end0-func_arg_type
.cantunwind
.fnend
@ -- End function
.globl func_ret_int @ -- Begin function func_ret_int
.p2align 2
.type func_ret_int,%function
.code 32 @ @func_ret_int
func_ret_int:
.fnstart
@ %bb.0:
.pad #16
sub sp, sp, #16
mov r0, #0
str r0, [sp, #12]
str r0, [sp, #8]
str r0, [sp, #4]
str r0, [sp]
mov r0, #1
str r0, [sp]
mov r0, #2
str r0, [sp, #4]
mov r0, #3
str r0, [sp, #8]
ldr r0, .LCPI1_0
.LPC1_0:
add r0, pc, r0
str r0, [sp, #12]
ldr r0, [sp, #8]
add sp, sp, #16
bx lr
.p2align 2
@ %bb.1:
.LCPI1_0:
.long .L.str.1-(.LPC1_0+8)
.Lfunc_end1:
.size func_ret_int, .Lfunc_end1-func_ret_int
.cantunwind
.fnend
@ -- End function
.globl func_ret_struct @ -- Begin function func_ret_struct
.p2align 2
.type func_ret_struct,%function
.code 32 @ @func_ret_struct
func_ret_struct:
.fnstart
@ %bb.0:
mov r1, #0
str r1, [r0, #12]
str r1, [r0, #8]
str r1, [r0, #4]
str r1, [r0]
mov r1, #1
str r1, [r0]
mov r1, #2
str r1, [r0, #4]
mov r1, #3
str r1, [r0, #8]
ldr r1, .LCPI2_0
.LPC2_0:
add r1, pc, r1
str r1, [r0, #12]
bx lr
.p2align 2
@ %bb.1:
.LCPI2_0:
.long .L.str.1-(.LPC2_0+8)
.Lfunc_end2:
.size func_ret_struct, .Lfunc_end2-func_ret_struct
.cantunwind
.fnend
@ -- End function
.globl main @ -- Begin function main
.p2align 2
.type main,%function
.code 32 @ @main
main:
.fnstart
@ %bb.0:
.save {r11, lr}
push {r11, lr}
.setfp r11, sp
mov r11, sp
.pad #120
sub sp, sp, #120
mov r2, #0
str r2, [r11, #-4]
str r0, [r11, #-8]
str r1, [r11, #-12]
add r0, sp, #28
mov r1, #80
str r2, [sp, #4] @ 4-byte Spill
bl __aeabi_memclr4
mov r0, #3
str r0, [sp, #40]
ldr r0, [sp, #4] @ 4-byte Reload
str r0, [sp, #24]
b .LBB3_1
.LBB3_1: @ =>This Inner Loop Header: Depth=1
ldr r0, [sp, #24]
cmp r0, #79
bhi .LBB3_4
b .LBB3_2
.LBB3_2: @ in Loop: Header=BB3_1 Depth=1
ldr r0, [sp, #24]
add r1, r0, #3
add r2, sp, #28
str r1, [r2, r0, lsl #2]
ldr r0, [sp, #24]
add r1, r0, #6
str r1, [r2, r0, lsl #2]
b .LBB3_3
.LBB3_3: @ in Loop: Header=BB3_1 Depth=1
ldr r0, [sp, #24]
add r0, r0, #1
str r0, [sp, #24]
b .LBB3_1
.LBB3_4:
add r0, sp, #28
str r0, [sp, #20]
mov r0, #79
str r0, [sp, #16]
b .LBB3_5
.LBB3_5: @ =>This Inner Loop Header: Depth=1
ldr r0, [sp, #16]
cmp r0, #0
blo .LBB3_8
b .LBB3_6
.LBB3_6: @ in Loop: Header=BB3_5 Depth=1
ldr r0, [sp, #16]
add r1, r0, #9
ldr r2, [sp, #20]
str r1, [r2, -r0, lsl #2]
b .LBB3_7
.LBB3_7: @ in Loop: Header=BB3_5 Depth=1
ldr r0, [sp, #16]
sub r0, r0, #1
str r0, [sp, #16]
b .LBB3_5
.LBB3_8:
mov r0, #0
str r0, [sp, #12]
b .LBB3_9
.LBB3_9: @ =>This Inner Loop Header: Depth=1
ldr r0, [sp, #12]
cmp r0, #99
bhi .LBB3_12
b .LBB3_10
.LBB3_10: @ in Loop: Header=BB3_9 Depth=1
ldr r0, [sp, #12]
ldr r1, .LCPI3_0
.LPC3_0:
add r1, pc, r1
strb r0, [r1, r0]
b .LBB3_11
.LBB3_11: @ in Loop: Header=BB3_9 Depth=1
ldr r0, [sp, #12]
add r0, r0, #1
str r0, [sp, #12]
b .LBB3_9
.LBB3_12:
mov r0, #0
str r0, [sp, #8]
b .LBB3_13
.LBB3_13: @ =>This Inner Loop Header: Depth=1
ldr r0, [sp, #8]
cmp r0, #99
bhi .LBB3_16
b .LBB3_14
.LBB3_14: @ in Loop: Header=BB3_13 Depth=1
ldr r0, [sp, #8]
ldr r1, .LCPI3_1
.LPC3_1:
add r1, pc, r1
strb r0, [r1, r0]
b .LBB3_15
.LBB3_15: @ in Loop: Header=BB3_13 Depth=1
ldr r0, [sp, #8]
add r0, r0, #1
str r0, [sp, #8]
b .LBB3_13
.LBB3_16:
mov r0, #0
mov sp, r11
pop {r11, lr}
bx lr
.p2align 2
@ %bb.17:
.LCPI3_0:
.long main.static_array-(.LPC3_0+8)
.LCPI3_1:
.long global_array-(.LPC3_1+8)
.Lfunc_end3:
.size main, .Lfunc_end3-main
.cantunwind
.fnend
@ -- End function
.type .L.str,%object @ @.str
.section .rodata.str1.1,"aMS",%progbits,1
.L.str:
.asciz "%c %d %d %d %d\r\n"
.size .L.str, 17

.type .L.str.1,%object @ @.str.1
.L.str.1:
.asciz "123"
.size .L.str.1, 4

.type n1,%object @ @n1
.data
.globl n1
.p2align 2
n1:
.long 333 @ 0x14d
.size n1, 4

.type global_array,%object @ @global_array
.bss
.globl global_array
global_array:
.zero 100
.size global_array, 100

.type main.static_array,%object @ @main.static_array
.local main.static_array
.comm main.static_array,100,1

.ident "Android (5900059 based on r365631c) clang version 9.0.8 (https://android.googlesource.com/toolchain/llvm-project 207d7abc1a2abf3ef8d4301736d6a7ebc224a290) (based on LLVM 9.0.8svn)"
.section ".note.GNU-stack","",%progbits

位操作

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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
    .text
.syntax unified
.eabi_attribute 67, "2.09" @ Tag_conformance
.cpu arm7tdmi
.eabi_attribute 6, 2 @ Tag_CPU_arch
.eabi_attribute 8, 1 @ Tag_ARM_ISA_use
.eabi_attribute 9, 1 @ Tag_THUMB_ISA_use
.eabi_attribute 34, 0 @ Tag_CPU_unaligned_access
.eabi_attribute 15, 1 @ Tag_ABI_PCS_RW_data
.eabi_attribute 16, 1 @ Tag_ABI_PCS_RO_data
.eabi_attribute 17, 2 @ Tag_ABI_PCS_GOT_use
.eabi_attribute 20, 1 @ Tag_ABI_FP_denormal
.eabi_attribute 21, 1 @ Tag_ABI_FP_exceptions
.eabi_attribute 23, 3 @ Tag_ABI_FP_number_model
.eabi_attribute 24, 1 @ Tag_ABI_align_needed
.eabi_attribute 25, 1 @ Tag_ABI_align_preserved
.eabi_attribute 38, 1 @ Tag_ABI_FP_16bit_format
.eabi_attribute 18, 4 @ Tag_ABI_PCS_wchar_t
.eabi_attribute 26, 2 @ Tag_ABI_enum_size
.eabi_attribute 14, 0 @ Tag_ABI_PCS_R9_use
.file "func.c"
.globl func_arg_type @ -- Begin function func_arg_type
.p2align 2
.type func_arg_type,%function
.code 32 @ @func_arg_type
func_arg_type:
.fnstart
@ %bb.0:
.save {r4, r5, r6, r7, r8, r10, r11, lr}
push {r4, r5, r6, r7, r8, r10, r11, lr}
.setfp r11, sp, #24
add r11, sp, #24
.pad #40
sub sp, sp, #40
ldr r12, [r11, #16]
ldr lr, [r11, #12]
ldr r4, [r11, #8]
mov r5, r1
mov r6, r0
strb r0, [r11, #-25]
strh r1, [r11, #-28]
str r2, [sp, #32]
str r3, [sp, #28]
ldrb r1, [r11, #-25]
ldrsh r2, [r11, #-28]
ldr r3, [sp, #32]
ldr r0, [sp, #28]
ldr r7, [r11, #8]
mov r8, sp
str r7, [r8, #4]
str r0, [r8]
ldr r0, .LCPI0_0
.LPC0_0:
add r0, pc, r0
str r12, [sp, #24] @ 4-byte Spill
str lr, [sp, #20] @ 4-byte Spill
str r4, [sp, #16] @ 4-byte Spill
str r5, [sp, #12] @ 4-byte Spill
str r6, [sp, #8] @ 4-byte Spill
bl printf
sub sp, r11, #24
pop {r4, r5, r6, r7, r8, r10, r11, lr}
bx lr
.p2align 2
@ %bb.1:
.LCPI0_0:
.long .L.str-(.LPC0_0+8)
.Lfunc_end0:
.size func_arg_type, .Lfunc_end0-func_arg_type
.cantunwind
.fnend
@ -- End function
.globl func_ret_int @ -- Begin function func_ret_int
.p2align 2
.type func_ret_int,%function
.code 32 @ @func_ret_int
func_ret_int:
.fnstart
@ %bb.0:
.pad #16
sub sp, sp, #16
mov r0, #0
str r0, [sp, #12]
str r0, [sp, #8]
str r0, [sp, #4]
str r0, [sp]
mov r0, #1
str r0, [sp]
mov r0, #2
str r0, [sp, #4]
mov r0, #3
str r0, [sp, #8]
ldr r0, .LCPI1_0
.LPC1_0:
add r0, pc, r0
str r0, [sp, #12]
ldr r0, [sp, #8]
add sp, sp, #16
bx lr
.p2align 2
@ %bb.1:
.LCPI1_0:
.long .L.str.1-(.LPC1_0+8)
.Lfunc_end1:
.size func_ret_int, .Lfunc_end1-func_ret_int
.cantunwind
.fnend
@ -- End function
.globl func_ret_struct @ -- Begin function func_ret_struct
.p2align 2
.type func_ret_struct,%function
.code 32 @ @func_ret_struct
func_ret_struct:
.fnstart
@ %bb.0:
mov r1, #0
str r1, [r0, #12]
str r1, [r0, #8]
str r1, [r0, #4]
str r1, [r0]
mov r1, #1
str r1, [r0]
mov r1, #2
str r1, [r0, #4]
mov r1, #3
str r1, [r0, #8]
ldr r1, .LCPI2_0
.LPC2_0:
add r1, pc, r1
str r1, [r0, #12]
bx lr
.p2align 2
@ %bb.1:
.LCPI2_0:
.long .L.str.1-(.LPC2_0+8)
.Lfunc_end2:
.size func_ret_struct, .Lfunc_end2-func_ret_struct
.cantunwind
.fnend
@ -- End function
.globl main @ -- Begin function main
.p2align 2
.type main,%function
.code 32 @ @main
main:
.fnstart
@ %bb.0:
.pad #36
sub sp, sp, #36
mov r2, #0
str r2, [sp, #32]
str r0, [sp, #28]
str r1, [sp, #24]
mov r0, #256
str r0, [sp, #20]
ldr r0, [sp, #20]
mvn r0, r0 @ ~
str r0, [sp, #16]
ldr r0, [sp, #20]
ldr r1, [sp, #16]
and r0, r0, r1 @ &
str r0, [sp, #12]
ldr r0, [sp, #20]
ldr r1, [sp, #12]
eor r0, r0, r1 @ ^ eor
str r0, [sp, #8]
ldr r0, [sp, #20]
asr r0, r0, #1 @ >>
str r0, [sp, #4]
ldr r0, [sp, #20]
lsl r0, r0, #1 @ <<
str r0, [sp]
mov r0, r2
add sp, sp, #36
bx lr
.Lfunc_end3:
.size main, .Lfunc_end3-main
.cantunwind
.fnend
@ -- End function
.type .L.str,%object @ @.str
.section .rodata.str1.1,"aMS",%progbits,1
.L.str:
.asciz "%c %d %d %d %d\r\n"
.size .L.str, 17

.type .L.str.1,%object @ @.str.1
.L.str.1:
.asciz "123"
.size .L.str.1, 4

.type n1,%object @ @n1
.data
.globl n1
.p2align 2
n1:
.long 333 @ 0x14d
.size n1, 4

.type global_array,%object @ @global_array
.bss
.globl global_array
global_array:
.zero 100
.size global_array, 100


.ident "Android (5900059 based on r365631c) clang version 9.0.8 (https://android.googlesource.com/toolchain/llvm-project 207d7abc1a2abf3ef8d4301736d6a7ebc224a290) (based on LLVM 9.0.8svn)"
.section ".note.GNU-stack","",%progbits


C++程序逆向分析

  • 类、对象的内存布局
  • this指针
  • 构造函数、析构函数
  • 成员函数、成员变量
  • 虚函数
  • 继承、重载
  • 异常

函数入口__libc_init,再执行main函数

main_class.cpp

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

#include <stdio.h>

class CNumber{
//1. 空类
//2. 成员变量
//3. 成员函数, 隐藏的this, r0
//4. 构造函数,
//5. 全局对象 .init_array 初始化, 静态对象在函数中初始化一次
//6. 析构函数
//7. 虚函数
// 虚表 构造和析构函数中,把虚表地址存在对象的0地址上,虚表中有虚函数的地址
// ida 静态分析添加虚表的结构体, 按T把偏移转成结构体
public:
CNumber() {
printf("CNumber");
}
~CNumber() {
printf("~CNumber");
}
void setNum1(int n) {
num1 = n;
}
int getNum1() {
return num1;
}
int getNum2() {
return num2;
}
virtual void setNum3(int n) {
num3 = n;
}
virtual int getNum3() {
return num3;
}
public:
int num1;
int num2;
int num3;
};

// CNumber g_cnumber; // .init_array 初始化
// CNumber g_cnumber2;
int main(int argc, char const *argv[])
{
// static CNumber s_cnumber;
CNumber cnumber;
cnumber.num1 = 1;
cnumber.num2 = 2;
cnumber.setNum3(3);
CNumber* lpnumber = &cnumber;
lpnumber->setNum3(100);
return lpnumber->getNum3() ;
}

main_class_1.s

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
    .text
.syntax unified
.eabi_attribute 67, "2.09" @ Tag_conformance
.cpu arm7tdmi
.eabi_attribute 6, 2 @ Tag_CPU_arch
.eabi_attribute 8, 1 @ Tag_ARM_ISA_use
.eabi_attribute 9, 1 @ Tag_THUMB_ISA_use
.eabi_attribute 34, 0 @ Tag_CPU_unaligned_access
.eabi_attribute 15, 1 @ Tag_ABI_PCS_RW_data
.eabi_attribute 16, 1 @ Tag_ABI_PCS_RO_data
.eabi_attribute 17, 2 @ Tag_ABI_PCS_GOT_use
.eabi_attribute 20, 1 @ Tag_ABI_FP_denormal
.eabi_attribute 21, 1 @ Tag_ABI_FP_exceptions
.eabi_attribute 23, 3 @ Tag_ABI_FP_number_model
.eabi_attribute 24, 1 @ Tag_ABI_align_needed
.eabi_attribute 25, 1 @ Tag_ABI_align_preserved
.eabi_attribute 38, 1 @ Tag_ABI_FP_16bit_format
.eabi_attribute 18, 4 @ Tag_ABI_PCS_wchar_t
.eabi_attribute 26, 2 @ Tag_ABI_enum_size
.eabi_attribute 14, 0 @ Tag_ABI_PCS_R9_use
.file "main_class.cpp"
.globl main @ -- Begin function main
.p2align 2
.type main,%function
.code 32 @ @main
main:
.fnstart
@ %bb.0:
.pad #12
sub sp, sp, #12
mov r2, #0
str r2, [sp, #8]
str r0, [sp, #4]
str r1, [sp]
mov r0, r2
add sp, sp, #12
bx lr
.Lfunc_end0:
.size main, .Lfunc_end0-main
.cantunwind
.fnend
@ -- End function

.ident "Android (5900059 based on r365631c) clang version 9.0.8 (https://android.googlesource.com/toolchain/llvm-project 207d7abc1a2abf3ef8d4301736d6a7ebc224a290) (based on LLVM 9.0.8svn)"
.section ".note.GNU-stack","",%progbits

main_class_2.s

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
    .text
.syntax unified
.eabi_attribute 67, "2.09" @ Tag_conformance
.cpu arm7tdmi
.eabi_attribute 6, 2 @ Tag_CPU_arch
.eabi_attribute 8, 1 @ Tag_ARM_ISA_use
.eabi_attribute 9, 1 @ Tag_THUMB_ISA_use
.eabi_attribute 34, 0 @ Tag_CPU_unaligned_access
.eabi_attribute 15, 1 @ Tag_ABI_PCS_RW_data
.eabi_attribute 16, 1 @ Tag_ABI_PCS_RO_data
.eabi_attribute 17, 2 @ Tag_ABI_PCS_GOT_use
.eabi_attribute 20, 1 @ Tag_ABI_FP_denormal
.eabi_attribute 21, 1 @ Tag_ABI_FP_exceptions
.eabi_attribute 23, 3 @ Tag_ABI_FP_number_model
.eabi_attribute 24, 1 @ Tag_ABI_align_needed
.eabi_attribute 25, 1 @ Tag_ABI_align_preserved
.eabi_attribute 38, 1 @ Tag_ABI_FP_16bit_format
.eabi_attribute 18, 4 @ Tag_ABI_PCS_wchar_t
.eabi_attribute 26, 2 @ Tag_ABI_enum_size
.eabi_attribute 14, 0 @ Tag_ABI_PCS_R9_use
.file "main_class.cpp"
.globl main @ -- Begin function main
.p2align 2
.type main,%function
.code 32 @ @main
main:
.fnstart
@ %bb.0:
.pad #16
sub sp, sp, #16
mov r2, #0
str r2, [sp, #12]
str r0, [sp, #8]
str r1, [sp, #4]
mov r0, #1 @ r0 = 1
str r0, [sp] @ cnumber.num1 = r0;
ldr r0, [sp] @ return cnumber.num1;
add sp, sp, #16
bx lr
.Lfunc_end0:
.size main, .Lfunc_end0-main
.cantunwind
.fnend
@ -- End function

.ident "Android (5900059 based on r365631c) clang version 9.0.8 (https://android.googlesource.com/toolchain/llvm-project 207d7abc1a2abf3ef8d4301736d6a7ebc224a290) (based on LLVM 9.0.8svn)"
.section ".note.GNU-stack","",%progbits

main_class_3.s

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
    .text
.syntax unified
.eabi_attribute 67, "2.09" @ Tag_conformance
.cpu arm7tdmi
.eabi_attribute 6, 2 @ Tag_CPU_arch
.eabi_attribute 8, 1 @ Tag_ARM_ISA_use
.eabi_attribute 9, 1 @ Tag_THUMB_ISA_use
.eabi_attribute 34, 0 @ Tag_CPU_unaligned_access
.eabi_attribute 15, 1 @ Tag_ABI_PCS_RW_data
.eabi_attribute 16, 1 @ Tag_ABI_PCS_RO_data
.eabi_attribute 17, 2 @ Tag_ABI_PCS_GOT_use
.eabi_attribute 20, 1 @ Tag_ABI_FP_denormal
.eabi_attribute 21, 1 @ Tag_ABI_FP_exceptions
.eabi_attribute 23, 3 @ Tag_ABI_FP_number_model
.eabi_attribute 24, 1 @ Tag_ABI_align_needed
.eabi_attribute 25, 1 @ Tag_ABI_align_preserved
.eabi_attribute 38, 1 @ Tag_ABI_FP_16bit_format
.eabi_attribute 18, 4 @ Tag_ABI_PCS_wchar_t
.eabi_attribute 26, 2 @ Tag_ABI_enum_size
.eabi_attribute 14, 0 @ Tag_ABI_PCS_R9_use
.file "main_class.cpp"
.globl main @ -- Begin function main
.p2align 2
.type main,%function
.code 32 @ @main
main:
.fnstart
@ %bb.0:
.save {r11, lr}
push {r11, lr}
.setfp r11, sp
mov r11, sp
.pad #24
sub sp, sp, #24
mov r2, #0
str r2, [r11, #-4]
str r0, [r11, #-8]
str r1, [sp, #12]
mov r0, #1
str r0, [sp, #4]
mov r0, #2
str r0, [sp, #8]
add r0, sp, #4
mov r1, #3
str r0, [sp] @ 4-byte Spill
bl _ZN7CNumber7setNum1Ei
ldr r0, [sp] @ 4-byte Reload
bl _ZN7CNumber7getNum1Ev
mov sp, r11
pop {r11, lr}
bx lr
.Lfunc_end0:
.size main, .Lfunc_end0-main
.fnend
@ -- End function
.section .text._ZN7CNumber7setNum1Ei,"axG",%progbits,_ZN7CNumber7setNum1Ei,comdat
.weak _ZN7CNumber7setNum1Ei @ -- Begin function _ZN7CNumber7setNum1Ei
.p2align 2
.type _ZN7CNumber7setNum1Ei,%function
.code 32 @ @_ZN7CNumber7setNum1Ei
_ZN7CNumber7setNum1Ei:
.fnstart
@ %bb.0:
.pad #8
sub sp, sp, #8
str r0, [sp, #4]
str r1, [sp]
ldr r0, [sp, #4]
ldr r1, [sp]
str r1, [r0]
add sp, sp, #8
bx lr
.Lfunc_end1:
.size _ZN7CNumber7setNum1Ei, .Lfunc_end1-_ZN7CNumber7setNum1Ei
.cantunwind
.fnend
@ -- End function
.section .text._ZN7CNumber7getNum1Ev,"axG",%progbits,_ZN7CNumber7getNum1Ev,comdat
.weak _ZN7CNumber7getNum1Ev @ -- Begin function _ZN7CNumber7getNum1Ev
.p2align 2
.type _ZN7CNumber7getNum1Ev,%function
.code 32 @ @_ZN7CNumber7getNum1Ev
_ZN7CNumber7getNum1Ev:
.fnstart
@ %bb.0:
.pad #4
sub sp, sp, #4
str r0, [sp]
ldr r0, [sp]
ldr r0, [r0]
add sp, sp, #4
bx lr
.Lfunc_end2:
.size _ZN7CNumber7getNum1Ev, .Lfunc_end2-_ZN7CNumber7getNum1Ev
.cantunwind
.fnend
@ -- End function

.ident "Android (5900059 based on r365631c) clang version 9.0.8 (https://android.googlesource.com/toolchain/llvm-project 207d7abc1a2abf3ef8d4301736d6a7ebc224a290) (based on LLVM 9.0.8svn)"
.section ".note.GNU-stack","",%progbits


C++11&Art虚拟机开发


LLVM&OLLVM

  • llvm简介与llvm编译,调试llvm
  • llvm pass
  • ollvm
  • Control Flow Flattening(fla,控制流程平坦化)
  • Bogus Control Flow(bcf, 虚假控制流程)
  • Instructions Substitution(sub, 指令替换)
  • StringObfuscation(sobf,字符串加密)
  • 逆向ollvm的通用方法
  • 逆向ollvm的非通用方法

llvm简介与调试

  • clang -emit-llvm -S hello_clang.c -o hello_clang.ll 将c代码转成IR
  • lli hello_clang.ll 利用lli在本地环境执行这个IR代码
  • llvm-as 将IR转成Bytecode
  • llvm-dis hello_clang.bc -o hello_clang.ll 把Bytecode转成IR
  • llc将Bytecode转成二进制

调试Clang

使用clion调试的时候llvm/tools/driver/driver.cpp为代码入口

直接使用clang命令进行一个c的编译工作,即可进入driver.cpp的流程

具体编译的事项填入program arguments

调试opt

llvm自带的pass是用来精简优化的, ollvm是用来做混淆的

  • opt --help 自带的pass
  • opt --print-bb hello_clang.bc:打印ByteCode的用途

pass是框架, 在llvm最重要的一部分, 可以完成编译器的转换和优化的工作, pass继承llvm:Pass类
https://llvm.org/doxygen/classllvm_1_1Pass.html

一个案例pass

llvm/lib/Transforms/Hello/Hello.cpp
打印一下函数的名字
命令为:opt -load 'xxxx/llvm-project-9.0.1/llvm/cmake-build-debug/lib/LLVMHello.so' -hello hello_clang.bc
执行效果后就是把函数名字打印出来

如何调试

opt -load xxx -xxx hello.c
具体的参数填入program arguments
代码的opt入口在于llvm/tools/opt/opt.cpp


自写llvm pass

函数名称加密Pass

1
2
3
4
5
路径:/llvm/lib/Transforms/EncodeFunctionName

EncodeFunctionName
-- CMakeLists.txt
-- EncodeFunctionName.cpp
CMakeLists.txt
1
2
3
4
5
6
add_llvm_library( EncodeFunctionName MODULE
EncodeFunctionName.cpp

PLUGIN_TOOL
opt
)

EncodeFunctionName.cpp

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
#include "llvm/Pass.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/raw_ostream.h"

#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"

using namespace llvm;

namespace {
struct EncodeFunctionName : public FunctionPass{
static char ID;
EncodeFunctionName() : FunctionPass(ID) {}
bool runOnFunction(Function &F) override {
errs() << "Hello ThisIsEncodeFunctionName: ";
errs().write_escaped(F.getName()) << " --->";

if (F.getName().compare("main") != 0){
llvm::MD5 Hasher;
llvm::MD5::MD5Result Hash;
Hasher.update("test");
Hasher.update(F.getName());
Hasher.final(Hash);

SmallString<32> HexString;
llvm::MD5::stringifyResult(Hash, HexString);
F.setName(HexString);
}

errs().write_escaped(F.getName()) << "\r\n" ;
return false;
}
};
}

char EncodeFunctionName::ID = 0;
static RegisterPass<EncodeFunctionName> X("encode", "Encode Function Name Pass",
false /* Only looks at CFG */,
false /* Analysis Pass */);

static llvm::RegisterStandardPasses Y(
llvm::PassManagerBuilder::EP_EarlyAsPossible,
[](const llvm::PassManagerBuilder &Builder,
llvm::legacy::PassManagerBase &PM) { PM.add(new EncodeFunctionName()); });

编译命令

1
2
3
./bin/clang -emit-llvm -S /root/Documents/some_c_cpp/arm02/hello.c -o hello.ll

./bin/opt -load ./lib/EncodeFunctionName.so -encode /root/Documents/some_c_cpp/arm02/hello.bc

在LLvm源码之外开发Pass

1
2
3
4
5
6
7
路径:/root/Document/some_c_cpp/arm02/OutPassEncodeFunctionName

OutPassEncodeFunctionName
-- OutEncodeFunctionName
-- CMakeLists.txt
-- OutEncodeFunctionName.cpp
-- CMakeLists.txt
CMakeLists.txt-out
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cmake_minimum_required(VERSION 3.18)

project(outPass)

set(LLVM_DIR /root/Documents/llvm-project-9.0.1/llvm/cmake-build-debug/lib/cmake/llvm)

find_package(LLVM REQUIRED CONFIG)

list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}")
include(AddLLVM)

add_definitions(${LLVM_DEFINITIONS})
include_directories(${LLVM_INCLUDE_DIRS})

add_subdirectory(OutEncodeFunctionName)
CMakeLists.txt-inner
1
2
3
4
5
6
7
8
add_llvm_library(OutEncodeFunctionName MODULE
OutEncodeFunctionName.cpp

DEPENDS
intrinsics_gen
PLUGIN_TOOL
opt
)
OutEncodeFunctionName.cpp
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
#include "llvm/Pass.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/raw_ostream.h"

#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"

using namespace llvm;

namespace {
struct OutEncodeFunctionName : public FunctionPass{
static char ID;
OutEncodeFunctionName() : FunctionPass(ID) {}
bool runOnFunction(Function &F) override {
errs() << "Hello ThisIsOutEncodeFunctionName: ";
errs().write_escaped(F.getName()) << " --->";

if (F.getName().compare("main") != 0){
llvm::MD5 Hasher;
llvm::MD5::MD5Result Hash;
Hasher.update("test");
Hasher.update(F.getName());
Hasher.final(Hash);

SmallString<32> HexString;
llvm::MD5::stringifyResult(Hash, HexString);
F.setName(HexString);
}

errs().write_escaped(F.getName()) << "\r\n" ;
return false;
}
};
}

char OutEncodeFunctionName::ID = 0;
static RegisterPass<OutEncodeFunctionName> X("encode", "Encode Function Name Pass",
false /* Only looks at CFG */,
false /* Analysis Pass */);

static llvm::RegisterStandardPasses Y(
llvm::PassManagerBuilder::EP_EarlyAsPossible,
[](const llvm::PassManagerBuilder &Builder,
llvm::legacy::PassManagerBase &PM) { PM.add(new OutEncodeFunctionName()); });

编译命令

1
/root/Documents/llvm-project-9.0.1/llvm/cmake-build-debug/bin/opt -load ./OutEncodeFunctionName.so -encode /root/Documents/some_c_cpp/arm02/hello.bc

让Clang使用Pass

命令clang路由so文件
1
clang -Xclang -load -Xclang /root/CLionProjects/outPassEncodeFunctionName/cmake-build-debug/OutEncodeFunctionName/OutEncodeFunctionName.so -encode hello.ll -o hello
直接嵌套在clang llvm体系内
include

新增头文件在llvm/include/llvm/Transforms/EncodeFunctionName/EncodeFunctionName.h

1
2
3
4
5
6
7
8
9
10
11
12
13
//
// Created by root on 5/21/21.
//

#ifndef LLVM_ENCODEFUNCTIONNAME_H
#define LLVM_ENCODEFUNCTIONNAME_H
#include "llvm/Pass.h"

namespace llvm{
Pass* createEncodeFunctionName();
}

#endif // LLVM_ENCODEFUNCTIONNAME_H
source
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
#include "llvm/Pass.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/raw_ostream.h"

#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"

#include "llvm/Transforms/EncodeFunctionName/EncodeFunctionName.h"

using namespace llvm;

namespace {
struct EncodeFunctionName : public FunctionPass{
static char ID;
EncodeFunctionName() : FunctionPass(ID) {}
bool runOnFunction(Function &F) override {
errs() << "Hello ThisIsEncodeFunctionName: ";
errs().write_escaped(F.getName()) << " --->";

if (F.getName().compare("main") != 0){
llvm::MD5 Hasher;
llvm::MD5::MD5Result Hash;
Hasher.update("test");
Hasher.update(F.getName());
Hasher.final(Hash);

SmallString<32> HexString;
llvm::MD5::stringifyResult(Hash, HexString);
F.setName(HexString);
}

errs().write_escaped(F.getName()) << "\r\n" ;
return false;
}
};
}

char EncodeFunctionName::ID = 0;
static RegisterPass<EncodeFunctionName> X("encode", "Encode Function Name Pass",
false /* Only looks at CFG */,
false /* Analysis Pass */);

static llvm::RegisterStandardPasses Y(
llvm::PassManagerBuilder::EP_EarlyAsPossible,
[](const llvm::PassManagerBuilder &Builder,
llvm::legacy::PassManagerBase &PM) { PM.add(new EncodeFunctionName()); });




// 新增
Pass* createEncodeFunctionName(){
return new EncodeFunctionName();
}
IPO/PassManagerBuilder

路径:llvm/lib/Transforms/IPO/PassManagerBuilder

  • 新增头文件
1
#include "llvm/Transforms/EncodeFunctionName/EncodeFunctionName.h"
  • PassManagerBuilder::populateModulePassManager

新增一条

1
MPM.add(createEncodeFunctionName());

so动态库转a静态库
  • EncodeFunctionName/CMAKELists.txt
1
2
3
4
5
6
7
8
9
10
add_llvm_library(LLVMEncodeFunctionName 
EncodeFunctionName.cpp

ADDITIONAL_HEADER_DIRS
${LLVM_MAIN_INCLUDE_DIR}/llvm/Transforms
${LLVM_MAIN_INCLUDE_DIR}/llvm/Transforms/Scalar

DEPENDS
intrinsics_gen
)
  • /llvm/lib/Transforms/LLVMBuild.txt
1
2
3
4
5
6
7
[common]
subdirectories = AggressiveInstCombine Coroutines IPO InstCombine Instrumentation Scalar Utils Vectorize ObjCARC EncodeFunctionName

[component_0]
type = Group
name = Transforms
parent = Libraries
  • /llvm/lib/Transfroms/IPO/LLVMBuild.txt
1
2
3
4
5
6
[component_0]
type = Library
name = IPO
parent = Transforms
library_name = ipo
required_libraries = AggressiveInstCombine Analysis BitReader BitWriter Core InstCombine IRReader Linker Object ProfileData Scalar Support TransformUtils Vectorize Instrumentation EncodeFunctionName
  • /llvm/lib/Transfroms/EncodeFunctionName
1
2
3
4
5
[component_0]
type = Library
name = EncodeFunctionName
parent = Transforms
library_name = EncodeFunctionName

调用命令

clang -mllvm -encode_function_name /root/Document/some_c_cpp/arm02/hello.bc -o /root/Document/some_c_cpp/arm02/hello

调试Pass

这里直接在外部的代码块打断点, 然后debug执行二进制opt即可


ollvm的简介和移植

OLLVM简介

ollvm是一个开源项目, 主要是llvm里面Transforms的子模块Obfuscator

OLLVM使用、特性

Bogus Control Flow
  • -mllvm -bcf: activates the bogus control flow pass
  • -mllvm -bcf_loop=3: if the pass is activated, applies it 3 times on a function. Default: 1
  • -mllvm -bcf_prob=40: if the pass is activated, a basic bloc will be obfuscated with a probability of 40%. Default: 30
Control Flow Flattening
  • -mllvm -fla: activates control flow flattening
  • -mllvm -split: activates basic block splitting. Improve the flattening when applied together.
  • -mllvm -split_num=3: if the pass is activated, applies it 3 times on each basic block. Default: 1
Substitution
  • -mllvm -sub: activate instructions substitution
  • -mllvm -sub_loop=3: if the pass is activated, applies it 3 times on a function. Default : 1.


ControlFlowFlattening

移植ollvm代码到单独的so

请查看本地资料相关代码

调试ollvm pass

clang -Xclang -load -Xclang ../obfuscation.so hello.c -o hello

将相关的so进行debug, 执行以上的命令

调试Flattening

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Module 
Function
BasicBlock
Instruction
Instruction
Instruction
SwichInst
BranchInst
ReturnInst
InsertValueInst
StoreInst
Function
Function
Function
Function
Function
Function


调试VPM