Linux动态库so反编译,linux 下动态库函数调用--反汇编知识。

****************************************

linux 下动态库函数调用--反汇编知识。

author: hjjdebug

date:   2016年 09月 07日 星期三 14:41:49 CST

****************************************

#include #include void test_so() {     char buffer[256];     printf("---------- hello android ------------\n");     FILE * fp=fopen("1.txt","rt");     if(fp)     {         memset(buffer,0,sizeof(buffer));         fread(buffer,sizeof(buffer),1,fp);         printf("%s\n",buffer);         fclose(fp);     }         else     {         printf("error open 1.txt\n");     } } ---------------------------------------- 1. c 函数框架。 ---------------------------------------- c 函数会使用堆栈,故而会使用sp寄存器, 但函数执行完后要恢复sp寄存器, 所以框架通常会保留原来的寄存器,我们把它叫old-sp寄存器。 arm 用fp寄存器 充当这个角色。 下面是它的框架示意: frame pointer 在函数生命期内其值是不变的。  2b0:    e92d4800     push    {fp, lr}  2b4:    e28db004     add    fp, sp, #4  2b8:    e24ddf42     sub    sp, sp, #264    ; 0x108 ......  34c:    e24bd004     sub    sp, fp, #4  350:    e8bd8800     pop    {fp, pc} ---------------------------------------- 2.如何调用其它函数, 参数传递方法。 ---------------------------------------- arm 的传参,小于4个的用寄存器, r0,r1,r2,r3. 大于4个用堆栈。 返回值用r0 ---------------------------------------- 3. 如何访问常量数据?例如: "hello android" ----------------------------------------   printf("hello android\n");  2bc:    e59f3090     ldr    r3, [pc, #144]    ; 354  2c0:    e08f3003     add    r3, pc, r3  2c4:    e1a00003     mov    r0, r3  2c8:    ebffffe9     bl    274 ......  354:    0000009c     .word    0x0000009c  358:    000000b4     .word    0x000000b4  35c:    000000b0     .word    0x000000b0  360:    00000050     .word    0x00000050 在程序区造一个表, 表中存放数据地址,这个地址与当前pc值相加才是真实数据地址。 待补充: 数据段 pc值2c0+9c = 36c 正好是"hello android"的的地址 Contents of section .rodata:  0364 2d2d2d2d 2d2d2d2d 2d2d2068 656c6c6f  ---------- hello  0374 20616e64 726f6964 202d2d2d 2d2d2d2d   android -------  0384 2d2d2d2d 2d000000 312e7478 74000000  -----...1.txt...  0394 72740000 6572726f 72206f70 656e2031  rt..error open 1  03a4 2e747874 00000000                    .txt....         ---------------------------------------- 4. 如何访问局部变量? ---------------------------------------- 局部变量是保存在堆栈中的,函数退出即丢弃。 例如:        memset(buffer,0,sizeof(buffer));  2f8:    e24b3f42     sub    r3, fp, #264    ; 0x108  2fc:    e1a00003     mov    r0, r3  300:    e3a01000     mov    r1, #0  304:    e3a02c01     mov    r2, #256    ; 0x100  308:    ebffffdf     bl    28c 通过frame pointer 可以获得局部变量的地址。 ---------------------------------------- 5. 如何调用动态链接函数, 找到函数入口地址。 ---------------------------------------- 还以printf 为例, printf 是c 库函数, 编译期并不知道其地址。 我们不看354了, 看bl 274  2bc:    e59f3090     ldr    r3, [pc, #144]    ; 354  2c0:    e08f3003     add    r3, pc, r3  2c4:    e1a00003     mov    r0, r3  2c8:    ebffffe9     bl    274 .... 274 处是另外一个表项,该表名称叫plt (procedure leakage table) 翻译为过程连接表 00000260 <.plt>:  260:    e52de004     .word    0xe52de004  264:    e59fe004     .word    0xe59fe004  268:    e08fe00e     .word    0xe08fe00e  26c:    e5bef008     .word    0xe5bef008  270:    000011d4     .word    0x000011d4  274:    e28fc600     .word    0xe28fc600  278:    e28cca01     .word    0xe28cca01  27c:    e5bcf1d4     .word    0xe5bcf1d4  280:    e28fc600     .word    0xe28fc600  284:    e28cca01     .word    0xe28cca01  288:    e5bcf1cc     .word    0xe5bcf1cc .plt:00000274 ; =============== S U B R O U T I N E ======================================= .plt:00000274 .plt:00000274 ; Attributes: thunk .plt:00000274 .plt:00000274 ; int puts(const char *s) .plt:00000274 puts                                    ; CODE XREF: test_so+18p .plt:00000274                                         ; test_so+7Cp ... .plt:00000274                 ADR             R12, 0x27C .plt:00000278                 ADD             R12, R12, #0x1000 .plt:0000027C                 LDR             PC, [R12,#(puts_ptr - 0x127C)]! ; __imp_puts .plt:0000027C ; End of function puts .plt:0000027C .plt:00000280 .plt:00000280 ; =============== S U B R O U T I N E ======================================= .plt:00000280 .plt:00000280 ; Attributes: thunk .plt:00000280 .plt:00000280 ; FILE *fopen(const char *filename, const char *modes) .plt:00000280 fopen                                   ; CODE XREF: test_so+34p .plt:00000280                 ADR             R12, 0x288 .plt:00000284                 ADD             R12, R12, #0x1000 .plt:00000288                 LDR             PC, [R12,#(fopen_ptr - 0x1288)]! ; __imp_fopen .plt:00000288 ; End of function fopen 274处是一个三条指令组成的小代码区。它首先计算出一个地址,这个地址在数据区,然后从那里取得数值, 转去执行。 可以感受到,取到的地址就是动态连接库的函数地址。这个地址要由加载器把数值填充好。 这个表位于一个数据区内,叫.got 区(global offset table). 显然,等价于pe 格式文件的导入地址表。 extern:00001468 ; Segment type: Externs extern:00001468 ; int puts(const char *s) extern:00001468                 IMPORT __imp_puts       ; CODE XREF: puts+8j extern:00001468                                         ; DATA XREF: .got:puts_ptro extern:0000146C ; FILE *fopen(const char *filename, const char *modes) extern:0000146C                 IMPORT __imp_fopen      ; CODE XREF: fopen+8j extern:0000146C                                         ; DATA XREF: .got:fopen_ptro ---------------------------------------- 由此总结一下,动态跳转的过程是: ---------------------------------------- 1. 加载器把外部so文件的调用函数地址都填充好了, 这个导入地址表叫.got 全局偏移表。 2. 有一个plt过程连接表,属于微代码区,每项由3行代码组成,用于从.got表中取得数据,并跳转执行。 ---------------------------------------- 扩展知识: ---------------------------------------- 可重定位代码(windows->dll) 和 位置无关代码(linux->so) 可重定位代码:(windows) 生成动态库时假定它被加载在地址 0 处。加载时它会被加载到一个地址(base), 这时要根据代码重定位(relocation)信息,对代码进行定址,so 才能正确寻址。 缺点: 不同的进程会把so加载到不同的地址, 而这些地址是不同的,重定位后的代码也是不同的, 所以这些代码没有办法共享。内存中有多份。 这样失掉了共享库的优势,跟不共享没多少差别。 除非进程能把共享库加载到同一个地址(但这个要求是过分的). 位置无关代码:(linux) linux so文件使用 -fPIC 来生成位置无关代码。这些代码可以被加载到内存的任何位置都可以运行。 怎样做到? 不管是程序地址还是数据地址,都是通过pc值加上一个偏移量来获得,就实现了位置无关。 例如访问外部函数,将外部函数地址全部放入.got table, 通过 [pc+offset] 获取。 优点: 虽然不同的进程会把so 映射到不同的地址空间,但操作系统将把它们映射到相同的物理地址, 节省了代码空间。 缺点: 代码执行效率上有一点损失。 但是,没有了重定位,加载也变快了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值