Valhalla Legends Forums Archive | Battle.net Bot Development | [How to] Parse Warden 0x02 useing pattern detection

AuthorMessageTime
Ringo
I got a PM a few days ago, asking about warden's 0x02 packet, and how i'm parseing it.
I might as well post the responce here, since I don't fancy explaining this again and again :)
[quote]
I'm having some trouble translating the Get0x02Data function. My current revision should be equivalent, but I'm not matching wc3's warden output.

Could you explain what Get0x02Data is supposed to be doing? The first part appears to be checking memory offsets, but the second part makes no sense to me.

Also, how did you generate the .ini files? If I have game.dll (and other files) available, can I dynamically beat these checks?
[/quote]
I will explain how im parseing it, in depth, from start to finish.
This will be are example packet dump, we will be braking up/working with:
[code]
02 08 67 61 6D 65 2E 64 6C 6C 00 9F DE FF AC 88       ..game.dll......
A3 F7 06 ED 66 20 37 A9 6B BC A5 DE B3 27 B0 93       ....f 7.k....'..
0E E0 18 D2 B2 F1 00 00 2A 9F C2 22 5E 69 07 6B       ........*.."^i.k
C2 C1 5A BB C7 8C 74 5F 7C D9 9E F2 D2 9F 5C 6C       ..Z...t_|.....\l
03 80 F8 D4 06 00 30 9F 2E A1 91 55 87 F2 38 B9       ......0....U..8.
3C 44 A1 AB 67 8A 99 A9 F7 9E 9E 31 26 35 91 38       <D..g......1&5.8
F4 D7 06 00 30 9F 66 5F E0 96 5A 4C B6 FB 50 7D       ....0.f_..ZL..P}
87 01 E0 99 50 3B EC EE F1 E7 B4 EE E6 69 08 59       ....P;.......i.Y
00 00 19 53 01 8E 1E 3A 00 07 9F E9 32 09 08 D8       ...S...:....2...
FE 41 AD B2 C7 16 6A 0A 89 A0 2F 0F 34 AD 6D 9D       .A....j.../.4.m.
93 E9 15 20 50 00 00 10 E0                            ... P....
[/code]

The 1st byte, 0x02, is the packet ID.
After the packet ID, you have a list of strings.
Each string has the following format:
(BYTE) String lengh
(VOID) string
As soon as you hit a string lengh of 0x00, you are at the end of the string list.
So, in the above log, the 1st string lengh is 0x08.
08 67 61 6D 65 2E 64 6C 6C "game.dll"
The 2nd string lengh is 0x00.
So, we should have now parsed the string list from the packet, and have only 1 string, "game.dll".
The string list should start at index 1, in the string list. (List[0]="", list[1]="game.dll", etc)

Now, appending the string list, you should be seeing a list of commands.
The 1st byte of each command, is a sort of 2nd level packet ID, telling you how to parse the command.
In the above example, the 1st byte following the string list, is 0x9F.
Note, this 0x9F ID will be differnt for each logon.
You should ntoice, the very last byte in the 0x02 packet, is 0xE0.
This byte was calculated from the 0x05/0x04 warden packet data.
To get the "reall" command ID, you xor the command ID with this end byte (0x9F^0xE0=0x7F)
So, we now know, the "reall" command ID for this request/command, is 0x7F.
This will remain true, for the given module. However, every module has a differnt set of ID's hardcoded into it -- so on another warden module, it maybe 0x54 for the same request/command type etc etc.
For this reason, we are not going to take notice of this command ID. We are going to do some basic pattern detection on the appending data to determin what command it really is.
At the moment, warcraft 3 and starcraft/broodwar are only useing 2 command types.
A memory check command, and a memory page check.
We know, currently, a memory check command, is 7 bytes in lengh and a page check command is 30 bytes in lengh.
I have posted the format for both checks, here.
For the memory check command, the format is:
(BYTE) String index
(DWORD) Offset/address
(BYTE) Lengh

So, is this 0x9F command a memory check command, or a page check command?
1st thing we need to do, is check if we have at least 7 bytes remaining, after are current parseing position.
In this case, we do.
Now we know theres enough bytes remaining for it to be a memory check, we need to test/compare a few values.
If the 1st byte into the command (string index) is greater than the number of strings in the string list, then this can not be a memory check.
In the above packet dump, the next byte (0xDE) is much higher than the number of strings in the list.
How ever, lets assume it did point to a valid string index.
We then check the lengh byte, to make sure it's not to high. 0x40 bytes is a good max number of bytes to go with.
Lets say for a moment, all the above checks (at least 7 bytes remaining, string index byte is inrange of the string list and the lengh of data to read byet is =< 0x40) all pass.
There is 1 more check we can do, to verify this really is a memory check command, and that is, check the offset/address dword.
If this matchs an address we know is currently been checked, and the lengh byte matchs the given address, then its safe to say this command is infact a memory check.
If we did not check the offset dword, and for ie, just read this offset from the binary, the chances of falsely identifying this command as a memory check, go up massively.

If i've confused you/over explained it, this is where we are at in the packet;
9F DE FF AC 88 A3 F7........
The code below (not compiler friendly) shows some simple checks done on this data (note; pos is the 0x9F byte, pos+1 is 0xDE etc)
[code]
//Is there enough bytes remaining in the packet, to be a memory check?
IsMem = ((pos+6) < LenghOfPacket)
//if so, does the string index byte line up with the string list?
If IsMem  Then IsMem = IsMem And (data[pos+1] <= NumberOfStrings)
//if so, check the offset, lengh and string, match a known offset/address/string
If IsMem  Then
    S = StringList[data[pos+1]] & "&H" & Hex(data[pos+2, 4]) & "_" & data[pos+6]
    select case S
        case "game.dll&H3A1DCE_7": S = "E8 5D D6 C6 FF 8B D0"
        case "game.dll&H285B3A_8": S = "E8 81 FA 22 00 8B 40 10"
        case "game.dll&H3A1E9B_4": S = "23 CA 75 32"
        ..........
        case else: S= ""
    end select
    IsMem = IsMem And (len(S)>0)
    //S should now hold the memory from the given offset
    If IsMem then
        //ITS A MEMORY CHECK, S HOLDS THE RESPONCE MEMORY.
       Exit
    End if
End if
//Its not looking like a memory check command, unless there is a new offset that is unknown to us.
//maybe it looks like a page check command?
[/code]
So, you should beable to clearly see, that the data "9F DE FF AC 88 A3 F7" fails this simple IsMemoryCheckCommand pattern detection.
(If you would like to step through these conditions compareing them with known strings/offsets/lenghs, here is a list;
[code]
game.dll&H3A1DCE_7=E8 5D D6 C6 FF 8B D0
game.dll&H285B3A_8=E8 81 FA 22 00 8B 40 10
game.dll&H743576_8=C1 E0 08 03 E8 8B 84 AE
game.dll&H361DD3_7=E8 78 F4 1C 00 85 C0
game.dll&HF453_9=8B 41 14 8B 49 10 BA 02 00
game.dll&H3C1354_8=F6 D0 8A C8 8B 44 24 1C
game.dll&H3F92CA_6=75 0A 83 7B 14 00
game.dll&H3A1E8E_7=8B 54 24 20 0F B7 32
game.dll&H285B33_7=B9 0D 00 00 00 8B E8
game.dll&H283444_7=8B C8 BA 01 00 00 00
game.dll&H39A39B_6=8B 97 98 01 00 00
game.dll&H39A458_6=74 27 39 6C 24 44
game.dll&HF490_6=74 08 8B 00 83 C4
game.dll&H73DFFC_7=E8 DF 3D FF FF 85 C0
game.dll&H361DF9_7=33 C9 B8 01 00 00 00
game.dll&H431569_6=85 C0 0F 84 AD 00
game.dll&H356F1C_8=3B 86 18 02 00 00 89 44
game.dll&H3A1DE3_4=75 04 A8 02
game.dll&H36040A_6=EB 08 C7 44 24 18
game.dll&H285BA2_5=75 29 53 8B CF
game.dll&H3A1DE9_7=8B 44 24 24 66 09 18
game.dll&H39A3B1_10=55 50 56 E8 37 7B 00 00 23 D8
game.dll&H356C67_8=85 DB 8A 8E E8 07 00 00
game.dll&H361DFC_6=01 00 00 00 D3 E8
game.dll&H39A465_13=66 85 87 F4 01 00 00 74 1D 8B 8F 98 01
game.dll&H285B8C_6=74 2A 8B 44 24 20
game.dll&H28345C_4=C3 CC CC CC
game.dll&H3A1E64_6=8B 0C 41 66 8B 04
game.dll&H356E7E_5=66 85 C0 76 04
game.dll&H73DEC9_6=8A 90 6C 68 AA 6F
game.dll&H3C135C_10=3D FF 00 00 00 76 05 C1 F8 1F
game.dll&H362211_10=85 C0 0F 84 30 04 00 00 8B 03
game.dll&H431556_6=85 C0 0F 84 C0 00
game.dll&H3A1E9B_4=23 CA 75 32
game.dll&H3C5C22_12=74 0B 81 88 7C 02 00 00 00 02 00 00
game.dll&H73DEB7_10=0F B7 0C 4A 81 C9 00 F0 00 00
[/code]
So if its not a memory check command, maybe its a page check command?
Lets see!
9F DE FF AC 88 A3 F7 06 ED 66 20 37 A9 6B BC A5 DE B3 27 B0 93 0E E0 18 D2 B2 F1 00 00 2A
Do we have at least 30 bytes of data remaining in the packet?
in this case, we do.
Based on the format posted here, is the last byte (lengh byte) <= 0x80?
0x2A is =< 0x80, so yes!
Is the 2nd from last byte equal to 0x00?
0x00 = 0x00, so yes!
Is the 3rd from last byte <= 0x40?
0x00 is =< 0x40, so yes!
Now lets check the last 4 bytes and compare it with a list of known values, to make sure.
[code]
&HD0000E8
&HE000622
&H300006D4
&H19000059
&H300006D7
&H23000048
&H2A0000F1
&H24000032
&HE0001FD
&H20000049
&H300007A8
&H1700007C
&H1F000234
&H100000A1
&H10000050
&HD000160
&H10000070
&H1A0000C3
&H24000030
&H3700008E
&H3000069C
&H1F000219
&H2A0000E1
&H28000091
[/code]
F1 00 00 2A is = &H2A0000F1, so yes!
We know its probly not a memory check, but it looks like a known page check.

Now we know this is a page check, we move the position up 30 bytes, to the next command.
You should notice, these are the actual commands;
[code]
9F DE FF AC 88 A3 F7 06 ED 66 20 37 A9 6B BC A5 DE B3 27 B0 93 0E E0 18 D2 B2 F1 00 00 2A
9F C2 22 5E 69 07 6B C2 C1 5A BB C7 8C 74 5F 7C D9 9E F2 D2 9F 5C 6C 03 80 F8 D4 06 00 30
9F 2E A1 91 55 87 F2 38 B9 3C 44 A1 AB 67 8A 99 A9 F7 9E 9E 31 26 35 91 38 F4 D7 06 00 30
9F 66 5F E0 96 5A 4C B6 FB 50 7D 87 01 E0 99 50 3B EC EE F1 E7 B4 EE E6 69 08 59 00 00 19
53 01 8E 1E 3A 00 07
9F E9 32 09 08 D8 FE 41 AD B2 C7 16 6A 0A 89 A0 2F 0F 34 AD 6D 9D 93 E9 15 20 50 00 00 10
[/code]
The 1st 4 are clearly page check commands, the 5th is a memory check command, and the 6th is another page check command.
So, for building the responce to these, we give the all clear for the 1st 4 page checks.
Are responce data is now, "00 00 00 00" (4x all clears)
We now append the "success on reading memory" for the memory check command, as well as it's related memory chunk.
So the responce data should now be "00 00 00 00" + "00 8B 54 24 20 0F B7 32"
We then give the all clear for the last page check, giving us "00 00 00 00 00 8B 54 24 20 0F B7 32 00"

Now we have the responce data for the above 0x02 packet, we SHA1 this data with wardens SHA1 function and xor each of the resulting dwords together.
This should give us "D5 32 D1 02".
The lengh of the responce data is 0x0D bytes.
And that makes up are responce 0x02 packet;
(BYTE) packet ID 0x02
(WORD) lengh of responce data 0x0D
(DWORD) hash of responce data 0x02D132D5
(VOID) Responce data "00 00 00 00 00 8B 54 24 20 0F B7 32 00"
[code]
02 0D 00 D5 32 D1 02 00 00 00 00 00 8B 54 24 20       ....2........T$
0F B7 32 00                                           ..2.
[/code]


Now, if by chance, it fail's these pattern checks, it's more than likely that the memory check/page check offsets/lenghs have been changed.
So, you're probly wundering, how do you get these new offsets and their related memory blobs?
Well, for memory checks, the memory returned can be found at the strings base address+offset, or if the string is empty (string index 0x00) then the offset is a base address.
For example, the memory check "53 01 8E 1E 3A 00 07"
the string index points to the string "game.dll"
If you open up game.dll, and go to the address 0x003A1E8E and look at the following 7 bytes, you will see the data "8B 54 24 20 0F B7 32"
This is the memory that is returned to battle.net.
When these offsets change, I takes around 5mins, to check and double check the offsets and get the related memory data to add to a new list.
IMO, it's safer go over the packets manualy a few times, and build a list.
This way, you can identify changes to the protocol and most of all, have peace of mind.

This is just one of many ways you can parse this packet.
I figured, releaseing code that works in this way, would be the most easyest to understand and probly the most fail safe.

If anyone wants to add to the above (i've probly over explained a few things or missed a few things) or doesnt understand somthing, feel free to ask and I will add to and/or correct the above.
May 25, 2009, 12:28 PM
Strilanc
You explained what the memory check actually does (reads bytes from the file; or presumably from the file loaded in memory), but you didn't explain what the page check actually does.
May 25, 2009, 3:22 PM
Ringo
[quote author=Strilanc link=topic=17961.msg182823#msg182823 date=1243264953]
You explained what the memory check actually does (reads bytes from the file; or presumably from the file loaded in memory), but you didn't explain what the page check actually does.
[/quote]
Ah, as for what the checks acutaly do, it's been awhile since i've poked around, but from what I remember, there is 3 types of page checks (2 were currently active on d2 last time i checked) but I forget what one is what.
iirc, the one active on sc/w3 at the moment, loops through the memory pages useing the VirtualQuery() api, SHA1's blocks from each page (I think a few values from the MEMORY_BASIC_INFORMATION structure also get computed with the hash, such as the protection flag), compares the hash's and returns 0 if none matched. iirc, if a match is found, the result has the 1st 7 or 8 bits sent back.
It's really been awhile since I was looking at this, so take this with a pinch of salt.
May 25, 2009, 3:37 PM
Strilanc
So it doesn't include any checksums in the result? You just have to say "Yep, that check I definitely did sure did succeed!"?

That seems... pointless. It would make more sense to randomly send a check that should fail, which would make it hard to answer.
May 25, 2009, 4:13 PM
Ringo
Thats pretty much the bases of all the checks. altho, some others also send back data to be check'ed. It just so happens, this one doesn't.
They are not really worryed about bot user's trying to replicate the handlers, since warden was souly designed to monitor a game client for people useing cheats.
I would assume, this type of page check would be used to find injected dlls and modifications to the warden module it's self, etc etc.
No one really knows what goes through the collective minds of blizzard, altho the theory is, not alot. :)
I really havent looked into the other commands that are yet to be used, or these page check commands, enough to really say more, since they could all change at any given moment.
May 25, 2009, 5:30 PM

Search