在Linux环境下,常用的反汇编命令是objdump -Sd file
。该命令采用的是AT&T格式,与Intel格式有一些不同之处, 主要的区别是源操作数和目标操作数的顺序是相反的。
测试环境: GCC4.8.5 -O3
基础知识
常见的汇编子程序实现方式是利用栈帧的概念。SP指向栈顶,入栈操作会使SP减少,而出栈操作则会使SP增加。
call MySub ; 入栈函数返回地址(下一条指令的地址), 跳转到MySub执行
; ...
MySub PROC
push %ebp ; 将BP入栈
mov %esp, %ebp ; 备份SP(栈顶指针)到BP
sub $8, %esp ; 为局部变量保留空间
; ...
mov %ebp, %esp ; 从堆栈中删除局部变量
pop %ebp ; 出栈BP
ret ; 调用call时入栈的函数返回地址
MySub ENDP
int/float 传参
float test_p(int i1, float f1, int i2, float f2)
{
float k = i1 + i2 + f1 + f2;
return k;
}
test_p(1, 2, 3, 4);
0000000000000770 <test_p>:
770: 01 f7 add %esi,%edi
772: f3 0f 2a d7 cvtsi2ss %edi,%xmm2
776: f3 0f 58 d0 addss %xmm0,%xmm2
77a: f3 0f 58 d1 addss %xmm1,%xmm2
77e: 0f 28 c2 movaps %xmm2,%xmm0
781: c3 retq
782: 0f 1f 40 00 nopl 0x0(%rax)
786: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
78d: 00 00 00
400584: be 03 00 00 00 mov $0x3,%esi
400589: bf 01 00 00 00 mov $0x1,%edi
40058e: f3 0f 10 0d ae 01 00 movss 0x1ae(%rip),%xmm1 # 400744 <__dso_handle+0xc>
400595: 00
400596: f3 0f 10 05 aa 01 00 movss 0x1aa(%rip),%xmm0 # 400748 <__dso_handle+0x10>
40059d: 00
40059e: e8 cd ff ff ff callq 400570 <test_p@plt>
根据System V AMD64 ABI,前六个整数或指针参数是通过寄存器RDI、RSI、RDX、RCX、R8和R9传递,浮点参数是通过XMM0、XMM1、XMM2、XMM3、XMM4、XMM5和XMM6来传递的。
结构体参数
如果结构体不大于16个字节, 则通过寄存器传值, 否则通过堆栈传值或返回.
小结构体参数
struct small {
char c1;
char c2;
char sz1[14];
};
struct small test_small(int i, struct small in) {
struct small out;
memset(&out, i, sizeof(out));
out.c1 = in.c2;
return out;
}
struct small in;
struct small out = test_small(5, in);
0000000000000750 <test_small>:
750: 40 0f b6 d7 movzbl %dil,%edx
754: 48 b8 01 01 01 01 01 movabs $0x101010101010101,%rax
75b: 01 01 01
75e: 66 c1 ee 08 shr $0x8,%si
762: 48 0f af d0 imul %rax,%rdx
766: 48 89 54 24 d8 mov %rdx,-0x28(%rsp)
76b: 40 88 74 24 d8 mov %sil,-0x28(%rsp)
770: 48 8b 44 24 d8 mov -0x28(%rsp),%rax
775: c3 retq
776: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
77d: 00 00 00
400584: bf 05 00 00 00 mov $0x5,%edi
400589: 48 8b 54 24 08 mov 0x8(%rsp),%rdx
40058e: 48 8b 34 24 mov (%rsp),%rsi
400592: e8 a9 ff ff ff callq 400540 <test_small@plt>
400597: 48 8d 74 24 10 lea 0x10(%rsp),%rsi
40059c: 48 89 44 24 10 mov %rax,0x10(%rsp)
4005a1: 48 89 54 24 18 mov %rdx,0x18(%rsp)
in参数通过%rsi、%rdx传入, 返回值通过%rax和%rdx传出
大结构体参数
struct large
{
char c1;
char c2;
char sz1[15];
};
struct large test_large(int i, struct large in)
{
struct large out;
memset(&out, i, sizeof(out));
out.c1 = in.c2;
return out;
}
struct large in;
struct large out = test_large(5, in);
0000000000000780 <test_large>:
780: 40 0f b6 d6 movzbl %sil,%edx
784: 48 b9 01 01 01 01 01 movabs $0x101010101010101,%rcx
78b: 01 01 01
78e: 48 89 f8 mov %rdi,%rax
791: 48 0f af d1 imul %rcx,%rdx
795: 0f b6 4c 24 09 movzbl 0x9(%rsp),%ecx ; 0x9(%rsp) 是 in.c2
79a: 40 88 77 10 mov %sil,0x10(%rdi)
79e: 48 89 54 24 d8 mov %rdx,-0x28(%rsp)
7a3: 88 4c 24 d8 mov %cl,-0x28(%rsp)
7a7: 48 8b 4c 24 d8 mov -0x28(%rsp),%rcx
7ac: 48 89 57 08 mov %rdx,0x8(%rdi)
7b0: 48 89 0f mov %rcx,(%rdi)
7b3: c3 retq
400580: 48 83 ec 68 sub $0x68,%rsp
400584: be 05 00 00 00 mov $0x5,%esi ; 参数 int i
400589: 48 8b 44 24 20 mov 0x20(%rsp),%rax
40058e: 48 8d 7c 24 40 lea 0x40(%rsp),%rdi ; 返回值
400593: 48 89 04 24 mov %rax,(%rsp) ; 参数 struct large in
400597: 48 8b 44 24 28 mov 0x28(%rsp),%rax
40059c: 48 89 44 24 08 mov %rax,0x8(%rsp) ; 参数 struct large in
4005a1: 0f b6 44 24 30 movzbl 0x30(%rsp),%eax
4005a6: 88 44 24 10 mov %al,0x10(%rsp) ; 参数 struct large in
4005aa: e8 91 ff ff ff callq 400540 <test_large@plt>
在主调函数中,in变量的地址是0x20(%rsp),out变量的地址是0x40(%rsp), 将out变量作为第一个参数,将函数声明的第一个参数int i作为第二个参数,将函数声明的第二个参数struct large in放入堆栈