searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

利用远程线程进行注入

2024-05-22 03:16:21
6
0

通过在加载所需DLL的目标进程中创建一个线程来注入DLL,其思想是在目标进程中创建一个线程,该线程使用要注入的DLL路径调用LoadLibrary函数。

下面的注入器示例演示了这种技术。首先,我们需要检查命令行参数:

int main(int argc, const char* argv[]) {
    if (argc < 3) {
        printf("Usage: injector <pid> <dllpath>\n");
    return 0;
}

注入器需要目标的进程ID和要注入的DLL。接下来,我们打开目标进程的句柄:

HANDLE hProcess = ::OpenProcess(
    PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_CREATE_THREAD,
    FALSE, atoi(argv[1]));
if (!hProcess)
    return Error("Failed to open process");

我们需要相当多的访问掩码位来为这种注入技术获得足够的权限。这意味着某些进程将无法访问。这个注入方法的诀窍是,从二进制的角度来看,LoadLibrary函数和线程的函数本质上是相同的:

HMODULE WINAPI LoadLibrary(PCTSTR);
DWORD WINAPI ThreadFunction(PVOID);

这两个原型都接受一个指针,这就是诀窍所在:我们可以创建一个运行函数 LoadLibrary 的线程!这样做很好,因为 LoadLibrary 的代码已经在目标进程中了(因为它是 kernel32.dll 的一部分,必须加载到每个属于 Windows 子系统的进程中)。

下一项任务是准备要加载的 DLL 路径。路径字符串本身必须放在目标进程中,因为 LoadLibrary 将在目标进程中执行。为此,我们可以使用 VirtualAllocEx 函数:

void* buffer = ::VirtualAllocEx(hProcess, nullptr, 1 << 12,
    MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (!buffer)
    return Error("Failed to allocate buffer in target process");

使用VirtualAllocEx需要PROCESS_VM_OPERATION访问掩码,我们在OpenProcess时请求。我们分配了一个4 KB的缓冲区,这是多余的,但即使我们指定的缓冲区小于4 KB,它也会四舍五入到4 KB。注意,返回的指针在调用者的进程中没有任何意义——它是在目标进程中分配的地址。

接下来,我们需要用WriteProcessMemory将DLL路径复制到分配的缓冲区:

if (!::WriteProcessMemory(hProcess, buffer,
    argv[2], ::strlen(argv[2]) + 1, nullptr))
    return Error("Failed to write to target process");

使用WriteProcessMemory要求进程句柄具有PROCESS_VM_WRITE访问掩码,它确实具有。一切就绪,是时候创建远程线程了:

DWORD tid;
HANDLE hThread = ::CreateRemoteThread(hProcess, nullptr, 0,
    (LPTHREAD_START_ROUTINE)::GetProcAddress(::GetModuleHandle(L"kernel32"), "LoadLibraryA"),
    buffer, 0, &tid);
if (!hThread)
    return Error("Failed to create remote thread");

CreateRemoteThread接受目标进程句柄(必须具有PROCESS_CREATE_THREAD访问掩码)、NULL安全描述符、默认堆栈大小和线程的启动例程。这就是我们利用LoadLibrary和线程函数的二进制等价的地方。GetProcAddress调用用于动态定位LoadLibraryA的地址,利用它与当前进程中的地址相同这一事实。这是该技术的关键——不需要将代码复制到目标进程中。线程的参数是buffer——我们复制DLL路径的目标进程中的地址。

就是这样。需要注意的重要一点是,注入的DLL必须与目标进程具有相同的位数。

剩下的就是做一些清理工作:

printf("Thread %u created successfully!\n", tid);
if (WAIT_OBJECT_0 == ::WaitForSingleObject(hThread, 5000))
    printf("Thread exited.\n");
else
    printf("Thread still hanging around...\n");

// be nice
::VirtualFreeEx(hProcess, buffer, 0, MEM_RELEASE);

::CloseHandle(hThread);
::CloseHandle(hProcess);

等待线程终止并不是强制性的,但是我们需要在调用VirtualFreeEx之前给它一些时间来移除用VirtualAllocEx完成的分配。这是礼貌的,但不是绝对必要的。我们不妨将提交的4 KB保留在目标进程中。

下面是一个命令行测试示例:

C:\>Injector.exe 44532 C:\Temp\Injected.dll

必须指定DLL的完整路径,因为加载规则是从目标进程的角度出发,而不是从调用方的角度出发。注入DLL的DllMain显示了一个简单的消息框:

BOOL WINAPI DllMain(HMODULE hModule, DWORD reason, PVOID) {
    switch (reason) {
        case DLL_PROCESS_ATTACH:
            wchar_t text[128];
            ::StringCchPrintf(text, _countof(text),
                L"Injected into process %u",
                ::GetCurrentProcessId());
            ::MessageBox(nullptr, text, L"Injected.Dll", MB_OK);
            break;
    }
    return TRUE;
}

完整代码如下:

// Injector.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

#include <Windows.h>
#include <stdio.h>

int Error(const char* msg) {
    printf("%s (%u)\n", msg, ::GetLastError());
    return 1;
}

int main(int argc, const char* argv[]) {
    if (argc < 3) {
        printf("Usage: injector <pid> <dllpath>\n");
        return 0;
    }

    HANDLE hProcess = ::OpenProcess(PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_CREATE_THREAD,
        FALSE, atoi(argv[1]));
    if (!hProcess)
        return Error("Failed to open process");

    void* buffer = ::VirtualAllocEx(hProcess, nullptr, 1 << 12, 
        MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
    if (!buffer)
        return Error("Failed to allocate buffer in target process");

    if (!::WriteProcessMemory(hProcess, buffer, argv[2], ::strlen(argv[2]) + 1, nullptr))
        return Error("Failed to write to target process");

    DWORD tid;
    HANDLE hThread = ::CreateRemoteThread(hProcess, nullptr, 0, 
        (LPTHREAD_START_ROUTINE)::GetProcAddress(::GetModuleHandle(L"kernel32"), "LoadLibraryA"), 
        buffer, 0, &tid);
    if (!hThread)
        return Error("Failed to create remote thread");

    printf("Thread %u created successfully!\n", tid);
    if (WAIT_OBJECT_0 == ::WaitForSingleObject(hThread, 5000))
        printf("Thread exited.\n");
    else
        printf("Thread still hanging around...\n");
    
    // be nice
    ::VirtualFreeEx(hProcess, buffer, 0, MEM_RELEASE);

    ::CloseHandle(hThread);
    ::CloseHandle(hProcess);
    
    return 0;
}

0条评论
0 / 1000
王****明
5文章数
2粉丝数
王****明
5 文章 | 2 粉丝
原创

利用远程线程进行注入

2024-05-22 03:16:21
6
0

通过在加载所需DLL的目标进程中创建一个线程来注入DLL,其思想是在目标进程中创建一个线程,该线程使用要注入的DLL路径调用LoadLibrary函数。

下面的注入器示例演示了这种技术。首先,我们需要检查命令行参数:

int main(int argc, const char* argv[]) {
    if (argc < 3) {
        printf("Usage: injector <pid> <dllpath>\n");
    return 0;
}

注入器需要目标的进程ID和要注入的DLL。接下来,我们打开目标进程的句柄:

HANDLE hProcess = ::OpenProcess(
    PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_CREATE_THREAD,
    FALSE, atoi(argv[1]));
if (!hProcess)
    return Error("Failed to open process");

我们需要相当多的访问掩码位来为这种注入技术获得足够的权限。这意味着某些进程将无法访问。这个注入方法的诀窍是,从二进制的角度来看,LoadLibrary函数和线程的函数本质上是相同的:

HMODULE WINAPI LoadLibrary(PCTSTR);
DWORD WINAPI ThreadFunction(PVOID);

这两个原型都接受一个指针,这就是诀窍所在:我们可以创建一个运行函数 LoadLibrary 的线程!这样做很好,因为 LoadLibrary 的代码已经在目标进程中了(因为它是 kernel32.dll 的一部分,必须加载到每个属于 Windows 子系统的进程中)。

下一项任务是准备要加载的 DLL 路径。路径字符串本身必须放在目标进程中,因为 LoadLibrary 将在目标进程中执行。为此,我们可以使用 VirtualAllocEx 函数:

void* buffer = ::VirtualAllocEx(hProcess, nullptr, 1 << 12,
    MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (!buffer)
    return Error("Failed to allocate buffer in target process");

使用VirtualAllocEx需要PROCESS_VM_OPERATION访问掩码,我们在OpenProcess时请求。我们分配了一个4 KB的缓冲区,这是多余的,但即使我们指定的缓冲区小于4 KB,它也会四舍五入到4 KB。注意,返回的指针在调用者的进程中没有任何意义——它是在目标进程中分配的地址。

接下来,我们需要用WriteProcessMemory将DLL路径复制到分配的缓冲区:

if (!::WriteProcessMemory(hProcess, buffer,
    argv[2], ::strlen(argv[2]) + 1, nullptr))
    return Error("Failed to write to target process");

使用WriteProcessMemory要求进程句柄具有PROCESS_VM_WRITE访问掩码,它确实具有。一切就绪,是时候创建远程线程了:

DWORD tid;
HANDLE hThread = ::CreateRemoteThread(hProcess, nullptr, 0,
    (LPTHREAD_START_ROUTINE)::GetProcAddress(::GetModuleHandle(L"kernel32"), "LoadLibraryA"),
    buffer, 0, &tid);
if (!hThread)
    return Error("Failed to create remote thread");

CreateRemoteThread接受目标进程句柄(必须具有PROCESS_CREATE_THREAD访问掩码)、NULL安全描述符、默认堆栈大小和线程的启动例程。这就是我们利用LoadLibrary和线程函数的二进制等价的地方。GetProcAddress调用用于动态定位LoadLibraryA的地址,利用它与当前进程中的地址相同这一事实。这是该技术的关键——不需要将代码复制到目标进程中。线程的参数是buffer——我们复制DLL路径的目标进程中的地址。

就是这样。需要注意的重要一点是,注入的DLL必须与目标进程具有相同的位数。

剩下的就是做一些清理工作:

printf("Thread %u created successfully!\n", tid);
if (WAIT_OBJECT_0 == ::WaitForSingleObject(hThread, 5000))
    printf("Thread exited.\n");
else
    printf("Thread still hanging around...\n");

// be nice
::VirtualFreeEx(hProcess, buffer, 0, MEM_RELEASE);

::CloseHandle(hThread);
::CloseHandle(hProcess);

等待线程终止并不是强制性的,但是我们需要在调用VirtualFreeEx之前给它一些时间来移除用VirtualAllocEx完成的分配。这是礼貌的,但不是绝对必要的。我们不妨将提交的4 KB保留在目标进程中。

下面是一个命令行测试示例:

C:\>Injector.exe 44532 C:\Temp\Injected.dll

必须指定DLL的完整路径,因为加载规则是从目标进程的角度出发,而不是从调用方的角度出发。注入DLL的DllMain显示了一个简单的消息框:

BOOL WINAPI DllMain(HMODULE hModule, DWORD reason, PVOID) {
    switch (reason) {
        case DLL_PROCESS_ATTACH:
            wchar_t text[128];
            ::StringCchPrintf(text, _countof(text),
                L"Injected into process %u",
                ::GetCurrentProcessId());
            ::MessageBox(nullptr, text, L"Injected.Dll", MB_OK);
            break;
    }
    return TRUE;
}

完整代码如下:

// Injector.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

#include <Windows.h>
#include <stdio.h>

int Error(const char* msg) {
    printf("%s (%u)\n", msg, ::GetLastError());
    return 1;
}

int main(int argc, const char* argv[]) {
    if (argc < 3) {
        printf("Usage: injector <pid> <dllpath>\n");
        return 0;
    }

    HANDLE hProcess = ::OpenProcess(PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_CREATE_THREAD,
        FALSE, atoi(argv[1]));
    if (!hProcess)
        return Error("Failed to open process");

    void* buffer = ::VirtualAllocEx(hProcess, nullptr, 1 << 12, 
        MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
    if (!buffer)
        return Error("Failed to allocate buffer in target process");

    if (!::WriteProcessMemory(hProcess, buffer, argv[2], ::strlen(argv[2]) + 1, nullptr))
        return Error("Failed to write to target process");

    DWORD tid;
    HANDLE hThread = ::CreateRemoteThread(hProcess, nullptr, 0, 
        (LPTHREAD_START_ROUTINE)::GetProcAddress(::GetModuleHandle(L"kernel32"), "LoadLibraryA"), 
        buffer, 0, &tid);
    if (!hThread)
        return Error("Failed to create remote thread");

    printf("Thread %u created successfully!\n", tid);
    if (WAIT_OBJECT_0 == ::WaitForSingleObject(hThread, 5000))
        printf("Thread exited.\n");
    else
        printf("Thread still hanging around...\n");
    
    // be nice
    ::VirtualFreeEx(hProcess, buffer, 0, MEM_RELEASE);

    ::CloseHandle(hThread);
    ::CloseHandle(hProcess);
    
    return 0;
}

文章来自个人专栏
Windows
5 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0