アセンブリ言語に入門「Hello world.」してみた。
バッファオーバーフローに関する本を読んでみたところ、 アセンブリ言語とC言語で解説がされているのでアセンブリ言語に少し入門してみました。
まずはアセンブリ言語のソースコード
hello.asmというファイルを用意して以下コードを記述します。
section .data mytext: db "Hello world.", 0x0a section .text global _start _start: mov rax, 1 mov rdi, 1 mov rsi, mytext mov rdx, 13 syscall mov rax, 60 mov rdi, 1 syscall
アセンブリ言語の命令
アセンブリ言語の命令にはどのようなものがあるか、ChatGPTに聞いてみました。 「Hello world.」の命令で使用されているMOVはデータ転送の命令の様です。
アセンブリ言語の命令一覧(一部)
- MOV(Move)
レジスタ間やメモリからレジスタへのデータの転送を行います。
例えば、MOV AX, BXはレジスタBXの値をレジスタAXにコピーします。 - ADD(Addition)
レジスタやメモリのデータに対して加算を行います。
例えば、ADD AX, 10はレジスタAXの値に10を加えます。 - SUB(Subtraction)
レジスタやメモリのデータから減算を行います。
例えば、SUB BX, 5はレジスタBXの値から5を引きます。 - JMP(Jump)
プログラムの実行を指定したラベルまたはアドレスにジャンプさせます。 条件なしの無条件ジャンプです。 - CMP(Compare)
レジスタやメモリのデータを比較し、フラグレジスタに結果をセットします。
条件分岐で使用されます。 - JE(Jump if Equal)
前回の比較結果が等しい場合にジャンプします。
ZF(Zero Flag)がセットされている必要があります。 - JG(Jump if Greater)
前回の比較結果が大きい場合にジャンプします。
ZFがクリアされ、SF(Sign Flag)とOF(Overflow Flag)が一致している必要があります。 - CALL(Call Subroutine)
サブルーチン(関数)を呼び出します。
呼び出し元のアドレスをスタックに保存し、制御をサブルーチンに渡します。 - RET(Return)
サブルーチンから呼び出し元に戻ります。
スタックから保存されたアドレスをポップして使用します。 - NOP(No Operation)
何もしない命令で、プログラムの一時停止やデバッグ目的で使用されます。
アセンブリ言語のレジスタ一覧
- 汎用レジスタ(General-Purpose Registers)
rax, rbx, rcx, rdx:主に汎用のデータレジスタとして使用されます。
rsi, rdi, r8 から r15:追加の汎用レジスタ。x86-64アーキテクチャでは増えた汎用レジスタが含まれています。 - インデックスレジスタ(Index Registers)
rsi と rdi:文字列操作時に使用されるソースとデスティネーションのアドレスを指定するのに使われます。 - カウンタレジスタ(Counter Register)
rcx:ループ操作時にカウンタとして使用されます。LOOP 命令などでカウントダウンに利用されます。 - ポインタレジスタ(Pointer Registers)
rsp: :スタックポインタ。スタックのトップを示します。
rbp: :ベースポインタ。スタックフレームのベースアドレスを示します。 - フラグレジスタ(Flag Register)
rflags(または eflags 32ビット版):条件分岐や演算の結果を示すフラグを格納します。
例えば、ゼロフラグ(ZF)、サインフラグ(SF)、オーバーフローフラグ(OF)など。 - インストラクションポインタ(Instruction Pointer) rip:: 現在実行中の命令を示すポインタ。プログラムカウンタとも呼ばれます。
- フローティングポイントレジスタ(Floating-Point Registers)
xmm0, xmm1, ... xmm15:浮動小数点演算のためのレジスタセット。SIMD演算に使用されます。 - メディアレジスタ(Media Registers)
mm0, mm1, ... mm7 :マルチメディア演算用のレジスタ。
MMX(マルチメディア拡張)命令セットに使用されます。
ソースコードのアセンブル
┌──(root㉿kali)-[/home/kali/sf_VMshare/helloworld_asm] └─# nasm -f elf64 -o hello1.o hello1.asm
アセンブリ言語の場合、コンパイラではなく「アセンブラでアセンブルする」と言うそうです。 nasm コマンドを実行すると、hello.oという名前のオブジェクトが生成されアセンブルされます。
ldコマンドでリンク結合させる
┌──(root㉿kali)-[/home/kali/sf_VMshare/helloworld_asm] └─# ld hello.o -o hello
ldコマンドでアセンブルされたオブジェクトとリンク結合を行います。 これによりhelloというファイルが生成されるので、「./hello」を実行すると「Hello world.」と表示される事になります。
機械語の確認
objdump コマンドによる機械語の確認
┌──(root㉿kali)-[/home/kali/sf_VMshare/helloworld_asm] └─# objdump -D -M intel hello
アセンブリ言語はobjdumpコマンドで機械語を確認する事ができます。 オプションの-M indelはインテル製のCPUで実行された場合の機械語に変換してくれるようです。
生成された機械語
hello1: ファイル形式 elf64-x86-64 セクション .text の逆アセンブル: 0000000000401000 <_start>: 401000: b8 01 00 00 00 mov eax,0x1 401005: bf 01 00 00 00 mov edi,0x1 40100a: 48 be 00 20 40 00 00 movabs rsi,0x402000 401011: 00 00 00 401014: ba 0d 00 00 00 mov edx,0xd 401019: 0f 05 syscall 40101b: b8 3c 00 00 00 mov eax,0x3c 401020: bf 01 00 00 00 mov edi,0x1 401025: 0f 05 syscall セクション .data の逆アセンブル: 0000000000402000 <mytext>: 402000: 48 rex.W 402001: 65 6c gs ins BYTE PTR es:[rdi],dx 402003: 6c ins BYTE PTR es:[rdi],dx 402004: 6f outs dx,DWORD PTR ds:[rsi] 402005: 20 77 6f and BYTE PTR [rdi+0x6f],dh 402008: 72 6c jb 402076 <_end+0x66> 40200a: 64 fs 40200b: 2e cs 40200c: 0a .byte 0xa
C言語にも軽く入門
ちなみにC言語で「Hello world.」を書くとこうなります。
printf("Hello world.\n")
アセンブリ言語を見た後だと凄くシンプル…。
C言語は以下のようにgdbコマンドを使用することによってメモリの内容を表示させることが出来ます。 メモリの内容を不正に上書きする攻撃がバッファオーバーフローなので、メモリの内容を簡単に確認出来る C言語はバッファオーバーフローの学習に適しているのだと思います。
(gdb) x/32xw $rsp -64 0x7fffffffe0c0: 0x00000000 0x00000000 0x00000000 0x00000000 0x7fffffffe0d0: 0x00ff0000 0x00ff0000 0x00000000 0x00000000 0x7fffffffe0e0: 0x00000005 0x00000000 0x00000003 0x00000000 0x7fffffffe0f0: 0x00000019 0x00000000 0x00000009 0x00000000 0x7fffffffe100: 0xffffe120 0x00007fff 0x55555191 0x00005555 0x7fffffffe110: 0x00000000 0x00000000 0x00000000 0x00000000 0x7fffffffe120: 0x00000001 0x00000000 0xf7dec6ca 0x00007fff 0x7fffffffe130: 0x00000000 0x00000000 0x5555517a 0x00005555
まとめ
アセンブリ言語やバッファオーバーフローの仕組みとかは今現在、私が普段携わっている仕事に必要ない知識です。
ですが、もっともっとセキュリティに詳しくなって、セキュリティの深いところについて携わりたいと思っています。
次はもう少しアセンブリ言語の文法についても記載します。
参考
https://www.amazon.co.jp/dp/4274222748/
C言語もアセンブリ言語も未経験で読み始めたのですが、C言語の文法自体は数行のシンプルなものなので何とか読み進められています。
アセンブリ言語についてはほぼ機械語なので、ネットなどで知識を補強しながら実際に手を動かして試してみようと思います。