《coredump问题原理探究》Linux x86版5.3节C风格数据结构内存布局之数组
2013-03-14 21:24
344 查看
在C语言里,数组就是相同类型变量的集合体。由这个定义,可大致得知数组的特征:
1.有首元素。而首元素的地址和数组地址一样,即有基地址
2.每个元素的大小是一样的。那么每个元素相对基地址的偏移值应该是元素大小和索引值的乘积。
也就是说,基地址和与索引值成比例的偏移值有可能是数组的特征。
还是按照上面的方式来逐个对各类型的数组进行探究。
先看一下char型的数组
再看一下它的汇编:
由
可以看到:
1. 第二个参数,buf是由eax得来,而eax是由esp+0x18,也就是说,esp+0x18是buf的基地址
2. 第三个参数,&buf[15],是由esp+0x18 + 0xf得来的。0xf刚好和buf[15]相对buf基地址的偏移相等。
3. 由buf[15]的地址比buf[0]的高,可知,数组在栈上是递增的。
由
可知,局部变量c是放在esp+0x2f里。
再由
这个循环里的
可知
c递增的步长是1,刚好和char的大小一样。
且由
和
可知,数组的元素地址确实是递增的,且每个元素的地址都是esp+0x18+i,即基地址+i
PS:下面的汇编指令
的意思,就是c++, i++和i < 15。但把c++放入在i++,i<15之间主要是对c,i两个变量操作的指令之间没有依赖,混编的话,在多核多线程处理器能够同时并发执行。
打一下断点来验证一下上面结论
继续看一下short的数组:
看一下相应的汇编:
按照char型数组类似的分析,可得:
1. 局部变量s存放在esp+0x3e
2. buf的首地址是esp+0x18,尾元素的地址是esp+0x18+0x1e。可见buf在栈里也是递增的。
3. buf的空间大小是0x1e+2=0x20 = 32。正好是16个short类型的大小。
4. 由0x080485bc可知,short型数组的递增步长是2,刚好是short的大小。
5. 由0x080485b2可知,对short数组每一个元素的引用,都要用到esp+0x18+2*eax,即基地址+i*sizeof(short)
继续对int, long,float,double,可以得到下表:
其实,基地址+索引值*sizeof( element)这些方式,用汇编可以有很多种表示形式。如下面也算是一种
详细可以搜索一下“寄存器寻址方式”里的”寄存器变址寻址方式“
1.有首元素。而首元素的地址和数组地址一样,即有基地址
2.每个元素的大小是一样的。那么每个元素相对基地址的偏移值应该是元素大小和索引值的乘积。
也就是说,基地址和与索引值成比例的偏移值有可能是数组的特征。
还是按照上面的方式来逐个对各类型的数组进行探究。
先看一下char型的数组
#include <stdio.h> int main() { char buf[16]; char c = 'a'; printf( "head of array:%x, tail of array:%x", buf, &buf[15] ); for ( int i = 0; i < 16; i++, c++ ) { buf[i] = c; } buf[15] = '\0'; printf( "%s\n", buf ); return 0; }
再看一下它的汇编:
(gdb) disassemble main Dump of assembler code for function main: 0x080485a0 <+0>: push %ebp 0x080485a1 <+1>: mov %esp,%ebp 0x080485a3 <+3>: and $0xfffffff0,%esp 0x080485a6 <+6>: sub $0x30,%esp 0x080485a9 <+9>: movb $0x61,0x2f(%esp) 0x080485ae <+14>: lea 0x18(%esp),%eax 0x080485b2 <+18>: add $0xf,%eax 0x080485b5 <+21>: mov %eax,0x8(%esp) 0x080485b9 <+25>: lea 0x18(%esp),%eax 0x080485bd <+29>: mov %eax,0x4(%esp) 0x080485c1 <+33>: movl $0x80486b4,(%esp) 0x080485c8 <+40>: call 0x8048460 <printf@plt> 0x080485cd <+45>: movl $0x0,0x28(%esp) 0x080485d5 <+53>: jmp 0x80485f2 <main+82> 0x080485d7 <+55>: lea 0x18(%esp),%edx 0x080485db <+59>: mov 0x28(%esp),%eax 0x080485df <+63>: add %eax,%edx 0x080485e1 <+65>: movzbl 0x2f(%esp),%eax 0x080485e6 <+70>: mov %al,(%edx) 0x080485e8 <+72>: addl $0x1,0x28(%esp) 0x080485ed <+77>: addb $0x1,0x2f(%esp) 0x080485f2 <+82>: cmpl $0xf,0x28(%esp) 0x080485f7 <+87>: setle %al 0x080485fa <+90>: test %al,%al 0x080485fc <+92>: jne 0x80485d7 <main+55> 0x080485fe <+94>: movb $0x0,0x27(%esp) 0x08048603 <+99>: lea 0x18(%esp),%eax 0x08048607 <+103>: mov %eax,(%esp) 0x0804860a <+106>: call 0x8048470 <puts@plt> 0x0804860f <+111>: mov $0x0,%eax 0x08048614 <+116>: jmp 0x804861e <main+126> 0x08048616 <+118>: mov %eax,(%esp) 0x08048619 <+121>: call 0x8048490 <_Unwind_Resume@plt> 0x0804861e <+126>: leave 0x0804861f <+127>: ret End of assembler dump.
由
0x080485ae <+14>: lea 0x18(%esp),%eax 0x080485b2 <+18>: add $0xf,%eax 0x080485b5 <+21>: mov %eax,0x8(%esp) 0x080485b9 <+25>: lea 0x18(%esp),%eax 0x080485bd <+29>: mov %eax,0x4(%esp) 0x080485c1 <+33>: movl $0x80486b4,(%esp) 0x080485c8 <+40>: call 0x8048460 <printf@plt>
可以看到:
1. 第二个参数,buf是由eax得来,而eax是由esp+0x18,也就是说,esp+0x18是buf的基地址
2. 第三个参数,&buf[15],是由esp+0x18 + 0xf得来的。0xf刚好和buf[15]相对buf基地址的偏移相等。
3. 由buf[15]的地址比buf[0]的高,可知,数组在栈上是递增的。
由
0x080485a9 <+9>: movb $0x61,0x2f(%esp)
可知,局部变量c是放在esp+0x2f里。
再由
0x080485d7 <+55>: lea 0x18(%esp),%edx 0x080485db <+59>: mov 0x28(%esp),%eax 0x080485df <+63>: add %eax,%edx 0x080485e1 <+65>: movzbl 0x2f(%esp),%eax 0x080485e6 <+70>: mov %al,(%edx) 0x080485e8 <+72>: addl $0x1,0x28(%esp) 0x080485ed <+77>: addb $0x1,0x2f(%esp) 0x080485f2 <+82>: cmpl $0xf,0x28(%esp) 0x080485f7 <+87>: setle %al 0x080485fa <+90>: test %al,%al 0x080485fc <+92>: jne 0x80485d7 <main+55>
这个循环里的
0x080485ed <+77>: addb $0x1,0x2f(%esp)
可知
c递增的步长是1,刚好和char的大小一样。
且由
0x080485d7 <+55>: lea 0x18(%esp),%edx 0x080485db <+59>: mov 0x28(%esp),%eax 0x080485df <+63>: add %eax,%edx
和
0x080485e8 <+72>: addl $0x1,0x28(%esp)
可知,数组的元素地址确实是递增的,且每个元素的地址都是esp+0x18+i,即基地址+i
PS:下面的汇编指令
0x080485e8 <+72>: addl $0x1,0x28(%esp)
0x080485ed <+77>: addb $0x1,0x2f(%esp)
0x080485f2 <+82>: cmpl $0xf,0x28(%esp)
的意思,就是c++, i++和i < 15。但把c++放入在i++,i<15之间主要是对c,i两个变量操作的指令之间没有依赖,混编的话,在多核多线程处理器能够同时并发执行。
打一下断点来验证一下上面结论
(gdb) tbreak *0x080485fe Temporary breakpoint 1 at 0x80485fe (gdb) r Starting program: /home/buckxu/work/5/2/xuzhina_dump_c5_s2 Temporary breakpoint 1, 0x080485fe in main () (gdb) x /16c $esp+0x18 0xbffff468: 97 'a' 98 'b' 99 'c' 100 'd' 101 'e' 102 'f' 103 'g' 104 'h' 0xbffff470: 105 'i' 106 'j' 107 'k' 108 'l' 109 'm' 110 'n' 111 'o' 112 'p'
继续看一下short的数组:
#include <stdio.h> int main() { short buf[16]; short s = 'a'; printf( "head of array:%x, tail of array:%x", buf, &buf[15] ); for ( int i = 0; i < 16; i++, s++ ) { buf[i] = s; } return buf[15]; }
看一下相应的汇编:
(gdb) disassemble main Dump of assembler code for function main: 0x08048570 <+0>: push %ebp 0x08048571 <+1>: mov %esp,%ebp 0x08048573 <+3>: and $0xfffffff0,%esp 0x08048576 <+6>: sub $0x40,%esp 0x08048579 <+9>: movw $0x61,0x3e(%esp) 0x08048580 <+16>: lea 0x18(%esp),%eax 0x08048584 <+20>: add $0x1e,%eax 0x08048587 <+23>: mov %eax,0x8(%esp) 0x0804858b <+27>: lea 0x18(%esp),%eax 0x0804858f <+31>: mov %eax,0x4(%esp) 0x08048593 <+35>: movl $0x8048674,(%esp) 0x0804859a <+42>: call 0x8048440 <printf@plt> 0x0804859f <+47>: movl $0x0,0x38(%esp) 0x080485a7 <+55>: jmp 0x80485c2 <main+82> 0x080485a9 <+57>: mov 0x38(%esp),%eax 0x080485ad <+61>: movzwl 0x3e(%esp),%edx 0x080485b2 <+66>: mov %dx,0x18(%esp,%eax,2) 0x080485b7 <+71>: addl $0x1,0x38(%esp) 0x080485bc <+76>: addw $0x1,0x3e(%esp) 0x080485c2 <+82>: cmpl $0xf,0x38(%esp) 0x080485c7 <+87>: setle %al 0x080485ca <+90>: test %al,%al 0x080485cc <+92>: jne 0x80485a9 <main+57> 0x080485ce <+94>: movzwl 0x36(%esp),%eax 0x080485d3 <+99>: cwtl 0x080485d4 <+100>: jmp 0x80485de <main+110> 0x080485d6 <+102>: mov %eax,(%esp) 0x080485d9 <+105>: call 0x8048460 <_Unwind_Resume@plt> 0x080485de <+110>: leave 0x080485df <+111>: ret End of assembler dump.
按照char型数组类似的分析,可得:
1. 局部变量s存放在esp+0x3e
2. buf的首地址是esp+0x18,尾元素的地址是esp+0x18+0x1e。可见buf在栈里也是递增的。
3. buf的空间大小是0x1e+2=0x20 = 32。正好是16个short类型的大小。
4. 由0x080485bc可知,short型数组的递增步长是2,刚好是short的大小。
5. 由0x080485b2可知,对short数组每一个元素的引用,都要用到esp+0x18+2*eax,即基地址+i*sizeof(short)
继续对int, long,float,double,可以得到下表:
类型 | 特征 |
char | 基地址 + 索引值*1 |
short | 基地址 + 索引值*2 |
int | 基地址 + 索引值*4 |
long | 32-bit:基地址 + 索引值*4 64-bit:基地址 + 索引值*8 |
float | 基地址 + 索引值*4 (因为单精度是占4个字节的),要配合浮点计算的指令确认 |
double | 基地址 + 索引值*8 (双精度占8个字节) ,要配合浮点计算的指令确认 |
指针 | 32-bit:基地址 + 索引值*4 64-bit:基地址 + 索引值*8 |
lea $base,%eax // 把base地址放到eax里 mov $index,%ecx // 把index放到ecx里。 mul $4, %ecx add %ecx, %eax //这里eax就存放了index指向的元素地址了。 |
相关文章推荐
- 《coredump问题原理探究》Linux x86版6.1节C++风格数据结构内存布局之无成员变量的类
- 《coredump问题原理探究》Linux x86版5.9节C风格数据结构内存布局之联合体
- 《coredump问题原理探究》Linux x86版5.7节C风格数据结构内存布局之结构体数组
- 《coredump问题原理探究》Linux x86版5.4节C风格数据结构内存布局之数组coredump例子
- 《coredump问题原理探究》Linux x86版7.4节List coredump例子
- 《coredump问题原理探究》Linux x86版7.7节 set对象
- 《coredump问题原理探究》Linux x86版3.8节栈布局之栈溢出coredump例子
- 《coredump问题原理探究》Linux x86版第二章coredump捕获的环境配置 转
- 《coredump问题原理探究》Linux x86版7.9节list相关的iterator对象
- 《coredump问题原理探究》Linux x86版7.7节 set对象
- 《coredump问题原理探究》Linux x86版7.3节List对象
- 《coredump问题原理探究》Linux x86版5.8节C风格数据结构内存布局之结构体数组结构体coredump
- 《coredump问题原理探究》Linux x86版5.1节C风格数据结构内存布局之引言
- 《coredump问题原理探究》Linux x86版4.4节函数的逆向之循环结构
- 《coredump问题原理探究》Linux x86版5.2节C风格数据结构内存布局之基本数据类型
- 《coredump问题原理探究》Linux x86版7.8节vector相关的iterator对象
- 《coredump问题原理探究》Linux x86版6.2节C++风格数据结构内存布局之有成员变量的类
- 《coredump问题原理探究》Linux x86版6.6节单继承
- 《coredump问题原理探究》Linux x86版5.5节C风格数据结构内存布局之基本数据类型构成的结构体
- 《coredump问题原理探究》Linux x86版4.2节函数的逆向之顺序结构