Example 7

From SkullSecurity
Revision as of 17:28, 16 March 2007 by Ron (talk | contribs)
Jump to navigation Jump to search
Stop hand.png This page is under construction. USE AT YOUR OWN RISK!







Assembly Language Tutorial
Please choose a tutorial page:

This is what this entire tutorial has been building up to: writing a cheat for a game!

I have chosen the simplest cheat I can think of that demonstrates most of the concepts I've attempted to teach: displaying a notification whenever a player spends minerals in Starcraft.

This demonstration will use Starcraft 1.05. There are two reasons:

  • So it can't easily be translated to modern versions, which should avoid pissing off Blizzard.
  • Because the newer versions break TSearch, and I don't really want to find/write another memory searcher.

Two locations need to be found:

  • The function that can be called to display messages on-screen.
  • The function that is called when minerals are spent.

Creating the .dll

This .dll will be written in Microsoft Visual Studio and injected with my Injector.

Here's how to create the .dll (this works in Visual Studio 2005):

  • Run Visual Studio.
  • Create a new project.
  • Choose "Win32 Console Application" and give it a name.
  • In the wizard, set the application type to "DLL".
  • Disable "Precompiled header" (you don't have to, but I prefer to).

I generally start by removing all the crap that Visual Studio adds, then I add a switch over the two conditions I care about. Here's the starting code:

#include <stdio.h>
#include <windows.h>

BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
	switch(ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		break;

	case DLL_PROCESS_DETACH:
		break;
	}

    return TRUE;
}

This should compile into a .dll file, which can be injected/ejected (though it does nothing).

Displaying Messages

Finding the function to call is always tricky. It goes back to the same principle as finding the place to crack a game: you have to find a starting point, and trace your way to the appropriate function. Depending on the game, this could be significantly difficult.

Some ways to do this might be:

  • Searching for messages you see on the screen.
  • Typing a message, searching in memory for it, and pressing enter.
  • Figuring out how events in Use Map Settings games work.

The first message I think of that's displayed on-screen is chat messages from other players, which look like "player: message". Another common chat message is "[team] player: message". That seems like a good place to start looking.

In IDA, load Starcraft.exe and wait till it finishes analysis. Then go to the strings window/tab and search (by typing it in) for "[", and the first result is "[%s] %s: %s". Anybody who knows C format strings will know that, in a format specifier, %s indicates a string. Since we have a reasonable idea of how strings work, we can guess that "%s: %s" would be a normal message, so we search for that and double-click it.

The address of that string should be 0x004F2AE0. If it's not, you might be on the wrong version of Starcraft, which should be fine. Just remember that the addresses I provide may not be right.

On that address, press ctrl-x. There's only one cross reference, at sub_004696C0+105, so double-click that. We see that this string is a function call, shown here:

push eax
push offset aSS_2
push 100h
push ecx
call sub_4D2820

Anybody well-versed in C will likely recognize this as a call to snprintf(). The first variable, ecx, is the buffer. Then 100h is the length, aSS_2 ("%s: %s") is the format string, and the two string that are substituted for %s are eax and (not shown here) edi.

Since ecx is a volatile variable, it's likely going to change after the function call, which means that this code won't be reliant on the value. If we look above, we can find where ecx is loaded with the buffer:

lea ecx, [esp+110h+var_100] 

Note that the frame pointer isn't being used here, but that IDA still managed to name the local variable. Click on var_100, press 'n', and call it 'buffer'.

On the line before, you should see:

lea eax, dword_6509E3[esi]

This is the first string parameter that will be substituted, which means it corresponds to the first %s, which is the player's name. Presumably "esi" is the player number. This will be important later.

Click on the "buffer" variable you defined to highlight all instances of it, then scroll down. You'll eventually see the buffer put into ecx right before a function call, which indicates a __fastcall function. If you double-click on that function and scroll way down to the bottom, you'll find the return is:

retn 8

So now we know that it's a __fastcall with two stack parameters, so a total of four parameters.

Press "Esc" to get back.

Looking at edx, we see that it gets its value from ebx, and involves a subtraction. If you follow ebx up the function, you'll see that esi is derived from it, so presumably ebx is or involves the player number. For now, we'll ignore that, and set it to 0.

The first stack parameter (that is, the last one pushed) is "eax". Remember that eax is the return variable. The function GetTickCount() is called just above the push, then 0x1B58 is added to the result. Right-click on the 0x1B58 to see the variable in different forms. In decimal, it's "7000". That's a much better number, so click on that.

GetTickCount() returns the number of milliseconds that Windows has been running. Adding 7000 milliseconds, or 7 seconds, creates the time it will be in 7 seconds. Messages in Starcraft stay on the screen for roughly 7 seconds, so presumably this parameter is the time for a message to stop displaying.

Finally, the last stack parameter is 0. That's nice and easy!

As for the return value, eax isn't used after the function call, so there may not be a return value. We won't worry about it.

So now we can define the function this way:

void __fastcall DisplayMessage(char *strMessage, int unknown, DWORD dwDisplayUntil, int unknown0);

It's not necessary here, but for education, do the following:

  • Double-click on the display function (sub_469380)
  • Scroll up, and click on the function's name
  • Press 'n', and call it 'DisplayMessage'
  • Press 'y', and define it as shown above (don't forget the semicolon).
  • Press 'Esc' to get back to where the function is called.

If you're using a modern version of IDA (support for __fastcall started fairly late), you'll see that the parameters to this function are commented now.

Of course, we have to test it works, now. We already have a .dll that can be loaded, so we'll add a function to it. Here's the function:

void __stdcall DisplayMessage(char *strMessage, int intDurationInSeconds)
{
	int intDisplayUntil = GetTickCount() + (intDurationInSeconds * 1000);
	int fcnDisplayMessage = 0x469380;

	__asm
	{
		push 0
		push intDisplayUntil
		mov  edx, 0
		mov  ecx, strMessage
		call fcnDisplayMessage
	}
}

Then to test, add a call to this function from DLL_PROCESS_ATTACH in DllMain(), and you're ready to test your first hack!

To test this:

  • Run Starcraft
  • Start a single player game (if you had the newest version, this would work in multiplayer too)
  • Alt-tab out
  • Run injector.exe
  • Tell it to inject in the "Starcraft" window, and give it the full path to the .dll file
  • Press "Inject"
  • Go back into the game
  • Hopefully your message will be waiting!

Click here for the full code.

Another option that I've started using more recently is to use a function pointer:

typedef void (__fastcall *fcnShowMessage) (const char* strMessage, int unk, int intDisplayUntil, int unk0);
static const fcnShowMessage ShowMessage = (fcnShowMessage)    0x00469380;

Click here for the full code using a function pointer.


Mineral Spending

We're going to use TSearch to track down the function called when a user spends minerals.

This uses a searc

Stop hand.png This page is under construction. USE AT YOUR OWN RISK!







Questions

Feel free to edit this section and post questions, I'll do my best to answer them. But you may need to contact me to let me know that a question exists.