一般而言要注入DLL到一个目标进程最简单的方法 就是先获取DLL文件路径,然后在目标进程分配内存空间将路径写入到目标进程,写入到目标进程后再调用CreateRemoteThread()/NtCreateThread()/RtlCreateUserThread()函数来运行LoadLibraryA/W函数调用自己的DLL,这种方法的缺陷也很明显那就是容易被游戏检测到,很容易被游戏拦截,比如CSGO最新版就已经有这个限制了。
( l3 D/ z, N2 J" _; d' W 想要突破CSGO的限制注入DLL进去,我们可以采用反射式注入的方法(也可以先恢复CSGOhook的api进行远程线程注入),那么什么是反射式注入呢?又有什么有点呢?8 J; k5 c& @3 z4 O
反射式dll注入与常规dll注入类似,而不同的地方在于反射式dll注入技术自己实现了一个reflective loader()函数来代替LoadLibaryA()函数去加载dll,示意图如下图所示。蓝色的线表示与用常规dll注入相同的步骤,红框中的是reflective loader()函数行为,也是下面重点描述的地方。4 H$ d8 h7 _3 z% j; G& m. S
Reflective loader实现思路如下:
3 S9 P, r. C# \ m. l 获得被注入进程未解析的dll的基地址。 获得必要的dll句柄和函数为修复导入表做准备。 分配一块新内存去取解析dll,并把pe头复制到新内存中和将各节复制到新内存中。 修复导入表和重定向表。 执行DllMain()函数。: H6 j/ F. Y& c' A9 j
2 n" P& y' _6 W, N. _# w8 r
8 R! e4 `( y. l; w- O& ~ 7 F; _3 m2 p. r8 ?6 C
核心代码如下:
/ o+ R1 K- J( f: y ManualMapInject.h" h0 ?9 d% U4 P' r& z+ a
#pragma once
#include "Injector.h"
using f_LoadLibraryA = HINSTANCE(WINAPI*)(const char* lpLibFilename);
using f_GetProcAddress = FARPROC(WINAPI*)(HMODULE hModule, LPCSTR lpProcName);
using f_DLL_ENTRY_POINT = BOOL(WINAPI*)(void* hDll, DWORD dwReason, void* pReserved);
#ifdef _WIN64
using f_RtlAddFunctionTable = BOOL(WINAPIV*)(PRUNTIME_FUNCTION FunctionTable, DWORD EntryCount, DWORD64 BaseAddress);
#endif
struct MANUAL_MAPPING_DATA
{
f_LoadLibraryA pLoadLibraryA;
f_GetProcAddress pGetProcAddress;
#ifdef _WIN64
f_RtlAddFunctionTable pRtlAddFunctionTable;
#endif
BYTE* pbase;
HINSTANCE hMod;
DWORD fdwReasonParam;
LPVOID reservedParam;
BOOL SEHSupport;
};
//Note: Exception support only x64 with build params /EHa or /EHc
bool ManualMapDll(HANDLE hProc, BYTE* pSrcData, SIZE_T FileSize, bool ClearHeader = true, bool ClearNonNeededSections = true, bool AdjustProtections = true, bool SEHExceptionSupport = true, DWORD fdwReason = DLL_PROCESS_ATTACH, LPVOID lpReserved = 0);
void __stdcall Shellcode(MANUAL_MAPPING_DATA* pData);
class CManualMapInject :public CInjector
{
public:
CManualMapInject();
virtual ~CManualMapInject();
virtual bool InjectorDLL(TCHAR* szPath,DWORD dwPid);
};
ManualMapInject.cpp
! x+ ^; F, T0 A2 Q/ T #include "pch.h"
#include "ManualMapInject.h"
#ifdef _WIN64
#define CURRENT_ARCH IMAGE_FILE_MACHINE_AMD64
#else
#define CURRENT_ARCH IMAGE_FILE_MACHINE_I386
#endif
#define RELOC_FLAG32(RelInfo) ((RelInfo >> 0x0C) == IMAGE_REL_BASED_HIGHLOW)
#define RELOC_FLAG64(RelInfo) ((RelInfo >> 0x0C) == IMAGE_REL_BASED_DIR64)
#ifdef _WIN64
#define RELOC_FLAG RELOC_FLAG64
#else
#define RELOC_FLAG RELOC_FLAG32
#endif
CManualMapInject::CManualMapInject()
{
}
CManualMapInject::~CManualMapInject()
{
}
bool CManualMapInject::InjectorDLL(TCHAR* szPath, DWORD dwPid)
{
HANDLE hProc = GetProcessHandle(dwPid);
if (!hProc || !IsCorrectTargetArchitecture(hProc) || GetFileAttributes(szPath) == INVALID_FILE_ATTRIBUTES)
{
return false;
}
// std::ifstream File(szPath, std::ios::binary | std::ios::ate);
//
// if (File.fail())
// {
// printf("Opening the file failed: %X\n", (DWORD)File.rdstate());
// File.close();
// CloseHandle(hProc);
// system("PAUSE");
// return -5;
// }
//
// auto FileSize = File.tellg();
// if (FileSize < 0x1000)
// {
// printf("Filesize invalid.\n");
// File.close();
// CloseHandle(hProc);
// system("PAUSE");
// return -6;
// }
//
// BYTE* pSrcData = new BYTE[(UINT_PTR)FileSize];
// if (!pSrcData)
// {
// printf("Can't allocate dll file.\n");
// File.close();
// CloseHandle(hProc);
// system("PAUSE");
// return -7;
// }
//
// File.seekg(0, std::ios::beg);
// File.read((char*)(pSrcData), FileSize);
// File.close();
CFile file;
file.Open(szPath, CFile::modeRead);
ULONGLONG nFileSize = file.GetLength();
BYTE* pSrcData = new BYTE[nFileSize];
ZeroMemory(pSrcData,nFileSize);
file.SeekToBegin();
file.Read(pSrcData,nFileSize);
file.Close();
if (!ManualMapDll(hProc, pSrcData, nFileSize))
{
delete[] pSrcData;
CloseHandle(hProc);
return false;
}
delete[] pSrcData;
CloseHandle(hProc);
return false;
}
bool ManualMapDll(HANDLE hProc, BYTE* pSrcData, SIZE_T FileSize, bool ClearHeader,
bool ClearNonNeededSections, bool AdjustProtections,
bool SEHExceptionSupport, DWORD fdwReason, LPVOID lpReserved)
{
IMAGE_NT_HEADERS* pOldNtHeader = nullptr;
IMAGE_OPTIONAL_HEADER* pOldOptHeader = nullptr;
IMAGE_FILE_HEADER* pOldFileHeader = nullptr;
BYTE* pTargetBase = nullptr;
if (reinterpret_cast<IMAGE_DOS_HEADER*>(pSrcData)->e_magic != 0x5A4D)//"MZ"
{
return false;
}
pOldNtHeader = reinterpret_cast<IMAGE_NT_HEADERS*>(pSrcData + reinterpret_cast<IMAGE_DOS_HEADER*>(pSrcData)->e_lfanew);
pOldOptHeader = &pOldNtHeader->OptionalHeader;
pOldFileHeader = &pOldNtHeader->FileHeader;
if (pOldFileHeader->Machine != CURRENT_ARCH)
{
return false;
}
pTargetBase = reinterpret_cast<BYTE*>(VirtualAllocEx(hProc, nullptr, pOldOptHeader->SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE));
if (!pTargetBase)
{
return false;
}