2016-12-07 7 views
0

私はプログラムのコマンドライン引数で遊んでいます。特に、私は文字列argv [1]についていくつかのテストをしようとしています。私がargv [1]にアドレスを取得する2ステップの方法を使用すると、私のコードは正常に動作します。NASMのメモリアドレス指定

mov ebx, [ebp+12] 
mov eax, [ebx+4] ; address of argv[1] 

私は1つのステップを使用する場合、私のプログラムはジバリッシュを印刷します。

mov eax, [ebp+16] ; address of argv[1] 

どちらの方法でもアドレス[ebp + 16]を参照していると仮定して正しくありませんか?私は何か些細なことを逃していますか?

+5

これらは同じではありません。最初のものは '*(*(ebp + 12)+4)'です.2番目のものは '*(ebp + 16)'だけです。逆参照を取り除くことはできません。 – Jester

+2

@DanMašekに面倒なことには役に立たなかったことに感謝します。 – Eric

+0

@Jester非常に感謝しています。私はそれについて考えます。 – Eric

答えて

4

アセンブリでポインタへのポインタを操作すると、混乱することは簡単です。引数として渡されたときに

argvは真実argvでは、CHARまたはchar** argvへのポインタへのポインタであり、それらのアイテムタイプへのポインタにCアレイ減衰であるため、「文字列の配列」またはそれcharへのポインタの配列であります。

これは、いずれかの文字列の文字にアクセスするために2つの逆参照が必要であり、1つはそのような文字列へのポインタにアクセスする必要があることを示しています。

パラメータは逆の順序でスタックに渡されCDECL規則を仮定すると、我々はargcの値がebp+0chであることを持っている標準的なフレームポインタを設定し、標準的なプロローグを想定。
ebpはポインタの意味を持ちますので、ebp+0chは別のポインタを得るためのポインタ算術に過ぎません。今度はargcの値になります。

ebp+0chをC型にするとchar***となるため、ポインタargv[1]にアクセスするには2つの逆参照が必要です。

ESIargv[1]を取得するためのコードは次のとおりです。

;typeof(ebp+0ch) = char*** 

mov esi, DWORD [ebp+0ch]  ;1st defer, esi = argv, typeof(esi) = char** 
mov esi, DWORD [esi+04h]  ;2nd defer, esi = argv[1], typeof(esi) = char* 

;Optional, Get a char 
mov al, BYTE [esi]   ;3rd defer, al = argv[1][0], typeof(al) = char 

種類がチェック。


音が混乱しますか?
これらのポインタを描画しましょう!

 The stack          The memory 

100ch | 2000h | argv       2000h | 2008h | argv[0] 
1008h | 2  | argc       2004h | 2010h | argv[1] 
1004h | yyyyyy | return address    2008h | file | argv[0][0..3] 
1000h | xxxxxx | old frame pointer   200ch | .a\0\0 | argv[0][4..7] 
               2010h | -arg | argv[1][0..3] 
EBP = 1000h         2014h | 1\0\0\0 | argv[1][4..7] 

ebp+0ch 1000H + 0CH = 100CHであり、それはargv値のアドレスです。
mov esi, DWORD [ebp+0ch]mov esi, DWORD [100ch]と同じで、ESIを2000hに設定します。
2000hはargvの値であるため、アドレスはargv[0]です。

argv[1]のアドレスは4バイト先であり、したがって2000h + 04h = 2004hです。
mov esi, DWORD [esi+04h]mov esi, DWORD [2004h]と同じで、2010hにはESIと設定されています。
2010hは、文字列 "-arg1"のアドレスです。画像は上記CやC++のようなargv[argc]準拠規格0
でなければならないこと


注私は左その画像のうち。

+0

よく考えて、精巧な答えをありがとう。私はCのポインタの観点から理解しています。アドレスの表記法をすばやく詳しく説明できますか?私はhとchで混乱している。 – Eric

+4

@Eric: 'h'接尾辞は16進数を意味し、DOS/Windowsアセンブリでは' 0x100c'に代わるものとして一般的です。 'c'は16進数です。 (多くのDOS/Windowsアセンブラは '0xDEADBEEF'をサポートしていませんが、読みにくいIMOの' 0DEADBEEFh'しかサポートしていません。残念なことに、このtrailing-hスタイルはx86 asmでは一般的です。)NASMはwayとその他の接尾辞/他の数ベース。 (基数2はしばしば有用です)。 –

-2

これはあなたの質問に対する答えです。

mov eax, [ebp+16] 
lea ebx, [ebp+12] 
mov eax, [ebx+4] 

又は

mov eax, [ebp+16] 
mov ebx, ebp 
add ebx, 12 
mov eax, [ebx+4] 

前者はコードの数バイトを保存し、それらは機能的に等価です。

+2

mmm ...なぜ 'mov eax、[ebp + 16]'を使用せずに 'eax'の2/3命令を上書きするかは分かりません。それに加えて、あなたのコードは、 'argv [1]'ではなく、* main *の3番目の(標準ではないがしばしば提供される)パラメータにアクセスする 'mov eax、[ebp + 16]'を書くばかりの方法です。これは、OPの質問に対する答えではありません。 –