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

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

本文关键字:无效 lpBaseAddress 分配 系统 失败 目标 进程 CreateRemoteThread      更新时间:2023-10-16

我想将一个函数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 succeedn";
    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 calln");
        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 %dn", 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 succeedn"};
    HANDLE hTargetHandle;
    int TargetProcessId = GetTargetProcessId();
    hTargetHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, TargetProcessId);
    if (hTargetHandle == INVALID_HANDLE_VALUE)
    {
        DWORD d = GetLastError();
        printf("openprocess failn");
        printf("the TargetProcessId is: %dn", TargetProcessId);
        printf("the result of getlast is: %dn", 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 failn");
        printf("the last error is:%dn", 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 failn");
        printf("the last error is %dn", 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 failn");
        printf("the last error is:%dn", 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 failn");
        printf("the last error is:%dn", 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("succeedn");
        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仍然崩溃。

谁能告诉我问题在哪里?

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

首先,计算create()函数的字节大小不能保证按您期望的方式工作。它依赖于GetTargetProcessId()位于内存紧接在create()之后,但是编译器/链接器不能保证这一点。它们可能顺序相反,或者它们之间可能有其他代码。 第二,即使您可以将整个函数的原始字节复制到远程进程中,create()函数仍然可能失败,因为:
  1. 开头声明错误。它与LPTHREAD_START_ROUTINE期望的签名不匹配。特别是,它的返回值和调用约定都是错误的,这将导致调用堆栈的管理不当。

  2. create()静态调用CreateFileW()WriteFile(),因此它依赖于kernel32.dll在你的进程地址空间中的加载内存地址,这可能与目标进程地址空间中的加载地址不同。如果启用了地址空间布局随机化,这一点尤为重要。

有更干净和更安全的方法来实现代码注入,比如将create()代码移动到一个单独的DLL中,然后使用CreateRemoteThread()将该DLL加载到目标进程中。当DLL被加载到目标进程中时,它的DLL_PROCESS_ATTACH处理程序可以正常调用它的create()代码,而不需要使用难看的hack。

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

在你自己的进程中使用GetProcAddress()来获得一个指向进程中LoadLibrary()的指针。如果没有启用ASLR,并且kernel32.dll没有在目标进程中重新基于,则可以将LoadLibrary()指针直接传递给CreateRemoteThread()作为线程过程,因为kernel32.dll的加载地址和LoadLibrary()的地址在两个进程中是相同的。

但是,如果涉及到ALSR或rebase,那么您必须在将LoadLibrary()指针传递给CreateRemoteThread()之前对其进行调整。您可以在自己的进程中使用GetModuleHandle()来获取kernel32.dll的加载地址。要获取目标进程中kernel32.dll的加载地址,请使用CreateToolhelp32Snapshot(TH32CS_SNAPMODULE)/Module32First()/Module32Next(),或EnumProcessModules()EnumProcessModulesEx()。如果两个地址不一致,在传递给CreateRemoteThread()之前,先调整LoadLibrary()指针的差值。

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

试试这样写:

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 succeedn";
    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 %un", GetLastError());
        return 0;
    }
    if (!Process32First(hHandle, &pe32))
    {
        if (GetLastError() != ERROR_NO_MORE_FILES)
            printf("Process32First failed with error %un", GetLastError());
    }
    else
    {
        do
        {
            printf("[%u] %wsn", 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 %un", 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 %un", 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 %un", 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 processn", 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: %un", 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 %un", 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 %un", GetLastError());
        CloseHandle(hTargetHandle);
        system("PAUSE");
        return -1;
    }
    if (!WriteProcessMemory(hTargetHandle, lpRemoteBuffer, lpFilename, dwBufferSize, NULL);
    {
        printf("WriteProcessMemory failed with error %un", 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 %un", 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 failedn");
        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("successn");
    system("PAUSE");
    return 0;
}

另一件要注意的事情是32位vs 64位。如果你的应用程序运行在64位系统上,你将不得不编译不同版本的DLL,然后检测目标进程的实际位(你可以使用IsWow64Process()),这样你就可以决定注入哪个版本的DLL。

BOOL TargetIs32Bit(HANDLE hProcess)
{
    BOOL bResult;
    #ifdef _WIN64
    bResult = FALSE;
    IsWow64Process(hTargetHandle, &bResult);
    #else
    typedef void (WINAPI *LPFN_GSI)(SYSTEM_INFO*);
    LPFN_GSI lpGetSystemInfo = GetProcAddress(GetModuleHandleW(L"kernel32"), "GetNativeSystemInfo");
    if (!lpGetSystemInfo) lpGetSystemInfo = &GetSystemInfo;
    SYSTEM_INFO sysInfo = {0};
    lpGetSystemInfo(&sysInfo);
    switch (sysInfo.wProcessorArchitecture)
    {
        case PROCESSOR_ARCHITECTURE_AMD64:
        case PROCESSOR_ARCHITECTURE_IA64:
        {
            bResult = FALSE;
            IsWow64Process(hTargetHandle, &bTargetIs32Bit);
            break;
        }
        default:
            bResult = TRUE;
            break;
    }
    #endif
    return bResult;
}
...
wchar_t lpFilename[MAX_PATH];
if (TargetIs32Bit(hTargetHandle))
    lstrcpy(lpFilename, L"C:\CodeInjectTest32.dll");
else
    lstrcpy(lpFilename, L"C:\CodeInjectTest64.dll");
// inject DLL ...

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

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

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

二进制码|汇编指令

  1. 6A XX ----push XX
  2. E8 XXXXXXXX ----call create ->第二行…
  3. FF25 XXXXXXXX ----jmp XXXXXXXX ->第三行

因此,由于您在目标进程中复制了create()函数,换句话说,您在目标进程中复制了二进制代码,因此在操作跳转到未复制到目标进程的第三行的第二行时可能会出现访问冲突。可能是目标进程损坏的地方。

有一幅图描述了这种情况:

腐败situatioin

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

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

我不知道你是怎么想的:
DWORD dwBufferSize=(DWORD)GetTargetProcessId-(DWORD)create;

将要做,但它可能不会。a)进程ID不是指针;b)如果你编译了一个64位的应用程序,指针将不适合DWORD。

同样,你看起来像是在试图复制create函数的可执行文件——但是数据段呢?(比如CreateFile的地址)