what is the best way to create ur load i made it with load library and some1 told me that load library is not good or secured he suggested to make it using manual mapping
but when i made it with manual mapping now i have issue loading libprotobuf.lib to combile the protobuf messages using protobuf
so please tell me what do u really think
here is how iam using my manual mapper
but when i made it with manual mapping now i have issue loading libprotobuf.lib to combile the protobuf messages using protobuf
so please tell me what do u really think
here is how iam using my manual mapper
C++:
DWORD GetProcessIdByName(const std::wstring& processName) {
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snapshot == INVALID_HANDLE_VALUE) {
return 0;
}
PROCESSENTRY32W pe32;
pe32.dwSize = sizeof(pe32);
if (!Process32FirstW(snapshot, &pe32)) {
CloseHandle(snapshot);
return 0;
}
do {
if (_wcsicmp(pe32.szExeFile, processName.c_str()) == 0) {
CloseHandle(snapshot);
return pe32.th32ProcessID;
}
} while (Process32NextW(snapshot, &pe32));
CloseHandle(snapshot);
return 0;
}
std::string GetDllPath() {
// Get the directory where the injector is located
char exePath[MAX_PATH];
GetModuleFileNameA(nullptr, exePath, MAX_PATH);
std::filesystem::path injectorPath(exePath);
std::filesystem::path dllPath = injectorPath.parent_path() / "BetterDota.dll";
return dllPath.string();
}
// Get the Dota 2 game directory from the running process
std::string GetGameDirectory(DWORD processId) {
HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, processId);
if (!hProcess) return "";
char exePath[MAX_PATH];
DWORD size = MAX_PATH;
if (!QueryFullProcessImageNameA(hProcess, 0, exePath, &size)) {
CloseHandle(hProcess);
return "";
}
CloseHandle(hProcess);
std::filesystem::path gamePath(exePath);
return gamePath.parent_path().string();
}
// Copy dependency DLLs to game directory for manual mapping
bool CopyDependencies(const std::string& injectorDir, const std::string& gameDir) {
std::vector<std::string> dependencies = { "sentry.dll" };
for (const auto& dll : dependencies) {
std::filesystem::path srcPath = std::filesystem::path(injectorDir) / dll;
std::filesystem::path dstPath = std::filesystem::path(gameDir) / dll;
if (std::filesystem::exists(srcPath)) {
try {
// Only copy if it doesn't exist or is different
if (!std::filesystem::exists(dstPath)) {
std::filesystem::copy_file(srcPath, dstPath);
std::cout << COLOR_GREEN << "[+] Copied " << dll << " to game directory\n" << COLOR_RESET;
}
} catch (const std::exception& e) {
std::cout << COLOR_YELLOW << "[!] Warning: Could not copy " << dll << ": " << e.what() << "\n" << COLOR_RESET;
}
}
}
return true;
}
int main() {
EnableColors();
PrintBanner();
// Find DLL path
std::string dllPath = GetDllPath();
if (!std::filesystem::exists(dllPath)) {
std::cout << COLOR_RED << "[!] DLL not found: " << dllPath << "\n" << COLOR_RESET;
std::cout << COLOR_YELLOW << "[*] Make sure BetterDota.dll is in the same folder as this injector\n" << COLOR_RESET;
std::cout << "\nPress Enter to exit...";
std::cin.get();
return 1;
}
std::cout << COLOR_CYAN << "[*] DLL Path: " << dllPath << "\n" << COLOR_RESET;
std::cout << COLOR_YELLOW << "[*] Waiting for dota2.exe...\n" << COLOR_RESET;
// Wait for Dota 2 process
DWORD processId = 0;
while (processId == 0) {
processId = GetProcessIdByName(L"dota2.exe");
if (processId == 0) {
Sleep(1000);
std::cout << ".";
}
}
std::cout << "\n" << COLOR_GREEN << "[+] Found dota2.exe (PID: " << processId << ")\n" << COLOR_RESET;
// Copy dependencies to game directory
std::string gameDir = GetGameDirectory(processId);
if (!gameDir.empty()) {
std::filesystem::path injectorPath(dllPath);
CopyDependencies(injectorPath.parent_path().string(), gameDir);
// Set game directory for manual mapper to use full paths
ManualMapper::SetGameDirectory(gameDir);
std::cout << COLOR_CYAN << "[*] Game directory: " << gameDir << "\n" << COLOR_RESET;
}
// Wait a bit for game to initialize
std::cout << COLOR_YELLOW << "[*] Waiting for game to initialize...\n" << COLOR_RESET;
Sleep(5000);
// Inject using Manual Mapping (no LoadLibrary!)
std::cout << COLOR_CYAN << "[*] Manual mapping DLL...\n" << COLOR_RESET;
if (ManualMapper::InjectDll(processId, dllPath)) {
std::cout << COLOR_GREEN << "\n[+] Manual map injection successful!\n" << COLOR_RESET;
std::cout << COLOR_GREEN << "[+] DLL is now hidden from module list!\n" << COLOR_RESET;
} else {
std::cout << COLOR_RED << "\n[!] Manual map injection failed!\n" << COLOR_RESET;
}
std::cout << "\nPress Enter to exit...";
std::cin.get();
return 0;
}
peb_loader.h:
#pragma once
#include <cstdint>
extern "C" {
uint64_t FindDllByName(const char* dllName);
uint64_t FindApiByName(uint64_t dllBase, const char* apiName);
uint32_t ApiHashing(const char* str);
uint32_t DllHashing(const wchar_t* str);
uint64_t GetLdr();
uint64_t GetDllBase(uint64_t ldrEntry);
uint64_t GetNextLdr(uint64_t ldrEntry);
uint64_t GetDllBuffer(uint64_t ldrEntry);
uint64_t FindDllByHash(uint32_t hash);
uint64_t GetApiAddr(uint64_t dllBase, uint32_t hash);
}
manualmapper.h:
namespace ManualMapper {
// Global: Game directory for loading dependencies with full path
inline std::string g_gameDirectory;
inline void SetGameDirectory(const std::string& dir) {
g_gameDirectory = dir;
}
// Helper: Get module base address in target process
inline UINT_PTR GetRemoteModuleBase(HANDLE hProcess, DWORD processId, const char* moduleName) {
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, processId);
if (hSnap == INVALID_HANDLE_VALUE) {
return 0;
}
MODULEENTRY32W me32;
me32.dwSize = sizeof(me32);
if (!Module32FirstW(hSnap, &me32)) {
CloseHandle(hSnap);
return 0;
}
// Convert module name to wide string for comparison
wchar_t wideModName[256];
MultiByteToWideChar(CP_ACP, 0, moduleName, -1, wideModName, 256);
do {
if (_wcsicmp(me32.szModule, wideModName) == 0) {
CloseHandle(hSnap);
return (UINT_PTR)me32.modBaseAddr;
}
} while (Module32NextW(hSnap, &me32));
CloseHandle(hSnap);
return 0;
}
// Helper: Get function address in target process by loading the DLL locally and calculating offset
inline UINT_PTR GetRemoteProcAddress(HANDLE hProcess, DWORD processId, const char* moduleName, const char* funcName, WORD ordinal = 0) {
// First, get the module base in the target process
UINT_PTR remoteBase = GetRemoteModuleBase(hProcess, processId, moduleName);
// If module not loaded in target, we need to inject LoadLibrary call first
if (remoteBase == 0) {
// Try loading it via LoadLibraryA in the target process
HMODULE hKernel32 = GetModuleHandleA("kernel32.dll");
FARPROC pLoadLibraryA = GetProcAddress(hKernel32, "LoadLibraryA");
// Build full path for non-system DLLs
std::string loadPath = moduleName;
if (!g_gameDirectory.empty() &&
_stricmp(moduleName, "kernel32.dll") != 0 &&
_stricmp(moduleName, "ntdll.dll") != 0 &&
_stricmp(moduleName, "user32.dll") != 0 &&
_stricmp(moduleName, "d3d11.dll") != 0 &&
_stricmp(moduleName, "dxgi.dll") != 0) {
// Check if it's a custom DLL that we need to load with full path
std::string fullPath = g_gameDirectory + "\\" + moduleName;
// Check if file exists at full path
DWORD attrs = GetFileAttributesA(fullPath.c_str());
if (attrs != INVALID_FILE_ATTRIBUTES) {
loadPath = fullPath;
std::cout << MM_CYAN << "[*] Using full path: " << loadPath << "\n" << MM_RESET;
}
}
// Allocate memory for module path in target
size_t nameLen = loadPath.length() + 1;
LPVOID pRemoteName = VirtualAllocEx(hProcess, nullptr, nameLen, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!pRemoteName) return 0;
WriteProcessMemory(hProcess, pRemoteName, loadPath.c_str(), nameLen, nullptr);
// Create remote thread to load the library
HANDLE hThread = CreateRemoteThread(hProcess, nullptr, 0, (LPTHREAD_START_ROUTINE)pLoadLibraryA, pRemoteName, 0, nullptr);
if (hThread) {
WaitForSingleObject(hThread, 5000);
CloseHandle(hThread);
}
VirtualFreeEx(hProcess, pRemoteName, 0, MEM_RELEASE);
// Try again
remoteBase = GetRemoteModuleBase(hProcess, processId, moduleName);
if (remoteBase == 0) {
std::cout << MM_RED << "[!] Could not load " << moduleName << " in target\n" << MM_RESET;
return 0;
}
std::cout << MM_GREEN << "[+] Loaded " << moduleName << " in target at 0x" << std::hex << remoteBase << std::dec << "\n" << MM_RESET;
}
// Now load locally and calculate offset - also use full path
std::string localLoadPath = moduleName;
if (!g_gameDirectory.empty()) {
std::string fullPath = g_gameDirectory + "\\" + moduleName;
DWORD attrs = GetFileAttributesA(fullPath.c_str());
if (attrs != INVALID_FILE_ATTRIBUTES) {
localLoadPath = fullPath;
}
}
HMODULE hLocal = GetModuleHandleA(moduleName);
if (!hLocal) {
hLocal = LoadLibraryA(localLoadPath.c_str());
}
if (!hLocal) {
std::cout << MM_RED << "[!] Could not load " << moduleName << " locally\n" << MM_RESET;
return 0;
}
FARPROC localAddr;
if (ordinal != 0) {
localAddr = GetProcAddress(hLocal, (LPCSTR)ordinal);
} else {
localAddr = GetProcAddress(hLocal, funcName);
}
if (!localAddr) {
return 0;
}
// Calculate offset and apply to remote base
UINT_PTR offset = (UINT_PTR)localAddr - (UINT_PTR)hLocal;
return remoteBase + offset;
}
class Mapper {
private:
HANDLE m_hProcess = nullptr;
DWORD m_processId = 0;
public:
Mapper(DWORD processId) : m_processId(processId) {}
~Mapper() { if (m_hProcess) CloseHandle(m_hProcess); }
bool MapDll(const std::string& dllPath) {
// Read DLL file
std::ifstream file(dllPath, std::ios::binary | std::ios::ate);
if (!file.is_open()) {
std::cout << MM_RED << "[!] Failed to open DLL file: " << dllPath << MM_RESET << "\n";
return false;
}
auto fileSize = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<BYTE> fileData((size_t)fileSize);
file.read((char*)fileData.data(), fileSize);
file.close();
std::cout << MM_GREEN << "[+] DLL loaded: " << fileSize << " bytes\n" << MM_RESET;
return MapFromMemory(fileData.data(), fileData.size());
}
bool MapFromMemory(BYTE* pFileData, size_t fileSize) {
// Validate PE
auto* pDosHeader = (IMAGE_DOS_HEADER*)pFileData;
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
std::cout << MM_RED << "[!] Invalid DOS signature\n" << MM_RESET;
return false;
}
auto* pNtHeaders = (IMAGE_NT_HEADERS*)(pFileData + pDosHeader->e_lfanew);
if (pNtHeaders->Signature != IMAGE_NT_SIGNATURE) {
std::cout << MM_RED << "[!] Invalid NT signature\n" << MM_RESET;
return false;
}
if (!(pNtHeaders->FileHeader.Characteristics & IMAGE_FILE_DLL)) {
std::cout << MM_RED << "[!] Not a DLL\n" << MM_RESET;
return false;
}
if (pNtHeaders->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
std::cout << MM_RED << "[!] Not a 64-bit DLL\n" << MM_RESET;
return false;
}
std::cout << MM_GREEN << "[+] PE validated (x64 DLL)\n" << MM_RESET;
// Open target process
m_hProcess = OpenProcess(
PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION |
PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ,
FALSE, m_processId
);
if (!m_hProcess) {
std::cout << MM_RED << "[!] Failed to open process. Error: " << GetLastError() << MM_RESET << "\n";
return false;
}
std::cout << MM_GREEN << "[+] Process opened\n" << MM_RESET;
SIZE_T imageSize = pNtHeaders->OptionalHeader.SizeOfImage;
// Allocate memory for the image
LPVOID pRemoteImage = VirtualAllocEx(m_hProcess, nullptr, imageSize,
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!pRemoteImage) {
std::cout << MM_RED << "[!] Failed to allocate image memory\n" << MM_RESET;
return false;
}
std::cout << MM_GREEN << "[+] Image memory: 0x" << std::hex << (UINT_PTR)pRemoteImage << std::dec << "\n" << MM_RESET;
// Prepare local image - copy headers and sections with proper layout
std::vector<BYTE> localImage(imageSize, 0);
// Copy headers
memcpy(localImage.data(), pFileData, pNtHeaders->OptionalHeader.SizeOfHeaders);
// Copy sections
auto* pSection = IMAGE_FIRST_SECTION(pNtHeaders);
for (WORD i = 0; i < pNtHeaders->FileHeader.NumberOfSections; i++, pSection++) {
if (pSection->SizeOfRawData > 0) {
memcpy(
localImage.data() + pSection->VirtualAddress,
pFileData + pSection->PointerToRawData,
min(pSection->SizeOfRawData, pSection->Misc.VirtualSize ? pSection->Misc.VirtualSize : pSection->SizeOfRawData)
);
}
}
std::cout << MM_GREEN << "[+] Sections mapped\n" << MM_RESET;
// ========================================
// Process Relocations LOCALLY
// ========================================
UINT_PTR delta = (UINT_PTR)pRemoteImage - pNtHeaders->OptionalHeader.ImageBase;
std::cout << MM_YELLOW << "[*] Relocation delta: 0x" << std::hex << delta << std::dec << "\n" << MM_RESET;
if (delta != 0) {
auto* pRelocDir = &pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
if (pRelocDir->Size > 0) {
auto* pReloc = (IMAGE_BASE_RELOCATION*)(localImage.data() + pRelocDir->VirtualAddress);
BYTE* pRelocEnd = (BYTE*)pReloc + pRelocDir->Size;
while ((BYTE*)pReloc < pRelocEnd && pReloc->VirtualAddress && pReloc->SizeOfBlock >= sizeof(IMAGE_BASE_RELOCATION)) {
UINT numEntries = (pReloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
WORD* pEntry = (WORD*)(pReloc + 1);
for (UINT i = 0; i < numEntries; i++, pEntry++) {
WORD type = *pEntry >> 12;
WORD offset = *pEntry & 0xFFF;
if (pReloc->VirtualAddress + offset >= imageSize)
continue;
BYTE* pPatch = localImage.data() + pReloc->VirtualAddress + offset;
if (type == IMAGE_REL_BASED_DIR64) {
*(UINT_PTR*)pPatch += delta;
}
else if (type == IMAGE_REL_BASED_HIGHLOW) {
*(DWORD*)pPatch += (DWORD)delta;
}
}
pReloc = (IMAGE_BASE_RELOCATION*)((BYTE*)pReloc + pReloc->SizeOfBlock);
}
std::cout << MM_GREEN << "[+] Relocations processed\n" << MM_RESET;
}
}
// ========================================
// Process Imports - Resolve to TARGET PROCESS addresses
// ========================================
auto* pImportDir = &pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
if (pImportDir->Size > 0) {
auto* pImportDesc = (IMAGE_IMPORT_DESCRIPTOR*)(localImage.data() + pImportDir->VirtualAddress);
while (pImportDesc->Name) {
char* szModName = (char*)(localImage.data() + pImportDesc->Name);
std::cout << MM_YELLOW << "[*] Resolving: " << szModName << "\n" << MM_RESET;
// Get thunks
auto* pOrigThunk = pImportDesc->OriginalFirstThunk ?
(IMAGE_THUNK_DATA*)(localImage.data() + pImportDesc->OriginalFirstThunk) : nullptr;
auto* pThunk = (IMAGE_THUNK_DATA*)(localImage.data() + pImportDesc->FirstThunk);
if (!pOrigThunk) pOrigThunk = pThunk;
while (pOrigThunk->u1.AddressOfData) {
UINT_PTR procAddr = 0;
if (IMAGE_SNAP_BY_ORDINAL(pOrigThunk->u1.Ordinal)) {
WORD ordinal = (WORD)IMAGE_ORDINAL(pOrigThunk->u1.Ordinal);
procAddr = GetRemoteProcAddress(m_hProcess, m_processId, szModName, nullptr, ordinal);
}
else {
auto* pImport = (IMAGE_IMPORT_BY_NAME*)(localImage.data() + pOrigThunk->u1.AddressOfData);
procAddr = GetRemoteProcAddress(m_hProcess, m_processId, szModName, pImport->Name, 0);
}
if (!procAddr) {
auto* pImport = (IMAGE_IMPORT_BY_NAME*)(localImage.data() + pOrigThunk->u1.AddressOfData);
std::cout << MM_RED << "[!] Failed to resolve: " << szModName << "!"
<< (pImport ? pImport->Name : "ordinal") << "\n" << MM_RESET;
VirtualFreeEx(m_hProcess, pRemoteImage, 0, MEM_RELEASE);
return false;
}
pThunk->u1.Function = procAddr;
pOrigThunk++;
pThunk++;
}
pImportDesc++;
}
std::cout << MM_GREEN << "[+] All imports resolved (target process addresses)\n" << MM_RESET;
}
// ========================================
// Write the processed image to target
// ========================================
if (!WriteProcessMemory(m_hProcess, pRemoteImage, localImage.data(), imageSize, nullptr)) {
std::cout << MM_RED << "[!] Failed to write image. Error: " << GetLastError() << "\n" << MM_RESET;
VirtualFreeEx(m_hProcess, pRemoteImage, 0, MEM_RELEASE);
return false;
}
std::cout << MM_GREEN << "[+] Image written to target process\n" << MM_RESET;
// ========================================
// Prepare and execute DllMain caller shellcode
// ========================================
DWORD entryRVA = pNtHeaders->OptionalHeader.AddressOfEntryPoint;
if (entryRVA == 0) {
std::cout << MM_YELLOW << "[*] No entry point - done\n" << MM_RESET;
return true;
}
UINT_PTR pEntryPoint = (UINT_PTR)pRemoteImage + entryRVA;
std::cout << MM_CYAN << "[*] Entry point: 0x" << std::hex << pEntryPoint << std::dec << "\n" << MM_RESET;
// ========================================
// Get exception directory for RtlAddFunctionTable
// ========================================
auto* pExceptionDir = &pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
UINT_PTR pExceptionTable = 0;
DWORD exceptionTableSize = 0;
DWORD exceptionEntryCount = 0;
if (pExceptionDir->Size > 0) {
pExceptionTable = (UINT_PTR)pRemoteImage + pExceptionDir->VirtualAddress;
exceptionTableSize = pExceptionDir->Size;
exceptionEntryCount = exceptionTableSize / sizeof(RUNTIME_FUNCTION);
std::cout << MM_GREEN << "[+] Exception table: " << exceptionEntryCount << " entries at 0x"
<< std::hex << pExceptionTable << std::dec << "\n" << MM_RESET;
}
// ========================================
// Get TLS directory for TLS callbacks (needed for CRT)
// ========================================
auto* pTlsDir = &pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS];
UINT_PTR pTlsCallbacks = 0;
DWORD tlsCallbackCount = 0;
if (pTlsDir->Size > 0 && pTlsDir->VirtualAddress > 0) {
// Safety check: make sure VirtualAddress is within bounds
if (pTlsDir->VirtualAddress < imageSize) {
auto* pTls = (IMAGE_TLS_DIRECTORY*)(localImage.data() + pTlsDir->VirtualAddress);
// TLS callbacks are stored as a null-terminated array of function pointers
// After relocation, AddressOfCallBacks points to the REMOTE address
if (pTls->AddressOfCallBacks) {
// The AddressOfCallBacks was relocated to point to remote memory
// We need to convert it back to local offset to read the callback array
UINT_PTR remoteCallbacksAddr = pTls->AddressOfCallBacks;
// Check if it's a valid remote address (should be within our mapped image)
UINT_PTR remoteBase = (UINT_PTR)pRemoteImage;
if (remoteCallbacksAddr >= remoteBase && remoteCallbacksAddr < remoteBase + imageSize) {
UINT_PTR callbacksOffset = remoteCallbacksAddr - remoteBase;
pTlsCallbacks = remoteCallbacksAddr;
// Count callbacks (in local image) - safely
if (callbacksOffset + sizeof(UINT_PTR) <= imageSize) {
UINT_PTR* pLocalCallbacks = (UINT_PTR*)(localImage.data() + callbacksOffset);
// Limit to 16 callbacks max to prevent infinite loop
while (callbacksOffset + (tlsCallbackCount + 1) * sizeof(UINT_PTR) <= imageSize
&& *pLocalCallbacks && tlsCallbackCount < 16) {
tlsCallbackCount++;
pLocalCallbacks++;
}
if (tlsCallbackCount > 0) {
std::cout << MM_GREEN << "[+] TLS callbacks: " << tlsCallbackCount << " at 0x"
<< std::hex << pTlsCallbacks << std::dec << "\n" << MM_RESET;
}
}
}
}
}
}
// Get RtlAddFunctionTable address (same in all processes)
HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
UINT_PTR pRtlAddFunctionTable = (UINT_PTR)GetProcAddress(hNtdll, "RtlAddFunctionTable");
if (!pRtlAddFunctionTable) {
std::cout << MM_YELLOW << "[!] RtlAddFunctionTable not found - exceptions may not work\n" << MM_RESET;
}
// Shellcode data structure - expanded to include exception registration and TLS
#pragma pack(push, 1)
struct ShellcodeData {
UINT_PTR pDllMain; // offset 0x00
UINT_PTR pImageBase; // offset 0x08
UINT_PTR pRtlAddFunctionTable; // offset 0x10
UINT_PTR pExceptionTable; // offset 0x18
DWORD exceptionEntryCount; // offset 0x20
DWORD imageSize; // offset 0x24
UINT_PTR pTlsCallbacks; // offset 0x28
DWORD tlsCallbackCount; // offset 0x30
};
#pragma pack(pop)
// x64 shellcode to:
// 1. Call RtlAddFunctionTable to register exception handlers
// 2. Call TLS callbacks (for CRT initialization)
// 3. Call DllMain(pImageBase, DLL_PROCESS_ATTACH, NULL)
//
// Data layout at [pData]:
// +0x00: pDllMain
// +0x08: pImageBase
// +0x10: pRtlAddFunctionTable
// +0x18: pExceptionTable
// +0x20: exceptionEntryCount (DWORD)
// +0x24: imageSize (DWORD)
// +0x28: pTlsCallbacks
// +0x30: tlsCallbackCount (DWORD)
unsigned char shellcode[] = {
// Prologue - save RCX (pData) and setup stack
0x55, // push rbp
0x48, 0x89, 0xE5, // mov rbp, rsp
0x48, 0x83, 0xEC, 0x50, // sub rsp, 80 (shadow + alignment + locals)
0x48, 0x89, 0x4D, 0xF8, // mov [rbp-8], rcx (save pData)
// Check if we have an exception table to register
0x48, 0x8B, 0x41, 0x18, // mov rax, [rcx+0x18] (pExceptionTable)
0x48, 0x85, 0xC0, // test rax, rax
0x74, 0x1E, // jz skip_exception (30 bytes forward)
// RtlAddFunctionTable(FunctionTable=RCX, EntryCount=RDX, BaseAddress=R8)
0x48, 0x8B, 0x4D, 0xF8, // mov rcx, [rbp-8] (reload pData)
0x48, 0x8B, 0x49, 0x18, // mov rcx, [rcx+0x18] -> RCX = pExceptionTable
0x48, 0x8B, 0x55, 0xF8, // mov rdx, [rbp-8]
0x8B, 0x52, 0x20, // mov edx, [rdx+0x20] -> EDX = entryCount
0x4C, 0x8B, 0x45, 0xF8, // mov r8, [rbp-8]
0x4D, 0x8B, 0x40, 0x08, // mov r8, [r8+0x08] -> R8 = pImageBase
0x48, 0x8B, 0x45, 0xF8, // mov rax, [rbp-8]
0xFF, 0x50, 0x10, // call [rax+0x10] (call pRtlAddFunctionTable)
// skip_exception: Call DllMain(hModule=RCX, fdwReason=RDX, lpReserved=R8)
0x48, 0x8B, 0x45, 0xF8, // mov rax, [rbp-8] (pData)
0x48, 0x8B, 0x48, 0x08, // mov rcx, [rax+0x08] -> RCX = pImageBase (hModule)
0xBA, 0x01, 0x00, 0x00, 0x00, // mov edx, 1 -> EDX = DLL_PROCESS_ATTACH
0x4D, 0x31, 0xC0, // xor r8, r8 -> R8 = NULL (lpReserved)
0xFF, 0x10, // call [rax] (call pDllMain)
// Epilogue
0x48, 0x89, 0xEC, // mov rsp, rbp
0x5D, // pop rbp
0xC3 //ret
};
// Allocate memory for data + shellcode
SIZE_T totalSize = sizeof(ShellcodeData) + sizeof(shellcode) + 16;
LPVOID pRemote = VirtualAllocEx(m_hProcess, nullptr, totalSize,
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!pRemote) {
std::cout << MM_YELLOW << "[!] Could not allocate shellcode - image mapped but DllMain not called\n" << MM_RESET;
return true;
}
// Prepare data
ShellcodeData data;
data.pDllMain = pEntryPoint;
data.pImageBase = (UINT_PTR)pRemoteImage;
data.pRtlAddFunctionTable = pRtlAddFunctionTable;
data.pExceptionTable = pExceptionTable;
data.exceptionEntryCount = exceptionEntryCount;
data.imageSize = (DWORD)imageSize;
data.pTlsCallbacks = pTlsCallbacks;
data.tlsCallbackCount = tlsCallbackCount;
// Write data
WriteProcessMemory(m_hProcess, pRemote, &data, sizeof(data), nullptr);
// Write shellcode after data (aligned)
LPVOID pShellcode = (LPVOID)(((UINT_PTR)pRemote + sizeof(ShellcodeData) + 15) & ~15);
WriteProcessMemory(m_hProcess, pShellcode, shellcode, sizeof(shellcode), nullptr);
std::cout << MM_CYAN << "[*] Executing init shellcode (exception reg + DllMain)...\n" << MM_RESET;
// Execute: CreateRemoteThread calls shellcode, passing pRemote (data ptr) in RCX
HANDLE hThread = CreateRemoteThread(m_hProcess, nullptr, 0,
(LPTHREAD_START_ROUTINE)pShellcode, pRemote, 0, nullptr);
if (!hThread) {
std::cout << MM_RED << "[!] CreateRemoteThread failed: " << GetLastError() << "\n" << MM_RESET;
VirtualFreeEx(m_hProcess, pRemote, 0, MEM_RELEASE);
return true; // Image is still mapped
}
// Wait for DllMain to complete
DWORD waitResult = WaitForSingleObject(hThread, 20000);
DWORD exitCode = 0;
GetExitCodeThread(hThread, &exitCode);
CloseHandle(hThread);
// Cleanup shellcode memory
VirtualFreeEx(m_hProcess, pRemote, 0, MEM_RELEASE);
if (waitResult == WAIT_TIMEOUT) {
std::cout << MM_YELLOW << "[!] DllMain execution timed out\n" << MM_RESET;
} else {
std::cout << MM_GREEN << "[+] DllMain returned: " << exitCode << "\n" << MM_RESET;
}
if (exitCode == 0) {
std::cout << MM_RED << "[!] DllMain returned FALSE - injection may have failed\n" << MM_RESET;
}
std::cout << MM_GREEN << "\n[+] SUCCESS! DLL manually mapped at: 0x"
<< std::hex << (UINT_PTR)pRemoteImage << std::dec << "\n" << MM_RESET;
std::cout << MM_GREEN << "[+] DLL is NOT visible in module list!\n" << MM_RESET;
std::cout << MM_GREEN << "[+] Exception handlers registered!\n" << MM_RESET;
return true;
}
};
inline bool InjectDll(DWORD processId, const std::string& dllPath) {
Mapper mapper(processId);
return mapper.MapDll(dllPath);
}
}
Последнее редактирование: