Yonderknight's Unit Alert Tutorial!

Introduction:

I am writing this tutorial not only for the unit alert competition at bwhacks.com, but also to help people learn. I think this will help the people who started game-hacking and know how to do things like mineral hacks and changing numbers around, but can't really go any farther than that. This will help (hopefully) you guys getting started on some more complicated hacks.

I want this tutorial to cover all aspects of making this hack from debugging it to actually writing the hack. I hope to explain everything very clearly and explain what each line of code does.

I also want to give many thanks to

In this tutorial you will learn how to:

• Write a DLL to be injected
• Find functions in StarCraft
• Excecute a piece of code when SC runs a certain routine

More Specifically how to:

• Make a unit alert hack
• Find starcraft's texting function
• Find how SC keeps track of units
• Run a piece of code when a specific unit is made

Tools Needed:

• Simple memory searcher (TSearch, Artmoney, Winhack...)
• Debugger (SoftICE, I will use Ollydbg because it's free and easy to use)
• [Optional] if you use OllyDbg, you'll either have to use a program to restore system resolution (Powerstrip) or run Starcraft in a window. I use DxWnd to run SC in a window because it is much easier and faster than hacking to restore your resolution every time SC pops.
mASM32 (This tutorial will cover how to make a DLL in mASM32 only, although making it in other languages should be simiilar)
• [Highly Recommended] radASM. This is a tool that will help you write mASM32 programs and stuff. Also comes with a bunch of utilities and support for other ASM languages.

The beggining: THINKING about our hack

Well, the first thing I like to do when starting a hack is actuallly thinking about it. It might be a little more complicated than you think at first, and starting the wrong way can take a lot of your time away.

Starcraft keeps count of every kind of unit in different addresses. This means that it keeps count of how many SCV's player 1 has, player 2 has, player 3...how many Drones player 1 has....and so on. Lets say we build a unit, starcraft has to edit this value. If we can find the specific line of code that edit's these values, we can make a JMP to our own code (stored in our DLL) that then finds which unit was built and alerts us if it matches our specifications. We will have to find a lin eof code that is ran every time a building is made too, because in the contest specifications, we need to make a warning if a Nexus/Command Center/Hatchery is being created. We also need some way to find the number of units. Then we need to use Text_out to make an alert in the following format:
<playername> has (<unitNumber>) of <UnitType>

Finding WHERE Starcraft stores unit counts

The first thing we need to do is find where Starcraft stores each unit count. This part is pretty simple.
Start a challenger game with a computer. You should start with 4 drones/SCVs/probes. Open up your memory searcher (I used TSearch) and start a new search for the number 4 (four bytes long). The reason that it is 4 bytes long is because starcraft stores information like this in four bytes (00 00 00 04).
Once your search is done, ALT+TAB back into starcraft and make another unit, search within results/filter/seive/whatever your memory searcher calls it for the number 5 (four bytes long again), and keep doing this until you narrow your results down to about 8 offsets.

Finding HOW Starcraft edits unit counts

Starcraft may use a single line of code to edit more than one unit count. This is a good thing. We are going to have to make a JMP from wherever starcraft edits the units, and if the line we make a JMP from is used when ANY unit is built, then we only have to make ONE jmp. This will jmp to our own code which will then find which unit was built and alert us if necessary.

So basically we're looking for a line of code that is ran when
A) Buildings are built
B) Units are built
C) Enemy units and buildings are built

So lets start. In the previous section, you should have narrowed your search down to about 8 values or so. Select the first one and copy the address. Next, open up your debugger (I will show you how to use OllyDbg), and attatch it to the Starcraft process. This is done by going to File>Attatch, then choosing Brood War. After it is done loading, SC should be frozen, so press the PLAY button in olly (looks like the play on a VCR).

In the bottom left corner you should see a "Hex Dump." You can see SC's memory from here. Right click anywhere in here, and click on Goto>Expression. In the box type one of the addresses you found in your memory searcher, then press OK. If you don't see the Hex Dump change, do it again. The upper left corner of the Hex Dump should now contain the number of SCVs/Drones/Probes you had. Right click on this, and click Breakpoint>Memory, on write.

Now, any time starcraft WRITES to this address (i.e. increasing/decreasing the unit count) OllyDbg will freeze SC and pop up telling you what line of code edited this address. It is important that SC is running in a window or you have Powerstrip running because if SC freezes, you'll have no way of going back to OllyDbg.

ALT+Tab back to SC and try creating an SCV/Probe/Drone. OllyDbg should now pop up. In the main code section, a line of code should be highlighted. It is not important what this does at this moment, so just ignore it. Click on the play button.

Now we have to check if this line of code deals with more than one unit counter (and building counter). Remove your memory breakpoint by going back to the Hex-Dump and clicking Breakpoint>Remove memory breakpoint. Now, instead of setting a breakpoing on the SCV/Probe/Drone count, we will set a breakpoint on the line of code that dealt with them. Do this by selecting the line of code that Olly highlighted and press F2. This will make OllyDbg pop every time this LINE OF CODE is ran. Remember that the CODE could be used for more than one kind of unit.

Alt+Tab back into SC and try making ANOTHER type of unit. If OllyDbg pops, that's a good thing. If not, start the same process with the next address from your memory searcher (remember to remove all of your breakpoints). If Olly did pop, click on play and ALT+Tab back into SC. Next, look into your enemies territory (maphack or blacksheepwall) and check if Olly pops if THEY build a unit. Next, check if olly pops if you build a building, or if they build a building.

This is the address I found (1.11b offset): 0040E143

Keep repeating this for all of your addresses until you find one that matches ALL of the criteria (buildings, units, enemy buildings and units). Once you find one, we are ready to move on to the next section!

ANALYZING the line of code you found:

Now we are almost ready to make a JMP to our own DLL, but first we must find a way to determine what kind of building/unit was made, and WHO made the building/unit.

Start by setting a bp on whatever address you found (0040E143). Next, make ANY unit/building. SC should break. Now, look into the register window. This is located on the top-right section of Olly and is titled "Registers".
You should see this:

EAX 12345678
ECX: 1234578
EDX: 12345678
EBX: 12345678
ESP: 12345678
EBP: 12345678
ESI: 12345678
EDI: 12345678

These are where all of our information will be held (Unit Type, Person who built it, etc...) Now all we have to do is look for it. Most of these should be red (this signifies that they changed from the last BP) so build another unit of the same type to check which have changed, and which have stayed the same.

Try to find which values contain what in them. Find your player number (If you're in a challenger map, it is 0 if you are on top, and 1 if you are on the bottom) in one of the register's. Now watch when your enemy build something. Try to find what has changed from 0 to 1 or from 1 to 0. This is proably the player number. I found this to be stored in EAX.

Now, we need to find something that changes to which unit was being created. It would be helpful if you either kill your enemy or stop it from creating units so Olly doesn't pop every second.

Try creating different kinds of units and seeing which values change. I found that ESI changes to which unit was created (EBX does too, but we will use that for something else). Make of list of all the units we need and their according ESI numbers:
    48 = Carrier
    C = Battlecruiser
    67 = Lurker
    3D = Dark Templar
    53 = Reaver
    2C = Guardian
    1E = Siege Tank (Siege Mode)
    5 = Siege Tank (Tank mode)
    E = Nuke
    27 = Ultralisk
    9A = Nexus
    6A = Command Center
    83 = Hatchery
    84 = Lair
    85 = Hive
    82 = Infested Command Center
One more thing you should note is that Olly breaks if a unit is killed, too. ECX is 1 if a unit is built, and something else if it is killed.

Now we need to find the number of the unit being built. This isn't in the registers, so we have a couple of choices. Either we can make a list of all the unit counters, with tSearch, or you can do it the smart way (which I will teach you). Look at the line of code we set a breakpoint on.
    0040E143 ADD DWORD PTR DS:[EBP*4+503194],ECX
This basically says add ECX to the offset EBP*4+503194. Now think about this for a second. Remember the very first memory offset we set a memory BP on that landed here. This line of code HAS to modify that memory address, or else olly wouldn't have popped. This means that EBP*4+503194 MUST equal the memory offset that our SCV/Probe/Drone where in. Even try it. Make another SCV/Drone/Probe, and write down EBP. Then multiply this number by 4 and add 503194 to it (make sure your calculator is in HEX mode). When you get the final address, goto it in the Hex dump in Olly. The number in this address is the number of units that were created!

Lets get a summary on where our information is stored:

EAX holds the player number
ESI holds the type of unit created
[EBP*4+503194] +1 holds the amount of the unit created.
ECX Contains 1 when a unit is created

Finding Starcraft's Text Function

Now it's time to get away from all this number stuff and get to the text! This part is very important because this is the best way to display our information on screen. If you read Drakken's tutorial about writing to SC's screen, it might've sounded a bit complicated, but it is MUCH easier (and better) if you find the function that SC uses to display messeges.

The first thing we need to do is find WHERE our text is located. I would use ArtMoney for this because TSearch has problems with stuff like this...

Start a multiplayer game (LAN or Bnet)

Once the game has started, press <enter> My Message!<enter>. At this point, "<Name>: My Message!" should appear on the screen. Now alt-tab over to TSearch. TSearch provides a screen to view a hex dump of memory(called Hex Editor). Run this.

You will see that there are two more magnifying glasses on a toolbar for the Hex Editor. Click the one to start the search. This time type in "My Message!" exactly (case sensitive). Make sure that Value is Ascii, and Case is Match. Now click Find Next. You should see the hex dump change. If you scroll up in the Hex Editor window, you should see "Name>: My Message!" This is because Starcraft writes a null byte to the beggining of the message in order to stop it from appearing on the screen.(That part always seems to confuse people looking for their chats).

Because there are 12 or so places where SC displays text, if you type in something else, Olly won't break yet. Wait until all of the chats dissappear until you start checking for breaks, because we don't want to catch starcraft writing the 00 into the first character, we want to find it writing the actual text. Keep sending chats until Olly breaks. If olly doesn't break, then you picked the wrong address.
You should now be at this location (1.11b addresses):
	15030988 OR ESI,ECX
	1503098A AND EDI,81010101
	15030990 JNZ SHORT Storm.150309B8
	15030992 ADD EAX,4
	15030995 MOV DWORD PTR DS:[EAX+EBP-4],ECX << olly landed here 
	15030999 JNS Storm.15030A2E
	1503099F MOV ECX,DWORD PTR DS:[EDX]
	150309A1 SUB ESI,ECX
	150309A3 LEA EDI,DWORD PTR DS:[ECX+7EFEFEFF]
	150309A9 XOR EDI,ESI
	
It is not really that important what all this stuff means, but it is important to understand this is NOT the text function. If we were to draw a map of SC's text function, it would look soemthing like this:
	Original Text Function{
		Do some stuff
		Another Text Function{
			do some stuff
			Yet Another Text Function{
				find where to display text
				another Function{
					Mov DWORD PTR DS: [EAX+EBP-4],ECX << this is where we are 
				}
				more stuff
			}	
			do more stuff
		}	
		do some more stuff 
		maybe some sound stuff too 
	}	
	
Basically, we are deep inside the original text function, which is the one we want. We are in some code located god know's where (actually in Storm.dll). We need to go up a couple of "levels" to finally reach the original one. This one should not have any parameters besides the Text to be printed.

So how can we go UP a couple of levels? I mean, to go DOWN one, we can always step inside a function, but how are we supposed to find which function called our function? It is actually very simple! Ollydbg has something called "Run till Return". This will run Starcraft's code until it finds a Return. If we actually return, we will be a level higher than we were.

So now to start. Set your breakpoint again on the same address and make Olly break by typing in stuff. When it breaks, remove your memory breakpoint, but DO NOT press the Play button. This is because Olly will continue running SC's code. We only want it to run the code until we find a Return. So instead of pressing the Play button, press the button Execute Till Return. It is the 7th button right from the play button (it is blue). After you press this, the code Olly highlighted should now move to a code that says RETN.

Now we want to actually Return, so press the button called "Step Into." This basically runs one line of code, and if the next line of code is a function or call, it will Step Into the call. You should now be somewhere like this:
	00469AA6 PUSH EBX
	00469AA7 ADD EDX,starcraf.0064CDD8 ; ASCII "Henry: asdf"
	00469AAD PUSH EDX
	00469AAE CALL <JMP.&Storm.#501> <This is where we used to be located in!
	00469AB3 MOV AL,BYTE PTR DS:[ESI]
	00469AB5 XOR EDI,EDI
	00469AB7 TEST AL,AL
Well, we know that this IS NOT the main text function because this is located in Storm.dll. We need the text function located in Starcraft.exe. So Execute till return again, step into, and you should be somewhere like this:
	00469DDA ADD BL,2
	00469DDD CALL DWORD PTR DS:[<&KERNEL32.GetTickCou>; kernel32.GetTickCount
	00469DE3 ADD EAX,1B58
	00469DE8 PUSH EAX
	00469DE9 MOV DL,BL
	00469DEB LEA ECX,DWORD PTR SS:[ESP+14]
	00469DEF CALL starcraf.004699B0 << This is where we used to be 
	00469DF4 POP ESI
	00469DF5 POP EDI
	00469DF6 POP EBX
	00469DF7 ADD ESP,100
Now we know this IS NOT the main text function because you can see GetTickCount in there. GetTickCount is used for starcraft to set the time for how long the message to stay up. This means that we are still too far deep. Execute till return yet again, step into, and you should be here:
	00475B22 TEST EAX,EAX
	00475B24 JNZ SHORT starcraf.00475B3B
	00475B26 MOV ECX,EDI
	00475B28 CALL starcraf.00469C90 << This is where we used to be
	00475B2D POP EDI
	00475B2E MOV EAX,ESI
	00475B30 POP ESI
Hmm. We have no way of telling if this is the main text function or not, so press PLAY. Now put a breakpoint on this CALL, and type in something in SC. Olly should pop. Now look into the registers:

ECX is a pointer to the address which contains our text
EDI is also a pointer to an address which contains our text
EDX holds our player number. I found this out later, and was definitely confused, but it is our player number.

Try sending another message.

The only things that have changed are ECX and EDI, which are pointers to our text. This is good, because there are no other numbers like timers and things in the registers. This is probably our text function! Press PLAY and remove your breakpoint. Now Try NOPping (right click and click binary>fill with NOPs). This will basically erase the call. Also NOP out MOV ECX, EDI. Now try sending some text. Yay! Nothing happened. If this was NOT the text very first text function, some other things might have happened (Like the sound playing), but now we know for sure this is the right one.

Write this down in your notes (Which you SHOULD be keeping ;) )

TextFunction : 00469C90
ECX and EDI hold pointers to Text

And that's that!

Finding Player Names

We also need to find the names of each player. This will be fairly simple, because by now, you should be noticing that mostly everything in starcraft is stored in an array with a base address (Player 0's name in this case).

Well, this is pretty straightforward, start a game with a single player account whose name is not in any Starcraft folders, or in any user names for windows (don't pick "asdf" if your user name for windows is "asdf"). While in the game, open up TSearch/whatever memory editor you like, and search for your name in the Hex Dump. You should see your name surrounded by 00's (dots) and then the computer's name (which is "Computer"). Write down the first one you found, this would be player 0's name. I found this offset:

00640C60

Also record how many bytes until the next player's name, I found it to be 72 bytes long.

Finding the Current Player Number:

We need to find the address that stores which player you currently are in the game. This is because we don't want the Unit Alert alerting us if WE are making the unit, we only need it for other player's.

This too, is pretty simple. Start a challenger game with a computer. If you start off on the top, then your player ID is 0, if otherwise, your player ID is 1. Search for 0 or 1 in your memory searcher. Start a new game, search for your new player number. This could take a while, and there are a bunch of addresses that store the same value, so just pick any. I used this one:

004F1A6C

Final Chapter! Making our DLL

Well, in this section we're going to actually write the DLL in mASM32. This may seem a little complicated for those new to programming, so I'll try to explain as best I can.

When I first wrote the DLL, I used Drakken's DLL template, just because I'm new to DLL's, and his is set up in an organized way. If you don't have Drakken's DLL template, that's fine, because I'll show you what you need anyways.

First of all, make sure mASM32 is installed on your PC, and I would highly recommend using radASM (just because it color coats everything).

Now to get to the juicy part. Open up the file called "sample.asm". I'm really sorry I couldn't color code this, but it was taking way too much time in HTML.
	; Masm32 Dll Plugin Example for Damnation by Drakken
	
	.386
	.Model Flat, StdCall
	OPTION CASEMAP :NONE

	include \masm32\include\windows.inc
	include \masm32\include\masm32.inc
	include \masm32\include\user32.inc
	include \masm32\include\kernel32.inc
	include \masm32\include\debug.inc
	includelib \masm32\lib\masm32.lib
	includelib \masm32\lib\user32.lib
	includelib \masm32\lib\kernel32.lib
	includelib \masm32\lib\debug.lib
	
	include Sample.inc
	include Events.inc
	
	.Data
	
	JmpByte		db 0EBh
	
	.Data?
	
	ThreadID	dd ?
	hThread		dd ?
	
	.code
	
	DllEntryPoint proc hInstDLL:DWORD, reason:DWORD, unused:DWORD
	
		mov eax,reason
		.if eax == DLL_PROCESS_ATTACH	; Called when our dll loaded
			call DLLStartup		; Memory patches and jmp patches
			invoke CreateThread, NULL, 0, addr DLLProc, 0, 0, addr ThreadID	; Create a thread for hotkeys and events
			mov hThread, eax	; Store thread handle
		.endif
		ret
	
	DllEntryPoint endp
	
	DLLStartup proc
	
	; Here you can put the patches you want to use when the dll loads
	; Also you should put all your jump patches here
	
	; Examples:
	
		; WriteMem works similar to WriteProcessMemory.
		; WriteMem, offset, data pointer, number of bytes to write
		invoke WriteMem, 004528D7h, addr JmpByte, 1	; Start a game without an opponent
	
		; JmpPatch works like code injection only easier. :)
		; It dynamically patches a jmp to your function.
		; You must have it execute any code you've overwritten
		; and have it jump back or return to the appropriate memory location.
		; Use "jmp dword ptr [variablename]" if you need to have it jump back instead of ret
		; It will write 5 bytes at the offset you specify for the jump.
		; JmpPatch, from offset, to offset
		invoke JmpPatch, 004ADA68h, addr StartupMsg		; Game startup message
	
		ret
	
	DLLStartup endp
	
	End DllEntryPoint
Now you must be saying 'What is all this crap!," but its really not that complicated once you break it down.
	.386
	.Model Flat, StdCall
	OPTION CASEMAP :NONE

	include \masm32\include\windows.inc
	include \masm32\include\masm32.inc
	include \masm32\include\user32.inc
	include \masm32\include\kernel32.inc
	include \masm32\include\debug.inc
	includelib \masm32\lib\masm32.lib
	includelib \masm32\lib\user32.lib
	includelib \masm32\lib\kernel32.lib
	includelib \masm32\lib\debug.lib
	
	include Sample.inc
	include Events.inc
This is just all the basic header stuff, all those "Include"s just load all the libraries so we can use the functions that are stored in them.
As you can see, we also included Sample.inc and Events.inc, because this is where all of OUR functions are stored!
	.Data
	
	JmpByte		db 0EBh
	
	.Data?
	
	ThreadID	dd ?
	hThread		dd ?
This is all of our data, ThreadID and hTHread are in ."Data?" because we don't know what they are going to be (they change every time we load SC up)

Finally, our .code section!
	.code
	
	DllEntryPoint proc hInstDLL:DWORD, reason:DWORD, unused:DWORD
	
		mov eax,reason
		.if eax == DLL_PROCESS_ATTACH	; Called when our dll loaded
			call DLLStartup	; Memory patches and jmp patches
			invoke CreateThread, NULL, 0, addr DLLProc, 0, 0, addr ThreadID	; Create a thread for hotkeys and events
			mov hThread, eax ; Store thread handle
		.endif
		ret
	
	DllEntryPoint endp
Well, DLLEntryPoint is called by Damnation or any DLL loader when we inject it into starcraft. I'm not exactly sure about the first couple of lines, but apparantly eax = DLL_PROCESS_ATTACH when it is being attached to a program, like StarCraft.

The first thing we do is call DLLStartup, which we will discuss what it does in the next paragraph or so
Next, it creates a Thread (a process related thingy....) for our hotkey stuff to run in (I will also explain this later). Notice the

addr DLLProc

This is a function declared in Events.inc, so we will discuss what this does later. Right now, all you need to know is that the new process is running the DLLProc function.

the CreateThread API returns a handle (an ID) to the thread we created in eax, so we store this in hThread by using mov hThread, eax

Next is DLLStartup, which we previously called in DLLEntryPoint (using call DLLStartup)
	DLLStartup proc
	
	; Here you can put the patches you want to use when the dll loads
	; Also you should put all your jump patches here
	
	; Examples:
	
		; WriteMem works similar to WriteProcessMemory.
		; WriteMem, offset, data pointer, number of bytes to write
		invoke WriteMem, 004528D7h, addr JmpByte, 1		; Start a game without an opponent
	
		; JmpPatch works like code injection only easier. :)
		; It dynamically patches a jmp to your function.
		; You must have it execute any code you've overwritten
		; and have it jump back or return to the appropriate memory location.
		; Use "jmp dword ptr [variablename]" if you need to have it jump back instead of ret
		; It will write 5 bytes at the offset you specify for the jump.
		; JmpPatch, from offset, to offset
		invoke JmpPatch, 004ADA68h, addr StartupMsg	; Game startup message
	
		ret
	
	DLLStartup endp
As Drakken so nicely describes, this is basically ran first, so we can patch our functions in here. We're not realy going to do that right now since we haven't written any functions to patch! We are going to replace his patches with our own later, so you can delete them.

Well hopefully that wasn't so painful. There's not really much I can explain, but hopefully after this it won't be as hard.

Open up the file called Events.Inc:
ResourceHack PROTO :DWORD

.Data

.Data?

HndHook			dd ?

.Code

HotKeys proc nCode:DWORD, wParam:DWORD, lParam:DWORD; Hotkey handling function

	.if nCode != HC_ACTION	; Keystroke message
		jmp HotKeys_End
	.endif

	mov ebx, lParam
	or ebx, 00FFFFFFh
	.if ebx == 0C0FFFFFFh; WM_KEYUP
		jmp HotKeys_End
	.endif

	.if wParam == VK_F12	; Hotkey Examples:
		invoke ResourceHack, 00501370h
		invoke BWTextDisplay, CTEXT ("Minerals H4x0r3d!")

	.elseif wParam == VK_F11
		invoke ResourceHack, 005013A0h
		invoke BWTextDisplay, CTEXT ("Gas H4x0r3d!")

	.endif

HotKeys_End:
	invoke CallNextHookEx, HndHook, nCode, wParam, lParam	; Call next hook in chain
	ret

HotKeys endp

DLLProc proc

	invoke FindWindow, CTEXT ("SWarClass"), 0	; Get Starcraft window handle
	invoke GetWindowThreadProcessId, eax, 0	; Get Starcraft's Thread ID
	invoke SetWindowsHookEx, WH_KEYBOARD, addr HotKeys, NULL, eax	; Hook our hotkey function
	mov HndHook, eax	; Save the handle to our hook

Events:		; Events - Here we keep our thread in a loop.
	xor eax, eax	; Use this function for events checking. (ally alert..etc)
	jmp Events	; If we terminate this thread our hook is unset. :(

DLLProc endp

ResourceHack proc uses eax ebx ecx Resource:DWORD

	mov ebx, 004F5944h
	xor eax, eax
	mov al, byte ptr [ebx]
	mov ebx, Resource
	mov ecx, dword ptr [eax*4+ebx]
	add ecx, 00002710h
	mov dword ptr [eax*4+ebx], ecx
	ret

ResourceHack endp

StartupMsg proc
	
	pushad
	invoke BWTextDisplay, CTEXT ("Sample.dll by Drakken")	; Print text
	popad
	ret

StartupMsg endp	
Well, as you can see this has a lot of stuff that we are not going to use, so I'm going to go ahead and delete most of this:
	ResourceHack PROTO :DWORD
This just declares the function ResourceHack to be a function, we can delete this since we're not going to use it.

Next are the data sections which I don't need to explain again, we don't need to delete anything out of here. Next comes the HotKeys stuff. We're just going to leave a lot of this exactly the same, but change the hotkey stuff to call our functions.
	HotKeys proc nCode:DWORD, wParam:DWORD, lParam:DWORD	; Hotkey handling function

	.if nCode != HC_ACTION		; Keystroke message
		jmp HotKeys_End
	.endif

	mov ebx, lParam
	or ebx, 00FFFFFFh
	.if ebx == 0C0FFFFFFh	; WM_KEYUP
		jmp HotKeys_End
	.endif

	.if wParam == VK_F12	; Hotkey Examples:
		invoke ResourceHack, 00501370h
		invoke BWTextDisplay, CTEXT ("Minerals H4x0r3d!")

	.elseif wParam == VK_F11
		invoke ResourceHack, 005013A0h
		invoke BWTextDisplay, CTEXT ("Gas H4x0r3d!")

	.endif

HotKeys_End:
	invoke CallNextHookEx, HndHook, nCode, wParam, lParam		; Call next hook in chain
	ret

HotKeys endp
Well, first of all this checks to see if a key is being pressed, then it checks which key was pressed. We are going to update this part so we only need to press 1 key to toggle off and on our hack. Replace the ".if wParam" stuff with this (this is different from what Jakor used because I like to use .if's:
		.if wParam == VK_F9
			.if hackon == 0						; if the hack is off
				invoke TxtOut, addr OnText		; display "Unit Alert Hack On"
				mov hackon, 1					; put 1 into Hackon
			.else								; if the hack is already on
				invoke TxtOut, addr OffText		; display "Unit Alert Hack off"
				mov hackon, 0					; And put a 0 into hackon
			.endif
		.endif
When F9 is pressed, we display "Unit Alert Hack On" or "Unit Alert Hack Off" according to whether the hack was already on or not.
Hackon stores a 1 when the hack is on, and a 0 when the hack is off.
And put this in the .data section:
	.data
	hackon				db 0
	OnText				db 03h,"Unit Alert Hack",07h," On",0
	OffText				db 03h,"Unit Alert Hack",06h," Off",0
The reason why OnText and OffText have some numbers in front, is because those are the color numbers SC uses.
3 is yellow, 7 is green, and 6 is red. We need to finish all of our strings with a 0 at the end.

So now your Hotkeys proc should look like this:
		HotKeys proc nCode:DWORD, wParam:DWORD, lParam:DWORD ; Hotkey handling function
	
		.if nCode != HC_ACTION	; Keystroke message
			jmp HotKeys_End
		.endif
	
		mov ebx, lParam
		or ebx, 00FFFFFFh
		.if ebx == 0C0FFFFFFh	; WM_KEYUP
			jmp HotKeys_End
		.endif
	
		.if wParam == VK_F9
			.if hackon==0
				mov hackon, 1
				invoke TxtOut, addr OnText
			.else
				mov hackon, 0
				invoke TxtOut, addr OffText
			.endif
		.endif
	
		HotKeys_End:
		invoke CallNextHookEx, HndHook, nCode, wParam, lParam		; Call next hook in chain
		ret
	
		HotKeys endp
Notice that we are using a function called TxtOut. We will use this to send text to SC, but we haven't made the function yet. This tut is a little out of order, so we will cover the Txt function later.
The next thing is DLLProc. Maybe I should've done this first because it was the first thing being executed when we created our process (Remember the "CreateProcess..."stuff in sample.asm?)
	DLLProc proc

	invoke FindWindow, CTEXT ("SWarClass"), 0	; Get Starcraft window handle
	invoke GetWindowThreadProcessId, eax, 0		; Get Starcraft's Thread ID
	invoke SetWindowsHookEx, WH_KEYBOARD, addr HotKeys, NULL, eax		; Hook our hotkey function
	mov HndHook, eax	; Save the handle to our hook

	Events:	; Events - Here we keep our thread in a loop.
		xor eax, eax	; Use this function for events checking. (ally alert..etc)
		jmp Events	; If we terminate this thread our hook is unset. :(
	
	DLLProc endp
	
Well, this just finds the necessary information about starcraft to be able to set our windows hook. A windows hook basically monitors information windows deals with, like keystrokes, so we will use this for our hotkeying. We made a windows hook to the function we previously covered called "HotKeys".

Notice we get into a loope. This is because: We have nothing else to do with our process so we must close it, if we close it, our windows hook gets unset, if our windows hook gets unset, then no more hotkeys will work. The only problem with this is that the loop takes up some CPU ussage and could even cause SC to lag. We can fix this by not going into a loop at all. We can just add "Invoke Sleep,-1" to keep our program sleeping forever without closing!
	DLLProc proc
	invoke FindWindow, CTEXT ("SWarClass"), 0	; Get Starcraft window handle
	invoke GetWindowThreadProcessId, eax, 0	; Get Starcraft's Thread ID
	invoke SetWindowsHookEx, WH_KEYBOARD, addr HotKeys, NULL, eax	; Hook our hotkey function
	mov HndHook, eax	; Save the handle to our hook
	invoke Sleep,-1	; If we terminate this thread our hook is unset. :(

	DLLProc endp
	
The rest of the stuff is basically resource hack stuff and a startup message which we haven't gone into, so just delete all of it.

Not we're going to actually write the coding for our Unit Alert! We need to make a function that will get called every time a unit is made. We'll do this by making a patch in Sample.asm to the function we are about to create. But first, we need to make the function. Now recall where we are going to make our patch from:
	0040E143 ADD DWORD PTR DS:[EBP*4+503194],ECX
EAX holds the player number
ESI holds the type of unit created
[EBP*4+503194] +1 holds the amount of the unit created.
ECX Contains 1 when a unit is created

The register's hold all of the information we need, so we will have to be messing with them a lot in our function. If we mess with them in our function, and then return back to starcraft without changing them back the way they were, SC might not run properly, or even crash! In order to fix this, we will save all the registers before doing anything, and then restore them afterwords. Luckily, this isn't so hard. We will use "pushad" to save all the registerers, then "popad" to restore them when we're done. So go ahead and make a function called "Unit Creation", and put pushad/popad in it.
UnitCreation proc
	pushad
	
	popad
UnitCreation endp
Next, we are going to run all of our checks and things in this order

1. Check if our hack is turned on.
2. Check if a unit was created (not destroyed)
3. Check if the unit that was created is on our list
4. Check if we are the ones who made this unit

We can do this by putting ".if aregister==something". We will compare values to the register's that we recorded (eax, ecx, ebx...)
UnitCreation proc
	pushad
	.if hackon==1 ; check if our hack is on
		.if ecx==1 ; check if a unit was created, not destroyed
		
			.if esi==48h || esi== 0Ch || esi==67h || esi==3Dh || esi==53h 
				|| esi==2Ch || esi==5h || esi== 0Eh || esi==27h || esi==9Ah || esi==6Ah ||
				esi==83h || esi==84h || esi==85h ||
				 esi==82h ; check if the unit that was created is on our list
				 
				.if eax!= DWORD PTR DS:[004F1A6Ch]
; check if we created this unit. Remember that 4F1A6C was the address we
; found that contains the current player number?
				
				
				.endif
			.endif
		.endif
	.endif
	popad
	ret
UnitCreation endp

Notice that the really long .if comparing esi is on seperate lines. If you are copying and pasting this code, be sure to put all the lines back together.

Now, we need to record all the values we need into data sections. We will need to make space for them, so at the very beginning of your document, add these to the .data? section:
	.Data?

	Pname           	dd ?
	UnitNum         	dd ?
	UnitType			dd ?
	UnitText			db 80 dup (?)

We will use UnitText to store the very last string, which will be "<Pname> has (<UnitNum>) <UnitType>".
db 80 dup (?) basically means, put 80 ?? that are 1 byte each. A question mark could be the equivilant of a zero, so it would store our space like this:

00 00 00 00 00 00 00 .... 80 times

Each 00 is a letter is ASCII, and the reason why we only have 80 of them is because Starcraft cuts off chats longer than 80 bytes.
Pname will store a pointer to our player name, UnitNum will store the unit number, and UnitType will store a pointer to the Unit Type.

To find the player name, we will use the pointer we found earlier: 00640C60+72*Playernumber
	imul eax, 72d	;name's are 15bytes long, but 72 bytes apart
	add eax, 00640C60h	;Player0 name address
	mov Pname, eax

Remember that eax was the person creating the unit. This basically multiplies the player number by 72, then adds it to our base address, then moves it into Pname.

Next, we need to move the name of the unit created into UnitType. We will just make a big set of .if's and compare them to esi.
	.if esi==05h
		mov UnitType, CTEXT("Seige Tank")
	.elseif esi==0Ch
		mov UnitType, CTEXT("Battle Cruiser")
	.elseif esi==0Eh
		mov UnitType, CTEXT("Nuke")	
	.elseif esi==27h
		mov UnitType, CTEXT("Ultralisk")			
	.elseif esi==2Ch
		mov UnitType, CTEXT("Guardian")
	.elseif esi==3Dh
		mov UnitType, CTEXT("Dark Templar")
	.elseif esi==48h
		mov UnitType, CTEXT("Carrier")
	.elseif esi==53h
		mov UnitType, CTEXT("Reaver")
	.elseif esi==67h
		mov UnitType, CTEXT("Lurker")
	.elseif esi==6Ah
		mov UnitType, CTEXT("Command Center")			
	.elseif esi==82h
		mov UnitType, CTEXT("Infested Command Center")
	.elseif esi==83h
		mov UnitType, CTEXT("Hatchery")
	.elseif esi==84h
		mov UnitType, CTEXT("Lair")
	.elseif esi==85h
		mov UnitType, CTEXT("Hive")
	.elseif esi==9Ah
		mov UnitType, CTEXT("Nexus")
	.endif

CTEXT passes the address of the memory location that stores that certain string. This is easier than putting all the strings in the data section.

And finally, the last thing we need to do is get the number of units. We will do this by using our pointer [EBP*4+503194] +1. Although we don't need the +1 because I will explain later.
	imul ebp, 4	
	add ebp, 00503194hd!
	mov UnitNum, dword ptr [ebp]
Now we need to format our string to contain "<pname> has (<UnitNum>) <unitType>" We will use the wsprintf API to do this for us.
The wsprintf api is kind of confusing, so I think it would be easier to explain if I show it to you, and not tell it to you.
First you need to add this into your .data section:
	UnitFrmtString		db "%s has (%d) %s",0
This tells wsprintf that the first pointer we will be passing to it will print fo a string (%s, s for string). Then we tell it that the next one is a number (%d, d for decimal). Then we tell it that the last one is a string again.
And just to add in some color, I used this:
	UnitFrmtString		db 03,"%s ","has (",07,"%d",03,") ","%s",0
I used 03 for yellow, and 07 for green. Now we have to actually call wsprintf. We do it like this:
	invoke wsprintf,addr UnitText, addr UnitFrmtString,Pname,addr UnitNum,UnitType
Wsprintf only takes pointers, this means it only takes addresses to the actual values. That's why we use addr, this basically means, return the address of the certain value. The reason why we don't use addr for Pname and UnitType is because we used CTEXT. Remember I told you that CTEXT just returns addresses? This means that all Pname is storing is some numbers like 01234567. It is inside the address at these numbers where our text is located, so Pname and UnitType are already pointers, which is what wsprintf uses.

And finally, we need to display our Alert!
	invoke TxtOut,addr UnitText
So the final UnitCreation proc should look like this:
	UnitCreation proc
		pushad
		.if hackon==1 ; check if our hack is on
			.if ecx==1 ; check if a unit was created, not destroyed
			
				.if esi==48h || esi== 0Ch || esi==67h || esi==3Dh || esi==53h 
					|| esi==2Ch || esi==5h || esi== 0Eh || esi==27h || esi==9Ah || esi==6Ah ||
					esi==83h || esi==84h || esi==85h ||
					 esi==82h ; check if the unit that was created is on our list
					 
					.if eax!= DWORD PTR DS:[004F1A6Ch]
; check if we created this unit. Remember that 4F1A6C was the address we
; found that contains the current player number?
	
						imul eax, 72d	;name's are 15bytes long, but 72 bytes apart
						add eax, 00640C60h	;Player0 name address
						mov Pname, eax
					
						.if esi==05h
							mov UnitType, CTEXT("Seige Tank")
						.elseif esi==0Ch
							mov UnitType, CTEXT("Battle Cruiser")
						.elseif esi==0Eh
							mov UnitType, CTEXT("Nuke")	
						.elseif esi==27h
							mov UnitType, CTEXT("Ultralisk")			
						.elseif esi==2Ch
							mov UnitType, CTEXT("Guardian")
						.elseif esi==3Dh
							mov UnitType, CTEXT("Dark Templar")
						.elseif esi==48h
							mov UnitType, CTEXT("Carrier")
						.elseif esi==53h
							mov UnitType, CTEXT("Reaver")
						.elseif esi==67h
							mov UnitType, CTEXT("Lurker")
						.elseif esi==6Ah
							mov UnitType, CTEXT("Command Center")			
						.elseif esi==82h
							mov UnitType, CTEXT("Infested Command Center")
						.elseif esi==83h
							mov UnitType, CTEXT("Hatchery")
						.elseif esi==84h
							mov UnitType, CTEXT("Lair")
						.elseif esi==85h
							mov UnitType, CTEXT("Hive")
						.elseif esi==9Ah
							mov UnitType, CTEXT("Nexus")
						.endif
	
						imul ebp, 4	
						add ebp, 00503194hd!
						mov UnitNum, dword ptr [ebp]
	
						invoke wsprintf,addr UnitText, addr UnitFrmtString,Pname,addr UnitNum,UnitType
						invoke TxtOut,addr UnitText
	
						.endif
					.endif
				.endif
			.endif
			popad
			ret
	UnitCreation endp
Yay, we finally are done with events.inc. Lets look at the completed code:
	.data
	
	hackon              db 0
	
	OnText				db 03h,"Unit Alert Hack",07h," On",0
	OffText				db 03h,"Unit Alert Hack",06h," Off",0
	UnitFrmtString		db 03,"%s ","has (",07,"%d",03,") ","%s",0
	
	.Data?
	
	HndHook				dd ?
	Pname           	dd ?
	UnitNum         	dd ?
	UnitType			dd ?
	UnitText			db 80 dup (?)
	
	HotKeys proc nCode:DWORD, wParam:DWORD, lParam:DWORD	; Hotkey handling function
	
		.if nCode != HC_ACTION	; Keystroke message
			jmp HotKeys_End
		.endif
	
		mov ebx, lParam
		or ebx, 00FFFFFFh
		.if ebx == 0C0FFFFFFh	; WM_KEYUP
			jmp HotKeys_End
		.endif
	
		.if wParam == VK_F9
			.if hackon==0
				mov hackon, 1
				invoke TxtOut, addr OnText
			.else
				mov hackon, 0
				invoke TxtOut, addr OffText
			.endif
		.endif
	
	HotKeys_End:
		invoke CallNextHookEx, HndHook, nCode, wParam, lParam		; Call next hook in chain
		ret
	
	HotKeys endp
	
	DLLProc proc
		invoke FindWindow, CTEXT ("SWarClass"), 0 ; Get Starcraft window handle
		invoke GetWindowThreadProcessId, eax, 0	; Get Starcraft's Thread ID
		invoke SetWindowsHookEx, WH_KEYBOARD, addr HotKeys, NULL, eax		; Hook our hotkey function
		mov HndHook, eax  ; Save the handle to our hook
		invoke Sleep,-1	  ; If we terminate this thread our hook is unset. :(
	
	DLLProc endp
	
	UnitCreation proc
		pushad
		.if hackon==1 ; check if our hack is on
			.if ecx==1 ; check if a unit was created, not destroyed
			
				.if esi==48h || esi== 0Ch || esi==67h || esi==3Dh || esi==53h 
					|| esi==2Ch || esi==5h || esi== 0Eh || esi==27h || esi==9Ah || esi==6Ah ||
					esi==83h || esi==84h || esi==85h ||
					 esi==82h ; check if the unit that was created is on our list
					 
					.if eax!= DWORD PTR DS:[004F1A6Ch]
; check if we created this unit. Remember that 4F1A6C was the address we
; found that contains the current player number?
	
		imul eax, 72d	;name's are 15bytes long, but 72 bytes apart
		add eax, 00640C60h	;Player0 name address
		mov Pname, eax
	
		.if esi==05h
			mov UnitType, CTEXT("Seige Tank")
		.elseif esi==0Ch
			mov UnitType, CTEXT("Battle Cruiser")
		.elseif esi==0Eh
			mov UnitType, CTEXT("Nuke")	
		.elseif esi==27h
			mov UnitType, CTEXT("Ultralisk")			
		.elseif esi==2Ch
			mov UnitType, CTEXT("Guardian")
		.elseif esi==3Dh
			mov UnitType, CTEXT("Dark Templar")
		.elseif esi==48h
			mov UnitType, CTEXT("Carrier")
		.elseif esi==53h
			mov UnitType, CTEXT("Reaver")
		.elseif esi==67h
			mov UnitType, CTEXT("Lurker")
		.elseif esi==6Ah
			mov UnitType, CTEXT("Command Center")			
		.elseif esi==82h
			mov UnitType, CTEXT("Infested Command Center")
		.elseif esi==83h
			mov UnitType, CTEXT("Hatchery")
		.elseif esi==84h
			mov UnitType, CTEXT("Lair")
		.elseif esi==85h
			mov UnitType, CTEXT("Hive")
		.elseif esi==9Ah
			mov UnitType, CTEXT("Nexus")
		.endif
	
	imul ebp, 4	
	add ebp, 00503194hd!
	mov UnitNum, dword ptr [ebp]
	
	invoke wsprintf,addr UnitText, addr UnitFrmtString,Pname,addr UnitNum,UnitType
	invoke TxtOut,addr UnitText
	
					.endif
				.endif
			.endif
		.endif
		popad
		ret
	UnitCreation endp
Actually, we are not really done, but there are only a couple of minor things we need to do. But for now, lets get on to making our patch! Switch back to Sample.asm

DLLstartup should be empty and this is where we are going to make our patch. First of all, we need to find the code we are going to make our patch from:
0040E143  |. 010CAD 94315000     ADD DWORD PTR DS:[EBP*4+503194],ECX
Now see the stuff in between the address and the code? These are the OP codes. These are the codes that go directly into the processor.

The function we are going to use to set our patch (JmpPatch) writes 5 bytes to wherever we set it to. So split ADD DWORD PTR DS....into bytes, like this:
01 0C AD 94 31 50 00
Now because JmpPatch uses 5 of them, the resulting will be this:
01 0C AD 94 31 50 00
^  ^  ^  ^  ^
1  2  3  4  5  <all of these will be used
Now what about the 50 00? Well, these are just left sitting there, and they are invalid OP codes that the processor can't understand. So we must replace these with NOPs (or 90's).
First of all, we need to find the address where to write the NOP's to. Because our original address was 0040E143, we need to write the NOPs 5 bytes later, so we need to write them at 0040E148. Add the following to your .data section:
doubleNOP db 9090h
And add the following into the DLLStartup:
DLLStartup proc

	
	invoke JmpPatch, 0040E143h, addr UnitCreation
	invoke WriteMem, 0040E148h, addr doubleNOP,2
	
	ret

DLLStartup endp
And that's it for the Sample.asm! Sample.asm should look like this now:
; Masm32 Dll Plugin Example for Damnation by Drakken

.386
.Model Flat, StdCall
OPTION CASEMAP :NONE

include \masm32\include\windows.inc
include \masm32\include\masm32.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\debug.inc
includelib \masm32\lib\masm32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\debug.lib

include Unit.inc
include Events.inc

.Data
doubleNOP     		dw 9090h
.Data?

ThreadID			dd ?
hThread				dd ?

.code

DllEntryPoint proc hInstDLL:DWORD, reason:DWORD, unused:DWORD

	mov eax,reason
	.if eax == DLL_PROCESS_ATTACH	; Called when our dll loaded
		call DLLStartup		; Memory patches and jmp patches
		invoke CreateThread, NULL, 0, addr DLLProc, 0, 0, addr ThreadID	; Create a thread for hotkeys and events
		mov hThread, eax ; Store thread handle
	.endif
	ret

DllEntryPoint endp

DLLStartup proc
	
	invoke JmpPatch, 0040E143h, addr UnitCreation
	invoke WriteMem, 0040E148h, addr doubleNOP,2
	
	ret

DLLStartup endp

End DllEntryPoint
And because we overwrote the instruction "ADD DWORD PTR [blahblahblah..." we need to run it from within UnitCreation proc. Otherwise, SC won't ever run it and bad things will happen. Go back to UnitCreation proc in Events.inc and add this to the very beginning:
UnitCreation proc

	ADD DWORD PTR DS:[EBP*4+503194h],ECX	;we gotta do the overwritten code too!
	pushad
Also, we need to make a Jmp back to SC, so put this at the end of your code:
	popad
	jmp dword ptr [UnitJmpBack]
	ret
UnitCreation endp
Also, put this into your data section:
UnitJmpBack			dd 0040E14Ah
Finally, lets go to Sample.inc.
	.Data
	BWFXN_PrintText			dd 0046B7D0h
	
	.Data?
	lgJmp 					db 5 dup(?)
	
	.Code
	
	BWTextDisplay proc	uses ecx edx	text:DWORD
	
		xor edx, edx
		mov ecx, text
		call dword ptr [BWFXN_PrintText]
		ret
	
	BWTextDisplay endp
	
	WriteMem proc	MemOffset:DWORD, DataPtr:DWORD, dataLen:DWORD
	
		LOCAL OldProt:DWORD
	
		invoke VirtualProtect, MemOffset, dataLen, PAGE_EXECUTE_READWRITE, addr OldProt
		invoke RtlMoveMemory, MemOffset, DataPtr, dataLen
		invoke VirtualProtect, MemOffset, dataLen, OldProt, addr OldProt
		ret
	
	WriteMem endp
	
	JmpPatch proc	uses ecx ebx	from:DWORD, to:DWORD
	
		mov	ebx, to
		mov	ecx, from
		add	ecx, 05h
		sub	ebx, ecx
		lea	ecx, lgJmp
		mov	byte ptr [ecx], 0E9h
		mov	dword ptr [ecx+1], ebx
		invoke WriteMem, from, addr lgJmp, 5
		ret
	
	JmpPatch endp
Well, there's not really much here. You don't really need to understand JmpPatch and WriteMem right now, so just ignore it. Notice that drakken already has a BWTextDisplay. Only problem is that his is different from ours, and we named ours differently, so replace that, with this:
	TxtOut  proc	uses ecx edi ebx	text:DWORD
	
		mov ebx, 10h
		mov ecx, text
		mov edi, ecx
		call dword ptr [BWFXN_PrintText]
		ret
	
	TxtOut endp
This is all very very simple. Remember the values we recorded for this function? Edi and ecx store the text, and ebx stores the player number. Because we don't want "c00lDood: blahblahblah has (10) blah" to appear, I put 10 into player number because there is no player 10, and so there won't be anything coming up at the beginning. Jakor told me today that if you put -1 into there, then it will come up in the white space in the middle, but I haven't tried that yet.

And that's pretty much it! You can compile it by making a .bat file in the same directory and putting this into it:
	if exist Sample.obj del Sample.obj
	if exist Sample.dll del Sample.dll
	\masm32\bin\ml /c /coff Sample.asm
	\masm32\bin\Link /SUBSYSTEM:WINDOWS /DLL Sample.obj
	
	Pause
Then run the .bat file. If all goes well, you should see your DLL appear! Now just load it into damnation or inhale and congratulations, you're done!

Shoutz!

Special thanks to Jakor who was my partner during this and fixed all of my syntax errors and many other things =p
I also want to thank TheTempest for always helping me and everybody else!

Thanks to Drakken for making the DLL template and for many many other things,
and everybody who ever helped and everyone bwhacks: FishBeans, Nickolay, Overflow, Titan, Dt, Indulgence....and other that I forgot!