Security Record

セキュリティ全般に関する情報を発信しています

アセンブリ言語で「Hello world.」してみた

アセンブリ言語に入門「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はデータ転送の命令の様です。

アセンブリ言語の命令一覧(一部)

  1. MOV(Move)
    レジスタ間やメモリからレジスタへのデータの転送を行います。
    例えば、MOV AX, BXはレジスタBXの値をレジスタAXにコピーします。
  2. ADD(Addition)
    レジスタやメモリのデータに対して加算を行います。
    例えば、ADD AX, 10はレジスタAXの値に10を加えます。
  3. SUB(Subtraction)
    レジスタやメモリのデータから減算を行います。
    例えば、SUB BX, 5はレジスタBXの値から5を引きます。
  4. JMP(Jump)
    プログラムの実行を指定したラベルまたはアドレスにジャンプさせます。 条件なしの無条件ジャンプです。
  5. CMP(Compare)
    レジスタやメモリのデータを比較し、フラグレジスタに結果をセットします。
    条件分岐で使用されます。
  6. JE(Jump if Equal)
    前回の比較結果が等しい場合にジャンプします。
    ZF(Zero Flag)がセットされている必要があります。
  7. JG(Jump if Greater)
    前回の比較結果が大きい場合にジャンプします。
    ZFがクリアされ、SF(Sign Flag)とOF(Overflow Flag)が一致している必要があります。
  8. CALL(Call Subroutine)
    サブルーチン(関数)を呼び出します。
    呼び出し元のアドレスをスタックに保存し、制御をサブルーチンに渡します。
  9. RET(Return)
    サブルーチンから呼び出し元に戻ります。
    スタックから保存されたアドレスをポップして使用します。
  10. NOP(No Operation)
    何もしない命令で、プログラムの一時停止やデバッグ目的で使用されます。

アセンブリ言語のレジスタ一覧

  1. 汎用レジスタ(General-Purpose Registers)
    rax, rbx, rcx, rdx:主に汎用のデータレジスタとして使用されます。
    rsi, rdi, r8 から r15:追加の汎用レジスタ。x86-64アーキテクチャでは増えた汎用レジスタが含まれています。
  2. インデックスレジスタ(Index Registers)
    rsi と rdi:文字列操作時に使用されるソースとデスティネーションのアドレスを指定するのに使われます。
  3. カウンタレジスタ(Counter Register)
    rcx:ループ操作時にカウンタとして使用されます。LOOP 命令などでカウントダウンに利用されます。
  4. ポインタレジスタ(Pointer Registers)
    rsp: :スタックポインタ。スタックのトップを示します。
    rbp: :ベースポインタ。スタックフレームのベースアドレスを示します。
  5. フラグレジスタ(Flag Register)
    rflags(または eflags 32ビット版):条件分岐や演算の結果を示すフラグを格納します。
    例えば、ゼロフラグ(ZF)、サインフラグ(SF)、オーバーフローフラグ(OF)など。
  6. インストラクションポインタ(Instruction Pointer) rip:: 現在実行中の命令を示すポインタ。プログラムカウンタとも呼ばれます。
  7. フローティングポイントレジスタ(Floating-Point Registers)
    xmm0, xmm1, ... xmm15:浮動小数点演算のためのレジスタセット。SIMD演算に使用されます。
  8. メディアレジスタ(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言語の文法自体は数行のシンプルなものなので何とか読み進められています。
アセンブリ言語についてはほぼ機械語なので、ネットなどで知識を補強しながら実際に手を動かして試してみようと思います。