Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
302 views
in Technique[技术] by (71.8m points)

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

I want to inject a function, create(), into a target process (in this case, notepad.exe). Here is my code:

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <TlHelp32.h>

void create(wchar_t wRemoteBuffer[2][60])                  //the function to be injected to the target proccess (notepad.exe)
{
    LPCWSTR lpFileName = wRemoteBuffer[0];
    //wchar_t *lpFileName = L"C:\CodeInjectTest.txt";

    HANDLE hFile = CreateFile(lpFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, NULL, NULL);

    //BYTE bBuffer[] = "if you see this file,then the CodeInjectTest has succeed
";
    LPCWSTR lpBuffer = wRemoteBuffer[1];

    DWORD dNumberOfByteToWrite;
    WriteFile(hFile, lpBuffer, sizeof(lpBuffer), &dNumberOfByteToWrite, NULL);
}

int GetTargetProcessId()       
{
    PROCESSENTRY32 pe32;
    pe32.dwSize = sizeof(pe32);

    DWORD ProcessId;

    HANDLE hHandle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hHandle == INVALID_HANDLE_VALUE)
    {
        printf("fail to call
");
        exit(-1);
    }

    int TargetProcessId;
    const wchar_t *target = L"notepad.exe";

    BOOL bMore = ::Process32First(hHandle, &pe32);
    while(bMore)
    {
        printf("the name is %ws ,the id is %d
", pe32.szExeFile, pe32.th32ProcessID);

        if (wcscmp(pe32.szExeFile, target) == 0)
        {
            TargetProcessId = pe32.th32ProcessID;
            return TargetProcessId;
        }

        bMore = ::Process32Next(hHandle, &pe32);
    }

    return -1;
}

int main()
{
    wchar_t wBuffer[2][60] = {L"C:\CodeInjectTest.txt", L"if you see this file,then the CodeInjectTest has succeed
"};

    HANDLE hTargetHandle;
    int TargetProcessId = GetTargetProcessId();

    hTargetHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, TargetProcessId);
    if (hTargetHandle == INVALID_HANDLE_VALUE)
    {
        DWORD d = GetLastError();
        printf("openprocess fail
");
        printf("the TargetProcessId is: %d
", TargetProcessId);
        printf("the result of getlast is: %d
", d);
        system("PAUSE");
        exit(-1);
    }

    DWORD dwBufferSize = (DWORD)GetTargetProcessId - (DWORD)create;

    LPVOID lpProcBaseAddress = VirtualAllocEx(hTargetHandle, NULL, dwBufferSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    if (lpProcBaseAddress == NULL)
    {
        DWORD d = GetLastError();
        printf("virtualallocex has fail
");
        printf("the last error is:%d
", d);
        system("PAUSE");
        exit(-1);
    }

    BOOL a = WriteProcessMemory(hTargetHandle, lpProcBaseAddress, create, dwBufferSize, NULL);                   //write the function to be injected to the target process
    if (a == 0)
    {
        DWORD d = GetLastError();
        printf("writeprocessmemory has fail
");
        printf("the last error is %d
", d);
        system("PAUSE");
        exit(-1);
    }

    LPVOID lpRemoteBuffer = ::VirtualAllocEx(hTargetHandle, NULL, sizeof(wBuffer[0]) + sizeof(wBuffer[1]), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    if (lpRemoteBuffer == NULL)
    {
        DWORD d = GetLastError();
        printf("buffer virtualallocex has fail
");
        printf("the last error is:%d
", d);
        system("PAUSE");
        exit(-1);
    }

    BOOL b = ::WriteProcessMemory(hTargetHandle, lpRemoteBuffer, wBuffer, sizeof(wBuffer[0]) + sizeof(wBuffer[1]), NULL);        //write the parameter the create function needs to the target process
    if (b == 0)
    {
        DWORD d = GetLastError();
        printf("buffer writeprocessmemory has fail
");
        printf("the last error is:%d
", d);
        system("PAUSE");
        exit(-1);
    }

    DWORD dwThreadId;
    HANDLE hRemoteThreadHandle = CreateRemoteThread(hTargetHandle, NULL, NULL, (LPTHREAD_START_ROUTINE)lpProcBaseAddress, lpRemoteBuffer, 0, &dwThreadId);
    if (hRemoteThreadHandle != NULL)
    {
        WaitForSingleObject(hRemoteThreadHandle, 5000);
        printf("succeed
");
        system("PAUSE");
        return 0;
    }

    system("PAUSE");
    return 1;
}

However, the target process crashes when CreateRemoteThread() is called, and the create() function is not called. But the return value of CreateRemoteThread() is not NULL.

I run this program on Windows XP Pro SP3. At first, when running this program, it displays a dialog box, showing DEP has close notepad.exe to protect the system, and notepad.exe crashes. But I have passed PAGE_EXECUTE_READWRITE to the last parameter of VirtualAllocEx(). Then I close the DEP dialog, and run the program again. This time it does not show the DEP dialog, but notepad.exe still crashes.

Can anyone tell me where the problem is?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This is a VERY BAD way to implement code injection.

For one thing, your calculation of the create() function's byte size is not guaranteed to work the way you are expecting. It relies on GetTargetProcessId() being located in memory immediately after create(), but that is not guaranteed by the compiler/linker. They may be in reverse order, or they may have other code in between them.

Second, even if you could copy the raw bytes of the entire function into the remote process like you are attempting, the create() function will still likely fail because:

  1. it is declared wrong to begin with. It does not match the signature that LPTHREAD_START_ROUTINE is expecting. In particular, its return value and calling convention are both wrong, which will cause mismanagement of the call stack.

  2. create() calls CreateFileW() and WriteFile() statically, so it relies on the loaded memory address of kernel32.dll within YOUR PROCESS's address space, which may be different than the loaded address within the TARGET PROCESS's address space. This is especially important if Address Space Layout Randomization is enabled.

There are much cleaner and safer ways to implement code injection, like moving your create() code into a separate DLL and then using CreateRemoteThread() to load that DLL into the target process. When the DLL is loaded into the target process, its DLL_PROCESS_ATTACH handler can then call its create() code normally without using ugly hacks.

You inject a DLL by using LoadLibrary() as the remote thread procedure, and a (remote-allocated) pointer to the DLL filename as the thread parameter.

Use GetProcAddress() within your own process to get a pointer to LoadLibrary() within your process. If ASLR is not enabled, and kernel32.dll is not re-based in the target process, you can pass that LoadLibrary() pointer directly to CreateRemoteThread() as the thread procedure, since the load address of kernel32.dll, and thus the address of LoadLibrary(), will be the same in both processes.

However, if ALSR or rebasing is involved, then you will have to adjust the LoadLibrary() pointer before passing it to CreateRemoteThread(). You can use GetModuleHandle() to get the load address of kernel32.dll within your own process. To get the load address of kernel32.dll within the target process, use CreateToolhelp32Snapshot(TH32CS_SNAPMODULE)/Module32First()/Module32Next(), or EnumProcessModules() or EnumProcessModulesEx(). If the two addresses differ, adjust the LoadLibrary() pointer by the difference before passing it to CreateRemoteThread().

Using LoadLibrary() in this manner works because the signature of LoadLibrary() is compatible with LPTHREAD_START_ROUTINE. This also has the added benefit that the return value of LoadLibrary() becomes the thread's exit code, so your app can call CreateRemoteThread(), wait for the thread to terminate when LoadLibrary() exits, and then grab the exit code to determine if the injection was successful or not (but you cannot access the actual error code if LoadLibrary() fails).

Try something like this:

DLL:

BOOL create()
{
    HANDLE hFile = CreateFileW(L"C:\CodeInjectTest.txt", GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
        return FALSE;

    const char *lpBuffer = "if you see this file, then the CodeInjectTest has succeed
";
    DWORD dNumberOfByteToWrite;
    WriteFile(hFile, lpBuffer, strlen(lpBuffer), &dNumberOfByteToWrite, NULL);
    CloseHandle(hFile);

    return TRUE;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
{
    if (fdwReason == DLL_PROCESS_ATTACH)
    {
        DisableThreadLibraryCalls(hinstDLL);

        // your injected code here...
        return create();
    }

    return TRUE;
}

EXE:

#include <windows.h>

#include <stdio.h>
#include <stdlib.h>

#include <tlhelp32.h>
#include <psapi.h>
#include <shlwapi.h>

DWORD GetTargetProcessId(const wchar_t *target)
{
    DWORD TargetProcessId = 0;

    PROCESSENTRY32 pe32 = {0};
    pe32.dwSize = sizeof(pe32);

    HANDLE hHandle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hHandle == INVALID_HANDLE_VALUE)
    {
        printf("CreateToolhelp32Snapshot failed with error %u
", GetLastError());
        return 0;
    }

    if (!Process32First(hHandle, &pe32))
    {
        if (GetLastError() != ERROR_NO_MORE_FILES)
            printf("Process32First failed with error %u
", GetLastError());
    }
    else
    {
        do
        {
            printf("[%u] %ws
", pe32.th32ProcessID, pe32.szExeFile);
            if (wcscmp(pe32.szExeFile, target) == 0)
            {
                TargetProcessId = pe32.th32ProcessID;
                break;
            }

            if (!Process32Next(hHandle, &pe32))
            {
                if (GetLastError() != ERROR_NO_MORE_FILES)
                    printf("Process32Next failed with error %u
", GetLastError());
                break;
            }
        }
        while (true);
    }

    CloseHandle(hHandle);

    return TargetProcessId;
}

LPVOID GetTargetProcAddress(HANDLE hProcess, const wchar_t *szWantedModule, const char *szProcName)
{
    // note, there is a very interesting gotcha in a comment to this answer:
    //
    // Would ASLR cause friction for the address with DLL injection?
    // http://stackoverflow.com/a/8569008/65863
    //  
    // "The address of the module may not change but that does not make
    // what the OP is doing safe! Consider the case where your app is
    // running with shims enabled (something which you do not control!)
    // or even the case where some other pieces of software which also
    // performs EAT hooks is running in your process (again, not something
    // you control). In that case, GetProcAddress could return a pointer
    // to a function in another module to what you're expecting/asking,
    // including one which is not loaded in the process which you're going
    // to call CreateRemoteThread on, in that case the target will crash."
    //
    // you probably won't run into this very often, if ever, but you should
    // be aware of it nonetheless!

    HANDLE hLocalMod = GetModuleHandleW(szWantedModule);
    LPVOID lpProcAddress = GetProcAddress(hLocalMod, szProcName);
    if (!lpProcAddress)
    {
        printf("GetProcAddress failed with error %u
", GetLastError());
        return NULL;
    }

    // return lpProcAddress;

    HANDLE hRemoteMod = NULL;
    wchar_t szModName[MAX_PATH];

    HMODULE hMods[1024];
    DWORD cbNeeded;

    if (!EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded))
    {
        printf("EnumProcessModules failed with error %u
", GetLastError());
        return NULL;
    }

    cbNeeded /= sizeof(HMODULE);
    for (DWORD i = 0; i < cbNeeded; ++i)
    {
        if (GetModuleFileNameEx(hProcess, hMods[i], szModName, sizeof(szModName) / sizeof(wchar_t)))
        {
            if (wcscmp(PathFindFileNameW(szModName), szWantedModule) == 0)
            {
                hRemoteMod = hMods[i];
                break;
            }
        }
    }

    if (!hRemoteMod)
    {
        printf("Cannot find %ws in remote process
", szWantedModule);
        return NULL;
    }

    if (hLocalMod != hRemoteMod)
        lpProcAddress = (LPVOID)((INT_PTR)hRemoteMod - (INT_PTR)hLocalMod);

    return lpProcAddress;
}

int main()
{
    DWORD TargetProcessId = GetTargetProcessId(L"notepad.exe");
    if (TargetProcessId == 0)
    {
        system("PAUSE");
        return -1;
    }    

    printf("TargetProcessId: %u
", TargetProcessId);

    HANDLE hTargetHandle = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, TargetProcessId);
    if (!hTargetHandle)
    {
        printf("OpenProcess failed with error %u
", GetLastError());
        system("PAUSE");
        return -1;
    }

    LPVOID lpRemoteProcAddress = GetTargetProcAddress(hTargetHandle, L"kernel32.dll", "LoadLibraryW");
    if (!lpRemoteProcAddress)
    {
        CloseHandle(hTargetHandle);
        system("PAUSE");
        return -1;
    }    

    const wchar_t lpFilename[] = L"C:\CodeInjectTest.dll";
    DWORD dwBufferSize = sizeof(lpFilename);

    LPVOID lpRemoteBuffer = VirtualAllocEx(hTargetHandle, NULL, dwBufferSize, MEM_COMMIT, PAGE_READWRITE);
    if (!lpRemoteBuffer)
    {
        printf("VirtualAllocEx failed with error %u
", GetLastError());
        CloseHandle(hTargetHandle);
        system("PAUSE");
        return -1;
    }

    if (!WriteProcessMemory(hTargetHandle, lpRemoteBuffer, lpFilename, dwBufferSize, NULL);
    {
        printf("WriteProcessMemory failed with error %u
", GetLastError());
        VirtualFreeEx(hTargetHandle, lpRemoteBuffer, 0, MEM_RELEASE);
        CloseHandle(hTargetHandle);
        system("PAUSE");
        return -1;
    }

    DWORD dwThreadId;
    HANDLE hRemoteThreadHandle = CreateRemoteThread(hTargetHandle, NULL, NULL, lpRemoteProcAddress, lpRemoteBuffer, 0, &dwThreadId);
    if (!hRemoteThreadHandle)
    {
        printf("CreateRemoteThread failed with error %u
", GetLastError());
        VirtualFreeEx(hTargetHandle, lpRemoteBuffer, 0, MEM_RELEASE);
        CloseHandle(hTargetHandle);
        system("PAUSE");
        return -1;
    }

    WaitForSingleObject(hRemoteThreadHandle, INFINITE);

    DWORD dwExitCode;
    GetExitCodeThread(hRemoteThreadHandle, &dwExitCode);
    CloseHandle(hRemoteThreadHandle);

    VirtualFreeEx(hTargetHandle, lpRemoteBuffer, 0, MEM_RELEASE);

    if (dwExitCode == 0)
    {
        printf("Remote LoadLibrary failed
");
        CloseHandle(hTargetHandle);
        system("PAUSE");
        return -1;
    }

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...