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
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.
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.
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.
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?
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.
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.
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.
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.
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?
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.
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.
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…
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
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