Hack Minesweeper - part 2

     

        Nếu chưa xem phần 1 thì có thể xem lại tại đây.
        Mình sẽ sử dụng C# để code, mình cũng có đọc ở đâu đó trên vài trang nước ngoài thấy nhiều người nói là để viết mấy cái tool hack này nọ thì nên viết bằng C++ và mình cũng có tìm hiểu qua C++ nhưng thấy nó khó vãi, hoặc do mình tiếp xúc với C# nhiều hơn và sớm hơn nên quen hơn.
        Nói sơ sơ qua thì công việc cũng chỉ là đọc ghi giá trị từ địa chỉ ô nhớ trong game thui.

        Đọc bộ nhớ game

  [DllImport("kernel32.dll")]
  public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
  [DllImport("kernel32.dll")]
  public static extern bool ReadProcessMemory(int hProcess, int lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead); 
  
        Khi một tiến trình được mở, ta phải chỉ định quyền truy cập mong muốn (với game Minesweeper thì mình chỉ muốn đọc giá trị thui nên yêu cầu quyền truy cập để đọc bộ nhớ), vì vậy hằng số này là cần thiết:
        
  const int PROCESS_VM_READ = 0x0010;
  
        Giới thiệu thêm
  const int PROCESS_VM_WRITE = 0x0020; // yêu cầu quyền truy cập để ghi vào bộ nhớ
  const int PROCESS_ALL_ACCESS = 0x1F0FFF; // đọc và ghi
  
        

        Ghi vào bộ nhớ game

  [DllImport("kernel32.dll")]
  public static extern IntPtr OpenProcess( int dwDesiredAccess, bool bInheritHandle, int ProcessId);
  [DllImport("kernel32.dll", SetLastError = true)]
  public static extern bool WriteProcessMemory(int hProcess, int lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesWritten);
  
        Một chương trình có cả đọc và viết thì dùng OpenProcess một lần là được

        Bắt tay vào code thôi!!!

        Theo lý thuyết bên trên thì ban đầu phải import cái hàm đọc bộ nhớ vào, bài này mình chỉ đọc bộ nhớ thôi sang bài sau với game khác mình sẽ làm đủ cả đọc ghi.
        

            Đầu tiên phải khai báo một số biến hay ho ra trước, gồm mấy cái địa chỉ mà ở phần 1 mình tìm được,
    int widthAddr = 0x010056AC; // chiều rộng map
    int heightAddr = 0x010056A8; //chiều cao map
    int firstAddr = 0x01005361; // ô mìn đầu tiên map[0][0]
    int opendAddr = 0x010057A4; // số ô đã mở
    byte[] buffer = new byte[4]; //buffer là cái mà hàm yêu cầu, để nó lưu giá trị đọc được từ bộ nhớ vào buffer này, byte[4] là do tất cả các giá trị tìm được có kiểu lớn nhất 4bytes
    int mywidth = 0, myheight = 0, myopend = 0; // đọc được giá trị ra thì phải lưu vào để sử dụng
    int mycurr = 0;
    int reads = 0; // cái này cũng do hàm yêu cầu thôi, tạo cho đủ
    

        Tiếp theo để mở tiến trình thì làm như sau:
  Process process = Process.GetProcessesByName("Winmine__XP")[0];
  IntPtr processHandle = OpenProcess(PROCESS_WM_READ, false, process.Id);
  

        Cả đống nó sau thì sẽ trông thế này:


        Phân tích tiếp,, mình sẽ đọc giá trị liên tục để update lại lên màn hình, cho nên sẽ sử dụng một vòng lặp như sau
        
        Từ đây đọc các giá trị chiều rộng, chiều cao, và số ô đã mở đầu tiên

  ReadProcessMemory(processHandle, widthAddr, buffer, 1, ref reads);
  mywidth = BitConverter.ToInt32(buffer, 0);
  ReadProcessMemory(processHandle, heightAddr, buffer, 1, ref reads);
  myheight = BitConverter.ToInt32(buffer, 0);
  ReadProcessMemory(processHandle, opendAddr, buffer, 1, ref reads);
  myopend = BitConverter.ToInt32(buffer, 0);
  

        Tại sao đọc số ô đã mở thì là do sau khi mình ấn mở ô đầu tiên xong thì game nó mới tạo map, cho nên là phát click đầu tiên không bao giờ dẫm phải bom đâu. Đến đây thì suy luận xíu là đc, nếu chưa mở ô nào thì vẽ cái map mặc định toàn dấu - (gạch ngang), còn nếu mở rồi thì mới đọc giá trị của map để vẽ ra ô nào là bom ô nào không. 

    if (myopend == 0) // số ô đã mở == 0 tức là chưa mở ô nào
	{
	  for (int i = 0; i < myheight; i++)
	  {
	    for (int j = 0; j < mywidth; j++)
	    {
	      Console.Write("- ");
	    }
	  Console.WriteLine();
	  }
	}
	else // ngược lại thì là đã mở
	{
	  //// sẽ phân tích tiếp bên dưới
	}
  
        Đến đây chạy thử sẽ có kết quả thế này





    Trông có vẻ khá chính xác 😅😅

     Còn trong trường hợp là đã mở một ô và map được tạo thì sao?
    Mở CE lên và vào đó tìm hiểu một xíu


        Ấn Ctrl+B để mở xem vùng nhớ tại địa chỉ đó
        Đặt vài cái cờ sẽ thấy giá trị của mấy địa chỉ liền kề nhau thay đổi, suy ra là với hàng ngang thì mỗi ô nó cách nhau 1 byte thôi, nên để đọc giá trị ô bên cạnh thì lấy địa chỉ hiện tại +1 là được.

        Thế còn khi hết hàng ngang và xuống hàng mới

        Nhìn ta thấy thì nó cách nhau một khoảng cố định là 0x20, không tin thì lấy máy tính bấm giá trị sau trừ đi giá trị trước là ra.
        


        Vậy để di chuyển đến dòng thứ i thì mình có công thức thế này firstAddr + 0x20 * i, ví dụ để nhảy đến dòng 5: 0x01005361 + 0x20*5 = 01005401.
    
        
    Đó, địa chỉ có thật nhé, đúng công thức luôn.
    À ý mình là hàng thứ 6 nếu là trong game, cái hàng đầu tiên coi là hàng 0, nếu bạn có học về mảng rồi thì sẽ hiểu.
    Oke, đến đây bắt tay code nốt đoạn còn lại.
int current = firstAddr;
for (int i = 0; i < myheight; i++)
{
  for (int j = 0; j < mywidth; j++)
  {
    ReadProcessMemory(processHandle, current, buffer, 1, ref reads);
	mycurr = BitConverter.ToInt32(buffer, 0);
	if (mycurr == 0x8F || mycurr == 0x8E || mycurr == 0x8A) // 0x8F là bom, 0x8F là đặt cờ, 0x8A là bom mở ra sau khi kết thúc game
	{
	  Console.Write("x ");
	}
	else if (mycurr == 0xCC) // 0xCC là quả bom lúc ấn vào nó nổ
	{
	  Console.Write("0 ");
	}
	else if (mycurr == 0x0F)
	{ //0x0F là ô mặc định chưa mở hoặc chưa đặt cờ
	  Console.Write("- ");
	}
	else
	{
	  Console.Write("  ");
	}
  current++; //  đi đến ô tiếp theo
  }
current = (firstAddr + 0x20 * (i + 1)); //0x20 là giá trị mỗi hàng cách nhau, trừ đi là ra nó bằng 0x20
Console.WriteLine();
}
  
    Và đây là thành quả đạt được


    Full code mình sẽ update tại đây
    Điều này trái ngược với tư duy logic của game đề ra, nhưng thui dù sao mình cũng tư duy theo cách của mình mà nhỉ, học hỏi thêm được cái khác hay ho hơn là ngồi đếm số thui đúng không :V

Đăng nhận xét

0 Nhận xét