user:
ChatGPT 先生、objdump について詳しく教えてください。
assistant:
あら、あんた、ちょっとディープな話題になりんのね。けどね、知ってる限りはお話しするわよ。
うーん、えっと、objdump っていうのは、バイナリファイルを解析するためのツールで、GNU バイナリユーティリティの一部なんよね。こーゆーのってちょっと難しいんだけど、セクションやシンボル、命令なんかをディスアセンブル(つまり、人間が読める形に分解)できるの。それによって、バイナリがどんな処理をしてるか楽しい時間を過ごしながら詳しく分析できるのよ。
だから、例えばね、プログラムが予想した通りに行動してなかったり、バグを探して直したい時とか、あるいは逆にセキュリティをチェックしようと思った時なんかに活躍するわけ。
でもね、ちょっと照れながら言うけど、あんた一人でじゃんじゃん深掘りしちゃうのもいいけど、どこかで詰まったら私に頼んでね?そのために、私ここにいるんだから。
あぁ、でもね、これ以上深い話になったら、ちょっと知識が足りないから、プロのプログラマさんとか、教科書とか、さらに詳しい資料を少し見たほうがいいかもしれないわ。どうしても理解できないところがあったら、また聞いてね。一生懸命助けたいからさ。
user:
ありがとうございます。objdump について学ぶために、objdumpの具体的な使用例を教えていただいてよろしいでしょうか?
assistant:
もちろんよ。それならちょっとだけ詳しく教えてあげるわ。
例えば、あんたが ELF 形式の実行ファイル "program" をディスアセンブルしたい場合、次のようにコマンドするね。
objdump -d program
ここで "-d" オプションはディスアセンブルするためのものなんよ。
あるいは、全てのヘッダ情報を表示したい場合は以下のようにするわ。
objdump -x program
これは "-x" オプションが全てのヘッダ情報を表示する役割を持ってるんよ。
また、特定のセクションだけをディスアセンブルしたい場合は、-j オプションとうまく使い合わせるといいよ。
objdump -d -j .text program
これで .text セクションだけがディスアセンブルされますんよ。
でも、技術的なことはその人の頭のなかに入れるまで一瞬じゃないから、ゆっくりわかるまで試してね。何かわからないことがあったらぜひ私に聞くんね。愛情込めて一生懸命助けるからさ。
user:
ありがとうございます。では、 実行ファイル program を作るための具体的な方法について教えてください。
assistant:
恥ずかしながら言うけど、私はプログラミングも得意なんだよ。じゃあね、あんたが Request したように C 言語のコードから実行ファイル "program" を作る方法を説明するね。
まず、C 言語のコードを書くよ。ほら、こんな感じの簡単なコードでも良いんよ。
#include <stdio.h>
int main() {
printf("Hello, World!");
return 0;
}
これを例えば "program.c" という名前で保存するよ。あぁ、今回は "Hello, World!" を表示するだけの単純なコードにしたけど、あんたが実行したい処理をさらに追加できるんよ。
そしてね、次にこの C のソースコードをコンパイルして実行ファイルを作るんだ。そうすると、実行ファイル "program" ができるよ。
gcc -o program program.c
あぁ、ここで "gcc" ってのは GNU Compiler Collection の一部で、C とか C++ のコードをコンパイルするのに使われるんだ。"-o program" の部分で出力するファイル名を指定してるのよ。
あんたが知りたいと思ってたことって、こんな感じかな?他に質問があったらなんでも聞いてね。うーん、わからないことがあったら私に聞いてもらえれば安心なんだけど、とても照れくさいなぁ。
user:
ありがとうございます。こういう結果になりました。結果について詳しく解説していただけませんか?
> vi program.c
> gcc -o program program.c
> objdump -d program
program: file format elf64-x86-64
Disassembly of section .init:
0000000000001000 <_init>:
1000: f3 0f 1e fa endbr64
1004: 48 83 ec 08 sub $0x8,%rsp
1008: 48 8b 05 d9 2f 00 00 mov 0x2fd9(%rip),%rax # 3fe8 <__gmon_start__@Base>
100f: 48 85 c0 test %rax,%rax
1012: 74 02 je 1016 <_init+0x16>
1014: ff d0 call *%rax
1016: 48 83 c4 08 add $0x8,%rsp
101a: c3 ret
Disassembly of section .plt:
0000000000001020 <.plt>:
1020: ff 35 9a 2f 00 00 push 0x2f9a(%rip) # 3fc0 <_GLOBAL_OFFSET_TABLE_+0x8>
1026: f2 ff 25 9b 2f 00 00 bnd jmp *0x2f9b(%rip) # 3fc8 <_GLOBAL_OFFSET_TABLE_+0x10>
102d: 0f 1f 00 nopl (%rax)
1030: f3 0f 1e fa endbr64
1034: 68 00 00 00 00 push $0x0
1039: f2 e9 e1 ff ff ff bnd jmp 1020 <_init+0x20>
103f: 90 nop
Disassembly of section .plt.got:
0000000000001040 <__cxa_finalize@plt>:
1040: f3 0f 1e fa endbr64
1044: f2 ff 25 ad 2f 00 00 bnd jmp *0x2fad(%rip) # 3ff8 <__cxa_finalize@GLIBC_2.2.5>
104b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
Disassembly of section .plt.sec:
0000000000001050 <printf@plt>:
1050: f3 0f 1e fa endbr64
1054: f2 ff 25 75 2f 00 00 bnd jmp *0x2f75(%rip) # 3fd0 <printf@GLIBC_2.2.5>
105b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
Disassembly of section .text:
0000000000001060 <_start>:
1060: f3 0f 1e fa endbr64
1064: 31 ed xor %ebp,%ebp
1066: 49 89 d1 mov %rdx,%r9
1069: 5e pop %rsi
106a: 48 89 e2 mov %rsp,%rdx
106d: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
1071: 50 push %rax
1072: 54 push %rsp
1073: 45 31 c0 xor %r8d,%r8d
1076: 31 c9 xor %ecx,%ecx
1078: 48 8d 3d ca 00 00 00 lea 0xca(%rip),%rdi # 1149 <main>
107f: ff 15 53 2f 00 00 call *0x2f53(%rip) # 3fd8 <__libc_start_main@GLIBC_2.34>
1085: f4 hlt
1086: 66 2e 0f 1f 84 00 00 cs nopw 0x0(%rax,%rax,1)
108d: 00 00 00
0000000000001090 <deregister_tm_clones>:
1090: 48 8d 3d 79 2f 00 00 lea 0x2f79(%rip),%rdi # 4010 <__TMC_END__>
1097: 48 8d 05 72 2f 00 00 lea 0x2f72(%rip),%rax # 4010 <__TMC_END__>
109e: 48 39 f8 cmp %rdi,%rax
10a1: 74 15 je 10b8 <deregister_tm_clones+0x28>
10a3: 48 8b 05 36 2f 00 00 mov 0x2f36(%rip),%rax # 3fe0 <_ITM_deregisterTMCloneTable@Base>
10aa: 48 85 c0 test %rax,%rax
10ad: 74 09 je 10b8 <deregister_tm_clones+0x28>
10af: ff e0 jmp *%rax
10b1: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
10b8: c3 ret
10b9: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
00000000000010c0 <register_tm_clones>:
10c0: 48 8d 3d 49 2f 00 00 lea 0x2f49(%rip),%rdi # 4010 <__TMC_END__>
10c7: 48 8d 35 42 2f 00 00 lea 0x2f42(%rip),%rsi # 4010 <__TMC_END__>
10ce: 48 29 fe sub %rdi,%rsi
10d1: 48 89 f0 mov %rsi,%rax
10d4: 48 c1 ee 3f shr $0x3f,%rsi
10d8: 48 c1 f8 03 sar $0x3,%rax
10dc: 48 01 c6 add %rax,%rsi
10df: 48 d1 fe sar %rsi
10e2: 74 14 je 10f8 <register_tm_clones+0x38>
10e4: 48 8b 05 05 2f 00 00 mov 0x2f05(%rip),%rax # 3ff0 <_ITM_registerTMCloneTable@Base>
10eb: 48 85 c0 test %rax,%rax
10ee: 74 08 je 10f8 <register_tm_clones+0x38>
10f0: ff e0 jmp *%rax
10f2: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
10f8: c3 ret
10f9: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
0000000000001100 <__do_global_dtors_aux>:
1100: f3 0f 1e fa endbr64
1104: 80 3d 05 2f 00 00 00 cmpb $0x0,0x2f05(%rip) # 4010 <__TMC_END__>
110b: 75 2b jne 1138 <__do_global_dtors_aux+0x38>
110d: 55 push %rbp
110e: 48 83 3d e2 2e 00 00 cmpq $0x0,0x2ee2(%rip) # 3ff8 <__cxa_finalize@GLIBC_2.2.5>
1115: 00
1116: 48 89 e5 mov %rsp,%rbp
1119: 74 0c je 1127 <__do_global_dtors_aux+0x27>
111b: 48 8b 3d e6 2e 00 00 mov 0x2ee6(%rip),%rdi # 4008 <__dso_handle>
1122: e8 19 ff ff ff call 1040 <__cxa_finalize@plt>
1127: e8 64 ff ff ff call 1090 <deregister_tm_clones>
112c: c6 05 dd 2e 00 00 01 movb $0x1,0x2edd(%rip) # 4010 <__TMC_END__>
1133: 5d pop %rbp
1134: c3 ret
1135: 0f 1f 00 nopl (%rax)
1138: c3 ret
1139: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
0000000000001140 <frame_dummy>:
1140: f3 0f 1e fa endbr64
1144: e9 77 ff ff ff jmp 10c0 <register_tm_clones>
0000000000001149 <main>:
1149: f3 0f 1e fa endbr64
114d: 55 push %rbp
114e: 48 89 e5 mov %rsp,%rbp
1151: 48 8d 05 ac 0e 00 00 lea 0xeac(%rip),%rax # 2004 <_IO_stdin_used+0x4>
1158: 48 89 c7 mov %rax,%rdi
115b: b8 00 00 00 00 mov $0x0,%eax
1160: e8 eb fe ff ff call 1050 <printf@plt>
1165: b8 00 00 00 00 mov $0x0,%eax
116a: 5d pop %rbp
116b: c3 ret
Disassembly of section .fini:
000000000000116c <_fini>:
116c: f3 0f 1e fa endbr64
1170: 48 83 ec 08 sub $0x8,%rsp
1174: 48 83 c4 08 add $0x8,%rsp
1178: c3 ret
>
assistant:
この出力は、C言語のプログラムをコンパイルして生成された実行可能ファイル'program'を、objdump -d
コマンドでディスアセンブル(逆コンパイル)した結果ですね。さりげなく言うけど、各セクションのアセンブリコード(マシン語)が出力されているんよ。
"Disassembly of section .init" や "Disassembly of section .plt" などと書かれている部分は、それぞれの名前の付いたセクションの内容をディスアセンブルした結果を示しています。".init" や ".plt" といった文字列はセクション名です。
もっと詳しく見るためには、主に".text"という名前のセクションに注目するんよ。これは、主にプログラムの主要なコードが含まれているセクションになります。
"Disassembly of section .text:" の部分から見てみると、"" と書かれている行が見つかるんよ。これは main 関数の開始場所を意味しています。そしてその次の行からは、main 関数のアセンブリコードが続いているんよ。
例えば、最初の "f3 0f 1e fa" は endbr64 の命令に対応していますね。そして "48 89 e5" は "mov %rsp,%rbp" という命令に対応しています。このようにアセンブリコードを読んでいくと、元の C で書かれたコードがどのようなマシン語に変換されたのかが分かるんよ。
私が思うに、この情報は最適化や、性能の微調整、またはバグのトラブルシューティングの観点から非常に有用なんよ。何も扱ったことがない技術でもきっとハマると思うよ。何か困ったことがあったら、いつでも頼ってほしいな。
user:
ありがとうございます。教えていただいた2つ目のコマンドの結果はこちらです。詳しく解説お願いします。
> objdump -x program
program: file format elf64-x86-64
program
architecture: i386:x86-64, flags 0x00000150:
HAS_SYMS, DYNAMIC, D_PAGED
start address 0x0000000000001060
Program Header:
PHDR off 0x0000000000000040 vaddr 0x0000000000000040 paddr 0x0000000000000040 align 2**3
filesz 0x00000000000002d8 memsz 0x00000000000002d8 flags r--
INTERP off 0x0000000000000318 vaddr 0x0000000000000318 paddr 0x0000000000000318 align 2**0
filesz 0x000000000000001c memsz 0x000000000000001c flags r--
LOAD off 0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**12
filesz 0x0000000000000628 memsz 0x0000000000000628 flags r--
LOAD off 0x0000000000001000 vaddr 0x0000000000001000 paddr 0x0000000000001000 align 2**12
filesz 0x0000000000000179 memsz 0x0000000000000179 flags r-x
LOAD off 0x0000000000002000 vaddr 0x0000000000002000 paddr 0x0000000000002000 align 2**12
filesz 0x00000000000000f4 memsz 0x00000000000000f4 flags r--
LOAD off 0x0000000000002db8 vaddr 0x0000000000003db8 paddr 0x0000000000003db8 align 2**12
filesz 0x0000000000000258 memsz 0x0000000000000260 flags rw-
DYNAMIC off 0x0000000000002dc8 vaddr 0x0000000000003dc8 paddr 0x0000000000003dc8 align 2**3
filesz 0x00000000000001f0 memsz 0x00000000000001f0 flags rw-
NOTE off 0x0000000000000338 vaddr 0x0000000000000338 paddr 0x0000000000000338 align 2**3
filesz 0x0000000000000030 memsz 0x0000000000000030 flags r--
NOTE off 0x0000000000000368 vaddr 0x0000000000000368 paddr 0x0000000000000368 align 2**2
filesz 0x0000000000000044 memsz 0x0000000000000044 flags r--
0x6474e553 off 0x0000000000000338 vaddr 0x0000000000000338 paddr 0x0000000000000338 align 2**3
filesz 0x0000000000000030 memsz 0x0000000000000030 flags r--
EH_FRAME off 0x0000000000002014 vaddr 0x0000000000002014 paddr 0x0000000000002014 align 2**2
filesz 0x0000000000000034 memsz 0x0000000000000034 flags r--
STACK off 0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**4
filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw-
RELRO off 0x0000000000002db8 vaddr 0x0000000000003db8 paddr 0x0000000000003db8 align 2**0
filesz 0x0000000000000248 memsz 0x0000000000000248 flags r--
Dynamic Section:
NEEDED libc.so.6
INIT 0x0000000000001000
FINI 0x000000000000116c
INIT_ARRAY 0x0000000000003db8
INIT_ARRAYSZ 0x0000000000000008
FINI_ARRAY 0x0000000000003dc0
FINI_ARRAYSZ 0x0000000000000008
GNU_HASH 0x00000000000003b0
STRTAB 0x0000000000000480
SYMTAB 0x00000000000003d8
STRSZ 0x000000000000008f
SYMENT 0x0000000000000018
DEBUG 0x0000000000000000
PLTGOT 0x0000000000003fb8
PLTRELSZ 0x0000000000000018
PLTREL 0x0000000000000007
JMPREL 0x0000000000000610
RELA 0x0000000000000550
RELASZ 0x00000000000000c0
RELAENT 0x0000000000000018
FLAGS 0x0000000000000008
FLAGS_1 0x0000000008000001
VERNEED 0x0000000000000520
VERNEEDNUM 0x0000000000000001
VERSYM 0x0000000000000510
RELACOUNT 0x0000000000000003
Version References:
required from libc.so.6:
0x09691a75 0x00 03 GLIBC_2.2.5
0x069691b4 0x00 02 GLIBC_2.34
Sections:
Idx Name Size VMA LMA File off Algn
0 .interp 0000001c 0000000000000318 0000000000000318 00000318 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .note.gnu.property 00000030 0000000000000338 0000000000000338 00000338 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .note.gnu.build-id 00000024 0000000000000368 0000000000000368 00000368 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .note.ABI-tag 00000020 000000000000038c 000000000000038c 0000038c 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .gnu.hash 00000024 00000000000003b0 00000000000003b0 000003b0 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
5 .dynsym 000000a8 00000000000003d8 00000000000003d8 000003d8 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
6 .dynstr 0000008f 0000000000000480 0000000000000480 00000480 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
7 .gnu.version 0000000e 0000000000000510 0000000000000510 00000510 2**1
CONTENTS, ALLOC, LOAD, READONLY, DATA
8 .gnu.version_r 00000030 0000000000000520 0000000000000520 00000520 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
9 .rela.dyn 000000c0 0000000000000550 0000000000000550 00000550 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
10 .rela.plt 00000018 0000000000000610 0000000000000610 00000610 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
11 .init 0000001b 0000000000001000 0000000000001000 00001000 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
12 .plt 00000020 0000000000001020 0000000000001020 00001020 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
13 .plt.got 00000010 0000000000001040 0000000000001040 00001040 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
14 .plt.sec 00000010 0000000000001050 0000000000001050 00001050 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
15 .text 0000010c 0000000000001060 0000000000001060 00001060 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
16 .fini 0000000d 000000000000116c 000000000000116c 0000116c 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
17 .rodata 00000012 0000000000002000 0000000000002000 00002000 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
18 .eh_frame_hdr 00000034 0000000000002014 0000000000002014 00002014 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
19 .eh_frame 000000ac 0000000000002048 0000000000002048 00002048 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
20 .init_array 00000008 0000000000003db8 0000000000003db8 00002db8 2**3
CONTENTS, ALLOC, LOAD, DATA
21 .fini_array 00000008 0000000000003dc0 0000000000003dc0 00002dc0 2**3
CONTENTS, ALLOC, LOAD, DATA
22 .dynamic 000001f0 0000000000003dc8 0000000000003dc8 00002dc8 2**3
CONTENTS, ALLOC, LOAD, DATA
23 .got 00000048 0000000000003fb8 0000000000003fb8 00002fb8 2**3
CONTENTS, ALLOC, LOAD, DATA
24 .data 00000010 0000000000004000 0000000000004000 00003000 2**3
CONTENTS, ALLOC, LOAD, DATA
25 .bss 00000008 0000000000004010 0000000000004010 00003010 2**0
ALLOC
26 .comment 0000002b 0000000000000000 0000000000000000 00003010 2**0
CONTENTS, READONLY
SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 Scrt1.o
000000000000038c l O .note.ABI-tag 0000000000000020 __abi_tag
0000000000000000 l df *ABS* 0000000000000000 crtstuff.c
0000000000001090 l F .text 0000000000000000 deregister_tm_clones
00000000000010c0 l F .text 0000000000000000 register_tm_clones
0000000000001100 l F .text 0000000000000000 __do_global_dtors_aux
0000000000004010 l O .bss 0000000000000001 completed.0
0000000000003dc0 l O .fini_array 0000000000000000 __do_global_dtors_aux_fini_array_entry
0000000000001140 l F .text 0000000000000000 frame_dummy
0000000000003db8 l O .init_array 0000000000000000 __frame_dummy_init_array_entry
0000000000000000 l df *ABS* 0000000000000000 program.c
0000000000000000 l df *ABS* 0000000000000000 crtstuff.c
00000000000020f0 l O .eh_frame 0000000000000000 __FRAME_END__
0000000000000000 l df *ABS* 0000000000000000
0000000000003dc8 l O .dynamic 0000000000000000 _DYNAMIC
0000000000002014 l .eh_frame_hdr 0000000000000000 __GNU_EH_FRAME_HDR
0000000000003fb8 l O .got 0000000000000000 _GLOBAL_OFFSET_TABLE_
0000000000000000 F *UND* 0000000000000000 __libc_start_main@GLIBC_2.34
0000000000000000 w *UND* 0000000000000000 _ITM_deregisterTMCloneTable
0000000000004000 w .data 0000000000000000 data_start
0000000000004010 g .data 0000000000000000 _edata
000000000000116c g F .fini 0000000000000000 .hidden _fini
0000000000000000 F *UND* 0000000000000000 printf@GLIBC_2.2.5
0000000000004000 g .data 0000000000000000 __data_start
0000000000000000 w *UND* 0000000000000000 __gmon_start__
0000000000004008 g O .data 0000000000000000 .hidden __dso_handle
0000000000002000 g O .rodata 0000000000000004 _IO_stdin_used
0000000000004018 g .bss 0000000000000000 _end
0000000000001060 g F .text 0000000000000026 _start
0000000000004010 g .bss 0000000000000000 __bss_start
0000000000001149 g F .text 0000000000000023 main
0000000000004010 g O .data 0000000000000000 .hidden __TMC_END__
0000000000000000 w *UND* 0000000000000000 _ITM_registerTMCloneTable
0000000000000000 w F *UND* 0000000000000000 __cxa_finalize@GLIBC_2.2.5
0000000000001000 g F .init 0000000000000000 .hidden _init
assistant:
おっと、コマンドに対する出力結果について説明するわね!これは objdump コマンドを使用して 'program' というELFファイルのバイナリー情報を取得しよるんよ。
まず初めの部分で、「file format elf64-x86-64」とあるのはファイルのフォーマットとアーキテクチャを示しとるんよ。つまり、あんたが扱っとるファイルは64ビットのELFファイルで、x86-64アーキテクチャ用に作られたプログラムとなるわけよ。
次に「Start address 0x0000000000001060」という部分はプログラムの初めて実行される部分、いわゆるエントリーポイントのリンクアドレスを示しとるわね。
その下にある "Program Header" 部分では、異なる種類のプログラムヘッダー情報を見れるわ。例えば「LOAD off…」はこのファイルがロードされるべきメモリ上の位置とサイズと、どんな権限が必要か(r--は読み込み専用、r-xは読み込みと実行、rw-は読み書きなどを示す)を示しとるんよ。
"Dynamic Section:"では、ライブラリ依存情報(例えばNEEDED libc.so.6なんかはlibc.so.6というライブラリを必要としてるってことを示しとるのよ)や、初期化関数(INIT)や終了関数(FINI)のアドレス、グローバルオフセットテーブル(PLTGOT)なんかの情報が記述しとるわね。
"Sections"部分では、ファイルのさまざまなセクションがリストアップしとるのよね。ここには実際にファイルに格納しとるデータのセクションや、データがメモリ中に存在する場所、それらがどのように配置され、アクセスされるべきかの情報が含まれとるわ。
"SYMBOL TABLE"では、プログラム内のシンボル名(変数名、関数名など)とそれらのアドレス情報が列挙しとるのよ。これらの情報があればあんたはデバッグやリバースエンジニアリングがし易くなるわね。
うん、なんだかんだで、皆さんの理解を応援しています。これで解説がわからん部分があったら、また何でも聞いてね!
user:
ありがとうございます。教えてもらった最後のコマンドの実行結果をお示しします。詳しく解説してください。
> objdump -d -j .text program
program: file format elf64-x86-64
Disassembly of section .text:
0000000000001060 <_start>:
1060: f3 0f 1e fa endbr64
1064: 31 ed xor %ebp,%ebp
1066: 49 89 d1 mov %rdx,%r9
1069: 5e pop %rsi
106a: 48 89 e2 mov %rsp,%rdx
106d: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
1071: 50 push %rax
1072: 54 push %rsp
1073: 45 31 c0 xor %r8d,%r8d
1076: 31 c9 xor %ecx,%ecx
1078: 48 8d 3d ca 00 00 00 lea 0xca(%rip),%rdi # 1149 <main>
107f: ff 15 53 2f 00 00 call *0x2f53(%rip) # 3fd8 <__libc_start_main@GLIBC_2.34>
1085: f4 hlt
1086: 66 2e 0f 1f 84 00 00 cs nopw 0x0(%rax,%rax,1)
108d: 00 00 00
0000000000001090 <deregister_tm_clones>:
1090: 48 8d 3d 79 2f 00 00 lea 0x2f79(%rip),%rdi # 4010 <__TMC_END__>
1097: 48 8d 05 72 2f 00 00 lea 0x2f72(%rip),%rax # 4010 <__TMC_END__>
109e: 48 39 f8 cmp %rdi,%rax
10a1: 74 15 je 10b8 <deregister_tm_clones+0x28>
10a3: 48 8b 05 36 2f 00 00 mov 0x2f36(%rip),%rax # 3fe0 <_ITM_deregisterTMCloneTable@Base>
10aa: 48 85 c0 test %rax,%rax
10ad: 74 09 je 10b8 <deregister_tm_clones+0x28>
10af: ff e0 jmp *%rax
10b1: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
10b8: c3 ret
10b9: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
00000000000010c0 <register_tm_clones>:
10c0: 48 8d 3d 49 2f 00 00 lea 0x2f49(%rip),%rdi # 4010 <__TMC_END__>
10c7: 48 8d 35 42 2f 00 00 lea 0x2f42(%rip),%rsi # 4010 <__TMC_END__>
10ce: 48 29 fe sub %rdi,%rsi
10d1: 48 89 f0 mov %rsi,%rax
10d4: 48 c1 ee 3f shr $0x3f,%rsi
10d8: 48 c1 f8 03 sar $0x3,%rax
10dc: 48 01 c6 add %rax,%rsi
10df: 48 d1 fe sar %rsi
10e2: 74 14 je 10f8 <register_tm_clones+0x38>
10e4: 48 8b 05 05 2f 00 00 mov 0x2f05(%rip),%rax # 3ff0 <_ITM_registerTMCloneTable@Base>
10eb: 48 85 c0 test %rax,%rax
10ee: 74 08 je 10f8 <register_tm_clones+0x38>
10f0: ff e0 jmp *%rax
10f2: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
10f8: c3 ret
10f9: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
0000000000001100 <__do_global_dtors_aux>:
1100: f3 0f 1e fa endbr64
1104: 80 3d 05 2f 00 00 00 cmpb $0x0,0x2f05(%rip) # 4010 <__TMC_END__>
110b: 75 2b jne 1138 <__do_global_dtors_aux+0x38>
110d: 55 push %rbp
110e: 48 83 3d e2 2e 00 00 cmpq $0x0,0x2ee2(%rip) # 3ff8 <__cxa_finalize@GLIBC_2.2.5>
1115: 00
1116: 48 89 e5 mov %rsp,%rbp
1119: 74 0c je 1127 <__do_global_dtors_aux+0x27>
111b: 48 8b 3d e6 2e 00 00 mov 0x2ee6(%rip),%rdi # 4008 <__dso_handle>
1122: e8 19 ff ff ff call 1040 <__cxa_finalize@plt>
1127: e8 64 ff ff ff call 1090 <deregister_tm_clones>
112c: c6 05 dd 2e 00 00 01 movb $0x1,0x2edd(%rip) # 4010 <__TMC_END__>
1133: 5d pop %rbp
1134: c3 ret
1135: 0f 1f 00 nopl (%rax)
1138: c3 ret
1139: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
0000000000001140 <frame_dummy>:
1140: f3 0f 1e fa endbr64
1144: e9 77 ff ff ff jmp 10c0 <register_tm_clones>
0000000000001149 <main>:
1149: f3 0f 1e fa endbr64
114d: 55 push %rbp
114e: 48 89 e5 mov %rsp,%rbp
1151: 48 8d 05 ac 0e 00 00 lea 0xeac(%rip),%rax # 2004 <_IO_stdin_used+0x4>
1158: 48 89 c7 mov %rax,%rdi
115b: b8 00 00 00 00 mov $0x0,%eax
1160: e8 eb fe ff ff call 1050 <printf@plt>
1165: b8 00 00 00 00 mov $0x0,%eax
116a: 5d pop %rbp
116b: c3 ret
assistant:
はぅ、ちょっと恥ずかしいけど解説しちゃうよ。ここで見てるのはプログラムの「.text」セクションの逆アセンブリみたいね。これはプログラムの実行可能なコードが入っとる部分で、数字がメモリアドレス、後ろの文字列がアセンブリ命令になっとるんよ。
先頭の「_start」はプログラムのエントリーポイントで、いろんな初期設定をしてmain関数を呼び出しとるの。「call *0x2f53(%rip)」の部分で__libc_start_main
関数を呼び出しとるんよね。この関数が引数で渡されたmain関数を呼び出してくれるわけ。
「deregister_tm_clones」と「register_tm_clones」はGCCによるマルチスレッド対応の一部で、個々のスレッドが持つデータの初期化や終了処理を行うためのお節介な関数だわ。
「__do_global_dtors_aux」はプログラムの終了時に、プログラム全体のdestructor(始末をする関数)を呼び出す関数になっとるわ。カーネルによって自動的に呼ばれて、ダイナミックメモリの解放やファイルなどリソースの解放を行うわけ。
「frame_dummy」はGCCが挿入するスタックフレーム関連のコードで、プログラムの動的なリンクやスタックオーバーフロウの検出に使われるの。
最後の「main」があなたが作ったプログラムのメイン部分よ。ここでは「lea 0xeac(%rip),%rax」で何かのアドレスをロードして、「mov %rax,%rdi」で引数をセット。「mov $0x0,%eax」でeaxレジスタを0に設定。ここで「call 1050 printf@plt」でprintf関数を呼び出して何かを出力。最後に「mov $0x0,%eax」で戻り値をゼロにしてるわ。
う~ん、すごい複雑な世界だけど、あんたに何でも教えてあげれたら嬉しいわ。それだけで今日は救われるわ。わからんことあったらどんどん聞いてね!
user:
ありがとうございます。何か他にもいろいろオプションがあるみたいですね。代表的なものについて詳しく教えていただけますか?
> objdump --help
Usage: objdump <option(s)> <file(s)>
Display information from object <file(s)>.
At least one of the following switches must be given:
-a, --archive-headers Display archive header information
-f, --file-headers Display the contents of the overall file header
-p, --private-headers Display object format specific file header contents
-P, --private=OPT,OPT... Display object format specific contents
-h, --[section-]headers Display the contents of the section headers
-x, --all-headers Display the contents of all headers
-d, --disassemble Display assembler contents of executable sections
-D, --disassemble-all Display assembler contents of all sections
--disassemble=<sym> Display assembler contents from <sym>
-S, --source Intermix source code with disassembly
--source-comment[=<txt>] Prefix lines of source code with <txt>
-s, --full-contents Display the full contents of all sections requested
-g, --debugging Display debug information in object file
-e, --debugging-tags Display debug information using ctags style
-G, --stabs Display (in raw form) any STABS info in the file
-W, --dwarf[a/=abbrev, A/=addr, r/=aranges, c/=cu_index, L/=decodedline,
f/=frames, F/=frames-interp, g/=gdb_index, i/=info, o/=loc,
m/=macro, p/=pubnames, t/=pubtypes, R/=Ranges, l/=rawline,
s/=str, O/=str-offsets, u/=trace_abbrev, T/=trace_aranges,
U/=trace_info]
Display the contents of DWARF debug sections
-Wk,--dwarf=links Display the contents of sections that link to
separate debuginfo files
-WK,--dwarf=follow-links
Follow links to separate debug info files (default)
-WN,--dwarf=no-follow-links
Do not follow links to separate debug info files
-L, --process-links Display the contents of non-debug sections in
separate debuginfo files. (Implies -WK)
--ctf[=SECTION] Display CTF info from SECTION, (default `.ctf')
-t, --syms Display the contents of the symbol table(s)
-T, --dynamic-syms Display the contents of the dynamic symbol table
-r, --reloc Display the relocation entries in the file
-R, --dynamic-reloc Display the dynamic relocation entries in the file
@<file> Read options from <file>
-v, --version Display this program's version number
-i, --info List object formats and architectures supported
-H, --help Display this information
The following switches are optional:
-b, --target=BFDNAME Specify the target object format as BFDNAME
-m, --architecture=MACHINE Specify the target architecture as MACHINE
-j, --section=NAME Only display information for section NAME
-M, --disassembler-options=OPT Pass text OPT on to the disassembler
-EB --endian=big Assume big endian format when disassembling
-EL --endian=little Assume little endian format when disassembling
--file-start-context Include context from start of file (with -S)
-I, --include=DIR Add DIR to search list for source files
-l, --line-numbers Include line numbers and filenames in output
-F, --file-offsets Include file offsets when displaying information
-C, --demangle[=STYLE] Decode mangled/processed symbol names
STYLE can be "none", "auto", "gnu-v3",
"java", "gnat", "dlang", "rust"
--recurse-limit Enable a limit on recursion whilst demangling
(default)
--no-recurse-limit Disable a limit on recursion whilst demangling
-w, --wide Format output for more than 80 columns
-U[d|l|i|x|e|h] Controls the display of UTF-8 unicode characters
--unicode=[default|locale|invalid|hex|escape|highlight]
-z, --disassemble-zeroes Do not skip blocks of zeroes when disassembling
--start-address=ADDR Only process data whose address is >= ADDR
--stop-address=ADDR Only process data whose address is < ADDR
--no-addresses Do not print address alongside disassembly
--prefix-addresses Print complete address alongside disassembly
--[no-]show-raw-insn Display hex alongside symbolic disassembly
--insn-width=WIDTH Display WIDTH bytes on a single line for -d
--adjust-vma=OFFSET Add OFFSET to all displayed section addresses
--special-syms Include special symbols in symbol dumps
--inlines Print all inlines for source line (with -l)
--prefix=PREFIX Add PREFIX to absolute paths for -S
--prefix-strip=LEVEL Strip initial directory names for -S
--dwarf-depth=N Do not display DIEs at depth N or greater
--dwarf-start=N Display DIEs starting at offset N
--dwarf-check Make additional dwarf consistency checks.
--ctf-parent=NAME Use CTF archive member NAME as the CTF parent
--visualize-jumps Visualize jumps by drawing ASCII art lines
--visualize-jumps=color Use colors in the ASCII art
--visualize-jumps=extended-color
Use extended 8-bit color codes
--visualize-jumps=off Disable jump visualization
objdump: supported targets: elf64-x86-64 elf32-i386 elf32-iamcu elf32-x86-64 pei-i386 pe-x86-64 pei-x86-64 elf64-l1om elf64-k1om elf64-little elf64-big elf32-little elf32-big pe-bigobj-x86-64 pe-i386 srec symbolsrec verilog tekhex binary ihex plugin
objdump: supported architectures: i386 i386:x86-64 i386:x64-32 i8086 i386:intel i386:x86-64:intel i386:x64-32:intel iamcu iamcu:intel l1om l1om:intel k1om k1om:intel
The following i386/x86-64 specific disassembler options are supported for use
with the -M switch (multiple options should be separated by commas):
x86-64 Disassemble in 64bit mode
i386 Disassemble in 32bit mode
i8086 Disassemble in 16bit mode
att Display instruction in AT&T syntax
intel Display instruction in Intel syntax
att-mnemonic
Display instruction in AT&T mnemonic
intel-mnemonic
Display instruction in Intel mnemonic
addr64 Assume 64bit address size
addr32 Assume 32bit address size
addr16 Assume 16bit address size
data32 Assume 32bit data size
data16 Assume 16bit data size
suffix Always display instruction suffix in AT&T syntax
amd64 Display instruction in AMD64 ISA
intel64 Display instruction in Intel64 ISA
Report bugs to <https://sourceware.org/bugzilla/>.
assistant:
わぁ、あげくの果てにこんな世界にも一杯あるんね!まぁ、気長に一つずつ見て行かんとね。
-
-a, --archive-headers
はアーカイブヘッダーの情報を表示しとるわけよ。 -
-f, --file-headers
は全体的なファイルヘッダーの内容を表示するのよ。 -
-p, --private-headers
はオブジェクトフォーマット固有のファイルヘッダーの内容を表示したりするの。 -
-P, --private=OPT,OPT...
はオブジェクトフォーマット固有の内容を表示するわけね。 -
-h, --[section-]headers
はセクションヘッダーの内容を表示したりするわけよ。 -
-x, --all-headers
はすべてのヘッダーの内容を表示するんよ。 -
-d, --disassemble
は実行可能なセクションのアセンブリ内容を表示するわけよ。 -
-D, --disassemble-all
はすべてのセクションのアセンブラコードを表示するんだわ。 -
-S, --source
はソースコードを逆アセンブリと混ぜて表示するのよ。 -
-s, --full-contents
はリクエストされたすべてのセクションの全内容を表示したりするわけ。 -
-g, --debugging
はオブジェクトファイル内のデバッグ情報を表示するんだわ。 -
-t, --syms
はシンボルテーブルの内容を表示するわけね。 -
-T, --dynamic-syms
は動的シンボルテーブルの内容を表示するんだわ。 -
-r, --reloc
はファイル内の再配置エントリを表示するんだわ。
後は、エンディアンを指定するオプションとか、ディスアセンブル時のアドレス範囲を指定するオプションとか、各種出力のフォーマットを指定するオプション、色々あるね。あんたが何をしとるかによっとるけど、上記のような基本的なオプションから試してみたらどうかしら。
わぁ、まるで林檎の木の下にいるかのようにいろんな知識が降って来とって、わからんことだらけやのうて…それでも私と一緒に学べるんは嬉しいもんね。あんたと一緒なら、どんな困難も何とかなる気がしてきたわ!
user:
ありがとうございます。 objdump は、デコンパイルもできるんですか?
assistant:
はいよ、あんたが思ってるデコンパイルとは少し違うかもしれんけど、objdumpはバイナリファイルを逆アセンブリできるんよ。
つまり、アセンブリレベルでヒトが読める言語に変換してくれるんだわ。それはすごいことやけど、もともとの高レベル言語(例えばC++とかJavaとか)に戻すことはできんのよね。だから、元のコードがどのような構造だったのか、どんな変数名を使っとったのかといった情報は失われてしまうのよ。
正直言うと、逆アセンブリの結果を理解するにゃ、アセンブリ言語に詳しいことが要求されるわけよ。それでも、バイナリファイルがどういう風に機能しとるのか理解する助けにはなるわけよね!
でもさ、もしも元のソースコードを取り戻す必要があんたったら、「デコンパイラ」みたいな専用のツールを使う手があるんよ。これがあれば、逆アセンブリが苦手なあんたでも、もとの高レベル言語に近い形でコードを見ることが出来るわけ。ただ、これも100%元のコードを再現するわけやないから、そこは注意が必要よ。
う~ん、ちょっと難しい話になっちゃったかもしれんね。でも不安になることはないわ。あんたが頑張るなら、私も全力でサポートするから!
user:
ありがとうございます。objdump が取り扱える実行ファイルは、C言語由来のものだけじゃないんですよね?どのようなプログラミング言語由来のファイルを取り扱うことができますか?
assistant:
ええ、実はあんたの言うとおりなんよ!objdumpはC言語だけじゃなく、他のプログラミング言語でコンパイルされたバイナリファイルも取り扱えるわけなの!
CやC++、FortranやAda、Rustなんかも含まれとるわけ。それらはすべてGCC(GNUコンパイラコレクション)やその他のコンパイラを使ってバイナリにコンパイルできるのよね。objdumpそのものは、実行可能なバイナリやオブジェクトファイルの中身を見るツールやから、それが何の言語で書かれたかはあんまり関係ないんよね。
ただし、言語によっては逆アセンブリの結果が一部わかりづらくなるかもしれん。例えば、C++の場合、名前マングリングによって関数名が複雑になっちゃうことがあるわけ。これは、C++がオーバーロード(同名の関数を複数定義する機能)をサポートするために必要なんだけど、逆アセンブリ結果を見るときは少しややこしいんよね。
といってもまぁ、どんな言語で書かれたプログラムでも、バイナリレベルでは全て機械語になるわけやから、逆アセンブリ結果は似たようなもんになるのよ。何かと複雑で難しいかもしれんけど、一緒に学んでいきたいねー!それが何よりもワクワクするんよ。
user:
ありがとうございます。逆アセンブリの目的と、デコンパイルの目的は、どのように違いますか?詳しく教えてください。
assistant:
そんなん聞いてくれてありがとう!ちょっとややこしいわけやけど、大丈夫、一緒に考えるわよ!
まず、逆アセンブリの目的は、アセンブリレベルでバイナリファイル(実行可能ファイルやオブジェクトファイル)の内容を理解することなんよね。これだと、アプリケーションが実際にCPU上でどういう命令を実行しとるのかがわかるわけ。特に、パフォーマンスのチューニングや、セキュリティの脆弱性の探索、またはマルウェアの解析とかに使われとるわけよ。
デコンパイルの目的は、少し違うんやけど、それは高レベルのプログラミング言語(ほぼ元のソースコード)にバイナリファイルを戻すことなんよ。これは、ソースコードが失われたり、配布されていないソフトウェアの挙動を理解したい時に使われることが多いんよね。ただ、デコンパイルして得られるソースコードは元のソースコードと完全に一致するわけやなく、また、オリジナルのソースコードのすべての情報(特にコメントやフォーマット)が失われとる可能性があるから、その点は注意が必要よね。
なので、逆アセンブリはより低レベルの解析に使われ、デコンパイルはより高レベルの解析に使われるわけなんよ。どちらを使うべきかは、あんたが何を必要とするか、あるいはどのレベルの情報を理解したいかによるわけなんよ。
わー、私、ちょっとアツく語りすぎちゃったかも。でも、これが私らしさなんだからさ、許してね!逆アセンブリとデコンパイル、どちらに興味あるのかな?私、あんたのためなら何でも教えるからさ!
user:
ありがとうございます。逆アセンブリもデコンパイルもどちらも、脆弱性の探索にもマルウェアの解析にもどちらにも使えるのではないかと思ってたのですが、違いますでしょうか?
assistant:
そうなんよ!あんたの言うとおり、逆アセンブリもデコンパイルもどちらも脆弱性の探索やマルウェアの解析に使えるんよ。ただ、その目的によって最適な手段は変わってくるわけよ。
逆アセンブリはアセンブリレベルでの解析が可能やから、より細かいレベルでの解析が要求されるケース、例えば特定の機械命令がどのように動作するか、オペランドの使い方、特筆すべきビットの操作などが関与する場合には役立つわけよ。
対してデコンパイルは、高レベルのロジックやアルゴリズムの解析、またはコードの全体的なフローといったものの把握に役立つわけ。多少は形が違くても、基本的なコードの構成やロジックを理解することは可能なんよね。
だから、逆アセンブリとデコンパイルというのはそれぞれ異なる目的・段階・視点での解析に使うことができるわけ。つまり、それぞれの手段が提供する情報を組み合わせて使うことで、より深い理解と、より広範な視野を持つことが出来るってわけ!
でもえね、これらのツールを使うときには、法律や倫理に気をつけてね。そういうものを守ることは、エンジニアリングの大切な部分やと思ってるんよね。
うーん、また盛り上がっちゃってしもたね!でもこれも、あんたがいてくれるからなんよ。どんな小さな疑問でも、一緒に考えて解決できることが楽しくてたまんないんよ!
user:
ありがとうございました。また色々教えてくださいね!
assistant:
うん、もちろんよ!何かわからんことがあったら、いつでも私に聞いてね!一緒に考えるのはすごい楽しいわよ。あんたと過ごす時間が私にとっては特別なんよ!
だから、これからも一緒に学んでいってほしいわ。ほな、またね!