一般情況下,為了應付遊戲的動態記憶體地址,我們用基址的方式來讀取記憶體,不過前提是可以知道基址。有的遊戲,基址是很難分析到的,而且很多遊戲加了隱藏和變換,搞的分析起來特別複雜。這個時候,藉助Seraph,我們可以用另一種方式——搜尋記憶體。
雖然找不出基址,但是我們可以很輕鬆的找到一些我們想要的資料的直接地址。比如我們在一次遊戲的運行裡,可以用CE找到血的地址是&H900010, 血最大值的地址是&H900014,MP地址&H900020,最大MP&H900024。
雖然下一次遊戲運行時,這些地址都會變,但是我們知道,這些地址互相間的相對位址位移是不變的。也就是說最大血地址=血地址+4,MP地址=血地址+&H10
這樣我們就可以搜尋了。舉個例子,我們知道我們的血/最大血/MP/最大MP分別是8000/8000/12000/12000。那麼在指令碼裡,告訴Seraph的記憶體搜尋引擎,我們要搜尋一個地址,它的值是8000,這個地址+4的值也是8000,這個地址+&H10和+&H14的值是12000。這樣的地址,一般在遊戲裡只有一個。
怎麼搜尋呢?
複製代碼 代碼如下:
ClearSearchMemoryTable()
AddSearchMemoryItem(&H900010, 2, 8000)
AddSearchMemoryItem(&H900014, 2, 8000)
AddSearchMemoryItem(&H900020, 2, 12000)
AddSearchMemoryItem(&H900024, 2, 12000)
if SearchMemory(&H000000, Address) then
Print("找到地址:"&Address)
end if
注意,以上代碼裡,我們調用AddSearchMemoryItem函數來設定一些我們要搜尋的條件。
第一個參數是每個值的地址。不用擔心這個地址在下一次遊戲運行時不正確,Seraph搜尋只關心所有添加的搜尋項之間的“相對位址”。
第二個參數是指定類型,2代表四位元組整型。我們同樣可以添加雙位元組,浮點數,以及字串等不同類型。詳見協助手冊。
第三個參數就是這個搜尋項的值。你必須填入這個資料的當前值,如果填錯了,就搜不到了。(關於怎麼在指令碼裡指定當前的用於搜尋的值,我們之後講)
注意,第一行的ClearSearchMemoryTable()是用於清除上一次添加的搜尋項。重新開始一次搜尋前我們都要調一上這個函數。
添加完了這4個搜尋項以後,我們用SearchMemory開始搜尋。第一個參數表示搜尋的開始值,我們一般都可以用&H000000。第二個參數值用於返回搜尋到的結果。
搜到的結果就是第一個搜尋項的地址,也就是當前血的地址。我們可以儲存這個地址,用ReadMemory隨時去讀資料。
那麼,整體的流程應該是:
1. 在參數設定裡,我們添加一些參數,用於填入搜尋的資料。告訴使用者在指令碼開始前,先設定這些資料。比如,填入自己的血,MP,並在紅藍滿的時候開始指令碼。
2. 在指令碼一開始,用GetConfigNumber等函數取出設定的值,用以上程式碼搜尋記憶體,把得到的資料儲存下來
3. 在指令碼運行中,用儲存下的地址,加上各種我們已經知道的位移量,隨時讀取各項資料的值。
小技巧:
怎樣添加搜尋項才可以最方便準確的搜尋到我想要的那個唯一的記憶體位址?
當然是與角色越相關越好的。比如角色的各項屬性值。使用更多的搜尋項可以有效防止搜尋的不準確(即搜到不止一個地址),但是也會帶來每次啟動指令碼時的麻煩,因為我們啟動時都要設定一下搜尋值。建議用一些不經常變的值,比如,等級,攻擊值等,只有升了級才會變。而血值是經常變的。
同時,根據我們的經驗,角色名稱字是很好的一個搜尋項。如果我們可以分析到角色名稱字的地址,加在搜尋項裡(字串型),一般就可以很準確的搜尋到結果。
一般的遊戲角色名稱是UTF8形式的。我們可能要先將角色名稱用GBToUTF8函數轉換成UTF8編碼的字串,再用AddSearchMemoryItem添加。
如果有時候我們添加的數項不夠,或者我們在調試自己指令碼的時候,會搜尋到不同的記憶體位址,我們想讓指令碼全部輸出,怎麼辦?
複製內容到剪貼簿
代碼:
AddSearchMemoryItem(...)
AddSearchMemoryItem(...)
...
Address=0
while SearchMemory(Address+1, Address)
Print("找到地址:"&Address)
wend
原理就是從0開始,每搜尋到一個地址,先輸出,然後從這個地址+1繼續往下搜,一直到搜不到為止。