提到Dll的注入,立马能够想到的方法就有很多,比如利用远程线程、Apc等等,这里我对Ring3层的Dll注入学习做一个总结吧。
我把注入的方法分成六类,分别是:1.创建新线程、2.设置线程上下背景文,修改寄存器、3.插入Apc队列、4.修改注册表、5.挂钩窗口消息、6.远程手动实现LoadLibrary。
那么下面就开始学习之旅吧!
0x02.预备工作
在涉及到注入的程序中,提升程序的权限自然是必不可少的,这里我提供了两个封装的函数,都可以用于提权。第一个是通过权限令牌来调整权限;第二个是通过ntdll.dll的导出的未文档化函数RtlAdjustPrivilege来调整权限。
// 传入参数 SE_DEBUG_NAME,提升到调试权限 BOOL GrantPriviledge(WCHAR* PriviledgeName) { TOKEN_PRIVILEGES TokenPrivileges, OldPrivileges; DWORD dwReturnLength = sizeof(OldPrivileges); HANDLE TokenHandle = NULL; LUID uID; // 打开权限令牌 if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &TokenHandle)) { if (GetLastError() != ERROR_NO_TOKEN) { return FALSE; } if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &TokenHandle)) { return FALSE; } } if (!LookupPrivilegeValue(NULL, PriviledgeName, &uID)) // 通过权限名称查找uID { CloseHandle(TokenHandle); return FALSE; } TokenPrivileges.PrivilegeCount = 1; // 要提升的权限个数 TokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // 动态数组,数组大小根据Count的数目 TokenPrivileges.Privileges[0].Luid = uID; // 在这里我们进行调整权限 if (!AdjustTokenPrivileges(TokenHandle, FALSE, &TokenPrivileges, sizeof(TOKEN_PRIVILEGES), &OldPrivileges, &dwReturnLength)) { CloseHandle(TokenHandle); return FALSE; } // 成功了 CloseHandle(TokenHandle); return TRUE; }
权限令牌
// 传入参数 SE_DEBUG_PRIVILEGE,提升到调试权限 #define SE_DEBUG_PRIVILEGE (20L) typedef NTSTATUS(NTAPI * pfnRtlAdjustPrivilege)( UINT32 Privilege, BOOLEAN Enable, BOOLEAN Client, PBOOLEAN WasEnabled); BOOL GrantPriviledge(IN UINT32 Priviledge) { pfnRtlAdjustPrivilege RtlAdjustPrivilege = NULL; BOOLEAN WasEnable = FALSE; RtlAdjustPrivilege = (pfnRtlAdjustPrivilege)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "RtlAdjustPrivilege"); if (RtlAdjustPrivilege == NULL) { return FALSE; } RtlAdjustPrivilege(Priviledge, TRUE, FALSE, &WasEnable); return TRUE; }
RtlAdjustPrivilege紧接着,既然我们要对目标进程注入Dll,那么获得目标进程的Id是不可或缺的吧,因为OpenProcess是肯定会使用的,这里我也提供了两种通过目标进程映像名称获得进程Id的方法。第一种是最常见的使用TlHelp创建系统的进程快照;第二种是借助Psapi枚举系列函数,不过这个方法我实现的有缺憾,32位下不能得到64位进程的Id。
// 使用ToolHelp系列函数 #include <TlHelp32.h> BOOL GetProcessIdByProcessImageName(IN PWCHAR wzProcessImageName, OUT PUINT32 ProcessId) { HANDLE ProcessSnapshotHandle = INVALID_HANDLE_VALUE; PROCESSENTRY32 ProcessEntry32 = { 0 }; ProcessEntry32.dwSize = sizeof(PROCESSENTRY32); // 初始化PROCESSENTRY32结构 ProcessSnapshotHandle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); // 给系统所有的进程快照 if (ProcessSnapshotHandle == INVALID_HANDLE_VALUE) { return FALSE; } if (Process32First(ProcessSnapshotHandle, &ProcessEntry32)) // 找到第一个 { do { if (lstrcmpi(ProcessEntry32.szExeFile, wzProcessImageName) == 0) // 不区分大小写 { *ProcessId = ProcessEntry32.th32ProcessID; break; } } while (Process32Next(ProcessSnapshotHandle, &ProcessEntry32)); } CloseHandle(ProcessSnapshotHandle); ProcessSnapshotHandle = INVALID_HANDLE_VALUE; if (*ProcessId == 0) { return FALSE; } return TRUE; }
TlHelp