Android逆向7-Native逆向
Android逆向7-Native逆向
来自吾爱破解-正己
https://www.52pojie.cn/thread-1701353-1-1.html
NDK(Native Development Kit)是一套用于开发Android应用程序的工具集,它允许在C/C++中编写性能关键的部分代码,并将这些代码与Java代码进行连接。
下载NDK和CMake
下面是cmakelist.txt和native-lib.cpp文件的作用以及简要说明:
文件名 | 作用 | 说明 |
---|---|---|
CMakeLists.txt | 构建配置文件 | CMakeLists.txt是用于配置NDK项目的构建系统的文件。它指定了构建所需的源文件、依赖项、编译选项等。在构建过程中,CMake会根据该文件的指示生成对应的构建脚本,用于编译本地代码并生成本地库。 |
native-lib.cpp | 本地代码实现文件 | native-lib.cpp是包含本地代码实现的文件。它定义了通过Java和本地代码之间进行通信的本地方法。该文件中的函数实现将被编译为本地库,供Java代码调用。 |
1 | public class MainActivity extends AppCompatActivity { |
1 | # For more information about using CMake with Android Studio, read the |
1 |
|
NDK是开发套件,JNI才是调用的框架。所以与其说是NDK开发,不如说是JNI的开发。不过NDK是Android提供的开发套件。JNI可不是,JNI全称Java Native Interface,即Java本地接口,JNI是Java调用Native 语言的一种特性。通过JNI可以使得Java与C/C++机型交互。即可以在Java代码中调用C/C++等语言的代码或者在C/C++代码中调用Java代码。
JNI的两种注册方式
jni静态注册方式
- 优点: 理解和使用方式简单, 属于傻瓜式操作, 使用相关工具按流程操作就行, 出错率低
- 缺点: 当需要更改类名,包名或者方法时, 需要按照之前方法重新生成头文件, 灵活性不高
jni动态注册方式
1 |
|
下面是一些常见的C++数据类型和它们在Java中的对应关系,以及它们在JNI动态注册中的数据类型签名(signature):
C++ 数据类型 | Java 数据类型 | JNI 数据类型签名 |
---|---|---|
jint | int | “I” |
jboolean | boolean | “Z” |
jbyte | byte | “B” |
jchar | char | “C” |
jshort | short | “S” |
jlong | long | “J” |
jfloat | float | “F” |
jdouble | double | “D” |
jobject | Object | “Ljava/lang/Object;” |
jstring | String | “Ljava/lang/String;” |
jarray | Array | “[elementType” |
jobjectArray | Object[] | “[Ljava/lang/Object;” |
jbooleanArray | boolean[] | “[Z” |
jbyteArray | byte[] | “[B” |
jcharArray | char[] | “[C” |
jshortArray | short[] | “[S” |
jintArray | int[] | “[I” |
jlongArray | long[] | “[J” |
jfloatArray | float[] | “[F” |
jdoubleArray | double[] | “[D” |
在JNI动态注册中,需要使用正确的数据类型签名来声明本地方法。例如,如果你要注册一个返回int
类型的本地方法,其数据类型签名应为I
。
ARM基础知识
常见寻址方式
寻址方式 | 描述 |
---|---|
立即数寻址 | 直接使用立即数值作为操作数,例如:MOV R0, #5 |
寄存器直接寻址 | 使用寄存器中的值作为操作数,例如:MOV R0, R1 |
寄存器间接寻址 | 使用寄存器中的值作为内存地址,访问该地址中的数据,例如:LDR R0, [R1] |
寄存器相对寻址 | 使用寄存器中的值加上一个立即偏移量作为内存地址,例如:LDR R0, [R1, #4] |
寄存器变址寻址 | 使用两个寄存器中的值相加作为内存地址,例如:LDR R0, [R1, R2] |
带有变址寄存器的寄存器相对寻址 | 使用寄存器中的值加上另一个寄存器的值乘以一个比例因子作为内存地址,例如:LDR R0, [R1, R2, LSL #2] |
堆栈寻址 | 使用堆栈指针寄存器(如SP)进行操作,例如:PUSH {R0, R1} 或 POP {R0, R1} |
压栈和出栈指令
指令类型 | 指令示例 | 描述 |
---|---|---|
压栈 | PUSH {R0, R1} |
将寄存器R0和R1的内容压入堆栈中 |
压栈 | PUSH {R0-R5} |
将寄存器R0到R5的内容压入堆栈中 |
压栈 | STMDB SP!, {R0-R5} |
将寄存器R0到R5的内容压入堆栈中(与PUSH等效) |
出栈 | POP {R0, R1} |
从堆栈中弹出数据,恢复到寄存器R0和R1中 |
出栈 | POP {R0-R5} |
从堆栈中弹出数据,恢复到寄存器R0到R5中 |
跳转指令
指令类型 | 指令示例 | 描述 |
---|---|---|
无条件跳转 | B label |
无条件跳转到标签label 指向的位置 |
子程序调用 | BL label |
调用子程序,将当前指令的下一条指令地址存入链接寄存器(LR),然后跳转到标签label 指向的位置 |
子程序返回 | BX LR |
返回子程序调用前的位置,跳转到链接寄存器(LR)中存储的地址 |
寄存器跳转 | BX Rn |
跳转到寄存器Rn中存储的地址 |
算术运算指令
汇编中也可以进行算术运算, 比如加减乘除,常用的运算指令用法如表 所示:
指令 | 计算公式 | 备注 |
---|---|---|
ADD Rd, Rn, Rm | Rd = Rn + Rm | 加法运算,指令为 ADD |
ADD Rd, Rn, #immed | Rd = Rn + #immed | 加法运算,指令为 ADD |
ADC Rd, Rn, Rm | Rd = Rn + Rm + 进位 | 带进位的加法运算,指令为 ADC |
ADC Rd, Rn, #immed | Rd = Rn + #immed + 进位 | 带进位的加法运算,指令为 ADC |
SUB Rd, Rn, Rm | Rd = Rn - Rm | 减法 |
SUB Rd, #immed | Rd = Rd - #immed | 减法 |
SUB Rd, Rn, #immed | Rd = Rn - #immed | 减法 |
SBC Rd, Rn, #immed | Rd = Rn - #immed - 借位 | 带借位的减法 |
SBC Rd, Rn ,Rm | Rd = Rn - Rm - 借位 | 带借位的减法 |
MUL Rd, Rn, Rm | Rd = Rn * Rm | 乘法 (32 位) |
UDIV Rd, Rn, Rm | Rd = Rn / Rm | 无符号除法 |
SDIV Rd, Rn, Rm | Rd = Rn / Rm | 有符号除法 |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Matriy's blog!
评论