2013-01-24 81 views
2

如何將參數傳遞給Assembly中的函數? 我沒有推最後帕拉姆,推動二帕拉姆,推第一個參數..ASM x86中的函數參數FASM

但我不能Meh功能中訪問參數..我在做什麼導致程序崩潰..

format PE console        ;Format PE OUT GUI 4.0 
entry main 

include 'macro/import32.inc' 

section '.idata' import data readable   ;Import Section. 
library msvcrt,'msvcrt.dll' 
import msvcrt, printf, 'printf',\ 
exit,'exit', getchar, 'getchar' 

section '.data' data readable writeable   ;Constants/Static Section. 
InitialValue dd 0 

section '.code' code readable executable 
main:  
    push 67 
    push 66 
    push 65 
    call MEH 

    call [getchar] 
    mov eax, 0 
    ret 0 

MEH: 
    push ebx 
    mov ebp, esp 
    sub esp, 0 

    mov eax, [ebp + 8] ; Trying to print first parameter.. 
    push eax 
    call [printf] 
    add esp, eax 

    mov esp, ebp 
    pop ebx 
ret 

回答

2

讓我們來看看...

說你的ESP是0x00180078上開始,然後三推之後你有

00180078: 67 
00180074: 66 
00180070: 65 

然後調用MEH,這直接LY所以現在推EBX你有堆棧

00180078: 67 
00180074: 66 
00180070: 65 
0018006C: return address 
00180068: ebx value 

你現在負載EBP與ESP = 00180068

sub esp,0 does nothing 

mov eax, [ebp+8] ~ 00180068 + 8 = 00180070 = 65 

所以不是第一次,而是最後一個參數

call [printf] 

這裏來您的問題,但:

add esp, eax 

這應該做什麼好?假設printf保存傳入的參數(這是偶然不需要做的),爲什麼要將參數添加到堆棧指針呢?這肯定會弄糟你的回報。 你想要做的是將esp恢復爲ebp的值並彈出保存的ebx值。

+0

我刪除了子esp,0和添加esp,eax ..它仍然崩潰,但是:l它不打印65這是字符A. – Brandon

+0

我從來沒有使用msvcrt從asm,但我有一種感覺您沒有正確調用printf - c運行時函數通常使用cdecl調用約定。您可能想要查看此問題的線索:http://stackoverflow.com/questions/3902697/x86-masm-hello-world –

+0

我認爲這是調用正確..當我這樣做: mov eax,65 push eax call [printf] pop eax 它打印字符A. – Brandon

2

如果printf()的調用約定是正確的(它適用於Linux上的32位MinGW和32位gcc),那麼您完全忽略了該函數的期望,並且沒有獲得所需的輸出。

功能的原型爲:

int printf(const char* format, ...); 

format,第一個參數,是一個指向ASCIIZ串,它包含文本打印和/或特殊記號等%d由適當解釋來代替以下可選參數format

所以,如果你想printf()打印'A',那麼這就是你需要在C++做什麼:

printf("A"); 

printf("%c", 'A'); 

下面是你會怎麼做相同的組件:

myformatstring db "A", 0 ; this line goes into section .data 

push myformatstring ; push address of the string 
call [printf] 
add esp, 4 ; remove all parameters from the stack 

myformatstring db "%c", 0 ; this line goes into section .data 

push 'A'  
push myformatstring ; push address of the string 
call [printf] 
add esp, 2*4 ; remove all parameters from the stack 
4

小額外筆記。該過程的 正確的頁眉/頁腳使用PUSH/POP EBP:

MEH: 
    push ebp 
    mov ebp, esp 

    mov esp, ebp 
    pop ebp 
    ret 

的原因是,我們需要保存/使用它作爲一個指針參數和局部變量之前恢復EBP寄存器。

其次,調用者在過程返回後恢復堆棧指針的CCALL調用約定對於C/C++語言來說是很常見的,但對於彙編編程而言則是不常見的。原因很明顯 - 編譯器可以正確計算堆棧中推送的參數數量。在手寫彙編程序中,使用這個約定會使代碼不清晰。

更好的方法是使用STDCALL調用約定:

MEH: 
    push ebp 
    mov ebp, esp 

    mov esp, ebp 
    pop ebp 
    retn 12 ; how many bytes to be automatically 
      ; removed from the stack after return. 

更好的做法是使用一些宏以自動化的標準程序元素的創建,並提供了參數和局部人類可讀標籤變量。例如,在FreshLib庫提供的宏有語法如下:

proc MEH, .arg1, .arg2, .arg3 
; define local variables here, if needed. 
begin 
    ; place your code here without headers and footers 
    return ; will clean the stack automatically. 
endp 

; pushes the arguments in the stack and call MEH 
stdcall MEH, 65, 66, 67 

提供FASM包的標準宏庫有稍微不同的語法,由FASM programmers manual覆蓋的細節。