2

アセンブリで関数にパラメーターを渡すにはどうすればよいですか? Last Paramをプッシュし、Second Paramをプッシュし、First Paramをプッシュしました..

しかし、関数内のパラメーターにアクセスできません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
4

3 に答える 3

5

小さな追加のメモ。プロシージャの適切なヘッダー/フッターは、push /popebpを使用します。

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プログラマーのマニュアルで詳しく説明されています。

于 2013-02-01T07:05:51.480 に答える
3

どれどれ...

ESP が最初は 0x00180078 であるとします。その後、3 回プッシュした後、

00180078: 67
00180074: 66
00180070: 65

MEH を呼び出すと、すぐに ebx がプッシュされるため、スタックは次のようになります。

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

ESP = 00180068 で EBP をロードします。

sub esp,0 does nothing

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

最初の引数ではなく、最後の引数

   call [printf]

ただし、問題は次のとおりです。

   add esp, eax

これは何の役に立つはずだったのですか?printf が渡されたこの引数を保持すると仮定すると (ちなみにこれは必須ではありません)、なぜ引数をスタック ポインターに追加するのでしょうか? それはあなたのリターンを台無しにするはずです。あなたがしたいことは、espをebpの値に復元し、保存されたebx値をポップバックすることです。

于 2013-01-24T22:13:53.930 に答える
3

の呼び出し規則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
于 2013-01-25T04:38:51.273 に答える