Author | Message | Time |
---|---|---|
UnknowN-TerroR | Hi, I'm developing a multi-os battle.net client written in python that i'm going to release under GPL. I'm analyzing the behaviour of the new anticheat system under warcraft III, i've managed to decrypt and encrypt the first packets, but i can't understand what i should do when i receive the SID_WARDEN packet with the 0x05 code, that changes the keys, breaking the decription. i've looked here: http://www.skullsecurity.org/wiki/index.php/Warden_Modules but nothing is written about this kind of packet. bnet docs only says: [code] 0x05 - Initialization (DWORD)[5] Unknown[/code] I've looked also here: https://davnit.net/bnet/vL/index.php?topic=17903.msg182385#msg182385 but i can't understand very well what the program calls. I read that the client must first initialize the warden module sent in the 0x00 packet, then use it to generate a new key, but i'm not very sure... What should i do? Thanks for you great work in analyzing the blizzard protocol. | May 31, 2009, 9:51 AM |
Ringo | Once you have mapped/preped the warden module to memory (assuming m_Mod is a pointer to that preped warden module) you then get the address/offset to the init function with in the warden module like so; [code] Private Sub InitModule() Dim A As Long Dim B As Long Dim C As Long Dim bCode(15) As Byte C = getInteger(m_Mod, &H18) B = 1 - C If (B > getInteger(m_Mod, &H14)) Then Exit Sub A = getInteger(m_Mod, &H10) A = getInteger(m_Mod, A + (B * 4)) + m_Mod Debug.Print " Initialize Function is mapped at 0x" & Hex(A) [/code] The init function address should now be stored in the value of A. You then build a list of callback functions with in you're own program, and call the init function, passing this callback function list, in ecx. [code] m_CallBack(0) = Addr2Ptr(AddressOf SendPacket) m_CallBack(1) = Addr2Ptr(AddressOf CheckModule) m_CallBack(2) = Addr2Ptr(AddressOf ModuleLoad) m_CallBack(3) = Addr2Ptr(AddressOf AllocateMem) m_CallBack(4) = Addr2Ptr(AddressOf FreeMemory) m_CallBack(5) = Addr2Ptr(AddressOf SetRC4Data) m_CallBack(6) = Addr2Ptr(AddressOf GetRC4Data) m_CallBack(7) = VarPtr(m_CallBack(0)) 'MOV ECX, &Param 'CALL Address 'RETN 16 m_ModMem = ExecuteCode(bCode(), &HB9, VarPtr(m_CallBack(7)), _ &H15FF, VarPtr(A), _ &HC2, 16, 0) End Sub [/code] Notice, ecx gets the address of a variable that holds the address of the callback function list. This is pretty much; [code] mov ecx, dword ptr m_CallBack(7) call dword ptr A [/code] While this function is being called, warden will back a few calls to you're AllocateMem() function, and expect you to allocate memory for it to use. The return value from wardens init function, with be a pointer to one of blocks of memory you allocated for it. This returned pointer is important, since you need to pass it to the warden module when calling it's other functions. (since the module stores infomation with in this pointer, such as states/encryption keys/etc) Now, assuming m_ModMem holds the return pointer, returned from wardens init function. The 1st 32bits into this m_ModMem pointer, is a pointer to a list of functions that the warden module exports. [code] '//Copys 1st pointer into m_ModMem, into i Call CopyMemory(i, ByVal m_ModMem, 4) '//Copys the 1st 3 export functions pointed to by i, to m_Func() Call CopyMemory(m_Func(0), ByVal i, 12) [/code] Now, m_Mod should point to the whole warden module mapped in memory. m_ModMem should be the pointer/value, returned from wardens init function. m_Func(0), m_Func(1), m_Func(2) should be 3 exported functions from the warden module (obtained from the address of the 1st dword in ModMem) The 1st function in that export list (m_Func(0)) is wardens init rc4 function. The 2nd function in that export list (m_Func(1)) is the unload function. The 3rd function in that export list (m_Func(2)) is the handle packet function. We must call the init rc4 function, before the warden module can handle a packet. Lets assume we have done all the above, and now we get are warden 0x05 packet. When you call this rc4 init function and pass it you're encrpytion seed (1st 4 bytes of 0x51 CDKey hash), warden will call you're GetRC4Data function, with the address of the rc4 keys, and the size of those keys. You then have 2 options with in you're callback. Return 1, if you already have a set of RC4 keys, and you have wrote them to the given address space. Return 0, if you would like the warden module to generate a new set of rc4 keys, from the seed passed to the init rc4 function. [code] m_RC4 = 0 'PUSH Size 'PUSH &Seed 'MOV ECX, Param 'XOR EDX, EDX 'MOV EAX, Address 'CALL EAX 'RETN 16 Call ExecuteCode(bCode(), &H68, &H4&, _ &H68, VarPtr(m_Seed), _ &HB9, m_ModMem, _ &HD233, _ &HB8, m_Func(0), _ &HD0FF, _ &HC2, 16, 0) If (m_RC4 = 0) Then Exit Function [/code] This is pretty much; [code] push SizeOfSeed (0x04) push dword ptr m_Seed mov ecx, m_ModMem call dword ptr m_Func(0) [/code] Notice here, when calling init rc4, we set m_RC4 to 0. While this init rc4 function is being called, you will get a callback from warden on the GetRC4Data() function. [code] Private Function GetRC4Data(ByVal lpBuffer As Long, ByRef dwSize As Long) As Long 'Debug.Print "Warden.GetRC4Data() 0x" & Hex(lpBuffer) & "/0x" & Hex(dwSize) 'GetRC4Data = 1 'got the keys already 'GetRC4Data = 0 'generate new keys GetRC4Data = m_RC4 m_RC4 = lpBuffer End Function [/code] We return 0 (value of m_RC4) to make the warden module generate a new set of RC4 keys. We also store the address where those RC4 keys will be stored, in m_RC4. So niow we have inited the rc4 keys and have the address where they are stored (m_RC4) The 0x05 packet is raw/decrypted, since we had to decrypt it to know it was an 0x05 packet. Inorder to pass 0x05 to the warden module, we must encrypt it. Inorder for the warden module to beable to read this 0x05 packet after it decrypts it, we must encrypt it with the warden modules current set of RC4 keys. [code] '//Copy the whole decrypted 0x05 packet into bData() ReDim bData(wLen - 1) Call CopyMemory(bData(0), ByVal hData, wLen) '//Copy warden RC4 in key, into bKey() Call CopyMemory(bKey(0), ByVal m_RC4 + 258, 258) '//Encrypt the 0x05 packet, with the warden modules inkey '//since warden will decrypt it with this very same key, '//warden will end up with the orginal decrypted 0x05 packet. Call RC4Crypt(bData(), bKey(), wLen) '//Copy wardens out rc4 key into bKey() Call CopyMemory(bKey(0), ByVal m_RC4, 258) [/code] Notice, we copy the out key before calling the wardens handler packet function. This is because, warden will return an encrypted responce (0x04 packet) Inorder for us to beable to decrypt it, we need to know the encryption key used to encrypt it. Now, we can call wardens handle packet function and pass it the encrypted 0x05 packet. [code] m_PKT = vbNullString 'PUSH &BytesRead 'PUSH Lengh 'PUSH &bData 'MOV ECX, Mem 'XOR EDX, EDX 'MOV EAX, Address 'CALL EAX 'RETN 16 Call ExecuteCode(bCode(), &H68, VarPtr(lngRecv), _ &H68, wLen, _ &H68, VarPtr(bData(0)), _ &HB9, m_ModMem, _ &HD233, _ &HB8, m_Func(2), _ &HD0FF, _ &HC2, 16, 0) If (Len(m_PKT) = 0) Then Exit Function [/code] This is pretty much; [code] push dword ptr lngRecv push LenghOf0x05Packet push dword ptr bData mov ecx, m_ModMem call dword ptr m_Func(2) [/code] Notice here, we set m_PKT to an empty string before calling the handle packet function. This is because, when calling this function, warden will call you're SendPacket() function, telling you their is a packet ready to be sent to battle.net. [code] Private Sub SendPacket(ByVal ptrPacket As Long, ByVal dwSize As Long) m_PKT = Space(dwSize) Call CopyMemory(ByVal m_PKT, ByVal ptrPacket, dwSize) End Sub [/code] So after calling the warden handle packet function, m_PKT should hold the encrypted 0x04 packet. We can not send this 0x04 packet directly to battle.net, since the warden module encrypted it with a differnt set of RC4 keys to the ones used by the bot. This is why we stored the outkey before calling the handle packet function, so we can decrypt the 0x04 packet back to its raw form. [code] '//Decrypt the responce/0x04 packet with the out key we copyed before. Call RC4CryptStr(m_PKT, bKey(), 1) '//Encrypt it with are bot's out key, so its readable by bnet. Call RC4CryptStr(m_PKT, m_KeyOut(), 1) '//Copy the new set of rc4 keys, that were generated by the handleing of 0x05 '//over are existing bot's rc4 keys. Call CopyMemory(m_KeyOut(0), ByVal m_RC4, 258) Call CopyMemory(m_KeyIn(0), ByVal m_RC4 + 258, 258) '//Send the encrypred 0x04 packet to battle.net Call OnSendPacket(m_PKT) [/code] Now, you should have you're new set of RC4 keys, so data recved from bent after this, will now be readable. Once you have finished with the warden module, you can then unload it, by 1st calling its unload export function (the warden module will callback on you're freememory() function, asking you to free memory you orginaly allocated when initing the module) [code] 'MOV ECX, Param 'CALL Address 'RETN 16 Call ExecuteCode(bCode(), &HB9, m_ModMem, _ &H15FF, VarPtr(m_Func(1)), _ &HC2, 16, 0) [/code] pretty much; [code] mov ecx, m_ModMem call dword ptr m_Func(1) [/code] Once you have unloaded the warden module like this, you can then free the memory used to hold the whole module (m_Mod) and null the export functions, and what ever else you feel needs to be cleared. hope this helps | May 31, 2009, 2:18 PM |
HdxBmx27 | UnknowN-TerroR, Short answer is that currently the actuall format or function of 0x05 is not publically [or as far as I know privately] documented. So, unless python has a way of running machine code you are out of luck. | June 1, 2009, 9:23 AM |
Ringo | [quote author=Hdx link=topic=17967.msg182867#msg182867 date=1243848210] UnknowN-TerroR, Short answer is that currently the actuall format or function of 0x05 is not publically [or as far as I know privately] documented. So, unless python has a way of running machine code you are out of luck. [/quote] hmm? You must have a bad memory :) [quote author=Ringo link=topic=17356.msg180673#msg180673 date=1228490202] The "HANDLE_0x05" i'm pretty sure, is what handles 0x05, or at least, generates 0x04 and the new encryption keys. It basicly parse's 0x10 bytes (0x05 data), does some stuff with it, then does an MD5, SHA1, encrypts the result with standard RC4, makes a callback to have the packet sent, then generates the 2 new RC4 keys. [/quote] It still works along those lines. 0x05 is feed into a variable md5 function, the result is then feed into wardens standard SHA1, where the result is then send back to battle.net as 0x04 payload. Iirc, the new out keys are created from the resulting variable md5 function and the new in key from the resulting sha1. I call it variable md5, since this function is differnt for every module. So unless you want to reverse that function every 8 hours (or how ever long the interval is between module changes), we pretty much have to let the warden module do it for us. Wasn't you trying to work out the differnces in that variable md5 function, some time ago, hdx? If it was possible for 1 warden module to manage 0x05 requests and produce the same output as all over modules, then 0x05 handler would of been reversed long ago :) | June 1, 2009, 11:54 AM |
chyea | [quote author=Hdx link=topic=17967.msg182867#msg182867 date=1243848210] UnknowN-TerroR, Short answer is that currently the actuall format or function of 0x05 is not publically [or as far as I know privately] documented. So, unless python has a way of running machine code you are out of luck. [/quote] I'd suggest a dynamic library. There are options for an inline assembler for Python but they're not good. So, sure, Python should be able to do anything any other language of choice is going to do. | June 1, 2009, 1:00 PM |
UnknowN-TerroR | [quote author=Hdx link=topic=17967.msg182867#msg182867 date=1243848210] UnknowN-TerroR, Short answer is that currently the actuall format or function of 0x05 is not publically [or as far as I know privately] documented. So, unless python has a way of running machine code you are out of luck. [/quote] I plan to make the module loader in C and, if there are no ways to run that code under non-windows system, to use wine. I was able to make every passage (RC4, broken sha, zlib inflate) through python, so this is the only problem. | June 1, 2009, 3:45 PM |
Ringo | If you have already loaded and mapped the module to memory with python, there shouldnt really be a problem. Assuming, you can get the address of a variable and call a function pointer (even if that requires useing user32.dll's CallWindowProcA) I've never used python in my life, so i've really no idea about problems you would run into. If running on non-window os' requires you not to use winapis, such as LoadLibrary and GetProcAddress (to write api function address's to the warden module) you should beable to just null them api address's. If some api's are required by the module, you could always just supply the address of a dummy std function with in you're program, and just return values that will keep the module from failing. iirc, warden modules only use a few apis when initialized and I dont think they use any for the rc4/handle packet function (as long as you only allow it to handle 0x05) specifically, what do you see as a problem at the moment? | June 1, 2009, 4:35 PM |
UnknowN-TerroR | [quote author=Ringo link=topic=17967.msg182872#msg182872 date=1243874158] If you have already loaded and mapped the module to memory with python, there shouldnt really be a problem. Assuming, you can get the address of a variable and call a function pointer (even if that requires useing user32.dll's CallWindowProcA) I've never used python in my life, so i've really no idea about problems you would run into. If running on non-window os' requires you not to use winapis, such as LoadLibrary and GetProcAddress (to write api function address's to the warden module) you should beable to just null them api address's. If some api's are required by the module, you could always just supply the address of a dummy std function with in you're program, and just return values that will keep the module from failing. iirc, warden modules only use a few apis when initialized and I dont think they use any for the rc4/handle packet function (as long as you only allow it to handle 0x05) specifically, what do you see as a problem at the moment?[/quote] Python, like many modern languages, is not a compiled language, but an interpreted language. Strings in Python, PHP, Perl or Ruby arent real strings, but structures handled internally by the interpreter (NOT by the script) that can't be hacked or executed for security reason (for instance you can't make a program crash, but you can only get errors). So, if i want to load and execute a precompiled code, like in this case, i must do it with a lower language. Why am i using python? Because it's very simple, intuitive, it has NATIVELY every library i need (socket, SHA1, zlib, gtk (i'll need it to build the interface), etc) and has the best string managing system i've ever seen. You're right, warden modules are pure assembly/binary with a few calls, they can be found with a simple hexadecimal editor, but i prefer using an engine like wine instead of mapping these functions manually, because they're windows-native. Thanks for the help, in a couple of weeks i'll get my first results. | June 1, 2009, 7:41 PM |
Ringo | Ah, ic why that could be a problem then :) Just an idea; If you can use win api's with python (1st link on google brought up this, somthing about ctypes) mainly to allocate/free memory, read/write values to that memory (with rtlMoveMemory or alike) and CallWindowProcA to call/execute that memory, you should still beable to do it in python. That should allow you to map the module to memory and call it's function's. As for the callbacks, you could just allocate a small block of memory, and write/copy some code to it, and have warden just call those memory blocks. (kind of a proxie function in memory) Just an idea anyway | June 2, 2009, 2:20 PM |
UnknowN-TerroR | I can't execute the module code without crashing (with a C program). This is the module i tried to run (the file isn't a real .txt file but the decoded and decompressed code sent by battle.net, rename it as you like): http://filebeam.com/c0d8f7eb04ad97715466becad4b286d0 The module crashes in the main function, calling a wrong address: [code]0xC0000005: Access violation at address 0xe9000021[/code] I've used exactly iago's preparation and init code, showed here: http://www.skullsecurity.org/wiki/index.php/Warden_Modules Can anyone try to load it and tell me if it runs or not with a tested system, like ringo's script? i want to find out if the bug is in the preparation or in the code itself (maybe i haven't downloaded it in the right way). Thank you. | June 4, 2009, 3:45 PM |
BreW | That's a real odd address, you sure you're calling that function in the valid range? | June 4, 2009, 4:52 PM |
UnknowN-TerroR | [quote author=brew link=topic=17967.msg182885#msg182885 date=1244134337] That's a real odd address, you sure you're calling that function in the valid range? [/quote] i did only this: [code] DWORD ECX, EDX, EBP; EBP = getInteger(pModule, 0x18); EDX = 1 - EBP; if(EDX > getInteger(pModule, 0x14)) return FALSE; ECX = getInteger(pModule, 0x10); // offsetWardenSetup ECX = getInteger(pModule, ECX + (EDX * 4)) + (DWORD)pModule; fnInitializeModule fpInitializeModule = (fnInitializeModule)ECX; [/code] and called the function with a valid function pointers array. Can anyone run the module i posted before? i'm almost sure it's an error in my decryption. | June 4, 2009, 6:53 PM |
HdxBmx27 | Stupid problem that got me for a while. Are you 100% sure that your memory space is being zeroed out before you prop the module into it? I'll see if I can find some debugging code I can get you to prep a module for comparison to another. I just don't know where I put it :/ | June 4, 2009, 7:45 PM |
UnknowN-TerroR | [quote author=Hdx link=topic=17967.msg182887#msg182887 date=1244144705] Stupid problem that got me for a while. Are you 100% sure that your memory space is being zeroed out before you prop the module into it? I'll see if I can find some debugging code I can get you to prep a module for comparison to another. I just don't know where I put it :/ [/quote] Yes, i realized it while comparing ringo's code to iago's code. I've already added a ZeroMemory() afterm malloc(). Edit: i've tried to run the module attached to the SCGP bot and it gives me the same error... it means something is wrong in my code preparation, i'll check it later. Thanks for all your support. | June 4, 2009, 7:50 PM |
UnknowN-TerroR | Nothing. I've installed Visual Basic 6 Enterprise Edition, learned some basic, compiled and tested ringo's warden handler with a module: it worked PERFECTLY, and i didn't know any Basic function/struct. With the C code, which is written in a language i write for years, nothing came. I've compared every single line of the C interpreter to the Basic one, adding debug outputs, comparing the prepared modules, changing definitions for hours, but the error remains the same. Functions are the same, code is the same, input is the same. I checked them (too) many times. this is my C project with a module attached, you need only to compile and run it (Visual Studio 9): http://filebeam.com/b9d795e7a3518ea6729f33b7e317ec48 if someone could solve this strange problem, i would be very grateful. | June 6, 2009, 4:32 PM |
BreW | I think that's because you didn't move the class pointer into ecx before calling, since it's a thiscall. | June 6, 2009, 11:21 PM |
UnknowN-TerroR | [quote author=brew link=topic=17967.msg182901#msg182901 date=1244330512] I think that's because you didn't move the class pointer into ecx before calling, since it's a thiscall. [/quote] I've switched to assembly: [code] _asm { mov ecx, dword ptr dwTable call A }[/code] it still crashes, but with another wrong address. same here: [code] _asm { mov ecx, dword ptr dwTable call dword ptr A }[/code] Edit: code fully converted from C++ to C. Nothing changes. | June 7, 2009, 8:05 AM |
UnknowN-TerroR | [center][color=red]SOLVED![/color][/center] wrong: [code] memset(&dwTable, 0, sizeof(FuncList)); dwTable.fpSendPacket = cSendPacket; dwTable.fpCheckModule = cCheckModule; dwTable.fpLoadModule = cLoadModule; dwTable.fpAllocateMemory = cAllocateMemory; dwTable.fpReleaseMemory = cReleaseMemory; dwTable.fpSetRC4Data = cSetRC4Data; dwTable.fpGetRC4Data = cGetRC4Data; fpInitializeModule((DWORD*)&dwTable);[/code] RIGHT: [code] memset(&dwTable, 0, sizeof(FuncList)); dwTable.fpSendPacket = cSendPacket; dwTable.fpCheckModule = cCheckModule; dwTable.fpLoadModule = cLoadModule; dwTable.fpAllocateMemory = cAllocateMemory; dwTable.fpReleaseMemory = cReleaseMemory; dwTable.fpSetRC4Data = cSetRC4Data; dwTable.fpGetRC4Data = cGetRC4Data; uint32 tableptr = &dwTable; fpInitializeModule((DWORD*)&tableptr);[/code] well...3 days wasted. Thanks to all for your support. I'm going to update this page http://www.skullsecurity.org/wiki/index.php/Warden_Modules so no one else will have this problem. Simple C warden module handler: http://filebeam.com/f1489d8beca6c075933e558e150bf0fa Edit: it works very well under windows, but it still crashes in wine... Edit2: problems under wine SOLVED! if you want to execute some code you must use VirtualAlloc() with a specific flag. | June 7, 2009, 8:57 AM |
Ringo | [quote author=UnknowN-TerroR link=topic=17967.msg182905#msg182905 date=1244365027] [code] .... uint32 tableptr = &dwTable; fpInitializeModule((DWORD*)&tableptr);[/code] well...3 days wasted. Thanks to all for your support. [/quote] idk why you wasted 3 days on that, the answer was already there. :) iirc, that is going to be a problem when calling other warden module functions, later on. tableptr is on the stack. You will be passing the stack address of tableptr to the init function, where the address of the callback table is stored. Every time warden needs to call one of them callback functions, it will access the table/list of functions, through the address of tableptr. So, for that reason, it's a good idea to have tableptr a static variable. Thats why I stored the address of the list, in the list's last item, then pass's the address of the last item to ecx. (since the list is static) [quote author=Ringo link=topic=17967.msg182862#msg182862 date=1243779535] [code] .... m_CallBack(7) = VarPtr(m_CallBack(0)) 'MOV ECX, &Param 'CALL Address 'RETN 16 m_ModMem = ExecuteCode(bCode(), &HB9, VarPtr(m_CallBack(7)), _ &H15FF, VarPtr(A), _ &HC2, 16, 0) End Sub [/code] Notice, ecx gets the address of a variable that holds the address of the callback function list. [/quote] | June 7, 2009, 2:58 PM |