CreateAndInject leads to crash #9

Closed
spazzarama opened this Issue Aug 14, 2015 · 1 comment

Comments

Projects
None yet
1 participant
@spazzarama
Member

spazzarama commented Aug 14, 2015

Moved from CodePlex
Originally submitted by werker

I tried the CreateAndInject function and the FileMon tool with the full path to the exe file.
In both cases the target exe crashed right after the start.

FileMon shows the following: "System.ApplicationException: STATUS_INTERNAL_ERROR: Unknown error in injected assembler code. (Code: 5)"

The target exe files were the Windows notepad.exe and calc.exe (and some others).
The operating system is Windows 7 64 bit (also tested 32 bit in VM).
I tested the .NET 4.0 and 3.5 version.
I used the latest EasyHook 2.7 download on this website and a self compiled binary from the latest source code.

Comments

Sebmaster wrote Jan 13 at 11:06 AM

I've found a possible cause for this, I'm not sure if it applies to your case though.

It occurs when EasyHook tries to inject a library into a suspended process. I spent the whole day debugging and managed to RhInjectLibrary.

In the 2.7 release branch you'll find the following lines:

Info->LoadLibraryW   = (PVOID)GetRemoteFuncAddress(InTargetPID, hProc, "kernel32.dll", "LoadLibraryW");
Info->FreeLibrary    = (PVOID)GetRemoteFuncAddress(InTargetPID, hProc, "kernel32.dll", "FreeLibrary");
Info->GetProcAddress = (PVOID)GetRemoteFuncAddress(InTargetPID, hProc, "kernel32.dll", "GetProcAddress");
Info->VirtualFree    = (PVOID)GetRemoteFuncAddress(InTargetPID, hProc, "kernel32.dll", "VirtualFree");
Info->VirtualProtect = (PVOID)GetRemoteFuncAddress(InTargetPID, hProc, "kernel32.dll", "VirtualProtect");
Info->ExitThread     = (PVOID)GetRemoteFuncAddress(InTargetPID, hProc, "kernel32.dll", "ExitThread");
Info->GetLastError   = (PVOID)GetRemoteFuncAddress(InTargetPID, hProc, "kernel32.dll", "GetLastError");

all of them will return a NULL pointer in a suspended process (although I haven't looked much into why that is). I have no idea why those function pointers are determined so weirdly, there may be a reason, but for my use-case I just patched those with the ones which are still in trunk:

Info->LoadLibraryW = (PVOID)GetProcAddress(hKernel32, "LoadLibraryW");
Info->FreeLibrary = (PVOID)GetProcAddress(hKernel32, "FreeLibrary");
Info->GetProcAddress = (PVOID)GetProcAddress(hKernel32, "GetProcAddress");
Info->VirtualFree = (PVOID)GetProcAddress(hKernel32, "VirtualFree");
Info->VirtualProtect = (PVOID)GetProcAddress(hKernel32, "VirtualProtect");
Info->ExitThread = (PVOID)GetProcAddress(hKernel32, "ExitThread");
Info->GetLastError = (PVOID)GetProcAddress(hKernel32, "GetLastError");

werker wrote Jan 14 at 7:52 AM

I think you are absolutely right.

A while ago I read that you can't enumerate the modules of a remote process when it is created and in a suspended state. So GetRemoteFuncAddress can't get the address of kernel32.dll to look further for the function addresses.

For now I don't know a solution that can still use GetRemoteFuncAddress.

werker wrote Jan 15 at 8:12 PM

I think I found the solution in another injection library for c++. It's called hadesmem by RaptorFactor.
He creates a remote thread that returns immediately, which seems to make it possible to enumerate the modules afterwards.

I created a function for this in the RemoteHook/thread.c mentioned above.

EASYHOOK_NT_INTERNAL NtForceLdrInitializeThunk(HANDLE hProc)
{
    HANDLE                  hRemoteThread = NULL;
    UCHAR*                  RemoteInjectCode = NULL;
    BYTE                    InjectCode[3];
    ULONG                   CodeSize;
    SIZE_T                  BytesWritten;
    NTSTATUS                NtStatus;

#ifdef _M_X64
    InjectCode[0] = 0xC3;
    CodeSize = 1;
#else
    InjectCode[0] = 0xC2;
    InjectCode[1] = 0x04;
    InjectCode[2] = 0x00;
    CodeSize = 3;
#endif

    if((RemoteInjectCode = (BYTE*)VirtualAllocEx(hProc, NULL, CodeSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE)) == NULL)
        THROW(STATUS_NO_MEMORY, L"Unable to allocate memory in target process.");

    if(!WriteProcessMemory(hProc, RemoteInjectCode, InjectCode, CodeSize, &BytesWritten) || (BytesWritten != CodeSize))
        THROW(STATUS_INTERNAL_ERROR, L"Unable to write into target process memory.");

    if((hRemoteThread = CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)RemoteInjectCode, NULL, 0, NULL)) == NULL)
        THROW(STATUS_ACCESS_DENIED, L"Unable to create remote thread.");

    WaitForSingleObject(hRemoteThread, INFINITE);

    RETURN;

THROW_OUTRO:
FINALLY_OUTRO:
    return NtStatus;
}

Then I call the function right before getting the remote function addresses, so it looks like this:

    FORCE(NtForceLdrInitializeThunk(hProc));

    // Determine function addresses within remote process
    Info->LoadLibraryW   = (PVOID)GetRemoteFuncAddress(InTargetPID, hProc, "kernel32.dll", "LoadLibraryW");
    Info->FreeLibrary    = (PVOID)GetRemoteFuncAddress(InTargetPID, hProc, "kernel32.dll", "FreeLibrary");
    Info->GetProcAddress = (PVOID)GetRemoteFuncAddress(InTargetPID, hProc, "kernel32.dll", "GetProcAddress");
    Info->VirtualFree    = (PVOID)GetRemoteFuncAddress(InTargetPID, hProc, "kernel32.dll", "VirtualFree");
    Info->VirtualProtect = (PVOID)GetRemoteFuncAddress(InTargetPID, hProc, "kernel32.dll", "VirtualProtect");
    Info->ExitThread     = (PVOID)GetRemoteFuncAddress(InTargetPID, hProc, "kernel32.dll", "ExitThread");
    Info->GetLastError   = (PVOID)GetRemoteFuncAddress(InTargetPID, hProc, "kernel32.dll", "GetLastError");

spazzarama wrote Jan 18 at 12:59 PM

The reason for the change to GetRemoteFuncAddress is that there are situations under which using your current process to determine the addresses within the remote process will not give you the same offset as within the target resulting in crashes also.

I like the idea of using the temporary remote thread solution that werker has posted, will need to test this out.

koczkatamas wrote Feb 25 at 10:24 PM

I had the same problem and werker's workaround solved it. Thanks werker!
wrote Feb 25 at 10:40 PM

tqtam1993 wrote May 14 at 1:53 PM

Thank you werker!

This solution helps me so much.

@spazzarama spazzarama added the bug label Aug 14, 2015

@spazzarama spazzarama self-assigned this Aug 14, 2015

@spazzarama spazzarama added this to the 2.7 Stable milestone Aug 14, 2015

@spazzarama

This comment has been minimized.

Show comment
Hide comment
@spazzarama

spazzarama Aug 15, 2015

Member

Implemented the fix provided by werker, confirmed to work using FileMon example performing CreateAndInject into notepad.exe

Member

spazzarama commented Aug 15, 2015

Implemented the fix provided by werker, confirmed to work using FileMon example performing CreateAndInject into notepad.exe

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