Archive

Posts Tagged ‘wowmimic’

Hardcoded IPC Name Still There

May 21st, 2009

It seems that whilst the WoWMimic folks have ‘kinda’ listened to Kynox, Harko, Myself, etc. they still have selective hearing. Although they’ve started hooking VirtualQueryEx (they still haven’t worked out to hook the NT API it seems) the hardcoded IPC name seems to still be there.

That means that this still works: In-process WoWMimic Detection

On a nicer note though it seems that they have finally realized that they are not immune to Warden. Granted, they learned this the hard way (it seems that either WoW or Warden is detecting it in some shape or form), but at least it finally happened.

Still no WoWMimic build

May 17th, 2009

Come on WoWMimic devs, push a new build! I’m getting bored here.

More WoWMimic Detection Code

May 15th, 2009

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.

WoWMimic API Hook List

May 15th, 2009

Here is a list of functions WoWMimic hooks.

Version .44

VirtualQuery

GetCursorPos

SetCursorPos

FindWindowW

FindWindowA

FindWindowExW

FindWindowExA

EnumWindows

EnumChildWindows

Version .43
VirtualQuery
GetCursorPos
SetCursorPos
Its obvious that I’m the cause of the .44 update, but what they don’t seem to realize is twofold:
  1. Warden can follow their hooked code path and hash their function. Obviously its going to have a unique signature. If you find the WoWMimic hook then just ban for it.
  2. Warden can also bypass all their hooks entirely by just doing manual syscall code for each major OS version. That is a very easy thing to do, and no amount of usermode hooking will be able to stop it. (Kernelmode is the only option)

Not only that, but they’re missing half the funcitons they need to hook. All warden needs to do is call VirtualQueryEx and they’ve bypassed the hook. It astounds me how stupid the WoWMimic team is. Lastly, afaik it’s possible to manually walk the VAD tree (I have never attempted it but have seen code that can do it). No amount of API hooking can protect you from that either.

To the WoWMimic devs:
Tip: Unless you decide to elevate to the kernel (which still won’t protect you from stack traces) or actively attack warden (which is a LOT of work and still impossible to get 100% right, even if you’re an expert), you’re screwed. It’s that simple. At the very least, if you’re going to do usermode protection, at least do a decent job, your current half-assed attempt is just plain sad.

WoWMimic Adds Window Finding API Hooks

May 14th, 2009

While doing the reversing for my previous post (In-process WoWMimic Detection) I noticed something funny. It seems the WoWMimic team took my ‘joke’ detection quite seriously and started hooking the Window finding APIs. Here’s the code:

___:10021860 ; =============== S U B R O U T I N E =======================================

___:10021860

___:10021860

___:10021860 HookWindowFinders proc near             ; CODE XREF: DllMain(x,x,x)+40p

___:10021860

___:10021860 var_4           = byte ptr -4

___:10021860

___:10021860                 push    ecx

___:10021861                 push    ebx

___:10021862                 push    ebp

___:10021863                 push    esi

___:10021864                 mov     esi, GetModuleHandleW

___:1002186A                 push    edi

___:1002186B                 push    offset aFindwindoww ; “FindWindowW”

___:10021870                 push    offset aUser32_dll ; “user32.dll”

___:10021875                 call    esi ; GetModuleHandleW

___:10021877                 mov     edi, GetProcAddress

___:1002187D                 push    eax

___:1002187E                 call    edi ; GetProcAddress

___:10021880                 mov     ebp, GetCurrentProcess

___:10021886                 mov     dword_10072238, eax

___:1002188B                 call    ebp ; GetCurrentProcess

___:1002188D                 mov     ecx, dword_10072238

___:10021893                 mov     ebx, WriteProcessMemory

___:10021899                 mov     dword_10072224, eax

___:1002189E                 mov     byte_10072230, 0E9h

___:100218A5                 mov     edx, [ecx]

___:100218A7                 mov     dword_10072228, edx

___:100218AD                 movzx   edx, byte ptr [ecx+4]

___:100218B1                 mov     byte_1007222C, dl

___:100218B7                 mov     edx, offset dword_10021120

___:100218BC                 sub     edx, ecx

___:100218BE                 sub     edx, 5

___:100218C1                 mov     dword_10072231, edx

___:100218C7                 lea     edx, [esp+14h+var_4]

___:100218CB                 push    edx

___:100218CC                 push    5

___:100218CE                 push    offset byte_10072230

___:100218D3                 push    ecx

___:100218D4                 push    eax

___:100218D5                 call    ebx ; WriteProcessMemory

___:100218D7                 push    offset aFindwindowa ; “FindWindowA”

___:100218DC                 push    offset aUser32_dll ; “user32.dll”

___:100218E1                 call    esi ; GetModuleHandleW

___:100218E3                 push    eax

___:100218E4                 call    edi ; GetProcAddress

___:100218E6                 mov     dword_10072254, eax

___:100218EB                 call    ebp ; GetCurrentProcess

___:100218ED                 mov     ecx, dword_10072254

___:100218F3                 mov     dword_10072240, eax

___:100218F8                 mov     byte_1007224C, 0E9h

___:100218FF                 mov     edx, [ecx]

___:10021901                 mov     dword_10072244, edx

___:10021907                 movzx   edx, byte ptr [ecx+4]

___:1002190B                 mov     byte_10072248, dl

___:10021911                 mov     edx, offset dword_10021200

___:10021916                 sub     edx, ecx

___:10021918                 sub     edx, 5

___:1002191B                 mov     dword_1007224D, edx

___:10021921                 lea     edx, [esp+14h+var_4]

___:10021925                 push    edx

___:10021926                 push    5

___:10021928                 push    offset byte_1007224C

___:1002192D                 push    ecx

___:1002192E                 push    eax

___:1002192F                 call    ebx ; WriteProcessMemory

___:10021931                 push    offset aFindwindowexw ; “FindWindowExW”

___:10021936                 push    offset aUser32_dll ; “user32.dll”

___:1002193B                 call    esi ; GetModuleHandleW

___:1002193D                 push    eax

___:1002193E                 call    edi ; GetProcAddress

___:10021940                 mov     dword_10072270, eax

___:10021945                 call    ebp ; GetCurrentProcess

___:10021947                 mov     ecx, dword_10072270

___:1002194D                 mov     dword_1007225C, eax

___:10021952                 mov     byte_10072268, 0E9h

___:10021959                 mov     edx, [ecx]

___:1002195B                 mov     dword_10072260, edx

___:10021961                 movzx   edx, byte ptr [ecx+4]

___:10021965                 mov     byte_10072264, dl

___:1002196B                 mov     edx, offset dword_100212E0

___:10021970                 sub     edx, ecx

___:10021972                 sub     edx, 5

___:10021975                 mov     dword_10072269, edx

___:1002197B                 lea     edx, [esp+14h+var_4]

___:1002197F                 push    edx

___:10021980                 push    5

___:10021982                 push    offset byte_10072268

___:10021987                 push    ecx

___:10021988                 push    eax

___:10021989                 call    ebx ; WriteProcessMemory

___:1002198B                 push    offset aFindwindowexa ; “FindWindowExA”

___:10021990                 push    offset aUser32_dll ; “user32.dll”

___:10021995                 call    esi ; GetModuleHandleW

___:10021997                 push    eax

___:10021998                 call    edi ; GetProcAddress

___:1002199A                 mov     dword_1007228C, eax

___:1002199F                 call    ebp ; GetCurrentProcess

___:100219A1                 mov     ecx, dword_1007228C

___:100219A7                 mov     dword_10072278, eax

___:100219AC                 mov     byte_10072284, 0E9h

___:100219B3                 mov     edx, [ecx]

___:100219B5                 mov     dword_1007227C, edx

___:100219BB                 movzx   edx, byte ptr [ecx+4]

___:100219BF                 mov     byte_10072280, dl

___:100219C5                 mov     edx, offset dword_100213E0

___:100219CA                 sub     edx, ecx

___:100219CC                 sub     edx, 5

___:100219CF                 mov     dword_10072285, edx

___:100219D5                 lea     edx, [esp+14h+var_4]

___:100219D9                 push    edx

___:100219DA                 push    5

___:100219DC                 push    offset byte_10072284

___:100219E1                 push    ecx

___:100219E2                 push    eax

___:100219E3                 call    ebx ; WriteProcessMemory

___:100219E5                 push    offset aEnumwindows ; “EnumWindows”

___:100219EA                 push    offset aUser32_dll ; “user32.dll”

___:100219EF                 call    esi ; GetModuleHandleW

___:100219F1                 push    eax

___:100219F2                 call    edi ; GetProcAddress

___:100219F4                 mov     dword_100722A8, eax

___:100219F9                 call    ebp ; GetCurrentProcess

___:100219FB                 mov     ecx, dword_100722A8

___:10021A01                 mov     dword_10072294, eax

___:10021A06                 mov     byte_100722A0, 0E9h

___:10021A0D                 mov     edx, [ecx]

___:10021A0F                 mov     dword_10072298, edx

___:10021A15                 movzx   edx, byte ptr [ecx+4]

___:10021A19                 mov     byte_1007229C, dl

___:10021A1F                 mov     edx, offset dword_10021660

___:10021A24                 sub     edx, ecx

___:10021A26                 sub     edx, 5

___:10021A29                 mov     dword_100722A1, edx

___:10021A2F                 lea     edx, [esp+14h+var_4]

___:10021A33                 push    edx

___:10021A34                 push    5

___:10021A36                 push    offset byte_100722A0

___:10021A3B                 push    ecx

___:10021A3C                 push    eax

___:10021A3D                 call    ebx ; WriteProcessMemory

___:10021A3F                 push    offset aEnumchildwindo ; “EnumChildWindows”

___:10021A44                 push    offset aUser32_dll ; “user32.dll”

___:10021A49                 call    esi ; GetModuleHandleW

___:10021A4B                 push    eax

___:10021A4C                 call    edi ; GetProcAddress

___:10021A4E                 mov     dword_100722C4, eax

___:10021A53                 call    ebp ; GetCurrentProcess

___:10021A55                 mov     ecx, dword_100722C4

___:10021A5B                 mov     esi, eax

___:10021A5D                 mov     dword_100722B0, esi

___:10021A63                 mov     byte_100722BC, 0E9h

___:10021A6A                 mov     eax, [ecx]

___:10021A6C                 mov     dword_100722B4, eax

___:10021A71                 movzx   edx, byte ptr [ecx+4]

___:10021A75                 mov     byte_100722B8, dl

___:10021A7B                 lea     edx, [esp+14h+var_4]

___:10021A7F                 push    edx

___:10021A80                 push    5

___:10021A82                 push    offset byte_100722BC

___:10021A87                 mov     eax, offset dword_100216C0

___:10021A8C                 sub     eax, ecx

___:10021A8E                 push    ecx

___:10021A8F                 sub     eax, 5

___:10021A92                 push    esi

___:10021A93                 mov     dword_100722BD, eax

___:10021A98                 call    ebx ; WriteProcessMemory

___:10021A9A                 pop     edi

___:10021A9B                 pop     esi

___:10021A9C                 pop     ebp

___:10021A9D                 pop     ebx

___:10021A9E                 pop     ecx

___:10021A9F                 retn

___:10021A9F HookWindowFinders endp

_

Just for the laughs, I might release some code that can detect the windows despite the hooks, but that’s something for another day. I just wanted to point out the fact they had gone to the trouble of doing it, which is kinda funny really. It’s kinda like having a huge gaping hole in your ship, the size of a basketball, and also a tiny hole the size of a pinhead. So what would a normal person do? You’d try to plug up the basketball sized hole of course. But not WoWMimic, pinhead is their middle name.

In-process WoWMimic Detection

May 14th, 2009

The following is code that will detect if WoWMimic is running using fully in-process checks. That means that you can lock it down with guest mode, hide the window, etc all you want, this will still work, because we’re operating entirely inside WoW.exe’s address space.

Before we begin, huge thanks to Kynox for unpacking Melete (WoWMimic’s injected DLL).

WoWMimic needs a way for its DLL to communicate with its application component, it does this through shared memory. Here is the disassembled function responsible for setting this up. I have commented the virtualized API calls to make it apparent what is going on:

___:1002F630 ; =============== S U B R O U T I N E =======================================

___:1002F630

___:1002F630 ; Attributes: bp-based frame

___:1002F630

___:1002F630 sub_1002F630    proc near               ; CODE XREF: DllMain(x,x,x)+45p

___:1002F630

___:1002F630 var_24          = dword ptr -24h

___:1002F630 var_20          = dword ptr -20h

___:1002F630 var_1C          = dword ptr -1Ch

___:1002F630 var_10          = dword ptr -10h

___:1002F630 var_8           = dword ptr -8

___:1002F630 var_4           = dword ptr -4

___:1002F630

___:1002F630                 push    ebp

___:1002F631                 mov     ebp, esp

___:1002F633                 push    0FFFFFFFEh

___:1002F635                 push    offset dword_10070488

___:1002F63A                 push    offset __except_handler4

___:1002F63F                 mov     eax, large fs:0

___:1002F645                 push    eax

___:1002F646                 sub     esp, 14h

___:1002F649                 push    ebx

___:1002F64A                 push    esi

___:1002F64B                 push    edi

___:1002F64C                 mov     eax, dword_10072054

___:1002F651                 xor     [ebp+var_8], eax

___:1002F654                 xor     eax, ebp

___:1002F656                 push    eax

___:1002F657                 lea     eax, [ebp+var_10]

___:1002F65A                 mov     large fs:0, eax

___:1002F660                 xor     ebx, ebx

___:1002F662                 mov     [ebp+var_24], ebx

___:1002F665                 mov     [ebp+var_1C], ebx

___:1002F668                 xor     esi, esi

___:1002F66A                 mov     [ebp+var_20], esi

___:1002F66D                 mov     [ebp+var_4], ebx

___:1002F670                 push    offset a0xa0c82e ; “0xA0C82E”

___:1002F675                 push    ebx

___:1002F676                 push    2

___:1002F678                 call    near ptr 3E0000h ; OpenFileMappingW

___:1002F67D                 nop

___:1002F67E                 mov     edi, eax

___:1002F680                 mov     [ebp+var_1C], edi

___:1002F683                 cmp     edi, ebx

___:1002F685                 jz      short loc_1002F6D8

___:1002F687                 push    0Ch

___:1002F689                 push    ebx

___:1002F68A                 push    ebx

___:1002F68B                 push    2

___:1002F68D                 push    edi

___:1002F68E                 call    near ptr 3E0370h ; MapViewOfFile

___:1002F693                 nop

___:1002F694                 mov     esi, eax

___:1002F696                 mov     [ebp+var_20], esi

___:1002F699                 cmp     esi, ebx

___:1002F69B                 jz      short loc_1002F6D8

___:1002F69D                 push    ebx

___:1002F69E                 push    ebx

___:1002F69F                 mov     eax, [esi]

___:1002F6A1                 push    eax

___:1002F6A2                 push    offset loc_1002F570

___:1002F6A7                 push    ebx

___:1002F6A8                 push    ebx

___:1002F6A9                 call    near ptr 21092Eh ; CreateThread

___:1002F6AE                 nop

___:1002F6AF                 mov     dword_100730C0, eax

___:1002F6B4                 cmp     eax, ebx

___:1002F6B6                 jz      short loc_1002F6D8

___:1002F6B8                 mov     ecx, [esi+10h]

___:1002F6BB                 mov     dword_100730C4, ecx

___:1002F6C1                 mov     edx, [esi+20h]

___:1002F6C4                 mov     dword_100730C8, edx

___:1002F6CA                 mov     [ebp+var_24], 1

___:1002F6D1                 mov     [esi], ebx

___:1002F6D3                 call    sub_10058CD0

___:1002F6D8

___:1002F6D8 loc_1002F6D8:                           ; CODE XREF: sub_1002F630+55j

___:1002F6D8                                         ; sub_1002F630+6Bj …

___:1002F6D8                 mov     [ebp+var_4], 0FFFFFFFEh

___:1002F6DF                 call    sub_1002F701

___:1002F6E4                 mov     eax, [ebp+var_24]

___:1002F6E7                 mov     ecx, [ebp+var_10]

___:1002F6EA                 mov     large fs:0, ecx

___:1002F6F1                 pop     ecx

___:1002F6F2                 pop     edi

___:1002F6F3                 pop     esi

___:1002F6F4                 pop     ebx

___:1002F6F5                 mov     esp, ebp

___:1002F6F7                 pop     ebp

___:1002F6F8                 retn

___:1002F6F8 sub_1002F630    endp

The file name they have is hardcoded and doesn’t change (even across mimic versions), so it’s obvious what we can do to abuse this fact:
bool IsMimicInjected()
{
HANDLE MimicIPC = OpenFileMapping(FILE_MAP_READ,FALSE,”0xA0C82E”);
if (!MimicIPC)
return false;
// You could do even more to check here by inspecting the memory
// mapped file but that is unnecessary.
CloseHandle(MimicIPC);
return true;
}
There you have it. WoWMimic detected yet again. This time though the bar has been raised heavily, as you can no longer hide just by restricing WoW’s security descriptors.
Your move, WoWMimic.

Finding WoWMimic

May 7th, 2009

Hey, called this little snippet “WardenMimic” because it’s just mimicing what warden would have to do to detect the fail bot known as WoWMimic. Very simple example, but afaik similar to how Glider was detected (i.e. via window names and contents). A more sophisticated attack would use hashing of the remote processes memory, but that’s totally unnecessary because  WoWMimic don’t bother to obfuscate or hide anything.

Keep in mind, this was written in literally 5 minutes, a much more solid and reliable method would not be much extra work (maybe just another 5?). The code is designed to be in a DLL that is loaded by (or injected into) WoW.exe so that it is in the same context and has the same privilege level as Warden would. I made sure to load WoW via WoWMimic to ensure anything they would be doing against Warden would happen against me also, though they let you attach on the fly so I don’t think security is one of their major concerns.

Please note that I don’t have a WoWMimic sub so I just downloaded it off their site and am amusing nothing special happens if you actually run the bot that activates any window hiding or obfuscation. Let me know if this is the case and I’ll get a sub and release a new finder.

// Windows API
#include <Windows.h>
#include <io.h>
#include <fcntl.h>
#include <tchar.h>

// C++ Standard Library
#include <iostream>
#include <cstdio>
#include <vector>

// StealthLib
#include “Conditional.h”
#include “Cloaker.h”
#include “Injector.h”

// Holds windows from EnumWindows
std::vector<HWND> Windows;

// Top level window enumeration callback
BOOL CALLBACK MyEnumWindowsProc(HWND hwnd, LPARAM /*lParam*/)
{
Windows.push_back(hwnd);
return TRUE;
}

// Child level window enumeration callback
BOOL CALLBACK MyEnumChildWindowsProc(HWND hwnd, LPARAM /*lParam*/)
{
std::vector<TCHAR> Buffer(MAX_PATH);
if (GetWindowText(hwnd,&Buffer[0],MAX_PATH) &&
std::tstring(&Buffer[0]) == TEXT(”http://www.mimicusa.com”))
std::cout << “Found WoWMimic URL label!” << std::endl;
Buffer.clear();
Buffer.resize(MAX_PATH);
if (GetWindowText(hwnd,&Buffer[0],MAX_PATH) &&
std::tstring(&Buffer[0]) == TEXT(”Us Ver 3.0.0.42″))
std::cout << “Found WoWMimic version label!” << std::endl;
return TRUE;
}

// DLL entry point
BOOL WINAPI DllMain(HINSTANCE hinstDLL,
DWORD fdwReason,
LPVOID /*lpvReserved*/)
{
// Stop unreferenced param warning when __STEALTH is undefined
UNREFERENCED_PARAMETER(hinstDLL);

#ifdef __CONSOLE
// Whether to free the process’s console upon detach
static bool NeedFree = false;
#endif

// Reason for calling DllMain
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
{
#ifdef __CONSOLE
// Set up debug console
NeedFree = (AllocConsole() ? true : false);
FILE* fpOld = NULL;
freopen_s( &fpOld, “CONOUT$”, “w”, stdout );
#endif

EnumWindows(MyEnumWindowsProc,NULL);
for each (HWND Current in Windows)
{
EnumChildWindows(Current,MyEnumChildWindowsProc,NULL);
}

#ifdef __STEALTH
// Cloaker
Cloaker::Get()->AddModule(hinstDLL,true);
#endif

break;
}
case DLL_PROCESS_DETACH:
{
#ifdef __CONSOLE
// Don’t free the console if it already existed upon injection
if (NeedFree)
FreeConsole();
#endif

break;
}
}

// Success
return TRUE;
}

Sorry for the messy code. The base was taken from one of my other projects because I was too lazy to whip up the skeleton by hand. Then I just slapped in the detection stuff. There’s lots of unnecessary crap there, but you get the point.

Obviously if WoW was started as guest you could defeat remote hashing attacks or those similar, but guess what… That’s how they detected Glider! They checked whether the given access token was restricted (i.e. started with reduced privileges).

Protip: Don’t use WoWMimic. Not only is it an awful bot, its a 30 second job (literally) to write a detection routine.