Android逆向学习笔记(1)——JNI

最近开始学习Android逆向了,刚入门的时候总会有一段比较困难的时期,所以接下来我会通过写博客来激励自己不断学习!博客的形式更偏向日记,所以不会有太多详细讲解。这是Android Security系列的第一篇博客,总所周知Native层逆向是CTF的典中典题型,所以今天的内容主要是安卓Native C++开发的环境搭建。

今天的内容主要参考了这个系列的文章,写得非常清楚并且详细:

上面第二篇文章介绍了传统的JNI实现方法,即创建jni文件夹,添加Android.mk文件等等。同时也提到了一种更加简单的方法——Native C++。

首先需要创建一个Native C++项目,一通设置之后就创建好了:

image-20211020165625383

创建好之后会发现它已经自动帮你实现了Native函数的调用:

image-20211020165921034

image-20211020165929525

更神奇的时修改Native函数定义后可以自动同步Cpp文件中的函数定义,无需重新生成头文件:

image-20211020170254385

image-20211020170303225

运行一下看看效果:

image-20211020170553215

打包APK,可以看到这里是编译了所有架构的so文件:

image-20211020171826133

如果只需要编译特定平台下的so文件可以在build.gradle中加入如下配置(参考官方文档):

1
2
3
4
5
6
7
android {
defaultConfig {
ndk {
abiFilters 'arm64-v8a', 'x86_64'
}
}
}

image-20211020172823960

再演示一下动态注册Native函数的方法,第三篇参考文章里面讲解得很详细:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <jni.h>
#include <string>

#ifdef __cplusplus
extern "C" {
#endif


static const char *className = "cn/bluesadi/ndkdemocpp/MainActivity";

static jstring sayHello(JNIEnv *env, jobject) {
char hello[] = "Hello Native C++";
return env->NewStringUTF(hello);
}

static JNINativeMethod gJni_Methods_table[] = {
{"sayHello", "()Ljava/lang/String;", (void*)sayHello},
};

static int jniRegisterNativeMethods(JNIEnv* env, const char* className,
const JNINativeMethod* gMethods, int numMethods){
jclass clazz;
clazz = (env)->FindClass( className);
if (clazz == nullptr) {
return -1;
}
int result = 0;
if ((env)->RegisterNatives(clazz, gJni_Methods_table, numMethods) < 0) {
result = -1;
}
(env)->DeleteLocalRef(clazz);
return result;
}

jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env = nullptr;
jint result = -1;

if (vm->GetEnv((void**) &env, JNI_VERSION_1_6) != JNI_OK) {
return result;
}

jniRegisterNativeMethods(env, className, gJni_Methods_table, sizeof(gJni_Methods_table) / sizeof(JNINativeMethod));

return JNI_VERSION_1_6;
}

#ifdef __cplusplus
}
#endif

image-20211020180215384

优点是动态注册后函数名可以写得很简洁,我记得某次CTF也出过一次动态注册的题。缺点是Java代码里会报错,看来Android Studio对动态注册的识别还是不太行:

image-20211020180250451