Lạy chúa đến phần cuối rồi, mình lười viết kinh khủng nhưng
vì hôm nay là ngày chủ nhật và trời đang mưa to vl, thời tiết rất mát mẻ nên
mình ngồi viết nốt, ở phần 2, 3 và 4 mình đã tìm được địa chỉ quan trọng để
dùng cho phần cuối này đó là viết tools, có lẽ sau series PvZ này mình sẽ
chả làm hướng dẫn viết tool nữa vì cái chính nó vẫn nằm ở CE có tìm được địa
chỉ ô nhớ hay không thôi, mà nếu còn làm tiếp thì thì mình đang nghĩ tới thứ
làm auto gì đó chứ không phải hack như này.
Bắt đầu nào, mở Visual Studio lên, tạo một project winform
và tạo một cái giao diện đơn giản như sau.
C# có cái form này kéo thả dễ vl, chứ bảo làm bằng C++
chắc ốm mất (được nhé, nhưng nó khó vl ý, mình không biết làm
thôi).
Danh sách địa chỉ và tất cả giá trị mình tìm được khai
báo hết ra nhé.
MyMemory memory; private const int autoAddress = 0x004352F2; private const int fastRechargeAddress = 0x004958C5; private const int nomalZombieAddress = 0x0054626C; private const int hatZombieAddress = 0x00545B36; private const int shieldZombieAddress = 0x00545781; private const int AUTO = 0xEB; private const int NOTAUTO = 0x75; private const int FASTRECHARGE = 0x9090; private const int NOTFASTRECHARGE = 0x147E; private const int ONEHIT = 0x9090; private const int NOMALNOTONEHIT = 0x1D7F; private const int HEADNOTONEHIT = 0x1175; private const int GUARDNOTONEHIT = 0x1875; private int baseAddr;
Còn giá trị tại sao là như vậy tẹo làm đến
mình sẽ nói, à quên phải tạo thêm class mình nói ở phần 1 mình có code lại rồi
nữa nhé, mình đặt tên nó là MyMemory.
Xong code sẽ trông thế này.
Để chương trình tự load game khi vừa chạy thì mình tạo thêm 1
hàm Init() như sau, gọi hàm thì đặt ở vị trí như ảnh trên nhé.
private void Init() { memory = new MyMemory("popcapgame1"); if (memory.isOK()) { int auto = memory.ReadByte(autoAddress); if (auto == AUTO) { cbAtuto.Checked = true; } int fast = memory.ReadUShort(fastRechargeAddress); if (fast == FASTRECHARGE) { cbTime.Checked = true; } int onehit = memory.ReadUShort(nomalZombieAddress); if (onehit == ONEHIT) { cbOnehit.Checked = true; } button3.Visible = false; // sorry vì có mấy nút mình lười không đặt lại tên, nó là nút load game baseAddr = memory.GetBaseAddress(); //MessageBox.Show("Load game oke"); } else { MessageBox.Show("mở game lên trước"); //Environment.Exit(1); } }
Đầu tiên là kiểm tra game đã mở chưa, nếu mở rồi thì kiểm tra
xem ở những địa chỉ như auto, fast recharge hay onehit có được kích hoạt
chưa thì đặt dấu check cho mấy cái checkbox, thật ra nó chả cần thiết lắm mà
chủ yếu do mình làm màu thôi, cái chính là mở được chương trình lên thì lấy
được base address của game là chính,,,, còn trong trường hợp game chưa chạy
thì hiện cái thông báo lên cho mình biết thôi chứ cũng chả có gì ghê
gớm.
Mình cũng viết thêm một hàm nữa đặt tên nó là
GameisRunning() tác dụng là để kiểm tra xem game còn chạy nữa không, nhằm
tránh những lỗi nào đó có thể xảy khi mình ấn chức năng nhưng game đã bị
tắt mất rồi thôi.
private bool GameisRunning() { if (memory.isOK()) { return true; } else { MessageBox.Show("Game đã bị tắt"); button3.Visible = true; return false; } }
Double click vào cái checkbox auto collect đi, làm cái này
trước, nó sẽ tạo ra một hàm sự kiện, viết vào bên trong như sau.
if (GameisRunning()) { if (cbAtuto.Checked) { memory.WriteNumber(autoAddress, AUTO, 1); } else { memory.WriteNumber(autoAddress, NOTAUTO, 1); } }
Giải thích một xíu, hàm WriteNumber trong class memory mình
viết có tham số truyền vào như sau WriteMemory(địa chỉ ô nhớ, giá trị muốn
ghi vào, số bytes). Địa chỉ thì không nói rồi, đến giá trị ghi vào mà số
bytes thì sao, mở CE lên, bấm vào địa chỉ đã lưu chọn Disassemble this
memory region
Thấy lệnh này có 2 bytes 75 09
Sửa thành jne thành jmp thì 75 chuyển thành EB
Vậy là chỉ có 1 byte thay đổi giá trị khi không auto là
0x75 còn auto thì là
0xEB, vì sửa 1 byte nên hàm WriteNumber truyền
giá trị là 1. đó là tất cả lý do đó :v
Đến với phần hồi nhanh cây trồng, ta có địa chỉ như
sau
2 bytes là 7E 14, giờ không
cần nó luôn nhảy nữa mà là không cho nó nhảy, nên đây mình sẽ phải nop
lệnh jle này lại
Lệnh nop chỉ có giá trị là 1 byte, còn lệnh cũ là 2 bytes,
nên để bảo toàn chương trình sẽ bù thêm vào để đủ là 2 bytes, cho nên biến
của fastrecharge của mình có giá trị là 0x9090,
mặc định là 0x147E, nhớ để ý nó phải viết ngược
lại.
Double click vào cái check box fast recharge và viết lệnh
như sau.
if (GameisRunning()) { if (cbTime.Checked) { memory.WriteNumber(fastRechargeAddress, FASTRECHARGE, 2); } else { memory.WriteNumber(fastRechargeAddress, NOTFASTRECHARGE, 2); } }
Vì là chỉnh sửa đến 2 bytes nên phải truyền 2 vào nhé,
tương tự với 4 cũng thế.
Tương tự thì code của one hit như sau, có 3 loại zombie
nên viết chung hết vào
if (GameisRunning()) { if (cbOnehit.Checked) { memory.WriteNumber(nomalZombieAddress, ONEHIT, 2); memory.WriteNumber(hatZombieAddress, ONEHIT, 2); memory.WriteNumber(shieldZombieAddress, ONEHIT, 2); } else { memory.WriteNumber(nomalZombieAddress, NOMALNOTONEHIT, 2); memory.WriteNumber(hatZombieAddress, HEADNOTONEHIT, 2); memory.WriteNumber(shieldZombieAddress, GUARDNOTONEHIT, 2); } }
Giờ là hack tăng mặt trời, lần trước mình tìm được
pointer rồi ý, giờ mình sẽ chỉ cách dùng. Double click vào button Add
sun và viết lệnh như sau
if (GameisRunning()) { int value = 0; int[] sunOffset = { 0x0032F3F4 + baseAddr, 0x68, 0x320, 0x18, 0x4, 0x4, 0x8, 0x5578 }; for (int i = 0; i < sunOffset.Length - 1; i++) { value = memory.ReadInt(sunOffset[i] + value); } int addr = value + sunOffset[sunOffset.Length - 1]; value = memory.ReadInt(addr); bool isnumber = int.TryParse(textBox1.Text, out int num); if (isnumber) { memory.WriteNumber(addr, value + num, 4); } }
Nếu chạy hết Length thì value sẽ là giá trị của mặt trời luôn
nhưng mình không cần cái đó, mình cần địa chỉ của nó cơ để có thể thực hiện
được cả đọc và ghi nên chỉ chạy đến Length -1 thôi, rồi từ đó lấy ra địa chỉ
bằng cách cộng value với phần tử cuối trong mảng, sau khi có địa chỉ rồi thì
đọc ghi như bình thường thôi.
Với xu cũng như vậy nhé, Cái khác ở đây là xu chỉ có giá
trị bằng 1/10 nên chia cho 10 trc khi cộng thôi.
if (GameisRunning()) { int value = 0; int[] coinOffset = { 0x0032E77C + baseAddr, 0x18, 0x10, 0x14, 0x10, 0x4, 0x4, 0x84 }; for (int i = 0; i < coinOffset.Length - 1; i++) { value = memory.ReadInt(coinOffset[i] + value); } int addr = value + coinOffset[coinOffset.Length - 1]; value = memory.ReadInt(addr); bool isnumber = int.TryParse(textBox1.Text, out int num); if (isnumber) { memory.WriteNumber(addr, value + num / 10, 4); } }
Trước đó thì mình có tìm hiểu trên mạng thì
người ta viết khá là chuối
Bao nhiêu offset bấy nhiêu biến, nó không sai nhưng nhìn sida
vl ý.
Lười quá tổng kết thôi.
Toàn bộ khóa học đến đây là kết thúc 😁😁
Chơi game mà hack thì đúng là nó nhàm chán vl ý, nhưng thui
mình chỉ chia sẻ cách lập trình là chính thôi chứ mục đích của blog này
không phải là hack game đâu nhé, baibai anh em.
Link download ở đây nhé. file ăn sẵn nằm ở trong thư mục /bin/Release/netcoreapp3.1/publish/
0 Nhận xét