【干货】Windows进程注入之CreateRemoteThread

函数介绍

百度百科

CreateRemoteThread是一个Windows API函数,它能够创建一个在其它进程地址空间中运行的线程(也称:创建远程线程)

这个函数很好理解,即控制其它进程来运行一个线程!由于是控制其它进程运行所以函数的参数以及需要的数据必须存在目标进程中,否则必须将数据写入到目标进程中,写入数据的过程如下:

  • CreateToolhelp32Snapshot获取目标进程PID
  • AdjustTokenPrivileges提升当前进程Debug权限
  • OpenProcess获取目标进程句柄
  • VirtualAllocEx在目标进程中分配空间
  • WriteProcessMemory将数据写入到目标进程
  • VirtualFreeEx释放空间以及CloseHandle关闭句柄

特别要注意WriteProcessMemory的第三个参数是一个指针类型,指向需要写入的数据的首地址

参数含义

int WINAPI CreateRemoteThread(
HANDLE hProcess,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
/*
hProcess:目标进程句柄
lpThreadAttributes:线程的属性通常设为NULL
dwStackSize:线程栈初始大小,通常设为0表示系统默认
lpStartAddress:线程函数地址,函数指针类型
lpParameter:传递给线程函数的参数
dwCreationFlags:线程创建标志,通常设为0
lpThreadId:返回线程ID,通常设为NULL
*/

特别注意第四个参数的类型是函数指针类型,可以用LPTHREAD_START_ROUTINE表示,也可以typedef自定义函数指针

代码注入

前置技能
遍历Windows进程
遍历Windows进程模块

写代码之前必须要知道很多的操作系统库是伴随系统启动而启动的,它们的基地址固定,譬如Kernel32User32GDINtdll

注入思路
用计算器进程做实验,让计算器进程自己调用MessageBox弹窗
失败过程

  • 由于User32地址固定,即进程间的库函数地址相同,所以先获取当前进程中的MessageBoxA的地址(相当于获取了目标进程中该函数的地址),然后创建一个结构体存放MessageBoxA的四个参数,将该结构体写入到目标进程(不要写入指针,必须写入真实字符,因为当前进程的指针指向的数据在目标进程中是没有的),最后用CreateRemoteThread创建远程线程调用MessageBoxA,最终失败了,失败原因未知!用 OD 附加目标进程发现参数是成功写入的!
  • 使用内联汇编写一段完整运行的代码,写入目标进程再创建远程线程执行,尚未实验!
  • 直接向远程进程空间中写入类似 ShellCode 的字节码,然后创建远程线程执行,尚未实验!

成功思路

  • 由于CreateRemoteThread传入多个参数时无法正确调用,所以我参考网上的一个思路,自写线程函数然后在线程函数中分配函数指针以及函数参数

注意事项
注意整数指针类型的强制转换

完整代码(附带注释)

#include <windows.h>
#include <stdio.h>
#include <Tlhelp32.h>

/* 自定义函数指针 */
typedef int (*MY_MESSAGEBOX)(HWND, LPCTSTR, LPCTSTR, DWORD);

/* 写入的数据 */
typedef struct Param
{
    char caption[22];
    DWORD dwFunc;
} param, *Pparam;

/* 写入的函数代码,主要用于赋予函数指针地址以及定义参数调用方式 */
DWORD threadProc(LPVOID lpdata)
{
    param *remoteparam = (param *)lpdata;
    /* 初始化之前定义函数指针 */
    MY_MESSAGEBOX messagebox;
    messagebox = (MY_MESSAGEBOX)remoteparam->dwFunc;
    messagebox(NULL, remoteparam->caption, remoteparam->caption, 0);
    messagebox(NULL, remoteparam->caption, remoteparam->caption, 0);
    return 0;
}

/* 根据进程名称获取进程的PID */
int getPID(char *target)
{
    PROCESSENTRY32 pe32;
    MODULEENTRY32 me32;
    HANDLE hProcess, hSnapshot_proc, hSnapshot_mod;
    pe32.dwSize = sizeof(pe32);
    hSnapshot_proc = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (Process32First(hSnapshot_proc, &pe32))
    {
        do
        {
            if (!strcmp(pe32.szExeFile, target))
            {
                return pe32.th32ProcessID;
            }
        } while (Process32Next(hSnapshot_proc, &pe32));
    }
    CloseHandle(hSnapshot_proc);
    return 0;
}

/* 获取当前进程的Debug权限 */
BOOL SetProcessPrivilege(char *lpName, BOOL opt)
{
    HANDLE tokenhandle;
    TOKEN_PRIVILEGES NewState;

    if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &tokenhandle))
    {
        LookupPrivilegeValue(NULL, lpName, &NewState.Privileges[0].Luid);
        NewState.PrivilegeCount = 1;
        NewState.Privileges[0].Attributes = opt != 0 ? 2 : 0;
        AdjustTokenPrivileges(tokenhandle, FALSE, &NewState, sizeof(NewState), NULL, NULL);
        CloseHandle(tokenhandle);
        return 1;
    }
    else
    {
        return 0;
    }
}

int main(int argc, char *argv[])
{
    HANDLE hThread, hProcess = NULL;
    int pid;
    param data;
    SetProcessPrivilege("SeDebugPrivilege", 1);
    /* 获取目标进程PID,根据PID打开进程获取目标进程句柄 */
    pid = getPID("calc.exe");
    if (pid)
    {
        hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
    }
    if (hProcess == NULL)
    {
        MessageBox(NULL, "OpenProcess Failed!", "Wrong", MB_OK);
    }
    /* 在目标进程中为线程函数那一段代码分配空间并写入数据 */
    LPVOID remoteFunc = VirtualAllocEx(hProcess, NULL, 4096, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (!WriteProcessMemory(hProcess, remoteFunc, &threadProc, 4096, NULL))
    {
        MessageBox(NULL, "WriteProcessMemory Failed!", "Wrong", MB_OK);
        VirtualFreeEx(hProcess, remoteFunc, 4096, MEM_RELEASE);
        CloseHandle(hProcess);
    }
    /* 获取MessageBoxA的地址赋值给param结构体 */
    data.dwFunc = (DWORD)GetProcAddress(GetModuleHandle("user32"), "MessageBoxA"); /* LPTHREAD_START_ROUTINE */
    strcpy(data.caption, "Inject Code Success!");
    /* 在目标进程中分配结构体的空间并写入数据 */
    LPVOID remoteData = VirtualAllocEx(hProcess, NULL, sizeof(data), MEM_COMMIT, PAGE_READWRITE);
    if (!WriteProcessMemory(hProcess, remoteData, &data, sizeof(data), NULL))
    {
        MessageBox(NULL, "WriteProcessMemory Failed!", "Wrong", MB_OK);
        VirtualFreeEx(hProcess, remoteData, sizeof(data), MEM_RELEASE);
        CloseHandle(hProcess);
    }
    /* 启动远程线程调用写入的线程函数,线程函数会自动获取参数并调用函数指针指向的函数 */
    hThread = CreateRemoteThread(hProcess, NULL, 0, remoteFunc, remoteData, 0, NULL);
    /* 释放空间并关闭句柄 */
    VirtualFreeEx(hProcess, remoteFunc, 4096, MEM_RELEASE);
    VirtualFreeEx(hProcess, remoteData, sizeof(data), MEM_RELEASE);
    if (hThread == NULL)
    {
        MessageBox(NULL, "CreateRemoteThread Failed!", "Wrong", MB_OK);
        CloseHandle(hProcess);
    }
    WaitForSingleObject(hThread, INFINITE);
    CloseHandle(hThread);
    CloseHandle(hProcess);
    SetProcessPrivilege("SeDebugPrivilege", 0);
    return 0;
}

编译命令gcc Inject_code1.c -o Inject_code1

运行截图
先打开计算器

clipboard.png

双击运行程序

clipboard.png

计算器进程成功弹窗

clipboard.png

总结
其实代码注入比直接 DLL 注入要复杂很多,我是强烈建议用 DLL 注入的,这代码注入写得我脑壳疼!QAQ

DLL注入

前置技能
遍历Windows进程
遍历Windows进程模块

写代码之前必须要知道很多的操作系统库是伴随系统启动而启动的,它们的基地址固定,譬如Kernel32User32GDINtdll

注入思路
比起代码注入这注入思路就简单许多了,先获取你想要注入的 DLL 完整路径,然后将路径写入到目标进程空间,最后启动远程线程在目标进程中调用LoadLibraryA就行了

注意事项
需要注意的有两点,一是位数统一,32位程序调32位dll注32位进程,64位同理,当然也有办法让32位程序注64位进程,尚未实验!二是注意释放资源,在目标进程中加载了dll,就应该启动一个远程线程帮忙卸载dll。我为了方便没去卸载dll,采用了注销电脑法!

完整代码
先编译一个 dll,方便之后注入用,dll 代码如下:

#include <windows.h>
#include <tchar.h>

BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD dwReason, LPVOID lpvRevered)
{
    MessageBox(NULL, TEXT("This is DllMain!"), TEXT("Tips"), MB_OK);
    switch (dwReason)
    {
    case DLL_PROCESS_ATTACH:
        /* 只有第一次会进到这里,之后再次Attach不会产生DLL_PROCESS_ATTACH,只会增加DLL的调用次数 */
        MessageBox(NULL, TEXT("Process Load Dll Success!"), TEXT("Tips"), MB_OK);
        break;
    case DLL_PROCESS_DETACH:
        MessageBox(NULL, TEXT("Process Unload Dll Success!"), TEXT("Tips"), MB_OK);
        break;
    case DLL_THREAD_ATTACH:
        MessageBox(NULL, TEXT("Thread load Dll Success!"), TEXT("Tips"), MB_OK);
        break;
    case DLL_THREAD_DETACH:
        MessageBox(NULL, TEXT("Thread Unload Dll Success!"), TEXT("Tips"), MB_OK);
        break;
    }
    return TRUE;
}

使用gcc test.c -shared -o test.dll编译成 dll 文件,下面是注入进程的代码

#include <windows.h>
#include <stdio.h>
#include <Tlhelp32.h>

/* typedef DWORD (*My_LoadLibrary)(char *); */

int getPID(char *target)
{
    PROCESSENTRY32 pe32;
    MODULEENTRY32 me32;
    HANDLE hProcess, hSnapshot_proc, hSnapshot_mod;
    pe32.dwSize = sizeof(pe32);
    hSnapshot_proc = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (Process32First(hSnapshot_proc, &pe32))
    {
        do
        {
            if (!strcmp(pe32.szExeFile, target))
            {
                return pe32.th32ProcessID;
            }
        } while (Process32Next(hSnapshot_proc, &pe32));
    }
    CloseHandle(hSnapshot_proc);
    return 0;
}

BOOL SetProcessPrivilege(char *lpName, BOOL opt)
{
    HANDLE tokenhandle;
    TOKEN_PRIVILEGES NewState;

    if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &tokenhandle))
    {
        LookupPrivilegeValue(NULL, lpName, &NewState.Privileges[0].Luid);
        NewState.PrivilegeCount = 1;
        NewState.Privileges[0].Attributes = opt != 0 ? 2 : 0;
        AdjustTokenPrivileges(tokenhandle, FALSE, &NewState, sizeof(NewState), NULL, NULL);
        CloseHandle(tokenhandle);
        return 1;
    }
    else
    {
        return 0;
    }
}

int main(int argc, char *argv[])
{
    HANDLE hThread, hProcess = NULL;
    DWORD remoteMod;
    LPVOID lpbuffer;
    int pid;
    char dllname[MAX_PATH] = "C:\\MyFiles\\Csrc\\Inject\\test.dll";
    lpbuffer = dllname;
    SetProcessPrivilege("SeDebugPrivilege", 1);
    pid = getPID("calc.exe");
    if (pid)
    {
        hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
    }
    if (hProcess == NULL)
    {
        MessageBox(NULL, "Wrong", "Wrong", MB_OK);
    }
    PTHREAD_START_ROUTINE lib0 = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle("Kernel32"), "LoadLibraryA"); /* LoadLibraryA而不是LoadLibrary */

    LPVOID remoteData = VirtualAllocEx(hProcess, NULL, sizeof(dllname), MEM_COMMIT, PAGE_READWRITE);
    /* lpbuffer或者&dllname */
    if (!WriteProcessMemory(hProcess, remoteData, lpbuffer, sizeof(dllname), NULL))
    {
        MessageBox(NULL, "Wrong", "Wrong", MB_OK);
        VirtualFreeEx(hProcess, remoteData, sizeof(dllname), MEM_RELEASE);
        CloseHandle(hProcess);
    }
    /* CreateRemoteThread的第三个参数是函数指针类型,可以用typedef自定义函数指针 */
    hThread = CreateRemoteThread(hProcess, NULL, 0, lib0, remoteData, 0, NULL);
    VirtualFreeEx(hProcess, remoteData, sizeof(dllname), MEM_RELEASE);
    if (hThread == NULL)
    {
        MessageBox(NULL, "Wrong", "Wrong", MB_OK);
        CloseHandle(hProcess);
    }
    WaitForSingleObject(hThread, INFINITE);
    CloseHandle(hThread);
    CloseHandle(hProcess);
    SetProcessPrivilege("SeDebugPrivilege", 0);
    return 0;
}

编译命令gcc Inject_dll2.c -o Inject_dll2

运行截图
先启动计算器进程

clipboard.png

双击运行程序

clipboard.png

计算器进程成功弹窗

clipboard.png

END

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值