c++ windows DLL远程注入

这次使用远程线程来进行DLL注入,它提供了最高的灵活性。从根本上说DLL注入要求目标进程中的一个线程使用LoadLibrary来载入我们的DLL。

由于我们不能随意的控制别的线程,因此我们需要在目标进程创建一个新的线程,远程创建线程可以使用 CreateRemoteThread 来进行创建,下面是函数原型。

HANDLE CreateRemoteThread( 
    [in] HANDLE hProcess, 
    [in] LPSECURITY_ATTRIBUTES lpThreadAttributes, 
    [in] SIZE_T dwStackSize, 
    [in] LPTHREAD_START_ROUTINE lpStartAddress, 
    [in] LPVOID lpParameter, 
    [in] DWORD dwCreationFlags, 
    [out] LPDWORD lpThreadId );

除了CreateRemoteThread有一个额外的参数 hProcess 以外其他的都和CreateThread完全相同,hProcess 这个参数表示创建的进程归那个进程所有。

下一步我们需要让该线程调用LoadLibrary函数来载入我们的DLL,通过函数原型发现他们两个非常相似,都是一个参数一个返回值,函数约定都是WINAPI 。

LoadLibrary函数原型
HMODULE WINAPI LoadLibraryA(_In_ LPCSTR lpLibFileName);
HMODULE WINAPI LoadLibraryW(_In_ LPCWSTR lpLibFileName);

线程回调的原型
DWORD WINAPI ThreadProc(_In_ LPVOID lpParameter);

那么只要我们把线程函数回调地址改成LoadLibrary是不是就ok了?如下

使用多字符
    HANDLE hThread = CreateRemoteThread(handle, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibraryA, "c:\\myLib.dll", 0, NULL);

使用宽字符
    HANDLE hThread = CreateRemoteThread(handle, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibraryW, L"c:\\myLib.dll", 0, NULL);

当新线程被创建时会立刻调用LoadLibrary,并传入DLL路径,但是这还存在其他两个问题。

第一 直接把LoadLibrary传给CreateRemoteThread第四个参数,在编译和链接的时候会把函数放到一个二进制文件的导入段中,通过这个二进制的文件来获取实际的跳转地址。当我们在远程直接调用LoadLibrary,会被解析成我们模块导入段的LoadLibrary,所以解析的数据鬼知道是那个地址,这样会造成违规访问。

第二个问题就是 你传入给LoadLibrary的字符串,也就是CreateRemoteThread第五个参数,你传入的是本地空间的字符串指针,所以当LoadLibrary进行操作字符串时也会发生访问违规。

解决第一个问题我们可以使用GetProcAddress函数来获取LoadLibrary的实际地址,因为LoadLibrary函数是在Kernel32.dll里面,系统会把Kernel32.dll映射到每一个进程里,所以以下操作可以获取LoadLibrary在当前线程的真实地址。

    PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandleW(L"Kernel32"), "LoadLibraryW");

    HANDLE hThread = CreateRemoteThread(handle, NULL, 0, pfnThreadRtn, L"c:\\myLib.dll", 0, NULL);

好了,第一个问题解决了还有第二个问题,怎么把字符串放到远程的线程中呢?好在windows为我们提供了在一个进程给另一个进程的地址空间分配一块内存 VirtualAllocEx ,还提供了让我们释放这块内存 VirtualFreeEx。

我们在另一个进程开辟了内存,还需要把字符串复制到开辟的内存中,所以需要WriteProcessMemory函数来把当前进程的字符串写入到另一个进程中。

    wstring dllPath = L"c:\\myLib.dll";

    DWORD dwSize = dllPath.length();
    dwSize = dwSize * sizeof(WCHAR);

    LPWCH pszLibFileRemote = (LPWCH)VirtualAllocEx(handle, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);

    // 把dll路径复制到创建的内存中
    WriteProcessMemory(handle, pszLibFileRemote, dllPath.c_str(), dwSize, NULL);

    PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandleW(L"Kernel32"), "LoadLibraryW");

    HANDLE hThread = CreateRemoteThread(handle, NULL, 0, pfnThreadRtn, "c:\\myLib.dll", 0, NULL);

这个样子就基本完成了。下面补充一下还需要进行获取要注入进程的句柄,可以通过进程id来获取,下面是完整的一个注入函数。

//参数dwProcessId 表示要注入的进程的PID
//参数dllPath 表示需要注入进程的路径
    
BOOL Injection(DWORD dwProcessId, wstring dllPath)
{
    HANDLE handle = OpenProcess(
        PROCESS_QUERY_INFORMATION | // 需要检索有关进程的某些信息
        PROCESS_CREATE_THREAD |     // 需要创建线程。
        PROCESS_VM_OPERATION |      // 需要对进程的地址空间执行操作
        PROCESS_VM_WRITE,           // 需要使用WriteProcessMemory 写入进程中的内存
        FALSE, dwProcessId);
    if (handle == NULL)
    {
        return FALSE;
    }

    DWORD dwSize = dllPath.length();
    dwSize = dwSize * sizeof(WCHAR);

    LPWCH pszLibFileRemote = (LPWCH)VirtualAllocEx(handle, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
    if (NULL == pszLibFileRemote)
    {
        return FALSE;
    }

    // 把dll路径复制到创建的内存中
    if (!WriteProcessMemory(handle, pszLibFileRemote, dllPath.c_str(), dwSize, NULL))
    {
        return FALSE;
    }

    // 获取LoadLibrary所在Kernel32.dll中的实际地址(系统每个进程都会需要Kernel32.dll)
    PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandleW(L"Kernel32"), "LoadLibraryW");

    HANDLE hThread = CreateRemoteThread(handle, NULL, 0, pfnThreadRtn, pszLibFileRemote, 0, NULL);
    if (hThread == NULL)
    {
        return FALSE;
    }

    if (pszLibFileRemote != NULL)
        VirtualFreeEx(handle, pszLibFileRemote, 0, MEM_RELEASE);

    if (hThread != NULL)
        CloseHandle(hThread);

    if (handle != NULL)
        CloseHandle(handle);

    return TRUE;
}

下面是卸载这个dll


BOOL UnInjection(DWORD processId, wstring dllPath)
{
    BOOL bRtn = FALSE;

    HANDLE hThread;
    HANDLE hProcess;
    HANDLE hthSnapshot;
    //远程删除操作步骤
    do
    {
        // 获取指定进程的快照,以及这些进程使用的堆、模块和线程。
        hthSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, processId);
        if (hthSnapshot == INVALID_HANDLE_VALUE) break;

        // 获取模块信息
        MODULEENTRY32W me = { sizeof(me) };
        BOOL bFound = FALSE;
        //检索模块的信息
        BOOL bMoreMods = Module32FirstW(hthSnapshot, &me);
        for (; bMoreMods; bMoreMods = Module32NextW(hthSnapshot, &me))
        {
            //判断是否是我们加载的dll
            bFound = (_wcsicmp(me.szModule, dllPath.c_str()) == 0) || (_wcsicmp(me.szExePath, dllPath.c_str()) == 0);
            if (bFound) break;
        }
        if (!bFound) break;

        hProcess = OpenProcess(
            PROCESS_QUERY_INFORMATION |   // Required by Alpha
            PROCESS_CREATE_THREAD |   // For CreateRemoteThread
            PROCESS_VM_OPERATION |   // For VirtualAllocEx/VirtualFreeEx
            PROCESS_VM_WRITE,             // For WriteProcessMemory
            FALSE, processId);
        if (hProcess == NULL)
            break;

        PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandleW(L"Kernel32"), "FreeLibrary");
        // 在远程进程中创建另一个线程,调用FreeLibrary
        hThread = CreateRemoteThread(hProcess, NULL, 0, pfnThreadRtn, me.modBaseAddr, 0, NULL);
        if (hThread == NULL)
            break;

        WaitForSingleObject(hThread, INFINITE);

        bRtn = TRUE;
    } while (FALSE);

    if (hProcess != NULL)
    {
        CloseHandle(hProcess);
    }

    if (hThread != NULL)
    {
        CloseHandle(hThread);
    }

    if (hthSnapshot != NULL)
    {
        CloseHandle(hthSnapshot);
    }

    return bRtn;
}

参考资料:windows核心编程第五版

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值