0


Windows 动态注入(远程线程、消息钩子、APC)

Windows PC端动态注入(远程线程、消息钩子、APC)

说明:实现注入进程“FlappyBird.exe”,实现弹窗。完整代码在文末。

文章目录

远程线程注入

注入DLL

首先需要一个事先准备好的DLL文件testDLL.dll,用vs2019创建一个DLL工程,在程序入口调用MessageBoxA()。

远程线程DLL注入的基本原理是利用Windows提供的API函数**CreateRemoteThread()**,实现在另外一个进程中注入一个线程,后续只要在线程体中执行加载testDLL.dll的操作,目标进程就会执行testDLL.dll中的代码了。

一个比较关键的问题是创建的远程线程是在目标进程中执行,无法调用本地函数,因此要利用系统API实现加载dll文件的操作。Win32程序在运行时都会加载kernel32.dll,而Windows默认同一个系统中dll的文件加载位置是固定的,因此kernel32.dll中的导出函数在任何进程的地址空间中的位置是固定的。动态加载dll文件需要系统API **LoadLibraryA()**,这个函数正好是kernel32.dll的导出函数。因此我们只需要在注射器程序中获取LoadLibraryA()的地址,令创建的远程线程执行LoadLibraryA(),传入参数testDLL.dll的路径,即可实现远程线程DLL注入。

具体实现步骤及部分源码如下(所有完整代码见文末):

1、根据进程名(“FlappyBird.exe”)查找进程ID

自己实现函数FindProcess(),入口参数:进程名、进程ID(出参);步骤如下:

调用CreateToolhelp32Snapshot()获取进程快照;传入参数Th32CS_SNAPPROCESS用于指定“在快照中包含系统中所有的进程”

调用**Process32First()、Process32Next()**遍历进程信息,对进程名进行字符串比较直至与目标进程名相等。

//由进程名获取进程ID
bool FindProcess(const wchar_t* processName, DWORD& dwProcess) {
    PROCESSENTRY32 pe32;  
    pe32.dwSize = sizeof(PROCESSENTRY32);      
    //获取进程快照
    HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);     
    if (hProcessSnap == INVALID_HANDLE_VALUE) {
        return false;
    }
    if (Process32First(hProcessSnap, &pe32)) {
        do {
            if (wcscmp(pe32.szExeFile, processName) == 0) {
                dwProcess = pe32.th32ProcessID;
                break;
            }
        } while (Process32Next(hProcessSnap, &pe32));
    }
    CloseHandle(hProcessSnap);
    if(dwProcess == 0) {
        return false;
    }
    return true;
}

2、调用OpenProcess()获取进程句柄,传入参数dwProcess(进程ID);调用VirtualAllocEx()在目标进程中为testDLL.dll的路径(字符串)分配内存空间(用于之后作为LoadLibraryA的参数);调用**WriteProcessMemory()**将testDLL.dll的路径写入内存。

调用LoadLibrary()加载动态链接库“kernel32.dll”,调用GetProcAddress()获取“LoadLibraryA”的地址。

调用CreateRemoteThread()创建远程线程,传入参数LoadLibraryA()地址和存放testDLL.dll路径的内存地址。等待线程结束关闭句柄,释放内存空间。

//DLL远程线程注入
void CreateRemoteThread_Inject(){
    DWORD dwProcess = 0;
    char myDLL[] = "C:\\testDLL.dll";
    //查找进程FlappyBird.exe
    if(FindProcess(L"FlappyBird.exe", dwProcess)){
        HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcess);    //打开进程
        LPVOID allocatedMem = VirtualAllocEx(hProcess, NULL, sizeof(myDLL), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);  //在进程中分配内存
        WriteProcessMemory(hProcess, allocatedMem, myDLL, sizeof(myDLL), NULL); //写入DLL路径
        HMODULE hModule = LoadLibrary(L"kernel32.dll");                //获取kernel32.dll模块句柄
        //获取LoadLibraryA函数地址
        LPTHREAD_START_ROUTINE pfnStarAddress = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "LoadLibraryA");
        if(pfnStarAddress == NULL){
            cout << "GetProcAddress failed" << endl;
            return;
        }
        HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, NULL, pfnStarAddress, allocatedMem, NULL, NULL);  //创建远程线程
        if(hRemoteThread == NULL){
            cout << "CreateRemoteThread failed" << endl;
            return;
        }
        WaitForSingleObject(hRemoteThread, INFINITE);  //等待远程线程结束
        CloseHandle(hRemoteThread);                                
        VirtualFreeEx(hProcess, allocatedMem, 0, MEM_FREE);
    }
    return;
}

注入Shellcode

DLL注入实现虽然简单,但是会在目标进程加载一个模块,已经能够被多数杀软主动拦截;我们用**CreateRemoteThread()**函数实现DLL远程线程注入,也可以自己编写Shellcode实现注入,相对DLL来说隐蔽性更强。

注入shellcode的难点是,由于注入的代码要“写入”其他进程空间当中,因此不能使用任何全局变量、不能调用本地定义的函数、不能调用一些库函数等等。当然,shellcode使用栈空间的局部变量、使用系统API(kernel32.dll)都是没有问题的,如果使用其他dll库的函数可以用kernel32.dll导出函数**LoadLibraryA()加载对应的dll后,再使用GetProcAddress()**获取函数地址。

远程线程注入shellcode的原理与注入DLL原理类似,都是使用**OpenProcess + VirtualAllocEx + WriteProcessMemory + CreateRemoteThread **的框架,关键在于shellcode的编写,以在目标进程执行MessageBox()为例,具体步骤和部分代码如下:

1、定义线程参数结构体,用于接收API(LoadLibraryA()和GetProcAddress()的地址)和参数(加载的dll名、调用的API、API的参数);定义函数指针;

//该结构体用于接收API和4个字符串
typedef struct _THREAD_PARAM {
    FARPROC pFunc[2];
    char szBuf[4][128];
} THREAD_PARAM, *PTHREAD_PARAM;
typedef HMODULE (WINAPI *PFLOADLIBRARYA)    
(
    LPCSTR lpLibFileName
);  //定义LoadLibraryA函数指针
typedef FARPROC (WINAPI *PFGETPROCADDRESS)
(
    HMODULE hModule,
    LPCSTR lpProcName
); //定义GetProcAddress函数指针
typedef int (WINAPI *PFMESSAGEBOXA)
(
    HWND hWnd,
    LPCSTR lpText,
    LPCSTR lpCaption,
    UINT uType
); //定义MessageBoxA函数指针

2、编写shellcode,以上述结构体类型作为参数,依次调用API,实现调用MessageBoxA();

//线程函数
//未直接调用相关API和未直接定义使用字符串,而通过THREAD_PARAM结构体以线程参数的形式传递使用
DWORD WINAPI ThreadProc(LPVOID lpParam)
{   
    PTHREAD_PARAM pParam = (PTHREAD_PARAM)lpParam;
    HMODULE hMod = NULL;
    FARPROC pFunc = NULL;
    hMod = ((PFLOADLIBRARYA)pParam->pFunc[0])(pParam->szBuf[0]);        //调用LoadLibraryA函数
    if (hMod == NULL)
    {
        return 1;
    }
    pFunc = ((PFGETPROCADDRESS)pParam->pFunc[1])(hMod, pParam->szBuf[1]);   //获取MessageBoxA函数地址
    if (pFunc == NULL)
    {
        return 1;
    }
    ((PFMESSAGEBOXA)pFunc)(NULL, pParam->szBuf[2], pParam->szBuf[3], 0);    //调用MessageBoxA函数
    return 0;
}

3、实现提权函数EnableDebugPrivilege()

bool EnableDebugPrivilege() {
    HANDLE hToken;
    TOKEN_PRIVILEGES tp;
    //打开进程令牌
    if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
        cout<<"OpenProcessToken failed"<<endl;
        return false;
    }
    //获取LUID
    if(!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid)) {
        cout<<"LookupPrivilegeValue failed"<<endl;
        return false ;
    }
    //设置权限
    tp.PrivilegeCount = 1;  //只设置一个权限
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;     //设置权限为启用,如果为SE_PRIVILEGE_REMOVED则为禁用
    //修改进程令牌,提升权限,使得能够访问受保护的进程,如系统进程,其他用户进程等
    if(!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL)) {
        cout<<"AdjustTokenPrivileges failed"<<endl;
        return false;
    }
    return true;
}

4、实现注入

准备好参数,使用CreatRemoteThread()框架实现注入,注意为代码申请的内存必须设置为可读可写可执行,注意将参数和shellcode都写入目标进程。

void CreateRemoteThread_shellcode()
{
    EnableDebugPrivilege(); //提升权限
    DWORD dwProcess = 0;
    //查找进程FlappyBird.exe
    if(FindProcess(L"FlappyBird.exe", dwProcess)){
        HMODULE hMod = NULL;
        THREAD_PARAM param = {0,};  //定义线程结构体变量
        HANDLE hProcess = NULL;
        HANDLE hThread = NULL;
        LPVOID pRemoteBuf[2] = {0,};
        DWORD dwSize = 0;

        hMod = GetModuleHandleA("kernel32.dll");

        //设置THREAD_PARAM结构体
        //加载到所有进程的kernel32.dll的地址都相同,因此从本进程获取的API与在目标进程中获取的API地址是一样的
        param.pFunc[0] = GetProcAddress(hMod, "LoadLibraryA");
        param.pFunc[1] = GetProcAddress(hMod, "GetProcAddress");
        strcpy_s(param.szBuf[0], "user32.dll");
        strcpy_s(param.szBuf[1], "MessageBoxA");
        strcpy_s(param.szBuf[2], "shellcode Success");
        strcpy_s(param.szBuf[3], "inject");
        
        hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcess);
        if (hProcess == NULL)
        {
            cout<<"OpenProcess failed"<<endl;
            return;
        }
        //分配内存
        dwSize = sizeof(THREAD_PARAM);
        pRemoteBuf[0] = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
        if (pRemoteBuf[0] == NULL)
        {
            cout<<"VirtualAllocEx failed"<<endl;
            return;
        }
        //将线程参数写入目标进程
        if (!WriteProcessMemory(hProcess, pRemoteBuf[0], (LPVOID)&param, dwSize, NULL))
        {
            cout<<"WriteProcessMemory failed"<<endl;
            return;
        }
        dwSize = (DWORD)CreateRemoteThread_shellcode - (DWORD)ThreadProc;  //计算shellcode大小
        pRemoteBuf[1] = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
        if (pRemoteBuf[1] == NULL)
        {
            cout<<"VirtualAllocEx failed"<<endl;
            return;
        }
        //将shellcode写入目标进程
        if (!WriteProcessMemory(hProcess, pRemoteBuf[1], (LPVOID)ThreadProc, dwSize, NULL))
        {
            cout<<"WriteProcessMemory failed"<<endl;
            return;
        }
        //创建远程线程
        hThread = CreateRemoteThread(
            hProcess, 
            NULL, 
            0, 
            (LPTHREAD_START_ROUTINE)pRemoteBuf[1], //线程函数地址
            pRemoteBuf[0],                     //线程参数地址
            0, NULL
        );
        if (hThread == NULL)
        {
            cout<<"CreateRemoteThread failed"<<endl;
            return;
        }
        WaitForSingleObject(hThread, INFINITE);
        CloseHandle(hThread);
        CloseHandle(hProcess);
    }
}

消息钩子注入DLL

消息钩子注入原理是利用Windows提供的**SetWindowsHookEx()**函数,它可以拦截进程的消息到指定的DLL中导出的函数,目标进程就会自动加载我们指定的DLL,利用这个特性可以实现消息钩子注入。

具体实现步骤和部分源码如下:

1、编写一个testDLL.dll用于Hook,在其中显式导出一个函数**NextHook()**;

//导出函数
extern "C" __declspec(dllexport) int NextHook(int code, WPARAM wParam, LPARAM lParam){
    return CallNextHookEx(NULL, code, wParam, lParam);
}

2、在注射器中首先调用FindWindow()查找窗口“FlappyBird”获取窗口句柄;调用GetWindowThreadProcessId()获取进程ID和线程ID;调用LoadLibraryEx()加载testDLL.dll到自身进程,传入参数DONT_RESOLVE_DLL_REFERENCES用于指定“不对DLL初始化”;调用**GetProcAddress()获取钩子函数NextHook()**的地址;

int SetWindowHookEx_inject() {
    HWND hwnd = FindWindow(NULL, L"FlappyBird"); //查找窗口
    if (hwnd == NULL) {
        cout << "FindWindow failed" << endl;
        return 1;
    }
    DWORD pid = NULL;
    DWORD tid = GetWindowThreadProcessId(hwnd, &pid); //获取进程ID
    if (tid == NULL) {
        cout << "GetWindow tid/pid failed" << endl;
        return 1;
    }
    
    HMODULE dll = LoadLibraryEx(L"C:\\testDLL.dll", NULL, DONT_RESOLVE_DLL_REFERENCES); //加载DLL
    if (dll == NULL) {
        cout << "LoadLibraryEx DLL failed" << endl;
        return 1;
    }
    
    HOOKPROC addr = (HOOKPROC)GetProcAddress(dll, "NextHook"); //获取钩子函数地址
    if (addr == NULL) {
        cout << "GetProcAddress failed" << endl;
        return 1;
    }
    
    HHOOK handle = SetWindowsHookEx(WH_GETMESSAGE, addr, dll, tid); //设置钩子
    if (handle == NULL) {
        cout << "SetWindowsHookEx failed" << endl;
        return EXIT_FAILURE;
    }
    
    PostThreadMessage(tid, WM_NULL, 0, 0); //发送消息,触发钩子
    cout<<"SetWindowsHookEx_inject success"<<endl;
    cout<<"Press any key to unhook"<<endl;
    getchar();

    BOOL unhook = UnhookWindowsHookEx(handle); //卸载钩子
    if (unhook == NULL) {
        cout << "UnhookWindowsHookEx failed" << endl;
        return EXIT_FAILURE;
    }
    return 0;
}

APC注入DLL

APC全称Asynchronous Procedure Call,叫异步过程调用,是指函数在特定现场中被异步执行,在操作系统中,APC是一种并发机制。

APC注入实现原理核心是利用QueueUserApc()这个API添加制定的回调函数到目标线程的APC队列中,系统会产生一个软中断,在线程下一次被调度/唤醒的时候,就会执行回调函数,因此我们只需要令LoadLibraryW作为回调函数,并且传入参数dll路径,即可实现注入。

当然APC注入是有条件的,用户态下的APC请求想要执行,必须等待线程进入“Alertable”状态,而只有当线程调用特定函数(SleepEx, SignalObjectAndWait, MsgWaitForMultipleObjectsEx, WaitForMultipleObjectsEx或WaitForSingleObjectEx)时,才会进入Alertable状态,所以为了应对这种苛刻的条件,提高注入成功的机率同时缩短等待时间,我们需要遍历进程的所有线程,并对每一个线程进行APC注入。

具体实现步骤和部分源码如下:

1、根据进程名(“FlappyBird.exe”)查找进程ID并且获取所有的线程ID,获取线程ID的方法与查找进程ID类似;

//根据进程名获取进程ID及其所有线程ID
bool FindProcess2(const wchar_t* processName, DWORD& dwProcess, vector<DWORD>& dwThreads) {
    PROCESSENTRY32 pe32;
    pe32.dwSize = sizeof(PROCESSENTRY32);
    HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);  //创建进程快照
    if (hProcessSnap == INVALID_HANDLE_VALUE) {
        cout << "CreateToolhelp32Snapshot failed" << endl;
        return false;
    }
    if (!Process32First(hProcessSnap, &pe32)) {     //获取第一个进程信息
        cout << "Process32First failed" << endl;
        CloseHandle(hProcessSnap);
        return false;
    }
    do {
        if (wcscmp(pe32.szExeFile, processName) == 0) {
            dwProcess = pe32.th32ProcessID;
            break;
        }
    } while (Process32Next(hProcessSnap, &pe32));
    CloseHandle(hProcessSnap);
    if (dwProcess == NULL) {
        cout << "FindProcess failed" << endl;
        return false;
    }
    THREADENTRY32 te32;
    te32.dwSize = sizeof(THREADENTRY32);
    HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);    //创建线程快照
    if (hThreadSnap == INVALID_HANDLE_VALUE) {
        cout << "CreateToolhelp32Snapshot failed" << endl;
        return false;
    }
    if (!Thread32First(hThreadSnap, &te32)) {
        cout << "Thread32First failed" << endl;
        CloseHandle(hThreadSnap);
        return false;
    }
    do {
        if (te32.th32OwnerProcessID == dwProcess) {
            dwThreads.push_back(te32.th32ThreadID);     //获取目标进程的所有线程ID
        }
    } while (Thread32Next(hThreadSnap, &te32));
    CloseHandle(hThreadSnap);
    if (dwThreads.size() == 0) {
        cout << "FindThread failed" << endl;
        return false;
    }
    return true;
}

2、调用OpenProcess()获取进程句柄,调用VirtuealAllocEx()为参数dll路径远程申请内存,调用WriteProcessMemory()写入dll路径;遍历所有线程,插入APC;具体插入步骤为:调用OpenThread()获取线程句柄,调用GetProcAddress()和GetModuleHandle()获取LoadLibraryW()地址,调用QueueUserAPC()实现插入APC。

void APC_Inject(){
    DWORD pid;
    vector<DWORD> tids;     //需要获取进程所有的线程ID
    if (FindProcess2(L"FlappyBird.exe", pid, tids)) {
        HANDLE hProcess = OpenProcess(PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, pid);
        if (hProcess == NULL) {
            cout << "OpenProcess failed" << endl;
            return;
        }
        auto p = VirtualAllocEx(hProcess, NULL, 1<<12, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); //在目标进程中申请内存
        wchar_t buffer[] = L"C:\\testDLL.dll";
        WriteProcessMemory(hProcess, p, buffer, sizeof(buffer), NULL); //将DLL路径写入目标进程中
        for(const auto& tid: tids){
            HANDLE hThread = ::OpenThread(THREAD_SET_CONTEXT, FALSE, tid);
            if (hThread == NULL) {
                cout << "OpenThread failed" << endl;
                return;
            }
            QueueUserAPC((PAPCFUNC)GetProcAddress(GetModuleHandle(L"kernel32"),"LoadLibraryW"),hThread,(ULONG_PTR)p); //将APC注入到目标进程中
        }
        VirtualFreeEx(hProcess, p, 0, MEM_RELEASE | MEM_DECOMMIT);
    }
}

完整代码

inject.cpp

#include <iostream>
#include <vector>
#include <Windows.h>
#include <TlHelp32.h>
#include <Psapi.h>
#include <string>
#include <tchar.h>
using namespace std;

//由进程名获取进程ID
bool FindProcess(const wchar_t* processName, DWORD& dwProcess) {
    PROCESSENTRY32 pe32;  
    pe32.dwSize = sizeof(PROCESSENTRY32);      
    //获取进程快照
    HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);     
    if (hProcessSnap == INVALID_HANDLE_VALUE) {
        return false;
    }
    if (Process32First(hProcessSnap, &pe32)) {
        do {
            if (wcscmp(pe32.szExeFile, processName) == 0) {
                dwProcess = pe32.th32ProcessID;
                break;
            }
        } while (Process32Next(hProcessSnap, &pe32));
    }
    CloseHandle(hProcessSnap);
    if(dwProcess == 0) {
        return false;
    }
    return true;
}

//DLL远程线程注入
void CreateRemoteThread_Inject(){
    DWORD dwProcess = 0;
    char myDLL[] = "C:\\testDLL.dll";
    //查找进程FlappyBird.exe
    if(FindProcess(L"FlappyBird.exe", dwProcess)){
        HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcess);    //打开进程
        LPVOID allocatedMem = VirtualAllocEx(hProcess, NULL, sizeof(myDLL), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);  //在进程中分配内存
        WriteProcessMemory(hProcess, allocatedMem, myDLL, sizeof(myDLL), NULL); //写入DLL路径
        HMODULE hModule = LoadLibrary(L"kernel32.dll");                //获取kernel32.dll模块句柄
        //获取LoadLibraryA函数地址
        LPTHREAD_START_ROUTINE pfnStarAddress = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "LoadLibraryA");
        if(pfnStarAddress == NULL){
            cout << "GetProcAddress failed" << endl;
            return;
        }
        HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, NULL, pfnStarAddress, allocatedMem, NULL, NULL);  //创建远程线程
        if(hRemoteThread == NULL){
            cout << "CreateRemoteThread failed" << endl;
            return;
        }
        WaitForSingleObject(hRemoteThread, INFINITE);  //等待远程线程结束
        CloseHandle(hRemoteThread);                                
        VirtualFreeEx(hProcess, allocatedMem, 0, MEM_FREE);
    }
    return;
}

//***********************************************Shellcode远程线程注入***********************************************
bool EnableDebugPrivilege() {
    HANDLE hToken;
    TOKEN_PRIVILEGES tp;
    //打开进程令牌
    if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
        cout<<"OpenProcessToken failed"<<endl;
        return false;
    }
    //获取LUID
    if(!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid)) {
        cout<<"LookupPrivilegeValue failed"<<endl;
        return false ;
    }
    //设置权限
    tp.PrivilegeCount = 1;  //只设置一个权限
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;     //设置权限为启用,如果为SE_PRIVILEGE_REMOVED则为禁用
    //修改进程令牌,提升权限,使得能够访问受保护的进程,如系统进程,其他用户进程等
    if(!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL)) {
        cout<<"AdjustTokenPrivileges failed"<<endl;
        return false;
    }
    return true;
}
//该结构体用于接收API和4个字符串
typedef struct _THREAD_PARAM {
    FARPROC pFunc[2];
    char szBuf[4][128];
} THREAD_PARAM, *PTHREAD_PARAM;
typedef HMODULE (WINAPI *PFLOADLIBRARYA)    
(
    LPCSTR lpLibFileName
);  //定义LoadLibraryA函数指针
typedef FARPROC (WINAPI *PFGETPROCADDRESS)
(
    HMODULE hModule,
    LPCSTR lpProcName
); //定义GetProcAddress函数指针
typedef int (WINAPI *PFMESSAGEBOXA)
(
    HWND hWnd,
    LPCSTR lpText,
    LPCSTR lpCaption,
    UINT uType
); //定义MessageBoxA函数指针

//线程函数
//未直接调用相关API和未直接定义使用字符串,而通过THREAD_PARAM结构体以线程参数的形式传递使用
DWORD WINAPI ThreadProc(LPVOID lpParam)
{   
    PTHREAD_PARAM pParam = (PTHREAD_PARAM)lpParam;
    HMODULE hMod = NULL;
    FARPROC pFunc = NULL;
    hMod = ((PFLOADLIBRARYA)pParam->pFunc[0])(pParam->szBuf[0]);        //调用LoadLibraryA函数
    if (hMod == NULL)
    {
        return 1;
    }
    pFunc = ((PFGETPROCADDRESS)pParam->pFunc[1])(hMod, pParam->szBuf[1]);   //获取MessageBoxA函数地址
    if (pFunc == NULL)
    {
        return 1;
    }
    ((PFMESSAGEBOXA)pFunc)(NULL, pParam->szBuf[2], pParam->szBuf[3], 0);    //调用MessageBoxA函数
    return 0;
}

//shellcode 注入
void CreateRemoteThread_shellcode()
{
    EnableDebugPrivilege(); //提升权限
    DWORD dwProcess = 0;
    //查找进程FlappyBird.exe
    if(FindProcess(L"FlappyBird.exe", dwProcess)){
        HMODULE hMod = NULL;
        THREAD_PARAM param = {0,};  //定义线程结构体变量
        HANDLE hProcess = NULL;
        HANDLE hThread = NULL;
        LPVOID pRemoteBuf[2] = {0,};
        DWORD dwSize = 0;

        hMod = GetModuleHandleA("kernel32.dll");

        //设置THREAD_PARAM结构体
        //加载到所有进程的kernel32.dll的地址都相同,因此从本进程获取的API与在目标进程中获取的API地址是一样的
        param.pFunc[0] = GetProcAddress(hMod, "LoadLibraryA");
        param.pFunc[1] = GetProcAddress(hMod, "GetProcAddress");
        strcpy_s(param.szBuf[0], "user32.dll");
        strcpy_s(param.szBuf[1], "MessageBoxA");
        strcpy_s(param.szBuf[2], "shellcode Success");
        strcpy_s(param.szBuf[3], "inject");
        
        hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcess);
        if (hProcess == NULL)
        {
            cout<<"OpenProcess failed"<<endl;
            return;
        }
        //分配内存
        dwSize = sizeof(THREAD_PARAM);
        pRemoteBuf[0] = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
        if (pRemoteBuf[0] == NULL)
        {
            cout<<"VirtualAllocEx failed"<<endl;
            return;
        }
        //将线程参数写入目标进程
        if (!WriteProcessMemory(hProcess, pRemoteBuf[0], (LPVOID)&param, dwSize, NULL))
        {
            cout<<"WriteProcessMemory failed"<<endl;
            return;
        }
        dwSize = (DWORD)CreateRemoteThread_shellcode - (DWORD)ThreadProc;  //计算shellcode大小
        pRemoteBuf[1] = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
        if (pRemoteBuf[1] == NULL)
        {
            cout<<"VirtualAllocEx failed"<<endl;
            return;
        }
        //将shellcode写入目标进程
        if (!WriteProcessMemory(hProcess, pRemoteBuf[1], (LPVOID)ThreadProc, dwSize, NULL))
        {
            cout<<"WriteProcessMemory failed"<<endl;
            return;
        }
        //创建远程线程
        hThread = CreateRemoteThread(
            hProcess, 
            NULL, 
            0, 
            (LPTHREAD_START_ROUTINE)pRemoteBuf[1], //线程函数地址
            pRemoteBuf[0],                     //线程参数地址
            0, NULL
        );
        if (hThread == NULL)
        {
            cout<<"CreateRemoteThread failed"<<endl;
            return;
        }
        WaitForSingleObject(hThread, INFINITE);
        CloseHandle(hThread);
        CloseHandle(hProcess);
    }
}

//***********************************************消息钩子注入***********************************************

int SetWindowHookEx_inject() {
    HWND hwnd = FindWindow(NULL, L"FlappyBird"); //查找窗口
    if (hwnd == NULL) {
        cout << "FindWindow failed" << endl;
        return 1;
    }
    DWORD pid = NULL;
    DWORD tid = GetWindowThreadProcessId(hwnd, &pid); //获取进程ID
    if (tid == NULL) {
        cout << "GetWindow tid/pid failed" << endl;
        return 1;
    }
    
    HMODULE dll = LoadLibraryEx(L"C:\\testDLL.dll", NULL, DONT_RESOLVE_DLL_REFERENCES); //加载DLL
    if (dll == NULL) {
        cout << "LoadLibraryEx DLL failed" << endl;
        return 1;
    }
    
    HOOKPROC addr = (HOOKPROC)GetProcAddress(dll, "NextHook"); //获取钩子函数地址
    if (addr == NULL) {
        cout << "GetProcAddress failed" << endl;
        return 1;
    }
    
    HHOOK handle = SetWindowsHookEx(WH_GETMESSAGE, addr, dll, tid); //设置钩子
    if (handle == NULL) {
        cout << "SetWindowsHookEx failed" << endl;
        return EXIT_FAILURE;
    }
    
    PostThreadMessage(tid, WM_NULL, 0, 0); //发送消息,触发钩子
    cout<<"SetWindowsHookEx_inject success"<<endl;
    cout<<"Press any key to unhook"<<endl;
    getchar();

    BOOL unhook = UnhookWindowsHookEx(handle); //卸载钩子
    if (unhook == NULL) {
        cout << "UnhookWindowsHookEx failed" << endl;
        return EXIT_FAILURE;
    }
    return 0;
}

//***********************************************APC注入***********************************************

//根据进程名获取进程ID及其所有线程ID
bool FindProcess2(const wchar_t* processName, DWORD& dwProcess, vector<DWORD>& dwThreads) {
    PROCESSENTRY32 pe32;
    pe32.dwSize = sizeof(PROCESSENTRY32);
    HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);  //创建进程快照
    if (hProcessSnap == INVALID_HANDLE_VALUE) {
        cout << "CreateToolhelp32Snapshot failed" << endl;
        return false;
    }
    if (!Process32First(hProcessSnap, &pe32)) {     //获取第一个进程信息
        cout << "Process32First failed" << endl;
        CloseHandle(hProcessSnap);
        return false;
    }
    do {
        if (wcscmp(pe32.szExeFile, processName) == 0) {
            dwProcess = pe32.th32ProcessID;
            break;
        }
    } while (Process32Next(hProcessSnap, &pe32));
    CloseHandle(hProcessSnap);
    if (dwProcess == NULL) {
        cout << "FindProcess failed" << endl;
        return false;
    }
    THREADENTRY32 te32;
    te32.dwSize = sizeof(THREADENTRY32);
    HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);    //创建线程快照
    if (hThreadSnap == INVALID_HANDLE_VALUE) {
        cout << "CreateToolhelp32Snapshot failed" << endl;
        return false;
    }
    if (!Thread32First(hThreadSnap, &te32)) {
        cout << "Thread32First failed" << endl;
        CloseHandle(hThreadSnap);
        return false;
    }
    do {
        if (te32.th32OwnerProcessID == dwProcess) {
            dwThreads.push_back(te32.th32ThreadID);     //获取目标进程的所有线程ID
        }
    } while (Thread32Next(hThreadSnap, &te32));
    CloseHandle(hThreadSnap);
    if (dwThreads.size() == 0) {
        cout << "FindThread failed" << endl;
        return false;
    }
    return true;
}

void APC_Inject(){
    DWORD pid;
    vector<DWORD> tids;     //需要获取进程所有的线程ID
    if (FindProcess2(L"FlappyBird.exe", pid, tids)) {
        HANDLE hProcess = OpenProcess(PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, pid);
        if (hProcess == NULL) {
            cout << "OpenProcess failed" << endl;
            return;
        }
        auto p = VirtualAllocEx(hProcess, NULL, 1<<12, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); //在目标进程中申请内存
        wchar_t buffer[] = L"C:\\testDLL.dll";
        WriteProcessMemory(hProcess, p, buffer, sizeof(buffer), NULL); //将DLL路径写入目标进程中
        for(const auto& tid: tids){
            HANDLE hThread = ::OpenThread(THREAD_SET_CONTEXT, FALSE, tid);
            if (hThread == NULL) {
                cout << "OpenThread failed" << endl;
                return;
            }
            QueueUserAPC((PAPCFUNC)GetProcAddress(GetModuleHandle(L"kernel32"),"LoadLibraryW"),hThread,(ULONG_PTR)p); //将APC注入到目标进程中
        }
        VirtualFreeEx(hProcess, p, 0, MEM_RELEASE | MEM_DECOMMIT);
    }
}

int main()
{
    printf("Inject\n");
    CreateRemoteThread_Inject();
    //CreateRemoteThread_shellcode();
    //SetWindowHookEx_inject();
    //APC_Inject();
    printf("执行结束\n");
    getchar();
    return 0;
}

dllmain.cpp

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include <Windows.h>

//在DLL入口点中调用MessageBoxA
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        MessageBox(NULL, L"Inject", L"注射成功", MB_OK);
        break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

//导出函数
extern "C" __declspec(dllexport) int NextHook(int code, WPARAM wParam, LPARAM lParam){
    return CallNextHookEx(NULL, code, wParam, lParam);
}
标签: c++ 安全

本文转载自: https://blog.csdn.net/m0_51762452/article/details/128939325
版权归原作者 晚安靴子 所有, 如有侵权,请联系我们删除。

“Windows 动态注入(远程线程、消息钩子、APC)”的评论:

还没有评论