APC注入

1.APC机制

是一种并发机制,APC函数在线程中被异步回调

线程调用SleepEx、SignalObjectAndWait、MsgWaitForMultipleObjectsEx、WaitForMultipleObjectsEx、WaitForSingleObjectEx函数时会进入警告状态,系统会产生一个软中断,线程再次被唤醒时会检查APC队列执行所有APC函数

内核APC:由系统产生,不涉及到换栈

用户APC:由应用程序产生,在内核、用户层切换时使用不同栈

APC队列:KTHREAD+0x034 指向 KAPC_STATE结构体(存储APC队列)

执行函数

KiServiceExit函数

用于在系统调用(x86sysenter/x64syscall)、异常、中断后回到用户层

_KAPC_STATE.KernelApcPending为1则调用 KiDeliverApc 执行APC函数,执行完再循环遍历

KiDeliverApc函数

执行APC函数

2.APC注入

要注入多线程进程,单线程睡眠了就注入不进去了,注入系统进程需要以管理员权限运行来获取 SeDebugPrivilege特权

Copy
1.获取进程PID、句柄
2.将DLL路径写入进程内存
3.插入APC函数
4.关闭句柄
Copy
#include 
#include 
#include 
#define _tmain wmain // main使用Unicode
 
DWORD GetProcessPID(LPCTSTR lpProcessName) { // LPCTSTR是指向字符串的指针类型,如果需要修改内容要用LPTSTR
    /*
    * CreateToolhelp32Snapshot是Windows API
    * 参数1:指定创建哪个快照,TH32CS_SNAPPROCESS是进程快照,包含所有进程信息
    * 参数2:进程ID,0表示所有进程
    */
    HANDLE lpSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); // 创建所有进程快照
 
    PROCESSENTRY32 p32 = { 0 }; // Windows API中的结构体,用来存储进程快照
    p32.dwSize = sizeof(PROCESSENTRY32); // 设置结构体正确大小,这样才能填充进程信息
    Process32First(lpSnapshot, &p32); // Windows API,将全部进程信息写入p32
 
    do {
        if (!lstrcmp(p32.szExeFile, lpProcessName)) { // 进程名正确
            CloseHandle(lpSnapshot); // 关闭句柄
            return p32.th32ProcessID;
        }
    } while (Process32Next(lpSnapshot, &p32)); // 循环每一个进程
}
 
void EnableDebugPrivilege() {
    HANDLE hToken;
    // 打开当前进程令牌,指定 TOKEN_ADJUST_PRIVILEGES 以修改令牌特权
    if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) {
        TOKEN_PRIVILEGES tp; // 用于存储令牌特权信息的结构体
        tp.PrivilegeCount = 1; // 特权数量为1
        LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid); // 指定启用的特权,SE_DEBUG_NAME 对应 SeDebugPrivilege特权
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // SE_PRIVILEGE_ENABLED 表示启用特权
        AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL); // 将令牌特权信息应用到令牌上
        CloseHandle(hToken); // 关闭句柄
    }
}
 
void APCInjectDLL(DWORD PID, LPCWSTR DllPath) {
    EnableDebugPrivilege(); // 获取 SeDebugPrivilege特权
 
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID); // 获取进程全部权限的句柄
 
    DWORD size = (wcslen(DllPath) + 1) * sizeof(WCHAR); // 申请的空间大小,+1是加上\0,乘是因为Unicode
    LPVOID pAllocMemory = VirtualAllocEx(hProcess, NULL, size, MEM_COMMIT, PAGE_READWRITE); // 指针指向远程申请的内存
    WriteProcessMemory(hProcess, pAllocMemory, DllPath, size, NULL); // 写入内存
 
    FARPROC pFuncAddr = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW"); // 获取Kernel32、LoadLibrary
 
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); // 创建所有进程快照
    /*
    * Thread32First是Windows API
    * 参数1:CreateToolhelp32Snapshot返回的句柄
    * 参数2:一个指向THREADENTRY32结构体(存储线程信息)的指针
    */
    THREADENTRY32 te = { 0 }; // 每个成员初始化为0
    te.dwSize = sizeof(te); // 设置结构体正确大小,这样才能填充进程信息
    Thread32First(hSnap, &te); // 获取所有进程的线程的第一个线程的信息
    do {
        if (te.th32OwnerProcessID == PID) { // PID正确,找到目标进程
            HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te.th32ThreadID); // 通过线程ID打开线程
            QueueUserAPC((PAPCFUNC)pFuncAddr, hThread, (ULONG_PTR)pAllocMemory); // 插入APC函数
            CloseHandle(hThread); // 关闭句柄
        }
    } while (Thread32Next(hSnap, &te)); // 取下一个线程
 
    CloseHandle(hSnap); // 关闭句柄
    CloseHandle(hProcess);
}
 
/*
* 传参
* 这里是Unicode,Ascii用main和char*
* argv[0]是第一个参数(xxx.exe自身)
*/
int _tmain(int argc, TCHAR* argv[]) {
    DWORD PID = GetProcessPID(argv[1]); // 传入进程名获取进程PID
    APCInjectDLL(PID, L"C:\\xxx\\x64.dll");
    return 0;
}

你可能感兴趣的:(网络安全)