2017-03-06 3 views
1

私は今日機械語について学び始めました。私はC言語で "Hello、world!"という文字を印刷する基本的な "Hello World"プログラムを書いた。 forループを使用して10回。私は、(私のコンピュータは、x86プロセッサを持っていると私はインテルの構文を使用するようGDBを設定している)の主分解して機械語のコードを見てGNUデバッガを使用:コンパイル済みの "Hello World" Cプログラムは機械語を使用してStringをどのように格納しますか?

[email protected]:~/Path/To/Code$ gdb -q ./a.out 
Reading symbols from ./a.out...done. 
(gdb) list 
1  #include <stdio.h> 
2 
3  int main() 
4  { 
5   int i; 
6   for(i = 0; i < 10; i++) { 
7    printf("Hello, world!\n"); 
8   } 
9   return 0; 
10  } 
(gdb) disassemble main 
Dump of assembler code for function main: 
    0x0804841d <+0>:  push ebp 
    0x0804841e <+1>:  mov  ebp,esp 
    0x08048420 <+3>:  and  esp,0xfffffff0 
    0x08048423 <+6>:  sub  esp,0x20 
    0x08048426 <+9>:  mov  DWORD PTR [esp+0x1c],0x0 
    0x0804842e <+17>: jmp  0x8048441 <main+36> 
    0x08048430 <+19>: mov  DWORD PTR [esp],0x80484e0 
    0x08048437 <+26>: call 0x80482f0 <[email protected]> 
    0x0804843c <+31>: add  DWORD PTR [esp+0x1c],0x1 
    0x08048441 <+36>: cmp  DWORD PTR [esp+0x1c],0x9 
    0x08048446 <+41>: jle  0x8048430 <main+19> 
    0x08048448 <+43>: mov  eax,0x0 
    0x0804844d <+48>: leave 
    0x0804844e <+49>: ret 
End of assembler dump. 
(gdb) x/s 0x80484e0 
0x80484e0: "Hello, world!" 

私はマシンのほとんどを理解しますコードとそれぞれのコマンドが何をするかを示します。私が正しく理解していれば、アドレス「0x80484e0」がespレジスタにロードされ、このアドレスのメモリを使用できるようになります。私はアドレスを調べて、驚くことではないが、それは望む文字列を含んでいた。私の質問は今です - 最初にその文字列はどうやってそこに入りましたか?この場所に文字列を設定するプログラムの部分を見つけることができません。

また私は何か他のことを理解していません:プログラムを最初に起動すると、eipはを指し、ここで変数iは[esp + 0x1c]で初期化されます。ただし、espが指し示すアドレスはプログラムで後で(0x80484e0へ)変更されますが、その変更後も「i」には[esp + 0x1c]が使用されます。アドレスespが変更を指しているときに[esp + 0x1c]アドレスを変更してはいけませんか?

+1

バイナリにあります。OSがプログラムを起動したとき、プログラムのマシンコードと同じように、メモリにロードされています。 '[esp]'は 'esp'と同じではないことに注意してください。前者はメモリにアクセスし、' esp'自体は変更しません。 – Jester

+0

メモリにロードされます。私は、その文字列のメモリアドレスが '0x8048441'で始まり、' 0x80484e0'で終わると仮定しています。文字列は整数のリストです。 – Xorifelse

+0

@Jesterああ、 "mov DWORD PTR [esp]、0x80484e0"は実際にespが新しいアドレスを指し示すのではなく、現在指しているアドレスに0x80484e0を書き込むだけですか? –

答えて

2

バイナリまたはプログラムは、マシンコードとデータの両方で構成されています。この場合、ソースコードに入れた文字列、コンパイラだけでもバイトだけのデータ、それがどのように使用されたのかは読み込み専用データと見なされます。したがって、.rodataまたは.textになる可能性のあるコンパイラまたはコンパイラが使用する可能性がある別の名前です。 Gccはおそらくそれを.rodataと呼ぶでしょう。プログラム自体は.text形式です。リンカーが表示され、リンクすると、.text、.data、.bss、.rodata、その他のアイテムがある場所を見つけてから接続します。 printfの呼び出しの場合、リンカーは文字列、バイト配列をどこに置いているのかを知っていて、その名前(何らかの内部一時的な名前は間違いない)とその名前についてprintf呼び出しに伝えられたリンカは、printfを呼び出す前に、アドレスをフォーマット文字列に取り込む命令をパッチします。

Disassembly of section .text: 

0000000000400430 <main>: 
    400430: 53      push %rbx 
    400431: bb 0a 00 00 00   mov $0xa,%ebx 
    400436: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1) 
    40043d: 00 00 00 
    400440: bf e4 05 40 00   mov $0x4005e4,%edi 
    400445: e8 b6 ff ff ff   callq 400400 <[email protected]> 
    40044a: 83 eb 01    sub $0x1,%ebx 
    40044d: 75 f1     jne 400440 <main+0x10> 
    40044f: 31 c0     xor %eax,%eax 
    400451: 5b      pop %rbx 
    400452: c3      retq 
    400453: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1) 
    40045a: 00 00 00 
    40045d: 0f 1f 00    nopl (%rax) 



Disassembly of section .rodata: 

00000000004005e0 <_IO_stdin_used>: 
    4005e0: 01 00     add %eax,(%rax) 
    4005e2: 02 00     add (%rax),%al 
    4005e4: 48      rex.W 
    4005e5: 65 6c     gs insb (%dx),%es:(%rdi) 
    4005e7: 6c      insb (%dx),%es:(%rdi) 
    4005e8: 6f      outsl %ds:(%rsi),(%dx) 
    4005e9: 2c 20     sub $0x20,%al 
    4005eb: 77 6f     ja  40065c <__GNU_EH_FRAME_HDR+0x68> 
    4005ed: 72 6c     jb  40065b <__GNU_EH_FRAME_HDR+0x67> 
    4005ef: 64 21 00    and %eax,%fs:(%rax) 

コンパイラは、この命令をエンコードされたが、リンカが後でそれを埋めることができるように、おそらくゼロまたは一部が

400440: bf e4 05 40 00   mov $0x4005e4,%edi 

を埋めるように、アドレスを残しています。 gnu逆アセンブラは意味をなさない.rodata(と.dataなど)ブロックを逆アセンブルしようとします。したがって、アドレス0x4005e4で始まるあなたの文字列を解釈しようとしている命令は無視してください。

オブジェクトの分解をリンクする前に

Disassembly of section .text.startup: 

0000000000000000 <main>: 
    0: 53      push %rbx 
    1: bb 0a 00 00 00   mov $0xa,%ebx 
    6: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1) 
    d: 00 00 00 
    10: bf 00 00 00 00   mov $0x0,%edi 
    15: e8 00 00 00 00   callq 1a <main+0x1a> 
    1a: 83 eb 01    sub $0x1,%ebx 
    1d: 75 f1     jne 10 <main+0x10> 
    1f: 31 c0     xor %eax,%eax 
    21: 5b      pop %rbx 
    22: c3      retq 

0000000000000000 <.rodata.str1.1>: 
    0: 48      rex.W 
    1: 65 6c     gs insb (%dx),%es:(%rdi) 
    3: 6c      insb (%dx),%es:(%rdi) 
    4: 6f      outsl %ds:(%rsi),(%dx) 
    5: 2c 20     sub $0x20,%al 
    7: 77 6f     ja  78 <main+0x78> 
    9: 72 6c     jb  77 <main+0x77> 
    b: 64 21 00    and %eax,%fs:(%rax) 

がリンクされていないが、リンカーのオフセットこのアドレスは/後を埋めるためにちょうどパッドを有し、.textセクションと.rodataの二つの部分を示しています。

10: bf 00 00 00 00   mov $0x0,%edi 

また、オブジェクトには.rodataの文字列のみが含まれています。ライブラリや他のアイテムとリンクして完全なプログラムにするためには、もっと多くの.rodataを明示的に追加しましたが、リンカはそのすべてを管理します。この例オブジェクト

Disassembly of section .text: 

0000000000000000 <fun>: 
    0: 8b 35 00 00 00 00  mov 0x0(%rip),%esi  # 6 <fun+0x6> 
    6: 8b 3d 00 00 00 00  mov 0x0(%rip),%edi  # c <fun+0xc> 
    c: ba 07 00 00 00   mov $0x7,%edx 
    11: e9 00 00 00 00   jmpq 16 <fun+0x16> 

Disassembly of section .data: 

0000000000000000 <b>: 
    0: 05      .byte 0x5 
    1: 00 00     add %al,(%rax) 
    ... 

Disassembly of section .rodata: 

0000000000000000 <c>: 
    0: 07      (bad) 
    1: 00 00     add %al,(%rax) 
    ... 

、あなたは.bssセクションを参照して、それをリンクする必要が何らかの理由として解体

void more_fun (unsigned int, unsigned int, unsigned int); 

unsigned int a; 
unsigned int b=5; 
const unsigned int c=7; 

void fun (void) 
{ 
    more_fun(a,b,c); 
} 

で見ることがおそらく

容易になります。この例のポイントは、関数のマシンコードは.text、初期化されていないグローバルは.bss、初期化されたグローバルは.data、const初期化されたグローバルは.rodataです。コンパイラは、グローバルであってもconstを知っても分かりませんでしたので、その値を数学にハードコーディングするだけで、RAMから読み込む必要はありませんが、RAMから読み込まなければならない他の2変数は命令を生成しますリンク時にリンカによって記入されるアドレスのゼロを使用します。

あなたの場合、読み取り専用の/ constデータはバイトの集合であり、それはあなたのソースファイルで定義されたバイトがprintfの最初のパラメータとして指し示されるように、数学演算ではありませんでした。

バイナリにはマシンコードだけではありません。そして、コンパイラとリンカは、マシンコードが取得するメモリに配置されたものを持つことができます。マシンコード自体は、残りのマシンコードで使用されるすべての値を書き込む必要はありません。

+0

命令の奇妙なボールシーケンスを生成したテキストの逆アセンブリを使用する代わりに、オブジェクトセクション棒をデータ 'dw 1,2'' db" Hello、World! "'として表示する方がいいでしょう。 – rcgldr

+0

確かにコードが読み込みにクリーンではない(コンパイル時に-cの代わりに-s)、それは単にバイトの配列であることを理解することが重要です...プロセッサはASCIIを参照しません... –

+0

すでに何が起こっていたのかを知るためにすでに上記の誰かがいると仮定してください。-sでコンパイルしないで-save-tempsを使用すると、アセンブラに送られたアセンブリは削除されません。 –

0

コンパイラは文字列をオブジェクトコードにリンクし、リンカがマシンコードに「ハードワイヤ」します。

文字列がコードに埋め込まれておらず、データ領域に格納されていないため、文字列へのポインタを取得して変更しようとすると例外が発生します。

+0

これはもう一度修正されるかもしれません実行時にハッカーの作業を困難にすることができます。 –

関連する問題