More WoWMimic Detection Code
Well, we have found yet another hole in WoWMimic that is criminally easy to abuse. Credits to Kynox and Harko for working on this with me.
This code abuses the fact that the WoWMimic idiots are only hooking VirtualQuery, and none of the lower control paths, so we can easily call VirtualQueryEx to verify that the results we get back are untainted. If we find something that has been modified we know we have a hidden page. Mimic only hides the base page, not the entire module, so at the point I’ve marked “Warden function to do hashing goes here”, you simply need to do standard hashing of  data in the page, something which Warden already does (the reason for the hook in the first place).
void FindHiddenPages()
{
std::cout << “Start!” << std::endl << std::hex;
PVOID p1 = 0, p2 = 0;
MEMORY_BASIC_INFORMATION MyMbi = { 0 };
typedef std::vector< std::pair<PVOID,PVOID> > tAddresses;
tAddresses AddressList;
tAddresses Hidden;
while (VirtualQuery(p1,&MyMbi,sizeof(MyMbi)) == sizeof(MyMbi))
{
if (MyMbi.State == MEM_COMMIT && MyMbi.AllocationBase != p2 &&
MyMbi.AllocationBase == MyMbi.BaseAddress)
{
AddressList.push_back(std::make_pair(MyMbi.AllocationBase,MyMbi.BaseAddress) );
}
p2 = MyMbi.AllocationBase;
p1 = reinterpret_cast<PVOID>(reinterpret_cast<DWORD_PTR>(p1) + MyMbi.RegionSize);
}
p1 = p2 = 0;
while (VirtualQueryEx(GetCurrentProcess(),p1,&MyMbi,sizeof(MyMbi)) == sizeof(MyMbi))
{
if (MyMbi.State == MEM_COMMIT && MyMbi.AllocationBase != p2 &&
MyMbi.AllocationBase == MyMbi.BaseAddress)
{
std::pair<PVOID,PVOID> MyAddresses(MyMbi.AllocationBase,MyMbi.BaseAddress);
const tAddresses::const_iterator iCurrent(std::find(AddressList.begin(),AddressList.end(), MyAddresses));
if (iCurrent == AddressList.end())
Hidden.push_back(MyAddresses);
}
p2 = MyMbi.AllocationBase;
p1 = reinterpret_cast<PVOID>(reinterpret_cast<DWORD_PTR>(p1) + MyMbi.RegionSize);
}
for (tAddresses::const_iterator i = Hidden.begin(); i != Hidden.end(); ++i)
{
std::cout << “Hidden page! Allocation Base: ” << i->first
<< “. Base Address: ” << i->second << “.” << std::endl;
// Warden function to do hashing goes here
}
std::cout << “End!” << std::endl << std::dec;
}
On its own, even without hashing, you still have ground for a kick at the very least because hiding memory pages is something that only malicious software normally does. Once you add hashing though you have a guarantee of no false positives so you can go ahead and ban for it.
Yet again it seems that WoWMimic just doesn’t stand up when it comes to the detection test. Don’t trust their website, this thing is trivial to detect.
I’m curious about the manual VAD walking you mentioned. How could you do that in userland, since IIRC all the VAD’s live in nonpaged pool?
@amadmonk
Like I said, I’ve personally never done it. I’ll do some research though and get back to you if I have time.