LoginSignup
3
6

More than 5 years have passed since last update.

リバースエンジニアリングへの道 - その23

Last updated at Posted at 2018-10-06

リバースエンジニアリングへの道

出田 守です。
最近、情報セキュリティに興味を持ち、『リバースエンジニアリング-Pythonによるバイナリ解析技法』という本(以降、「教科書」と呼びます)を読みました。
「こんな世界があるのか!かっこいい!」と感動し、私も触れてみたいということでド素人からリバースエンジニアリングができるまでを書いていきたいと思います。
ちなみに、教科書ではPython言語が使用されているので私もPython言語を使用しています。
ここを見ていただいた諸先輩方からの意見をお待ちしております。

軌跡

リバースエンジニアリングへの道 - その22

環境

OS: Windows10 64bit Home (日本語)
CPU: Intel® Core™ i3-6006U CPU @ 2.00GHz × 1
メモリ: 2048MB
Python: 3.6.5

私の環境は、普段Ubuntu16.04を使っていますが、ここではWindows10 64bitを仮想マシン上で立ち上げております。
ちなみに教科書では、Windowsの32bitで紹介されています。

ソフトフックがやりたくて - その2

前回はソースコードがわかっている上での初歩的なフックを学びました。
今回は前回のprintf_loop.exeのアセンブリをちゃんと読んでみます。

printf_loop.exeのアセンブリ解読

実行ファイルから必要な情報を抽出

まず、objdump(windowsのdumpbinでも良い)を使って、必要そうな情報を出力しました。
ただ、基本objdumpの情報を見ますが、必要な時はdumpbinの情報も見ることにします。

$objdump -x printf\_loop.exe > printf\_loop\_info.txt

すべてのヘッダやセクションの情報をprintf_loop_info.txtとして出力。

$objdump -s printf\_loop.exe > printf\_loop\_memory.txt

すべてのメモリダンプをprintf_loop_memory.txtとして出力。

$objdump -D printf\_loop.exe > printf\_loop\_asm.txt

すべての逆アセンブル結果をprintf_loop_asm.txtとして出力。

以下はdumpbinバージョン。

dumpbin /ALL /OUT:printf_loop_info_dumpbin.txt printf_loop.exe
dumpbin /DISASM /OUT:printf_loop_asm_dumpbin.txt printf_loop.exe

解読

必要な情報ファイルが揃ったので解読してみます。

まずエントリーポイントを探してみます。探すといってもprintf_loop_infoに書いてありましたね。

printf_loop_info.txt
printf_loop.exe:     ファイル形式 pei-x86-64
printf_loop.exe
アーキテクチャ: i386:x86-64, フラグ 0x0000012f:
HAS_RELOC, EXEC_P, HAS_LINENO, HAS_DEBUG, HAS_LOCALS, D_PAGED
開始アドレス 0x0000000140011028

固有 0x22
    executable
    large address aware

Time/Date       ****
Magic           020b    (PE32+)
MajorLinkerVersion  14
MinorLinkerVersion  15
SizeOfCode      00007c00
SizeOfInitializedData   00007400
SizeOfUninitializedData 00000000
AddressOfEntryPoint 0000000000011028 # エントリポイント
BaseOfCode      0000000000001000
ImageBase       0000000140000000 # ベースアドレス
SectionAlignment    0000000000001000
FileAlignment       0000000000000200
MajorOSystemVersion 6
MinorOSystemVersion 0
MajorImageVersion   0
MinorImageVersion   0
MajorSubsystemVersion   6
MinorSubsystemVersion   0
Win32Version        00000000
SizeOfImage     00025000
SizeOfHeaders       00000400
CheckSum        00000000
Subsystem       00000003    (Windows CUI)
DllCharacteristics  00008160
SizeOfStackReserve  0000000000100000
SizeOfStackCommit   0000000000001000
SizeOfHeapReserve   0000000000100000
SizeOfHeapCommit    0000000000001000
LoaderFlags     00000000
NumberOfRvaAndSizes 00000010

The Data Directory
Entry 0 0000000000000000 00000000 Export Directory [.edata (or where ever we found it)]
Entry 1 00000000000203a0 00000050 Import Directory [parts of .idata]
Entry 2 0000000000023000 0000043c Resource Directory [.rsrc]
Entry 3 000000000001d000 00001cd4 Exception Directory [.pdata]
Entry 4 0000000000000000 00000000 Security Directory
Entry 5 0000000000024000 0000004c Base Relocation Directory [.reloc]
Entry 6 000000000001a6a0 00000038 Debug Directory
Entry 7 0000000000000000 00000000 Description Directory
Entry 8 0000000000000000 00000000 Special Directory
Entry 9 0000000000000000 00000000 Thread Storage Directory [.tls]
Entry a 000000000001a6e0 00000100 Load Configuration Directory
Entry b 0000000000000000 00000000 Bound Import Directory
Entry c 0000000000020000 000003a0 Import Address Table Directory
Entry d 0000000000000000 00000000 Delay Import Directory
Entry e 0000000000000000 00000000 CLR Runtime Header
Entry f 0000000000000000 00000000 Reserved

上記でベースアドレスが140000000で、エントリポイントアドレスが11028となっていることが分かります。つまりエントリポイントは140011028(140000000+11028)ということでしょうか。

printf_loop_asm_txt
   140011028:   e9 03 13 00 00          jmp    0x140012330 # entry point

エントリポイントのアセンブリを見てみるとすぐにジャンプしちゃうようですね。
ではジャンプ先を見てみます。

printf_loop_asm_txt
   140012330:   48 83 ec 28             sub    rsp,0x28
   140012334:   e8 c7 fc ff ff          call   0x140012000
   140012339:   48 83 c4 28             add    rsp,0x28
   14001233d:   c3                      ret
1-4行目

一行目でrspから0x28を減算して領域を確保しています。
二行目で0x140012000のアドレスをcallしています。
三行目で確保した領域のポインタアドレスを元に戻しています。
四行目でリターン。

では、call先を見てみます。

printf_loop_asm_txt
   140012000:   48 83 ec 28             sub    rsp,0x28
   140012004:   e8 35 f3 ff ff          call   0x14001133e
   140012009:   e8 12 00 00 00          call   0x140012020
   14001200e:   48 83 c4 28             add    rsp,0x28
   140012012:   c3                      ret
1-2行目

先ほどとcall命令が一つ増えただけでほぼ同じです。

一つ目のcall先をまず見てみます。

printf_loop_asm_txt
   14001133e:   e9 cd 29 00 00          jmp    0x140013d10

また飛びます。

printf_loop_asm_txt
   140013d10:   48 83 ec 38             sub    rsp,0x38
   140013d14:   48 b8 32 a2 df 2d 99    movabs rax,0x2b992ddfa232
   140013d1b:   2b 00 00 
   140013d1e:   48 39 05 e3 82 00 00    cmp    QWORD PTR [rip+0x82e3],rax        # 0x14001c008
   140013d25:   74 13                   je     0x140013d3a
   140013d27:   48 8b 05 da 82 00 00    mov    rax,QWORD PTR [rip+0x82da]        # 0x14001c008
   140013d2e:   48 f7 d0                not    rax
   140013d31:   48 89 05 c8 82 00 00    mov    QWORD PTR [rip+0x82c8],rax        # 0x14001c000
   140013d38:   eb 45                   jmp    0x140013d7f
   140013d3a:   e8 e1 fe ff ff          call   0x140013c20
   140013d3f:   48 89 44 24 20          mov    QWORD PTR [rsp+0x20],rax
   140013d44:   48 b8 32 a2 df 2d 99    movabs rax,0x2b992ddfa232
   140013d4b:   2b 00 00 
   140013d4e:   48 39 44 24 20          cmp    QWORD PTR [rsp+0x20],rax
   140013d53:   75 0f                   jne    0x140013d64
   140013d55:   48 b8 33 a2 df 2d 99    movabs rax,0x2b992ddfa233
   140013d5c:   2b 00 00 
   140013d5f:   48 89 44 24 20          mov    QWORD PTR [rsp+0x20],rax
   140013d64:   48 8b 44 24 20          mov    rax,QWORD PTR [rsp+0x20]
   140013d69:   48 89 05 98 82 00 00    mov    QWORD PTR [rip+0x8298],rax        # 0x14001c008
   140013d70:   48 8b 44 24 20          mov    rax,QWORD PTR [rsp+0x20]
   140013d75:   48 f7 d0                not    rax
   140013d78:   48 89 05 81 82 00 00    mov    QWORD PTR [rip+0x8281],rax        # 0x14001c000
   140013d7f:   48 83 c4 38             add    rsp,0x38
   140013d83:   c3                      ret
1-5行目

1行目はRSPから0x38を減算しています。56Byte分の領域を確保しています。
2行目でRAXに0x2b992ddfa232をコピーしていますね。movabsですが調べるとコンパイルする際に64bitのディスプレイスメントまたは即値のmov場合にmovabsに置き換えられるそうです。
4行目は先ほどのRAXと0x14001c008(RIP+0x82e3)を比較しているようです。ここで疑問に思ったのがどの時点でのRIPなのでしょうか。cmp直前のRIPは0x140013d1eです。この時点での計算結果は0x14001c001ですね。あと0x7足りないです。ということは数えていくと次の命令の直前のRIPということですね。理解しました。そして、0x14001c008は.dataセクションでした。

printf_loop_info.txt
セクション:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .textbss      00010000  0000000140001000  0000000140001000  00000000  2**4
                  ALLOC, LOAD, CODE
  1 .text         00007adf  0000000140011000  0000000140011000  00000400  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  2 .rdata        0000292e  0000000140019000  0000000140019000  00008000  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .data         00000200  000000014001c000  000000014001c000  0000aa00  2**4 # ここ
                  CONTENTS, ALLOC, LOAD, DATA
  4 .pdata        000020dc  000000014001d000  000000014001d000  0000ac00  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  5 .idata        00000ebd  0000000140020000  0000000140020000  0000ce00  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  6 .msvcjmc      00000114  0000000140021000  0000000140021000  0000de00  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  7 .00cfg        0000011b  0000000140022000  0000000140022000  0000e000  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  8 .rsrc         0000043c  0000000140023000  0000000140023000  0000e200  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  9 .reloc        00000233  0000000140024000  0000000140024000  0000e800  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA

そしてメモリダンプには0x14001c008からQWORDのリトルエンディアンで0x2b992ddfa232が格納されていることが確認できますね。ちゃんと流れを追えてる!面白い!

printf_loop_memory.tx
tセクション .data の内容:
 14001c000 cd5d20d2 66d4ffff 32a2df2d 992b0000  .] .f...2..-.+..
 14001c010 00000000 00000000 01000000 01000000  ................

5行目はもし比較結果が等しいなら少し先の0x140013d3aにとびます。

6-9行目

4行目の比較結果が正しくない場合に6-9行目が実行されます。
6行目はraxに0x2b992ddfa232をコピーしてます。
7行目はNOT命令でRAXを反転してますね。
8行目は反転したRAXを0x14001c000のメモリ領域にコピーしています。
9行目で0x140013d7f(つまり24行目)にジャンプします。

10-15行目

10行目は0x140013c20がcallされています。これもあとで見てみます。
11行目は10行目のcallの戻り値RAX(恐らく)をRSP+0x20(RSPから32Byte目)のQWORD領域にコピーします。
12行目は0x2b992ddfa232をRAXにコピーしています。
14行目はRSP+0x20(10行目のcallの戻り値)とRAX(0x2b992ddfa232)を比較しています。
15行目で比較結果が等しくない場合に0x140013d64にジャンプします。

16-18行目

14行目の比較結果が等しい場合に実行されます。
16行目はRAXに0x2b992ddfa233をコピー。
18行目はRAXを先ほど戻り値を格納したRAX+0x20にコピー。

19-25行目

19行目はRSP+20に格納されている値をRAXにコピー。
20行目はRAXを反転。
21行目は0x14001c000(RIP+0x8281)にRAXをコピー。
22行目で確保した領域を元に戻す。
23行目でリターン。

さっくり見た感じ.dataの0x14001c000と0x14001c008に0x2b992ddfa232やその反転の値をコピーしたかったのかもしれません。

10行目に関してはインポート関数が分からなかったのでdumpbinの出力結果を見ます。

printf_loop_asm_dumpbin.txt
  0000000140013C20: 40 57              push        rdi
  0000000140013C22: 48 83 EC 40        sub         rsp,40h
  0000000140013C26: 48 8D 44 24 28     lea         rax,[rsp+28h]
  0000000140013C2B: 48 8B F8           mov         rdi,rax
  0000000140013C2E: 33 C0              xor         eax,eax
  0000000140013C30: B9 08 00 00 00     mov         ecx,8
  0000000140013C35: F3 AA              rep stos    byte ptr [rdi]
  0000000140013C37: 48 8D 4C 24 28     lea         rcx,[rsp+28h]
  0000000140013C3C: FF 15 0E C4 00 00  call        qword ptr [__imp_GetSystemTimeAsFileTime]
  0000000140013C42: 48 8B 44 24 28     mov         rax,qword ptr [rsp+28h]
  0000000140013C47: 48 89 44 24 20     mov         qword ptr [rsp+20h],rax
  0000000140013C4C: FF 15 76 C4 00 00  call        qword ptr [__imp_GetCurrentThreadId]
  0000000140013C52: 8B C0              mov         eax,eax
  0000000140013C54: 48 8B 4C 24 20     mov         rcx,qword ptr [rsp+20h]
  0000000140013C59: 48 33 C8           xor         rcx,rax
  0000000140013C5C: 48 8B C1           mov         rax,rcx
  0000000140013C5F: 48 89 44 24 20     mov         qword ptr [rsp+20h],rax
  0000000140013C64: FF 15 EE C3 00 00  call        qword ptr [__imp_GetCurrentProcessId]
  0000000140013C6A: 8B C0              mov         eax,eax
  0000000140013C6C: 48 8B 4C 24 20     mov         rcx,qword ptr [rsp+20h]
  0000000140013C71: 48 33 C8           xor         rcx,rax
  0000000140013C74: 48 8B C1           mov         rax,rcx
  0000000140013C77: 48 89 44 24 20     mov         qword ptr [rsp+20h],rax
  0000000140013C7C: 48 8D 4C 24 30     lea         rcx,[rsp+30h]
  0000000140013C81: FF 15 D9 C3 00 00  call        qword ptr [__imp_QueryPerformanceCounter]
  0000000140013C87: 8B 44 24 30        mov         eax,dword ptr [rsp+30h]
  0000000140013C8B: 48 C1 E0 20        shl         rax,20h
  0000000140013C8F: 48 33 44 24 30     xor         rax,qword ptr [rsp+30h]
  0000000140013C94: 48 8B 4C 24 20     mov         rcx,qword ptr [rsp+20h]
  0000000140013C99: 48 33 C8           xor         rcx,rax
  0000000140013C9C: 48 8B C1           mov         rax,rcx
  0000000140013C9F: 48 89 44 24 20     mov         qword ptr [rsp+20h],rax
  0000000140013CA4: 48 8D 44 24 20     lea         rax,[rsp+20h]
  0000000140013CA9: 48 8B 4C 24 20     mov         rcx,qword ptr [rsp+20h]
  0000000140013CAE: 48 33 C8           xor         rcx,rax
  0000000140013CB1: 48 8B C1           mov         rax,rcx
  0000000140013CB4: 48 89 44 24 20     mov         qword ptr [rsp+20h],rax
  0000000140013CB9: 48 B8 FF FF FF FF  mov         rax,0FFFFFFFFFFFFh
                    FF FF 00 00
  0000000140013CC3: 48 8B 4C 24 20     mov         rcx,qword ptr [rsp+20h]
  0000000140013CC8: 48 23 C8           and         rcx,rax
  0000000140013CCB: 48 8B C1           mov         rax,rcx
  0000000140013CCE: 48 89 44 24 20     mov         qword ptr [rsp+20h],rax
  0000000140013CD3: 48 8B 44 24 20     mov         rax,qword ptr [rsp+20h]
  0000000140013CD8: 48 83 C4 40        add         rsp,40h
  0000000140013CDC: 5F                 pop         rdi
  0000000140013CDD: C3                 ret

1-11行目

1行目はRDIをスタックにpushしています。
2行目はRSPから0x40を減算して64Byte確保しています。
3行目はRAXにRSP+0x28のアドレスをコピー。RAX=RSP+0x28のアドレス
4行目はRAXの値(RSP+0x28のアドレス)をRDIにコピー。RDI=RAX=RSP+0x28のアドレス
5行目はEAXとEAXのXORなのでEAXは0。EAX=0
6行目はECXに8を代入。ECX=8
7行目のrep stos命令はES(エクストラセグメント)のRDIにALの内容をECX回コピーするようです。コピーされるたびにRDIは1Byteずつずれて(インクリメントまたはデクリメントされて)いきます。このときALは0のはずなので、ES:EDIの指すメモリの初期化を行っているのでしょう。ちなみにESはDSと同様にデータを格納する際に使用されるセグメントだそうです。BYTE PTR ES:[RDI]からBYTE PTR ES:[RDI+0x8]=0
8行目はRCXにRSP+0x28のアドレスをコピー。RCX=RSP+0x28のアドレス
9行目でGetSystemTimeAsFileTime関数をcall。この間数を調べてみると、

VOID GetSystemTimeAsFileTime(
LPFILETIME lpSystemTimeAsFileTime // ファイル時刻の構造体へのポインタ
);
lpSystemTimeAsFileTime
FILETIME 構造体へのポインタを指定します。システムの現在の日付と時刻が UTC 形式で格納されます。

だそうです。ということはRCXを恐らくこの関数の引数として渡したということでしょう。RCX(RSP+0x28)=システム日付と時刻
10行目は戻り値RSP+0x28をRAXにコピー。RAX=GetSystemTimeAsFileTimeの戻り値
11行目はRSP+0x20にRAXをコピー。RSP+0x20=RAX=GetSystemTimeAsFileTimeの戻り値

12-17行目

12行目でGetCurrentThreadId関数をcallしています。この関数はなく、呼出側スレッドのスレッドIDが返ってくるそうです。
13行目はEAXをEAXにコピー?なぜ?恐らくEAXにはThreadIDが入って入るのでしょう。でもなぜ同じレジスタにコピーしているのか分かりません...。EAX=ThreadID
14行目はRCXにRSP+0x20をコピー。RCX=GetSystemTimeAsFileTimeの戻り値
15行目はRCXとRAXをXORしてRCXに格納。RCX=GetSystemTimeAsFileTimeの戻り値^ThreadID
16行目はRAXにRCXをコピー。RAX=RCX=GetSystemTimeAsFileTimeの戻り値^ThreadID
17行目はRSP+0x20にRAXをコピー。RSP+0x20=RAX=RCX=GetSystemTimeAsFileTimeの戻り値^ThreadID

18-23行目

18行目でGetCurrentProcessId関数をcall。これはPIDを返します。
19行目は再び謎のeax同士コピー。EAX=PID
20行目はRSP+0x20をRCXにコピー。RCX=GetSystemTimeAsFileTimeの戻り値^ThreadID
21行目はRCXとRAXをXOR。RCX=GetSystemTimeAsFileTimeの戻り値^ThreadID^PID
22行目はRAXにRCXをコピー。RAX=RCX=GetSystemTimeAsFileTimeの戻り値^ThreadID^PID
23行目はRSP+0x20にRAXをコピー。RSP+0x20=RAX=RCX=GetSystemTimeAsFileTimeの戻り値^ThreadID^PID

24-38行目

24行目はRSP+0x30のアドレスをRCXにコピー。RCX=RSP+0x30のアドレス
25行目でQueryPerformanceCounter関数をcall。この関数は調べるとクロックを指定された引数に返すようです。
26行目は戻り値のRSP+0x30をEAXにコピー。EAX=RSP+0x30=QueryPerformanceCounterの戻り値
27行目はRAXを0x20(32bit)左シフト。RAX上位=RSP+0x30=QueryPerformanceCounterの戻り値
28行目はRAXとRSP+0x30をXOR。恐らくRAX下位を0に初期化という意味。
29行目はRCXにRSP+0x20をコピー。RCX=RSP+0x20=GetSystemTimeAsFileTimeの戻り値^ThreadID^PID
30行目はRCXとRAXをXOR。RCX=GetSystemTimeAsFileTimeの戻り値^ThreadID^PID^QueryPerformanceCounterの戻り値
31行目はRAXにRCXをコピー。RAX=RCX=GetSystemTimeAsFileTimeの戻り値^ThreadID^PID^QueryPerformanceCounterの戻り値
32行目はRSP+0x20にRAXをコピー。RSP+0x20=RAX=RCX=GetSystemTimeAsFileTimeの戻り値^ThreadID^PID^QueryPerformanceCounterの戻り値
33行目はRAXに0xFFFFFFFFFFFFをコピー。RAX=0xFFFFFFFFFFFF
34行目はRCXにRSP+0x20をコピー。RCX=RSP+0x20=GetSystemTimeAsFileTimeの戻り値^ThreadID^PID^QueryPerformanceCounterの戻り値
35行目はRCXとRAXをAND。RCX=(((GetSystemTimeAsFileTimeの戻り値^ThreadID)^PID)^QueryPerformanceCounterの戻り値)&0xFFFFFFFFFFFF
36行目はRAXにRCXをコピー。RAX=RCX=(((GetSystemTimeAsFileTimeの戻り値^ThreadID)^PID)^QueryPerformanceCounterの戻り値)&0xFFFFFFFFFFFF
37行目はRSP+0x20にRAXをコピー。RSP+0x20=RAX=RCX=(((GetSystemTimeAsFileTimeの戻り値^ThreadID)^PID)^QueryPerformanceCounterの戻り値)&0xFFFFFFFFFFFF
38行目はなぜかRAXにRSP+0x20をコピー。RSP+0x20=RAX=RCX=(((GetSystemTimeAsFileTimeの戻り値^ThreadID)^PID)^QueryPerformanceCounterの戻り値)&0xFFFFFFFFFFFF
39行目はRSPに0x40を加算し領域を元に戻す。
40行目はRDIをpop。
41行目でリターン。

つまり、この流れの戻り値は(((GetSystemTimeAsFileTimeの戻り値^ThreadID)^PID)^QueryPerformanceCounterの戻り値)&0xFFFFFFFFFFFFの結果ということですね!(長いわ!笑)

実はこの一つの流れは__security_init_cookie関数というものでした。
以下にURLを貼っておきます。

関数の説明で

オーバーランから保護されている関数を開始するときにクッキーはスタックに配置され、関数が終了するときにスタックの値がグローバルなクッキーと比較されます。 違いがある場合はバッファー オーバーランが発生したことを意味し、プログラムは直ちに終了します。

とあります。先ほどの一連の流れはグローバルなクッキーを設定していたということですね!面白い!

そして再び

printf_loop_asm_txt
   140012000:   48 83 ec 28             sub    rsp,0x28
   140012004:   e8 35 f3 ff ff          call   0x14001133e # __security_init_cookie
   140012009:   e8 12 00 00 00          call   0x140012020
   14001200e:   48 83 c4 28             add    rsp,0x28
   140012012:   c3                      ret

に戻ってきます。

いきなりmainへ

この後も色々設定があるようなので、いっきにmainへとびます。笑

printf_loop_asm_txt
   140011830:   40 55                   rex push rbp
   140011832:   57                      push   rdi
   140011833:   48 81 ec 28 01 00 00    sub    rsp,0x128
   14001183a:   48 8d 6c 24 20          lea    rbp,[rsp+0x20]
   14001183f:   48 8b fc                mov    rdi,rsp
   140011842:   b9 4a 00 00 00          mov    ecx,0x4a
   140011847:   b8 cc cc cc cc          mov    eax,0xcccccccc
   14001184c:   f3 ab                   rep stos DWORD PTR es:[rdi],eax
   14001184e:   48 8d 0d bb f7 00 00    lea    rcx,[rip+0xf7bb]        # 0x140021010
   140011855:   e8 2d f8 ff ff          call   0x140011087
   14001185a:   c7 45 04 00 00 00 00    mov    DWORD PTR [rbp+0x4],0x0
   140011861:   c7 45 24 00 00 00 00    mov    DWORD PTR [rbp+0x24],0x0
   140011868:   ff 15 4a ea 00 00       call   QWORD PTR [rip+0xea4a]        # 0x1400202b8
   14001186e:   89 45 24                mov    DWORD PTR [rbp+0x24],eax
   140011871:   33 c0                   xor    eax,eax
   140011873:   83 f8 01                cmp    eax,0x1
   140011876:   74 28                   je     0x1400118a0
   140011878:   44 8b 45 04             mov    r8d,DWORD PTR [rbp+0x4]
   14001187c:   8b 55 24                mov    edx,DWORD PTR [rbp+0x24]
   14001187f:   48 8d 0d a2 83 00 00    lea    rcx,[rip+0x83a2]        # 0x140019c28
   140011886:   e8 4b f9 ff ff          call   0x1400111d6
   14001188b:   8b 45 04                mov    eax,DWORD PTR [rbp+0x4]
   14001188e:   ff c0                   inc    eax
   140011890:   89 45 04                mov    DWORD PTR [rbp+0x4],eax
   140011893:   b9 e8 03 00 00          mov    ecx,0x3e8
   140011898:   ff 15 62 e7 00 00       call   QWORD PTR [rip+0xe762]        # 0x140020000
   14001189e:   eb d1                   jmp    0x140011871
   1400118a0:   48 8d a5 08 01 00 00    lea    rsp,[rbp+0x108]
   1400118a7:   5f                      pop    rdi
   1400118a8:   5d                      pop    rbp
   1400118a9:   c3                      ret    

1-10行目

1行目はRBPをスタックにpushしています。
2行目はRDIをスタックにpushしています。
3行目はRSPから0x128(296Byte)を減算しています。
4行目はRBPにRSP+0x20のアドレスをコピー。RBP=RSP+0x20のアドレス
5行目はRDIにRSPをコピー。RDI=RSP
6行目はECXに0x4Aをコピー。ECX=0x4A
7行目はEAXに0xCCCCCCCCをコピー。EAX=0xCCCCCCCC
8行目はES:RDIを0x4A(74)回EAXをコピー。
9行目はRCXにRIS+0xF7BBのアドレスをコピー。ECX=0x140021010
10行目で0x140011087をcall。これは__CheckForDebuggerJustMyCode関数だそうです。さっくり調べた感じですと、デバッガーが自分の所有するコードをデバッグしているかチェックしているようです(間違ってたらすみません)。

11-17行目

11行目はRBP+0x4に0x0をDWORDでコピー。DWORD PTR [RBP+0x4]=0x0
12行目はRBP+0x24に0x0をDWORDでコピー。DWORD PTR [RBP+0x24]=0x0
13行目で0x1400202b8(rip+0xEA4A)のQWORDアドレスをcall。これは__getpid関数です。
14行目でRBP+0x24にEAXをDWORDでコピー。DWORD PTR [RBP+0x24]=EAX(pid?)
15行目はEAXとEAXをXOR。
16行目はEAXと0x1を比較。あれ、15行目でEAX同士XORしているので、絶対この比較Falseちゃいますか?あ、ここがwhile(1)のところでしょうか!?そうに違いない!
17行目は等しければ0x1400118a0へジャンプ。

18-21行目

18行目はR8DにRBP+0X4をDWORDでコピー。R8D=DWORD PTR [RBP+0X4]
19行目はRDXにRBP+0x24をDWORDでコピー。RDX=DWORD PTR [RBP+0x24]
20行目はRCXに0x140019C28(rip+0x83A2)をコピー。RCX=0x140019C28のアドレス。
21行目で0x1400111D6をcall。printf関数です。

22-26行目

22行目はEAXにRBP+0x4をDWORDでコピー。EAX=RBP+0x4
23行目はEAXをインクリメント。
24行目はRBP+0x4にEAXをDWORDでコピー。おやおや?i+=1? RBP+0x4=EAX
25行目はECXに0x3E8(1000)をコピー。
26行目で0x140020000(RIP+0XE762)をcall。Sleep関数です。

27-31行目

27行目は0x140011871(15行目)にジャンプ。
28行目はRSPにRBP+0X108をコピー。RSP=RBP+0X108のアドレス。
29行目はRDIをpop。
30行目はRBPをpop。
31行目はリターン。

こうしてみると、一目瞭然ですね!
DWORD PTR [RBP+0x4]が変数iでDWORD PTR [RBP+0x24]が変数iですね。
ちなみに20行目の0x140019C28はメモリマップを見ると、

printf_loop_memory.txt
 140019c20 00000000 00000000 5b25645d 4c6f6f70  ........[%d]Loop
 140019c30 20697465 72617469 6f6e2025 64210a00   iteration %d!..

でした。

アセンブリを初めて解読しました。ソースコードを知った上での解読なので、理解も早かったように思います。
今までアセンブリを見ても記号の羅列のように見えましたがこうして時間をかけて解読すると当然ですが一つ一つ意味があり、理解できるのだと実感しました。今後もっとアセンブリを解読し、理解を深めていきたいです。

まとめ

  1. objdumpの出力結果でmovがmovabsに置き換えられることがある。
  2. dumpbinで実行ファイルの情報を得ることができる。
  3. アセンブリも時間をかけて解読すると理解できる。
3
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
6