0

Lets assume the remote thread procedure look like this:

DWORD __stdcall ThreadProc (void *pData) {
    ThreadData *p = (ThreadData*)pData; // Contains function references and strings
    p->MessageBoxW(NULL, p->Message, p->Title, MB_OK);
}

Then everything works fine and p->MessageBoxW(...) shows a message box as expected. But I don't want to call GetProcAddress for every function I use in the remote thread, so I thought I could create a function export within my module (EXE file creating the remote thread), so that the remote thread just calls LoadLibraryW to load my EXE file as module into the target process's address space and GetProcAddress to get the exported function's address in order to call it.

typedef void (__stdcall *_Test) ();
extern "C" void __stdcall Test () {
    return;
}

DWORD __stdcall ThreadProc (void *pData) {
    ThreadData *p = (ThreadData*)pData; // Contains function references and strings
    HMODULE hLib = p->LoadLibraryW(p->LibPath);
    _Test pTest = (_Test)p->GetProcAddress(hLib, p->ProcName);

    pTest();

    p->FreeLibrary(hLib);
    return NULL;
}

This still works fine. But as soon as I change the exported function to

extern "C" void __stdcall Test () {
    MessageBoxW(NULL, L"Message", L"Title", MB_OK);
    return;
}

the target process suddenly crashes. Doesn't LoadLibrary resolve intermodular references? Is it possible to load my module into the target process's address space so that the exported function can be coded without passing all function addresses to it?


Additional information: For everyone copying the code, I had to disable incremental linking, build as release and add a module definition file to ensure that Test is exported as Test and not as _Test@SoMeJuNk. Just prepending __declspec(dllexport) didn't work for some reason. The module definition file looks like this

EXPORTS
    Test@0

The ThreadData structure looks like this

typedef struct tagThreadData {
    typedef BOOL (__stdcall *_FreeLibrary) (HMODULE);
    typedef FARPROC (__stdcall *_GetProcAddress) (HMODULE, PSTR);
    typedef HMODULE (__stdcall *_LoadLibraryW) (LPWSTR);
    typedef DWORD (__stdcall *_MessageBoxW) (HWND, LPWSTR, LPWSTR, DWORD);

    _FreeLibrary FreeLibrary;
    _GetProcAddress GetProcAddress;
    _LoadLibraryW LoadLibraryW;
    _MessageBoxW MessageBoxW;

    WCHAR LibPath[100];
    WCHAR Message[30];
    CHAR ProcName[10];
    WCHAR Title[30];
} ThreadData, *PThreadData;
Cubi73
  • 1,891
  • 3
  • 31
  • 52
  • LoadLibrary doesnt resolve any references. At All. Because that would be very strange and silly and AFAIR that isnt even possible with unmanaged code - you will need to use one of the .NET languages if you want to "resolve" anything and even then it will get tricky since assemblynames can indeed be somewhat arbitrary and without additional checks you may end up with the wrong assembly version – specializt Nov 24 '15 at 08:26
  • @specializt Sorry, when I was not clear. With "resolving references" I mean loading all libraries referenced in the import section and setting the function addresses in the IAT. As far as I can tell, this is what happens, when an executable file is being loaded. Doesn't LoadLibrary do this? – Cubi73 Nov 24 '15 at 08:31
  • no, this isnt even remotely what happens. LoadLibrary has one job : lookup the filesystempath to a given library and load it properly. Thats about it. I dont know where you get your information but you should abandon your sources and stick to the official documentation : https://msdn.microsoft.com/en-us/library/windows/desktop/ms684175(v=vs.85).aspx. The MSDN is pretty thorough and almost always correct in every aspect. – specializt Nov 24 '15 at 08:35
  • @specializt So in theory, just in theory, if the injected DLL imports functions from another DLL, which has ASLR activated, how does a function call work without GetProcAddress within the injected DLL? – Cubi73 Nov 24 '15 at 09:46
  • thats simple : it doesnt -- UNLESS the injected DLL explicitly loads the other DLL manually _(if it isnt already loaded)_. Welcome to the world of C and C++ - you will need to get used to explicit operations, there are no automatisms. I really think .NET is more suited towards your needs - machine-level programming can be the most challenging task, languages like that are bad choices for beginners .... – specializt Nov 24 '15 at 11:07
  • @specializt Wow... I'm programming C++ for about a year now, but I never needed to create DLLs at all. It seems that this really is a new world for me... – Cubi73 Nov 24 '15 at 11:14
  • Yeah, DLLs are quite notorious for being difficult - if you want to avoid all of that you may as well stick with static libraries, these will always do their work, no matter the circumstance – specializt Nov 24 '15 at 11:17

1 Answers1

3

I came up with a temporary solution: Putting all remote code into an actual DLL. But putting the code into a DLL isn't my target, so if someone comes up with a clever solution, where the EXE file is the injector as well as the module being injected, I will mark the new answer as right.

Even though there are many tutorials on how to inject an actual DLL into another process's address space, I still give away my solution. I wrote my original solution only for UNICODE and 64-Bit, but I tried my best to make it work for both ASCII and UNICODE and 32-bit and 64-bit. But lets get started...


First of all, an explanation of the basic steps

  1. Obtain handle to the target process with at least the following access rights

    PROCESS_CREATE_THREAD
    PROCESS_QUERY_INFORMATION
    PROCESS_VM_OPERATION
    PROCESS_VM_WRITE
    PROCESS_VM_READ
    
  2. Allocate memory for the remote thread procedure and the data and function pointers needed for loading the target dll and its "entrypoint" (I don't mean the actual entrypoint DllMain, but a function designed to be called from within the remote thread)

    PVOID pThread = VirtualAllocEx(hProc, NULL, 4096, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    
  3. Copy remote thread procedure and important data over to the target process

    WriteProcessMemory(hProc, pThread, ThreadProc, ThreadProcLen, NULL);
    WriteProcessMemory(hProc, pParam, &data, sizeof(ThreadData), NULL);
    
  4. Create remote thread. This thread will load the target dll into the target process's address space and calls its "entrypoint"

    HANDLE hThread = CreateRemoteThread(hProc, NULL, 0, (PTHREAD_START_ROUTINE)pThread, pParam, NULL, NULL);
    
  5. Optional: Wait until the thread returns

    WaitForSingleObject(hThread, INFINITE);
    
    DWORD threadExitCode;
    GetExitCodeThread(hThread, &threadExitCode);
    
  6. Close thread handle, release memory, Close process handle

    CloseHandle(hThread);
    VirtualFreeEx(hProc, pThread, 4096, MEM_RELEASE);
    CloseHandle(hProc);
    

So here's my ThreadProc and ThreadData structure. ThreadProc is the remote thread procedure being called by CreateRemoteThread and should LoadLibrary the target dll, so it can call the target dll's "entrypoint". The ThreadData structure contains the addresses of LoadLibrary, GetProcAddress and FreeLibrary, the target dll's path TargetDll and the name of the "entrypoint" DllEntry.

typedef struct {
    typedef BOOL (__stdcall *_FreeLibrary) (HMODULE);
    typedef FARPROC (__stdcall *_GetProcAddress) (HMODULE, LPCH);
    typedef HMODULE (__stdcall *_LoadLibrary) (LPTSTR);
    typedef void (__stdcall *_DllEntry) ();

    _LoadLibrary LoadLibrary;
    TCHAR TargetDll[MAX_PATH];

    _GetProcAddress GetProcAddress;
    CHAR DllEntry[50]; // Some entrypoint designed to be
                       // called from the remote thread

    _FreeLibrary FreeLibrary;
} ThreadData, *PThreadData;



// ThreadProcLen should be smaller than 3400, because ThreadData can
// take up to 644 bytes unless you change the length of TargetDll or
// DllEntry
#define ThreadProcLen       (ULONG_PTR)2048
#define SPY_ERROR_OK        (DWORD)0
#define SPY_ERROR_LOAD_LIB  (DWORD)1
#define SPY_ERROR_GET_PROC  (DWORD)2

DWORD ThreadProc (PVOID pParam) {
    DWORD err = SPY_ERROR_OK;
    PThreadData p = (PThreadData)pParam;

    // Load dll to be injected
    HMODULE hLib = p->LoadLibrary(p->TargetDll);
    if (hLib == NULL)
        return SPY_ERROR_LOAD_LIB;

    // Obtain "entrypoint" of dll (not DllMain)
    ThreadData::_DllEntry pDllEntry = (ThreadData::_DllEntry)p->GetProcAddress(hLib, p->DllEntry);
    if (pDllEntry != NULL)
        // Call dll's "entrypoint"
        pDllEntry();
    else
        err = SPY_ERROR_GET_PROC;

    // Free dll
    p->FreeLibrary(hLib);
    return err;
}

Then there's the actual code injecting the remote thread procedure into the target process's address space

int main(int argc, char* argv[]) {
    // DWORD pid = atoi(argv[1]);

    // Open process
    HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
    if (hProc != NULL) {
        // Allocate memory in the target process's address space
        PVOID pThread = VirtualAllocEx(hProc, NULL, 4096, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        if (pThread != NULL) {
            PVOID pParam = (PVOID)((ULONG_PTR)pThread + ThreadProcLen);

            // Initialize data to be passed to the remote thread
            ThreadData data;

            HMODULE hLib = LoadLibrary(TEXT("KERNEL32.DLL"));
            data.LoadLibrary = (ThreadData::_LoadLibrary)GetProcAddress(hLib, "LoadLibrary");
            data.GetProcAddress = (ThreadData::_GetProcAddress)GetProcAddress(hLib, "GetProcAddress");
            data.FreeLibrary = (ThreadData::_FreeLibrary)GetProcAddress(hLib, "FreeLibrary");
            FreeLibrary(hLib);

            _tcscpy_s(data.TargetDll, TEXT("..."));         // Insert path of target dll
            strcpy_s(data.DllEntry, "NameOfTheDllEntry");   // Insert name of dll's "entrypoint"

            // Write procedure and data into the target process's address space
            WriteProcessMemory(hProc, pThread, ThreadProc, ThreadProcLen, NULL);
            WriteProcessMemory(hProc, pParam, &data, sizeof(ThreadData), NULL);

            // Create remote thread (ThreadProc)
            HANDLE hThread = CreateRemoteThread(hProc, NULL, 0, (PTHREAD_START_ROUTINE)pThread, pParam, NULL, NULL);
            if (hThread != NULL) {
                // Wait until remote thread has finished
                if (WaitForSingleObject(hThread, INFINITE) == WAIT_OBJECT_0) {
                    DWORD threadExitCode;

                    // Evaluate exit code
                    if (GetExitCodeThread(hThread, &threadExitCode) != FALSE) {
                        // Evaluate exit code
                    } else {
                        // The thread's exit code couldn't be obtained
                    }
                } else {
                    // Thread didn't finish for some unknown reason
                }

                // Close thread handle
                CloseHandle(hThread);
            }

            // Deallocate memory
            VirtualFreeEx(hProc, pThread, 4096, MEM_RELEASE);
        } else {
            // Couldn't allocate memory in the target process's address space
        }

        // Close process handle
        CloseHandle(hProc);
    }

    return 0;
}

The dll being injected has a real entrypoint DllMain that is called, when LoadLibrary loads the target dll into the target process's address space, and another "entrypoint" NameOfTheDllEntry called by the remote thread procedure (if it can be located in the first place)

// Module.def:
// LIBRARY NameOfDllWithoutExtension
// EXPORTS
//     NameOfTheDllEntry
__declspec(dllexport) void __stdcall NameOfTheDllEntry () {
    // Because the library is actually loaded in the target process's address
    // space, there's no need for obtaining pointers to every function.
    // I didn't try libraries other than kernel32.dll and user32.dll, but they
    // should be working as well as long as the dll itself references them

    // Do stuff
    return;
}



BOOL APIENTRY DllMain (HMODULE hLib, DWORD reason, PVOID) {
    if (reason == DLL_PROCESS_ATTACH)
        DisableThreadLibraryCalls(hLib);    // Optional

    return TRUE;
}
evandrix
  • 6,041
  • 4
  • 27
  • 38
Cubi73
  • 1,891
  • 3
  • 31
  • 52
  • 1
    To use LoadLibrary() for exe, read this: http://stackoverflow.com/q/19110747/1983398 And the [LoadLibraryEx() docu](https://msdn.microsoft.com/en-us/library/windows/desktop/ms684179.aspx) explains this a bit: If the specified module is an executable module, static imports are not loaded; instead, the module is loaded as if DONT_RESOLVE_DLL_REFERENCES was specified. See the dwFlags parameter for more information. – ssbssa Nov 30 '15 at 16:18