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
0 Nhận xét