adrp指令
一般adrp指令使用来取变量的,经常和add或者ldr指令配合使用。
下面这条指令一般用于将变量地址存入寄存器中
- adrp指令计算过程:4就是对应变量所在的页面相对于当前页面的页偏移(1页是4kb),当前页基地址=PC地址低12位置0,所以对应变量所在页的基地址为=当前页基地址 + 页偏移*0x1000 = 0x10002000+0x4000 = 0x10006000
- add指令得到对应变量的偏移地址 = adrp获取的变量页面基地址+ 对应变量的页内偏移= 0x10006000+0xF87 = 0x10006F87
0x10002345 adrp x8,#0x4
0x10002349 add x8,x8,0xF87
下面的代码是ndk实现的一个JNI接口函数,定义一个全局变量num,JNI接口函数会访问此全局变量。
jint num = 1;
extern "C" JNIEXPORT jstring JNICALL
stringFromJNI(JNIEnv* env, jobject /* this */) {
num = 2;
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
将编译生成的so文件拖进IDA里查看,这里的代码实际是IDA为了自动分析处理过的。#0x34000就是对应变量所在页面基地址(IDA自动分析的,算法和上面我们的算法一致)。
adrp x8,#0x34000实际对应的原始指令应该为adrp x8,#0x25。为什么呢?因为adrp处的PC地址为0x0000F15C, 所以当前页面的基地址为0x0000F000(低12位置0)。(变量页面基地址 - 当前页面基地址)/0x1000 = (0x34000 - 0xF000)/0x1000 = 对应的页偏移 =0x25
实际IDA会进一步优化将其直接显示为如下形式。
我们进一步分析,变量页面基地址为0x34000, 页内偏移为0xFD8,所以对应的变量地址为 0x34FD8.
IDA中查看Section发现其在got表中,而got表此0x34FD8存放的是真实num全局变量的地址,会在链接器加载程序时重定位修复。
我们进一步在so加载到内存中看一下,发现IDA同样会自动分析。
我们再来计算一下,原始指令为adrp x8,#0x25,0x25就是对应变量所在的页面相对于当前页面的页偏移(1页是4kb),当前页基地址=PC地址低12位置0,所以对应变量所在页的基地址为=当前页基地址 + 页偏移*0x1000 = 0x0000007694A90000+0x25000 = 0x0x0000007694Ab5000也就是IDA自动分析出来的那个。
adr和adrl指令
adr是小范围的地址读取指令,adrl是中等范围的地址读取指令。都是将基于当前PC的相对偏移地址值读取到目标寄存器中。
下面这条指令就是将相对与当前PC:0x10002000地址偏移0x432的地址 = 0x10002432这个地址放入x1寄存器中。
0x10002000 adr x1,0x432
一般在IDA中其都会自动分析成对应的地址,例如下面的这个实际指令应该是adrl x1,
#0x1923F