日期:2021.06.07
部落格期:181
星期一
【溫馨提示】:
我現在把資源先放到開頭,不想研究學習的就直接取用。如果修改器失效了,你們可以在部落格園本頁直接評論,也可以給我發郵件告訴我,就是不要到百度雲上去說了,百度雲我好久不登入一次的!大家給我發郵件的話,記得要註明是哪個遊戲,內容當然是越詳細越好啦!郵箱地址:nightskysxs@163.com
資源下載表 | |||
沒有部落格園賬號的網友 |
百度網盤下載連結:https://pan.baidu.com/s/1ohVFp0vvE3nt-tEv9Xy5bQ 提取碼:nort |
||
有部落格園賬號的網友 | 版本 | CT檔案 | 修改器 |
v2.5.3.21746 | 點我下載 | 點我下載 |
部落格防爬取部分:Steam遊戲《Northgard(北境之地)》修改器製作 - 初等變換不改變矩陣的秩 - 部落格園 (cnblogs.com)
前言
現在是準備畢業,碰巧遇到了遊戲我打不過,簡單花時間開發一下。預計本次部落格會不斷完善,直到我本人滿意為止,好吧!預期開發到各種指標的修改、個人組織成員無敵、建房速度啊、維修速度啊之類的。對於個人能力提升的方面,我沒什麼成見。或許這裡面的內容並沒有更多引起我注意的地方。基本是遊戲修改初等的應用了。
版本
修改內容
1、三項資源的修改(食物、木材、金錢)
這三項資源類似,我們以 “食物” 一項為例,搜尋 雙浮點型別 (Double)的值,得到如下圖所示的結果。其中我們需要的是最上方的有小數部分的那一個,因為實際上資料就應該是含小數部分的而不是給我們看的整數部分。
我們查詢是什麼改寫了這項程式碼,得到的結果是 —— “ 76CA9FC60A85 movsd [r10+48],xmm2 ” !得到區域程式碼:
程式碼所在記憶體地址 操作碼 虛擬碼 操作解釋
76CA9FC60A7F movsd xmm2,[rbp+18] xmm2 = [rbp+18] 將地址“rbp+18”所儲存的Double型別值賦值給暫存器 xmm2
76CA9FC60A85 movsd [r10+48],xmm2 [r10+48] = xmm2 將暫存器 xmm2所儲存的Double型別值賦值給地址“r10+48”位置上
這部分程式碼告訴我們上面講 [rbp+18] 的值傳給了 [r10+48] ,所以我們需要將 rbp+18 的值獲取之前將它改掉。
完成如下彙編:
push ebx // ebx 入棧
mov ebx,C34F // ebx 獲得 49999 的 4位元組值
cvtsi2sd xmm2,ebx // 將 ebx 的值 轉為 Double 型別 ,並賦值到 xmm2 暫存器中
pop ebx // ebx 出棧
movsd [rbp+18],xmm2 // 將 xmm2 的值 傳給 [rbp+18] , 完成修改
同理可以得到另外兩項修改,所有彙編:(目前未採用 AOB 注入方式)
1 [ENABLE] 2 //code from here to '[DISABLE]' will be used to enable the cheat 3 alloc(newmem,2048,76CA9FC60A7F) 4 label(returnhere) 5 label(originalcode) 6 label(exit) 7 8 newmem: //this is allocated memory, you have read,write,execute access 9 //place your code here 10 11 originalcode: 12 push ebx 13 mov ebx,C34F //49999 14 cvtsi2sd xmm2,ebx 15 pop ebx 16 movsd [rbp+18],xmm2 17 18 19 exit: 20 jmp returnhere 21 22 76CA9FC60A7F: 23 jmp newmem 24 nop 25 returnhere: 26 27 28 29 30 [DISABLE] 31 //code from here till the end of the code will be used to disable the cheat 32 dealloc(newmem) 33 76CA9FC60A7F: 34 movsd xmm2,[rbp+18] 35 //Alt: db F2 48 0F 10 55 18
1 [ENABLE] 2 //code from here to '[DISABLE]' will be used to enable the cheat 3 alloc(newmem,2048,76CA9FC60F02) 4 label(returnhere) 5 label(originalcode) 6 label(exit) 7 8 newmem: //this is allocated memory, you have read,write,execute access 9 //place your code here 10 11 originalcode: 12 push ebx 13 mov ebx,C34F //49999 14 cvtsi2sd xmm2,ebx 15 pop ebx 16 movsd [rbp+18],xmm2 17 18 exit: 19 jmp returnhere 20 21 76CA9FC60F02: 22 jmp newmem 23 nop 24 returnhere: 25 26 27 28 29 [DISABLE] 30 //code from here till the end of the code will be used to disable the cheat 31 dealloc(newmem) 32 76CA9FC60F02: 33 movsd xmm2,[rbp+18] 34 //Alt: db F2 48 0F 10 55 18
1 [ENABLE] 2 //code from here to '[DISABLE]' will be used to enable the cheat 3 alloc(newmem,2048,76CA9FC60CC2) 4 label(returnhere) 5 label(originalcode) 6 label(exit) 7 8 newmem: //this is allocated memory, you have read,write,execute access 9 //place your code here 10 11 originalcode: 12 push ebx 13 mov ebx,C34F //49999 14 cvtsi2sd xmm2,ebx 15 pop ebx 16 movsd [rbp+18],xmm2 17 18 exit: 19 jmp returnhere 20 21 76CA9FC60CC2: 22 jmp newmem 23 nop 24 returnhere: 25 26 27 28 29 [DISABLE] 30 //code from here till the end of the code will be used to disable the cheat 31 dealloc(newmem) 32 76CA9FC60CC2: 33 movsd xmm2,[rbp+18] 34 //Alt: db F2 48 0F 10 55 18
最終效果:
2、快速獲得村民
這個想要實現的方式有很多,我目前發現的一種比較簡單的是找到村民來到的百分數,如下圖:
如圖,我們需要搜尋這個 雙浮點數值 (Double),數值和上面顯示的一樣,如果是 65% 就搜尋 65 。有幾點需要注意的地方——第一,需要在剛進入遊戲對局的時候搜素,這個數值到後面就不是 65% 對應 65 的了;第二,搜素的時候也不要搜素 “65.0” 這樣,因為數值是有小數部分的,這樣搜是等於在搜純整數的 Double 值;第三,搜尋的時候一定要看清楚搜尋時的數值是不是遊戲當中的數值,實在不行配合 “變速齒輪”、“CE變速” 和 “搜尋時遊戲暫停” 等選項來搜尋,別到時候遊戲中的數值都變成 68 了,還在搜尋 65 呢!還有這個值實在難以滿足這三個條件的話,就直接去搜尋 Double 值吧!暫停遊戲時搜未變,增長搜增長的數值,新村民來了以後搜減少的數值,這樣找。
搜尋 “哪裡修改了此處程式碼”,得到的結果是 —— “ 76CA9FC731D4 movsd [rcx+60],xmm3 ” !得到區域程式碼:
程式碼所在記憶體地址 操作碼 虛擬碼 操作解釋
76CA9FC731CE movsd xmm3,[rbp+18] xmm3 = [rbp+18] 將地址“rbp+18”所儲存的Double型別值賦值給暫存器 xmm3
76CA9FC731D4 movsd [rcx+60],xmm3 [rcx+60] = xmm3 將暫存器 xmm3 所儲存的Double型別值賦值給地址“rcx+60”位置上
這部分程式碼告訴我們上面講 [rbp+18] 的值傳給了 [rcx+60] ,所以我們需要將傳來的 xmm3 的值以更快的速度增長。
完成如下彙編:
push ebx // ebx 入棧
mov ebx,19 // ebx 獲得 25 的 4位元組值
cvtsi2sd xmm0,ebx // 將 ebx 的值 轉為 Double 型別 ,並賦值到 xmm0 暫存器中 (因為後面 76CA9FC731DA 地址對應程式碼段 xmm3 值賦給了 xmm0 ,所以判定 xmm0的值是無用的)
pop ebx // ebx 出棧
addsd xmm3,xmm0 // xmm3 = xmm3 + xmm0
大致意思就是上述原賦值程式碼端每次執行會自動多增加25的值。彙編程式碼如下:(目前未採用 AOB 注入的方式)
1 [ENABLE] 2 //code from here to '[DISABLE]' will be used to enable the cheat 3 alloc(newmem,2048,76CA9FC731D4) 4 label(returnhere) 5 label(originalcode) 6 label(exit) 7 8 newmem: //this is allocated memory, you have read,write,execute access 9 //place your code here 10 11 originalcode: 12 push ebx 13 mov ebx,19 //25 14 cvtsi2sd xmm0,ebx 15 pop ebx 16 addsd xmm3,xmm0 17 18 exit: 19 movsd [rcx+60],xmm3 20 jmp returnhere 21 22 76CA9FC731D4: 23 jmp newmem 24 nop 25 returnhere: 26 27 28 29 30 [DISABLE] 31 //code from here till the end of the code will be used to disable the cheat 32 dealloc(newmem) 33 76CA9FC731D4: 34 movsd [rcx+60],xmm3 35 //Alt: db F2 48 0F 11 59 60
最終效果:
3、個人單位 NPC 無敵 (基本確保戰士無敵,其餘未測試)
每次看到己方小兵被敵方大兵騷擾,都氣憤不已卻又無能為力,現在吾將賜予汝等神力!己方血量不減,來,讓我用村民完勝你的強化巨人戰士!
我們要搜尋的值是 “收到傷害值” ,也就是說每次受到攻擊我們要搜的值是增加的。一般最好在你有兩名治療師的情況下,去找一匹狼戰鬥!
這樣可以找到兩個值,查詢是什麼程式碼段改寫了這個值,得到的結果是 —— “ 76CA9F81F265 movsd [rdx+00000188],xmm3 ” !得到區域程式碼:
程式碼所在記憶體地址 操作碼 虛擬碼 操作解釋
76CA9F81F25F movsd xmm3,[rbp+18] xmm3 = [rbp+18] 將地址“rbp+18”所儲存的Double型別值賦值給暫存器 xmm3
76CA9F81F265 movsd [rdx+00000188],xmm3 [rdx+188] = xmm3 將暫存器 xmm3所儲存的Double型別值賦值給地址“rdx+188”位置上
這部分程式碼告訴我們上面講 [rbp+18] 的值傳給了 [rdx+188] ,所以我們需要將傳來的 xmm3 的值改為0,以保證血量收到傷害為 0 。
所以,我就直接寫入了將如下彙編程式碼嵌入到原始碼中,它們的實際意義是 xmm3 = (Double) 0
push ebx // ebx 入棧
mov ebx,0 // ebx 獲得 25 的 4位元組值
cvtsi2sd xmm3,ebx // 將 ebx 的值 轉為 Double 型別 ,並賦值到 xmm3 暫存器中
pop ebx // ebx 出棧
結果您猜怎麼著?系統直接彈出並自動關閉遊戲程式,按我的歷史經驗來看這是報錯了。我立馬在 76CA9F81F265 處設定斷點,發現除了給戰鬥中的雙方賦值以外,還會帶有其他未知的賦值,這可能是最接近 剛才報錯原因 的原因了。之後,我按照做崩潰大陸的經驗檢視此處斷點的堆疊情況:
英雄被狼攻擊時:
狼被英雄攻擊時:
我左看看,右看看。這返回的地址都是變化的,而且沒有一個可以用的。我們需要的是靜態的能夠標記出兩邊哪一方是敵人的。最後我發現返回值前面有一句 76CA9F92CB16 處的 call 76CA9F81F210 ,我一瞬間想到這可能就是我一直想要找的突破口了。我在前面加入識別符號 tag_isHero 標記 程式碼是否是從這個地方呼叫一系列函式執行到這裡的。
1 [ENABLE] 2 //code from here to '[DISABLE]' will be used to enable the cheat 3 alloc(newmem,2048,76CA9F92CB16) 4 alloc(tag_isHero,8) 5 registersymbol(tag_isHero) 6 label(returnhere) 7 label(originalcode) 8 label(exit) 9 10 newmem: //this is allocated memory, you have read,write,execute access 11 //place your code here 12 13 originalcode: 14 mov [tag_isHero],00000001 15 call 76CA9F81F210 16 mov [tag_isHero],00000000 17 18 exit: 19 jmp returnhere 20 21 76CA9F92CB16: 22 jmp newmem 23 returnhere: 24 25 26 27 28 [DISABLE] 29 //code from here till the end of the code will be used to disable the cheat 30 dealloc(newmem) 31 dealloc(tag_isHero) 32 unregistersymbol(tag_isHero) 33 76CA9F92CB16: 34 call 76CA9F81F210 35 //Alt: db E8 F5 26 EF FF
之後在修改值的地方嵌入的程式碼又加上了如下判定:
cmp [tag_isHero],00000001 // 比較 tag_isHero 的值代表地址的值是不是 1
jne exit // 不是的話跳轉 exit 所指代的地址
之後我發現程式不會報錯了,但是雙方都變得 “無敵” 了,誰也打不死誰。看來是需要找一找它們的結構區別了。(這一點類似於我在做《九張羊皮紙》的時候,處理共用程式碼端的方法)
結構體對比圖:
經過長時間的對比,我們取 +44 處作為判定標準。敵人的 +40 處連帶 +44處都是地址,而己方則是 0 值。
之後在修改值的地方嵌入的程式碼又加上了如下判定:
cmp [rdx+44],00000000 // 比較 rdx+44 的值代表地址的值是不是 0
jne exit // 不是的話跳轉 exit 所指代的地址
最後測試果然成功了,彙編程式碼如下:
1 [ENABLE] 2 //code from here to '[DISABLE]' will be used to enable the cheat 3 alloc(newmem,2048,76CA9F81F25F) 4 label(returnhere) 5 label(originalcode) 6 label(exit) 7 8 newmem: //this is allocated memory, you have read,write,execute access 9 //place your code here 10 11 originalcode: 12 cmp [tag_isHero],00000001 13 jne exit 14 cmp [rdx+44],00000000 15 jne exit 16 push ebx 17 mov ebx,0 //0 18 cvtsi2sd xmm3,ebx 19 pop ebx 20 mov [tag_isHero],00000000 21 movsd [rbp+18],xmm3 22 jmp returnhere 23 24 // equals -> isHero -> damage = 0 25 exit: 26 movsd xmm3,[rbp+18] 27 jmp returnhere 28 29 76CA9F81F25F: 30 jmp newmem 31 nop 32 returnhere: 33 34 35 36 37 [DISABLE] 38 //code from here till the end of the code will be used to disable the cheat 39 dealloc(newmem) 40 76CA9F81F25F: 41 movsd xmm3,[rbp+18] 42 //Alt: db F2 48 0F 10 5D 18
測試結果:
修改器截圖: