Yulin.Li
Yulin.Li

Reputation: 1

CreateRemoteThread failed 5

CreateRemoteThread() fails with error 5 when calling inject_param->allocate() in inject_begin(). Comment it and CreateRemoteThread() is OK. I dont know why. The target platform is 64-bit.

#include <iostream>
#include <windows.h>
#include <TlHelp32.h>

std::uint32_t get_process_id(
    __in const std::basic_string<TCHAR>& name) {
    PROCESSENTRY32 process_entry;
    process_entry.dwSize = sizeof(PROCESSENTRY32);
    auto process_snapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
    if (Process32First(process_snapshot, &process_entry))
    {
        do {
            if (!wcscmp(process_entry.szExeFile, name.data()))
            {
                CloseHandle(process_snapshot);
                return process_entry.th32ProcessID;
            }
        } while (Process32Next(process_snapshot, &process_entry));
    }
    CloseHandle(process_snapshot);
    return 0;
}


void adjust_token_privileges() {
    HANDLE h_token = NULL;
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &h_token))
    {
        return;
    }
    TOKEN_PRIVILEGES priv = { 0 };
    priv.PrivilegeCount = 1;
    priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &priv.Privileges[0].Luid))
    {
        AdjustTokenPrivileges(h_token, FALSE, &priv, 0, NULL, NULL);
    }
    CloseHandle(h_token);
}

typedef NTSTATUS(WINAPI* NTALLOCATEVIRTUALMEMORY)(IN HANDLE ProcessHandle, IN OUT PVOID* BaseAddress, IN ULONG ZeroBits, IN OUT PSIZE_T RegionSize, IN ULONG AllocationType, IN ULONG Protect);


struct InjectParam {
    int age;
    NTALLOCATEVIRTUALMEMORY allocate;

};

//shell code 
ULONG_PTR WINAPI inject_begin(InjectParam* inject_param) {
    inject_param->age += 1;//age=2
    PVOID memory_address = NULL;
    SIZE_T size = 10;
    //comment it CreateRemoteThread is ok 
    inject_param->allocate((HANDLE)-1, &memory_address, 0, &size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    return 0;
}

void inject_end() {
    printf("11");
}

void remote_map_load_dll(HANDLE target_process)
{
    InjectParam inject_param;
    //将指定内存区域的内容清零
    RtlZeroMemory(&inject_param, sizeof(inject_param));
    inject_param.age = 1;
    //-------------------------------
    WORD* shell_code_begin = (WORD*)inject_begin;
    DWORD shell_code_size = 0;
    while (*shell_code_begin != 0XCCCC)
    {
        shell_code_begin++;
        shell_code_size += 2;
    }
    printf("shellcode length:%d\n", shell_code_size);
    PVOID shell_code_buffer = malloc(shell_code_size);
    RtlCopyMemory(shell_code_buffer, inject_begin, shell_code_size);
    //-------------------------------
    //获取本进程的 ntdll.dll 
    HMODULE h_nt_dll = GetModuleHandleA("ntdll.dll");
    inject_param.allocate = (NTALLOCATEVIRTUALMEMORY)GetProcAddress(h_nt_dll, "NtAllocateVirtualMemory");
    printf("NtAllocateVirtualMemory address :0x%p\r\n", inject_param.allocate);
    //-------------------------------
    PBYTE shellcode_address = (PBYTE)VirtualAllocEx(target_process, 0, shell_code_size + sizeof(inject_param), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    // printf("shellcode_address:0x%p\r\n", shellcode_address);
     //-------------------------------write shell code
    SIZE_T dw_writed = 0;
    PBYTE shell_code_address = shellcode_address;
    printf("shell_code_address:0X%p\r\n", shell_code_address);
    WriteProcessMemory(target_process, shell_code_address, shell_code_buffer, shell_code_size, &dw_writed);
    printf("write ShellCodeAddress bytes:%d\r\n", dw_writed);

    //------------------------------- write InjectParam
    PBYTE inject_param_address = shellcode_address + shell_code_size;
    printf("InjectParamAddress:0X%p\r\n", inject_param_address);
    BOOL result = WriteProcessMemory(target_process, (LPVOID)inject_param_address, &inject_param, sizeof(inject_param), &dw_writed);
    printf("write InjectParamAddress bytes:%d\r\n", dw_writed);

    //------------------------------- 
    HANDLE remote_thread = CreateRemoteThread(target_process, 0, 0, (LPTHREAD_START_ROUTINE)shell_code_address, inject_param_address, 0, 0);
    printf("remote_thread:0x%X\n", remote_thread);
    if (remote_thread)
    {
        WaitForSingleObject(remote_thread, -1);
        //ERROR_ACCESS_DENIED=0x5
        DWORD exit_code = 0;
        GetExitCodeThread(remote_thread, &exit_code);
        printf("error:0x%x\n", exit_code);
        //释放申请的内存
        VirtualFreeEx(target_process, shellcode_address, 0, MEM_FREE);
        CloseHandle(remote_thread);
    }
    //free
    free(shell_code_buffer);
}

int main() {
    int result = EXIT_SUCCESS;
    //
    adjust_token_privileges();

    //get dwmpid
    auto dwm_pid = get_process_id(L"CalculatorApp.exe");

    auto h_handle = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, dwm_pid);
    printf("dwmpid:%d\r\n", dwm_pid);
    //
    remote_map_load_dll(h_handle);

    CloseHandle(h_handle);
    system("pause");
    return result;
}

Upvotes: -4

Views: 84

Answers (1)

RbMm
RbMm

Reputation: 33754

Comment it and CreateRemoteThread() is OK.

not need confuse result of CreateRemoteThread(..) and thread exit code, returned by GetExitCodeThread. these are completely different things. and if you write such code - this is mandatory (and trivial) trace it under debugger, in both source and target process, to view exactly what happense, and where error, if any. and at least clearly formulate where the error is and show the actual code. you not check, what error return CreateRemoteThread, if it fail (i assume it not fail). exit_code is ERROR_ACCESS_DENIED=0x5 ? but in your current code inject_begin uncodtitionally return 0. even if NtAllocateVirtualMemory fail. even if your actual code is return inject_param->allocate(..) - this api return NTSTATUS not win32 error code ERROR_ACCESS_DENIED. so unclear from where you at all can got it. your current code can not got this code.

VirtualFreeEx(target_process, shellcode_address, 0, MEM_FREE);

not free memore, MEM_RELEASE must be used in place MEM_FREE.

no any sense, in current code, copy your code to temporary buffer, shell_code_buffer, for what ?! but exist sense copy shell code and data to single buffer, for do 1 call to, WriteProcessMemory instead 2 calls. but you not do this.

while (*shell_code_begin != 0XCCCC) the code size is not determined that way

You can't allocate PAGE_EXECUTE_READWRITE memory on recent Windows systems. It's blocked by the "Data Execution Protection" code, mostly to prevent what you're trying to do here.

you can not allocate it when call this api from process with ProhibitDynamicCode mitigation. NtAllocateVirtualMemory return STATUS_DYNAMIC_CODE_BLOCKED in this case. when called from this process. however this is not ERROR_ACCESS_DENIED and even not it win32 convert, ERROR_DYNAMIC_CODE_BLOCKED

code can look like:

struct InjectParam {

    NTSTATUS (NTAPI* NtAllocateVirtualMemory)(
        _In_ HANDLE ProcessHandle, 
        _Inout_ PVOID* BaseAddress, 
        _In_ ULONG ZeroBits, 
        _Inout_ PSIZE_T RegionSize, 
        _In_ ULONG AllocationType, 
        _In_ ULONG Protect);

    int age = 0;

    UCHAR code[];

#pragma code_seg(".text$mn$sc$0")

    static ULONG WINAPI inject_begin(InjectParam* This)
    {
        return This->inject();
    }

#pragma code_seg(".text$mn$sc$1")

    ULONG inject() 
    {
        age++;
        SIZE_T size = 10;
        PVOID memory_address = NULL;
        return NtAllocateVirtualMemory(NtCurrentProcess(), &memory_address, 0, &size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    }

#pragma code_seg(".text$mn$sc$2")

    static ULONG code_size()
    {
        return (ULONG)((ULONG_PTR)code_size - (ULONG_PTR)inject_begin);
    }

#pragma code_seg()

    void* operator new(size_t s){
        return LocalAlloc(LMEM_FIXED, s + code_size());
    }

    void operator delete(void* pv) {
        LocalFree(pv);
    }

    InjectParam() {
        memcpy(code, inject_begin, code_size());
    }

    InjectParam(InjectParam* p);

    BOOL Init()
    {
        if (HMODULE h_nt_dll = GetModuleHandleW(L"ntdll.dll"))
        {
            if ((void*&)NtAllocateVirtualMemory = GetProcAddress(h_nt_dll, "NtAllocateVirtualMemory"))
            {
                return TRUE;
            }
        }

        return FALSE;
    }
};

void remote_map_load_dll(HANDLE target_process)
{
    if (InjectParam* inject_param = new InjectParam)
    {
        if (inject_param->Init())
        {
            SIZE_T shell_code_size = sizeof(InjectParam) + InjectParam::code_size();

            if (PVOID shell_code_address = VirtualAllocEx(target_process, 
                0, shell_code_size, MEM_COMMIT, PAGE_EXECUTE_READWRITE))
            {
                if (WriteProcessMemory(target_process, shell_code_address, inject_param, shell_code_size, &shell_code_size))
                {
                    if (HANDLE remote_thread = CreateRemoteThread(target_process, 0, 0, 
                        (LPTHREAD_START_ROUTINE)&reinterpret_cast<InjectParam*>(shell_code_address)->code, 
                        shell_code_address, 0, 0))
                    {
                        if (WAIT_OBJECT_0 == WaitForSingleObject(remote_thread, INFINITE))
                        {
                            DWORD exit_code = 0;
                            if (GetExitCodeThread(remote_thread, &exit_code))
                            {
                                int age;
                                if (ReadProcessMemory(target_process, 
                                    &reinterpret_cast<InjectParam*>(shell_code_address)->age,
                                    &age, sizeof(age), 0))
                                {
                                    printf("age = %x, exit_code=%x\n", age, exit_code);
                                }
                            }
                        }
                        else
                        {
                            shell_code_address = 0;
                        }

                        CloseHandle(remote_thread);
                    }
                }

                VirtualFreeEx(target_process, shell_code_address, 0, MEM_RELEASE);
            }
        }

        delete inject_param;
    }
}

Upvotes: 0

Related Questions