
在實際游戲逆向和反外掛對抗中算術運算的底層實現是理解內存數據篡改、屬性計算和邏輯繞過的基石。很多初級逆向者能下斷點、能改數據卻不清楚游戲客戶端里一個簡單的“攻擊力 基礎攻擊 * (1 加成百分比)”是如何在匯編層面被計算和存儲的導致面對復雜的屬性加密或校驗時無從下手。本文將從 C 算術運算符與表達式的編譯原理出發結合游戲內存修改的典型場景帶你理解從高級語言代碼到 CPU 指令的映射關系掌握通過逆向算術邏輯來定位和修改關鍵游戲數據的方法。無論你是想學習游戲安全分析還是想理解反外掛機制的工作原理都需要先過這一關。1. 理解 C 算術運算的編譯與運行時表示在編寫或分析外掛時我們操作的目標并非 C 源代碼而是編譯后的二進制程序。因此理解源代碼中的算術表達式如何被編譯器翻譯成機器指令是進行有效逆向分析的先決條件。1.1 從源代碼到匯編指令的映射C 中的算術運算符,-,*,/,%在編譯后并不會留下任何高級語言的痕跡。編譯器會根據操作數的類型int, float, double 等、所在平臺的指令集x86, x64, ARM和優化等級生成對應的 CPU 指令序列。以一個最簡單的加法為例// C 源代碼 int playerHealth 100; int healAmount 50; playerHealth playerHealth healAmount;在 x86-64 架構下使用 MSVC 編譯器且關閉優化Debug 模式后對應的匯編代碼可能類似于mov eax, DWORD PTR [playerHealth] ; 將 playerHealth 的值加載到 eax 寄存器 add eax, 50 ; eax 寄存器中的值加上立即數 50 mov DWORD PTR [playerHealth], eax ; 將計算結果存回 playerHealth 的內存地址而在開啟優化Release 模式后編譯器可能會進行常量傳播甚至直接計算出結果150并存儲或者將這段邏輯內聯到更復雜的表達式中使得逆向時難以直接定位到這段獨立的“加法”操作。1.2 算術表達式在內存中的體現游戲中的角色屬性生命值、魔法值、攻擊力、金錢通常以基本數據類型int, float存儲在進程內存的特定地址中。算術表達式的作用就是讀取這些內存地址的值經過計算后再寫回內存或另一個內存地址。逆向外掛的常見思路就是定位數據通過搜索變動數值如生命值變化找到存儲該屬性的內存地址。分析訪問查找哪些匯編指令讀取或改寫了這個地址。理解計算分析這些指令構成的算術邏輯即表達式。實施修改通過注入代碼或直接修改內存/指令改變計算邏輯或結果。例如找到存儲“金錢”的地址后你可能會發現一段代碼在每次擊殺怪物時執行money money monsterReward。逆向分析就是要識別出這條加法指令并可能將其改為money money (monsterReward * 10)。1.3 整數與浮點數運算的逆向差異這是逆向分析中的一個關鍵分水嶺處理不當會導致修改失效或游戲崩潰。整數運算通常使用ADD,SUB,IMUL,IDIV等指令。逆向時相對直觀重點是識別操作數是寄存器、內存地址還是立即數。浮點數運算使用ADDSS,SUBSS,MULSS,DIVSS(單精度) 或ADDSD,SUBSD,MULSD,DIVSD(雙精度) 等 SSE/AVX 指令。浮點數在內存中以 IEEE 754 格式存儲直接搜索其整型表示需要轉換。例如游戲中的移動速度1.5f在內存中可能表示為0x3FC00000單精度。如果你用整數搜索工具搜1是找不到這個地址的。必須使用 Cheat Engine 等工具的“浮點數”或“雙浮點數”掃描類型。運算類型典型匯編指令 (x86/x64)內存數據特點逆向搜索要點整數運算ADD,SUB,IMUL,IDIV,INC,DEC直接以二進制補碼形式存儲使用“精確值”或“變動值”掃描整型。注意數據寬度4字節、8字節。浮點運算ADDSS/ADDSD,SUBSS/SUBSD,MULSS/MULSD,DIVSS/DIVSD遵循 IEEE 754 標準位模式非直觀必須使用“浮點數”掃描類型。修改時也要注意保持格式否則會讀成 NaN 或 Inf 導致崩潰。2. 環境與工具準備搭建逆向分析工作臺工欲善其事必先利其器。一個高效的逆向分析環境至少需要調試器、內存查看器和反編譯器。2.1 核心工具選擇與配置調試器與反匯編器x64dbg / OllyDbgWindows 平臺下強大的動態調試器適合跟蹤程序執行、下斷點、修改寄存器和內存。x64dbg 對 64 位程序支持更好是目前主流。IDA Pro靜態反匯編的行業標準能生成更易讀的偽代碼F5 功能極大提升分析復雜算術邏輯的效率。有免費版可用。GhidraNSA 開源的反編譯工具功能強大且免費是 IDA 的良好替代品。內存掃描與修改工具Cheat Engine游戲修改入門必備。核心功能是掃描內存中的數值變化定位變量地址。它還自帶調試器、匯編注入和腳本編寫功能非常全能。開發與編譯環境Visual Studio用于編寫測試用的 C 小程序理解不同編譯選項Debug/Release、優化等級對生成代碼的影響。編譯器MSVC、GCC 或 Clang。了解不同編譯器生成的匯編風格差異。2.2 創建一個用于逆向分析的目標程序為了安全、合法地練習我們首先自己編寫一個包含典型算術運算的小程序作為逆向分析的目標。創建一個名為ArithmeticTarget.cpp的文件#include iostream #include cstdlib #include ctime int main() { std::srand(static_castunsigned int(std::time(nullptr))); // 模擬游戲角色屬性 int baseHealth 1000; float defenseFactor 0.75f; int incomingDamage 0; int finalHealth baseHealth; // 模擬游戲循環 for (int i 0; i 10; i) { // 模擬受到隨機傷害 incomingDamage 100 (std::rand() % 200); // 傷害在100-300之間 // 核心算術表達式最終傷害 原始傷害 * 防御系數 int actualDamage static_castint(incomingDamage * defenseFactor); // 核心算術表達式最終生命 當前生命 - 最終傷害 finalHealth finalHealth - actualDamage; std::cout Round i 1 : Incoming incomingDamage , Actual actualDamage , Health Left finalHealth std::endl; if (finalHealth 0) { std::cout Player defeated! std::endl; break; } // 模擬每回合回血 5% finalHealth static_castint(finalHealth * 1.05f); } return 0; }使用 Visual Studio 或 g 編譯它# 使用 g g -o ArithmeticTarget.exe ArithmeticTarget.cpp -static-libgcc -static-libstdc關鍵點建議使用-O0關閉優化編譯第一個版本這樣生成的匯編代碼更直接易于理解。之后可以用-O2編譯對比觀察編譯器優化如何“混淆”我們的算術邏輯。這個程序模擬了游戲中最常見的兩種算術運算乘法傷害減免和減法生命扣除以及一個額外的乘法回血。我們的逆向目標就是定位并修改defenseFactor防御系數和回血系數1.05f。3. 逆向實戰定位并修改算術表達式現在我們以自己編寫的ArithmeticTarget.exe為目標使用 Cheat Engine 和 x64dbg 完成一次完整的逆向流程。3.1 使用 Cheat Engine 定位關鍵變量啟動并附加進程打開 Cheat Engine點擊左上角電腦圖標選擇ArithmeticTarget.exe進程。首次掃描程序輸出初始生命值1000。在 Cheat Engine 的“數值”框輸入1000掃描類型選擇“精確數值”數值類型選擇“4字節”因為int通常是4字節點擊“首次掃描”。過濾地址切回游戲程序讓它運行一輪生命值會變化。將新的生命值例如985輸入 Cheat Engine點擊“再次掃描”。重復此過程直到左側地址列表只剩下少數幾個地址。通常正確的地址其“值”會隨程序輸出實時變化。確認地址將疑似地址添加到下方地址列表并鎖定其值為一個很大的數如10000。如果程序輸出中的生命值不再減少說明找到了正確的finalHealth地址。找出是什么改寫了這個地址在地址上右鍵選擇“找出是什么改寫了這個地址”。Cheat Engine 會設置一個內存訪問斷點。切回程序繼續運行當有指令寫入該地址時調試器會中斷并顯示對應的匯編指令。你很可能看到一條SUB減法或MOV從計算結果移動過來指令這就是實現finalHealth finalHealth - actualDamage的關鍵代碼。3.2 使用 x64dbg/IDA 進行靜態與動態分析附加進程打開 x64dbg附加到ArithmeticTarget.exe。定位代碼通過 Cheat Engine 找到的指令地址在 x64dbg 中按CtrlG跳轉到該地址。你會看到類似下面的匯編片段; 假設 finalHealth 的地址是 [ebp-0x04] actualDamage 在 eax 寄存器中 sub dword ptr [ebp-0x04], eax ; 這就是 finalHealth - actualDamage向上分析查看這條SUB指令之前的代碼尋找actualDamage的計算過程。你可能會看到; incomingDamage 可能在 [ebp-0x08] mov eax, dword ptr [ebp-0x08] ; eax incomingDamage cvtsi2ss xmm0, eax ; 將整數 incomingDamage 轉換為浮點數到 xmm0 mulss xmm0, dword ptr [ebp-0x0C] ; xmm0 xmm0 * defenseFactor (浮點數乘法) cvtss2si eax, xmm0 ; 將浮點數結果轉換回整數到 eax即 actualDamage這段代碼清晰地展示了actualDamage incomingDamage * defenseFactor的浮點運算過程。其中[ebp-0x0C]很可能就是存儲defenseFactor(0.75f) 的內存地址。修改邏輯我們的目標是讓角色無敵即actualDamage 0。有幾種方法方法A修改內存數據找到defenseFactor的地址[ebp-0x0C]用 Cheat Engine 將其值從0.75改為0.0。方法B修改匯編指令在 x64dbg 中將mulss xmm0, dword ptr [ebp-0x0C]這條指令改為xorps xmm0, xmm0將 xmm0 寄存器清零這樣無論原始傷害是多少相乘結果都是0。方法C繞過減法直接找到sub dword ptr [ebp-0x04], eax指令將其改為nop空操作這樣生命值就不會被扣減。3.3 關鍵算術運算的逆向特征與修改策略下表總結了常見算術表達式在逆向中的特征和對應的外掛修改思路算術表達式 (C)可能的匯編指令 (x64)逆向識別特征外掛修改思路a b cmov eax, [b]; add eax, [c]; mov [a], eaxADD指令兩個操作數。1. 將ADD改為MOV直接賦值。2. 增大c的值如果是立即數或來自某個地址。a b - cmov eax, [b]; sub eax, [c]; mov [a], eaxSUB指令。1. 將SUB改為NOP或MOV。2. 將[c]改為 0。a b * cmov eax, [b]; imul eax, [c]; mov [a], eax(整數)mulss xmm0, [c](浮點)IMUL或MULSS/MULSD指令。1. 將乘數[c]改為 1整數或 1.0浮點。2. 將乘法指令替換為MOV。a b / cmov eax, [b]; cdq; idiv dword ptr [c]; mov [a], eax(整數)divss xmm0, [c](浮點)IDIV或DIVSS/DIVSD指令。IDIV前常有CDQ或CQO。1. 將除數[c]改為 1。2. 將除法指令替換為MOV。a b % cidiv指令余數在edx寄存器中。在IDIV指令后會使用MOV從edx取余數。修改使余數恒為0例如讓b是c的倍數或直接修改取余數后的結果。a 5add dword ptr [a], 5ADD指令第二個操作數是立即數。將立即數5改大或把ADD改為NOP。4. 進階處理加密、校驗與反調試真實的游戲客戶端不會將生命值、金錢等關鍵數據以明文int或float形式放在內存中。他們會采用各種混淆和加密技術。4.1 簡單的線性變換與校驗和這是最基本的保護方式。存儲值 真實值 * 系數 偏移量例如真實生命值100存儲時可能是100*2 50 250。逆向時需要找到乘法和加法指令反推出真實值。校驗和游戲可能同時存儲數據和它的校驗和如 CRC32。外掛只修改數據會導致校驗和不匹配引發游戲檢測或崩潰。逆向時需要找到校驗和計算函數并在修改數據后同步更新校驗和。在匯編中你可能會看到類似這樣的模式; 讀取存儲值 mov eax, [healthStored] ; 逆向變換真實值 (存儲值 - offset) / multiplier sub eax, 50 ; 減去偏移量 50 sar eax, 1 ; 算術右移1位相當于除以2 (如果乘數是2的冪) mov [healthReal], eax ; 得到真實生命值用于顯示和計算修改時你必須修改healthStored并且要保證(healthStored - 50) / 2等于你期望的真實生命值。4.2 面對反調試機制的策略游戲會使用IsDebuggerPresent、CheckRemoteDebuggerPresent、NtQueryInformationProcess等 API或通過 Timing Check、代碼完整性校驗來檢測調試器。在 x64dbg 中應對使用插件如ScyllaHide可以隱藏調試器繞過常見的反調試檢測。手動修改 API 返回值在IsDebuggerPresent函數開頭下斷點將其返回值在rax/eax中強制改為0mov eax, 0。避開檢測點通過靜態分析IDA找到反調試代碼所在的模塊或函數在調試時跳過或繞過這些代碼段。4.3 定位加密解密函數當直接搜索數值失效時需要尋找負責加密和解密的函數。數據訪問斷點對疑似加密的數據地址下“訪問”斷點。任何讀取該地址的代碼都會中斷這其中很可能包含解密函數。字符串引用在 IDA 或 x64dbg 中搜索可能相關的字符串如“health”、“damage”、“decrypt”、“decode”這些字符串可能出現在解密函數附近。調用棧分析在游戲更新UI顯示生命值的地方下斷點例如調用sprintf或文本渲染函數然后查看調用棧逆向回溯最終找到從加密數據到明文數據的轉換邏輯。5. 常見問題與排查路徑在逆向算術邏輯時你會遇到各種問題。下面是一個快速排查指南。問題現象可能原因檢查與解決思路找到地址并修改但游戲無反應或很快恢復。1. 地址是動態的每次啟動變化。2. 修改的是客戶端顯示值服務器有校驗。3. 數據有多層指針。1. 尋找“指針”。用 Cheat Engine 的“找出是什么訪問了這個地址”找到基地址和偏移。2. 對于網游修改本地內存通常無效需尋找發包函數修改發送給服務器的數據。3. 可能修改了錯誤的地址用更精確的掃描方式浮點、全部重新掃描。修改后游戲崩潰。1. 修改了代碼段破壞了指令完整性。2. 修改了非對齊的數據或錯誤的數據類型。3. 觸發了反作弊檢測。1. 確保只修改數據段或精心修改代碼段保持指令長度。2. 確認數據類型4字節整型、4字節浮點、8字節雙精度。浮點數修改要保證仍是合法浮點值。3. 在修改前暫停游戲線程或使用更隱蔽的寫入方式。無法在匯編中找到直觀的算術指令。1. 編譯器優化如常量傳播、循環展開。2. 運算被內聯到其他復雜函數中。3. 使用了向量化指令如 SIMD。1. 使用未優化的 Debug 版本進行分析理解邏輯后再看 Release 版本。2. 在反編譯器IDA F5, Ghidra中查看高級偽代碼理清邏輯。3. 學習基本的 SSE/AVX 指令識別PADDD,PMULLD,ADDPS等并行運算指令。Cheat Engine 掃描不到數值。1. 數值類型選錯如用整型搜浮點。2. 數值被加密或編碼。3. 數值不在主模塊在其他 DLL 中。1. 嘗試所有掃描類型字節、2字節、4字節、8字節、浮點、雙浮點、字符串。2. 使用“未知初始值”掃描通過數值變大/變小來過濾。3. 在 Cheat Engine 中勾選“掃描時包括保留區域”并確認掃描范圍包含了所有加載的模塊。6. 最佳實踐與安全建議逆向工程是一門需要謹慎和不斷學習的技能。僅在合法環境練習所有練習必須在你自己擁有完全控制權的程序如自己編寫的 demo、明確授權用于安全研究的 CTF 題目、舊版單機游戲上進行。未經授權對任何在線游戲、商業軟件進行逆向和修改不僅是違反用戶協議的行為也可能涉及法律風險。理解優先于修改不要滿足于找到“神奇”的地址和代碼。花時間理解整個計算流程、數據結構和類關系。這能讓你應對更復雜的保護措施。從簡單到復雜先從簡單的、無保護的 C 控制臺程序開始練習定位和修改int、float。然后嘗試有簡單混淆的程序最后再研究帶有反調試、代碼虛擬化的目標。做好筆記使用 IDA 的注釋功能、x64dbg 的標簽功能或單獨的文檔記錄下關鍵函數的地址、變量的含義、結構體的偏移。逆向是一個拼圖過程筆記至關重要。關注底層原理深入學習 x86/x64 匯編語言、Windows PE 文件結構、內存管理機制。這些底層知識是理解一切高級逆向技巧的基礎。利用社區資源逆向工程社區有很多開源工具、教程和論壇。遇到問題時善于搜索和查閱文檔。算術運算的逆向是游戲外掛分析與防御中最基礎、最核心的一環。它連接著高級語言邏輯與底層機器狀態。掌握它意味著你能夠透過內存中冰冷的字節理解并影響程序運行的邏輯。接下來的學習方向可以是指針與數據結構的逆向、函數調用約定的分析、以及網絡協議的逆向這些技能將共同構成你分析和理解復雜軟件系統的能力圖譜。