Method One: The EIP Skip
I'll let L. Spiro go into a little more depth on how the EIP register works, but it basically contains a pointer to the current line of ASM to be executed. If you change EIP, you change what the process is doing. If you want to skip an instruction, set EIP to the address of the instruction AFTER the one you want to skip.
- Code: Select all
01002FF5 | FF05 9C570001 | INC DWORD PTR [100579C] | ;increase the value of the timer
- Address = 01002FF5
- On execute, hardware breakpoint
- Callback Function: Script Function, Parm 1 (means we'll have to write a On_BP_1 script function)
- Code: Select all
//global variable used in the first breakpoint handler
bool winmine_skip_increment = false;
//this shows how one can use a breakpoint to skip over lines of
//ASM instructions to change the game's behavior
//this does not change any values in RAM and is, therefore, unable to
//be detected by a simple CRC check (which many games use for anti-hacking)
void On_BP_1(LPVOID lpvAddress, LPPROC_INFO_MHS lpProcInfo)
{
//on each breakpoint, alternate winmine_skip_increment between true and false
//that way, the incrementer is only skipped every other time, thus decreasing
//the timer by two
winmine_skip_increment = !winmine_skip_increment;
//if winmine_skip_increment is true (which only happens every other time)
if (winmine_skip_increment)
{
//tell the user that we're skipping the timer incrementer
PrintF("Timer increment skipped!");
//move the next operation to be executed to one line BEYOND the incrementer
//remember, the incrementer is held at 0x01002FF5... 0x01002FFB is the next line
lpProcInfo->pcContext->Eip = 0x1002FFB;
//remember to always set bSetContext to true if you make any changes
//to lpProcInfo->pcContext... if bSetContext isn't set to true, your changes
//will not take
lpProcInfo->bSetContext = true;
}
}
Voila! Now you'll have twice as much time to get the same score in Minesweeper. Sure, that's not useful, but I'm sure you can think of other ways to use this method. Don't like how a program does something every time you take an action? Change EIP to skip over the part you don't like. It doesn't change the checksum of the process' RAM so it's less detectable than simply NOPing or JMPing to another instruction and, as you can see, it is quite powerful.
Method Two: The External Variable
Another way to cut the timer in half is to set a breakpoint after the timer has been incremented and, when the breakpoint is hit, write our own timer value to memory. L. Spiro Script external variables make this quite easy. First, the breakpoint, which we set at the instruction directly after the timer increment:
- Code: Select all
01002FF5 | FF05 9C570001 | INC DWORD PTR [100579C] | ;timer increment
01002FFB | E8 B5F8FFFF | CALL 010028B5 | ;BREAKPOINT HERE (after timer has been incremented)
- Address = 01002FFB
- On execute, hardware
- Callback Function: Script Function, Parm 2 (we'll write function On_BP_2)
- Code: Select all
//global variable used in the second breakpoint handler
int winmine_realtimer = 0;
//this shows how one can use a breakpoint to change the value of an
//address in memory BEFORE it changes to what it's supposed to be.
//this wouldn't get picked up by a simple CRC anti-hack routine either, but
//is still just slightly riskier because whatever function(s) L. Spiro uses to
//write to memory when using an external variable might get picked up
//(i have no idea whether they would or not)
void On_BP_2(LPVOID lpvAddress, LPPROC_INFO_MHS lpProcInfo)
{
//define winmine_timer as the address that holds the value of the timer
extern int winmine_timer = {"winmine.exe", 0x579C};
//show the user the timer increments
PrintF("Timer: %d", winmine_timer);
//increment our variable that holds what the REAL time is (incremented
//once each time this BP is hit, just like the timer WOULD be if we
//weren't changing the value)
winmine_realtimer++;
//change the value of winmine's timer
//dividing by 2 cuts time in half; dividing by 3 makes it 3x slower, etc.
winmine_timer = winmine_realtimer / 2;
}
We have a global integer called winmine_realtimer that will keep track of the number of times the breakpoint is hit and, as a result, the number of seconds that have ACTUALLY passed. Inside the function, we define an external variable named winmine_timer as "winmine.exe" + 0x579C (which turns out to be 100579C, the address at which winmine's timer value is held). We incremement the realtimer with each bp hit, then set winmine_timer to realtimer / 2, or half of realtimer. This effectively writes to the address holding the game's timer HALF of the time that has actually passed. If realtimer is 4, we set winmine_timer to 2; if realtimer is 18, we set winmine_timer to 9.
This method is useful for changing the value of a game variable at exactly the right moment using breakpoints and external variables / memory writes. Find the operation that tells the game how much damage you just took and change the damage to whatever you want. Breakpoint on the instruction that limits how much time you have left to finish a quest and give yourself more time. Of course, those two examples are only useful in single-player games, but I'm sure you can put this method to use in your favorite multi-player game with just a little imagination
Method Three: The Codecave
This one will not use any breakpoints at all. This one is my favorite way of doing it because it's cut and dried and you can set it and forget it. It doesn't chew up one of your four available hardware breakpoints (you only get four of those, by the way, if you didn't know) and it will never make your game take a performance hit (not that breakpoints usually do, either, but you never know).
The only reason I don't like this one is it's easily detectable by a CRC/checksum check. This does alter the game code. However, in many games there either a) won't be a checksum check or b) will be one but it can be disabled or bypassed easily. So, really, whichever of these three methods--or any others you can think of, for that matter--speaks to you, use that one. This one speaks to me.
Now then, we know that the instruction at 01002FF5 increments the timer. I chose that address to overwrite with a JMP to the codecave that I wrote, which looks like this:
- Code: Select all
01004A60 | 8B35 985B0001 | MOV ESI, DWORD PTR [1005B98] |
01004A66 | 85F6 | TEST ESI, ESI |
01004A68 | 75 12 | JNZ SHORT 01004A7C |
01004A6A | FF05 9C570001 | INC DWORD PTR [100579C] |
01004A70 | C605 985B0001 01 | MOV BYTE PTR [1005B98], 1 |
01004A77 | E9 7FE5FFFF | JMP 01002FFB |
01004A7C | C605 985B0001 00 | MOV BYTE PTR [1005B98], 0 |
01004A83 | E9 73E5FFFF | JMP 01002FFB |
So, we move should_skip into ESI and then test esi to see if it's zero. If it's not zero--if (should_skip == true)--then we skip down to address 01004A7C, write 0 (false) to should_skip, and jump back to the main game routine; the timer isn't incremented.
If, however, we test ESI and it IS zero--if (should_skip == false)--then we don't skip the incrementer, write 1 to should_skip (true), and then jump back to the main game routine.
This means that the timer will only be incremented every other time. If you're having trouble visualizing this in action, attach MHS to winmine, inject the code, then add a breakpoint to line 01002FF5 (it'll say JMP 01004A60). Put Single Step as the Callback Function for the breakpoint. Then, start up the timer by clicking on a square in Minesweeper. It'll break on the code and allow you to step through it, showing you the route through the codecave it takes (press F8 to move to the next instruction). Press F9 when it's jumped back to the main game and it will break again in about one second, allowing you to step through the OTHER possible route through the codecave.
The code injection is pretty straight forward and already covered in the other tutorial I wrote, which has a link at the bottom of this post. Here's the code for the injection:
- Code: Select all
//byte array to hold the overwritten operation for injection
BYTE winmine_overwritten[6];
//handle to the process used for restoring the overwritten operation
//after winmine.exe has been closed
HANDLE winmine_pHandle;
//this shows how one can use code injection to achieve the same thing as the
//above two examples.
//however, since this uses code injection, it DOES alter the CRC/MD5/checksum
//of the RAM, so it's easily detectable
void On_Open_WINMINE_EXE(HANDLE hProcess, DWORD dwProcessId)
{
//ask the user if they want to inject or not
//if trying either of hte breakpoint examples, i suggest not injecting
if (MessageBox(MBS_YES | MBS_NO, "Inject Code?", "Would you like to inject code to make the timer half as fast?") == MBS_NO)
return;
//store a handle to winmine.exe in winmine_pHandle (used for cleaning up in On_Close)
winmine_pHandle = hProcess;
//read the original opcodes into winmine_overwritten
//these will be re-written to winmine if the user starts hacking another
//process with MHS -- it's always important to clean up after yourself, even if
//just to keep in good habit
ReadProcessMemory(hProcess, (void *)0x01002FF5, &winmine_overwritten, sizeof(winmine_overwritten), NULL);
/* OUR CODE CAVE
01004A60 | 8B35 985B0001 | MOV ESI, DWORD PTR [1005B98] | ;move our boolean variable into ESI
01004A66 | 85F6 | TEST ESI, ESI | ;test esi to see if it's zero
01004A68 | 75 12 | JNZ SHORT 01004A7C | ;if it's not zero, jump ahead
01004A6A | FF05 9C570001 | INC DWORD PTR [100579C] | ;if it's zero, increase the timer
01004A70 | C605 985B0001 01 | MOV BYTE PTR [1005B98], 1 | ;write 1 to our variable
01004A77 | E9 7FE5FFFF | JMP 01002FFB | ;jump back to main game routine
01004A7C | C605 985B0001 00 | MOV BYTE PTR [1005B98], 0 | ;if it's not zero, write zero to it
01004A83 | E9 73E5FFFF | JMP 01002FFB | ;jump back to main game routine*/
BYTE winmine_codecave[40] = {0x8B, 0x35, 0x98, 0x5B, 0x00, 0x01, 0x85, 0xF6, 0x75, 0x12,
0xFF, 0x05, 0x9C, 0x57, 0x00, 0x01, 0xC6, 0x05, 0x98, 0x5B,
0x00, 0x01, 0x01, 0xE9, 0x7F, 0xE5, 0xFF, 0xFF, 0xC6, 0x05,
0x98, 0x5B, 0x00, 0x01, 0x00, 0xE9, 0x73, 0xE5, 0xFF, 0xFF};
BYTE winmine_jmp[6] = {0xE9, 0x66, 0x1A, 0x00, 0x00, 0x90};
//write codecave to memory first so that it will already be there when the
//game code is overwritten to jump to the codecave
WriteProcessMemory(hProcess, (void *)0x01004A60, winmine_codecave, sizeof(winmine_codecave), NULL);
//write the jmp over the timer incrementer
WriteProcessMemory(hProcess, (void *)0x01002FF5, winmine_jmp, sizeof(winmine_jmp), NULL);
}
//clean up just in case MHS gets detached from winmine.exe
void On_Close_WINMINE_EXE()
{
//make sure winmine_pHandle isn't null
if ((DWORD)winmine_pHandle != 0)
{
//write the overwritten opcodes back to the main game routine
WriteProcessMemory(winmine_pHandle, (void *)0x01002FF5, winmine_overwritten, sizeof(winmine_overwritten), NULL);
//close the handle to the process
CloseHandle(winmine_pHandle);
}
}
Whenever MHS starts hacking winmine.exe, it will pop up a message box asking you if you'd like to inject the code. Click No to use one of the other methods or Yes to inject. Once it's injected, Minesweeper's timer will be half as fast even if MHS crashes, because it's all handled in the codecave which we injected.
You can find the fully documented, syntax highlighted version of my script HERE.
Here are links to my other two MHS tutorials, as well:
InfernoRose Packet - Using Code Injection and Multithreading
InfernoRose Packet - Using Script Breakpoint Handling
-Shynd