CreateRemoteThread失败,可能是目标进程中的lpBaseAddress无效,但它是由系统分配的?

时间:2016-09-01 01:32:56

标签: c++ c windows winapi process

我想将一个函数create()注入目标进程(在本例中为notepad.exe)。这是我的代码:

#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\n";
    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\n");
        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\n", 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\n"};

    HANDLE hTargetHandle;
    int TargetProcessId = GetTargetProcessId();

    hTargetHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, TargetProcessId);
    if (hTargetHandle == INVALID_HANDLE_VALUE)
    {
        DWORD d = GetLastError();
        printf("openprocess fail\n");
        printf("the TargetProcessId is: %d\n", TargetProcessId);
        printf("the result of getlast is: %d\n", 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\n");
        printf("the last error is:%d\n", 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\n");
        printf("the last error is %d\n", 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\n");
        printf("the last error is:%d\n", 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\n");
        printf("the last error is:%d\n", 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\n");
        system("PAUSE");
        return 0;
    }

    system("PAUSE");
    return 1;
}

但是,调用CreateRemoteThread()时目标进程崩溃,并且未调用create()函数。但CreateRemoteThread()的返回值不是NULL。

我在Windows XP Pro SP3上运行此程序。首先,在运行此程序时,它会显示一个对话框,显示DEP已关闭notepad.exe以保护系统,并且notepad.exe崩溃。但我已将PAGE_EXECUTE_READWRITE传递给VirtualAllocEx()的最后一个参数。然后我关闭DEP对话框,然后再次运行程序。这次它没有显示DEP对话框,但notepad.exe仍然崩溃。

谁能告诉我问题出在哪里?

3 个答案:

答案 0 :(得分:3)

这是实现代码注入的一种非常糟糕的方法。

首先,您对create()函数的字节大小的计算无法保证以您期望的方式工作。它依赖GetTargetProcessId()create()之后立即位于内存,但编译器/链接器无法保证这一点。它们可能是相反的顺序,或者它们之间可能有其他代码。

其次,即使您可以像尝试一样将整个函数的原始字节复制到远程进程中,create()函数仍可能会失败,因为:

  1. 开头宣称它是错误的。它与LPTHREAD_START_ROUTINE期望的签名不匹配。特别是,它的返回值和调用约定都是错误的,这将导致调用堆栈管理不善。

  2. create()静态调用CreateFileW()WriteFile(),因此它依赖于您的PROCESS的地址空间中kernel32.dll的加载内存地址,这可能与TARGET PROCESS的地址空间中加载的地址。如果启用了Address Space Layout Randomization,这一点尤为重要。

  3. much cleaner and safer ways来实现代码注入,例如将create()代码移动到单独的DLL中,然后使用CreateRemoteThread()load that DLL into the target process。当DLL加载到目标进程时,其DLL_PROCESS_ATTACH处理程序可以正常调用其create()代码而不使用丑陋的黑客。

    使用LoadLibrary()作为远程线程过程注入DLL,并使用(远程分配的)指向DLL文件名的指针作为线程参数。

    在您自己的流程中使用GetProcAddress()获取指向您流程中LoadLibrary()的指针。如果未启用ASLR,并且kernel32.dll未在目标进程中重新基于,则可以将该LoadLibrary()指针直接传递给CreateRemoteThread()作为线程过程,因为{的加载地址为{ {1}},因此kernel32.dll的地址在两个流程中都是相同的。

    但是,如果涉及ALSR或变基,则必须先调整LoadLibrary()指针,然后再将其传递给LoadLibrary()。您可以使用GetModuleHandle()在自己的流程中获取CreateRemoteThread()的加载地址。要在目标流程中获取kernel32.dll的加载地址,请使用CreateToolhelp32Snapshot(TH32CS_SNAPMODULE) / Module32First() / Module32Next()EnumProcessModules()EnumProcessModulesEx()。如果两个地址不同,请在将kernel32.dll指针传递给LoadLibrary()之前按差异进行调整。

    以这种方式使用CreateRemoteThread()是有效的,因为LoadLibrary()的签名与LoadLibrary()兼容。这还有一个额外的好处,即LPTHREAD_START_ROUTINE的返回值成为线程的退出代码,因此您的应用可以调用LoadLibrary(),等待线程在CreateRemoteThread()退出时终止,然后抓取用于确定注入是否成功的退出代码(但如果LoadLibrary()失败,则无法访问实际的错误代码)。

    尝试这样的事情:

    DLL:

    LoadLibrary()

    EXE:

    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\n";
        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;
    }
    

    要注意的另一件事是32位对64位。如果您的应用程序在64位系统上运行,您将必须编译DLL的单独版本,然后检测目标进程的实际位数(您可以使用IsWow64Process()),以便您可以决定DLL的哪个版本注入。

    #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\n", GetLastError());
            return 0;
        }
    
        if (!Process32First(hHandle, &pe32))
        {
            if (GetLastError() != ERROR_NO_MORE_FILES)
                printf("Process32First failed with error %u\n", GetLastError());
        }
        else
        {
            do
            {
                printf("[%u] %ws\n", 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\n", 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\n", 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\n", 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\n", 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\n", 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\n", 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\n", GetLastError());
            CloseHandle(hTargetHandle);
            system("PAUSE");
            return -1;
        }
    
        if (!WriteProcessMemory(hTargetHandle, lpRemoteBuffer, lpFilename, dwBufferSize, NULL);
        {
            printf("WriteProcessMemory failed with error %u\n", 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\n", 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\n");
            CloseHandle(hTargetHandle);
            system("PAUSE");
            return -1;
        }
    
        /* optional, depending on your DLL's needs...
    
        lpRemoteProcAddress = GetTargetProcAddress(hTargetHandle, L"kernel32.dll", "FreeLibrary");
        if (lpRemoteProcAddress)
        {
            hRemoteThreadHandle = CreateRemoteThread(hTargetHandle, NULL, NULL, lpRemoteProcAddress, (void*)dwExitCode, 0, &dwThreadId);
            if (hRemoteThreadHandle)
            {
                WaitForSingleObject(hRemoteThreadHandle, INFINITE);
                CloseHandle(hRemoteThreadHandle);
            }
        }
        */
    
        CloseHandle(hTargetHandle);
    
        printf("success\n");
        system("PAUSE");
    
        return 0;
    }
    

答案 1 :(得分:0)

首先,没有ALSR问题,几乎所有进程都具有相同的kernel32.dll基地址。我们可以通过尝试Remy Lebeau所说的方法来确认这一点,它使用CreateRemoteThread()创建一个名为的远程线程的LoadLibrary。

其次,如果你想以这种方式使用CreateRemoteThread(),你应该确保你的create()函数中没有其他模块(比如dll)的功能。但是为什么呢?

有关在Windows中调用函数的一些细节。如果使用Olly调试.exe文件,您可能会注意到在调用函数时,Olly可能会显示如下代码:

二进制代码|装配说明

  1. 6A XX ----推XX
  2. E8 XXXXXXXX ----调用create - &gt;第二行 ....
  3. FF25 XXXXXXXX ---- jmp XXXXXXXX - &gt;第三行
  4. 因此,因为您在目标进程中复制了create()函数,换句话说,您在目标进程中复制了二进制代码,所以在操作跳转到第三行的第二行时可能存在访问冲突然而,这并没有复制到目标进程中。也许目标进程已经损坏了。

    有一张描述这种情况的照片:

    corruption situatioin

    我们还应该注意另外一件事,那就是.exe文件中的导入表。我们知道当我们调用DLL导出的函数时,系统将在.idata中的导入表中查找函数地址但是在上面的情况下,当Olly跳转到0x0040100C时,系统将使用原始进程的导入表的地址查找目标的导入表,这也可能会导致每个进程的导入表的地址不同。破坏。

    所以,如果你想使用CreateRemoteThread(),你可以像Remy Lebeau所说的那样使用DLL注入技术,或者直接使用你所做的方法,但一定不要在create中调用其他DLL导出的函数( )功能。

答案 2 :(得分:-1)

我不知道你的想法:

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

将要做,但它可能不会。 a)进程ID不是指针; b)如果你编译了64位应用程序,那么无论如何指针都不适合DWORD。

此外,您看起来正在尝试复制create函数的可执行文件 - 但是数据段呢? (比如CreateFile的地址)。