Chrome 49 在 ReactOS 上 c0000005 崩溃的修复过程
Chrome 49 在 ReactOS 上 c0000005 崩溃的修复过程
概述
Chrome 49 (Chrome_V49) 在 ReactOS 上启动时立即崩溃,异常代码c0000005(访问违例),EIP=0。本文档详细记录了从问题分析到修复的完整过程。
1. 启用 Chrome 专用崩溃调试日志
修改文件
dll/win32/kernel32/client/except.c
修改内容
在UnhandledExceptionFilter调用的PrintStackTrace函数中,添加进程名判断,仅当当前进程为Chrome.exe时才打印详细的崩溃调试信息。
关键代码 - 进程名判断:
staticBOOLIsChromeProcess(VOID){CHAR szPath[MAX_PATH];CHAR*pName,*pSlash;if(!GetModuleFileNameA(NULL,szPath,sizeof(szPath)))returnFALSE;/* Extract filename from full path */pName=szPath;pSlash=strrchr(szPath,'\\');if(pSlash)pName=pSlash+1;pSlash=strrchr(szPath,'/');if(pSlash&&pSlash>pName-1)pName=pSlash+1;/* Convert to lowercase for comparison */for(pSlash=pName;*pSlash;pSlash++){if(*pSlash>='A'&&*pSlash<='Z')*pSlash+='a'-'A';}return(strcmp(pName,"chrome.exe")==0);}调试日志输出(10 步):
| 步骤 | 内容 | 说明 |
|---|---|---|
| Step 1 | Exception Basic Information | 异常代码、标志、地址 |
| Step 2 | Access Violation Details | 读/写/执行类型、目标地址 |
| Step 3 | Stack Data Dump | ESP 附近栈数据(定位调用参数) |
| Step 4 | Wine Stub Check | 缺失函数检查 |
| Step 5 | CPU Registers Dump | 所有寄存器值 |
| Step 6 | Crash Location Analysis | 崩溃所在的模块名、基址、偏移 |
| Step 7 | Call Stack Trace | 帧回溯(最多 128 帧) |
| Step 8 | All Loaded Modules | 所有已加载 DLL 列表 |
| Step 9 | Instruction at EIP | 崩溃位置的 16 字节机器码 |
| Step 10 | Debug Summary | 异常类型、崩溃位置摘要 |
2. 第一次崩溃分析:EIP=0 的 NULL 指针调用
崩溃日志摘要
ExceptionCode: c0000005 (ACCESS_VIOLATION) ExceptionAddress: 00000000 Operation: READ Faulting Address: 00000000 Registers: EAX: 00000000 EBX: 00000000 ECX: 0012fce4 EDX: c0000001 EBP: 0012fe14 ESI: 0015b0d0 ESP: 0012fca0 EDI: 00400000 EIP: 00000000 Call Stack (3 frames): Frame[0]: <Chrome.exe:0x1078f> (base=00400000) Frame[1]: <Chrome.exe:0x63e1a> (base=00400000) Frame[2]: <kernel32.dll:0x12535> (base=7C5E0000)分析过程
- 调用栈过短(仅 3 帧)→ 崩溃发生在 Chrome.exe 的非常早期初始化阶段
- EIP=0→ CPU 试图执行地址 0 处的代码 → 通过 NULL 函数指针调用
- Frame[0]=0x1078f→ 单例构造函数返回后的地址
反汇编 Chrome.exe 构造函数
使用 hex dump + objdump 反汇编 Chrome.exe:
关键代码段(RVA 0x104C0 ~ 0x1078f):
; Chrome 单例对象构造函数 (size 0x4C = 76 bytes) ; === 第一次 API 动态解析 === 0x104EE: call [GetCurrentProcess] ; 获取当前进程句柄 0x10504: call [GetModuleHandleW] ; GetModuleHandleW("kernel32.dll") 0x1050B: call [GetProcAddress] ; GetProcAddress(hMod, "IsWow64Process") 0x10513: test eax, eax 0x10515: je SKIP ; 如果 NULL 则跳过 ... 0x1052C: call *%eax ; 调用 IsWow64Process ; === 第二次 API 动态解析 === 0x106B9: push "GetProductInfo" ; 函数名 0x106BE: push "kernel32.dll" ; 模块名 0x106C3: call [GetModuleHandleW] ; GetModuleHandleW("kernel32.dll") 0x106C9: push eax ; hModule 0x106CA: call [GetProcAddress] ; GetProcAddress(hMod, "GetProductInfo") ... 0x106E7: call *%eax ; 调用 GetProductInfo ← CRASH HERE!根因定位
寄存器 + 栈数据综合分析:
ESP at 0x0012FCA0: 0x0012FCA0: 004106E9 00000006 00000000 00000000ESP 顶部值0x004106E9对应call *%eax指令后的返回地址(RVA 0x106E9),指令本身在RVA 0x106E7。
call *%eax的分析:
GetProcAddress(hKernel32, "GetProductInfo")→ EAX- 如果 EAX=0(函数未找到),则
call *%eax→ EIP=0 → 崩溃
根因确认
ReactOS 的kernel32.spec文件将GetProductInfo定义为转发器:
@ stdcall GetProductInfo(long long long long ptr) ntdll.RtlGetProductInfo但ntdll.dll 并没有导出 RtlGetProductInfo!
| DLL | 导出 RtlGetProductInfo |
|---|---|
| ntdll.dll | ❌ |
| ntdll_vista.dll | ✅ (Ordinal 2) |
Chrome 通过GetProcAddress(GetModuleHandleW("kernel32.dll"), "GetProductInfo")查找函数 → 转发器指向ntdll.RtlGetProductInfo→ ntdll 中没有此函数 → 返回 NULL → Chrome 调用 NULL 指针 → 崩溃。
3. 修复:修改 kernel32.spec 转发器
修改文件
dll/win32/kernel32/kernel32.spec
修改内容
- @ stdcall GetProductInfo(long long long long ptr) ntdll.RtlGetProductInfo + @ stdcall GetProductInfo(long long long long ptr) ntdll_vista.RtlGetProductInfo修复原理
- Chrome 在 Vista 兼容模式下运行(
LdrpInitializeProcessCompat: Found guid for winver 0x600) - Vista 兼容 shim DLL
ntdll_vista.dll会被自动加载 ntdll_vista.dll确实导出了RtlGetProductInfo- 修改转发器指向
ntdll_vista.RtlGetProductInfo后,GetProcAddress能正确解析
4. 修复验证
修复前
ExceptionCode: c0000005 ExceptionAddress: 00000000 Call Stack: 3 frames (Chrome.exe → Chrome.exe → kernel32)修复后
ExceptionCode: 80000003 (BREAKPOINT) ExceptionAddress: 021D7BCB (chrome.dll 内有效地址) Call Stack: 20 frames (全部在 chrome.dll 内) EIP Bytes: cc c3 6a 01 e8 05 2d ec 00 cc ...结论
- ✅
c0000005NULL 指针崩溃已完全修复 - ✅ Chrome 通过了单例构造函数阶段
- ✅ Chrome 进入了chrome.dll 的主初始化代码
- ⏳ 新的
80000003断点异常是 Chrome 内部断言失败,需要进一步分析
5. 文件变更汇总
| 文件 | 变更类型 | 说明 |
|---|---|---|
dll/win32/kernel32/client/except.c | 新增调试代码 | 添加IsChromeProcess()和 10 步调试日志 |
dll/win32/kernel32/kernel32.spec | 修复 | GetProductInfo转发器从ntdll改为ntdll_vista |
6. 编译与部署流程
# 编译ninja-C output-MinGW-i386 kernel32# 停止 VME:\VirtualBox\VBoxManage.exe controlvm"ReactOS-Test-New"poweroff# 部署到 VDIvdi_tool.exe add output-MinGW-i386\ReactOS-Test.vdi ^ output-MinGW-i386\dll\win32\kernel32\kernel32.dll ^/ReactOS/system32/kernel32.dll# 清除串口日志 + 启动 VMRemove-Itemoutput-MinGW-i386\serial_output.log E:\VirtualBox\VBoxManage.exe startvm"ReactOS-Test-New"# 查看日志Select-String-Path"output-MinGW-i386\serial_output.log"-Pattern"CHROME-DBG"