注意:本文提供的方法仅供参考,请以实际情况为准!
本文首发于Galgame求助吧,现搬运至个人博客,使用请遵循CC BY-NC-SA授权协议
问题描述
详见:【图片】有没有大佬知道这是什么情况【galgame吧】_百度贴吧
求助者为Windows 11 24H2系统,在远程协助中发现是从23H2升级上来的。

汉化程序运行后报错"应用程序无法正常启动(0xc0000005)",实际还有0xc000007b的报错。0x开头代表为十六进制数值。

使用DIE进行分析(这里只截取了其中一个程序的),提示打包工具:Sectionslike Enigma,因为Enigma Protector也是这提示,所以实际使用EnigmaVBUnpacker尝试解包并成功,确定为Enigma Virtual Box打包。
先尝试按常规方法寻找程序崩溃时产生的内存转储文件(DUMP,扩展名为.dmp),位置默认在
C:\Users\你的用户名\AppData\Local\CrashDumps
用户名根据自己的实际情况确定。或者Win + R调出运行窗口,输入
%localappdata%\CrashDumps
后按下回车键Enter,便可打开对应位置:

因为博主修改了默认的位置,所以下图地址栏中的路径仅供参考:

不过求助者电脑的对应位置并没有游戏名字的dmp(其实这里默认生成的是最小的内存转储,存储的信息比较少虽然多了博主也不一定能看出来啥),所以先继续尝试常规方法。
实际上从Windows 11的23H2 (22621.1992)版本开始,系统的任务管理器自带了生成实时内存转储的功能,不过当时博主忘了但有可能会失败,所以后面还是打算远程看一下。
因为有molebox打包的通过关闭VBS(基于虚拟化的安全)和内存完整性解决的案例,故先尝试这些方法,并未成功(成功了就没这篇文章了)。
然后尝试更改为全英文路径、安装常用运行库(DirectX和Visual C++)等常规方法,自然也没有成功。
后面就是通过远程控制进行更深入的分析了。


通过任务管理器创建内存转储文件(默认在%Temp%文件夹)
故障排除
为保护求助者的隐私,以下均为当时的情况复现还好能复现
使用传统搜索引擎和AI
首先是确认问题是否依旧存在:

可以看到依旧报错0xc0000005。实际上也可以通过搜索引擎来查找可能的解决方案,这里使用必应:(实际上博主当时根本就没搜)

前面这是AI总结,经过博主后续考证,这里所述的尝试访问不可访问的内存位置确实有其依据,这里先不揭晓答案。

后面也有一些方法,根据后续的情况来看,也是无效的。
另一个程序还有0xc000007b的报错,搜索结果如下:

其中一篇文章:

当然也没啥用,不过这个报错的含义倒是很接近问题的根本答案了,虽然博主当时不知道是什么意思(
以下是DeepSeek的回答(受限于篇幅,只截取部分),也还是差点意思:



后面就是通过远程控制工具实际操作进行分析了。
当时只分析了smee社Making Lovers的汉化程序,因为博主认为应该是通病,结果也确实如此。
通过WinDbg进行分析
WinDbg是微软公司开发的Windows操作系统多用途调试工具,可用于分析故障转储、调试实时用户模式和内核模式代码(程序),以及检查CPU 寄存器和内存。
博主主要用它来调试实时运行的用户程序,并创建程序故障时的内存转储,以供进一步的分析。
首先是下载和安装,在微软应用商店(Microsoft Store)就可以找到:

点获取,等它显示为打开,点击就可以。

然后就会打开如上的窗口,大多数人应该都看不懂,实际上博主也只是姑且会一点(
这里点击左上方的【文件】,会打开如下的窗口:

然后点击Launch executable,也就是启动可执行文件,即通常所说的exe文件,打开如下的窗口:

选择目标程序,这里为MakingLoversGBK.exe,选中后点击打开按钮:

启动时,下方的蓝色条中会显示加载一些内容,这是在联网下载所需的文件,务必确保网络畅通。不再加载后就会显示如上的界面。
可以看到,有
|
|
的输出,其中Access violation - code c0000005 (first chance)便是提示访问违规,错误代码(code)为c0000005,与之前的窗口提示的一致。
下面两行英文的意思是,这个异常(exception)可以被处理(handled),实际上有些错误挂上调试器后确实可以被跳过,虽然博主不知道原因是啥总之博主试过了,这里是没法跳过的,还是得继续看。
再下面有等号的那三行是程序当时在寄存器中存储的值。看不懂没关系,反正也是进一步分析的时候才会用到(
最后的ntdll!LdrRelocateImageWithBias+0x98:便是出错时所执行的函数(方法),是ntdll模块的LdrRelocateImageWithBias函数,偏移量为0x98。此偏移量处的反汇编代码为mov ecx,dword ptr \[edx+4\] ds:002b:00228002=,博主认为,单看这行代码也看不出什么,还是需要先看这个函数具体是干什么的。从名字来看,它应该是根据(With)基址偏移(Bias)来加载(Loader)并重定位(Relocate)映像(Image)。
博主原本想找下官方文档的解释,但好像没有,于是就去请教了一下D指导:

可以看到,这个函数确实是用来处理PE文件的基址重定位的。到这里读者可能要问了(或许早就该问了),什么是PE文件?这里引用韩国的逆向工程师李承远所著《逆向工程核心原理》一书中的解释:
PE文件是Windows操作系统下使用的可执行文件格式。它是微软在UNIX平台的COFF(Common Object File Format,通用对象文件格式)基础上制作而成的。最初(正如Portable这个单词所代表的那样)设计用来提高程序在不同操作系统上的移植性,但实际上这种文件格式仅用在Windows系列的操作系统下。
PE文件是指32位的可执行文件,也称为PE32。64位的可执行文件称为PE+或PE32+,是PE(PE32)文件的一种扩展形式(请注意不是PE64)。
可执行文件(exe)就是一种PE文件,dll库文件也是。那基址重定位又是指什么呢?首先需要知道映像(Image)的概念,同样引用自《逆向工程核心原理》:
讲解 PE 文件时经常出现"映像"(Image)这一术语,希望各位牢记。PE文件加载到内存时,文件不会原封不动地加载,而要根据节区头中定义的节区起始地址、节区大小等加载。因此,磁盘文件中的PE与内存中的PE具有不同形态。将装载到内存中的形态称为"映像"以示区别,使用这一术语能够很好地区分二者
一般来说,可执行文件都需要从本地硬盘加载到内存中才能运行,加载到内存中的部分就被称为映像(Image)。
最后就是重定位的概念了,这个就比较难解释了,姑且也引用一部分:
向进程的虚拟内存加载PE文件(EXE/DLL/SYS)时,文件会被加载到PE头的ImageBase所指的地址处。若加载的是DLL(SYS)文件,且在ImageBase位置处已经加载了其他DLL(SYS)文件那么PE装载器就会将其加载到其他未被占用的空间。这就涉及PE文件重定位的问题,PE重定位是指PE文件无法加载到ImageBase所指位置,而是被加载到其他地址时发生的一系列的处理行为。
提示:使用SDK(Software Development Kit,软件开发工具包)或Visual C++创建PE文件时,EXE默认的ImageBase为00400000,DLL默认的ImageBase为10000000
博主也尝试解释一下,每个程序在被执行时都会分配独立的虚拟内存空间,在这个内存空间里,程序(exe)本身,默认是以0x00400000作为基址进行加载,dll则是0x10000000。
如果要进行重定位的话,需要用到被称为ASLR(Address Space Layout Randomization,地址空间布局随机化)的技术,这是微软从NT6.0内核(对应Windows Vista系统)开始加入的一项技术。在之前版本的系统中没有该技术,也就不会生效。
借助ASLR技术,PE文件每次加载到内存的起始地址都会随机变化,并且每次运行程序时相应进程的栈以及堆的起始地址也会随机改变。也就是说,每次EXE文件运行时加载到进程内存的实际地址都不同,最初加载DLL文件时装载到内存中的实际地址也是不同的。
微软改用这种方式加载PE文件的原因何在呢?是为了增加系统安全性。大部分Windows OS安全漏洞(一般为缓冲区溢出)只出现在特定OS、特定模块、特定版本中。以这些漏洞为目标的漏洞利用代码(exploit code)中,特定内存地址以硬编码形式编入(因为在以前的OS中,根据OS版本的不同,特定DLL总是会加载到固定地址)。因此,微软采用了这种ASLR技术,增加了恶意用户编写漏洞利用代码的难度,从而降低了利用OS安全漏洞破坏系统的风险(UNIX/Linux OS等都已采用ASLR技术)。*
并且还需要在编译程序时启用了/DYNAMICBASE选项,编译出的程序才会使用此技术,如下图所示(这里使用Visual Studio 2022进行示范):

还有固定基址和数据执行保护(DEP,在某些疑难杂症的解决方法中经常被提及)的选项,这里就不赘述了。
总之,我们知道了,或许要从基址重定位的具体执行过程中,也就是
LdrRelocateImageWithBias
函数执行时发生了什么来分析,那怎么分析呢?其实博主也不知道(
不过博主知道原因,所以这里直接快进到:
解决方法
博主在之前翻找Windows 11的系统设置中,发现在Windows 安全中心(原Windows Defender)的应用和浏览器控制→攻击防护中有:

细心的读者应该就发现了,这里不就有个前文中提到的ASLR选项吗?对比博主自己的之后可知,原来应该是使用默认值(关闭),这里成了默认打开。关掉之后会显示要重启才能生效,重启电脑之后,游戏就可以正常打开了。

实际上,博主当时还发现控制流保护(CFG)选项是关着的,应该是开启的,就顺手改回去了,不知道会不会有影响。
这里还有DEP的选项(控制面板里有一个),右边还有程序设置,可以为对应的程序特定选项,在一些情况下或有奇效。
总之,这两个报错都随着强制映像随机化(强制ASLR)选项的关闭而解决了,可喜可贺(

后记
内存转储获取与分析
其实博主当时是先进行的内存转储,然后把文件传到自己的电脑上查看的,方法如下:

在箭头所指的红框处输入 .dump /ma D:\\crash.dmp
后按下回车键,内存转储文件就会保存为D盘的crash.dmp。
关于该命令,可参考微软官方说明:.dump(创建转储文件)| Microsoft Learn
摘取部分如下:
.dump 命令创建用户模式或内核模式故障转储文件。
.dump /m[MiniOptions]
创建小型内存转储(在内核模式)或小型转储(在用户模式下)有关详细信息,请参阅用户模式转储文件
如果/f或/m均未指定,则默认为/m。
在用户模式下,/m后可跟其他MiniOptions,指定要包含在转储中的额外数据
如果未包含MiniOptions,转储将包括模块、线程和堆栈信息,但不包含额外数据
可以添加以下任一MiniOptions来更改转储文件的内容;它们区分大小写:
| MiniOption | 效果 |
|---|---|
| a | 创建包含所有可选添加的小型转储。/ma选项等同于/mfFhut,它会在小型转储中添加完整内存数据、处理数据、未加载模块信息、基本内存信息和线程时间信息。任何读取内存的失败都会导致小型转储生成终止。 |
| A | /mA选项等同于/ma,但它会忽略读取不可访问内存的任何失败,并继续生成小型转储。 |
| f | 将完整内存数据添加到小型转储。目标应用程序拥有的所有可访问提交页面都将包括在内。 |
然后将保存的文件(如下图)通过远程控制软件的文件传输功能发送到本机。如果文件体积较大,可以先使用压缩软件进行压缩,实际上任何时候都建议先压缩一下。

本机安装有WinDbg的话,就可以直接双击打开了。会出现如下的窗口:

可以看到很多信息:
Loading Dump File \[Z:\\dump\\crash.dmp\]
User Mini Dump File with Full Memory: Only application data is available
这是说明加载的是只包含有对应程序所有内存空间的小型转储文件,听起来很别扭,实际上微软自己也是这么觉得:用户模式转储文件| Microsoft Learn

总之,此时的转储文件信息就算是比较全面了,虽然博主没法完全利用起来(
后面就是程序运行环境的信息:
Executable search path is:
Windows 10 Version 26100 MP (4 procs) Free x86 compatible
Product: WinNt, suite: SingleUserTS Personal
Edition build lab: 26100.1.amd64fre.ge_release.240331-1435
Debug session time: Fri May 30 18:45:15.000 2025 (UTC + 8:00)
System Uptime: 0 days 2:03:38.354
Process Uptime: 0 days 0:44:24.000
这里显示为Windows 10,通过具体的26100版本号可知,实际为Windows 11 24H2版本。还有产品(Product),内部版本号(Edition build lab),调试器启动的时间(Debug session time,UTC +8:00即为中国北京时区),系统运行时长(System Uptime)和进程运行时长(Process Uptime)。
图里还有两个可以点击的选项,分别是!analyze -v和.ecxr。
官方解释如下:
!analyze显示有关当前异常或错误检查的信息。
-v[0..99]
显示详细输出。 可以通过指定一个介于0到99的数字来显示更多信息。 如果未指定数字,则默认值为1。还可以指定"非常详细"(-vv)以显示所有可用信息。
对于用户模式,-v6显示全局和每个线程上已发现的内容。
在用户模式下,!analyze显示有关当前异常的信息。
在内核模式下,!analyze显示有关最新错误检查的信息。 如果发生错误检查,则会自动生成!analyze显示。 可以使用!analyze -v显示其他信息。 如果只想查看基本错误检查参数,可以使用.bugcheck(显示错误检查数据)命令。
.ecxr查找当前异常的上下文信息,并显示指定上下文记录的重要寄存器。
此命令还指示调试器使用与当前异常关联的上下文记录作为寄存器上下文。 运行.ecxr后,调试器可以访问此线程最重要的寄存器和堆栈跟踪。此寄存器上下文一直存在,直到你允许目标执行、更改当前进程或线程,或使用另一个寄存器上下文命令 (.cxr或.ecxr) 。 有关注册上下文的详细信息,请参阅注册上下文。
.excr 命令是同义词命令,具有相同的功能。
总之,可以都点一下,然后把结果CTRL + A全选,CTRL + C复制后扔给DeepSeek等大语言模型(LLM)。
