WoWMimic Mini-Banwave
Lol. Can’t say you weren’t warned.
That’s all I have to say on the issue.
Lol. Can’t say you weren’t warned.
That’s all I have to say on the issue.
WoWMimic v49 is out, a very quick bugfix release.
Nothing has changed in terms of anti-detection (so WardenMimic still works), but amusingly, they introduced a bug into their (recently removed) GetCursorPos and SetCursorPos hook subs for VEH.
They have removed a line from both that compares an internal flag to 1/0. What’s amusing about it is, they left in a conditional jump that relied on that line. That conditional jump will now fire based off the line above (a SUB instruction, which like CMP sets the ZF), and the jump will never be taken. Why is this amusing? Because its proof that the devs wrote the code using inline assemby. I was moderately sure of this before (due to the use of PUSHAD, POPAD, etc), but now we have confirmation. Not only is it totally unneeded to do all that crap in inline ASM, but it also explains that they did in fact implement the obfuscation code manually (mentioned in a previous post), obviously thinking it posed some kind of barrier.
Sorry guys, but it probably took me less time to undo it than it took you to figure it out and write it. REALLY obvious job. The call/pop trick is one of the oldest in the book, literally. C’mon. Raise the bar and give us something fun and challenging to play with. (Yeah I know, I’m asking for the world from these guys)
P.S. Still can’t figure out how to stop detection by WardenMimic?
More epic fail from the WoWMimic team with the release of PvPAdvance v15.
They have made ZERO improvements to their anti-detection since v14. That means that it can still be detected using a warden scan that’s already implemented. No new functionality is required, only a new hash.
The only thing they are currently doing to protect themselves is unlink their module from the linked list, but Warden doesn’t even use that list!*
Sigh. It seems like they’re never going to learn. Hopefully Blizzard will teach them the hard way, but given how lazy the Warden guy is, I can’t exatly say you should count on it.
Update:
*Whilst there is a scan in place that does in fact use that list through the use of Module32First/Module32Next, it is not currently activated. Thank you Kynox for catching and confirming that.
First things first, the current build of WardenMimic (here) still works! Kinda sad they couldn’t fix that.
Anyway, some minor changes in this build but nothing exciting.
As WardenMimic demonstrates its still a highly detectable piece of software. They got lucky this time in that the Warden guy majorly fucked up the timing of his latest scan because he’s an incompetent dipshit. Unfortunately due to the fact that ZwQueryVirtualMemory is hooked with VEH he will actually have to do some work to detect them this time (though, I’ve already done all he needs to do and it only took me 10-15 minutes so fuck knows what’s taking him so long).
For details on the Warden scan aimed at Mimic please check out my friend Kynox’s blog here.
As usual, I have taken the liberty of dumping out the full code from the anti-detecton part of Mimic and documenting it.
Lastly, please keep in mind that all they’ve done for this build is remove the feature that was getting them detected this time. They still havn’t fixed the huge hole that I’m using in WardenMimic, so don’t use Mimic on accounts you don’t want to potentially loose.
Bored, so I took a look at the new version of WoWMimic PvPAdvance to see what they changed. Unfortunately v13 wouldn’t run on my PC, so I don’t have anything to base it off, but when the next version comes out at least I will. I’m not surprised at all that there have been bans for this tool, its anti-detection is even worse than WOWMimic itselfs.
What they’re currently doing:
No. Seriously. That seems to be it.
They’re not hooking VirtualQuery (or any of its lower-level code paths), and they’re not even nulling out their PE header. This means that a Warden scan that is CURRENTLY IMPLEMENTED can be used to detect their module, all the Warden dev needs to do is add a new hash.
My guess is that with the bans from the previous version they made minor changes to their code, which changed the module hash at the point Warden was scanning, noticed they weren’t getting bans anymore, and assumed they fixed the problem. That, or they’re too lazy to fix it properly. Either way they’re idiots.
Epic fail. Quite disappointing really, that only took a minute or two to check and now I’m bored again.
New version of WardenMimic for v47 of WoWMimic.
If you havn’t already you may want to check out my post detailing the changes to WoWMimic in v47 here.
Please let me know if you experience a false positive or false negative. For version 47 ONLY, do not report flase negatives for any other versions.
Many of you will know by now that WoWMimic v47 is out. A few interesting changes, nothing really substantial though.
All in all quite disappointing. They broke the previous version of WardenMimic but only because they changed the offset at which they were hooking, and I was being nice and lowering the false positive rate by putting in lots of checks to detect only that exact build. Anyway, new WardenMimic incoming soon (when I don’t feel lazy).
Update:
Thanks to Harko for pointing out that GetCursorPos and SetCursorPos are still hooked with JMP hooks. It seems they implemented support for it in their VEH but didn’t actually hook it using VEH for some reason. Much like their unused changes to NtGetContextThread. Kinda retarded really….
Full code:
; MimicVectoredHandler
169B379E | 55 | PUSH EBP | ; Standard stack frame setup
169B379F | 8BEC | MOV EBP, ESP | ; Standard stack frame setup
169B37A1 | 81C4 00FFFFFF | ADD ESP, -100 | ; Sub ESP 100. Room for local variables.
169B37A7 | 60 | PUSHAD | ; Preserve all registers
169B37A8 | E8 00000000 | CALL 169B37AD | ; Obfuscation. ‘Calls’ next line and pushes the address of that line onto stack.
169B37AD | 5B | POP EBX | ; Pop address (of current line) back off and into EBX
169B37AE | 81EB 75164000 | SUB EBX, 401675 | ; Subtract 401675 from EBX (169B37AD) (Final: 165B2138)
169B37B4 | 8B75 08 | MOV ESI, DWORD PTR [EBP+8] | ; Get pExceptionInfo
169B37B7 | 8B7E 04 | MOV EDI, DWORD PTR [ESI+4] | ; Get pExceptionInfo->pContext
169B37BA | 8B36 | MOV ESI, DWORD PTR [ESI] | ; Get pExceptionInfo->pExceptionRecord
169B37BC | 8B87 B8000000 | MOV EAX, DWORD PTR [EDI+B8] | ; Get EIP
169B37C2 | 3B83 CD104000 | CMP EAX, DWORD PTR [EBX+4010CD] | ; Compare EIP to address of GetCursorPos.
169B37C8 | 75 0E | JNZ 169B37D8 | ; If not equal go to ContinueSearchRet
169B37CA | 8D83 F8164000 | LEA EAX, DWORD PTR [EBX+4016F8] | ; Get address of GetCursorPos_Hook
169B37D0 | 8987 B8000000 | MOV DWORD PTR [EDI+B8], EAX | ; Move address into EIP
169B37D6 | EB 4C | JMP 169B3824 | ; Go to ContinueExecRet
169B37D8 | 3B83 D1104000 | CMP EAX, DWORD PTR [EBX+4010D1] | ; Compare EIP to address of SetCursorPos
169B37DE | 75 0E | JNZ 169B37EE | ; If not equal go to ContinueSearchRet
169B37E0 | 8D83 2D174000 | LEA EAX, DWORD PTR [EBX+40172D] | ; Get address of SetCursorPos_Hook
169B37E6 | 8987 B8000000 | MOV DWORD PTR [EDI+B8], EAX | ; Move address into EIP
169B37EC | EB 36 | JMP 169B3824 | ; Go to ContinueExecRet
169B37EE | 3B83 84134000 | CMP EAX, DWORD PTR [EBX+401384] | ; Compare EIP to address of ZwQueryVirtualMemory_Retn
169B37F4 | 75 0E | JNZ 169B3804 | ; If not equal go to ContinueSearchRet
169B37F6 | 8D83 50174000 | LEA EAX, DWORD PTR [EBX+401750] | ; Get address of ZwQueryVirtualMemory_Hook
169B37FC | 8987 B8000000 | MOV DWORD PTR [EDI+B8], EAX | ; Move address into EIP
169B3802 | EB 20 | JMP 169B3824 | ; Go to ContinueExecRet
169B3804 | 3B83 8C134000 | CMP EAX, DWORD PTR [EBX+40138C] | ; Compare EIP to address of ZwGetContextThread_Hook. Currently not used/enabled.
169B380A | 75 0E | JNZ 169B381A | ; If not equal go to ContinueSearchRet
169B380C | 8D83 88174000 | LEA EAX, DWORD PTR [EBX+401788] | ; Get address of ZwGetContextThread_Hook.
169B3812 | 8987 B8000000 | MOV DWORD PTR [EDI+B8], EAX | ; Move address into EIP
169B3818 | EB 0A | JMP 169B3824 | ; Go to ContinueExecRet
169B381A | 61 | POPAD | ; Restore registers
169B381B | B8 00000000 | MOV EAX, 0 | ; Move EXCEPTION_CONTINUE_SEARCH into return value
169B3820 | C9 | LEAVE | ; Standard function prologue
169B3821 | C2 0400 | RETN 4 | ; Return
169B3824 | 61 | POPAD | ; Restore registers
169B3825 | B8 FFFFFFFF | MOV EAX, -1 | ; Move EXCEPTION_CONTINUE_EXECUTION into return value
169B382A | C9 | LEAVE | ; Standard function prolugue
169B382B | C2 0400 | RETN 4 | ; Return
; GetCursorPos_Hook
169B3830 | E8 00000000 | CALL 169B3835 | ; Obfuscation. ‘Calls’ next line and pushes the address of that line onto stack.
169B3835 | 5A | POP EDX | ; Pop address (of current line) back off and into EDX
169B3836 | 81EA FD164000 | SUB EDX, 4016FD | ; Subtract 4016FD from EDX (169B3835) (Final: 165B2138)
169B383C | 83BA 09104000 01 | CMP DWORD PTR [EDX+401009], 1 | ; Compare some internal flag to 1
169B3843 | 75 15 | JNZ 169B385A | ;
169B3845 | 8B4C24 04 | MOV ECX, DWORD PTR [ESP+4] | ; Get lpPoint
169B3849 | 8B82 0D104000 | MOV EAX, DWORD PTR [EDX+40100D] | ; Get custom mouse x
169B384F | 8901 | MOV DWORD PTR [ECX], EAX | ; Set lpPoint->x to custom mouse x
169B3851 | 8B82 11104000 | MOV EAX, DWORD PTR [EDX+401011] | ; Get custom mouse y
169B3857 | 8941 04 | MOV DWORD PTR [ECX+4], EAX | ; Set lpPoint->y to custom mouse y
169B385A | 8B82 CD104000 | MOV EAX, DWORD PTR [EDX+4010CD] | ; Get address of GetCursorPos
169B3860 | 83C0 02 | ADD EAX, 2 | ; Jump over invalid instruction
169B3863 | FFE0 | JMP NEAR EAX | ; Jump to GetCursorPos (+2)
; SetCursorPos_Hook
169B3865 | E8 00000000 | CALL 169B386A | ; Obfuscation. ‘Calls’ next line and pushes the address of that line onto stack.
169B386A | 5A | POP EDX | ; Pop address (of current line) back off and into EDX
169B386B | 81EA 32174000 | SUB EDX, 401732 | ; Subtract 401732 from EDX (169B386A) (Final: 165B2138)
169B3871 | 83BA 09104000 01 | CMP DWORD PTR [EDX+401009], 1 | ; Compare some internal flag to 1
169B3878 | 75 03 | JNZ 169B387D | ; Jump if not equal to RetnCallOrig
169B387A | C2 0800 | RETN 8 | ; Return without calling original
169B387D | 8B82 D1104000 | MOV EAX, DWORD PTR [EDX+4010D1] | ; Get address of SetCursorPos
169B3883 | 83C0 02 | ADD EAX, 2 | ; Jump over invalid instruction
169B3886 | FFE0 | JMP NEAR EAX | ; Jump to SetCursorPos (+2)
; ZwQueryVirtualMemory_Hook
169B3888 | E8 00000000 | CALL 169B388D | ; Obfuscation. ‘Calls’ next line and pushes the address of that line onto stack.
169B388D | 5A | POP EDX | ; Pop address (of current line) back off and into EDX
169B388E | 81EA 55174000 | SUB EDX, 401755 | ; Subtract 401755 from EDX (169B388D) (Final: 165B2138)
169B3894 | 50 | PUSH EAX | ; Preserve EAX
169B3895 | 8B4424 14 | MOV EAX, DWORD PTR [ESP+14] | ; Get pMemoryBasicInformation
169B3899 | 8B48 04 | MOV ECX, DWORD PTR [EAX+4] | ; Get pMemoryBasicInformation->Allocationbase
169B389C | 3B8A 21104000 | CMP ECX, DWORD PTR [EDX+401021] | ; Compare to MimicModuleBase
169B38A2 | 75 18 | JNZ 169B38BC | ; If not equal jump to LocRetn
169B38A4 | C700 00000000 | MOV DWORD PTR [EAX], 0 | ; Zero out pMemoryBasicInformation->BaseAddress
169B38AA | C740 04 00000000 | MOV DWORD PTR [EAX+4], 0 | ; Zero out pMemoryBasicInformation->AllocationBase
169B38B1 | C740 10 00000100 | MOV DWORD PTR [EAX+10], 10000 | ; Set pMemoryBasicInformation->State to MEM_FREE
169B38B8 | 58 | POP EAX | ; Restore EAX
169B38B9 | C2 1800 | RETN 18 | ; Return
169B38BC | 58 | POP EAX | ; Restore EAX
169B38BD | C2 1800 | RETN 18 | ; Return
; ZwGetContextThread_Hook
169B38C0 | 8B4C24 08 | MOV ECX, DWORD PTR [ESP+8] | ; Get pContext
169B38C4 | C741 04 00000000 | MOV DWORD PTR [ECX+4], 0 | ; Zero out DR0
169B38CB | C741 08 00000000 | MOV DWORD PTR [ECX+8], 0 | ; Zero out DR1
169B38D2 | C741 0C 00000000 | MOV DWORD PTR [ECX+C], 0 | ; Zero out DR2
169B38D9 | C741 10 00000000 | MOV DWORD PTR [ECX+10], 0 | ; Zero out DR3
169B38E0 | C2 0800 | RETN 8 | ; Return
With WoWMimic v.46 being out for a while now and still no update for my detector, I figured I’d explain some of the changes made because I’m bored.
First things first, credits to Harko and Kynox for working with me on this.
The WoWMimic devs have removed a lot of their previous API hooks and left only the following:
The hooks on GetCursorPos and SetCursorPos have nothing to do with anti-detection and are just there to allow them to use the mouse properly from their injected DLL, so I am not interested in those. What I am interested in is NtGetContextThread, NtQueryVirtualMemory, and their new hooking method.
Rather than use an inline jump hook like previous builds they have gotten a little smarter and use vectored exception handling by inserting an invalid instruction at the start of the APIs. The reason this is a smarter approach is that it’s harder to track down the hook functions at runtime and hence harder to hash them. A downside of this approach though is that its very easy to identify their unique hook. Also, its still possible to manually walk the VEH linked list and find their VEH and hence their hook functions. Then again, there’s nothing stopping WoW registering a VEH above mimics (which happens if you run mimic before logging in) then using that to intercept the hooked APIs and handle it before mimic can.
Currently their vectored exception handler does three things:
Their NtQueryVirtualMemory hook is simple. It stops Warden from RVA hashing their module using its regular module scan by returning modified data pages in their module’s memory range.
The NtGetContextThred is also quite simple. It hides hardware breakpoints by overwriting the ContextFlags in the context structure passed to the API.
The third and unknown hook seems simply to be a skeleton for a hardware breakpoint hook (hence the NtGetContextThread hook). What’s interesting though is that currently it appears to be totally unused.
Whilst WoWMimic is still HIGHLY detectable (its a fairly trivial process to detect it), they are at least making some progress. Still though, it’s far from safe so don’t use it on any accounts you value.
Appendix:
In case anyone is interested, I have dumped their VectoredHandler and API hooks. They are fully commented so even if you’re not great with ASM or Windows internals it should be easy to follow. (You will want to copy all the text into a larger window because the blog has a small width and its very difficult to read the code when it’s line-wrapped)
; LONG CALLBACK VectoredHandler(PEXCEPTION_POINTERS ExceptionInfo)
23C4E9E0 | 8B4424 04 | MOV EAX, DWORD PTR [ESP+4] | ; Access ExceptionInfo
23C4E9E4 | 8B40 04 | MOV EAX, DWORD PTR [EAX+4] | ; Access ExceptionInfo->ContextRecord
23C4E9E7 | 8B88 B8000000 | MOV ECX, DWORD PTR [EAX+B8] | ; Access ExceptionInfo->ContextRecord->Eip
23C4E9ED | 3B0D EC1EC923 | CMP ECX, DWORD PTR [23C91EEC] | ; Compare to some address (always zero on my machine. seems to be for an unused HWBP hook.)
23C4E9F3 | 75 12 | JNZ 23C4EA07 | ; If not equal jump to NtQueryVirtualMemory check
23C4E9F5 | 8B0D E41EC923 | MOV ECX, DWORD PTR [23C91EE4] | ; Get unknown address (always zero on my machine. seems to be for an unused HWBP hook.)
23C4E9FB | 8988 B8000000 | MOV DWORD PTR [EAX+B8], ECX | ; Move unknown address into EIP
23C4EA01 | 83C8 FF | OR EAX, FFFFFFFF | ; Set return value to EXCEPTION_CONTINUE_EXECUTION
23C4EA04 | C2 0400 | RETN 4 | ; Return
23C4EA07 | 3B0D D41EC923 | CMP ECX, DWORD PTR [23C91ED4] | ; Check if EIP is NtQueryVirtualMemory
23C4EA0D | 75 12 | JNZ 23C4EA21 | ; If not eual jump to NtGetContextThread check
23C4EA0F | 8B15 CC1EC923 | MOV EDX, DWORD PTR [23C91ECC] | ; Get address of NtQueryVirtualMemory hook sub
23C4EA15 | 8990 B8000000 | MOV DWORD PTR [EAX+B8], EDX | ; Move address into EIP
23C4EA1B | 83C8 FF | OR EAX, FFFFFFFF | ; Set return value to EXCEPTION_CONTINUE_EXECUTION
23C4EA1E | C2 0400 | RETN 4 | ; Return
23C4EA21 | 3B0D C41EC923 | CMP ECX, DWORD PTR [23C91EC4] | ; Check if EIP is NtGetContextThread
23C4EA27 | 75 12 | JNZ 23C4EA3B | ; If not equal jump to ‘return 0′
23C4EA29 | 8B0D BC1EC923 | MOV ECX, DWORD PTR [23C91EBC] | ; Get address of NtGetContextThread hook sub
23C4EA2F | 8988 B8000000 | MOV DWORD PTR [EAX+B8], ECX | ; Move address into EIP
23C4EA35 | 83C8 FF | OR EAX, FFFFFFFF | ; Set return value to EXCEPTION_CONTINUE_EXECUTION
23C4EA38 | C2 0400 | RETN 4 | ; Return
23C4EA3B | 33C0 | XOR EAX, EAX | ; Set return value to EXCEPTION_CONTINUE_SEARCH
23C4EA3D | C2 0400 | RETN 4 | ; Return
; NtQueryVirtualMemory Hook
; NTSTATUS NTAPI NtQueryVirtualMemory(IN HANDLE ProcessHandle, IN PVOID BaseAddress,
; IN MEMORY_INFORMATION_CLASS MemoryInformationClass, OUT PVOID Buffer,
; IN ULONG Length, OUT PULONG ResultLength OPTIONAL );
23C2EA52 | 60 | PUSHAD | ; Preserve registers (ESP += 0×20)
23C2EA53 | 8B4424 28 | MOV EAX, DWORD PTR [ESP+28] | ; Get BaseAddress (Param2)
23C2EA57 | 3B05 E01EC723 | CMP EAX, DWORD PTR [23C71EE0] | ; Compare BaseAddress to ModuleStart
23C2EA5D | 72 10 | JB 23C2EA6F | ; Take jump if BaseAddress is out of module address range
23C2EA5F | 3B05 DC1EC723 | CMP EAX, DWORD PTR [23C71EDC] | ; Compare BaseAddres to ModuleEnd
23C2EA65 | 77 08 | JA 23C2EA6F | ; Take jump if BaseAddress is out of module address range
23C2EA67 | C74424 28 00000000 | MOV DWORD PTR [ESP+28], 0 | ; Move zero into BaseAddress
23C2EA6F | 61 | POPAD | ; Restore registers
23C2EA70 | A1 D81EC723 | MOV EAX, DWORD PTR [23C71ED8] | ; Run overwritten code
23C2EA75 | 8B15 D01EC723 | MOV EDX, DWORD PTR [23C71ED0] | ; Get address of original func
23C2EA7B | FFE2 | JMP NEAR EDX | ; Call original func
; NtGetContextThread Hook
; NTSTATUS NTAPI NtGetContextThread(IN HANDLE ThreadHandle, OUT PCONTEXT pContext );
23C2EAA2 | 60 | PUSHAD | ; Preserve registers
23C2EAA3 | 8B4424 28 | MOV EAX, DWORD PTR [ESP+28] | ; Get pContext (Param2)
23C2EAA7 | C700 07000100 | MOV DWORD PTR [EAX], 10007 | ; Move 0×00010007 into pContext->ContextFlags to hide Debug Registers
23C2EAAD | 61 | POPAD | ; Restore registers
23C2EAAE | A1 C81EC723 | MOV EAX, DWORD PTR [23C71EC8] | ; Run overwritten code
23C2EAB3 | 8B15 C01EC723 | MOV EDX, DWORD PTR [23C71EC0] | ; Get address of original func
23C2EAB9 | FFE2 | JMP NEAR EDX | ; Call original func
Releasing the initial version of my latest project, dubbed WardenMimic. Its purpose is to basically ‘mimic’ Warden and try and detect WoWMimic. It will be fully in-process so it works within the constraints of Warden itself. Releasing it as a binary so the devs have to work for it this time, they’ve had enough charity handouts.
Simply run the loader, point it to WoW, and the module will spawn a console and let you know whether its detected WoWMimic or not. Go back to the loader, hit enter again, and the module will be unloaded for your convenience.
I will continually update this for any new WoWMimic builds until I get bored or they really pick up their game and make it too much trouble for me to bother (keeping in mind I’m a very lazy person).
P.S. Has recieved limited testing. If it crashes please post your system specs (os, architecture, etc etc) plus a crash dump.
Credits:
Huge credits to both Harko and Kynox for their help/input/etc.
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