L. Spiro
December 5th, 2006, 03:22
This was originally a tutorial posted on another site to demonstrate a new way of making bots.
Although this is in tutorial format, this doubles as a mini project—the script you get after following this (or just skipping to the code and copying/pasting) will give you a bot that will legally play Minesweeper for you.
Without modifying Minesweeper at all, the bot will move your mouse and correctly click on all valid squares in the Minesweeper field, as fast or as slow as you would like it to click.
If you opt to skip to the code without reading the setup, be sure to at least read the part explaining how to get the code to compile.
Demo #6 can be found here: http://www.memoryhacking.com/MemHack/MHSDemo6.rar
The unmodified tutorial follows.
This tutorial demonstrates the botting capabilities readily available in MHS Demo 6 and greater. Minesweeper is a simple game with a point-click interface, which as luck would have it is the basis of every bot for MMORPG and RTS game out there.
This tutorial is advanced, and while it is simple to do, it takes a coder to actually understand. If you are not adept in coding in C or C++, read on at your own risk.
Other tutorials show how to hack Minesweeper and win with a very low time score.
MHS is unique in that it has a full programming language built into it, which offers us a few alternative solutions that I will explore here because they are useful alternatives that can applied to many other types of situations, especially in the realm of botting.
This tutorial explains how to easily make a simple bot that literally plays Minesweeper for you, correctly clicking on the squares where there are no bombs.
This works without modifying the game RAM (freezing timers, for example, modifies game RAM and is highly detectable) and is the most “legal” way to cheat the game.
It is also impressive and fun to watch your cursor quickly move along the tiles and click the correct squares.
TERMINOLOGY
Botting: The use of an application to play your game for you, typically by simply sending mouse clicks that result in your in-game persona performing tedious or complicated actions undesirable or unattainable by humans.
TOOLS REQUIRED
MHS Demo 6 or greater: No other application can be used as a substitute; MHS is the only program currently available with L. Spiro Script, which is used in this example and makes things very simple.
Minesweeper: We hack this. It comes with Windows®, typically in Start/Programs/Games/Minesweeper.
Let’s Get Started!
Open MHS and Minesweeper.
Select Minesweeper in MHS by going to File/Open Process (Ctrl-P) and selecting “winmine.exe”. The list can be sorted by clicking the column header.
Finding What We Need
Our goal is to make a bot that can click the tiles and avoid the bombs.
One thing we know we will need is the width and height of the board.
It’s time to perform a search.
Go to Search/Data-Type Search. It is assumed by this level you know the idea being searching, so we get to the meat of the process.
We guess the data is stored as a Long.
Go to Minesweeper and hit Game/Custom.... Set the width to 11 and hit OK.
Now we know the width of the board, so go back to MHS, select Exact Value in the Evaluation Type, and then enter 11 in the Value to Find field. Check Aligned.
The standard search gives you around 375 results.
Go back to Minesweeper, Game/Custom....
Set the width to 13 and hit OK.
Back to MHS and go to Search/Sub Search.
Select Exact Value and enter 13, then search again.
We have 2 results now. One on <font color="Red">0x010056AC</font> and one on <font color="DarkGreen">0x01005334</font>.
Add them both to the main window by double-clicking them both (or selecting them both and hitting the Add button).
When they are in the main list, select them both and right-click one of them, then select View in Hex Editor.
The Hex Editor is real-time; anything that changes gets highlighted brighter blue.
When we examine both addresses it becomes obvious that <font color="DarkGreen">0x01005334</font> is the one we need, because the next address (<font color="Blue">0x01005338</font>
is the height of our board.
Now we have the width and height of our board already!
Get The Board Tiles
View the <font color="DarkGreen">0x01005334</font> address in the Hex Editor again.
The board height was right there, and the chances are good that the board array (the tiles on the playing field) are there too.
As mentioned, the Hex Editor will highlight bytes that change, so use this to our advantage.
Go to Minesweeper, Game/New (or press the smiley face).
This makes a new game with a new board array. If anything changed in our Hex Editor, it will be related to this change in our game.
Looking back at the Hex Editor shows that indeed several bytes have changed just below <font color="darkgreen">0x01005334</font>.
Guess what. We just found the board array already.
Further investigation reveals that it starts on <font color="Indigo">0x01005361</font>.
Decode The Mine Array
This is quite simple and doesn’t even need its own section.
After playing with things a bit, you will find that mines have the highest bit set (they appear as values 0x80 and above in the Hex Editor).
In other words, if ([Square] & 0x80) returns non-zero, there is a mine there, and we don’t want to click it.
Make The Bot Already!
It’s time to make use of the 3 things we’ve gathered.
We know the width and height of the board, and we know how to tell which squares should not be clicked.
Time to write some code.
MHS comes with its own language, compiler, and interpreter, so you need no extra tools. Just go to Tools/Script Editor.
Place the floating windows where you like.
To begin a new script, press Ctrl-N. A new empty file is created.
But this file isn’t related to MHS yet; it has no idea you want this file to be compiled and use by it. We need to add it to MHS’s script set by pressing Ctrl-D.
It will prompt you to save it first, so think of a fitting name and save it. My file is Minesweeper.lss.
Now the file is recognized by MHS as a file that you want it to compile and use in its scripting, but the file is empty. Let’s solve that problem now.
It will be easier to show the code first and then go over it.
Paste this code into the file, then hit Ctrl-S to save it and F5 to compile it (very important).
VOID SolveMinesweeperBoard() {
HWND hWnd = FindWindow( "Minesweeper" 
;
if ( !hWnd 
{ return; }
// Put the Minesweeper board in front if possible.
ShowWindowAsync( hWnd, SW_SHOW 
;
SetForegroundWindow( hWnd 
;
// Get its position.
INT iX;
INT iY;
GetWindowPos( hWnd, &iX, &iY 
;
PrintF( "Minesweeper board located at %d %d.", iX, iY 
;
if ( iX < 0 || iY < 0 
{
PrintF( "Minesweeper board is out of range!" 
;
return;
}
extern struct BOARDSIZE {
INT iWidth;
INT iHeight;
} e_bwSize = { "winmine.exe", 0x5334 }; // The width and height of the board.
extern BYTE e_pbBoard[32*32] = { "winmine.exe", 0x5361 }; // The board array itself.
// Note that 32*32 here is meaningless, except to show us how
// large the array is in the target process (it does not actually
// declare the array in the target process; it already exists).
POINT pClick;
// To calculate the final click positions we need the screen
// resolution.
INT iScreenW = GetPrimScreenWidth();
INT iScreenH = GetPrimScreenHeight();
// System settings change the location of mines on the board, so
// get the settings needed to know where to click.
INT iBorderHeight = GetSystemMetrics( SM_CYEDGE 
; // Window border (height).
INT iCaptionHeight = GetSystemMetrics( SM_CYCAPTION 
; // Title bar height.
INT iMenuHeight = GetSystemMetrics( SM_CYMENU 
; // Menu height.
INT iFinalYOffset = iBorderHeight + iCaptionHeight + iMenuHeight;
INT iFinalXOffset = GetSystemMetrics( SM_CXEDGE 
; // Window border (width).
// Save the externs to locals. This makes it faster to scan the
// board.
INT iBoardWidth = e_bwSize.iWidth;
INT iBoardHeight = e_bwSize.iHeight;
// Run through the board and click on each square that has no mine.
for ( INT J = 0; J < iBoardHeight; J++ 
{
for ( INT I = 0; I < iBoardWidth; I++ 
{
if ( (e_pbBoard[(32*J)+I] & 0x80) == 0 
{
GetClickPos( iFinalXOffset, iFinalYOffset, iX, iY, &pClick, I, J 
;
// Translate the click position into the actual screen coordinates (Windows® forces us to do this).
pClick.x = pClick.x * 65535.0f / iScreenW;
pClick.y = pClick.y * 65535.0f / iScreenH;
// Move there.
MouseEvent( MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,
pClick.x, pClick.y,
0 
;
// Click there (down and up).
MouseEvent( MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTDOWN,
0, 0, // X and Y positions aren’t used.
0 
;
MouseEvent( MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTUP,
0, 0,
0 
;
// Optionally sleep for a bit in case you like to see the action unfold.
//Sleep( 10 
;
}
}
}
}
// Constants that tell us the dimensions of the board.
const INT iSquareSize = 16;
const INT iXOff = 14;
const INT iYOff = 56;
const INT iFinalX = iXOff + (iSquareSize / 2);
const INT iFinalY = iYOff + (iSquareSize / 2);
VOID GetClickPos( INT iScreenOffX, INT iScreenOffY, INT iPosX, INT iPosY, POINT * ppRet, INT iX, INT iY 
{
// The window is at (iPosX, iPosY). From there, go to the corner of the board
// and then offset based on which square we are hitting.
ppRet->x = iPosX + iX * iSquareSize + iFinalX + iScreenOffX;
ppRet->y = iPosY + iY * iSquareSize + iFinalY + iScreenOffY;
}
Is That Just C Code?
Sure looks like it, doesn’t it?
L. Spiro Script isn’t actually a script at all. It’s a full language on par with C.
You can use pointers and all C operators, including indirection (*) and address-of (&
.
But at the top you may have noticed one major difference: <font color="Blue">extern</font>.
But first thing first.
We started by using the standard Windows® function FindWindow() to get a handle to the Minesweeper window.
From there, GetWindowPos() gets the actual position of the window. This is important if we want to click on the window to play the game.
We then declare those <font color="Blue">extern</font> variables.
These are variables that are actually in Minesweeper itself, not in our script, or in MHS. We can read and write to these variables the same way as we can with any normal variable, but the values when reading come from the RAM inside Minesweeper, and changing these variables will change the RAM inside Minesweeper.
We found the board width and height on “winmine.exe”+0x5334, and we declare a BOARDSIZE variable there.
The board array is on “winmine.exe”+0x5361, and we declare a byte array there.
From here out, reading the information from the game is a very simple task.
Next we get the screen width and height. This is because we need it for translations required by Windows® when using MouseEvent() (same function as mouse_event() in the Windows® API).
Then we start the loop through the game board, and you can see how e_bsSize.x and e_bsSize.y are used to get the board width and height directly from winmine.exe itself.
There is no more need for the hassle involved with ReadProcessMemory() and WriteProcessMemory().
Then we use e_pbBoard to check the square on the Minesweeper board.
If the highest bit is set ([byte] & 0x80), we do not want to click there.
If we do want to click there, we start by getting the actual position where we want to click. We have written our own function to do this called GetClickPos() which is defined at the bottom. This takes the X and Y of the Minesweeper window and the square we want to click, then returns the X and Y (in pixels on the screen) where we should click. Unfortunately we have to perform a simple translation on it as required by Windows®, but then we are on our way.
We use MouseEvent() to put the mouse in position and then click.
We can optionally call Sleep() for a little bit to slow things down and make it fun to watch. The cursor will glide across the board and gracefully click the correct squares, solving any board in no time at all.
That Was Advanced Yet Simple
And it has many other implications besides just quick Minesweeper boards.
You can use <font color="Blue">extern</font> to easily go inside any game and get/set any data you need to make any kind of interactive bot that you want.
But How Do I Make This Script Run?
Attach it to a hotkey!
First, write this function and place it at the bottom of the file:
VOID On_HK_0() {
SolveMinesweeperBoard();
}
Now go to Tools/Hotkeys (Ctrl-K).
Hit New and select the Key and Mod of your choice.
Then select ScriptFunction in the Function field.
Our hotkey function is On_HK_0() so enter 0 into the Parm 1 field. Parm 2 and Parm 3 are not used, so ignore them.
Uncheck the Poll Method check and hit OK.
WARNING: DO NOT USE SHIFT AS A MOD FOR YOUR HOTKEY. DOING SO WILL PREVENT SOME MOUSE CLICKS FROM BEING SENT.
Now go to Minesweeper and hit the key and mod you selected to activate the hotkey.
Your script function will be called and your mouse will move across the board clicking all the correct squares.
It’s more than magic; it’s L. Spiro Script (/Me puts up flame shield).
Incredible, But What More?
Imagine what you can do to RTS and MMORPG games.
Mouse clicks aren’t the only things you can send; keyboard and joystick/controller events are supported as well, allowing you to make bots that can play all of your games for you, depending on your own level of coding and logical abilities.
You’ve all seen the video of Super Mario Bros.™ 3 being beaten in 11 minutes.
Now you can make a bot that can beat the game in 10 minutes by sending perfectly timed button presses to the emulator.
Easily make bots to perform the dull routine mouse/key sequences in your favorite MMORPG games.
Your bots can even change behavior depending on different game states.
What you can do is up to you.
This tutorial just gets you started.
I hope you enjoyed.
L. Spiro