ARM ELF函數重定位
阿新 • • 發佈:2018-10-28
lose 沒有 成了 strong idea 指令 include 重定向 技術分享
ARM ELF的函數重定位與x86是一致的,但由於匯編指令不同,再鼓搗一遍。
示例代碼:
#include <stdio.h>
#include <stdlib.h>
int main () {
puts ("Hello world");
sleep (1);
FILE *fp = fopen ("1.c", "r");
fclose (fp);
exit (0);
}
通過 readelf -r 可以查看ELF中所有需要重定位的函數,我們以fopen()函數為例,分析其重定位過程。
$ arm-linux-androideabi-readelf -r elf_2 Relocation section ‘.rel.plt‘ at offset 0x278 contains 7 entries: Offset Info Type Sym.Value Sym. Name 00009ff4 00000516 R_ARM_JUMP_SLOT 00000000 fopen
首先main()函數中,通過 bl 82f4
調用fopen(),82f4是一個16進制表示的地址,位於.plt節。
$ arm-linux-androideabi-objdump -d elf_2
000083c8 <main>:
...
8404: ebffffba bl 82f4 <fopen@plt>
Disassembly of section .plt: 000082f4 <fopen@plt>: 82f4: e28fc600 add ip, pc, #0, 12 @由於ARM三級流水,PC = 0x82f4 + 0x8 82f8: e28cca01 add ip, ip, #4096 82fc: e5bcfcf8 ldr pc, [ip, #3320]! @ip + 0xcf8 = 0x9ff4
以上三條指令執行完,從0x9ff4位置取值給pc,完成間接尋址的跳轉。看一下0x9ff4處內容:
(gdb) p/x *0x9ff4
$1 = 0x82b0
程序跳轉到0x82b0位置:
Disassembly of section .plt: 000082b0 <__libc_init@plt-0x14>: 82b0: e52de004 push {lr} ; (str lr, [sp, #-4]!) 82b4: e59fe004 ldr lr, [pc, #4] ; 82c0 <__libc_init@plt-0x4> 82b8: e08fe00e add lr, pc, lr 82bc: e5bef008 ldr pc, [lr, #8]! 82c0: 00001d18 andeq r1, r0, r8, lsl sp
可以看到,這是.plt節的開始位置,IDA幫助我們做了一些顯示的優化,所以其匯編結果與objdump看到的不同,它假裝替我們完成了GOT的重定位過程,實際並非如此:
@ida的顯示結果
.got:00009FF4 fopen_ptr DCD __imp_fopen
下面解析一下.plt節開頭的這幾條指令:
@ 1. stack <- lr
@ 2. lr <- 0x1d18
@ 3. lr <- 0x82c0 + 0x1d18 = 0x9fd8
@ 4. pc <- [0x9fd8 + 0x8], lr <- 0x9fd8 + 0x8 = 0x9fe0
發現程序最終從0x9fe0地址處取值,並間接尋址將其作為地址跳轉過去執行。使用gdb發現,此處靜態值為0x0。顯然這塊地址內容,要由程序運行時動態補充的,否則這條指令將產生0地址訪問異常。
(gdb) p/x *0x82c0
$1 = 0x1d18
(gdb) p/x *0x9fe0
$2 = 0x0
(gdb)
原來GOT的前3項,是為系統預留的(GOT[0][1][2]),其中GOT[1]中是ELF中所有動態庫構成的鏈表的指針,GOT[2]是_dl_runtime_resolve函數指針。這個函數將具體完成函數的重定向過程,並將結果反饋到GOT表中。
因此上面靜態分析時,GOT這3個表項是沒有值的,它們由加載器動態填充。
以前畫的x86的圖,同樣適應 ARM:
ARM ELF函數重定位