跳到主要内容

逆向工程权威指南读书笔记

相关环境

在线汇编编译 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
  1. 编译器在字符串常量的尾部添加了十六进制的数字0,即00h;依据C/C++字符串的标准规范,编译器要为这个字符串常量添加结束标志(即数值为零的单个字节 2._TEXT:代码段 CONST:数据段 3._main main函数 主函数的函数体有标志性的函数序言(function prologue)和函数尾声(function epilogue)
  2. 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, EBPPOP 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