网上例子很多,但对细枝末节的处理少,某些情况下这些细节决定了注入的成功与否。
花了一点时间,自己写了个通用的标准远程线程注入。
#include <iostream>
#include <Windows.h>
#include <tchar.h>
#include <tlhelp32.h>
#include <shlobj_core.h>
#include <psapi.h>
#pragma comment(lib, "psapi.lib")
// 寻找进程PID
DWORD FindPid(LPCTSTR name);
// 远程写入值
void WriteVal(LPVOID addr, LPVOID buff, SIZE_T size);
// 开启调试权限
BOOL EnableDebugPrivilege(BOOL fEnable);
// 提升特权
void PromotePrivilege();
// false=无特权 true=有特权
bool HasPrivilege();
HANDLE g_hProcess;
wchar_t* char2wchar(const char* cchar);
int main(int argc, char const* argv[])
{
if (argc <= 1) return 0;
// 提升管理员特权
if (!HasPrivilege()) {
printf("无特权,是否尝试提升特权?\n");
system("pause");
PromotePrivilege();
}
// 获取参数
const char* dll_path = argv[1];
wchar_t* process_name = char2wchar(argv[2]);
DWORD pid = FindPid(process_name);
if (pid <= 0) {
printf("无效进程 \n");
return 0;
}
printf("DLL路径:%s\n进程名:%ws (%d)(%0X)\n", dll_path, process_name, pid, pid);
DWORD buffSize = strlen(dll_path) + 1;
//1打开目标进程
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
if (hProcess == NULL) {
printf("目标进程无效\n");
return 0;
}
printf("目标进程HANDLE:%d\n", (int)hProcess);
//2.申请远程内存空间
char* str = (char*)VirtualAllocEx(hProcess, 0, buffSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
printf("申请到的远程内存空间地址:%0X\n", (int)str);
if (str == NULL) {
printf("申请失败!\n");
return 0;
}
//3.将dll文件路径写入到远程内存
DWORD realWrite = 0;
BOOL bs1 = WriteProcessMemory(hProcess, str, dll_path, buffSize, &realWrite);
printf("将dll文件路径[%s]写入被注入程序的内存:%s\n", dll_path, bs1 ?"成功":"失败");
//4.创建远程线程
HMODULE hK32Mod = GetModuleHandleA("Kernel32.dll");
if (hK32Mod == NULL) {
printf("获取Kernel32.dll地址为空\n");
return 0;
}
printf(" -- Kernel32.dll : %0X \n", (DWORD)hK32Mod);
int* pLoadLibrary = (int*)GetProcAddress(hK32Mod, "LoadLibraryA");
if (pLoadLibrary == NULL) {
printf("获取LoadLibraryA地址为空\n");
return 0;
}
printf(" LoadLibraryA : %0X \n", (DWORD)pLoadLibrary);
HANDLE hThread = CreateRemoteThread(hProcess, 0, 0, (LPTHREAD_START_ROUTINE)pLoadLibrary, str, 0, 0);
if (hThread == NULL) {
printf("远程线程创建失败\n");
return 0;
}
printf("使用CreateRemoteThread呼叫LoadLibraryA,远程线程句柄:%0X \n", (int)hThread);
printf("等待信号量 ...\n");
WaitForSingleObject(hThread, -1);
printf("收到退出信号量 ...\n");
DWORD pExitCode; // 线程退出代码,在这里当然是LoadLibrary的返回值啦
GetExitCodeThread(hThread, &pExitCode);
printf("被注入的DLL在对方内存的地址:%0X \n", pExitCode);
CloseHandle(hThread); //关闭线程句柄
//释放内存空间
BOOL bs2 = VirtualFreeEx(hProcess, str, 0, MEM_RELEASE);
printf("释放dll文件路径的内存空间:%s\n", bs2 ? "成功" : "失败");
CloseHandle(hProcess); //关闭进程句柄
system("pause");
return 0;
}
// 远程内存写入
void WriteVal(LPVOID addr, LPVOID buff ,SIZE_T size) {
// 代码段置为可写
DWORD oldProtect;
VirtualProtect(addr, sizeof(DWORD), PAGE_READWRITE, &oldProtect);
SIZE_T realWriteNum;
WriteProcessMemory(g_hProcess, addr, buff, size, &realWriteNum);
// 代码段置为不可写
DWORD oldProtect2;
VirtualProtect(addr, sizeof(WORD), oldProtect, &oldProtect2);
}
// 根据进程名获取进程id
DWORD FindPid(LPCTSTR name) {
DWORD aProcesses[1024], cbNeeded, ModNeeded;
if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded))
return -1;
HANDLE hProcess;
HMODULE hMod;
TCHAR szProcessName[MAX_PATH] = _T("unknown");
int nProcesses = cbNeeded / sizeof(DWORD);
for (int i = 0; i < nProcesses; i++) {
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
PROCESS_VM_READ,
FALSE, aProcesses[i]);
if (NULL != hProcess) {
if (EnumProcessModules(hProcess, &hMod, sizeof(hMod), &ModNeeded)) {
GetModuleBaseName(hProcess, hMod, szProcessName, sizeof(szProcessName));
if (lstrcmpi(szProcessName, name) == 0) {
return aProcesses[i];
}
}
else
continue;
}
}
return -1;
}
// 单字符转宽字符
wchar_t* char2wchar(const char* cchar)
{
wchar_t* m_wchar;
int len = MultiByteToWideChar(CP_ACP, 0, cchar, strlen(cchar), NULL, 0);
m_wchar = new wchar_t[len + 1];
MultiByteToWideChar(CP_ACP, 0, cchar, strlen(cchar), m_wchar, len);
m_wchar[len] = '\0';
return m_wchar;
}
//提升为调试权限
BOOL EnableDebugPrivilege(BOOL fEnable) {
BOOL fOk = FALSE; HANDLE hToken;
// 以修改权限的方式,打开进程的令牌
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES,
&hToken)) {
// 令牌权限结构体
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
//获得LUID
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
tp.Privileges[0].Attributes = fEnable ? SE_PRIVILEGE_ENABLED : 0;
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL); //修改权限
fOk = (GetLastError() == ERROR_SUCCESS);
CloseHandle(hToken);
}
return(fOk);
}
// 提升特权
void PromotePrivilege() {
// 2. 获取当前程序路径
WCHAR szApplication[MAX_PATH] = { 0 };
DWORD cchLength = _countof(szApplication);
QueryFullProcessImageName(GetCurrentProcess(), 0,
szApplication, &cchLength);
// 3. 以管理员权限重新打开进程
SHELLEXECUTEINFO sei = { sizeof(SHELLEXECUTEINFO) };
sei.lpVerb = L"runas"; // 请求提升权限
sei.lpFile = szApplication; // 可执行文件路径
sei.lpParameters = NULL; // 不需要参数
sei.nShow = SW_SHOWNORMAL; // 正常显示窗口
if (ShellExecuteEx(&sei)) {
// 重启获得特权
printf("重启程序 尝试提权\n");
exit(0);
} else {
// 已有特权
return;
}
}
// false=无特权 true=有特权
bool HasPrivilege() {
HANDLE hToken = NULL;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
return false;
// 2. 获取提升类型
TOKEN_ELEVATION_TYPE ElevationType = TokenElevationTypeDefault;
BOOL bIsAdmin = false;
DWORD dwSize = 0;
if (GetTokenInformation(hToken, TokenElevationType, &ElevationType,
sizeof(TOKEN_ELEVATION_TYPE), &dwSize)) {
// 2.1 创建管理员组的对应SID
BYTE adminSID[SECURITY_MAX_SID_SIZE];
dwSize = sizeof(adminSID);
CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, &adminSID, &dwSize);
// 2.2 判断当前进程运行用户角色是否为管理员
if (ElevationType == TokenElevationTypeLimited) {
// a. 获取连接令牌的句柄
HANDLE hUnfilteredToken = NULL;
GetTokenInformation(hToken, TokenLinkedToken, (PVOID)&hUnfilteredToken,
sizeof(HANDLE), &dwSize);
// b. 检查这个原始的令牌是否包含管理员的SID
if (!CheckTokenMembership(hUnfilteredToken, &adminSID, &bIsAdmin))
return false;
CloseHandle(hUnfilteredToken);
}
else {
bIsAdmin = IsUserAnAdmin();
}
CloseHandle(hToken);
}
// 3. 判断具体的权限状况
BOOL bFullToken = false;
switch (ElevationType) {
case TokenElevationTypeDefault: /* 默认的用户或UAC被禁用 */
if (IsUserAnAdmin()) bFullToken = true; // 默认用户有管理员权限
else bFullToken = false;// 默认用户不是管理员组
break;
case TokenElevationTypeFull: /* 已经成功提高进程权限 */
if (IsUserAnAdmin()) bFullToken = true; //当前以管理员权限运行
else bFullToken = false;//当前未以管理员权限运行
break;
case TokenElevationTypeLimited: /* 进程在以有限的权限运行 */
if (bIsAdmin) bFullToken = false;//用户有管理员权限,但进程权限有限
else bFullToken = false;//用户不是管理员组,且进程权限有限
}
return bFullToken;
}
使用方法,生成exe,假设名称为RemoteThreadDll.exe
exe同目录下创建一个bat文件,内容如下
RemoteThreadDll.exe D:\ap114\Dll1.dll kyodai.exe
注入器exe dll文件路径 被注入的目标程序
运行run.bat效果