-1

I am using CreateRemoteThread() + LoadLibrary() method to inject code. Everything is OK when I running my injector in my Windows7 64bit OS laptop, and it still work in Windows Server 2012 R2 64bit for some target app.

BUT, in this Windows Server 2012 environment, for some target app, which is old MFC application, the CreateRemoteThread succeeded but the DllMain did not get called and I found the LoadLibrary() seems failed, by using GetExitCodeThread() on the created remote thread.

For the memory to write in target process, I counted the terminating 0 byte.

Also, I already knew the kernel32.dll address are the same for both the Windows 7 and Windows Server 2012, using the method introduced in below URL answer part.

CreateRemoteThread fails,maybe the lpBaseAddress in the target process is invalid,but it is allocated by the system?

The GetExitCodeThread() below got an zero exit code.

    HANDLE hThread = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)addr, arg, NULL, NULL);
    if(hThread == NULL) {
        OutputDebugString(_T("Error: the remote thread could not be created.\n"));
        writeLog("Error: the remote thread could not be created.");
    }
    else {
        DWORD dResult = WAIT_OBJECT_0;
        dResult = WaitForSingleObject(hThread, 1000*3);// the thread may already exited, so do not wait INFINITE
        DWORD dwExitCode = 0;
        GetExitCodeThread(hThread, &dwExitCode);
        if(dwExitCode == 0)
        {
            writeLog("Error: LoadLibraryA failed.");
        }
        else
        {
            OutputDebugString(_T("Success: the remote thread was successfully created.\n"));
            writeLog("Success: the remote thread was successfully created.");
        }
    }

Do you have any idea what should I suspect next?

To summarize, in below diagram, you can see the only fail is only when I run injector on Windows Server 2012 to inject into some old MFC application.

results on the 2 OS

In below diagram, there is the information about HOW old the MFC application is:

dlls the old MFC using

I am trying to provide enough information, kindly let me know if you need some more information.

below is the complete code for inject my dll:

void inject(int procID, char* pszHookDll)
{
    g_nTargetProcId = procID;
    HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, procID);
    g_hTargetProc = process;

    BOOL bInit = SymInitialize(g_hTargetProc, g_sPdbFolder, TRUE);// for analysing the information spy.dll send out

    if(process == NULL) {
        writeLog("Error: the specified process couldn't be found.");
    }
    /*
    * Get address of the LoadLibrary function.
    */
    LPVOID addr = (LPVOID)GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA");
    if(addr == NULL) {
        writeLog("Error: the LoadLibraryA function was not found inside kernel32.dll library.");
    }
    //addr = getProcAddrInTargetProcess(procID, process);

    /*
    * Allocate new memory region inside the process's address space.
    */
    int nBufSize = strlen(pszHookDll)+1;
    LPVOID arg = (LPVOID)VirtualAllocEx(process, NULL, nBufSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
    if(arg == NULL) {
        writeLog("Error: the memory could not be allocated inside the chosen process.");
    }

    /*
    * Write the argument to LoadLibraryA to the process's newly allocated memory region.
    */
    int n = WriteProcessMemory(process, arg, pszHookDll, nBufSize, NULL);
    if(n == 0) {
        writeLog("Error: there was no bytes written to the process's address space.");
    }

    /*
    * Inject our DLL into the process's address space.
    */
    HANDLE hThread = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)addr, arg, NULL, NULL);
    if(hThread == NULL) {
        writeLog("Error: the remote thread could not be created.");
    }
    else {
        DWORD dResult = WAIT_OBJECT_0;
        dResult = WaitForSingleObject(hThread, 1000*3);
        DWORD dwExitCode = 0;
        GetExitCodeThread(hThread, &dwExitCode);
        if(dwExitCode == 0)
        {
            writeLog("Error: LoadLibraryA failed.");
        }
        else
        {
            OutputDebugString(_T("Success: the remote thread was successfully created.\n"));
            writeLog("Success: the remote thread was successfully created.");
        }
    }

    /*
    * Close the handle to the process, becuase we've already injected the DLL.
    */
    //CloseHandle(process);close after symcleanup
}
Gang Li
  • 37
  • 10
  • I'm assuming you are using WriteProcessMemory in conjunction with VirtualAllocEx to write the LoadLibrary argument into the target memory. If that is the case, could you please post the rest of your code? Also, is the test application the same on both systems? Are you sure that the LoadLibrary signature is the same (it's not hooked or anything the likes)? Are you sure that you aren't trying to load a 64 bit compiled DLL into a 32 bit process? If everything else fails, i suggest attaching x64dbg and putting a breakpoint on LoadLibrary(A, whichever you are calling) to see whats up there. – buffy Jun 07 '19 at 08:12
  • Character limit was exceeded there, so new comment it is. Does your DLL have any dependencies that may not be present on the target system? Especially the redistributable packages and whatever else you are using are of very high importance. A lack of those *can* result in a completly silent and error free injection fail under certain circumstances. – buffy Jun 07 '19 at 08:18
  • A likely culprit is passing an incorrect file path to `LoadLibrary()`. Please show the actual value you are passing to it. Worse case, you could allocate a whole function in the remote process that calls `LoadLibrary()` and `GetLastError()` and stores the error code where you can then read it with `ReadProcessMemory()`. Also, when calling `WaitForSingleObject()`, you should be using an infinite timeout. The thread handle is signaled when the remote thread ends. You need to wait for that end before you can call `GetExitCodeThread()`. It is OK if the thread ends before you enter the wait – Remy Lebeau Jun 07 '19 at 08:19
  • I posted my complete source code. I just can not understand: I can inject my dll into both the old-fashion MFC app and new-fashion MFC app on my windows 7 OS, but failed for old-fashion mFC app on the windows Server 2012.. – Gang Li Jun 07 '19 at 08:23
  • 1
    I honestly find dependencies to be a super likely issue here, especially because its an old build. Are you sure that you have setup the proper target platform, all redistributable packages / external dependencies are present or statically linked? you can use /MT to avoid having to rely on external redistributable packages. Code looks good aside from what @RemyLebeau mentioned – buffy Jun 07 '19 at 08:25
  • @buffy I have not the option to rebuild the old MFC application. Do you mean the dependency may be different when my spy dll is Loadlibrary-Ed by an old mfc app and when Loadlibrary-Ed by a new mfc app? On the same computer, I can inject my spy dll into the new mfc app, but failed to do same thing to the old mfc app. I will try to figure out the absent dependent DLLs. – Gang Li Jun 07 '19 at 09:18
  • In that case, i suggest you debug the call to LoadLibrary itself by using a debugger like x64dbg and setting up a breakpoint on LoadLibrary. Then run your injection code and trace through the call to see whats going on. – buffy Jun 07 '19 at 09:20
  • @RemyLebeau thanks. Since I can successfully inject my spy dll into another app, my dll path should be ok, right. Thanks for other suggestions, I will try to inject my whole function into target process if no other easier solution. – Gang Li Jun 07 '19 at 09:22
  • @buffy thanks for your quick response . But it is the production environment where I have this problem. There is no debugger installed there, I have to do remote debug. The debug on production environment itself is dangerous in case get the database stuck. I will keep this the last option. Thank you anyway. – Gang Li Jun 07 '19 at 09:26
  • Another solution would be to use manual mapping, which doesn't rely on LoadLibrary. You can use available injectors and try to invoke them via CLI or use existing code. For example from https://github.com/DarthTon/Xenos – buffy Jun 07 '19 at 09:40
  • @buffy "*Code looks good*" - really? Because I see all kinds of mistakes in the code. Calling `OpenProcess()` with too many rights. Ignoring errors reported by API functions. Memory/resource leaks. – Remy Lebeau Jun 07 '19 at 15:47
  • @buffy maybe because my too low reputation, I was said can not up vote your comment. Thanks for your quick reply and finally solved my problem! – Gang Li Jun 08 '19 at 00:36

1 Answers1

2

I got the reason: it was a dependency problem.

Here are the dependencies of spy.dll:

dependencies

The spy.dll depends on msvcr100d.dll, which is not available by default on my windows Server 2012 environment.

The new MFC app I mentioned was deployed together with msvcr100d.dll on Windows Server 2012, so there was no problem.

Thanks buffy and Remy!!

Gang Li
  • 37
  • 10
  • 1
    Good to see that you solved it! Keep in mind that msvcr100`d`.dll is for debug builds, you probably want to deploy as release. – buffy Jun 11 '19 at 06:30