上次经过一段时间的研究后,发现了通过修改ntkrnlpa.exe可以突破Windows XP 4G内存限制,完全使用4G以上物理内存(http://bbs.pediy.com/showthread.php?t=137830),但是在测试过程中发现使用USB存储设备时会出现蓝屏等问题,后来因为太忙,也没能继续研究。最近难得短暂清闲,功夫不负有心人,花了一个多星期终于找到问题所在了,下面谈谈在探索过程中的一些心得。
1.首先替换usb相关驱动
既然一插U盘就蓝屏,而使用USB鼠标、摄像头等都没有问题,那么首先怀疑是usbstor.sys的问题。Windows 2003可以支持4G以上物理内存,那么我们将Windows 2003的USB驱动替换到XP上来,首先替换usbstor.sys,现象依旧。那么全部替换usb相关驱动,包括usbstor.sys、usbport.sys、usbhub.sys、usbehci.sys等,XP可以正常启动,USB设备也能使用,但经反复测试,发现使用U盘时有时还是会蓝屏。
2.是谁造成了内存访问越界?
种种蓝屏迹象表明,很可能是因为内存读写越界造成的。破解4G限制后,虽然物理地址扩展到32位以上,但是虚拟地址依然是32位的啊,使用虚拟地址的程序不可能造成内存访问越界啊。那么有哪些代码用到了物理地址呢?
首先想到的是内存分页管理,负责映射物理地址到虚拟地址空间,维护了一个物理内存的页帧数据库MmPfnDatabase,会不会是因为物理地址空间扩大后,这个页帧数据库并没有扩大,而导致映射4G以上物理内存时发生错误呢?
一番折腾,在MiInitMachineDependent函数中找到了如下代码
代码:
MmFreePagesByColor[0] = (PMMCOLOR_TABLES)&MmPfnDatabase[MmHighestPossiblePhysicalPage + 1];
代码:
lkd> dd MmPfnDatabase 8088b0c8 818c6000 0000ff00 00000006 0000003f lkd> dd MmHighestPossiblePhysicalPage 8088b120 00137fff 00137fff 000f7379 00000040
代码:
lkd> dt _MMPFN nt!_MMPFN +0x000 u1 : __unnamed +0x004 PteAddress : Ptr32 _MMPTE +0x008 u2 : __unnamed +0x00c u3 : __unnamed +0x010 OriginalPte : _MMPTE +0x018 u4 : __unnamed lkd> ?818c6000+(137fff+1)*1c Evaluate expression: -2085724160 = 83ae6000 lkd> dd MmFreePagesByColor 80886388 83ae6000 83ae6300 c0883000 f77fffff
3.谁动了我的物理地址?
感谢Geoff Chappell的一篇文章
《Licensed Memory in 32-Bit Windows Vista》
其中提到了微软官方关于Windows XP sp2以后版本无法显示全部物理内存的答复:
《The RAM reported by the System Properties dialog box and the System Information tool is less than you expect in Windows Vista or in Windows XP Service Pack 2 or later version》,
从这里又链接到完整版的介绍:
《Changes to Functionality in Microsoft Windows XP Service Pack 2
Part 3: Memory Protection Technologies》
其中讲到了
那么我们遇到的问题很可能就是DMA的问题了,因为在DMA传输过程中,DMA不经过页表映射,直接访问物理地址。经过我们的4G内存限制破解后,物理地址范围扩大到了4G以上,PAE最大可以寻址64G(36位物理地址),如果驱动在请求DMA向4G以上物理地址传输数据时,DMA就会将36位地址截断为32位地址,从而将数据传输到错误的内存地址,导致蓝屏,难道我们只有祈求驱动分配4G以下物理内存了吗?DMA硬件都只能接受32位地址,在碰到36位地址时就无能为力了吗?当然不能!
4.双缓冲DMA传输
5.难道是USB驱动不支持双缓冲映射?在向DMA写入物理地址时将36位物理地址截断了?那么逆向usb驱动,感谢tiamo对USB体系的分析
http://bbs3.driverdevelop.com/read.p...67&skinco=wind,
不然真是无从着手,USB数据传输时的调用堆栈如下:
代码:
b82540e0 f805a042 81fc29dc 81840c80 817e88f4 usbehci!EHCI_SubmitTransfer+0x53 b8254128 f805a74a 81fc2028 81840b08 804e3ec4 USBPORT!USBPORT_DmaEndpointActive+0x1f0 b8254154 f805cb7c 81fc2028 00000000 804e3ec4 USBPORT!USBPORT_DmaEndpointWorker+0x140 b825417c f805e4c3 81fc2028 00000003 00000001 USBPORT!USBPORT_CoreEndpointWorker+0x6d2 b82541f0 806f2a98 81fc2028 00000000 413e504d USBPORT!USBPORT_MapTransfer+0x76f b825421c 8052e551 821c7788 81fc205c 00000001 hal!HalAllocateAdapterChannel+0x126 b8254234 f805e6df 821c7788 81fc2028 00000001 nt!IoAllocateAdapterChannel+0x2a b8254278 f805f4d9 81fc2028 817adad0 804e3ec4 USBPORT!USBPORT_FlushMapTransferList+0x1b1
代码:
signed int __stdcall USBPORT_MapTransfer(PDEVICE_OBJECT DeviceObject, PIRP CurrentIrp, PVOID MapRegisterBase, PTRANSFER pTransfer)
TRANSLATION_ENTRY结构体如下
代码:
typedef struct _TRANSLATION_ENTRY { PVOID VirtualAddress; ULONG PhysicalAddress; ULONG Index; } TRANSLATION_ENTRY, *PTRANSLATION_ENTRY;
USBPORT_MapTransfer会调用hal!IoMapTransfer,hal!IoMapTransfer大致执行路径如下:
代码:
__int64 __stdcall IoMapTransfer(PADAPTER_OBJECT AdapterObject, PMDL Mdl, PVOID MapRegisterBase, PVOID CurrentVa, PULONG Length, BOOLEAN WriteToDevice) if ( MapRegisterBase ) { result = HalpMapTransfer(AdapterObject, Mdl, MapRegisterBase, CurrentVa, Length, WriteToDevice); } else { 计算缓冲区Mdl中的连续物理内存块,返回64位物理基址 } return result; }
在HalpMapTransfer中,
代码:
PHYSICAL_ADDRESS HalpMapTransfer( IN PADAPTER_OBJECT AdapterObject, IN PMDL Mdl, IN PVOID MapRegisterBase, IN PVOID CurrentVa, IN OUT PULONG Length, IN BOOLEAN WriteToDevice ) { if ((ULONG) MapRegisterBase & NO_SCATTER_GATHER && transferLength < *Length) { logicalAddress = translationEntry->PhysicalAddress + pageOffset; translationEntry->Index = COPY_BUFFER; index = 0; transferLength = *Length; useBuffer = TRUE; } if (useBuffer && WriteToDevice) { HalpCopyBufferMap( Mdl, translationEntry + index, CurrentVa, *Length, WriteToDevice ); } }
VMWare里调试一下,断点下在USBPORT_MapTransfer,MapRegisterBase还真为0,既然MapRegisterBase来自于nt!IoAllocateAdapterChannel,那么继续到这个函数中去寻找答案,在nt!IoAllocateAdapterChannel中发现如下代码:
代码:
if ( NumberOfMapRegisters && AdapterObject->NeedsMapRegisters ) { MasterAdapter = AdapterObject->MasterAdapter; MapRegisterBase = (int)((char *)MasterAdapter->MapRegisterBase + 12 * MapRegisterId); } else { AdapterObject->MapRegisterBase = 0; AdapterObject->NumberOfMapRegisters = 0; }
代码:
PADAPTER_OBJECT HalGetAdapter(PDEVICE_DESCRIPTION DeviceDescription, PULONG NumberOfMapRegisters)
在HalGetAdapter函数中发现了如下代码:
代码:
if ( ScatterGather && (LessThan16Mb || InterfaceType == Eisa || InterfaceType == PCIBus) ) { MapRegistersNeed = 0; }
代码:
if ( bScatterGather && (!HalpPhysicalMemoryMayAppearAbove4GB || DevDesc->Dma64BitAddresses) && (LessThan16Mb || InterfaceType == Eisa || InterfaceType == PCIBus) ) { MapRegistersNeed = 0; }
既然关键点找到了,那就patch吧jz改成jmp。此外还要注意Windows 2003在多个地方判断了HalpPhysicalMemoryMayAppearAbove4GB这个标志,涉及到修改的地方主要在HalInitSystem里面。由于XP原来主要工作于32位物理地址空间,现在地址空间变大,启用了双缓冲模式,所有相应参数要改大:
HalpMapBufferSize为映射缓存大小,原来为0x10000,改为0x30000
SizeOfBitMap为映射寄存器使用位图大小,原来为0x10,改为0x30
HalInitSystem调用了HalpAllocPhysicalMemory申请映射缓存,物理内存少于4G时,申请参数为
HalpAllocPhysicalMemory(LoaderBlock, 0x1000000u, 0x10u, 1);
即在物理地址0x1000000u以下的空间申请0x10u页,最后一个参数表示是否64k对齐。而当物理内存大于4G时,Windows 2003申请参数为
HalpAllocPhysicalMemory(LoaderBlock, 0xFFFFFFFFu, 0x30u, 1);
即在4G以下物理地址空间申请0x30u页,64k对齐。汇编代码改动结果如下:
代码:
push 1 push 10h push 1000000h push ebx mov DefaultSizeOfBitMap, 40h mov esi, 10000h call _HalpAllocPhysicalMemory@16
代码:
push 1 push 30h push FFFFFFFFh push ebx mov DefaultSizeOfBitMap, 4000h mov esi, 30000h call _HalpAllocPhysicalMemory@16
搞到最后,原来还是hal.dll的问题,还白费力气,研究了半天usb驱动。既然是hal.dll的问题,为什么硬盘、网卡等等这些使用DMA的设备就没有引发系统蓝屏呢?难道说这些驱动在一开始发起请求的时候就总是申请4G以下物理内存来进行DMA操作?或者是其他设备的DMA硬件已经都支持36位以上寻址了?还有待去考证。
总算搞完了,希望这次补丁能够更加稳定些,还有问题的话不知道什么时候再能去解决了。在此感谢我敬爱的领导兼导师,感谢他把我领进ROOTKIT的大门,感谢他的包容和理解,感谢他对我年轻气盛的容忍和悉心开导。不玩了,好好工作,努力干活。