注册

黑神话:悟空——揭秘风灵月影的技术魔法

作为一名“手残党”,你是否常常因为复杂的操作和高难度的游戏内容而感到沮丧?不用担心,《黑神话:悟空》不仅仅是为硬核玩家准备的,它同样为我们这些操作不那么娴熟的玩家提供了无比精彩的游戏体验。


本文将带你深入探讨《黑神话:悟空》背后的技术原理,揭示风灵月影团队如何通过创新的技术手段,让每一位玩家,无论技术水平如何,都能在游戏中找到属于自己的乐趣。我们将揭秘那些让你在游戏中感受到无比真实和沉浸的细节,从逼真的角色动画到动态的环境效果,每一个细节都展示了团队的卓越才能和对游戏品质的追求。让我们一同走进这场技术与艺术的盛宴,感受《黑神话:悟空》背后的科技魔法,了解这些技术如何让你在游戏中无论是战斗还是探索,都能享受到极致的体验。


我的黑神话悟空数值


image.png


欢迎加入宗门


风灵月影修改器


Attach一个游戏进程,黑神话悟空进程名固定
image.png


Attach一个进程之后,可以修改对应的游戏数值
image.png


使用方式上每次启动游戏都要启动风灵月影,重启的游戏风铃月影也会重新Attach进程。


游戏修改器的工作原理


游戏修改器的技术原理主要涉及对游戏内存的实时修改和对游戏数据的动态调整。以下是修改器的主要技术原理和工作机制:


内存修改



  1. 内存扫描:修改器首先会扫描游戏进程的内存空间,找到存储特定游戏数据(如生命值、金钱、资源等)的内存地址。
  2. 地址定位:通过反复扫描和比较内存数据的变化,确定具体的内存地址。例如,玩家在游戏中增加或减少金钱,修改器会通过这些变化找到金钱的内存地址。
  3. 数据修改:一旦找到目标内存地址,修改器会直接修改该地址处的数据。例如,将生命值地址的数据修改为一个极大值,从而实现无限生命。

动态链接库(DLL)注入



  1. DLL注入:修改器可以通过将自定义的DLL文件注入到游戏进程中,来拦截和修改游戏的函数调用。
  2. 函数劫持:通过劫持游戏的关键函数,修改器可以在函数执行前后插入自定义代码。例如,拦截角色受伤的函数调用,将伤害值修改为零,从而实现无敌效果。
  3. 实时调整:DLL注入还可以实现对游戏数据的实时监控和调整,确保修改效果持续生效。

调试工具



  1. 调试接口:一些高级修改器使用调试工具(如Cheat Engine)提供的调试接口,直接与游戏进程交互。
  2. 断点调试:通过设置断点,修改器可以在特定代码执行时暂停游戏,进行数据分析和修改。
  3. 汇编指令修改:修改器可以修改游戏的汇编指令,改变游戏的逻辑。例如,将减血指令修改为加血指令。

数据文件修改



  1. 配置文件:一些游戏的关键数据存储在配置文件中(如INI、XML等),修改器可以直接修改这些文件来改变游戏设置。
  2. 存档文件:修改器可以修改游戏的存档文件,直接改变游戏进度和状态。例如,增加存档中的金钱数量或解锁所有关卡。

反作弊机制



  1. 反检测:为了避免被游戏的反作弊机制检测到,修改器通常会使用一些反检测技术,如代码混淆、动态加密等。
  2. 隐蔽操作:修改器可能会模拟正常的用户操作,避免直接修改内存或数据,降低被检测到的风险。

以上是游戏修改器的主要技术原理。通过这些技术,修改器能够对游戏进行各种修改和调整,提供丰富的功能来提升玩家的游戏体验。然而,使用修改器时应注意相关法律和游戏规定,避免影响游戏的公平性和他人体验。


blog.csdn.net/m0_74942241… (可部分阅读)


代码示例


以下是一个简单的内存扫描示例:


#include <windows.h>
#include <iostream>
#include <vector>

// 扫描目标内存
std::vector<LPVOID> ScanMemory(HANDLE hProcess, int targetValue) {
std::vector<LPVOID> addresses;
MEMORY_BASIC_INFORMATION mbi;
LPVOID address = 0;

while (VirtualQueryEx(hProcess, address, &mbi, sizeof(mbi))) {
if (mbi.State == MEM_COMMIT && (mbi.Protect == PAGE_READWRITE || mbi.Protect == PAGE_WRITECOPY)) {
SIZE_T bytesRead;
std::vector<BYTE> buffer(mbi.RegionSize);
if (ReadProcessMemory(hProcess, address, buffer.data(), mbi.RegionSize, &bytesRead)) {
for (SIZE_T i = 0; i < bytesRead - sizeof(targetValue); ++i) {
if (memcmp(buffer.data() + i, &targetValue, sizeof(targetValue)) == 0) {
addresses.push_back((LPVOID)((SIZE_T)address + i));
}
}
}
}
address = (LPVOID)((SIZE_T)address + mbi.RegionSize);
}

return addresses;
}

int main() {
DWORD processID = 1234; // 替换为目标进程的实际PID
int targetValue = 100; // 要查找的值

HANDLE hProcess = OpenTargetProcess(processID);
if (hProcess) {
std::vector<LPVOID> addresses = ScanMemory(hProcess, targetValue);
for (auto addr : addresses) {
std::cout << "Found value at address: " << addr << std::endl;
ModifyMemory(hProcess, addr, 999); // 修改内存
}
CloseHandle(hProcess);
}

return 0;
}


修改目标进程的内存


#include <windows.h>
#include <iostream>

// 打开目标进程
HANDLE OpenTargetProcess(DWORD processID) {
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processID);
if (hProcess == NULL) {
std::cerr << "Failed to open process. Error: " << GetLastError() << std::endl;
}
return hProcess;
}

// 查找目标内存地址(假设我们已经知道地址)
void ModifyMemory(HANDLE hProcess, LPVOID address, int newValue) {
SIZE_T bytesWritten;
if (WriteProcessMemory(hProcess, address, &newValue, sizeof(newValue), &bytesWritten)) {
std::cout << "Memory modified successfully." << std::endl;
} else {
std::cerr << "Failed to modify memory. Error: " << GetLastError() << std::endl;
}
}

int main() {
DWORD processID = 1234; // 替换为目标进程的实际PID
LPVOID targetAddress = (LPVOID)0x00ABCDEF; // 替换为目标内存地址
int newValue = 999; // 要写入的新值

HANDLE hProcess = OpenTargetProcess(processID);
if (hProcess) {
ModifyMemory(hProcess, targetAddress, newValue);
CloseHandle(hProcess);
}

return 0;
}


以下是一个使用内联钩子实现函数劫持的简单示例(基于Windows平台):


#include <windows.h>

// 原始函数类型定义
typedef int (WINAPI *MessageBoxAFunc)(HWND, LPCSTR, LPCSTR, UINT);

// 保存原始函数指针
MessageBoxAFunc OriginalMessageBoxA = NULL;

// 自定义函数
int WINAPI HookedMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) {
// 修改参数或执行其他逻辑
lpText = "This is a hooked message!";
// 调用原始函数
return OriginalMessageBoxA(hWnd, lpText, lpCaption, uType);
}

// 设置钩子
void SetHook() {
// 获取原始函数地址
HMODULE hUser32 = GetModuleHandle("user32.dll");
OriginalMessageBoxA = (MessageBoxAFunc)GetProcAddress(hUser32, "MessageBoxA");

// 修改函数头部指令
DWORD oldProtect;
VirtualProtect(OriginalMessageBoxA, 5, PAGE_EXECUTE_READWRITE, &oldProtect);
*(BYTE*)OriginalMessageBoxA = 0xE9; // JMP指令
*(DWORD*)((BYTE*)OriginalMessageBoxA + 1) = (DWORD)HookedMessageBoxA - (DWORD)OriginalMessageBoxA - 5;
VirtualProtect(OriginalMessageBoxA, 5, oldProtect, &oldProtect);
}

int main() {
// 设置钩子
SetHook();
// 测试钩子
MessageBoxA(NULL, "Original message", "Test", MB_OK);
return 0;
}


CheatEngine


上述通过代码的方式成本比较高,我们通过工具修改内存值和函数劫持
http://www.cheatengine.org/downloads.p…


使用教程 blog.csdn.net/CYwxh0125/a…


image.png


如何实现自动扫描


风灵月影如何实现自动修改值,不需要每次搜索变量内存地址?


猜想1:变量内存地址固定?


经过测试发现不是


猜想2:通过变量字符串搜索?


算了不猜了,直接问GPT,发现应该是通过指针内存扫描的方式。


指针扫描适合解决的问题


指针扫描是一种高级技术,用于解决动态内存地址变化的问题。在某些应用程序(特别是游戏)中,内存地址在每次运行时可能会变化,这使得简单的内存扫描方法难以长期有效地找到目标变量。指针扫描通过查找指向目标变量的指针链,可以找到一个稳定的基址(静态地址),从而解决动态内存地址变化的问题。


1. 动态内存分配


许多现代应用程序和游戏使用动态内存分配,导致每次运行时同一变量可能位于不同的内存地址。指针扫描可以找到指向这些变量的指针链,从而定位到一个稳定的基址。


2. 多次重启后的地址稳定性


通过指针扫描找到的静态基址和指针链,即使在应用程序或系统重启后,仍然可以有效地找到目标变量的位置。这样,用户无需每次重新扫描内存地址。


3. 多级指针


有些变量可能通过多级指针间接引用。指针扫描可以处理这种情况,通过多级指针链找到最终的目标变量。


指针扫描的基本步骤


以下是使用 Cheat Engine 进行指针扫描的基本步骤:


1. 初始扫描


首先,使用普通的内存扫描方法找到目标变量的当前内存地址。例如,假设在游戏中找到当前金钱值的地址是 0x00ABCDEF


2. 指针扫描



  1. 在找到的内存地址上右键单击,选择“指针扫描此地址”。
  2. Cheat Engine 会弹出一个指针扫描窗口。在窗口中设置扫描参数,例如最大指针级别和偏移量。
  3. 点击“确定”开始扫描。Cheat Engine 会生成一个包含可能的指针路径的列表。

3. 验证指针路径



  1. 重启游戏或应用程序,再次找到目标变量的当前内存地址。
  2. 使用新的内存地址进行指针扫描,验证之前找到的指针路径是否仍然有效。
  3. 通过多次验证,找到一个稳定的指针路径。

4. 使用指针路径



  1. 在 Cheat Engine 中保存指针路径。
  2. 以后可以直接使用这个指针路径来访问目标变量,无需每次重新扫描。

示例:使用 Cheat Engine 进行指针扫描


假设你在游戏中找到了当前金钱值的地址 0x00ABCDEF,并想通过指针扫描找到一个稳定的基址。


1. 初始扫描



  1. 启动 Cheat Engine 并附加到游戏进程。
  2. 使用普通的内存扫描方法找到当前金钱值的地址 0x00ABCDEF

2. 指针扫描



  1. 右键单击找到的地址 0x00ABCDEF,选择“指针扫描此地址”。
  2. 在弹出的指针扫描窗口中,设置最大指针级别为 5(可以根据需要调整),偏移量保持默认。
  3. 点击“确定”开始扫描。

3. 验证指针路径



  1. 重启游戏,重新找到当前金钱值的地址(假设新的地址是 0x00DEF123)。
  2. 使用新的地址进行指针扫描,验证之前找到的指针路径是否仍然有效。
  3. 通过多次验证,找到一个稳定的指针路径。例如,指针路径可能是 [game.exe+0x00123456] + 0x10 + 0x20

4. 使用指针路径



  1. 在 Cheat Engine 中保存这个指针路径。
  2. 以后可以直接使用这个指针路径来访问金钱值,无需每次重新扫描。

注意事项



  1. 指针级别:指针级别越高,扫描时间越长,但也能处理更复杂的多级指针情况。根据实际需要设置合适的指针级别。
  2. 验证指针路径:指针路径需要多次验证,以确保其稳定性和可靠性。重启游戏或应用程序,重新扫描并验证指针路径。
  3. 性能影响:指针扫描可能会对系统性能产生一定影响,特别是在大型游戏或应用程序中。建议在合适的环境下进行扫描。

通过以上步骤,指针扫描技术可以帮助用户找到稳定的基址,解决动态内存地址变化的问题,从而实现更可靠的内存修改。


作者:AuthorK
来源:juejin.cn/post/7426389669527207936

0 个评论

要回复文章请先登录注册