Originally Posted by
Cypher
I know for a fact you've ****ed up your test somehow. It is IMPOSSIBLE for the memory to be marked as free like that if you haven't hooked the API or manually changed the VADs.
So I modified the checking code slightly and I'm still wondering why I get the results I get. The information is correct prior to the "UNLINK" and module memory zeroing. This is the code I use when injecting into Launcher.exe for WoW for testing purposes.
Here is the DLL code:
Code:
#include <windows.h>
#include <winnt.h>
#include <WinBase.h>
#include <tlhelp32.h>
#include <shlwapi.h>
#include <stdio.h>
#pragma comment(lib, "shlwapi.lib")
#define UPPERCASE(x) if((x) >= 'a' && (x) <= 'z') (x) -= 'a' - 'A'
#define UNLINK(x) (x).Blink->Flink = (x).Flink; \
(x).Flink->Blink = (x).Blink;
#pragma pack(push, 1)
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
typedef struct _ModuleInfoNode
{
LIST_ENTRY LoadOrder;
LIST_ENTRY InitOrder;
LIST_ENTRY MemoryOrder;
HMODULE baseAddress; // Base address AKA module handle
unsigned long entryPoint;
unsigned int size; // Size of the modules image
UNICODE_STRING fullPath;
UNICODE_STRING name;
unsigned long flags;
unsigned short LoadCount;
unsigned short TlsIndex;
LIST_ENTRY HashTable; // A linked list of any other modules that have the same first letter
unsigned long timestamp;
} ModuleInfoNode, *pModuleInfoNode;
typedef struct _ProcessModuleInfo
{
unsigned int size; // Size of a ModuleInfo node?
unsigned int initialized;
HANDLE SsHandle;
LIST_ENTRY LoadOrder;
LIST_ENTRY InitOrder;
LIST_ENTRY MemoryOrder;
} ProcessModuleInfo, *pProcessModuleInfo;
#pragma pack(pop)
// Forward prototypes
void CloakDll( HMODULE hMod );
bool CloakModule( ModuleInfoNode * module );
void CheckModule(FILE * file, ModuleInfoNode * module);
// Globals
HMODULE hDLL = 0;
// Define the DLL's main function
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ulReason, LPVOID lpReserved) {
// Get rid of compiler warnings since we do not use this parameter
UNREFERENCED_PARAMETER(lpReserved);
switch( ulReason ) {
// If we are attaching to a process
case DLL_PROCESS_ATTACH:
//MessageBox(0, "DLL Locked and Loaded.", "DLL Injection Works!", 0);
hDLL = hModule;
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return (TRUE);
}
extern "C" __declspec(dllexport) void Initialize() {
DWORD ThreadID;
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&CloakDll, GetModuleHandle("GDI32.dll"), 0, &ThreadID);
}
void 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
}
ModuleInfoNode* origModule = 0, *tohideModule = 0;
module = (ModuleInfoNode *)(pmInfo->LoadOrder.Flink);
while( module->baseAddress ) {
if( module->baseAddress == hMod ) {
tohideModule = module;
}
else if ( module->baseAddress == hDLL ) {
origModule = module;
}
module = (ModuleInfoNode *)(module->LoadOrder.Flink);
}
FILE * file;
file = fopen("C:\\Users\\Public\\Cloak.txt", "w");
module = (ModuleInfoNode *)(pmInfo->LoadOrder.Flink);
// Check our Modules - should be there and fine
CheckModule(file, origModule);
CheckModule(file, tohideModule);
// Cloak the modules, if we found them, call return true/false but I don't check
// not sure why you would necessarily want to
CloakModule( origModule );
CloakModule( tohideModule );
// Check our work by printing all the modules for the PE we are injected
module = (ModuleInfoNode *)(pmInfo->LoadOrder.Flink);
while(module->baseAddress) {
fprintf(file, "Module: %S\n", module->fullPath.Buffer);
fflush(file);
module = (ModuleInfoNode *)(module->LoadOrder.Flink);
}
module = (ModuleInfoNode *)(pmInfo->LoadOrder.Flink);
// Check our Modules
CheckModule(file, origModule);
CheckModule(file, tohideModule);
// Close our file handles
fflush(file);
fclose(file);
// Exit our thread and free our DLL
if( hDLL ) {
__asm {
push -2
push 0
push hDLL
mov eax, TerminateThread
push eax
mov eax, FreeLibrary
jmp eax
}
}
}
bool CloakModule( ModuleInfoNode * module ) {
// Quick error checking in-case some module wasn't found
if( module->baseAddress ) {
// 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);
// 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
DWORD oldProt;
VirtualProtect(module->fullPath.Buffer, module->fullPath.Length, PAGE_READWRITE, &oldProt);
memset(module->fullPath.Buffer, 0, module->fullPath.Length);
VirtualProtect(module->fullPath.Buffer, module->fullPath.Length, oldProt, &oldProt);
// Zero out the memory of this module's node
VirtualProtect(module, sizeof(ModuleInfoNode), PAGE_READWRITE, &oldProt);
memset(module, 0, sizeof(ModuleInfoNode));
VirtualProtect(module, sizeof(ModuleInfoNode), oldProt, &oldProt);
return true;
}
return false;
}
void CheckModule(FILE * file, ModuleInfoNode * module){
MEMORY_BASIC_INFORMATION mbi;
DWORD oldProt;
VirtualProtect(module->baseAddress, module->size, PAGE_READWRITE, &oldProt);
SIZE_T retSize = VirtualQuery(module->baseAddress, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
fprintf(file, "\n===================================================================\n");
fprintf(file, "Checking Module: '%S'\n", module->fullPath.Buffer);
fprintf(file, "Size : %d\n", retSize);
fprintf(file, "BaseAddr : %p\n", mbi.BaseAddress);
fprintf(file, "AllocBase : %p\n", mbi.AllocationBase);
fprintf(file, "AllocProt : %08x\n", mbi.AllocationProtect);
fprintf(file, "State : %08x\n", mbi.State);
fprintf(file, "RegionSize : %d\n", mbi.RegionSize);
fprintf(file, "Type : %08x\n", mbi.Type);
VirtualProtect(module->baseAddress, module->size, oldProt, &oldProt);
fprintf(file, "\n");
}
Here are the results that get written to a file:
Code:
===================================================================
Checking Module: 'C:\Users\<name>\Documents\Visual Studio 10\Projects\SomeProject\Release\MyDLL.dll'
Size : 28
BaseAddr : 5EEE0000
AllocBase : 5EEE0000
AllocProt : 00000080
State : 00001000
RegionSize : 94208
Type : 01000000
===================================================================
Checking Module: 'C:\Windows\syswow64\GDI32.dll'
Size : 28
BaseAddr : 75A90000
AllocBase : 75A90000
AllocProt : 00000080
State : 00001000
RegionSize : 4096
Type : 01000000
Module: C:\Users\Public\Public Games\World of Warcraft\Launcher.exe
Module: C:\Windows\SysWOW64\ntdll.dll
Module: C:\Windows\syswow64\kernel32.dll
Module: C:\Windows\syswow64\KERNELBASE.dll
Module: C:\Windows\system32\iphlpapi.dll
Module: C:\Windows\syswow64\msvcrt.dll
Module: C:\Windows\syswow64\NSI.dll
Module: C:\Windows\system32\WINNSI.DLL
Module: C:\Windows\syswow64\RPCRT4.dll
Module: C:\Windows\syswow64\SspiCli.dll
Module: C:\Windows\syswow64\CRYPTBASE.dll
Module: C:\Windows\SysWOW64\sechost.dll
Module: C:\Windows\syswow64\USER32.dll
Module: C:\Windows\syswow64\LPK.dll
Module: C:\Windows\syswow64\USP10.dll
Module: C:\Windows\syswow64\ADVAPI32.dll
Module: C:\Windows\system32\MSIMG32.dll
Module: C:\Windows\syswow64\comdlg32.dll
Module: C:\Windows\syswow64\SHLWAPI.dll
Module: C:\Windows\WinSxS\x86_microsoft.windows.common-controls_6595b64144ccf1df_6.0.7600.16385_none_421189da2b7fabfc\COMCTL32.dll
Module: C:\Windows\syswow64\SHELL32.dll
Module: C:\Windows\system32\WINSPOOL.DRV
Module: C:\Windows\system32\oledlg.dll
Module: C:\Windows\syswow64\ole32.dll
Module: C:\Windows\syswow64\OLEAUT32.dll
Module: C:\Windows\syswow64\WS2_32.dll
Module: C:\Windows\syswow64\WININET.dll
Module: C:\Windows\syswow64\Normaliz.dll
Module: C:\Windows\syswow64\urlmon.dll
Module: C:\Windows\syswow64\CRYPT32.dll
Module: C:\Windows\syswow64\MSASN1.dll
Module: C:\Windows\syswow64\iertutil.dll
Module: C:\Windows\system32\VERSION.dll
Module: C:\Windows\system32\apphelp.dll
Module: C:\Windows\AppPatch\AcLayers.DLL
Module: C:\Windows\system32\USERENV.dll
Module: C:\Windows\system32\profapi.dll
Module: C:\Windows\system32\MPR.dll
Module: C:\Windows\system32\IMM32.DLL
Module: C:\Windows\syswow64\MSCTF.dll
Module: C:\Windows\system32\uxtheme.dll
Module: C:\Windows\system32\CRYPTSP.dll
Module: C:\Windows\system32\rsaenh.dll
Module: C:\Windows\system32\asycfilt.dll
Module: C:\Windows\syswow64\SETUPAPI.dll
Module: C:\Windows\syswow64\CFGMGR32.dll
Module: C:\Windows\syswow64\DEVOBJ.dll
Module: C:\Windows\syswow64\CLBCatQ.DLL
Module: C:\Windows\system32\propsys.dll
Module: C:\Windows\system32\ntmarta.dll
Module: C:\Windows\syswow64\WLDAP32.dll
Module: C:\Windows\system32\dwmapi.dll
Module: C:\Windows\SysWOW64\ieframe.dll
Module: C:\Windows\syswow64\PSAPI.DLL
Module: C:\Windows\SysWOW64\OLEACC.dll
Module: C:\Windows\system32\LINKINFO.dll
Module: C:\Windows\system32\SXS.DLL
Module: C:\Windows\system32\dnsapi.DLL
Module: C:\Windows\system32\RASAPI32.dll
Module: C:\Windows\system32\rasman.dll
Module: C:\Windows\system32\rtutils.dll
Module: C:\Windows\system32\sensapi.dll
Module: C:\Windows\system32\NLAapi.dll
Module: C:\Windows\system32\rasadhlp.dll
Module: C:\Windows\System32\mswsock.dll
Module: C:\Windows\System32\winrnr.dll
Module: C:\Windows\system32\napinsp.dll
Module: C:\Windows\system32\pnrpnsp.dll
Module: C:\Windows\System32\wshtcpip.dll
Module: C:\Windows\System32\wship6.dll
Module: C:\Windows\system32\peerdist.dll
Module: C:\Windows\system32\AUTHZ.dll
Module: C:\Windows\System32\fwpuclnt.dll
Module: C:\Windows\system32\MLANG.dll
Module: C:\Windows\SysWOW64\mshtml.dll
Module: C:\Windows\SysWOW64\msls31.dll
Module: C:\Windows\system32\msimtf.dll
Module: C:\Windows\SysWOW64\jscript.dll
Module: C:\Windows\system32\RpcRtRemote.dll
Module: C:\Windows\SysWow64\Macromed\Flash\Flash10c.ocx
Module: C:\Windows\system32\WINMM.dll
Module: C:\Windows\system32\mscms.dll
Module: C:\Windows\system32\MMDevAPI.DLL
Module: C:\Windows\system32\wdmaud.drv
Module: C:\Windows\system32\ksuser.dll
Module: C:\Windows\system32\AVRT.dll
Module: C:\Windows\system32\AUDIOSES.DLL
Module: C:\Windows\system32\msacm32.drv
Module: C:\Windows\system32\MSACM32.dll
Module: C:\Windows\system32\midimap.dll
Module: C:\Windows\system32\MSVCR100D.dll
===================================================================
Checking Module: '(null)'
Size : 28
BaseAddr : 00000000
AllocBase : 00000000
AllocProt : 00000000
State : 00010000
RegionSize : 65536
Type : 00000000
===================================================================
Checking Module: '(null)'
Size : 28
BaseAddr : 00000000
AllocBase : 00000000
AllocProt : 00000000
State : 00010000
RegionSize : 65536
Type : 00000000
EDIT: and I should qualify that I'm not claiming this will defeat NtQueryVirtualMemory(), but it does take care of VirtualQuery()
EDIT # 2: I started playing with using NtQueryVirtualMemory() to detect the module here is the code I came up with:
Code:
void checkNtQueryVirtualMemory(FILE * file, ModuleInfoNode * module) {
typedef enum _MEMORYINFOCLASS {
MemoryBasicInformation = 0,
MemoryWorkingSetList,
MemorySectionName,
MemoryBasicVlmInformation
} MEMORYINFOCLASS;
// Messing with NtQueryVirtualMemory
typedef NTSTATUS (WINAPI * NtQueryVirtualMemoryPtr) (
HANDLE ProcessHandle,
PVOID BaseAddress,
MEMORYINFOCLASS MemoryBasicClass,
PVOID Buffer,
ULONG MemoryInformationLength,
PULONG ReturnLength
);
typedef struct {
UNICODE_STRING SectionFileName;
WCHAR NameBuffer[ANYSIZE_ARRAY];
} MEMORY_SECTION_NAME, *PMEMORY_SECTION_NAME;
NtQueryVirtualMemoryPtr NtQueryVirtualMemory;
NTSTATUS ntStatus;
HMODULE hNTDLL;
MEMORY_BASIC_INFORMATION mbi;
MEMORY_SECTION_NAME msn;
hNTDLL = LoadLibraryA("ntdll.dll");
NtQueryVirtualMemory = (NtQueryVirtualMemoryPtr)GetProcAddress(hNTDLL, "NtQueryVirtualMemory");
ntStatus = NtQueryVirtualMemory(
GetCurrentProcess(),
module->baseAddress,
MemoryBasicInformation,
&mbi,
sizeof(MEMORY_BASIC_INFORMATION),
NULL
);
if( !ntStatus ) {
fprintf(file, "\nNtQueryVirtualMemory - Memory Basic Information\n");
fprintf(file, "===================================================================\n");
fprintf(file, "Checking Module: '%S'\n", module->fullPath.Buffer);
fprintf(file, "BaseAddr : %p\n", mbi.BaseAddress);
fprintf(file, "AllocBase : %p\n", mbi.AllocationBase);
fprintf(file, "AllocProt : %08x\n", mbi.AllocationProtect);
fprintf(file, "State : %08x\n", mbi.State);
fprintf(file, "RegionSize : %d\n", mbi.RegionSize);
fprintf(file, "Type : %08x\n", mbi.Type);
}
NtQueryVirtualMemory = (NtQueryVirtualMemoryPtr)GetProcAddress(hNTDLL, "NtQueryVirtualMemory");
ntStatus = NtQueryVirtualMemory(
GetCurrentProcess(),
module->baseAddress,
MemorySectionName,
&msn,
sizeof(MEMORY_SECTION_NAME),
NULL
);
fprintf(file, "ntStatus: 0x%08x\n", ntStatus);
if( !ntStatus ) {
fprintf(file, "\nNtQueryVirtualMemory - Memory Section Name\n");
fprintf(file, "===================================================================\n");
fprintf(file, "Name: '%S'\n", msn.SectionFileName.Buffer);
}
}
For MemoryBasicInformation, it gives the same results as VirtualQuery(), which it should because isn't one the wrapper around the other?
For MemorySectionName I get a NT_ERROR, so not sure what I do wrong there. Ideas?
here is the Output from those:
Code:
NtQueryVirtualMemory - Memory Basic Information
===================================================================
Checking Module: '(null)'
BaseAddr : 00000000
AllocBase : 00000000
AllocProt : 00000000
State : 00010000
RegionSize : 65536
Type : 00000000
ntStatus: 0xc0000141
EDIT #3: I fixed MemorySectionName to work, wasn't setting a large enough structure. So the first time I run it, before UNLINK happens I get:
Code:
NtQueryVirtualMemory - Memory Section Name
===================================================================
Name: '\Device\HarddiskVolume2\Windows\SysWOW64\gdi32.dll'
After UNLINK I get a NTSTATUS error 0xc0000141, which is proper because according to ntstatus.h:
Code:
#define STATUS_INVALID_ADDRESS ((NTSTATUS) 0xC0000141)
So looks like the UNLINK works just fine. Now, regarding the VAD (Virtual Address Descriptor), I've never looked at that stuff before, but from everything I've seen so far they require kernel-level access (usually via a driver). Does Warden use a driver?