未公开KERNELBASE方法致Windows服务崩溃:无效指针读取(访问违例)
纯托管C# Windows服务非托管线程崩溃问题分析与解决
问题背景
我有两个基于纯托管C#编写的不同Windows服务(无第三方库或PInvoke调用),它们会在非托管线程上因相同调用栈随机崩溃,崩溃间隔从数小时到数周不等。该问题仅在生产环境出现,只能通过崩溃转储文件和WinDbg进行调试。
WinDbg异常分析结果
执行!analyze -v得到的异常信息如下:
EXCEPTION_RECORD: (.exr -1) ExceptionAddress: 00007ffd56b56700 (ntdll!memcpy+0x0000000000000040) ExceptionCode: c0000005 (Access violation) ExceptionFlags: 00000000 NumberParameters: 2 Parameter[0]: 0000000000000000 Parameter[1]: 000001f76dea0f78 Attempt to read from address 000001f76dea0f78 PROCESS_NAME: *Removed* READ_ADDRESS: 000001f76dea0f78 ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%p referenced memory at 0x%p. The memory could not be %s. EXCEPTION_CODE_STR: c0000005 EXCEPTION_PARAMETER1: 0000000000000000 EXCEPTION_PARAMETER2: 000001f76dea0f78 STACK_TEXT: ntdll!memcpy+0x40 KERNELBASE!StmWrite+0x72 KERNELBASE!PerfpAddCounterToStream+0x89 KERNELBASE!PerfpAddInstanceToStream+0xcf KERNELBASE!PerfpAddQueryItemToStream+0x15d KERNELBASE!PerfpNotifyAndCollect+0x113 KERNELBASE!PerfpNotificationCallback+0xa6 ntdll!TppIopExecuteCallback+0x129 ntdll!TppWorkerThread+0x3c8 kernel32!BaseThreadInitThunk+0x14 ntdll!RtlUserThreadStart+0x21
两个服务业务逻辑完全不同,且均为纯托管代码,无法确定该非托管线程的来源及其执行操作,也找不到KERNELBASE中这些调用方法的官方文档,难以定位崩溃原因。
堆地址信息分析
查看尝试读取的堆地址信息如下:
!heap -i 000001f76dea0f78 Detailed information for block entry 000001f76dea0f78 Assumed heap : 0x000001f76df60000 (Use !heap -i NewHeapHandle to change) Header content : 0x00000000 0x00000000 Owning segment : 0x000001f76df60000 (offset 0) Block flags : 0x0 (free ) Total block size : 0x0 units (0x0 bytes) Previous block size: 0x74e units (0x74e0 bytes) Block CRC : OK - 0x0 List corrupted: (Blink->Flink = 0000000000000000) != (Block = 000001f76dea0f88) Free list entry : CORRUPTED Previous block : 0x000001f76de99a98 Next block : 0x000001f76dea0f78
定位思路
- 调用栈中
Perfp前缀方法属于Windows性能计数器相关的非托管代码,该线程是系统性能监控的回调线程,用于收集性能数据。 - 堆地址显示该内存块已被释放且链表结构损坏,说明存在堆内存使用后释放的野指针问题,或内存越界导致堆结构破坏。
- 虽然服务是纯托管代码,但托管代码可能通过隐式的COM互操作、.NET框架内部调用触发非托管代码执行,需排查服务中是否依赖了系统组件的COM交互(如Windows内置管理类库)。
- 可通过
!dumpheap、!gcroot等命令检查托管堆中是否存在与性能计数器或相关COM对象的引用异常,同时用!lock排查线程同步问题导致的内存访问冲突。
问题最终定位与解决
更新:两个服务均使用了包含AzMan(Windows授权管理器)类型库的DLL,该库依赖COM互操作。自行开发读取AzMan数据的C#库后,崩溃问题不再出现。
微软已宣称AzMan已弃用多年,但自身软件仍在使用,故其仍作为Windows组件保留。特此提醒仍在使用AzMan的开发者。
内容的提问来源于stack exchange,提问作者Brad
相关产品推荐
相关产品推荐

