Deceiving Blizzard Warden

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:

  1. Obtaining the list of relative addresses for scanning.
  2. Reading the required number of bytes at each of the addresses.
  3. Calculating hashes.
  4. 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.

Locked game account

Locked game account

 

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:

  1. To avoid known dangerous addresses when making code modifications.
  2. To use indirect penetration, intercepting one of DirectX — Device.EndScene () methods.
  3. 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:

Heart of Warden scanner

Heart of Warden scanner

 

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");
Search for the loader

Search for the loader

 

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 loader of Warden modules

The loader of Warden modules

 

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.

Warden activity log

Warden activity log

 

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.

Warden is not able to detect the hack

Warden is not able to detect the hack

 

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!


10 Responses to “Deceiving Blizzard Warden”

  1. disconnected

    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);

  2. 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

  3. nigel green

    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

Leave a Reply to Erik R.

Click here to cancel reply.

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>