Warden— that is how the developers of the most popular games in their genres employed by Blizzard decided to call their protection system. The system being, in fact, a part of Battle.net is used in such projects as World of Warcraft, StarCraft II and Diablo 3. According to official figures only, thousands of Battle.net accounts have been banned so far, and it is Warden that banned a considerable part of them.
Silent supervisor
Perhaps, first of all we should find out what Warden is. The system consists of two parts: the server part and the client part, and, of course, we will deal only with the client one. As mentioned above, Warden is not an integral part of the game code. Client-side code is loaded dynamically from Battle.net in the form of patterns vaguely resembling Portable Executable in their structure, which are then displayed at random addresses in the gameplay address space. It is also worth noting that most of Warden client-side code is obfuscated and may vary from one game session to another.
Warden is a passive protection mechanism, and all what Warden client part does is collection of the information, which is subsequently sent to the server side. In general, the approximate operating algorithm of Warden client side is as follows:
- Obtaining the list of relative addresses for scanning.
- Reading the required number of bytes at each of the addresses.
- Calculating hashes.
- Building the packet with hashes and sending it to the server.
The procedure is repeated at certain intervals several times a minute. If the server side detects that the hashes mismatch the reference values, it is considered that the illegal code modifications have occurred. In this case, no immediate actions are taken — the account is just marked as violating-the-rules, and only after a while, when your account is locked, you will discover that you have been “nailed”. When this happens, the entire Battle.net account (which may include multiple attached licenses) is not blocked — only the game account is.
Against the system
It is impossible to neutralize Warden simply by disconnecting it or locking its operation: the system is designed so that the server part, in any case, should receive response packets from the client part, which in turn should contain scanning information. Hence, there is only one way out — not to get nailed. There are at least three ways to achieve it:
- To avoid known dangerous addresses when making code modifications.
- To use indirect penetration, intercepting one of DirectX —
Device.EndScene ()
methods. - To hide all made modifications on the fly (during scanning).
The first method will be effective up to a certain time and generally speaking is not the bypass itself. The second method (interception of EndScene ()
) really works pretty well — the function is called when building of each displayed frame is completed and is intercepted by, for example, completely legal video capture applications, which restricts Warden’s ability to unambiguously interpret the changes in the function code as prohibited modifications. However, this method is more suitable for bots and has been successfully used by them for several years. The third method is ideal for static modifications (such as inclusion of rendering of the entire map in Star Craft — maphack), what is more, its implementation itself is more interesting and technological. And further we will consider in details the very third method.
It is obvious that to conceal made modifications you need to penetrate into Warden scanning code. As you know, this code is not present in the process from the start, moreover, it gets a random address while loading. For the first time it can be detected using a debugger just by setting the breakpoint to read any of the scanned addresses (from any old, long-detected hack). For example, for the last (at the time of writing) build of World of Warcraft, when setting the breakpoint at 0x0045A6F0
address, which is relative to the basic pattern base, we get to the following code fragment:
push esi push esi cld mov edx, dword ptr ss:[esp+14h] mov esi, dword ptr ss:[esp+10h] mov edx, dword ptr ss:[esp+0h] mov ecx, edx mov ecx, eax shr ecx, 2 je short ; Here, the data is written to a temporary buffer, from which the hash will be calculated. ; It is enough to substitute the modified bytes with the original ones rep movs dword ptr es:[edi], dword ptr ds:[esi] mov cl, 3 and ecx, edx je short rep movs byte ptr es:[edi], byte ptr ds:[esi] pop edi pop esi ret
Experience has shown that the detected code is not subject to polymorphic changes, unlike the rest of the module; moreover, it has been changed only once over recent years, which makes it an ideal target for penetration. But since this code is a part of the dynamically loaded module, you will also need to intercept the moment when it appears in the process to make changes before its first execution. In the case of WoW, the loader is a part of the game code and is located directly in Wow.exe (in 32 bit version) — you can find it by trawling through kilometers of listings in the disassembler, or choose a more clever method. The memory for the loaded patterns of Warden module is allocated by VirtualAlloc ()
function; the call log indicating the place from where the call has been made will contain the loader address.
void VA_hook_(DWORD dwCallAddr, DWORD dwMemBlock, DWORD dwSize) { if ( dwMemBlock && dwSize > 0x2000 ) { Logger::OutLog("Allocated block:%.8x - %.8x, called from:%.8x\r\n", dwMemBlock, dwMemBlock+dwSize, dwCallAddr ); } }
Thus, there is no need to go through all the records: after login and entering the game Realm, the required Warden module will have been already loaded and you can simply search through the address space of the binary pattern process, corresponding to the previously found data scanning procedure:
Scanner::TPattern WardenPattern ("\x56\x57\xFC\x8B\x54\x24\x14\x8B\x74\x24\x10\x8B\x44\x24\x0C\x8B\xCA\x8B\xF8\xC1\xE9\x02\x74\x02\xF3\xA5", "x26"); DWORD WardenProc = (DWORD) Scanner::ScanMem( &WardenPattern ); if ( WardenProc ) { Logger::OutLog("Warden::Scan proc:0x%.8X\r\n", WardenProc); } else Logger::OutLog("Warden::Scan proc not found\r\n");
This way we will find out the exact current location of the required Warden code, while VirtualAlloc ()
call log will allow us to determine the point from which the memory for this code was requested, thereby pointing at the loader of Warden modules. Through analyzing the loader code in the disassembler you can find the place suitable for interception. To do this, find the right moment, when all sections of the pattern obtained from the network are successfully displayed in the process address space, and then it will be possible to implement interception modifying Warden code. VirtualProtect ()
call can be a suitable fragment — it determines the final rights of access to the sections:
lea ecx, [ebp+flOldProtect] push ecx ; lpflOldProtect push dword ptr [esi+8] ; flNewProtect push eax ; dwSize push ebx ; lpAddress call ds:VirtualProtect test byte ptr [esi+8], 0F0h jz short loc_A5BE9C push [ebp+dwSize] ; dwSize push ebx ; lpBaseAddress call ds:GetCurrentProcess push eax ; hProcess call ds:FlushInstructionCache
The code of the trampoline function, proceeding to which is set instead of call ds: VirtualProtect
, may look like this:
void VA_hook_(DWORD dwCallAddr, DWORD dwMemBlock, DWORD dwSize) { __asm { push ebp mov ebp, esp pushad } if ( flNewProtect==PAGE_EXECUTE_READ ) // For the section containing the executable code WardenModulePatch(lpAddress, dwSize); // Patching Warden code __asm { popad pop ebp jmp dword ptr[VirtualProtect] } }
Patcher
To get the opportunity to hide your actions from Warden, you need to remember absolutely all changes made in the process memory and have the access to the original data that existed before the changes. Any changes (interceptions, substitution, etc.) have to be made by the same means, which should guarantee the fulfillment of the stated requirements:
/* pAddr — pointer to the point of modification pData — data for substitution pData — data size /* BOOL Patcher::MakePatch( PBYTE pAddr, PBYTE pData, DWORD dwDataSize ) { BOOL fRes = false; DWORD dwOldp; if ( VirtualProtect( pAddr, dwDataSize, PAGE_EXECUTE_READWRITE, &dwOldp) ) { // Remembering the original bytes pPatchStruc = &Patches[dwPatches]; // The last element pPatchStruc->addr = dwAddr; pPatchStruc->len = dwSize; memcpy( pPatchStruc->org , (PVOID) dwAddr, dwSize ); // Writing the new bytes memcpy( pAddr, pData, dwDataSize ); dwPatches++ fRes = true; } return fRes; }
A list of structures containing information about all changes made in the process may belong to the object with the global scope. Now, code modification is made as follows:
bool PatchVirutalProtect() { bool bRetval = false; PBYTE bCode = (PBYTE) "\xE8\x90\x90\x90\x90\x90"; // call rel32 DWORD pProc = (DWORD) GetProcAddress( GetModuleHandleA( "KernelBase.DLL"), "VirtualProtect" ); *((PDWORD)(bCode+1)) = (DWORD)&VP_hook - ((DWORD)pProc+5); if ( Patcher::Instance()->MakePatch( (PBYTE)pProc, bCode, 5 ) ) { Logger::OutLog( "VirtualProtect patched at: %x\r\n", pProc ); bRetval = true; } else Logger::OutLog( "VirtualProtect patch failed\r\n" ); return bRetval; }
The use of the centralized patcher causes no additional troubles, while, in addition to easy access to the original data, we get the opportunity to roll back any change, returning everything to its original state, which is sometimes very useful.
Warden’s unseeing eye
Now that you have all the necessary information and tools, one thing only remains — to replace Warden scanning procedure with your own, which will substitute the modified data with the original data. In this case hashes will be identical to those stored on the server, and code changes will remain unnoticed.
To prevent modified bytes from coming in sight of Warden, it is necessary to seek the intersection of the sets of the scanned addresses with patched data addresses at each scanning call. Since the patches will not likely go one by one (it does not make sense), there will be at most one intersection per scan and you will be able to take the data from the structure associated with one particular patch. All intersection options are limited to one double condition: either the address of the scanned byte set beginning is included in the set of patch addresses, or vice versa. So, we have to go through all the patches, checking the specified condition:
// Do the scanned addresses get under any patch? for ( unsigned int i=0; i< dwPatches; i++) // going through all patches if ( (PatchList[i].addr - dwAddr < dwSize) || ( dwAddr - PatchList[i].addr < PatchList[i].len ) ) // finding the intersection { pCurrentPatch = &(PatchList[i]); break; }
After obtaining the structure associated with the current scanning with information about the patch, the replacement of the data will be quite easy:
if (!pCurrentPatch) { // Scanning the unpatched area — direct copying memcpy(pOutBuff, (PVOID)dwAddr, dwSize); } else { // Byte-by-byte processing for ( unsigned int i=0; i< dwSize; i++) { unsigned int delta = dwAddr+i - pCurrentPatch->addr; byte* pCurrent; // Has the byte at this address been patched? if( delta < pCurrentPatch->len ) pCurrent = pCurrentPatch->org + delta; else pCurrent = (PBYTE)(dwAddr+i); pOutBuff[i] = *pCurrent; } }
Using this code instead of the original scanning procedure, we can control Warden activity without giving it the opportunity to detect any changes made in the code, even if Warden tries to check the integrity of itself.
Proof of concept
To demonstrate the efficiency of the bypass with some practical usefulness, it was decided to modify World of Warcraft code at the relative shift 0x008C9A3E
checked by Warden scanner. The procedure corresponding to this shift is responsible for checking the rights for Lua script execution (many of WoW API functions are blocked for the user and can be used only by the native user interface). The fragment of code in this shift is as follows:
mov ebp, esp mov edx,dword ptr ss:[ebp+8] mov eax,dword ptr ds:[17A5B10] xor ecx,ecx push esi cmp dword ptr ds:[15FBAA8],ecx je short 01309A84 cmp edx, 22
The shift itself corresponds to the conditional jump after comparison of the global variable containing the access level ID for the current context with zero (zero corresponds to the highest level rights). Replacing the conditional jump with the unconditional one, we get the opportunity to use any of WoW API functions, creating sophisticated and “smart” scripts that automate many gaming activities (the most primitive example of use: to bind all spell rotations to one button with cooldown check and so on, which initially is not possible). The simplified code of patch installation looks like this:
PBYTE bCode = (PBYTE) "\xEB"; // JMP SHORT Scanner::TPattern Pattern( "\x33\xC9\x56\x39\x0D\xFF\xFF\xFF\xFF\x74\x44\x83\xFA\x22", "x5?4x5"); DWORD dwProc = (DWORD) Scanner::ScanMem( &Pattern ); if ( dwProc ) { DWORD dwProcChangeOffset = dwProc+9; if ( Patcher::Instance()->MakePatch( (PBYTE)dwProcChangeOffset, bCode, 1 ); }
When the patch is installed, WoW API “protected” functions become available directly from the macros, and Warden activity log reflects the prevented attempts to scan the patched area. You can see it yourself by compiling and trying the source codes attached to the article.
Total freedom of action
The ability to make any changes in the game client with impunity opens tremendous prospects for further explorations. In fact, in Blizzard games you can perform anything you can imagine or wish. It is possible to write a separate article concerning only the capabilities of unblocked Lua scripts in WoW. After all, even simple scripts can release the player from routine actions or reduce the dependence on reaction and attention, allowing him/her to spend a little more time on other things. At that, possibilities of free client modifications are not limited to just unblocking of certain capabilities. So, just go for it!
“You can see it yourself by compiling and trying the source codes attached to the article.”
Where can I find the source? Is this referring to the code snippets?
Check it out https://github.com/xakepru/x14.08-coverstory-blizzard
I don’t game but if this is in response to Blizzard shutting down that non-profit fan-run server, all the more power to you! Also, I had to double take when I saw Olly then realized that WoW is probably 32bit since it was a 2004 product haha. Which Olly plugins were you using, and what do you normally use (Immunity + ___ ?)
Hey,
Thanks for this great writeup!
In the code snippet of PatchVirtualProtect there are two small errors:
* function name typo
* something wrong with ‘&’ encoding and html in this line: *((PDWORD)(bCode+1)) = (DWORD)&VP_hook – ((DWORD)pProc+5);
Curious as to how the Mac OS X side varies with respect to Warden. I’ve heard that it is far more lax and preferred by bots, but that could be outdated info.
Thanks for the article!
This is all great but is known.. Thing your not looking at is warden is old.. as in blizzard figured out another way to spy on you, warden is just a pretty name these days while blizzard is still looking at you in another source. try debugging wow.exe and the other files and I bet you will find some juicy stuff that shows they aren’t using warden so much anymore
i have put this warden before my MP Richard Burgon he as look at this warden and he is not impressed it a hacking tool and is a breach of the data protection act here in the uk he his looking to have it banned from the UK soon
it also can be use to hack a computer out right
All moot since 7.3 with wow code obfuscation. Any update since that?