freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

安卓逆向-NDK与JNI详述与实战
2021-08-24 17:34:40

前言

在Android OS上开发应用程序,有提供了两种开发包:SDK和NDK。
我们知道Android的SDK主要是基于Java的,所以在用Android SDK进行开发必须使用Java语言。不过,Android 平台一开始就支持C/C++了,Google说明Android也支持JNI编程,使用第三方应用(比如:NDK)通过JNI调用自己的C动态库。

一、什么是NDK,什么是JNI

1、什么是NDK

NDK全名:Native Develop Kit。

Android NDK 是一个工具集,可让您使用 C 和 C++ 等语言以原生代码实现应用的各个部分。对于特定类型的应用,这可以帮助您重复使用以这些语言编写的代码库。

简单来说,Android NDK 是一个工具集,可以让你通过 C 和 C++ 来实现Android中部分应用程序的功能,不用java开发也可以。

Android 开发语言是Java,Android是基于Linux的,其核心库很多都是C/C++,NDK的作用,就是一个在Java中调用C/C++的方式。NDK本身其实就是一个交叉工作链,包含了Android上的一些库文件,然后,NDK为了方便使用,提供了一些脚本,使得更容易的编译C/C++代码。一般情况,是用NDK工具把C/C++编译为.so文件,然后在Java中调用。

官网
https://developer.android.google.cn/ndk/index.html

image-20210816010314803

2、什么是JNI

JNI全称为Java Native Interface。

JNI是Java的本地接口。Java本支持调用C/C++,即JNI。JNI就是Java调用C++的规范。通过JNI可以使得Java与C/C++进行交互。即可以在Java代码中调用C/C++等语言的代码或者在C/C++代码中调用Java代码。由于JNI是JVM规范的一部分,因此可以将我们写的JNI的程序在任何实现了JNI规范的Java虚拟机中运行。

二、环境安装

在使用ndk之前需要先安装好环境

1、jdk下载地址(安装过程略过)

此处建议安装jdk1.8

https://www.androiddevtools.cn/

image-20210812184812321

2、NDK安装

下载地址,并配置好JAVA_HOME,PATH的环境变量

https://www.androiddevtools.cn/

image-20210816012551623

我们这里是安装的r10 STL debug info版本,下载后解压到C盘

image-20210816012708970

配置环境变量,将ndk的绝对目录配置的path中

image-20210816012841470

cmd窗口,输入

ndk-build

image-20210816013400859

注意:这里有个报错,是因为少配置文件、所在的目录等问题

3、adb安装

下载

https://www.androiddevtools.cn/

image-20210812185426578

解压,配置环境变量

image-20210812185545188

验证adb安装,cmd直接执行adb

image-20210812185627813

三、JNI接口及用法详解

image-20210816104925257

从上图中可知,JNI接口是处在java层和C/C++层之间,承担桥梁作用

1、JNI详解

前面我们也说了JNI的全称是Java Native Interface: Java本地开发接口,它提供了若干的API实现了Java和其他语言的通信(主要是C和C++),目的就是Java可以调用C或C++开发的函数,C或C++也能调用Java的方法。

特点:

其一就是效率,C/C++是本地语言,比java更高效;

其二就是可以复用已经存在的C/C++代码;

其三是Java反编译比C语言容易,一般加密算法都是用C语言编写,不容易被反编译。

如图JNI接口源码:

image-20210816105116287

JNI接口就是一大堆函数的API,纽带、桥梁的作用

2、JNI接口文件分析

刚开始,#include <**>,典型的C语言中代码

image-20210816162455380

之后我们看到很对typedef,typedef是C语言代码,您可以使用它来为类型取一个新的名字。下面的实例为单字节数字定义了一个术语 BYTE

typedef unsigned char BYTE;

在这个类型定义之后,标识符 BYTE 可作为类型 unsigned char的缩写,例如:

BYTE  b1, b2;

详细请参考

https://www.runoob.com/cprogramming/c-typedef.html

image-20210816105539879

typedef uint8_t         jboolean;

就是java的boolean数据类型进行重命名为uint8_t。(前面价格j代表为java语言)

3、NI接口讲解

接口中的类型

1.调用java层普通方法-Call0bjectMethod
2.获取Java层实例字段的值-GetMethodID
3.获取java层实列字段的值-GetObjectField
4.设置java层实例字段的值-SetObjectField
......

1、调用java层普通方法-CallObjectMethod

image-20210816113111680

jobject     (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...);

**jobject:**返回值是object,若是void是表示返回值为空。

**CallObjectMethod:**方法名,并且后面跟着三个参数,要想使用方法需要构建这三个参数。

**JNIEnv:**动态调试经常用到,本地调用的一个接口,提供了大量的jni接口函数去调用,就理解JNI默认传入的即可!默认参数型。
**jobject:**也是默认参数,那么CallObjectMethod最少有两个默认参数。
**jmethodID:**CallObjectMethod运行操作,还需要一个方法ID,这里是java层方法的ID,这个如何获取呢? 可以使用GetMethodID方法。

image-20210816114326548

想要使用jmethodID参数,先要使用GetMethodID方法,然后把这个方法ID作为一个返回值给jmethodID参数。

...:调用方法参数列表细信息。

2、获取Java层实列字段的值-GetMethodID

image-20210816120057305

jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);

**jmethodID:**方法的返回值

GetMethodID:方法名称,后面有四个参数

*JNIEnv:** 默认参数

jclass:jclass参数需要调用findclass()方法,获得findclass()方法的返回值,将返回值传入jclass参数使用。

image-20210816142527200

*const char:**java层方法的名称

*const char:** java层方法的一个签名,就是返回值+参数!

3、获取java层实列字段的值-GetObjectField

image-20210816142848042

jobject (*GetObjectField)(JNIEnv*, jobject, jfieldID);

**jobject:**返回值是object。

**GetObjectField:****方法名,后面跟着三个参数。

**JNIEnv:**动态调试经常用到,本地调用的一个接口,提供了大量的jni接口函数去调用,就理解JNI默认传入的即可!默认参数型。
**jobject:**也是默认参数,那么CallObjectMethod最少有两个默认参数。

**jfieldID:**需要GetFieldID()方法返回值获取jfieldID参数

jfieldID (*GetFieldID)(JNIEnv*, jclass, const char*, const char*);

调用GetFieldID方法需要四个参数

*JNIEnv:**动态调试经常用到,本地调用的一个接口,提供了大量的jni接口函数去调用,就理解JNI默认传入的即可!默认参数型。

**jclass:**jclass参数需要调用findclass()方法,获得findclass()方法的返回值,将返回值传入jclass参数使用

*const char:**是实列字段的名称

*const char:**是实例字段的签名信息

4、设置java层实例字段的值-SetObjectField

image-20210816143538377

void (*SetObjectField)(JNIEnv*, jobject, jfieldID, jobject);

**void:**返回值为void,代表设置好参数就可以,不需要返回

**JNIEnv:**动态调试经常用到,本地调用的一个接口,提供了大量的jni接口函数去调用,就理解JNI默认传入的即可!默认参数型。
**jobject:**也是默认参数,那么CallObjectMethod最少有两个默认参数。

**jfieldID:**都是GetFieldID的返回值

**jobject:**是一个java层实列字段设置的值

根据上面对接口方法的分析,方法大概分为三类

get:获取
set:配置
call:调用

四、通过编译so文件

1、编写C代码

使用记事本编写C语言实现输出Hello word

#include<stdio.h>
int main(){
printf("Hello word!")
return 0;
}

image-20210815200303889

将文件后缀名修改为.c

2、编写Android.mk文件

1)什么是Android.mk文件

Android.mk 文件位于项目 jni/ 目录的子目录中,用于向构建系统描述源文件和共享库。 它实际上是构建系统解析一次或多次的微小 GNU makefile 片段。 Android.mk 文件用于定义 Application.mk、构建系统和环境变量所未定义的项目范围设置。 它还可替换特定模块的项目范围设置。
Android.mk 的语法用于将源文件分组为模块。 模块是静态库、共享库或独立可执行文件。 可在每个 Android.mk 文件中定义一个或多个模块,也可在多个模块中使用同一个源文件。 构建系统只会将共享库放入应用软件包。 此外,静态库可生成共享库。

除了封装库之外,构建系统还可为您处理各种其他详细信息。例如,您无需在 Android.mk 文件中列出标头文件或生成的文件之间的显式依赖关系。 NDK 构建系统会自动为您计算这些关系。

2)Android.mk内容详述

LOCAL_PATH := $(call my-dir)	#获取jni文件路径
include $(CLEAR_VARS)
LOCAL_MODULE := test	#模块名称
LOCAL_SRC_FILES := test.c	#源文件 .c或者.cpp
LOCAL_ARM_MODE := arm	#编译后的指令集ARM指令
LOCAL_LDLIBS += -llog	#依赖库
LOCAL_CFLAGS += -pie -fPIE	#引入PIE
LOCAL_LDFLAGS += -pie -fPIE	#引入PIE
include $(BUILD_SHARED_LIBRARY)	#指定编译文件的类型.so
1)LOCAL_PATH :=(call my-dir):获取jni文件路径,每个mk必须以local开始,my-dir是由Build System提供的,他会返回一个包含mk文件的路径,就是要获取相应的文件目录路径去调用jni属性

2)include $(CLEAR_VARS):CLEAR_VARS变量也是由Build System提供,他会指定清理LOCAL_开头的文件,但不会清理LOCAL_PATH开头的!

3) LOCAL_ARM_MODE :=arm:编译后的指令集,arm每个指令由有四个字节。

4)LOCAL_MODULE :=test : 定义的模块名称,如果这里编译出so文件前面+lib后面+.so

5)LOCAL_SRC_FILES :=test.c : 表示同文件目录下.c文件

6) LOCAL_CFLAGS += -pie -fPIE;LOCAL_LDFLAGS += -pie -fPIE :PIE这个安全机制从4.1引入的,但是Android L之前的系统版本并不会去检验可执行文件是否基于PIE编译出的。因此不会报错,但是android L开始开启验证机制,如果调用可执行文件不是基于PIE方式编译的,则无法运行

6)include $(BUILD_SHARED_LIBRARY):把文件构造建成可执行程序,如果是动态链接库就用:include (shared library),如果是静态链接库:include $(static library)

3、编写Application.mk文件代码

1)什么是Application.mk文件

Application.mk文件,这是android NDK构建系统使用的一个可选构建文件。它的目的是描述应用程序需要哪些模块,也定义了所有模块的一些通用变量。

2)Application.mk内容详述

APP_ABI :=X86 armeabi-v7a

这里只有一行代码,这里是关于程序是否可以在不同框架的CPU上运行,目前主流的Android设备是armeabi-v7a架构的,然后就是x86和armeabi了。如果同时包含了 armeabi,armeabi-v7a和x86,armeabi-v7a是可以兼容armeabi的,所有设备都可以运行。

4、ndk编译c文件为so文件

1)编译方式

**第一种方式:**将三个文件放在$PROJECT/jni/目录下, 其中$PROJECT表示你的工程目录,这样就可以被ndk-build脚本文件找到.(注:在这种方式下,进入jni目录,即$PROJECT/jni/,然后执行ndk-build,就可以直接编译jni生成.so文件了)。

第二种方式:另将三个文件放在$NDK/app//目录,其中$NDK为NDK的安装目录,为你的应用程序名.在这种方式下,进入$NDK安装目录,然后输入make APP=,即可编译你的JNI代码.此种方法是ndk-r4之前的方法,虽然出于兼容的原因目录还支持,但是不建议使用此种方法,因为第一种方法简单,且方便。

2)编译测试

首先创建一个名字为jni的文件夹,分别编辑test.c、Application.mk、Android.mk

image-20210816094749675

在jni执行ndk-build,直接编译jni生成.so文件了

ndk-build

image-20210815213444258

生成两个文件夹:libs、obj

image-20210815213659565

libs文件加里面都是ELF文件,是可以直接在安卓执行的

3)libs库中的armeabi-v7a,armeabi和x86

armeabi-v7a,armeabi和x86都表示的是CPU类型,早期的Android系统几乎只支持ARMv5的CPU架构,但是现在已经很多种了。ARMv5,ARMv7 (从2010年起),x86 (从2011年起),MIPS (从2012年起),ARMv8,MIPS64和x86_64 (从2014年起)等,每一种都关联着一个相应的ABI(应用程序二进制接口(ApplicationBinary Interface)定义了二进制文件(尤其是.so文件)如何运行在相应的系统平台上,从使用的指令集,内存对齐到可用的系统函数库)。Android现在的主流CPU是armeabi-v7a。armeabi-v7a是针对有浮点运算或高级扩展功能的ARMv7CPU。

image-20210815213855355

image-20210815213902660

5、运行so文件

使用adb将armeabi-v7a目录下面的test文件上传到安卓模拟器或者真机(root过后的)

adb devices	#检查设备列表
adb push C:\test\libs\armeabi-v7a\test /data/local/tmp/	#将test文件上传安卓的tmp目录下
adb shell	#获得安卓的shell
su	#切换为root权限
cd /data/local/tmp/	#进入/data/local/tmp/	目录
ls -al	#查看目录下的详细

image-20210816003720055

赋777权限,并执行,测试是否输出hello word

chmod 777 test
./test

image-20210816004931797

五、总结

此章节使用的工具如下:

JDK:需使用jDK1.8B版本的,JDK是 Java 语言的软件开发工具包,主要用于移动设备、嵌入式设备上的java应用程序。JDK是整个java开发的核心,它包含了JAVA的运行环境(JVM+Java系统类库)和JAVA工具。

adb:全称为Android Debug Bridge,就是起到调试桥的作用。通过adb我们可以在Eclipse中方便通过DDMS来调试Android程序,说白了就是debug工具。

ndk:是Android的一种开发工具包,快速开发C、 C++的动态库,并自动将so和应用一起打包成 APK即可通过 NDK在 Android中 使用 JNI与本地代码(如C、C++)交互。

我们通过此章节熟悉了,ndk工具功能及使用方法,了解了jni的概念,通过实践将jni通过ndk编译为so文件,详细讲解了编译的方法及编译过程中使用的到配置文件详述,为日后进一步分析和调试安卓apk建立概念。如有不足之处请多见谅,谢谢。

# android安全 # c语言 # java # SDK # NDK
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录