Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -359,4 +359,6 @@ MigrationBackup/
.ionide/

# Fody - auto-generated XML schema
FodyWeavers.xsd
FodyWeavers.xsd
out/*
CMakeSettings.json
29 changes: 29 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
cmake_minimum_required(VERSION 3.15)

project(TheCruZMMap)

set(SRC_ROOT "Manual Map Injector")

include(CBuildKit)

add_library_ns(thecruz manualmap STATIC ${SRC_ROOT}/injector.cpp)
target_include_dir_iface(thecruz-manualmap PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/${SRC_ROOT} include)
install_target_and_headers(thecruz manualmap)
target_compile_features(thecruz-manualmap PUBLIC cxx_std_17)

add_executable(injector ${SRC_ROOT}/main.cpp)
target_link_libraries(injector thecruz::manualmap)
target_compile_definitions(injector PRIVATE -DUNICODE -D_UNICODE)

install(FILES
${SRC_ROOT}/injector.h
DESTINATION include/TheCruZ)

configure_file(TheCruZMMapConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/TheCruZMMapConfig.cmake
@ONLY)

# Realize Installation of Configuration cmake file
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/TheCruZMMapConfig.cmake
DESTINATION lib/cmake/TheCruZ)
110 changes: 88 additions & 22 deletions Manual Map Injector/injector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,43 @@
#define CURRENT_ARCH IMAGE_FILE_MACHINE_I386
#endif

bool ManualMapDll(HANDLE hProc, BYTE* pSrcData, SIZE_T FileSize, bool ClearHeader, bool ClearNonNeededSections, bool AdjustProtections, bool SEHExceptionSupport, DWORD fdwReason, LPVOID lpReserved) {
std::optional<uint64_t> GetLdrpHandleTlsDataRVA()
{
return 0x54590;
}

std::optional<HMODULE> GetModuleBaseAddress(DWORD processID, const char* moduleName) {
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, processID);
if (hSnapshot == INVALID_HANDLE_VALUE) {
return std::nullopt;
}

MODULEENTRY32 moduleEntry;
moduleEntry.dwSize = sizeof(MODULEENTRY32);

if (Module32First(hSnapshot, &moduleEntry)) {
do {
if (strcmp(moduleEntry.szModule, moduleName) == 0) {
CloseHandle(hSnapshot);
return moduleEntry.hModule; // Return the base address
}
} while (Module32Next(hSnapshot, &moduleEntry));
}

CloseHandle(hSnapshot);
return std::nullopt; // Return nullopt if the module was not found
}

std::optional<void*> ManualMapDll(HANDLE hProc, BYTE* pSrcData, SIZE_T FileSize, bool ClearHeader, bool ClearNonNeededSections, bool AdjustProtections, bool SEHExceptionSupport, DWORD fdwReason, LPVOID lpReserved) {
auto procNtdll = GetModuleBaseAddress(GetProcessId(hProc), "ntdll.dll");
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"
ILog("Invalid file\n");
return false;
return std::nullopt;
}

pOldNtHeader = reinterpret_cast<IMAGE_NT_HEADERS*>(pSrcData + reinterpret_cast<IMAGE_DOS_HEADER*>(pSrcData)->e_lfanew);
Expand All @@ -29,21 +57,55 @@ bool ManualMapDll(HANDLE hProc, BYTE* pSrcData, SIZE_T FileSize, bool ClearHeade

if (pOldFileHeader->Machine != CURRENT_ARCH) {
ILog("Invalid platform\n");
return false;
return std::nullopt;
}

ILog("File ok\n");

pTargetBase = reinterpret_cast<BYTE*>(VirtualAllocEx(hProc, nullptr, pOldOptHeader->SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE));
if (!pTargetBase) {
ILog("Target process memory allocation failed (ex) 0x%X\n", GetLastError());
return false;
return std::nullopt;
}

std::unique_ptr<BYTE[]> emptyBuffStrg = std::make_unique<BYTE[]>(1024 * 1024 * 20);
BYTE* emptyBuffer = emptyBuffStrg.get();
if (emptyBuffer == nullptr) {
ILog("Unable to allocate memory\n");
return std::nullopt;
}
memset(emptyBuffer, 0, 1024 * 1024 * 20);

DWORD oldp = 0;
VirtualProtectEx(hProc, pTargetBase, pOldOptHeader->SizeOfImage, PAGE_EXECUTE_READWRITE, &oldp);

MANUAL_MAPPING_DATA data{ 0 };
data.pDummyLdr = (LDR_DATA_TABLE_ENTRY*)VirtualAllocEx(hProc, nullptr, sizeof(LDR_DATA_TABLE_ENTRY) * 4, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
WriteProcessMemory(hProc, data.pDummyLdr, emptyBuffer, sizeof(LDR_DATA_TABLE_ENTRY) * 4, nullptr);
data.pLdrpHandleTlsData = [&]() -> f_LdrpHandleTlsData {
if (!procNtdll)
return nullptr;

auto rva = GetLdrpHandleTlsDataRVA();

if (!rva)
{
ILog("LdrpHandleTlsData Not found\n");
return nullptr;
}

auto r = (f_LdrpHandleTlsData)((uint64_t)(*procNtdll) + *rva);

// Hash Like Integrity Check
if (*(uint16_t*)r != 0x8948 ||
*(uint16_t*)((char*)r + 5) != 0x8948)
{
ILog("LdrpHandleTlsData Integrity Check Failed\n");
return nullptr;
}

return r;
}();
data.pLoadLibraryA = LoadLibraryA;
data.pGetProcAddress = GetProcAddress;
#ifdef _WIN64
Expand All @@ -61,7 +123,7 @@ bool ManualMapDll(HANDLE hProc, BYTE* pSrcData, SIZE_T FileSize, bool ClearHeade
if (!WriteProcessMemory(hProc, pTargetBase, pSrcData, 0x1000, nullptr)) { //only first 0x1000 bytes for the header
ILog("Can't write file header 0x%X\n", GetLastError());
VirtualFreeEx(hProc, pTargetBase, 0, MEM_RELEASE);
return false;
return std::nullopt;
}

IMAGE_SECTION_HEADER* pSectionHeader = IMAGE_FIRST_SECTION(pOldNtHeader);
Expand All @@ -70,7 +132,7 @@ bool ManualMapDll(HANDLE hProc, BYTE* pSrcData, SIZE_T FileSize, bool ClearHeade
if (!WriteProcessMemory(hProc, pTargetBase + pSectionHeader->VirtualAddress, pSrcData + pSectionHeader->PointerToRawData, pSectionHeader->SizeOfRawData, nullptr)) {
ILog("Can't map sections: 0x%x\n", GetLastError());
VirtualFreeEx(hProc, pTargetBase, 0, MEM_RELEASE);
return false;
return std::nullopt;
}
}
}
Expand All @@ -80,14 +142,14 @@ bool ManualMapDll(HANDLE hProc, BYTE* pSrcData, SIZE_T FileSize, bool ClearHeade
if (!MappingDataAlloc) {
ILog("Target process mapping allocation failed (ex) 0x%X\n", GetLastError());
VirtualFreeEx(hProc, pTargetBase, 0, MEM_RELEASE);
return false;
return std::nullopt;
}

if (!WriteProcessMemory(hProc, MappingDataAlloc, &data, sizeof(MANUAL_MAPPING_DATA), nullptr)) {
ILog("Can't write mapping 0x%X\n", GetLastError());
VirtualFreeEx(hProc, pTargetBase, 0, MEM_RELEASE);
VirtualFreeEx(hProc, MappingDataAlloc, 0, MEM_RELEASE);
return false;
return std::nullopt;
}

//Shell code
Expand All @@ -96,15 +158,15 @@ bool ManualMapDll(HANDLE hProc, BYTE* pSrcData, SIZE_T FileSize, bool ClearHeade
ILog("Memory shellcode allocation failed (ex) 0x%X\n", GetLastError());
VirtualFreeEx(hProc, pTargetBase, 0, MEM_RELEASE);
VirtualFreeEx(hProc, MappingDataAlloc, 0, MEM_RELEASE);
return false;
return std::nullopt;
}

if (!WriteProcessMemory(hProc, pShellcode, Shellcode, 0x1000, nullptr)) {
ILog("Can't write shellcode 0x%X\n", GetLastError());
VirtualFreeEx(hProc, pTargetBase, 0, MEM_RELEASE);
VirtualFreeEx(hProc, MappingDataAlloc, 0, MEM_RELEASE);
VirtualFreeEx(hProc, pShellcode, 0, MEM_RELEASE);
return false;
return std::nullopt;
}

ILog("Mapped DLL at %p\n", pTargetBase);
Expand All @@ -125,7 +187,7 @@ bool ManualMapDll(HANDLE hProc, BYTE* pSrcData, SIZE_T FileSize, bool ClearHeade
VirtualFreeEx(hProc, pTargetBase, 0, MEM_RELEASE);
VirtualFreeEx(hProc, MappingDataAlloc, 0, MEM_RELEASE);
VirtualFreeEx(hProc, pShellcode, 0, MEM_RELEASE);
return false;
return std::nullopt;
}
CloseHandle(hThread);

Expand All @@ -137,7 +199,7 @@ bool ManualMapDll(HANDLE hProc, BYTE* pSrcData, SIZE_T FileSize, bool ClearHeade
GetExitCodeProcess(hProc, &exitcode);
if (exitcode != STILL_ACTIVE) {
ILog("Process crashed, exit code: %d\n", exitcode);
return false;
return std::nullopt;
}

MANUAL_MAPPING_DATA data_checked{ 0 };
Expand All @@ -149,7 +211,7 @@ bool ManualMapDll(HANDLE hProc, BYTE* pSrcData, SIZE_T FileSize, bool ClearHeade
VirtualFreeEx(hProc, pTargetBase, 0, MEM_RELEASE);
VirtualFreeEx(hProc, MappingDataAlloc, 0, MEM_RELEASE);
VirtualFreeEx(hProc, pShellcode, 0, MEM_RELEASE);
return false;
return std::nullopt;
}
else if (hCheck == (HINSTANCE)0x505050) {
ILog("WARNING: Exception support failed!\n");
Expand All @@ -158,13 +220,6 @@ bool ManualMapDll(HANDLE hProc, BYTE* pSrcData, SIZE_T FileSize, bool ClearHeade
Sleep(10);
}

BYTE* emptyBuffer = (BYTE*)malloc(1024 * 1024 * 20);
if (emptyBuffer == nullptr) {
ILog("Unable to allocate memory\n");
return false;
}
memset(emptyBuffer, 0, 1024 * 1024 * 20);

//CLEAR PE HEAD
if (ClearHeader) {
if (!WriteProcessMemory(hProc, pTargetBase, emptyBuffer, 0x1000, nullptr)) {
Expand Down Expand Up @@ -225,7 +280,7 @@ bool ManualMapDll(HANDLE hProc, BYTE* pSrcData, SIZE_T FileSize, bool ClearHeade
ILog("WARNING: can't release mapping data memory\n");
}

return true;
return pTargetBase;
}

#define RELOC_FLAG32(RelInfo) ((RelInfo >> 0x0C) == IMAGE_REL_BASED_HIGHLOW)
Expand Down Expand Up @@ -300,6 +355,17 @@ void __stdcall Shellcode(MANUAL_MAPPING_DATA* pData) {
}
}

if (pData->pLdrpHandleTlsData)
{
pData->pDummyLdr->DllBase = pBase;
pData->pLdrpHandleTlsData(pData->pDummyLdr);
}

/*Todo
* Unlink from TLS list pData->pDummyLdr
* Finally release pData->pDummyLdr
*/

if (pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].Size) {
auto* pTLS = reinterpret_cast<IMAGE_TLS_DIRECTORY*>(pBase + pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress);
auto* pCallback = reinterpret_cast<PIMAGE_TLS_CALLBACK*>(pTLS->AddressOfCallBacks);
Expand Down
32 changes: 31 additions & 1 deletion Manual Map Injector/injector.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#include <TlHelp32.h>
#include <stdio.h>
#include <string>
#include <MSPDBX/PDBSymRVAResolver.h>
#include <optional>

using f_LoadLibraryA = HINSTANCE(WINAPI*)(const char* lpLibFilename);
using f_GetProcAddress = FARPROC(WINAPI*)(HMODULE hModule, LPCSTR lpProcName);
Expand All @@ -15,10 +17,38 @@ using f_DLL_ENTRY_POINT = BOOL(WINAPI*)(void* hDll, DWORD dwReason, void* pReser
using f_RtlAddFunctionTable = BOOL(WINAPIV*)(PRUNTIME_FUNCTION FunctionTable, DWORD EntryCount, DWORD64 BaseAddress);
#endif

struct UNICODE_STR
{
USHORT Length;
USHORT MaximumLength;
PWSTR pBuffer;
};

struct LDR_DATA_TABLE_ENTRY
{
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
PVOID DllBase;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STR FullDllName;
UNICODE_STR BaseDllName;
ULONG Flags;
SHORT LoadCount;
SHORT TlsIndex;
LIST_ENTRY HashTableEntry;
ULONG TimeDateStamp;
};

using f_LdrpHandleTlsData = DWORD(*)(LDR_DATA_TABLE_ENTRY*);

struct MANUAL_MAPPING_DATA
{
f_LoadLibraryA pLoadLibraryA;
f_GetProcAddress pGetProcAddress;
f_LdrpHandleTlsData pLdrpHandleTlsData;
LDR_DATA_TABLE_ENTRY* pDummyLdr;
#ifdef _WIN64
f_RtlAddFunctionTable pRtlAddFunctionTable;
#endif
Expand All @@ -31,5 +61,5 @@ struct MANUAL_MAPPING_DATA


//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);
std::optional<void*> 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);
7 changes: 7 additions & 0 deletions TheCruZMMapConfig.cmake.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@PACKAGE_INIT@ # Init

include(CMakeFindDependencyMacro)

find_dependency(MSPDBX)

include(${CMAKE_CURRENT_LIST_DIR}/thecruz-manualmap-targets.cmake)