1

I'm trying to inject my DLL into a 64-bit process that I just created. I initially create it suspended so that I can apply WinAPI patch trampolines in that process (from my injected DLL.) But if I understand it correctly, I cannot inject my DLL into a suspended process.

So I came up with the following code, following the guy's suggestions, but it doesn't go far. The VirtualProtectEx fails and I get an error code ERROR_INVALID_ADDRESS. I marked it in the source below.

Any idea where am I messing up?

PROCESS_INFORMATION pi = {0};
STARTUPINFO si = {0};
si.cb = sizeof(si);

WCHAR buffer[MAX_PATH];
::StringCchCopy(buffer, _countof(buffer), L"injected-process.exe");

if(CreateProcessW(0, buffer, 0, 0, 0, CREATE_SUSPENDED, 0, 0, &si, &pi))
{
    inject_dll_into_suspended_x64_proc(pi.hProcess, pi.hThread, "injected-process.exe");

    //... continue on
}

and then the code to prep process for injection:

bool inject_dll_into_suspended_x64_proc(HANDLE hProc, HANDLE hMainThread, const char* pstrProcFileName)
{
    bool bRes = false;
    int nOSError = NO_ERROR;

    DWORD dwEntryOffset = 0;
    LOADED_IMAGE li = { 0 };
    if (::MapAndLoad(pstrProcFileName, NULL, &li, FALSE, TRUE))
    {
        dwEntryOffset = li.FileHeader->OptionalHeader.AddressOfEntryPoint;
        ::UnMapAndLoad(&li);
    }

    if(dwEntryOffset)
    {
        //  90               nop              
        //  EB FE            jmp         self
        static BYTE inject_asm_x64[] = {
            0x90,
            0xEB, 0xFE,
        };
        BYTE buffBackup[sizeof(inject_asm_x64)] = { 0 };

        //Get process base addr
        BYTE* pBaseAddr = (BYTE*)::VirtualAllocEx(hProc, NULL, sizeof(buffBackup), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
        if (pBaseAddr)
        {
            BYTE* pAddr = pBaseAddr + dwEntryOffset;

            DWORD dwOldProtect = 0;
            if (::VirtualProtectEx(hProc, pAddr, sizeof(buffBackup), PAGE_EXECUTE_READWRITE, &dwOldProtect))    //** FAILS: With error code: 487, or ERROR_INVALID_ADDRESS
            {
                __try
                {
                    //Backup what we have there now
                    size_t szcbRead = 0;
                    if (::ReadProcessMemory(hProc, pAddr, buffBackup, sizeof(buffBackup), &szcbRead) &&
                        szcbRead == sizeof(buffBackup))
                    {
                        //Now write our code into entry point
                        size_t dwcbSzWrtn = 0;
                        if (WriteProcessMemory(hProc, pAddr, inject_asm_x64, sizeof(inject_asm_x64), &dwcbSzWrtn) &&
                            dwcbSzWrtn == sizeof(inject_asm_x64))
                        {
                            bool bIntermediateSuccess = false;
                            bool bThreadIsSuspended = true;

                            //Resume thread
                            if (ResumeThread(hMainThread) == 1)
                            {
                                bThreadIsSuspended = false;

                                CONTEXT context;
                                bool bReached = false;

                                //Wait for it to reach our JMP self opcode
                                for(;; ::Sleep(1))
                                {
                                    if(!::GetThreadContext(hMainThread, &context))
                                    {
                                        //Failed
                                        nOSError = ::GetLastError();
                                        break;
                                    }

                                    if(context.Rip == (DWORD64)(pAddr + 1))     //First is nop, so skip it
                                    {
                                        //Got it
                                        bReached = true;
                                        break;
                                    }
                                }

                                if(bReached)
                                {
                                    //Do our DLL injection now
                                    if(inject_dll_here(hProc))
                                    {
                                        //Injected OK
                                        bIntermediateSuccess = true;
                                    }
                                    else
                                        nOSError = ::GetLastError();

                                    //Suspend main thread
                                    if(::SuspendThread(hMainThread) == 0)
                                    {
                                        //Thread is again suspended
                                        bThreadIsSuspended = true;
                                    }
                                    else
                                    {
                                        //Failed
                                        nOSError = ::GetLastError();
                                        bIntermediateSuccess = false;
                                    }
                                }
                            }
                            else
                                nOSError = ::GetLastError();

                            if(bThreadIsSuspended)
                            {
                                //Revert process memory back
                                if (WriteProcessMemory(hProc, pAddr, buffBackup, sizeof(buffBackup), &dwcbSzWrtn) &&
                                    dwcbSzWrtn == sizeof(buffBackup))
                                {
                                    //Now restore the main thread
                                    if (ResumeThread(hMainThread) == 1)
                                    {
                                        //Done
                                        bRes = bIntermediateSuccess;
                                    }
                                    else
                                        nOSError = ::GetLastError();
                                }
                                else
                                    nOSError = ::GetLastError();
                            }
                        }
                        else
                            nOSError = ::GetLastError();
                    }
                    else
                        nOSError = ::GetLastError();
                }
                __finally
                {
                    //Reset protection flags
                    ::VirtualProtectEx(hProc, pAddr, sizeof(buffBackup), dwOldProtect, NULL);
                }
            }
            else
                nOSError = ::GetLastError();

            //Free mem
            ::VirtualFreeEx(hProc, pBaseAddr, 0, MEM_RELEASE);
        }
        else
            nOSError = ::GetLastError();
    }
    else
        nOSError = ::GetLastError();

    return bRes;
}
c00000fd
  • 20,994
  • 29
  • 177
  • 400
  • *I cannot inject my DLL into a suspended process* - absolute nonsense. why you not use apc for example for inject dll or arbitrary shellcode for process ? – RbMm Nov 05 '17 at 20:36
  • @RbMm: Well, I am using a shellcode that calls `LoadLibrary` that I inject into the target process and then execute with `CreateRemoteThread`, but the question is can I do so while the target process is still suspended, or hasn't run yet? – c00000fd Nov 05 '17 at 20:45
  • of course you can and must do this while new process is suspended – RbMm Nov 05 '17 at 20:46
  • @RbMm: So [that blog I linked to](https://opcode0x90.wordpress.com/2011/01/15/injecting-dll-into-process-on-load/) is incorrect in its assumption? – c00000fd Nov 05 '17 at 20:47
  • 1
    yes, not professional and nonsense. i have huge experience in topic and know what i say – RbMm Nov 05 '17 at 20:48
  • @RbMm: OK. Appreciate your take on it then. – c00000fd Nov 05 '17 at 20:48
  • 1
    the best choice for you inject apc to child process. as result your shell code (or LoadLibrary) will be executed in first process thread before entry point. why you not use this ? for what you try patch exe entry? why so ugly code with huge mistakes count ? – RbMm Nov 05 '17 at 20:51
  • @RbMm: I need to override a couple of WinAPIs in a `winsock` library for the target process. I've never used `APC` for code injection though. Can you show how it's done? – c00000fd Nov 05 '17 at 20:52
  • 1
    you can simply call `QueueUserAPC`. thread handle you have. if you try patch exe entry point - so execute code *before* exe entry point must be ok for you. – RbMm Nov 05 '17 at 20:54

0 Answers0