Code:
#include "HideModule.h"
PLINKED_MODULE HideMe::FirstModule = NULL;
__declspec(naked) DWORD __stdcall HideMe::ZwQueryVirtualMemory_Trampoline(HANDLE hProcess, PVOID Address, MEMORY_INFORMATION_CLASS MemoryInformationClass, PVOID Buffer, ULONG Length, PULONG ResultLength)
{
__asm
{
mov eax, 0xB2;
nop;
nop;
nop;
nop;
nop;
__emit 0xCC;
}
}
DWORD __stdcall HideMe::ZwQueryVirtualMemory_Detour(HANDLE hProcess, PVOID Address, MEMORY_INFORMATION_CLASS MemoryInformationClass, PVOID Buffer, ULONG Length, PULONG ResultLength)
{
MEMORY_BASIC_INFORMATION m_mbi = { 0 };
DWORD dwRet = 0;
PLINKED_MODULE pMod;
dwRet = HideMe::ZwQueryVirtualMemory_Trampoline(hProcess, Address, MemoryInformationClass, &m_mbi, Length, ResultLength);
//error checking
if (dwRet != 0)
return dwRet;
pMod = HideMe::FirstModule;
while (pMod != NULL && pMod->dwBaseAddress != NULL)
{
if (m_mbi.AllocationBase == 0) break;
if ((DWORD)m_mbi.BaseAddress >= pMod->dwBaseAddress && (DWORD)m_mbi.BaseAddress <= pMod->dwEndAddress)
{
m_mbi.Protect = PAGE_NOACCESS;
dwRet = STATUS_UNSUCCESSFUL;
break;
}
if (pMod->Next == NULL)
break;
pMod = pMod->Next;
}
if (Buffer != NULL) memcpy(Buffer, &m_mbi, sizeof(m_mbi));
return dwRet;
}
__declspec(naked) DWORD __stdcall HideMe::ZwProtectVirtualMemory_Trampoline(HANDLE hProcess, PVOID Address, PULONG Size, ULONG NewProtect, PULONG OldProtect)
{
__asm
{
mov eax, 0x89;
nop;
nop;
nop;
nop;
nop;
__emit 0xCC;
}
}
DWORD __stdcall HideMe::ZwProtectVirtualMemory_Detour(HANDLE hProcess, PVOID Address, PULONG Size, ULONG NewProtect, PULONG OldProtect)
{
DWORD dwAddress = *(DWORD *)Address;
PLINKED_MODULE pMod = HideMe::FirstModule;
while (pMod != NULL && pMod->dwBaseAddress != NULL)
{
if (dwAddress >= pMod->dwBaseAddress && dwAddress <= pMod->dwEndAddress)
{
if (Address != NULL) *(DWORD *)Address = 0;
if (Size !=NULL) *Size = 0;
if (OldProtect != NULL) *OldProtect = PAGE_NOACCESS;
return STATUS_UNSUCCESSFUL;
}
if (pMod->Next == NULL)
break;
pMod = pMod->Next;
}
return HideMe::ZwProtectVirtualMemory_Trampoline(hProcess, Address, Size, NewProtect, OldProtect);
}
HideMe::HideMe()
{
LPVOID lpZwProtectVirtualMemory, lpZwQueryVirtualMemory;
HMODULE hNtDll;
//we obviously want to do this before adding any modules
//especially if we're going to be protecting pieces of code
//that need to be detoured!
this->PatchVirtualQuery();
this->PatchVirtualProtect();
if ((hNtDll = GetModuleHandle("ntdll.dll")) == NULL)
return;
if ((lpZwQueryVirtualMemory = GetProcAddress(hNtDll, "NtQueryVirtualMemory")) == NULL)
return;
if ((lpZwProtectVirtualMemory = GetProcAddress(hNtDll, "NtProtectVirtualMemory")) == NULL)
return;
//protect our patches without attempting to hide the modules
HideMe::FirstModule = new LINKED_MODULE((DWORD)lpZwQueryVirtualMemory, ((DWORD)lpZwQueryVirtualMemory + 0x0F));
HideMe::FirstModule->Next = new LINKED_MODULE((DWORD)lpZwProtectVirtualMemory, ((DWORD)lpZwProtectVirtualMemory + 0x0F));
}
HideMe::~HideMe()
{
//destruct, remove all modules and patches
HMODULE hNtDll;
LPVOID lpZwQueryVirtualMemory, lpZwProtectVirtualMemory;
DWORD dwOldProtect;
PLINKED_MODULE cur, next;
cur = HideMe::FirstModule;
while (cur->Next != NULL)
{
next = cur->Next;
if (cur)
delete cur;
cur = NULL;
cur = next;
}
if (HideMe::FirstModule)
delete HideMe::FirstModule;
HideMe::FirstModule = 0;
if ((hNtDll = GetModuleHandle("ntdll.dll")) == NULL)
return;
if ((lpZwQueryVirtualMemory = GetProcAddress(hNtDll, "NtQueryVirtualMemory")) == NULL)
return;
if ((lpZwProtectVirtualMemory = GetProcAddress(hNtDll, "NtProtectVirtualMemory")) == NULL)
return;
if (VirtualProtect(lpZwQueryVirtualMemory, sizeof(bOrigVirtualQuery), PAGE_EXECUTE_READWRITE, &dwOldProtect))
{
memcpy(lpZwQueryVirtualMemory, bOrigVirtualQuery, sizeof(bOrigVirtualQuery));
VirtualProtect(lpZwQueryVirtualMemory, sizeof(bOrigVirtualQuery), dwOldProtect, NULL);
}
if (VirtualProtect(lpZwProtectVirtualMemory, sizeof(bOrigVirtualProtect), PAGE_EXECUTE_READWRITE, &dwOldProtect))
{
memcpy(lpZwProtectVirtualMemory, bOrigVirtualProtect, sizeof(bOrigVirtualProtect));
VirtualProtect(lpZwProtectVirtualMemory, sizeof(bOrigVirtualProtect), dwOldProtect, NULL);
}
}
bool HideMe::AddModule(HMODULE hModule)
{
return this->AddModule((DWORD)hModule);
}
bool HideMe::AddModule(DWORD dwModuleBaseAddress)
{
DWORD dwModuleEndAddress;
DWORD dwSectionAlignment;
DWORD dwNtDllBase, dwNtDllEnd;
DWORD *dwSearcher;
PLINKED_MODULE pMod;
IMAGE_DOS_HEADER *pDosHeader;
IMAGE_NT_HEADERS *pNtHeader;
bool bFound = false;
MEMORY_BASIC_INFORMATION mbi = { 0 };
//find the MZ
if (*(WORD *)dwModuleBaseAddress != IMAGE_DOS_SIGNATURE)
return false;
//get the dos and nt headers
pDosHeader = (PIMAGE_DOS_HEADER)dwModuleBaseAddress;
pNtHeader = (PIMAGE_NT_HEADERS)(dwModuleBaseAddress + pDosHeader->e_lfanew);
//make sure it's really the base address of a PE module
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE)
return false;
//calculate end address of module
dwModuleEndAddress = dwModuleBaseAddress + pNtHeader->OptionalHeader.SizeOfImage;
//get the section alignment (generally 0x1000, but might as well!)
dwSectionAlignment = pNtHeader->OptionalHeader.SectionAlignment;
//now we're going to loop through the next few pages and add anything readable to
//the memory we're going to protect as our module
while (true)
{
if (VirtualQuery((void *)(dwModuleEndAddress + 1), &mbi, sizeof(mbi)) != sizeof(mbi))
break;
if (mbi.AllocationBase == 0)
break;
if ((mbi.Protect | PAGE_READONLY) != PAGE_READONLY)
break;
dwModuleEndAddress += dwSectionAlignment;
}
dwModuleEndAddress--;
pMod = HideMe::FirstModule;
if (pMod != NULL)
while (pMod->Next != NULL)
pMod = pMod->Next;
pMod->Next = new LINKED_MODULE(dwModuleBaseAddress, dwModuleEndAddress);
if (!this->CloakDll((HMODULE)dwModuleBaseAddress))
return false;
pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandle("ntdll.dll");
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
return false;
pNtHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE)
return false;
dwNtDllBase = pNtHeader->OptionalHeader.ImageBase;
dwNtDllEnd = dwNtDllBase + pNtHeader->OptionalHeader.SizeOfImage;
//search for any instances of our module base in ntdll.dll
//and null them out (if writable)
for (dwSearcher = (DWORD *)dwNtDllBase; dwSearcher < (DWORD *)dwNtDllEnd; dwSearcher++)
{
if (*dwSearcher == dwModuleBaseAddress)
{
if (!VirtualQuery(dwSearcher, &mbi, sizeof(mbi)))
continue;
if ((mbi.Protect & PAGE_READWRITE) == PAGE_READWRITE || (mbi.Protect & PAGE_EXECUTE_READWRITE) == PAGE_EXECUTE_READWRITE)
*dwSearcher = 0;
}
}
return true;
}
void HideMe::PatchVirtualQuery()
{
DWORD lpZwQueryVirtualMemory;
DWORD lpZwQueryTrampolineRelativeJump, lpZwQueryDetourRelativeJump;
DWORD dwOldProtect;
MEMORY_BASIC_INFORMATION mbi = {0};
//obviously, get the address of ZwQueryVirtualMemory
lpZwQueryVirtualMemory = (DWORD)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQueryVirtualMemory");
//create our relative jumps
lpZwQueryTrampolineRelativeJump = RELATIVE_JMP(((DWORD)HideMe::ZwQueryVirtualMemory_Trampoline + 5), lpZwQueryVirtualMemory + 5);
lpZwQueryDetourRelativeJump = RELATIVE_JMP(lpZwQueryVirtualMemory, (DWORD)HideMe::ZwQueryVirtualMemory_Detour);
//get the base address and region size
if (!VirtualQuery(HideMe::ZwQueryVirtualMemory_Trampoline, &mbi, sizeof(mbi)))
return;
//please, oh CPU god, forget everything you knew about what we're patching
if (!FlushInstructionCache(GetCurrentProcess(), mbi.BaseAddress, mbi.RegionSize))
return;
//make the part we're patching writable
if (!VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &dwOldProtect))
return;
//patch the jump
*(BYTE *)((DWORD)HideMe::ZwQueryVirtualMemory_Trampoline + 5) = 0xE9;
*(DWORD *)((DWORD)HideMe::ZwQueryVirtualMemory_Trampoline + 6) = lpZwQueryTrampolineRelativeJump;
//re-enable whatever protection it had beforehand
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, dwOldProtect, NULL);
//again, base address and region size
if (!VirtualQuery((void *)lpZwQueryVirtualMemory, &mbi, sizeof(mbi)))
return;
//again, flush cache so old shit doesn't get executed on accident
if (!FlushInstructionCache(GetCurrentProcess(), mbi.BaseAddress, mbi.RegionSize))
return;
//make it writable
if (!VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &dwOldProtect))
return;
memcpy((void *)lpZwQueryVirtualMemory, &bOrigVirtualQuery, sizeof(bOrigVirtualQuery));
//patch
*(BYTE *)lpZwQueryVirtualMemory = 0xE9;
*(DWORD *)(lpZwQueryVirtualMemory + 1) = lpZwQueryDetourRelativeJump;
//enable our own protection to prevent detours from being changed and (hopefully) detected
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE, &dwOldProtect);
}
void HideMe::PatchVirtualProtect()
{
DWORD lpZwProtectVirtualMemory;
DWORD lpZwProtectTrampolineRelativeJump, lpZwProtectDetourRelativeJump;
DWORD dwOldProtect;
MEMORY_BASIC_INFORMATION mbi = {0};
//obviously, get the address of ZwProtectVirtualMemory
lpZwProtectVirtualMemory = (DWORD)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtProtectVirtualMemory");
if (lpZwProtectVirtualMemory == 0)
return;
//create our relative jumps
lpZwProtectTrampolineRelativeJump = RELATIVE_JMP(((DWORD)HideMe::ZwProtectVirtualMemory_Trampoline + 5), lpZwProtectVirtualMemory + 5);
lpZwProtectDetourRelativeJump = RELATIVE_JMP(lpZwProtectVirtualMemory, (DWORD)HideMe::ZwProtectVirtualMemory_Detour);
//get the base address and region size
if (!VirtualQuery(HideMe::ZwProtectVirtualMemory_Trampoline, &mbi, sizeof(mbi)))
return;
//please, oh CPU god, forget everything you knew about what we're patching
if (!FlushInstructionCache(GetCurrentProcess(), mbi.BaseAddress, mbi.RegionSize))
return;
//make the part we're patching writable
if (!VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &dwOldProtect))
return;
//patch the jump
*(BYTE *)((DWORD)HideMe::ZwProtectVirtualMemory_Trampoline + 5) = 0xE9;
*(DWORD *)((DWORD)HideMe::ZwProtectVirtualMemory_Trampoline + 6) = lpZwProtectTrampolineRelativeJump;
//re-enable whatever protection it had beforehand
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, dwOldProtect, NULL);
//again, base address and region size
if (!VirtualQuery((void *)lpZwProtectVirtualMemory, &mbi, sizeof(mbi)))
return;
//again, flush cache so old shit doesn't get executed on accident
if (!FlushInstructionCache(GetCurrentProcess(), mbi.BaseAddress, mbi.RegionSize))
return;
//make it writable
if (!VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &dwOldProtect))
return;
memcpy((void *)lpZwProtectVirtualMemory, &bOrigVirtualProtect, sizeof(bOrigVirtualProtect));
//patch
*(BYTE *)lpZwProtectVirtualMemory = 0xE9;
*(DWORD *)(lpZwProtectVirtualMemory + 1) = lpZwProtectDetourRelativeJump;
//enable our own protection to prevent detours from being changed and (hopefully) detected
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE, &dwOldProtect);
}
//credit Darawk from CloakDLL.cpp
bool HideMe::CloakDll(HMODULE hMod)
{
ProcessModuleInfo *pmInfo;
ModuleInfoNode *module;
_asm
{
mov eax, fs:[18h] // TEB
mov eax, [eax + 30h] // PEB
mov eax, [eax + 0Ch] // PROCESS_MODULE_INFO
mov pmInfo, eax
}
module = (ModuleInfoNode *)(pmInfo->LoadOrder.Flink);
while(module->baseAddress && module->baseAddress != hMod)
module = (ModuleInfoNode *)(module->LoadOrder.Flink);
if(!module->baseAddress)
return false;
// Remove the module entry from the list here
///////////////////////////////////////////////////
// Unlink from the load order list
UNLINK(module->LoadOrder);
// Unlink from the init order list
UNLINK(module->InitOrder);
// Unlink from the memory order list
UNLINK(module->MemoryOrder);
// Unlink from the hash table
UNLINK(module->HashTable);
// Erase all traces that it was ever there
///////////////////////////////////////////////////
// This code will pretty much always be optimized into a rep stosb/stosd pair
// so it shouldn't cause problems for relocation.
// Zero out the module name
memset(module->fullPath.Buffer, 0, module->fullPath.Length);
// Zero out the memory of this module's node
memset(module, 0, sizeof(ModuleInfoNode));
return true;
}
Scan.cpp by me