1. 程式人生 > >基於Android arm64 Linux got 除錯

基於Android arm64 Linux got 除錯

gdb除錯一下got實現,加深理解。涉及一些gdb常用命令,記錄一下。

環境:Ubuntu 15.10
程式碼: Android-6.0.1_r9
直接編譯的arm 64位eng版本。
啟動模擬器:emulator -system system.img -data userdata.img -ramdisk ramdisk.img
這裡寫圖片描述

這裡寫圖片描述
adb shell直接可用,Good!

示例程式碼:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h> int flag = 1; int count = 0; int main() { pid_t pid; pid = getpid(); printf("sizeof(int) = %ld\n", sizeof(int)); printf("sizeof(long) = %ld\n", sizeof(long)); printf("Target pid = %d\n", pid); while(flag) { printf("Target is running:%d\n"
, count); count++; sleep(10); } return 0; }

反編譯命令,加上-S反編譯帶上原始碼:

/opt/android-6.0.1_r9/out/target/product/generic_arm64/obj/EXECUTABLES/hello_intermediates/LINKED$ aarch64-linux-android-objdump -S hello > hello.dis

    hello:     file format elf64-littleaarch64  


    Disassembly of section .plt:  

    00000000000006
d0 <__libc_init@plt-0x20>: 6d0: a9bf7bf0 stp x16, x30, [sp,#-16]! 6d4: 90000090 adrp x16, 10000 <pthread_atfork+0xf754> 6d8: f947d211 ldr x17, [x16,#4000] 6dc: 913e8210 add x16, x16, #0xfa0 6e0: d61f0220 br x17 6e4: d503201f nop 6e8: d503201f nop 6ec: d503201f nop 00000000000006f0 <__libc_init@plt>: 6f0: 90000090 adrp x16, 10000 <pthread_atfork+0xf754> 6f4: f947d611 ldr x17, [x16,#4008] 6f8: 913ea210 add x16, x16, #0xfa8 6fc: d61f0220 br x17 0000000000000700 <getpid@plt>: 700: 90000090 adrp x16, 10000 <pthread_atfork+0xf754> 704: f947da11 ldr x17, [x16,#4016] 708: 913ec210 add x16, x16, #0xfb0 70c: d61f0220 br x17 0000000000000710 <sleep@plt>: 710: 90000090 adrp x16, 10000 <pthread_atfork+0xf754> 714: f947de11 ldr x17, [x16,#4024] 718: 913ee210 add x16, x16, #0xfb8 71c: d61f0220 br x17 0000000000000720 <__cxa_atexit@plt>: 720: 90000090 adrp x16, 10000 <pthread_atfork+0xf754> 724: f947e211 ldr x17, [x16,#4032] 728: 913f0210 add x16, x16, #0xfc0 72c: d61f0220 br x17 0000000000000730 <printf@plt>: 730: 90000090 adrp x16, 10000 <pthread_atfork+0xf754> 734: f947e611 ldr x17, [x16,#4040] 738: 913f2210 add x16, x16, #0xfc8 73c: d61f0220 br x17 0000000000000740 <__register_atfork@plt>: 740: 90000090 adrp x16, 10000 <pthread_atfork+0xf754> 744: f947ea11 ldr x17, [x16,#4048] 748: 913f4210 add x16, x16, #0xfd0 74c: d61f0220 br x17 Disassembly of section .text: 0000000000000750 <main>: int flag = 1; int count = 0; int main() { 750: a9bd7bfd stp x29, x30, [sp,#-48]! 754: 910003fd mov x29, sp 758: a90153f3 stp x19, x20, [sp,#16] 75c: f90013f5 str x21, [sp,#32] pid_t pid; pid = getpid(); 760: 97ffffe8 bl 700 <getpid@plt> 764: 2a0003f3 mov w19, w0 printf("sizeof(int) = %ld\n", sizeof(int)); 768: 90000000 adrp x0, 0 <abitag-0x250> 76c: d2800081 mov x1, #0x4 // #4 770: 9123a000 add x0, x0, #0x8e8 printf("sizeof(long) = %ld\n", sizeof(long)); printf("Target pid = %d\n", pid); while(flag) { 774: b0000094 adrp x20, 11000 <__dso_handle> int main() { pid_t pid; pid = getpid(); printf("sizeof(int) = %ld\n", sizeof(int)); 778: 97ffffee bl 730 <printf@plt> printf("sizeof(long) = %ld\n", sizeof(long)); 77c: 90000002 adrp x2, 0 <abitag-0x250> 780: d2800101 mov x1, #0x8 // #8 784: 91240040 add x0, x2, #0x900 788: 97ffffea bl 730 <printf@plt> printf("Target pid = %d\n", pid); 78c: 90000001 adrp x1, 0 <abitag-0x250> 790: 91246020 add x0, x1, #0x918 794: 2a1303e1 mov w1, w19 798: 97ffffe6 bl 730 <printf@plt> while(flag) { 79c: b9400a83 ldr w3, [x20,#8] 7a0: 34000203 cbz w3, 7e0 <main+0x90> printf("Target is running:%d\n", count); 7a4: 90000015 adrp x21, 0 <abitag-0x250> 7a8: b0000084 adrp x4, 11000 <__dso_handle> 7ac: 9124c2b5 add x21, x21, #0x930 7b0: 91003093 add x19, x4, #0xc printf("sizeof(int) = %ld\n", sizeof(int)); printf("sizeof(long) = %ld\n", sizeof(long)); printf("Target pid = %d\n", pid); while(flag) { 7b4: 91002294 add x20, x20, #0x8 printf("Target is running:%d\n", count); 7b8: b9400261 ldr w1, [x19] 7bc: aa1503e0 mov x0, x21 7c0: 97ffffdc bl 730 <printf@plt> count++; 7c4: b9400265 ldr w5, [x19] sleep(10); 7c8: 52800140 mov w0, #0xa // #10 printf("Target pid = %d\n", pid); while(flag) { printf("Target is running:%d\n", count); count++; 7cc: 110004a6 add w6, w5, #0x1 7d0: b9000266 str w6, [x19] sleep(10); 7d4: 97ffffcf bl 710 <sleep@plt> printf("sizeof(int) = %ld\n", sizeof(int)); printf("sizeof(long) = %ld\n", sizeof(long)); printf("Target pid = %d\n", pid); while(flag) { 7d8: b9400287 ldr w7, [x20] 7dc: 35fffee7 cbnz w7, 7b8 <main+0x68> printf("Target is running:%d\n", count); count++; sleep(10); } return 0; } 7e0: 52800000 mov w0, #0x0 // #0 7e4: f94013f5 ldr x21, [sp,#32] 7e8: a94153f3 ldp x19, x20, [sp,#16] 7ec: a8c37bfd ldp x29, x30, [sp],#48 7f0: d65f03c0 ret

模擬器上輸入:gdbserver :1234 hello
這裡寫圖片描述

adb forward tcp:1234 tcp:1234
主機端執行:aarch64-linux-android-gdb
這裡寫圖片描述

gdb命令:target remote :1234
這裡寫圖片描述

list命令檢視原始碼,提示file命令載入symbol.
這裡寫圖片描述

android上編譯的動態連結的應用程式類似有位置無關特性,每次載入的地址可能都會變動。靜態編譯的不受此影響。
這裡寫圖片描述

這裡寫圖片描述

hello程式碼段載入至0x5586738000(每次執行都會變動).
aarch64-linux-android-readelf -S hello檢視程式碼段偏移0x750
這裡寫圖片描述

使用如下命令載入symbol,之後可以使用list命令檢視程式碼:
add-symbol-file hello (0x5586738000+0x750)
對應的remove-symbol-file -a addr 可以刪除載入過的符號資訊,加錯了可以重新來過!
這裡寫圖片描述

使用如下命令可以設定載入庫檔案資訊:
set solib-absolute-prefix /opt/android-6.0.1_r9/out/target/product/generic_arm64/symbols
這裡寫圖片描述
暫時只有linker64載入了。

設定幾個除錯斷點。

0000000000000700 <getpid@plt>:
700:    90000090    adrp    x16, 10000 <pthread_atfork+0xf754>
 704:   f947da11    ldr x17, [x16,#4016]
 708:   913ec210    add x16, x16, #0xfb0
 70c:   d61f0220    br  x17

0000000000000710 <sleep@plt>:
 710:   90000090    adrp    x16, 10000 <pthread_atfork+0xf754>
 714:   f947de11    ldr x17, [x16,#4024]
 718:   913ee210    add x16, x16, #0xfb8
 71c:   d61f0220    br  x17

0000000000000720 <__cxa_atexit@plt>:
 720:   90000090    adrp    x16, 10000 <pthread_atfork+0xf754>
 724:   f947e211    ldr x17, [x16,#4032]
 728:   913f0210    add x16, x16, #0xfc0
 72c:   d61f0220    br  x17

0000000000000730 <printf@plt>:
 730:   90000090    adrp    x16, 10000 <pthread_atfork+0xf754>
 734:   f947e611    ldr x17, [x16,#4040]
 738:   913f2210    add x16, x16, #0xfc8
 73c:   d61f0220    br  x17

這幾個庫函式跳轉首地址剛好0x700 0x710 0x720 0x730, 當然不能忘了我們的偏移:0x5586738000
就以sleep為例,x17第一次拿到的地址應該不是正確sleep地址,got修復之後才是正確的。
watch *(0x5586738000+0x10000+0xfb8),來跟蹤檢視。

(gdb) p/x 0x5586738000+0x10000+0xfb8
$1 = 0x5586748fb8

如下列印程式碼也可以得到其地址

(gdb) x/8i 0x5586738710
   0x5586738710:    adrp    x16, 0x5586748000
   0x5586738714:    ldr    x17, [x16,#4024]
   0x5586738718:    add    x16, x16, #0xfb8
   0x558673871c:    br    x17
   0x5586738720:    adrp    x16, 0x5586748000
   0x5586738724:    ldr    x17, [x16,#4032]
   0x5586738728:    add    x16, x16, #0xfc0
   0x558673872c:    br    x17
(gdb) p/x 0x5586748000+4024
$2 = 0x5586748fb8
(gdb) x/8i 0x5586748fb8
   0x5586748fb8:    .inst    0x000006d0 ; undefined
   0x5586748fbc:    .inst    0x00000000 ; undefined
   0x5586748fc0:    .inst    0x000006d0 ; undefined
   0x5586748fc4:    .inst    0x00000000 ; undefined
   0x5586748fc8:    .inst    0x000006d0 ; undefined
   0x5586748fcc:    .inst    0x00000000 ; undefined
   0x5586748fd0:    .inst    0x000006d0 ; undefined
   0x5586748fd4:    .inst    0x00000000 ; undefined
(gdb)

這裡寫圖片描述

b main
再設定一個觀察count值變化的量,方便後續主動暫停。
這裡寫圖片描述
helllo symbol偏移資訊都白加了?或者只加了程式碼段?管它呢,有了偏移一樣幹活。

(gdb) p/x 0x5586738000+0x1100c
$5 = 0x558674900c
(gdb) watch *0x558674900c
Hardware watchpoint 3: *0x558674900c
(gdb) 

就整這三個斷點試試。
這裡寫圖片描述

這裡寫圖片描述
第二個斷點先到。

這裡寫圖片描述
此時還是未顯示其他庫載入。

注意一個細節,檢視下程序的載入庫資訊。
這裡寫圖片描述
從程序的資訊看到,其實庫已經載入完畢,這些資訊直接從核心呈現出來。gdb載入比這個慢,正常。

執行bt命令:
這裡寫圖片描述
深入載入細節不是今天重點,有興趣的自行研究。

這裡寫圖片描述
檢視資訊,確實更新,而且更新後的地址確實有程式碼存在,雖然暫時不能證明就是sleep地址,待庫symbol完整加入之後(從上面可見,此時庫檔案核心已經載入完成)。

繼續全速執行。
這裡寫圖片描述
這回才停在main首地址的地方,由此可見got段修復在main執行前就成功完成了。

info r 可以顯示常規暫存器的值,記錄一下(x86上不能簡寫, info registers)。
這裡寫圖片描述

display/8i ($pc-16),這個可以每次停住顯示PC前後的幾條命令,很喜歡用。
這裡寫圖片描述

此時再看看庫檔案載入資訊,基本都有了。
這裡寫圖片描述

再檢視剛才sleep修改的地址,現在有資訊顯示了。
這裡寫圖片描述

再看看其他幾個got是不是也修改了:
這裡寫圖片描述

大部分都有值了,就看看__register_atfork 以及printf是不是已經設定。
這裡寫圖片描述

單步執行幾次
這裡寫圖片描述

這裡寫圖片描述
此時當然直接就是正確的地址了。

再c兩次,第三個斷點設了,還不知能不能幹活。
這裡寫圖片描述

這裡寫圖片描述
還不錯,如預期工作。

其實大家看我很簡單的設定了這三個斷點,這是事後總結此文的時候整的,前期除錯還是走了很多彎路。
主要是記錄下gdb玩法。

0000000000000710 <sleep@plt>:
 710:   90000090        adrp    x16, 10000 <pthread_atfork+0xf754>
 714:   f947de11        ldr     x17, [x16,#4024]
 718:   913ee210        add     x16, x16, #0xfb8
 71c:   d61f0220        br      x17

got段的理解,以上面這個程式碼簡單總結下:
main函式呼叫 sleep 不直接跳轉執行庫函式sleep(就是為了能做到動態載入),而是跳到如上程式碼,如上程式碼很加單,x17放的就是執行程式碼地址,只不過第一次不是正確的地址,需要修復下,後續每次跳到這,就是正確的地址,跳轉執行。