本文讲解如何把一个DLL注入在另一个EXE的进程中(附32bit例子)
方法之一 线程注入
步骤
第一步,要把dll路径字符串写到目标exe的内存中。
这里使用的是 “远程申请内存”和“向进程写内存”;
第二步,开启一个远程线程,远程执行 LoadLibaryA函数,来加载我们要注入的DLL。
test.exe inject.dll这里使用易语言编写。
inject.dll代码截图
将"inject.dll"注入“test.exe”,注入成功。
DLL注入器流程图
注入器代码(C++):
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include "windows.h"
#include "tlhelp32.h"
wchar_t* char2wchar(const char* cchar);
char* wchar2char(const wchar_t* wchar);
int process_getIdByName(const char pName[]);
void inject_thread()
{
std::string filePath = "C:\\Users\\Administrator\\Desktop\\线程注入\\inject.dll";
HANDLE targetProcessH = OpenProcess(PROCESS_ALL_ACCESS, false, process_getIdByName("test.exe"));
if (targetProcessH == NULL)
{
std::cout << "targetProcessId is null" << std::endl;
return;
}
LPVOID remoteDllPathTextAddr = VirtualAllocEx(targetProcessH, 0, filePath.length() + 1, MEM_COMMIT, PAGE_READWRITE);
if (remoteDllPathTextAddr == NULL)
{
std::cout << "remoteDllPathTextAddr is null" << std::endl;
return;
}
bool isWriteSuccess = WriteProcessMemory(targetProcessH, remoteDllPathTextAddr, filePath.c_str(), filePath.length(), 0);
if (!isWriteSuccess)
{
std::cout << "isWriteSuccess fail" << std::endl;
return;
}
LPTHREAD_START_ROUTINE loadLibraryAAddr = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandleA("Kernel32.dll"), "LoadLibraryA");
if (loadLibraryAAddr == NULL)
{
std::cout << "loadLibraryAAddr is null" << std::endl;
return;
}
std::cout << "CreateRemoteThread" << std::endl;
HANDLE targetThreadH = CreateRemoteThread(targetProcessH, 0, 0, loadLibraryAAddr, remoteDllPathTextAddr, 0, 0);
if (targetThreadH == NULL)
{
std::cout << "targetThreadH is null" << std::endl;
return;
}
int result = WaitForSingleObject(targetThreadH, INFINITE);
std::cout << "WaitForSingleObject result = "<<result << std::endl;
CloseHandle(targetProcessH);
CloseHandle(targetThreadH);
}
int main()
{
inject_thread();
getchar();
return 0;
}
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;
}
char* wchar2char(const wchar_t* wchar)
{
// 转换函数
char* m_char;
int len = WideCharToMultiByte(CP_ACP, 0, wchar, wcslen(wchar), NULL, 0, NULL, NULL);
m_char = new char[len + 1];
WideCharToMultiByte(CP_ACP, 0, wchar, wcslen(wchar), m_char, len, NULL, NULL);
m_char[len] = '\0';
return m_char;
}
int process_getIdByName(const char pName[])
{
// 进程.通过名称获取PID
PROCESSENTRY32 p;
PROCESSENTRY32* info = &p;
// 在使用这个结构之前,先设置它的大小
info->dwSize = sizeof(PROCESSENTRY32);
char proName[] = { "devenv.exe" };
HANDLE handlePro = NULL; //结束进程句柄
// 给系统内的所有进程拍一个快照
HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == INVALID_HANDLE_VALUE)
{
printf(" CreateToolhelp32Snapshot调用失败! \n");
return -1;
}
// 遍历进程快照,轮流显示每个进程的信息
BOOL bMore = ::Process32First(hProcessSnap, info);
BOOL terminate = FALSE;
while (bMore != FALSE)
{
//std::cout << pName<<" " <<wchar2char(info->szExeFile) << std::endl;
if (strcmp(pName, wchar2char(info->szExeFile)) == 0)
{
return info->th32ProcessID;
}
bMore = Process32Next(hProcessSnap, info);
}
::CloseHandle(hProcessSnap);
return 0;
}
对抗DLL注入
方案之一:HOOK LoadLibraryA函数
附:test.exe的自身HOOK易语言代码
Hook掉LoadLibaryA函数后,我们可以通过判断“lpLibFileName(DLL路径)”,来拦截非白名单DLL。
也可以根据路径找到文件,计算哈希,查询是否在白名单。
Hook掉LoadLibaryA函数,只是对抗方式之一。而且也有绕过方式,比如自己写一个跟LoadLibaryA功能差不多的函数,来加载资源和注册。这个如果有机会,会继续讲。