您的位置:首页 > 运维架构 > Linux

基于Android arm64 Linux got 调试

2016-02-08 22:03 926 查看
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:

00000000000006d0 <__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放的就是执行代码地址,只不过第一次不是正确的地址,需要修复下,后续每次跳到这,就是正确的地址,跳转执行。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: