逆向工程权威指南读书笔记
相关环境
在线汇编编译 https://godbolt.org/
hello world
1.cpp
#include <stdio.h>
int main()
{
printf("hello, world\n");
return 0;
}
x86 msvc 编译汇编
CONST SEGMENT
$SG3830 DB 'hello, world', 0AH, 00H
CONST ENDS
PUBLIC _main
EXTRN _printf:PROC
; Function compile flags: /Odtp
_TEXT SEGMENT
_main PROC
push ebp
mov ebp, esp
push OFFSET $SG3830 ; 字符串的指针即地址入栈
call _printf
add esp, 4
xor eax, eax
pop ebp
ret 0
_main ENDP
_TEXT ENDS
- 编译器在字符串常量的尾部添加了十六进制的数字0,即00h;依据C/C++字符串的标准规范,编译器要为这个字符串常量添加结束标志(即数值为零的单个字节
2.
_TEXT
:代码段CONST
:数据段 3._main
main函数 主函数的函数体有标志性的函数序言(function prologue)和函数尾声(function epilogue) add ESP,4
为什么要加上4
这是因为x86
平台的内存地址使用32
位(即4
字节)数据描述。同理,在x64
系统上释放这个指针时,ESP
就要加上8
5.xor eax, eax
用来计算返回值为0;不使用MOV EAX,0
指令。主要是因为异或运算的opcode较短(2字节:5字节) 6.RET
将控制权交给调用程序。通常它起到的作用就是将控制权交给操作系统 x86 gcc 编译
Main proc near
var_10 = dword ptr -10h
push ebp
mov ebp, esp
and esp, 0FFFFFFF0h
sub esp, 10h
mov eax, offset aHelloWorld ; "hello, world\n"
mov [esp+10h+var_10], eax
call _printf
mov eax, 0
leave
retn
main endp
and esp, 0FFFFFFF0h
它令栈地址(ESP的值)向16字节边界对齐(成为16的整数倍),属于初始化的指令。如果地址位没有对齐,那么CPU可能需要访问两次内存才能获得栈内数据leave
等效于MOV ESP, EBP
和POP EBP
两条指令。可见,这个指令调整了数据栈指针ESP,并将EBP的数值恢复到调用这个函数之前的初始状态
MSVC-x86-64
$SG2989 DB 'hello, world', 0AH 00H
main PROC
sub rsp, 40
lea rcx, OFFSET FLAT:$SG2989
call printf
xor eax, eax
add rsp, 40
ret 0
main ENDP
GCC-x86-64
.string "hello, world\n"
main:
sub rsp, 8
mov edi, OFFSET FLAT:.LC0 ; "hello, world"
xor eax, eax ; number of vector registers passed
call printf
xor eax, eax
add rsp, 8
ret