一、工具准备
反编译工具:IDA Pro
编辑工具:WinHex
依赖分析工具:Dependency Walker(随WinDDK安装)
PE工具:PE.Tools
证书工具:makecert(随WinDDK安装)
cat工具:inf2cat(随WinDDK安装)
签名工具:如沃通、亚洲诚信或signtool(随WinDDK安装)
调试工具:WinDbg(随WinDDK安装)(本例暂用不到)
事件日志工具:tracelog(随WinDDK安装)(本例暂用不到)
二、环境准备
1. 配置环境变量
变量名称:_NT_SYMBOL_PATH
cache后面的路径是符号文件存储本地的路径,已下载的符号文件,IDA Pro / WinDbg / x64dbg可以自动读取。
2. 准备证书(如果没有证书,则创建测试签名证书,它所签名的驱动模块只能在“测试模式”下加载)
3. 测试模式
显示已有启动项({current}为当前启动项):
bcdedit
复制启动项:
bcdedit /copy {current} /d "Windows 7 DEBUG"
开启测试模式:
bcdedit /set {xxx} testsigning yes
关闭测试模式:
bcdedit /deletevalue {xxx} testsigning
4. 动态调试环境(本例暂用不到)
(1)硬件准备
有COM口主板的主机2台,一台是调试机(debugger),另一台是被控机(debuggee)。
准备串口线,debuggee必须为COM接口,debugger可以是COM接口或USB模拟的COM接口。
(2)debuggee启动选项准备
打开调试:
bcdedit /set {xxx} debugtype serial
bcdedit /set {xxx} debugport 1
bcdedit /set {xxx} baudrate 115200
bcdedit /set {xxx} debug yes
关闭调试:
bcdedit /set {xxx} debug no
5. debuggee事件日志分析(本例暂用不到)
若动态调试不可行(例如debuggee是笔记本),还可以通过收集事件日志分析问题。
三、分析与修改步骤
1. 取得Win10驱动程序包
inf版本号2023.56.0502.2017。
2. 分析Win10驱动程序包
netrtwlane.inf 安装信息
rtwlane.sys 内核驱动(IHV driver)
netrtwlane.cat 安全目录
Rtlihvs.dll 用户模式WLAN扩展模块(WLANEXT.EXE加载)
rtldata.txt 未知
3. inf文件处理
将netrtwlane.inf复制为netrtwlane-1-NTAMD-HW.inf,接下来对netrtwlane-1-NTAMD-HW.inf进行修改。
(1)解除操作系统版本限制
引用
[Manufacturer]
%Realtek% = Realtek,NTamd64.10.0
[ControlFlags]
ExcludeFromSelect = *
[Realtek.NTamd64.10.0]
引用结束
NTamd64.10.0代表Win10,上述inf只能在Win10下安装。将'NTamd64.10.0'改为'NTamd64.6.1'或'NTamd64'后,即可在Win7或其他NT系列64位操作系统安装。
(2)防范连接耗时1分钟隐患
在每个支持型号区段里,都有下列类似语句。
引用
[RTL8723be.ndi.NT.HW]
Include = netvwifibus.inf
Needs = VWiFiBus.PnPFilterRegistration.Hw
AddReg = RTWlanE.MSI.AddReg
引用结束
其中'VWiFiBus.PnPFilterRegistration.Hw'是netvwifibus.inf(Win10)中的语句,给当前无线网卡设置UpperFilters过滤驱动——vwifibus。而在netvwifibus.inf(Win7)中并没有'VWiFiBus.PnPFilterRegistration.Hw',相同功能的区段叫做'VWiFiBus.PnPFilterRegistration'。需要将' VWiFiBus.PnPFilterRegistration.Hw'全部替换为'VWiFiBus.PnPFilterRegistration'。
如果没有修改此处,无线网卡不会具有UpperFilters过滤驱动——vwifibus。如果意外开启了承载网络(hosted networking),由于wlansvc.dll和其他模块处理问题(此问题分析起来较为复杂,用户态和内核态模块都涉及),会导致无线网卡耗时1分钟方可连接。
4. IHV driver处理
对于IHV driver——rtwlane.sys,会进行多步修改,在每一步都复制上一步产生的文件,如果出错可以快速恢复。
(1)去除原签名
将rtwlane.sys复制为rtwlane-1-nsec.sys(表示即将去除签名)。
使用PE Tools载入rtwlane-1-nsec.sys,点击“Directories”,发现“Security Directory”有签名数据,点击“R”按钮后保存即可(截图“R”、“Save”按钮不可用是因为文件属性为只读)。
接下来将对IHV driver进行patch处理,将刚才保存的rtwlane-1-nsec.sys复制为rtwlane-2-asm.sys(表示即将进行patch)。接下来用rtwlane-1-nsec.sys作为源文件进行分析,如果有任何patch操作,将rtwlane-2-asm.sys作为目标文件。
说明:截图中如有文件名称,是之前做的截图,还是应该按照文字内容去选择文件。
(2)导入表(IMPORTS)分析
使用Dependency Walker载入rtwlane-1-nsec.sys,可以看到,IHV driver需要的ExGetFirmwareEnvironmentVariable、KeInitializeSpinLock函数在Win7 ntoskrnl.exe中未导出,NdisMDeregisterWdiMiniportDriver、NdisMRegisterWdiMiniportDriver函数在Win7原版ndis.sys中未导出,而名字带“Wdi”的导入函数就是IHV driver基于WdiWiFi开发的最主要特征。
标注问号的模块表示找不到,这些模块可能在\Windows\system32\drivers中。如果环境变量Path添加“C:\Windows\system32\drivers”,则可以更快捷地判别。说明:截取下图时,系统里已经是修改后的ndis.sys,所以ndis.sys的导入函数没有报告不存在。
(3)security_cookie处理
使用IDA载入rtwlane-1-nsec.sys。
真正的DriverEntry位于62BE4处,__security_init_cookie在62BF4处被调用。
__security_init_cookie函数检查__security_cookie数值,不能为0,也不能为初始值2B992DDFA232h,否则会调用int 29h失败。
【patch - 1】因此,需要在66A019处将jz指令改为nop。定位到66A019处,使用Edit - Patch program - Change byte…,输入90 90,确定。
查看Edit - Patch program - Patched bytes,可以发现刚才输入的patch已被记录。
说明:直接跳过__security_init_cookie函数调用也可以,有多种方法:__security_init_cookie函数入口66A000直接改为retn,或上级函数call __security_init_cookie指令改为5个nop等。
(4)检查KMDF、NDIS版本要求
①KMDF
在62C7F处,WdfVersionBind被调用,r8参数指向KMDF绑定信息。
在394664处,表明所需的KMDF版本号为1.13。该版本的KMDF在Win8.1中被引入,因此需先行将版本不低于1.13的KMDF移植到Win7(常用版本有Win10初始版本1.15,Win10最终版本1.31,Win11初始版本1.33)(事实上当移植WdiWiFi.sys时,该条件已具备,因为它要求的KMDF版本是1.15)。
②NDIS——向ndis.sys注册小端口驱动程序时要求的NDIS版本号
在3E2A4处,NdisGetVersion被调用,此处可以看到,如果系统中安装的ndis.sys报告的版本号 < 6.50,此函数会返回失败;版本号检查通过后,IHV driver将版本号填入NdisMRegisterWdiMiniportDriver结构体的版本需求成员中——NdisGetVersion返回哪个版本,驱动程序就要求哪个版本,此要求一定会被ndis.sys接受,所以在结构体填充方面无需处理。
【patch - 2】因此,仅需将3E2A9处的jb指令改为nop。
(5)NdisGetVersion其他调用地点分析
NdisGetVersion()返回ndis.sys版本号,而IHV driver对ndis.sys版本号的需求,除了在调用NdisMRegisterWdiMiniportDriver时提供给ndis.sys外,还会在驱动代码内部根据版本号采取不同的行为。由于我们要将驱动程序移植到Win7中,该函数一定会返回0x60014的结果(代表NDIS6.20)。厂商在编写代码时,有的地方根据NDIS版本采取不同的兼容处理,有的地方则不予考虑直接走向失败。从下面内容可以看到,厂商所假设的兼容处理(厂商是站在Win10的视角去开发驱动程序的)不一定是好事,有时候会带来崩溃风险。
从“Imports”窗口找到NdisGetVersion函数,定位在IAT处。
根据它的xref可以看出,有一个JMP指令专门调用NdisGetVersion,定位过去(暂且称为“j_NdisGetVersion”)。
根据j_NdisGetVersion的xref可以看出,有4处调用。分别分析如下。
①248E2
判断NDIS版本号后返回不同数据,暂不处理。
②3E29D
调用NdisMRegisterWdiMiniportDriver之前的版本号判断,已在上文(4)中处理。
③3F937
此处属于sub_14003F8E0函数,对当前ndis.sys版本号进行判断,如果小于0x60032(NDIS6.50),调用NdisMDeregisterMiniportDriver;否则调用NdisMDeregisterWdiMiniportDriver。显而易见,sub_14003F8E0函数执行的是驱动程序退出时的清理动作,取消注册小端口驱动。向上追溯,可以分析出,sub_14003F8E0函数是在sub_140047CC0函数中,填充到MiniportDriverCharacteristics->UnloadHandler位置,然后提交到NdisMRegisterWdiMiniportDriver注册小端口驱动的,证实sub_14003F8E0函数确实是清理函数,对应UnloadHandler回调。
这个兼容处理看似符合操作系统状态——当NDIS版本低于6.50(Win10初始版本)时,调用NdisMDeregisterMiniportDriver(ndis.sys各版本支持)去清理;否则,调用NdisMDeregisterWdiMiniportDriver(ndis.sys在Win10生命周期各版本支持)去清理。但是,实质上我们是将驱动程序从Win10移植到Win7中使用的,既然注册小端口驱动时采用了NdisMRegisterWdiMiniportDriver,清理时理应采用对应的NdisMDeregisterWdiMiniportDriver——我们已经通过修改6.20版本的ndis.sys,实现了6.50版本的ndis.sys的WDI支持。
【patch - 3】因此,这里版本号如果小于0x60032,依然应该去调用NdisMDeregisterWdiMiniportDriver,所以3F948这里的jb指令应该进行nop处理。
【深入分析】如果这里不处理,当禁用网卡后再启用时,存在一定概率的BSOD风险。原理如下。驱动程序注册小端口驱动时调用NdisMRegisterWdiMiniportDriver,WdiWiFi.sys会将小端口驱动对象插入自身的全局链表,然后才提交给ndis.sys去注册(也插入ndis.sys的全局链表)。如果禁用无线网卡,驱动程序清理时调用NdisMDeregisterMiniportDriver,ndis.sys会将小端口驱动对象从它的全局链表中删除,但是WdiWiFi.sys没有删除的机会。无线网卡禁用后,IHV driver会从内存中销毁。当无线网卡再次启用时,IHV driver再次被加载到内存中,WdiWiFi.sys又将小端口驱动对象插入全局链表,出现实际只有一个无线网卡驱动,而WdiWiFi.sys的链表中存在多个驱动对象的现象(前面的对象对应已被销毁的IHV driver实例)。当WdiWiFi.sys调用IHV driver相关入口函数时,如果正好ndis.sys提供的NDIS_HANDLE和禁用网卡前的一致, WdiWiFi.sys会调用到刚才已被销毁的IHV driver实例的地址。如果IHV driver再次加载的地址和销毁前的地址不同,会导致WdiWiFi.sys调用到无效地址,造成内存访问冲突或代码跑飞,出现蓝屏死机。
④4A870
取得结果后填入结构体,暂不处理。
(6)缺失函数处理
一方面是函数名称的修改(在IDA中不修改,后续用WinHex完成);另一方面是调用处的处理,防止驱动程序失败。
对于KeInitializeSpinLock,改名为RtlRunOnceInitialize,调用处不修改。这两个函数的效果一样:将参数1指向的QWORD内存地址清零 - and qword ptr [rcx], 0。
【patch - 4】对于ExGetFirmwareEnvironmentVariable,只有11F3BF一处调用,将call指令改为向eax赋值C0000100(变量不存在)即可(向上追溯,该函数调用失败并不影响驱动整体运行)。为防止加载失败,ExGetFirmwareEnvironmentVariable函数名称还应该改为Win7中存在的函数名称,按照可能调用-最小影响原则,选用__chkstk,该函数在Win7中没有任何动作,直接返回。
对于NdisMDeregisterWdiMiniportDriver、NdisMRegisterWdiMiniportDriver,使用修改后的ndis.sys即可,无需处理。
(7)可能的动态获取函数入口分析
内核模式下常用的动态获取函数入口的函数是MmGetSystemRoutineAddress,网络类驱动程序也可以调用ndis.sys提供的NdisGetRoutineAddress。它们相当于用户态的GetProcAddress函数。因此,在“Imports”中搜索“Routine”,看IHV driver动态获取了哪些函数,是不是有Win7不支持的,如果不支持,IHV driver又是如何应对的。
可以发现只有MmGetSystemRoutineAddress被调用,对它的每一处xref相关的函数进行分析后发现,IHV driver并没有去获取Win7不支持的函数入口。所以每一处均无需处理。
(8)应用patch
综上,我们进行了4处patch。
在IDA中,使用Edit - Patch program - Apply patches to input file…,选择目标文件rtwlane-2-asm.sys,应用patch。
(9)修改导入函数名称
将刚才保存的rtwlane-2-asm.sys复制为rtwlane-3-IAT.sys(表示即将修改IAT函数名称)。
使用WinHex载入rtwlane-3-IAT.sys,把'ExGetFirmwareEnvironmentVariable'字符串修改为'__chkstk\0',将'KeInitializeSpinLock'字符串修改为'RtlRunOnceInitialize',保存。如果字符串找到多个结果,需要结合PE Tools判断输入表对应字符串位置进行修改。
使用Dependency Walker载入刚才保存的rtwlane-3-IAT.sys,将会发现,不支持函数仅剩Wdi两个函数(如果类似下图,本机已使用修改后的ndis.sys,则没有不支持函数)。
(待补充图片)
(10)重新签名
将rtwlane-3-IAT.sys复制为rtwlane-3-IAT-----ts.sys(表示即将添加测试签名)。
使用签名工具对rtwlane-3-IAT-----ts.sys签名。此为IHV driver修改成品。
5. 制作安装包
按照原驱动程序包结构,将各文件放置在同一文件夹中。netrtwlane.inf可以随意命名*.inf。但由于我们并没有修改netrtwlane.inf内容中的文件名,它在安装时还是会寻找rtwlane.sys。故我们需将rtwlane-3-IAT-----ts.sys改名为rtwlane.sys。netrtwlane.cat是安全目录,因为我们修改了netrtwlane.inf和rtwlane.sys,它已经无效,可以删除。文件夹中将包含下列4个文件。
netrtwlane-1-NTAMD-HW.inf(名称可以是任意*.inf)
rtwlane.sys
Rtlihvs.dll
rtldata.txt
(1)生成cat文件
inf2cat.exe /driver:【驱动目录文件夹】 /os:7_X64
运行后,新的netrtwlane.cat被生成。
(2)签名cat文件
使用签名工具对刚生成的netrtwlane.cat签名。签名cat文件所使用的证书和是否需要打开“测试模式”无关。
四、安装方法及运行测试
1. 总体安装步骤
在“设备管理器”找到无线网卡(如果未安装驱动程序,是名称为“网络控制器”的代码28的设备),选中后进行“更新驱动程序软件”操作。如果该设备已经安装了驱动程序,应先进行“卸载”操作,选中“删除此设备的驱动程序软件”(注意,事先在“属性”-“详细信息”-“服务”中记录【服务名称】,“卸载”操作完成后,运行sc delete 【服务名称】,防止新的驱动程序由于服务重复而安装失败)。如果此对话框看不到“删除此设备的驱动程序软件”,证明没有安装过驱动程序,无需进行“卸载”操作。
(待补充图片)
2. 常规安装方法
从【驱动目录文件夹】目录中搜索,自动安装。
3. 从磁盘安装
如果常规安装方法安装失败或提示已经是最新版本,则在“更新驱动程序软件”中依次选择“浏览计算机以查找驱动程序软件”-“从计算机的设备驱动程序列表中选择”-(可能经过此步骤)“列出所有设备”-“从磁盘安装”-选中inf文件,强制安装。
4. 运行效果
除了WLAN成功连接外,事件查看器还会指示用户模式WLAN扩展模块加载情况。如果加载失败,说明驱动包中的*.dll需要修改或者安装到了错误的位置。