程序很簡單,就一個printf函數,通過分析我們主要明白大致的反彙編流程,感性認識一下棧的空間佈局。
#include<stdio.h>
int main()
{
printf("hello,world");
return 0;
}
cl hello.c /Fa hello.asm
/Fa
表示生成彙編列表文件
TITLE C:\moddemod\demo\c\re\hello.cpp
.686P
.XMM
include listing.inc
.model flat
INCLUDELIB LIBCMT
INCLUDELIB OLDNAMES
CONST SEGMENT
$SG5543 DB 'hello,world', 00H
CONST ENDS
PUBLIC ___local_stdio_printf_options
PUBLIC __vfprintf_l
PUBLIC _printf
PUBLIC _main
PUBLIC ?_OptionsStorage@?1??__local_stdio_printf_options@@9@4_KA ; `__local_stdio_printf_options'::`2'::_OptionsStorage
EXTRN ___acrt_iob_func:PROC
EXTRN ___stdio_common_vfprintf:PROC
_BSS SEGMENT
?_OptionsStorage@?1??__local_stdio_printf_options@@9@4_KA DQ 01H DUP (?) ; `__local_stdio_printf_options'::`2'::_OptionsStorage
_BSS ENDS
_TEXT SEGMENT
_main PROC
push ebp
mov ebp, esp
push OFFSET $SG5543
call _printf
add esp, 4
xor eax, eax
pop ebp
ret 0
_main ENDP
_TEXT ENDS
_TEXT SEGMENT
__Result$ = -8 ; size = 4
__ArgList$ = -4 ; size = 4
__Format$ = 8 ; size = 4
_printf PROC ; COMDAT
push ebp
mov ebp, esp
sub esp, 8
lea eax, DWORD PTR __Format$[ebp+4]
mov DWORD PTR __ArgList$[ebp], eax
mov ecx, DWORD PTR __ArgList$[ebp]
push ecx
push 0
mov edx, DWORD PTR __Format$[ebp]
push edx
push 1
call ___acrt_iob_func
add esp, 4
push eax
call __vfprintf_l
add esp, 16 ; 00000010H
mov DWORD PTR __Result$[ebp], eax
mov DWORD PTR __ArgList$[ebp], 0
mov eax, DWORD PTR __Result$[ebp]
mov esp, ebp
pop ebp
ret 0
_printf ENDP
_TEXT ENDS
_TEXT SEGMENT
__Stream$ = 8 ; size = 4
__Format$ = 12 ; size = 4
__Locale$ = 16 ; size = 4
__ArgList$ = 20 ; size = 4
__vfprintf_l PROC ; COMDAT
push ebp
mov ebp, esp
mov eax, DWORD PTR __ArgList$[ebp]
push eax
mov ecx, DWORD PTR __Locale$[ebp]
push ecx
mov edx, DWORD PTR __Format$[ebp]
push edx
mov eax, DWORD PTR __Stream$[ebp]
push eax
call ___local_stdio_printf_options
mov ecx, DWORD PTR [eax+4]
push ecx
mov edx, DWORD PTR [eax]
push edx
call ___stdio_common_vfprintf
add esp, 24 ; 00000018H
pop ebp
ret 0
__vfprintf_l ENDP
_TEXT ENDS
_TEXT SEGMENT
___local_stdio_printf_options PROC ; COMDAT
push ebp
mov ebp, esp
mov eax, OFFSET ?_OptionsStorage@?1??__local_stdio_printf_options@@9@4_KA ; `__local_stdio_printf_options'::`2'::_OptionsStorage
pop ebp
ret 0
___local_stdio_printf_options ENDP
_TEXT ENDS
END
我們主要看一下main函數
_main PROC
push ebp
mov ebp, esp
push OFFSET $SG5543
call _printf
add esp, 4
xor eax, eax
pop ebp
ret 0
_main ENDP
假設初始的時候esp指向9,也即9是棧頂,當
push ebp
執行push ebp的時候,(esp先減4字節(32位),如果是64位減8字節),
在我們這裏減後指向8這個地址,此時esp寄存器的值變爲8,隨後將ebp的值壓在8-9這塊空間裏
mov ebp, esp
隨後更新ebp寄存器的值,即將esp寄存器的值賦值給ebp寄存器
push OFFSET $SG5543
OFFSET $SG5543
這塊內存地址指向hello,world,也即是將這塊內存的偏移量存放在棧上,便於尋址hello,world的位置,
當push的時候,首先esp先減4指向7,然後把offset取到的偏移量存放在7-8這塊內存地址
call _printf
call指令在執行的時候首先將call指令的下一條語句的地址壓棧後再跳到_printf去執行,想一下這是爲什麼?
因爲當_printf執行完後,還要回來繼續執行,所以必須把回來的地址先存起來,這起始也就是對應高級語言裏的函數調用然後返回,底層就是這樣實現的。
剛纔說了先將add esp, 4
的地址壓棧,同樣,先將esp的值減4指向6,將該地址存放到6-7這段空間
隨後將eip寄存器的值修改爲_printf函數入口地址
下面到了_printf函數
push ebp
mov ebp, esp
同樣的操作,將在main函數裏面的ebp壓棧,此時esp指向5,同時更新ebp的值
sub esp, 8
將esp的值減8,即指向3的位置
…
後面的我們就暫時先不分析了…