Titan’s SC:BW Stats Hack Tutorial – From Start To Finish In MASM Win32 Assembly

Contents

I.                    Introduction

II.                 Tools Needed

III.               Making the Window

IV.              Creating Peek Function

V.                 Creating a Button and Use it to Set Timer

VI.              DoPaint: FindWindow

VII.            DoPaint: GetDC

VIII.         DoPaint: GetClientRect & InvalidateRect

IX.              DoPaint: Setting Our Colors

X.                 DoPaint: Peek the Minerals

XI.              DoPaint: Paint Player 1’s Minerals

XII.            Creating a Button and Killing Our Timer

XIII.         Source Code

XIV.         Conclusion

I.        Introduction

Hi, I go by Titan on the internet, and I’m writing this long tutorial as a step by step guide to creating a stats hack for Starcraft Broodwar v1.11b in pure 100% assembly.  I hope from this you can gain a new insight in writing programs in assembly, and you should be able to implement this to create other stats hacks for other games.  This tutorial shouldn’t require you know any assembly, but I STRONGLY recommend it.  I’ll try to explain each thing I do very well, but I may say some things that will confuse someone who has never worked with assembly.

People seem to get scared when they hear of assembly, but really it isn’t any harder than the ever-praised C++.  I’ll also try my best to not scare you away from assembly through this tutorial.

II.       Tools Needed

Required:

·        RadASM (http://radasm.visualassembler.com/)

·        A copy of Starcraft Broodwar v1.11b

Recommended:

·        Previous assembly knowledge

·        Memory searcher (TSearch, ArtMoney, ect…)

·        Win32 API Reference Manual (ftp://ftpc.borland.com/pub/delphi/techpubs/delphi2/win32.zip)

 

The only thing you really NEED in this would be RadASM(you could use MASM or a different assembler but since I’m doing this in RadASM, I think you should use it too.)  A copy of Brood War is needed if you actually want to test this.

I recommend previous assembly knowledge.  Like I said, I’ll try to explain myself as best as I can, but you may still get lost.  If you find yourself being confused, you should read up on some assembly tutorials first.

III.      Making the Window

Since this tutorial is from “Start to Finish”, I’ll go over every component that I used in making my hack.  You may have read Iczelion’s tutorials on creating a window and after that experience you probably never wanted to go back to learning assembly.  In fact, making a Window is very easy.  You don’t have to worry about all the details, especiall with RadASM.  In just a few clicks, you’ll have a window up and ready to go.  You’ll also have a nice interface that is almost as good as the VB programming interface.

So how do you make a window so easy in RadASM?  Here’s the steps:

1.      File > New Project

2.      Make sure “Assembler” is masm

3.      Project Type: Win32 App

4.      Project Name: “1.11b Stats Hack”

5.      Project Description: “Stats hack for Starcraft Broodwar 1.11b”

6.      Click Next

7.      Under templates choose “DialogAsMain.tpl

8.      Click Next

9.      Click Next

10.  Click Finish

 

And there you have it!  Go to “Make>Go” in the menu and you have yourself a working window.  You even have your own “.dlg” file where you can drag-and-drop components as easy as VB!  The “.mnu” file allows you to edit your menu… just as easy as VB.  Now that wasn’t too hard, now was it?  Assembly is a piece of cake.

 

IV.     Creating a Peek Function

Now we have our window.  So if we want to display our minerals, we will first have to read them, right?  We read values from another programs menu through the ReadProcessMemory() API.  Look it up in your Win32 API reference and you get:

________________________________________________________________________

 

BOOL ReadProcessMemory(

 

    HANDLE hProcess,       // handle of the process whose memory is read 

    LPCVOID lpBaseAddress,        // address to start reading

    LPVOID lpBuffer,          // address of buffer to place read data

    DWORD nSize, // number of bytes to read

    LPDWORD lpNumberOfBytesRead      // address of number of bytes read

   );

________________________________________________________________________

This may look daunting at first.  First off, this is more for C++, but it is easily converted into assembly.  The above would look like this in assembly: invoke ReadProcessMemory, hProcess, lpBaseAdress, lpBuffer, nSize, lpNumberOfBytesRead.  So here’s what we want to do – we want to make a “Peek” function to find our window, read our memory, and store it in a variable of our choice.

 

First step to making your function is to declare it in your INC file.  MASM provides an easy to use “invoke” statement that will automatically call your function and pop everything off the stack for you.  Therefore, you have to tell it what parameters your function wants so that invoke can do it’s job right.

 

So add this above your “.const”.

________________________________________________________________________

Peek                                        PROTO :DWORD, :DWORD, :BYTE

________________________________________________________________________

 

We are just telling the assembler that when we call our function, it’ll be something like: invoke Peek, (a DWORD value), (a DWORD value), (a BYTE value).

 

To create your function in assembly, go back to your .code and add the function:

________________________________________________________________________

 

Peek PROC aHack:DWORD, nVal:DWORD, iSize:BYTE

LOCAL phandle:DWORD

LOCAL pid:DWORD

LOCAL windhand:DWORD

Invoke FindWindow, NULL, addr ScWindow

mov windhand,eax

Invoke GetWindowThreadProcessId, windhand, addr pid

Invoke OpenProcess,PROCESS_ALL_ACCESS, 0, pid

mov phandle,eax

invoke ReadProcessMemory,phandle, aHack, nVal, iSize, NULL

ret

Peek ENDP

________________________________________________________________________

 

You can look those Win API’s up to see what I’m doing, it’s the basic process you use to peek.  This can be similarly done to poke.

 

And there you have your peek function!  To use it simply use “invoke Peek, Address To Peek From, Buffer to Store the Value, Length.”  Now that wasn’t any harder than C++, was it?

 

V.      Creating a Button and Use it to Set Timer

You can keep that peek function around, we’ll use it a little later.  Next we have to set up a timer that will paint our text every second.  So first, go to your “.dlg” file and create a button.  Look in the property window and you should see an ID.  You have to go into your .data and declare your button like this:

 

________________________________________________________________________

IDC_BTN1                              equ 1001

________________________________________________________________________

 

Replace 1001 with whatever RadASM declared your button (ID) as.

 

Now that you have the button, we need to set a timer when you click on it.  In WndProc you should see a code “.elseif eax==WM_COMMAND”.  This is where all commands are sent and processed.  To do a function when someone clicks on our button, we need to do this:

________________________________________________________________________

.elseif eax==IDC_BTN1         

________________________________________________________________________

 

To set our timer, we need to use the windows API called SetTimer.  Look it up in your Win32 API Reference and you should see:

________________________________________________________________________

UINT SetTimer(

 

    HWND hWnd,   // handle of window for timer messages

    UINT nIDEvent, // timer identifier

    UINT uElapse,   // time-out value

    TIMERPROC lpTimerFunc        // address of timer procedure

);

________________________________________________________________________

 

And here is how we will set our timer:

________________________________________________________________________

 

invoke SetTimer,hWnd,1,1,DoPaint

________________________________________________________________________

 

Am I going too fast?  That’s okay I’ll explain what I did right now.  hWnd stores our window handle(look up on your code at the .if eax==WM_INITDIALOG section).  Next, we need to identify our timer with a number, I chose 1.  Next, uElapse is the speed of our timer, I choose 1.  lpTimerFunct is what function we want our timer to do each second, I made a function called DoPaint(which we will create later).  I hope you understand what I’ve done so far.

 

VI.     DoPaint: FindWindow

 

We need to make our DoPaint function that will be done every second by our timer.  Declare it in your .inc file.

________________________________________________________________________

DoPaint                                                PROTO

________________________________________________________________________

 

Next make it in your .code section.

________________________________________________________________________

DoPaint Proc

 

 

DoPaint endp

________________________________________________________________________

 

Making a function is pretty easy in ASM, isn’t it?

 

The first thing we want to do in our function is to find Starcraft’s window handle.  We do this by using the FindWindow() API.  Look it up in your Win32 Reference Manual and you’ll find this:

________________________________________________________________________

HWND FindWindow(

 

    LPCTSTR lpClassName,           // pointer to class name

    LPCTSTR lpWindowName        // pointer to window name

 );

________________________________________________________________________

 

So to use FindWindow, we need SC’s class name, and the window name.  The window name is easy, “Brood War”.  Luckily for you, you don’t need the class name, I just use NULL.  So first declare your variables in the .const section.

________________________________________________________________________

.const

ScWindow                               db 'Brood War',0

 

.data?

ScWnd                                     dd ?

________________________________________________________________________

 

I also declare ScWnd as an unknown, that is where we are going to store the handle that FindWindow gives us.  So here’s how you do it:

 

________________________________________________________________________

                  invoke FindWindow,NULL,ADDR ScWindow

                  mov ScWnd,eax

________________________________________________________________________

 

Once you do FindWindow, the return value is in eax, so we move it into our variable ScWnd using the “mov” command.

 

VII.    DoPaint: GetDC

Next thing we need to do is get the device context.  We NEED this to be able to perform the painting on SC’s window.  Look up GetDC in your Win32 API Reference and you see this:

________________________________________________________________________

HDC GetDC(

 

    HWND hWnd    // handle of window 

 );

________________________________________________________________________

 

Well, we just got the starcraft’s handle, didn’t we?  So simply add this to your code:

 

________________________________________________________________________

invoke GetDC,ScWnd

________________________________________________________________________

 

That was easy enough.

 

VIII.    DoPaint: GetClientRect & InvalidateRect

I understand if the next things are confusing, you really have to understand how painting works on a window.  If you already know, then great!  I’ll try my best to make sense of these.  First, as always, look these up in your ever-so-handy Win32 API Reference.  You should find this:

________________________________________________________________________

BOOL GetClientRect(

 

    HWND hWnd,   // handle of window

    LPRECT lpRect             // address of structure for client coordinates

 );

________________________________________________________________________

 

________________________________________________________________________

BOOL InvalidateRect(

 

    HWND hWnd,   // handle of window with changed update region 

    CONST RECT *lpRect,            // address of rectangle coordinates

    BOOL bErase    // erase-background flag

 );

________________________________________________________________________

As you can see, GetClientRect gets the rectangle of a window and stores it in lpRect.  Basically it’s getting the coordinates of the window, and storing them in a RECT structure.  We’ll need to declare a LOCAL rect:RECT in the beginning of our function.  Then once we have those coordinates in our rect variable, we need to use InvalidateRect.  This makes SC's whole window an "update region" meaning that when we right to it, it'll refresh and not paint over our old text.

 

Now I hope you’re not very confused, but I bet you’re somewhat lost.  I’ll post our DoPaint function so far so you can check to make sure yours is correct.

 

________________________________________________________________________

DoPaint Proc

LOCAL rect:RECT ;Declare a local variable to store my RECT in.

 

                  invoke FindWindow,NULL,ADDR ScWindow

                  mov ScWnd,eax

                 

                  invoke GetDC,ScWnd

                  mov ScDC,eax

                  invoke GetClientRect,ScWnd,ADDR rect

                  invoke InvalidateRect,ScWnd,ADDR rect,TRUE

DoPaint endp

________________________________________________________________________

 

It’s not THAT bad.

 

IX.     DoPaint: Setting our Colors

Now that we have everything we need, it’s time to set up the colors we want.  I do this through a variety of Win32 API functions, you can look them up in your Win32 API Reference for further information.  This section is rather self-explanatory.

 

________________________________________________________________________

                  invoke SetBkMode,ScDC,TRANSPARENT

                  invoke SetBkColor,ScDC,0

                  invoke SetTextColor,ScDC,16777215

________________________________________________________________________

We set the background to transparent, the back color to black, and our text color to white.  Easy enough?

 

X.      Peek the Minerals

Before we can actually paint our data, we need to peek the minerals.  For now, I’m just going to show an example of peeking player 1’s minerals.  The same process will go for all the other players(same goes for gas).

So we finally get to use our peek function!

________________________________________________________________________

.const

P1Mins            equ 4FD4A0h

 

.data?

P1MinsData     dd ?

 

.code

invoke Peek, P1Mins, ADDR P1MinsData, 8

________________________________________________________________________

And there you go, it will peek the address at P1Mins and read the first 8 bytes, then store it in P1MinsData.

 

XI.     Paint Player 1’s Minerals

Now that we have player 1’s minerals, we have the daunting task of painting them.  This is complicated because if you just try TextOut, it will simply show the ASCII equivalent of the value, it won’t display it as an integer.  We have to use a function known as wsprintf.  This will convert our minerals stored in P1MinsData into an integer so that we can display it correctly.

 

Look up wsprintf:

________________________________________________________________________

int wsprintf(

 

    LPTSTR lpOut,  // pointer to buffer for output

    LPCTSTR lpFmt,           // pointer to format-control string

    ...           // optional arguments

);

________________________________________________________________________

This is one of the most complicated Win API functions you’ll use.  This is because it uses a C calling convention, in the fact that the “optional arguments” confuses the “invoke” function because it won’t know exactly how much to pop off, so be careful when using this function.  Also, the “format-control” string uses a very C-style.  Read up this API in your reference manual, and then refer to the source code below to help you understand.

________________________________________________________________________

.const

fil                      db "%i",0

 

.data?

buf                   db 12 dup(?)

 

.code

invoke wsprintf,  ADDR buf, ADDR fil, P1MinsData

________________________________________________________________________

That’s all you need.  Like I said, if you read the Win API reference you’ll see that the %i is the format control string which tells wsprintf to convert our string into an integer.  I highly recommend you learn this function.

XII.    Creating a Button and Killing our Timer

Now create a button like we did before, and under the commands make a new .if statement for your button.  To kill a timer we use the KillTimer() WinAPI.  So… go look it up in your API reference manual!

________________________________________________________________________

BOOL KillTimer(

 

    HWND hWnd,   // handle of window that installed timer

    UINT uIDEvent // timer identifier

);

________________________________________________________________________

 

Our window handle is of course stored in hWnd, and if you remember earlier I made our timer identifier 1.  So here’s the code to make your KillTimer button.

 

________________________________________________________________________

elseif eax==IDC_BTN7

invoke KillTimer,hWnd,1

________________________________________________________________________

 

And that’s all you really need to know!  I’ve shown you the barebones of painting another window.  Now you’ll have to figure out the rest!  Or you could just look at…

XIII.    Source Code

I’d much rather you not just skip to this section.  I only provide this because my tutorial only goes over the barebones, and source code is the second best thing to tutorials for learning.  I please request you use this for educational purposes only, and not to rip it off as your own code.  Thanks.

 

I’ve commented the code to make it more understandable, I hope this helps even more.  Also, this is a RadASM project file, so you kind of need RadASM to view it.  Although you could use any text editor to view the files separately, I strongly recommend RadASM.

 

http://www.angelfire.com/yt3/yt3-sc/Stat_Hack.zip

XIV.   Conclusion

And there you have it!  If you have any experience in C++, you should find that assembly isn’t very far off from it.  I hope if you’ve read and looked at my source you’ve learned something new.  I’m always looking forward to share the information I learn with everyone else. - Titan