After the scene of the "mine" game

zhaozj2021-02-08  273

Introduction

Want to know everything that happened behind the "Mine" game? Well, I thought, but also this decided to study it. This article is my research result, now in public.

Main concept

1. Call the Win32 API using P / Invoke.

2. Read the memory of another process directly.

Note 1: The first part of this article consists of some assembly code. If you don't understand it, it is not a need, this is not the purpose of this article, you can jump. However, if you want to ask me about these code, I welcome you to write to me.

Note 2: This program is tested under Windows XP, so if it cannot run in other systems, please indicate the information of the system, let us know.

Note 2 Renew: This code can now be run under Windows 2000 after modification. Thank you Ryan Schreiber found the memory address under Win2k.

First step - explore WINMINE.EXE

If you are not a compiler, you can jump to this step, just see the conclusion.

In order to better understand everything that happened behind the "mine", I opened this file as the beginning. My personal favorite debugger is OLLY Debugger V1.08, which is a very simple and intuitive debugger. In short, I open WinMine.exe in the debugger and view the file. I found that there is a line in the Import area (listed in all DLL functions used in the program):

010011B0 8D

52C

377 DD MSVCRT.RAND

This means that "mine" uses a random function of the VC runtime, so I think this may help me. I searched this file, I saw where to call the rand () function, but I found this function in one place:

01003940 FF15 B0110001 Call DWORD PTR DS: [<& msvcrt.rand>]

Then I insert a breakpoint and run the program in this line. I found that a new Büreu is generated whenever a smile icon is clicked. Britcha is created as follows:

1. First, assign a memory area to Breto and set all memory bytes to 0x.

0F

It is noted that there is no mine in this unit (Cell).

2. Second, according to the mine number, every mine is traversed:

2.. Randomized X position (value between 1 to the width). 2.2. Randomized Y position (between 1 to height). 2.3. Set the value of the selected unit in the memory block is 0X

8f

This means that there is a mine in this unit.

The following is the original code, I have added some comments and add a coarse point.

010036A

7 MOV DWORD PTR DS: [1005334], EAX; [0x1005334] = Width (ie, the number of horizontal grid)

010036AC

MOV DWORD PTR DS: [1005338], ECX; [0x1005338] = height (ie vertical grid)

010036B2 Call Winmine.01002ed5; Generating empty memory blocks and clears

010036B7 MOV EAX, DWORD PTR DS: [

10056A

4]

010036BC MOV DWORD PTR DS: [1005160], EDI

010036C

2 MOV DWORD PTR DS: [1005330], EAX; [0x1005330] = number of mines

; Circulation in mine

010036C

7 Push DWORD PTR DS: [1005334] Press the maximum width (Max Width) into the stack

010036CD Call Winmine.01003940; Mine_Width = Randomized X position (0 to max width-1) (ie randomly selected a value between 0 and Max Width-1) 010036D2 Push DWORD PTR DS: [1005338]; Press the maximum height In the stack

010036D8 MOV ESI, EAX

010036Da incni; mine_width = mine_width 1

010036db Call Winmine.01003940; Mine_Height = Randomized Y position

(0 to MAX HEIGHT-1)

010036E0 Inc Eax; Mine_Height = MINE_HEIGHT 1

010036E1 MOV ECX, EAX; the address of the calculation unit in the memory block (Bremen)

010036E3 SHL ECX, 5; press this calculation:

; Unit memory address = 0x1005340 32 * Height Width

010036E6 TEST BYTE PTR DS: [ECX ESI 1005340], 80; [Unit Memory Address] == Is it already mine?

010036ee Jnz Short Winmine

.010036C

7; if it is already mine, it will re-iteration

010036F

0 SHL EAX, 5; otherwise, set this unit for the mine

010036F

3 Lea Eax, DWORD PTR DS: [EAX ESI 1005340]

010036fa or byte PTR DS: [EAX], 80

010036fd dec DWORD PTR DS: [1005330]

01003703 JNZ Short Winmine

.010036C

7; carry out the next iteration

As you can see from the code, I found 4 points:

Read memory address [0x1005334] derived the width of Breto.

Read memory address [0x1005338] derived the height of Breto.

Read memory [0x1005330] The number of mines in Breto is obtained.

They give X, Y, which represent a unit in Bremet, located in the X column, Y line. Address [0x1005340 32 * Y X] gives the value of the unit so that we have entered the next step.

Step 2 - Design a solution

You may think, which solution I will talk about? Obviously, after all mines information can be found, what I have to do is read data from memory. I decided to write a small program that read this information and give a description. It can paint Bremen, showing every discovered mine.

So how do you design? What I did is to put the address into a pointer (yes, it still exists in C #), and read the data it refers to, do you do it? Well, it is not complete. Because the occasions are different, the memory stores these data is not in my application. To know, every process has its own address space, so it will not "unexpectedly" access memory belonging to other programs. Therefore, in order to read this data, a method must be found to read the memory of another process. In this example, this process is the "mine" process.

I decided to write a small class library, which will receive a process and provide the ability to read the process memory address. This is because I have to use it in many programs, and there is no need to repeat these codes repeatedly. This allows you to get this class and use it in your application and free. For example, if you write a debugger, this class will help you. As far as I know, all debuggers have the ability to read the memory of the debugged program. So how can we read the memory of other processes? The answer is a API called ReadProcessMemory. This API actually allows you to read a specified address in the process memory. However, before doing this, you must open the process in a specific mode, and after the operation is completed, you must close the handle to avoid resource leakage. We use the help description of the API of OpenProcess and CloseHandle to complete the corresponding operation.

In order to use the API in C #, P / Invoke must be used, which means that it needs to be declared before using the API. Under normal circumstances, it is not so easy to make you realize it in a .NET. I found these API declarations in MSDN:

Handle OpenProcess

DWORD dwdesiredAccess, // Access flag

Bool binherithandle, // handle inheritance option

DWORD DWPROCESSID // Process ID

);

Bool ReadprocessMemory

Handle hprocess, // process handle

LPCVOID LPBASEADDRESS, // memory area base

LPVOID LPBUFFER, // Data buffer

Size_t nsize, // The number of bytes to read

Size_t * lpnumberofBytesRead // The number of read bytes

);

Bool CloseHandle

Handle Hobject // Processes Handle

);

These statements are converted to the following C # declaration:

[DLLIMPORT ("kernel32.dll")]]]]

Public Static Extern INTPTR OpenProcess

Uint32 dwdesiredAccess,

Int32 binherithandle,

Uint32 dwprocessid

);

[DLLIMPORT ("kernel32.dll")]]]]

Public Static Extern INT32 ReadProcessMemory

INTPTR HPROCESS,

INTPTR LPBASEADDRESS,

[In, out] byte [] buffer,

UINT32 SIZE,

Out INTPTR LPNUMBEROFBYTESREAD

);

[DLLIMPORT ("kernel32.dll")] public static extern INT32 CloseHandle

INTPTR HOBJECT

);

If you want to know more information about type conversion between C and C #, I suggest you search for this topic from the msdn.microsoft.com site: "Marshaling Data with Platform Invoke". Basically, if you put logically proper programs, it can run, but sometimes it needs a little adjustment.

After declaring these functions, I have to do it with a simple class and use this class. I put the statement in a class called ProcessMemoryReaderaPi, which is more reasonable. The main practical class is called ProcessMemoryReade. This class has a ReadProcess property, which is derived from the System.Diagnostics.Process type, which is used to store the process you want to read. There is a method in the class to open the process in reading mode. Public void openprocess ()

{

m_hprocess = processMemoryReadeRapi.OpenProcess

ProcessMemoryReaderapi.Process_VM_READ, 1,

(uint) m_readprocess.id);

}

Process_vm_read constant tells the system to open the process in read mode, and m_readprocess.id declares what process I want to open.

The most important thing in this class is a method that reads memory from the process:

Public Byte [] ReadProcessMemory (INTPTR MemoryAddress, Uint Bytestoread,

Out Int BytesReaded)

{

Byte [] buffer = new byte [bytestoread];

INTPTR PTRBYTESREADED;

ProcessMemoryReaderapi.ReadProcessMemory (M_HProcess, MemoryAddress, Buffer,

Bytestoread, Out PtrbytesReaded;

BYTESREADED = PtrbytesReaded.Toint32 ();

Return buffer;

}

This function declares a byte array with the requested size and reads memory using the API. It's that simple!

Finally, the following method closes the process.

Public void closehandle ()

{

IRetValue;

IRetValue = processMemoryReadeRapi.CloseHandle (M_HPRocess);

IRetValue == 0)

Throw New Exception ("CloseHandle Failed");

}

Step 3 - Use the class

It is now an interesting part. Using this class is to read "mine" memory and unveil Breto. To use the class, you need to initialize it first:

ProcessMemoryReaderlib.ProcessMemoryReader Preader

= New processMemoryReaderlib.ProcessMemoryReader ();

Next, you must set the process you want to read it. The following is an example of how to get the "mine" process, once the process is loaded, is set to the ReadProcess property:

System.Diagnostics.Process [] MyProcesses

= System.Diagnostics.Process.getProcessesbyName ("WinMine");

preader.readprocess = myprocesses [0];

What we need now is: Open the process, read memory, and turn it off after completion. Below or an example of operations, it reads the address representing the width of Bremen.

preader.openprocess ();

Int iWidth;

BYTE [] Memory;

Memory = preader.readprocessmemory ((intptr) 0x1005334, 1, out bytesreaded; iWidth = Memory [0];

preader.closehandle ();

Simple!

In the conclusion, I lists the full code showing the Breto. Don't forget, all memory locations I have to access are in the first part of this article.

// Bremen's data manager

System.Resources.ResourceManager Resources = New System.Resources.ResourceManager (TypeOf (Form1));

ProcessMemoryReaderlib.ProcessMemoryReader Preader

= New processMemoryReaderlib.ProcessMemoryReader ();

System.Diagnostics.Process [] MyProcesses

= System.Diagnostics.Process.getProcessesbyName ("WinMine");

// Get the first list of "mine" process

IF (MyProcesses.Length == 0)

{

Messagebox.show ("No Minesweeper Process Found!");

Return;

}

preader.readprocess = myprocesses [0];

// Open the process by reading a memory mode

preader.openprocess ();

Int bytesreaded;

Int iWidth, Iheight, Imines

IISMINE;

INT ICELLADDRESS;

BYTE [] Memory;

Memory = preader.readprocessmemory ((intptr) 0x1005334, 1, out bytesreaded;

iWidth = Memory [0];

TXTWIDTH.TEXT = iWidth.toString ();

Memory = preader.readprocessmemory ((intptr) 0x1005338, 1, out bytesreaded;

iHeight = Memory [0];

TXTHEIGHT.TEXT = IHEIGHT.TOSTRING ();

Memory = preader.readprocessmemory ((intptr) 0x1005330, 1, out bytesreaded;

Imines = memory [0];

TXTMINES.TEXT = Imines.Tostring ();

/ / Delete the previous button array

THIS.CONTROLS.CLEAR ();

This.Controls.addrange (MainControls);

// Create a button array for drawing each grid of Breto

ButtonArray = new system.windows.Forms.Button [iWidth, Iheight];

INT X, Y;

FOR (y = 0; y

FOR (x = 0; x

{

ButtonArray [x, y] = new system.windows.Forms.Button ();

ButtonArray [x, y] .Location = new system.drawing.point (20 x * 16, 70 y * 16);

ButtonArray [x, y] .name = "";

ButtonArray [x, y] .size = new system.drawing.size (16,16); iCelladdress = (0x1005340) (32 * (Y 1)) (x 1);

Memory = preader.readprocessmemory ((INTPTR) iCelladdress, 1, out bytesReaded;

IISMINE = Memory [0];

IF (iismine == 0x

8f

) // If there is a thunder, draw a mine bitmap

ButtonArray [x, y] .image = (system.drawing.bitmap)

(Resources.getObject ("Button1.image")))))))

This.Controls.Add (ButtonArray [x, y]);

}

// Close process handle

preader.closehandle ();

These, I hope you can learn new things.

转载请注明原文地址:https://www.9cbs.com/read-795.html

New Post(0)