基于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!
反编译命令,加上-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
模拟器上输入: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加载了。
设置几个调试断点。
这几个库函数跳转首地址刚好0x700 0x710 0x720 0x730, 当然不能忘了我们的偏移:0x5586738000
就以sleep为例,x17第一次拿到的地址应该不是正确sleep地址,got修复之后才是正确的。
watch *(0x5586738000+0x10000+0xfb8),来跟踪查看。
如下打印代码也可以得到其地址
b main
再设置一个观察count值变化的量,方便后续主动暂停。
helllo symbol偏移信息都白加了?或者只加了代码段?管它呢,有了偏移一样干活。
就整这三个断点试试。
第二个断点先到。
此时还是未显示其他库加载。
注意一个细节,查看下进程的加载库信息。
从进程的信息看到,其实库已经加载完毕,这些信息直接从内核呈现出来。gdb加载比这个慢,正常。
执行bt命令:
深入加载细节不是今天重点,有兴趣的自行研究。
查看信息,确实更新,而且更新后的地址确实有代码存在,虽然暂时不能证明就是sleep地址,待库symbol完整加入之后(从上面可见,此时库文件内核已经加载完成)。
继续全速运行。
这回才停在main首地址的地方,由此可见got段修复在main运行前就成功完成了。
info r 可以显示常规寄存器的值,记录一下(x86上不能简写, info registers)。
display/8i ($pc-16),这个可以每次停住显示PC前后的几条命令,很喜欢用。
此时再看看库文件加载信息,基本都有了。
再查看刚才sleep修改的地址,现在有信息显示了。
再看看其他几个got是不是也修改了:
大部分都有值了,就看看__register_atfork 以及printf是不是已经设置。
单步执行几次
此时当然直接就是正确的地址了。
再c两次,第三个断点设了,还不知能不能干活。
还不错,如预期工作。
其实大家看我很简单的设置了这三个断点,这是事后总结此文的时候整的,前期调试还是走了很多弯路。
主要是记录下gdb玩法。
got段的理解,以上面这个代码简单总结下:
main函数调用 sleep 不直接跳转执行库函数sleep(就是为了能做到动态加载),而是跳到如上代码,如上代码很加单,x17放的就是执行代码地址,只不过第一次不是正确的地址,需要修复下,后续每次跳到这,就是正确的地址,跳转执行。
环境: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放的就是执行代码地址,只不过第一次不是正确的地址,需要修复下,后续每次跳到这,就是正确的地址,跳转执行。
相关文章推荐
- Linux ps命令详解与示例说明
- Linux学习笔记:为Nagios 4.1.1安装出图插件pnp 0.6.25
- 在Centos6.5上安装xen的两种方式
- Linux下安装webstorm
- 总结一下安装linux系统经验-版本选择-安装ubuntu
- linux 进程间通信 FIFO
- 设置Linux环境变量的方法和区别
- Linux netstat命令详解
- Linux入门回顾
- Linux中使用Boxes安装windows7
- 嵌入式Linux系统的电子书阅读器项目2——Display System
- linux内核的idr学习(二)
- 使用Linux操作系统命令检查服务器主板上每个内存插槽的内存大小
- 【翻译自mos文章】在使用Linux大页内存的配置中,使用drop_cache时导致的ORA-600 [KGHLKREM1]问题
- Linux关机/重启命令
- linux例行性命令
- linux Centos6.5使用yum安装mysql
- linux习惯每天一个命令之mount命令
- Linux忘记 root密码的解决办法
- 在linux下搭建java环境