Windows x86/ x64 Ring3层注入Dll总结

2019-10-16 15:36:08王振洲

0x01.前言

  提到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;
}

  紧接着,既然我们要对目标进程注入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;
}
// 使用Psapi系列枚举函数
#include <Psapi.h>
BOOL GetProcessIdByProcessImageName(IN PWCHAR wzProcessImageName, OUT PUINT32 ProcessId)
{
DWORD dwProcessesId[1024] = { 0 };
DWORD BytesReturned = 0;
UINT32 ProcessCount = 0;
// 获得当前操作系统中的所有进程Id,保存在dwProcessesId数组里
if (!EnumProcesses(dwProcessesId, sizeof(dwProcessesId), &BytesReturned))
{
return FALSE;
}
ProcessCount = BytesReturned / sizeof(DWORD);
// 遍历
for (INT i = 0; i < ProcessCount; i++)
{
HMODULE ModuleBase = NULL;
WCHAR wzModuleBaseName[MAX_PATH] = { 0 };
HANDLE ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessesId[i]);
if (ProcessHandle == NULL)
{
continue;
}
if (EnumProcessModulesEx(ProcessHandle, &ModuleBase, sizeof(HMODULE), &BytesReturned, LIST_MODULES_ALL))
{
// 获得进程第一模块名称
GetModuleBaseName(ProcessHandle, ModuleBase, wzModuleBaseName, MAX_PATH * sizeof(WCHAR));
}
CloseHandle(ProcessHandle);
ProcessHandle = NULL;
if (lstrcmpi(wzModuleBaseName, wzProcessImageName) == 0) // 不区分大小写
{
*ProcessId = dwProcessesId[i];
break;
}
}
if (*ProcessId == 0)
{
return FALSE;
}
return TRUE;
}