APC注入
APC注入的原理:
利用当线程被唤醒时APC中的注册函数会被执行的机制,并以此去执行我们的DLL加载代码,进而完成DLL注入的目的,其具体流程如下:
1)当EXE里某个线程执行到SleepEx()或者WaitForSingleObjectEx()时,系统就会产生一个软中断(或者是Messagebox弹窗的时候不点OK的时候也能注入)。
2)当线程再次被唤醒时,此线程会首先执行APC队列中的被注册的函数。
3)利用QueueUserAPC()这个API可以在软中断时向线程的APC队列插入一个函数指针,如果我们插入的是Loadlibrary()执行函数的话,就能达到注入DLL的目的。
局限:
这种注入方式局限性很明显,一是必须要等待时机,而是当注入成功后,SleepEx或者其他等待函数直接就会跳过当前等待继续往下走,这样可能造成被注入程序的不稳定行,经常导致被注入程序崩溃。
测试:双击测试.exe,先会弹窗提示开始测试,然后进入SleepEx,再双击APC注入.exe,因为要注入的程序是双击测试.exe的主线程,且它此时正在执行到SleepEx(),直接注入完成,弹出注入成功的对话框。
// APC注入.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 // // LoadExeWin32.cpp : 定义控制台应用程序的入口点。 // #include <iostream> #include <string> #include <windows.h> #include <shlwapi.h> #include <tlhelp32.h> #include <tchar.h> #include "APC.h" #pragma comment(lib, "shlwapi.lib") #pragma comment(lib,"ntdll.lib") using namespace std; BOOL __EnableDebugPrivilege = TRUE; //根据进程名字获取pid DWORD GetPidFromName(TCHAR* ProcessName) { HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnapshot == INVALID_HANDLE_VALUE) { return FALSE; } PROCESSENTRY32W pe = { sizeof(pe) }; BOOL bOk; for (bOk = Process32FirstW(hSnapshot, &pe); bOk; bOk = Process32NextW(hSnapshot, &pe)) { wstring wsNowProcName = pe.szExeFile; if (_tcsicmp(pe.szExeFile, ProcessName) == 0) { CloseHandle(hSnapshot); return pe.th32ProcessID; } } CloseHandle(hSnapshot); return 0; } /*提权代码*/ DWORD SunEnableSunDebugPrivilege(HANDLE ProcessHandle, BOOL IsEnable) { DWORD LastError; HANDLE TokenHandle = 0; if (!OpenProcessToken(ProcessHandle, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &TokenHandle)) { LastError = GetLastError(); if (TokenHandle) CloseHandle(TokenHandle); return LastError; } TOKEN_PRIVILEGES TokenPrivileges; memset(&TokenPrivileges, 0, sizeof(TOKEN_PRIVILEGES)); LUID v1; if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &v1)) { LastError = GetLastError(); CloseHandle(TokenHandle); return LastError; } TokenPrivileges.PrivilegeCount = 1; TokenPrivileges.Privileges[0].Luid = v1; if (IsEnable) TokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; else TokenPrivileges.Privileges[0].Attributes = 0; AdjustTokenPrivileges(TokenHandle, FALSE, &TokenPrivileges, sizeof(TOKEN_PRIVILEGES), NULL, NULL); LastError = GetLastError(); CloseHandle(TokenHandle); return LastError; } HANDLE SunOpenProcess(DWORD DesiredAccess, BOOL IsInheritHandle, HANDLE ProcessIdentify) { //提权 if (__EnableDebugPrivilege) //全局变量__EnableDebugPrivilege { SunEnableSunDebugPrivilege(GetCurrentProcess(), TRUE); } //打开进程,根据目标进程的进程ID得到进程句柄 HANDLE ProcessHandle = OpenProcess(DesiredAccess, IsInheritHandle, (DWORD)ProcessIdentify); DWORD LastError = GetLastError(); //关闭权限 if (__EnableDebugPrivilege) { SunEnableSunDebugPrivilege(GetCurrentProcess(), FALSE); } SetLastError(LastError); return ProcessHandle; } BOOL SunEnumThreadsInProcess(HANDLE ProcessIdentify,DWORD& MainThreadID) { BOOL IsOk = FALSE; NTSTATUS Status = 0; PSYSTEM_PROCESS_INFORMATION SystemProcessInfo = NULL; HMODULE ModuleBase = NULL; //模块句柄 //获取当前进程中的Ntdll模块的基地址 ModuleBase = GetModuleHandle(_T("Ntdll.dll")); if (ModuleBase == NULL) { return FALSE; } //从Ntdll模块的导出表中获取函数地址 __NtQuerySystemInformation = (LPFN_NTQUERYSYSTEMINFORMATION)GetProcAddress(ModuleBase,"NtQuerySystemInformation"); if (__NtQuerySystemInformation == NULL) { return FALSE; } //申请内存 PVOID SystemInfoBuffer = NULL; SystemInfoBuffer = malloc(1024 * 1024); if (!SystemInfoBuffer) { return FALSE; } // 在QuerySystemInformation系列函数中,查询SystemProcessInformation时,必须提前申请好内存,不能先查询得到长度再重新调用 Status = __NtQuerySystemInformation(SystemProcessInformation, SystemInfoBuffer, 1024 * 1024, NULL); if (!NT_SUCCESS(Status)) { free(SystemInfoBuffer); return FALSE; } SystemProcessInfo = (PSYSTEM_PROCESS_INFORMATION)SystemInfoBuffer; // 遍历进程 while (TRUE) { IsOk = FALSE; if (SystemProcessInfo->UniqueProcessId == (HANDLE)ProcessIdentify) { IsOk = TRUE; break; } else if (SystemProcessInfo->NextEntryOffset) { SystemProcessInfo = (PSYSTEM_PROCESS_INFORMATION)((PUINT8)SystemProcessInfo + SystemProcessInfo->NextEntryOffset); } else { break; } } if (IsOk) { //查询主线程 for (INT i = 0; i < SystemProcessInfo->NumberOfThreads; i++) { // 执行的不能是当前线程 MainThreadID = (DWORD)SystemProcessInfo->Threads[0].ClientId.UniqueThread; //主线程ID break; } } if (SystemInfoBuffer != NULL) { free(SystemInfoBuffer); } return IsOk; } //把wcCacheInDllPath DLL文件注入进程wsProcessName BOOL Injection_APC(TCHAR* wsProcessName, const TCHAR wcCacheInDllPath[]) { //初始化 DWORD dwProcessId = GetPidFromName(wsProcessName); HANDLE hProcess = SunOpenProcess(PROCESS_ALL_ACCESS, FALSE, (HANDLE)dwProcessId); if (!hProcess) { return FALSE; } PVOID lpData = VirtualAllocEx(hProcess, NULL, 1024, MEM_COMMIT, PAGE_EXECUTE_READWRITE); SIZE_T dwRet; if (lpData) { //在远程进程申请空间中写入待注入DLL的路径 WriteProcessMemory(hProcess, lpData, (LPVOID)wcCacheInDllPath, MAX_PATH, &dwRet); } CloseHandle(hProcess); //开始注入 DWORD MainThreadID = NULL; SunEnumThreadsInProcess((HANDLE)dwProcessId, MainThreadID); BOOL bStat = FALSE; //得到线程句柄 HANDLE handleThread = OpenThread(THREAD_ALL_ACCESS, FALSE, MainThreadID); if (handleThread) { //向主线程插入APC DWORD dwRet = QueueUserAPC( (PAPCFUNC)LoadLibraryW, handleThread, (ULONG_PTR)lpData); if (dwRet > 0) { bStat = TRUE; } //关闭句柄 CloseHandle(handleThread); } return bStat; } //Adds a user-mode asynchronous procedure call (APC) int main(int argc, char* argv[]) { //Sleep(1000*100); TCHAR* V1 = NULL; V1 = (TCHAR*)(_T("测试.exe")); Injection_APC(V1, _T("Dll.dll")); return 0; }
二、DLL部分
// dllmain.cpp : 定义 DLL 应用程序的入口点。 #include "pch.h" #include <tchar.h> BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: MessageBox(0, _T("APC注入实现啦"), _T("APC注入"),0 ); case DLL_THREAD_ATTACH: //MessageBox(0, _T("APC注入实现啦"), _T("APC注入"),0 ); case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; }
三、测试主程序
// 测试.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 // #include <iostream> #include <windows.h> #include <tchar.h> using namespace std; int main() { MessageBox(0, _T("开始测试"), _T("测试"), MB_OK); SleepEx(1000 * 60 * 5, TRUE); MessageBox(0, _T("slee之后弹窗"), _T("测试"), MB_OK); }
四、相关头文件和结构定义
#include <iostream> #include <windows.h> #include <tchar.h> using namespace std; #define STATUS_SUCCESS (NTSTATUS)0x00000000 #define NT_SUCCESS(x) ((x) >= 0) #define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L) typedef enum _SYSTEM_INFORMATION_CLASS { SystemProcessInformation = 5 } SYSTEM_INFORMATION_CLASS; typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } UNICODE_STRING; typedef UNICODE_STRING *PUNICODE_STRING; typedef struct _ANSI_STRING { USHORT Length; USHORT MaximumLength; PSTR Buffer; } ANSI_STRING; typedef ANSI_STRING *PANSI_STRING; typedef struct _CLIENT_ID { HANDLE UniqueProcess; HANDLE UniqueThread; } CLIENT_ID, *PCLIENT_ID; typedef LONG KPRIORITY; typedef struct _SYSTEM_THREAD_INFORMATION { LARGE_INTEGER KernelTime; LARGE_INTEGER UserTime; LARGE_INTEGER CreateTime; ULONG WaitTime; PVOID StartAddress; CLIENT_ID ClientId; KPRIORITY Priority; LONG BasePriority; ULONG ContextSwitches; ULONG ThreadState; ULONG WaitReason; ULONG PadPadAlignment; } SYSTEM_THREAD_INFORMATION, *PSYSTEM_THREAD_INFORMATION; typedef struct _SYSTEM_PROCESS_INFORMATION { ULONG NextEntryOffset; ULONG NumberOfThreads; LARGE_INTEGER WorkingSetPrivateSize; ULONG HardFaultCount; ULONG NumberOfThreadsHighWatermark; ULONGLONG CycleTime; LARGE_INTEGER CreateTime; LARGE_INTEGER UserTime; LARGE_INTEGER KernelTime; UNICODE_STRING ImageName; KPRIORITY BasePriority; HANDLE UniqueProcessId; HANDLE InheritedFromUniqueProcessId; ULONG HandleCount; ULONG SessionId; ULONG_PTR UniqueProcessKey; SIZE_T PeakVirtualSize; SIZE_T VirtualSize; ULONG PageFaultCount; SIZE_T PeakWorkingSetSize; SIZE_T WorkingSetSize; SIZE_T QuotaPeakPagedPoolUsage; SIZE_T QuotaPagedPoolUsage; SIZE_T QuotaPeakNonPagedPoolUsage; SIZE_T QuotaNonPagedPoolUsage; SIZE_T PagefileUsage; SIZE_T PeakPagefileUsage; SIZE_T PrivatePageCount; LARGE_INTEGER ReadOperationCount; LARGE_INTEGER WriteOperationCount; LARGE_INTEGER OtherOperationCount; LARGE_INTEGER ReadTransferCount; LARGE_INTEGER WriteTransferCount; LARGE_INTEGER OtherTransferCount; SYSTEM_THREAD_INFORMATION Threads[1]; }SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION; typedef NTSTATUS(NTAPI *LPFN_NTQUERYSYSTEMINFORMATION)( IN SYSTEM_INFORMATION_CLASS SystemInfoClass, OUT PVOID SystemInfoBuffer, IN ULONG SystemInfoBufferSize, OUT PULONG BytesReturned OPTIONAL); LPFN_NTQUERYSYSTEMINFORMATION __NtQuerySystemInformation = NULL; extern LPFN_NTQUERYSYSTEMINFORMATION __NtQuerySystemInformation;