Reverse mine clearance

1. Sample overview

one point one   Application information

Application Name: mine clearance

Size: 119808 bytes

File version: 5.1.2600.0 (xpclient.010817-1148)

Revised on: November 29, 2021, 8:34:38

MD5: 16A4FD569A3EB5CEBEB3DA99EF1D17E1

SHA1: 31A1A89BA067EA95F117754818429D6D8E8E59CF

CRC32: 43242150

Brief function introduction:

Click the chessboard with the left mouse button, and the box will become a number. Infer the number of surrounding mines through the numbers. The right mouse button can mark mines. If you point to mines, you will directly fail. After turning all the numbers, you can win

one point two   Analysis environment and tools

Tool: Cheat engine   Ollydbg VS2019

one point three   Analysis objectives

1. Obtain the coordinates of the mouse. When the mouse hovers over the chessboard, it will display whether the current selection is thunder

2. Click a key to automatically click all non thunder squares and mark thunder with a red flag

2. Specific analysis process

2.1 general idea

1. Dynamic debugging (starting from API)

          (1) Break points on the api, and then debug dynamically

          (2) Use debugging tools such as OD or x32Dbg to debug

2. Start data analysis based on some data of mine clearance procedure (start with data)

          (1) Data analysis, try to find changing data

          (2) It is convenient to use CE tools to search data

2.2 collecting information

Open exeinfo and drag the program into exeinfo

vc++7.1 compiler version is 7.0, and the probability is vs2003

Analysis using the pe view tool

It is found that the msvcrt.dll dynamic library is loaded, which is the Microsoft runtime library. Most of the code with this runtime is the SDK program

Obtain key data of mine clearance through CE

Click mine detection to customize the height, width and number of mines, so we use CE to search the data

 

Through the scanning of ce, we get the basic information, which is helpful for us to write a dll Dynamic Library in the later stage

 

  We speculate that the data is placed in the data segment. Looking at the section table, we find that the data segment is 0x5000(RVA) to 0xB98. The width height thunder number minus the base address (0x01000000 unopened random base address) rva in the above figure hits in the. Data segment

 

Therefore, we searched the memory and found that each time we clicked smiley face, a large area of memory would change. After analysis, we came to a conclusion

This is the two-dimensional array of the chessboard. After carefully analyzing the elements inside, we can get the following data

2D array first address 0x1005340

40 empty

41 1

42 2

43 3 

8f ray

of unselected box

0e is the flag

Cc is the thunder after clicking

  According to the preliminary analysis, we have obtained a lot of useful data. Next, let's look at the specific implementation with OD

2.3 OD debugging find callback function

  1. The SDK program needs to register the window class before creating the window, that is, the RegisterClassW function. Its only parameter is the pointer to the WNDCLASS structure, and the second parameter of this structure is the address of our window callback function

 

 

Set the mouse click breakpoint on the callback function

The parameter is assumed to be winproc

  Set the conditional breakpoint of left mouse click, because each time you click the mouse to turn the grid

 

2.4 code for finding screen coordinate to array subscript

Next, we need to clarify what the purpose is. We need to find out how the screen coordinates are converted into array subscripts

Click the mouse and the interface will be broken. See that lparam parameter is the coordinate of our mouse. We should always pay attention to this address and see where to access it

According to the analysis, we found that this place is the code for converting screen coordinates to array subscripts

 

3. Script mine clearance

3.1 ideas

(1) Using SetWindowLong function to replace the original callback function, we can intercept us in our code

Concerned about the program and do the corresponding processing

(2) Using dynamic library injection technology, we inject our own dll into the target program

(3) Assist us to complete the code according to the information we have collected

// dllmain.cpp: defines the entry point for the DLL application.
#include "pch.h"
#include <Windows.h>
#include <Math.h>
//Function pointer of callback function
typedef LRESULT(CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM);
//Replaced window callback
LRESULT CALLBACK MyWindowProc(HWND hwnd,      // handle to window
    UINT uMsg,      // message identifier
    WPARAM wParam,  // first message parameter
    LPARAM lParam   // second message parameter
);
//Open window handle
HANDLE hProcess = NULL;
//Own module handle
HMODULE g_hMod = NULL;
//Original window procedure function
WNDPROC g_pfnOldWndProc = NULL;
void init();//initialization
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        g_hMod = hModule;
        init();
        break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
//Callback function to inject dll
LRESULT CALLBACK MyWindowProc(HWND hwnd, 
    UINT uMsg,     
    WPARAM wParam,  
    LPARAM lParam 
)
{
    switch (uMsg)
    {
    case WM_KEYDOWN:
        if (wParam == VK_F5)
        {
            //Height of minesweeping array
            PDWORD hight = (PDWORD)0x1005338;
            //Width of minesweeping array
            PDWORD width = (PDWORD)0x1005334;
            DWORD array = 0x1005340;//Array header address
            char* szBuf = (char*)array;
            //Traversing minesweeping array
            for (int i = 0; i <= *width; i++)
            {
                for (int j = 0; j <= *hight; j++)
                {
                    if ((unsigned char)*((szBuf + j * 32) + i) == 0xf)
                    {
                        int xPos = (i << 4) - 4;
                        int yPos = (j << 4) + 0x27;
                        
                        SendMessage(hwnd, WM_LBUTTONDOWN, 0,MAKELPARAM(xPos, yPos));
                        SendMessage(hwnd, WM_LBUTTONUP, 0, MAKELPARAM(xPos, yPos));
                    }
                    if ((unsigned char)*((szBuf + j * 32) + i) == 0x8f)
                    {
                        int xPos = (i << 4) - 4;
                        int yPos = (j << 4) + 0x27;
                        SendMessage(hwnd, WM_RBUTTONDOWN, 0, MAKELPARAM(xPos, yPos));
                        SendMessage(hwnd, WM_RBUTTONUP, 0, MAKELPARAM(xPos, yPos));
                    }
                }
            }
        }
        break;
    case WM_MOUSEMOVE:
        {
        //Corresponding to the code of mouse cursor conversion array subscript in assembly
        DWORD temp = (DWORD)lParam;
        DWORD posX = temp & 0xffff;
        DWORD posY = (temp & 0xffff0000)>>0x10;
        posX = (posX + 4) >> 4;
        posY = (posY - 0x27) >> 4;

        //Take the width and height of the current minesweeping array
        PDWORD hight = (PDWORD)0x1005338;
        PDWORD width = (PDWORD)0x1005334;
        //Determine whether the converted subscript is on our valid grid
        if ((posX > 0 && posX <= *hight) && (posY > 0 && posY <= *width))
        {
            DWORD array = 0x1005340;//Array header address
            char* szBuf = (char*)array;
            //Change window title
            if ((unsigned char)*((szBuf+ posY *32)+ posX) == 0x8f)
            {
                SetWindowText(hwnd, L"Don't cry");
            }
            else
            {
                SetWindowText(hwnd, L"Order, order");
            }
        }
        break;
        }
    }
    return g_pfnOldWndProc(hwnd, uMsg, wParam, lParam);
}
void init()
{
   HWND hWnd = FindWindow(L"mine clearance", L"mine clearance");
   if (hWnd == NULL)
   {
       OutputDebugString(L"Handle acquisition failed");
       return;
   }
   DWORD dwProcid = 0;
   GetWindowThreadProcessId(hWnd, &dwProcid);
   hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcid);

   //Replace callback function
   g_pfnOldWndProc = (WNDPROC)SetWindowLong(hWnd,GWL_WNDPROC,(LONG)MyWindowProc);

}

Posted by monicka87 on Mon, 29 Nov 2021 20:12:44 -0800