linux 下动态库函数调用--反汇编知识(arm)。
来源:互联网 发布:wifi限制网速软件 编辑:程序博客网 时间:2024/06/15 17:43
linux 下动态库函数调用--反汇编知识。
author: hjjdebug
date: 2016年 09月 07日 星期三 14:41:49 CST
****************************************
#include <stdio.h>
#include <string.h>
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 函数框架。
以下分析代码为arm 指令, 由arm-linux-androideabi-gcc 编译
----------------------------------------
所以框架通常会保留原来的寄存器,我们把它叫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
这个变化,相比x86用堆栈传参,区别是参数不在栈中了, 返回地址也不在栈中了,
函数调用栈不明显了。函数调用参数也不明显了。 好处是不花力气维护栈了。 逆向会稍困难一点。
由于r0 做返回值,r0,r1,r2,r3 常用寄存器比较忙, 如果需要保存函数参数,需把r0,r1,r2,r3 保存到别处,包括栈内存或高位寄存器。
所以你会经常见到r0,r1,r2,r3 等(尤其是r0)寄存器传来传去。
----------------------------------------
3. 如何访问常量数据?例如: "hello android"
----------------------------------------
printf("hello android\n");
2bc: e59f3090 ldr r3, [pc, #144] ; 354 <test_so+0xa4>
2c0: e08f3003 add r3, pc, r3
2c4: e1a00003 mov r0, r3
2c8: ebffffe9 bl 274 <test_so-0x3c>
......
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....
这种访问全局变量的技巧,对thumb 指令尤其重要。 thumb 指令是16为, 而全局变量地址是32位,
如何加载32位地址到寄存器中? 那就是在程序区见一个数据表,用16位指令可以从手边的数据表中取到地址,
这个地址加上pc 值构成真正的数据地址(位置无关),然后从真实地址中再拿到数据,这样就访问了全局变量。
----------------------------------------
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 <test_so-0x24>
通过frame pointer 可以获得局部变量的地址。
----------------------------------------
5. 如何调用动态链接函数, 找到函数入口地址。
----------------------------------------
还以printf 为例, printf 是c 库函数, 编译期并不知道其地址。
我们不看354了(字符串地址表), 看bl 274(过程连接表)
2bc: e59f3090 ldr r3, [pc, #144] ; 354 <test_so+0xa4>
2c0: e08f3003 add r3, pc, r3
2c4: e1a00003 mov r0, r3
2c8: ebffffe9 bl 274 <test_so-0x3c>
....
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
过程连接表是小微代码区,看看ida-pro中的显示,如下:
.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
解释: 这个ADR R12,0x27c, 真实指令是 add ip, pc, #const, 就是说是基于pc 值的位置无关代码。
结果是从某一固定偏移的内存中取到一个数值付给PC.
对应此例,这个偏移是#puts_ptr -> 1450, 里面存的数要靠连接器加载后填入数据才能得到, 这个数据是puts 的地址。
加载器是根据导入表信息填写这个地址的。
.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过程连接表,属于微代码区,每项由很短的代码组成,用于从.got表中取得数据,并跳转执行。
----------------------------------------
扩展知识:
----------------------------------------
可重定位代码(windows->dll) 和 位置无关代码(linux->so)
可重定位代码:(windows)
生成动态库时假定它被加载在地址 0 处。加载时它会被加载到一个地址(base),
这时要根据代码重定位(relocation)信息,对代码进行定址,dll 才能正确寻址。
缺点: 不同的进程会把dll加载到不同的地址, 而这些地址是不同的,重定位后的代码也是不同的,
所以这些代码没有办法共享。内存中有多份。 这样失掉了共享库的优势,跟不共享没多少差别。
除非进程能把共享库加载到同一个地址(但这个要求是过分的).
位置无关代码:(linux)
linux so文件使用 -fPIC 来生成位置无关代码。这些代码可以被加载到内存的任何位置都可以运行。
怎样做到?
不管是程序地址还是数据地址,都是通过pc值加上一个偏移量来获得,就实现了位置无关。
例如访问外部函数,将外部函数地址全部放入.got table, 通过 [pc+offset] 获取。
优点: 虽然不同的进程会把so 映射到不同的地址空间,但操作系统将把它们映射到相同的物理地址,
节省了代码空间。
缺点: 代码执行效率上有一点损失。 但是,没有了重定位,加载也变快了。
- linux 下动态库函数调用--反汇编知识(arm)。
- Linux Debugging(七): 使用反汇编理解动态库函数调用方式GOT/PLT
- 使用反汇编理解动态库函数调用方式GOT/PLT
- Linux下如何反汇编arm raw binary文件
- ARM反汇编动态链接分析
- Linux下ARM汇编
- linux下反汇编实例
- linux下如何反汇编
- Linux下ARM汇编教程
- Linux下ARM汇编教程
- Linux下ARM汇编语法
- Linux下ARM汇编教程
- Linux下ARM汇编教程
- Linux下ARM汇编语法
- Linux下ARM汇编语法
- linux 下arm汇编语法
- Linux下ARM汇编教程
- Linux下ARM汇编教程
- android listview每个item定义动画呈现
- 如何建立一个建立一个网站
- python中的修饰符(@)
- OC和Swift中动画的暂停和恢复
- TcxComboBoxProperties下拉框填充
- linux 下动态库函数调用--反汇编知识(arm)。
- Linux信号处理
- GitHub超详细图文攻略 - Git客户端下载安装 GitHub提交修改源码工作流程 Git分支 标签 过滤 Git版本工作流
- 比较有意思的flash动画收藏
- HTML 5 <form> autocomplete 属性
- 建议84:使用PLINQ
- RabbitMQ (四) 路由选择 (Routing)
- Android设置按钮不可点击
- 大叔,谈创业路上的几条真理(中)