首页
社区
课程
招聘
[原创]Android逆向新手答疑解惑篇——JNI与动态注册
发表于: 2018-2-15 20:59 18889

[原创]Android逆向新手答疑解惑篇——JNI与动态注册

2018-2-15 20:59
18889

JNI全称为Java Native Interface,是使Java方法与C\C++函数互通的一座桥梁。通俗的讲,它的作用就是使Java可以调用C\C++写的函数、使C\C++可以调用Java写的方法。

众所周知,Android开发一般采用Java语言,虽Google推出了Kotlin语言的开发方案,但其实Kotlin的本质亦是基于Java虚拟机,那么在Android上系统,亦是基于Dalvik虚拟机的,所以性能上,与跟采用Java开发是没有任何区别的。由于Java是虚拟机语言(指需要被编译成虚拟机代码,由虚拟机执行的语言),所以无论是JVM(Java虚拟机)还是Dalvik(Android定制版JVM),其程序性能在性能需求较高的情况下,就显得有些不足了。
那么这个时候就需要编译型语言出马了,编译型语言将源代码编译为机器码直接由CPU执行代码,使性能大幅提升。

Java代码的安全性很弱! 如果你没有逆向Java或者Android程序的经验,那么可以请你写一个简单的Java程序或者Android程序,然后在Github或者其他地方下载一个jadx,打开jadx-gui或者使用命令行,反编译你编译出来的程序,你可能会发现这是一个新世界,噢天哪,代码逻辑清晰可见,简直就跟在看源码一样!当然,这些只是反编译器生成的伪代码,但也足以惊人。
这个时候,你就可以开始考虑将关键代码放到C\C++里面写了,因为其编译之后就只有机器码,机器码可以反编译成汇编,但汇编比高级语言更加的晦涩难懂,没有一定技术功底的人无法直观的理解汇编代码。虽可通过一些神器(如:IDA F5)来获取伪码,但这些伪码相比Java的伪码,简直不堪入目。
所以编写原生代码,不但可以拥有更高的性能,还可获得一定的代码安全性保障。

Google为Android的原生开发提供了开发者工具NDK(Native Development Kit),用来编译C/C++项目。起初的时候构建一个NDK项目还需一番配置,现在随着Android Studio的不断更新,已经可以在Android Studio的项目中直接编写、编译了。

需要先对Android Studio进行一番配置。首先打开Android Studio的设置页面,File-Settings,搜索Android SDK,勾选上CMake(编译C\C++源码的程序)、LLDB(调试器)、NDK,然后点击Apply进行更新。
图片描述
此处我没有勾选NDK是因为我使用自行下载的NDK版本,每个项目自行选择NDK路径。

打开Android Studio新建一个Project,并第一步勾选Include C++ support:
图片描述
其余选项可按需改动。新建完成后,就是一个完整的JNI的Hello World了。

在左侧的Andorid视图中,可以看到比正常的项目多了一个cpp目录,这就是我们存放C\C++源码的地方了:
图片描述
生成的这个函数声明看起来有点反人类,其实他是这样子的

Ctrl单击JNIEXPORT可以看到其宏定义,是一个defalut属性,而JNICALL则是个空定义,所以其实这两个是可以忽略的。
重点关注的是返回类型jstring函数名Java_cn_hluwa_demo01_MainActivity_stringFromJNI参数列表JNIEnv和jobject

大家伙知道,Java中的基本数据类型是int、long、short、float、double、char、byte、boolean这些,为了避免与C语言的基本数据类型冲突,在JNI中,将JAVA的基本数据类型重定义成了:jint、jlong、jshort、jfloat、jdouble、jchar、jbyte、jboolean。那jstring又是怎么回事呢?虽然String不是Java基本数据类型,但它实在是太常用了,所以便有了jstring;对于数组,则是再后面再加个Array,如:jintArray、jbyteArray,但是没有jstringArray,欸,那如何表示呢?还有其他的非基本类型呢? 除了上述以及jclass、jthrowable、jarray这些有专用重定义之外其他类型均使用jobject表示,所以String数组就是jobjectArray啦。Ctrl+单击jstring就可跳到jni.h头文件查看各个定义了。

可以看到这个函数名非常的长,这是因为JNI函数的绑定需要依赖于一个函数命名规则,让Java层一下子就可以找到对应的原生函数。可以先看到java层的代码:

stringFromJNI 加了一个native描述符,表示是一个原生函数,MainActivity是类名,cn.hluwa.demo01是包名,Java_cn_hluwa_demo01_MainActivity_stringFromJNI是对应的C函数名,那么这个规则就很显而易见了,将包名的.替换成_(因为.不能用于函数命名),然后Java_PackName_CLassName_MethodName。运行时,JNI就会依赖此规则来对函数进行绑定。

至于Native层调用Java层呢,JNI提供里一系列函数,比如:

同样在jni.h中可以看到,或可自行查阅文档。

在上述的Java代码中,可以看到static代码块中多了一个System.loadLibrary("native-lib");,在Android开发中,原生代码一般使用C\C++编写,然后编译为一个动态链接库,即文件后缀为".so"的ELF文件loadLibrary的作用就是加载这个动态链接库,这样后面的代码调用才能成功的找到对应的原生函数。而静态代码块的执行时机非常早,比什么构造函数、onCreate都要早,在类加载的时候就被调用库加载并非一定要在当前类、static块中!。加载库还有其他方法,比如使用System.load(String)方法,其传入链接库的具体路径;甚至有的是在Native层中使用dlopen、mmap等方式来进行加载,就相当于自己实现了一个loadLibrary,但是最终的目的都是一样的:将代码加载入内存中
Android编译后的Apk其实只是个zip压缩包,打开后在其lib目录中可以看到那些被loadLibrary加载的库(lib中可能有多个文件夹,对应多种CPU架构)。

如今许多开发者都出于安全性考虑或其他需求,不愿使用函数名规则绑定,而是自己动态注册来绑定native函数。方法也很简单,只需调用RegisterNatives函数即可。其申明如下:


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2019-2-1 18:20 被admin编辑 ,原因:
收藏
免费 6
支持
分享
最新回复 (23)
雪    币: 17
活跃值: (891)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
make
2018-2-16 08:39
0
雪    币: 7
活跃值: (263)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
葫芦娃
2018-2-16 09:33
0
雪    币: 22
活跃值: (139)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
感谢分享
2018-2-16 11:02
0
雪    币: 47147
活跃值: (20450)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
5
葫芦娃春节快乐!
2018-2-16 11:34
0
雪    币: 23080
活跃值: (3432)
能力值: (RANK:648 )
在线值:
发帖
回帖
粉丝
6
新春快乐,感谢分享
2018-2-16 13:02
0
雪    币: 699
活跃值: (444)
能力值: ( LV9,RANK:240 )
在线值:
发帖
回帖
粉丝
7
很棒……把我在CTF里遇到的很多东西都简明扼要的说清楚了
2018-2-17 11:01
0
雪    币: 203
活跃值: (53)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
8
橘子皮同学,我已经把橘子树给你砍来了。
2018-2-22 04:13
0
雪    币: 6818
活跃值: (153)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
2018-2-22 23:02
0
雪    币: 26205
活跃值: (63302)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
10
感谢分享
2018-2-23 16:20
0
雪    币: 19
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
好文章
2018-2-24 23:48
0
雪    币: 38
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
写的很详细,谢谢楼主
2018-2-25 20:38
0
雪    币: 210
活跃值: (641)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
13
2018-2-27 09:15
0
雪    币: 774
活跃值: (1036)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
mark
2018-3-1 16:07
0
雪    币: 6
活跃值: (77)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
15
谢谢分享
2018-3-1 18:57
0
雪    币: 5
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
感谢分享,对新手十分友好
2018-3-3 01:20
0
雪    币: 856
活跃值: (395)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
很详细,感谢
2018-3-4 00:10
0
雪    币: 3
活跃值: (12)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
很好,谢谢
2018-3-23 19:50
0
雪    币: 120
活跃值: (1597)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
都是好帖子!
2020-1-17 15:56
0
雪    币: 379
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
21
mark谢谢
2020-1-29 17:43
0
雪    币: 32
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
make
2020-2-8 03:34
0
雪    币: 231
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
23
感谢分享,学习到了
2020-2-8 11:15
0
雪    币: 709
活跃值: (87)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
谢谢大佬 又学到新知识了
2020-2-29 11:16
0
游客
登录 | 注册 方可回帖
返回
//