user3910665
user3910665

Reputation:

CreateRemoteThread() succeeds yet doesn't do anything

I'm trying to inject dll (found in dll_path[] and it's correct path) into target process using CreateRemoteThread(). Unfortunately completely nothing happens (expected result is MessageBox() call from DLL_PROCESS_ATTACH resulting in window pop up). It works properly when used with remote injector.

You can treat every what() call as GetLastError() and assert(0). It is guaranteed that variable PID will contain correct process id (I tested it many times). Also none of the function calls you see in code below fails. Funny thing is that it worked well until I rewrote it from C to more C++-like code which you can see below. I compiled code below and dll code without any warnings. I wonder where do I made mistake. Here's my code:

int main() {

    const WCHAR dll_path[] = L"C:\\myDLL.dll";

    const std::wstring process_name = L"target.exe";

    const DWORD PID = get_PID(process_name);

    if (!PID)
        what();

    HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, false, PID);
    if (!process)
        what();

    FARPROC lib = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryA");
    if (!lib)
        what();

    LPVOID base = (LPVOID)VirtualAllocEx(process, 0, wcslen(dll_path), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (!base)
        what();

    BOOL good = WriteProcessMemory(process, base, dll_path, wcslen(dll_path), 0);
    if (!good)
        what();

    HANDLE thread = CreateRemoteThread(process, 0, 0, reinterpret_cast<LPTHREAD_START_ROUTINE>(lib), base, 0, 0);
    if (!thread)
        what();    

    if (thread) {
        std::cout << "Remote thread successfully created." << std::endl;
        std::cout << "process ID = " << PID << std::endl;
    }

#if 1
    CloseHandle(thread);
    CloseHandle(process);
#endif

    return 0;
}

Upvotes: 1

Views: 3576

Answers (2)

Remy Lebeau
Remy Lebeau

Reputation: 596527

As @adelphus said, you are using LoadLibraryA() when you should be using LoadLibraryW() instead.

More importantly, you are not allocating enough memory in the remote process to hold your wide DLL path string. VirtualAllocEx() and WriteProcessMemory() operate on bytes, not on characters. And you need to make sure that you allocate AND copy the string's null terminator as well. So you need to use (wcslen(dll_path) + 1) * sizeof(WCHAR), or you can use sizeof(dll_path) since it is a static array.

Now, with that said, since the remote thread procedure is LoadLibrary() itself, the thread's exit code will be the return value of LoadLibrary(). If CreateRemoteThread() is successful, you can call WaitForSingleObject() to wait for the thread to terminate, and then call GetExitCodeThread() to get LoadLibrary()'s return value. If it is 0, LoadLibrary() failed, even though the thread itself succeeded.

int main() {

    const WCHAR dll_path[] = L"C:\\myDLL.dll";    
    const int dll_path_size = (wcslen(dll_path) + 1) * sizeof(WCHAR); // or sizeof(dll_path);

    const std::wstring process_name = L"target.exe";

    const DWORD PID = get_PID(process_name);    
    if (!PID)
        what();

    HANDLE process = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, PID);
    if (!process)
        what();

    FARPROC lib = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");
    if (!lib)
        what();

    LPVOID base = VirtualAllocEx(process, 0, dll_path_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (!base)
        what();

    BOOL good = WriteProcessMemory(process, base, dll_path, dll_path_size, 0);
    if (!good)
        what();

    HANDLE thread = CreateRemoteThread(process, 0, 0, reinterpret_cast<LPTHREAD_START_ROUTINE>(lib), base, 0, 0);
    if (!thread)
        what();    

    std::cout << "Remote thread successfully created." << std::endl;
    std::cout << "process ID = " << PID << std::endl;

    WaitForSingleObject(thread, INFINITE);

    DWORD exitCode = 0;
    GetExitCodeThread(thread, &exitCode);

    if (exitCode != 0)
        std::cout << "DLL loaded successfully." << std::endl;
    else
        std::cout << "DLL load failed." << std::endl;

    #if 1
    CloseHandle(thread);
    CloseHandle(process);
    #endif

    return 0;
}

Unfortunately, you will not be able to discover why LoadLibrary() failed, unless you change your remote thread logic to inject an entire function that calls LoadLibrary() and GetLastError() together and then returns the value from GetLastError(). The value from GetLastError() is stored in thread-local storage. One thread cannot read another thread's error code (unless you dig down into the bowls of the OS and read the remote thread's TIB structure directly).

And lastly, be sure to take into account whether the target process is 32bit or 64bit. You cannot inject a 32bit DLL into a 64bit process, and vice versa.

Upvotes: 6

adelphus
adelphus

Reputation: 10326

You are calling LoadLibraryA with a pointer that points to a widechar string. You probably meant LoadLibraryW?

Upvotes: 2

Related Questions