• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Is it possible to crash a wc3 client via a hack?

Status
Not open for further replies.
Level 15
Joined
Aug 7, 2013
Messages
1,337
Hi,

I was playing a semi-competitive game today and when I typed GG to my opponent, he responded with something. I couldn't see what he typed, only that he typed something because my WC3 instantly crashed.

Was this simply bad luck? I have heard WC3 can crash when a player is defeated or leaves the game.

Or did the player crash my client in response to losing? I know this is possible for older Blizzard titles.

Here is the crash message. Also, the music from the game is still playing (albeit looping) until I press "ok."

23mvjvo.png
 
Were you playing melee or a custom map? It is definitely possible to crash wc3 via code. You can always detect a particular chat message and locally force a crash.

If so, then that guy is a douche. Ask him for his P.O. box and send him hate mail.

But that aside, it could be coincidental. Did anything, besides the chat message, happen at that time? Also, do you still have a log of the crash report?
 
Level 15
Joined
Aug 7, 2013
Messages
1,337
Hi Purgeandfire.

Well I know for sure the map isn't rigged. I am asking if there's a third party hack can cause someone elses wc3 to crash.

I just played another game, and the same thing happened. I kicked some guy's ass, then I see a player type a text message but I can't see what it says, as the game immediately crashes. Here's the image.

vfxbh5.png


What do you mean by the log?

BTW I've been playing this map for a few years (yes the same map). Crashes like this have never happened before.

I'm attaching the 2 replays found in the log. These are the games where it crashed in the circumstances I described.

View attachment SETH-PC_080314_164825_replay.w3g

View attachment SETH-PC_080314_174409_replay.w3g
 
Level 14
Joined
Jun 27, 2008
Messages
1,325
Which map is it? Upload or link it, then we can check if there is a "drop" command..

But there were different drophacks in the past, most of them usually dont crash wc3 tho, they just disconnect you. Doesnt mean its not possible..
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,188
Yes a hack can crash WC3 clients.

The most famous was the order queue stack overflow crash that would occur if you queued up ~8,000 orders on a unit. Obviously blizzard used iterative function calls so eventually it overflowed the thread stack which for security reasons generally causes a process crash. You could tell someone was doing this by the horrific lag that occurred as he sent orders so fast that it caused network congestion between clients and host. He obviously protected himself with a modified executable with larger stack size so you all would crash before he did so it registers him as the winner.

This is why we now have a limit on the number of orders you can queue on a unit. They patched it simply by enforcing a limit to the number of orders you can queue so a stack overflow was no longer possible.

That said this is just one of the old ways to crash it. Beyond a doubt people will have found new ways.

By the sounds of it they are injecting some kind of invalid chat message into the game. The crash is caused by an instruction trying to write over itself! It crashes due to the pages reserved for program code being write protected so writing over an instruction results in the OS sending a critical error to the process and shutting it down. This is very bad as this means that they some how have write control over some piece of game code. If they can do this to crash it, it may also be possible that they can use messages to manipulate the game state however this would be very detectable.

Could you try recording your sessions with a video capture program? Maybe you can then read what they type.

Have you checked the replay files? Try and get them from the robot administrator if yours are invalid since they will show what happens as all messages are recorded.

This is very similar to an old crash the game Empire Earth had in 2000. If you typed a very long message (max length of 'W' characters which were among the widest in the typeset) it would cause everyone who saw it to crash. This was patched for the expansion.
 
Level 15
Joined
Aug 7, 2013
Messages
1,337
Dr Super Good said:
By the sounds of it they are injecting some kind of invalid chat message into the game. The crash is caused by an instruction trying to write over itself! It crashes due to the pages reserved for program code being write protected so writing over an instruction results in the OS sending a critical error to the process and shutting it down. This is very bad as this means that they some how have write control over some piece of game code. If they can do this to crash it, it may also be possible that they can use messages to manipulate the game state however this would be very detectable.

From the crash windows I showed are you sure this is what is happening or is this a hypothesis, e.g. could it just me some miscellaneous WC3 crash and it was just a coincidence both times that some unfortunate events caused this? As in are you certain that both these crashes were caused by a malicious human?

Also I attached the replays to my last post. Unfortunately they crash and you can't see the message being sent at all, as the replays crash in the frame right before the message was sent.
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,188
From the crash windows I showed are you sure this is what is happening or is this a hypothesis, e.g. could it just me some miscellaneous WC3 crash and it was just a coincidence both times that some unfortunate events caused this?
The error is very unusual as I mentioned. Explanation below.

As in are you certain that both these crashes were caused by a malicious human?
I am mostly certain it is not related to malicious players. Explanation below.

EDIT:

Ok what is happening is that the game is trying to call a function in a page that does not exist (not allocated at all!). Basically a dynamic function call (using a function pointer) gone wrong. The error generated is probably garbage as a result that there is nothing to call there.

The chat message you are seeing before the crash is probably sent by the host robot. At least 1 other player has less latency than you to the server so is ever so slightly ahead of you. As a result he crashes before you, this is picked up as a TCP disconnect by the host robot which then forwards this message on to you. This message is processed and displayed just before you crash due to the same error (your client reaches the same progression). There is no crash hack involed, purely WC3 being an annoying piece of buggy... um... stuff.

This is not all. Here is the actual game function call responsible for the crash! *queue law suit as I am not sure Blizzard completely agrees to people doing this*
Code:
6F049510  /$ 8B4424 04      MOV EAX,DWORD PTR SS:[ESP+4]
6F049514  |. 56             PUSH ESI
6F049515  |. 57             PUSH EDI
6F049516  |. 8B78 0C        MOV EDI,DWORD PTR DS:[EAX+C]
6F049519  |. 85FF           TEST EDI,EDI
6F04951B  |. 8BF1           MOV ESI,ECX
6F04951D  |. 74 76          JE SHORT Game.6F049595
6F04951F  |. F647 20 01     TEST BYTE PTR DS:[EDI+20],1
6F049523  |. 75 25          JNZ SHORT Game.6F04954A
6F049525  |. 8B46 30        MOV EAX,DWORD PTR DS:[ESI+30]
6F049528  |. 85C0           TEST EAX,EAX
6F04952A  |. 75 05          JNZ SHORT Game.6F049531
6F04952C  |. E8 5F934200    CALL Game.6F472890
6F049531  |> 6A 04          PUSH 4
6F049533  |. 6A 00          PUSH 0
6F049535  |. 57             PUSH EDI
6F049536  |. 8BC8           MOV ECX,EAX
6F049538  |. E8 A3B92300    CALL Game.6F284EE0
6F04953D  |. 85C0           TEST EAX,EAX
6F04953F  |. 74 09          JE SHORT Game.6F04954A
6F049541  |. F747 20 000080>TEST DWORD PTR DS:[EDI+20],800000
6F049548  |. 74 4B          JE SHORT Game.6F049595
6F04954A  |> 6A 00          PUSH 0
6F04954C  |. 56             PUSH ESI
6F04954D  |. 8BCF           MOV ECX,EDI
6F04954F  |. E8 8C1E2600    CALL Game.6F2AB3E0
6F049554  |. 6A 00          PUSH 0
6F049556  |. 56             PUSH ESI
6F049557  |. 8BCF           MOV ECX,EDI
6F049559  |. E8 721D2600    CALL Game.6F2AB2D0
6F04955E  |. 8B16           MOV EDX,DWORD PTR DS:[ESI]
6F049560  |. 8B82 14030000  MOV EAX,DWORD PTR DS:[EDX+314]
6F049566  |. 8BCE           MOV ECX,ESI
[U][B]6F049568  |. FFD0           CALL EAX[/B][/U]
6F04956A  |. 85C0           TEST EAX,EAX
6F04956C  |. 74 1B          JE SHORT Game.6F049589
6F04956E  |. F746 20 004000>TEST DWORD PTR DS:[ESI+20],4000
6F049575  |. 74 12          JE SHORT Game.6F049589
6F049577  |. 8BCE           MOV ECX,ESI
6F049579  |. E8 22E4FEFF    CALL Game.6F0379A0
6F04957E  |. 85C0           TEST EAX,EAX
6F049580  |. 74 07          JE SHORT Game.6F049589
6F049582  |. 8166 20 FFBFFF>AND DWORD PTR DS:[ESI+20],FFFFBFFF
6F049589  |> 68 80E4AA6F    PUSH Game.6FAAE480
6F04958E  |. 8BCE           MOV ECX,ESI
6F049590  |. E8 DBEBFEFF    CALL Game.6F038170
6F049595  |> 5F             POP EDI
6F049596  |. 5E             POP ESI
6F049597  \. C2 0400        RETN 4

This is the thread state...
EAX 3F028F5C
ECX 22B94F84
EDX 22B94A24
EBX 1F167964
ESP 0018EF5C
EBP 0DF6BA04
ESI 22B94F84
EDI 1F515404
EIP 3F028F5C
C 0  ES 002B 32bit 0(FFFFFFFF)
P 0  CS 0023 32bit 0(FFFFFFFF)
A 1  SS 002B 32bit 0(FFFFFFFF)
Z 0  DS 002B 32bit 0(FFFFFFFF)
S 0  FS 0053 32bit 7EFDD000(FFF)
T 0  GS 002B 32bit 0(FFFFFFFF)
D 0
O 0  LastErr ERROR_SUCCESS (00000000)
EFL 00210212 (NO,NB,NE,A,NS,PO,GE,G)
ST0 empty 0.0
ST1 empty 0.4602382779121398926
ST2 empty 0.4602382779121398926
ST3 empty -17344.562500000000000
ST4 empty 23872.000000000000000
ST5 empty 186.50000000000000000
ST6 empty 10752.000000000000000
ST7 empty 0.0
               3 2 1 0      E S P U O Z D I
FST 0020  Cond 0 0 0 0  Err 0 0 1 0 0 0 0 0  (GT)
FCW 027F  Prec NEAR,53  Mask    1 1 1 1 1 1
Underlined is the actual source of the error (that call will throw the fatal error and crash the game).

As we can see the dynamic function is part of a structure. Following that structure will be not at all useful since the fact the call is nonsense means likely the structure is also nonsense. However that structure is located by another pointer. This pointer appears to be computed by some very complicated spaghetti function.

How to progress from here is to find when this function runs normally what its purpose is. Dumping a functional instance of the struct (as far as known) would help as it could possibly allow one to guess what object it represents and so where the error is coming from. The fact it is of considerable size (at least 318 bytes) means that this is no light weight structure as it holds a considerable amount of state information.
 
Last edited:
Level 5
Joined
May 6, 2013
Messages
125
Are you sure that it's that call that causes the crash? That seems a bit fishy to me:
1: esi is a non-volatile register and therefor the call above does not modify it. Also, as can be seen in the beginning of the function, it's used to store the this* of this function.
2: seeing how esi this the this*, we can be pretty sure that the call is a regular call to a virtual function. Since the virtual table is located in constant memory and modifying it would have crashed the game, and the compiler wouldn't compile a call to a non-existent function, this can only be an invalid number if either the class has been typecasted "up" (into an extended type that has functions that the actual object doesn't), which would be a major oversight to the max, or if blizzard somehow wrote *(DWORD*)(classPtr) = xyz, which allready looks incredibly wrong.
3: calling an invalid function should crash when the memory has no execute access right (which it doesn't if its not even allocated). However, the errors complained about a missing write access right.

To be honest, i can't even think of any way how an instruction could possibly write to itself as a result of an exploit; you can't write to or read from eip, so even getting the codes location is far from a trivial task.
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,188
Are you sure that it's that call that causes the crash? That seems a bit fishy to me:
1. Dynamic function call return point is the last stack frame before the thread raises a fatal error.
2. The register it uses to determine the call jump location is set to the exact address that the fatal error message is reporting is trying to be "written" to.
3. The address being called is not in the program code sections of memory. It is not even allocated in the heap (why it crashes). It may have been allocated in the heap at one stage as it is close to the top of the heap.

1: esi is a non-volatile register and therefor the call above does not modify it. Also, as can be seen in the beginning of the function, it's used to store the this* of this function.
ESI gets pushed multiple times onto the stack and I am not sure where it is getting popped. ESI itself is unimportant since itself is used as a pointer to lookup the actual pointer of a structure. This structure causes the crash meaning that retrieved structure is invalid. There are two possible causes for this, ESI pointing to invalid data or the address ESI points to is pointing to invalid data. In any cases the addresses used look like heap addresses but that could be coincidence.
2: seeing how esi this the this*, we can be pretty sure that the call is a regular call to a virtual function. Since the virtual table is located in constant memory and modifying it would have crashed the game, and the compiler wouldn't compile a call to a non-existent function, this can only be an invalid number if either the class has been typecasted "up" (into an extended type that has functions that the actual object doesn't), which would be a major oversight to the max, or if blizzard somehow wrote *(DWORD*)(classPtr) = xyz, which allready looks incredibly wrong.
This is a false assumption. The same could could be generated by doing very stupid stuff with function pointers ("this" points to a pointer to a structure with a function pointer as a member). It could also be a virtual function call like you said but that helps little to track the error down. However something stupid like calling a virtual function on a destroyed member that has since been overwritten by other data would give the effects witnessed and not throw any compile errors and not even look stupid since nothing stops you calling a virtual function on an object that was deleted as long as the addresses resolved reside in an appropriate page.

3: calling an invalid function should crash when the memory has no execute access right (which it doesn't if its not even allocated). However, the errors complained about a missing write access right.
Except you are calling un-allocated pages. The error generation is done by a kernel level interrupt that eventually cascades to an error handler in user space. It is possible that the error handler or the error capture is reporting incorrect results in this case. The debugger I used captures the error itself displaying it as "does not know how to continue" so it is easily possible that the error report mechanisms is not designed to pickup on these errors. The stack dump WC3 makes confirms it is a dynamic function call gone wrong by the return address being present to the dynamic function call instruction which happens to be trying to call the same address reported in the error.

To be honest, i can't even think of any way how an instruction could possibly write to itself as a result of an exploit; you can't write to or read from eip, so even getting the codes location is far from a trivial task.
Easily possible with memory garbage however certainly is improbable. Unless you force it to a certain address that you know will call it since usually the program files get loaded into similar pieces of memory based on how the OS generates processes.
 
Last edited:
Level 5
Joined
May 6, 2013
Messages
125
Except you are calling un-allocated pages. The error generation is done by a kernel level interrupt that eventually cascades to an error handler in user space. It is possible that the error handler or the error capture is reporting incorrect results in this case. The debugger I used captures the error itself displaying it as "does not known how to continue" so it is easily possible that the error report mechanisms is not designed to pickup on these errors. The stack dump WC3 makes confirms it is a dynamic function call gone wrong by the return address being present to the dynamic function call instruction which happens to be trying to call the same address reported in the error.

Guess you never stop learning :)

Easily possible with memory garbage however certainly is improbable. Unless you force it to a certain address that you know will call it since usually the program files get loaded into similar pieces of memory based on how the OS generates processes.

considering that it was not the intention to do this, it's really not that easy to make a bug do this (easy and probability go hand in hand on this one). Anyways, i did a short test run (im kind of busy right now, stupid exams :goblin_cry:), and for me, the crashing instruction didn't even access itself (well, the crashing instruction doesn't even really exist, so i guess this is a quirk of the error message, which, as you explained before, seems to have problems with these kinds of errors in general.)

This is a false assumption. The same could could be generated by doing very stupid stuff with function pointers ("this" points to a pointer to a structure with a function pointer as a member).
Calling it a false assumption is a bold move when the alternative is so incredibly unlikely. In fact, i checked it: it really is the virtual table, and this really is a virtual call. For reasons unknown to me, the call right before the virtial call overwrites the virtual table with some nonsense (though it does not call msvcr.free or msvcr80.free). The Function it's supposed to call is Game.dll+69160 (seems to simply check a flag from a member), and if you restore the virtual table pointer using a debugger before the call, everything works totally fine. Can't wait for the weekend to find out why the heck they modify the virtual table :thumbs_up:
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,188
In fact, i checked it: it really is the virtual table, and this really is a virtual call. For reasons unknown to me, the call right before the virtial call overwrites the virtual table with some nonsense (though it does not call msvcr.free or msvcr80.free).
Depends how it over writes it. Does it do so in an array operation? Maybe it is copying some other object over the "this" object which was deleted so this gets replaced with garbage data. Could even be an invalid pointer (pointing to parts of this as it was previously a different object or that the object was deleted and overwritten so the pointer represents some other data).

It would be so easy if we had the source code... We would not have to guess what is going on.
 
Status
Not open for further replies.
Top