且行且远
分类: 逆向手札 由 ssfighter 于 2011年4月14日 发表

最近在做RNDIS设备的开发,这方面的资料网上都比较少,周围也找不到人可以问,只好自己对着MSDN的文档硬啃,不过却在起跑线上栽了个大跟头,经过分析之后,基本确定是一个Windows RNDIS驱动的小bug,在此写下来以作记录,同时也希望能帮到遇到同样诡异问题的人。

现象:
我们的USB设备是复合设备,除RNDIS功能之外还有其他的功能,我用IAD来将RNDIS的两个Interface聚合,同时,RNDIS的两个Interface紧接在已有的Interface之后,其接口编号是02、03。在按照MSDN上的inf文件修改过之后,插入设备后可以正确地找到驱动,但安装完驱动之后立刻蓝屏重启,估计是驱动程序初始化时候出现的错误。

分析:
RNDIS的驱动分为两层,一个是rndismpx.sys,一个是usb8023x.sys,前者实现RNDIS协议栈的功能,后者实现USB-RNDIS的枚举、通信等功能。这是因为RNDIS设备本身是总线无关的,只是目前只有USB-RNDIS的功能,但驱动仍然是分层设计的。用WinDBG打开死机后DUMP出来的log文件,可以看到系统崩溃在以下位置:

eax=00000000 ebx=893461b8 ecx=870da99b edx=870da99b esi=870da958 edi=86dd3870
eip=a6c39216 esp=ba507684 ebp=ba507698 iopl=0         nv up ei pl zr na pe nc
cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00010246
usb8023x!SelectConfiguration+0x9a:
a6c39216 8a4805          mov     cl,byte ptr [eax+5]        ds:0023:00000005=??
Resetting default scope

DEFAULT_BUCKET_ID:  DRIVER_FAULT

BUGCHECK_STR:  0x7E

LAST_CONTROL_TRANSFER:  from a6c39472 to a6c39216

STACK_TEXT:
ba507698 a6c39472 00000000 86dd3870 00000000 usb8023x!SelectConfiguration+0x9a
ba5076b0 a6c39aad 86dd3870 896c8de4 86d57738 usb8023x!InitUSB+0x32
ba5076c4 ba3e115b 86e22ecc 86e22efc 86e22cf8 usb8023x!RndisInitializeHandler+0x2d
ba507724 b9dd4dea ba507760 ba507768 b9dd0200 RNDISMPX!RndismpInitialize+0x2eb
ba5078dc b9dd49cc 8a0d9d88 ba507900 ba5079a8 NDIS!ndisMInitializeAdapter+0x3b7
ba5079b0 b9dd48ba 8a0d9d88 00000000 896dc648 NDIS!ndisInitializeAdapter+0xb9
ba5079e4 b9dd5daf 86ddb698 86ddb7bc 86ddb698 NDIS!ndisPnPStartDevice+0xd6
ba507a14 ba3e5ed6 86d57638 86ddb698 86ddb7e0 NDIS!ndisPnPDispatch+0x306
ba507a30 804ef19f 86d57638 00000000 ba507aac RNDISMPX!PnPDispatch+0x4c

可以看到,是在USB初始化检查配置描述符的时候出错的,于是赶紧检查自己的USB配置描述符,并未发现问题,只好用IDA打开usb8023x.sys,进行逆向分析,定位在系统崩溃的位置:

.text:00011210 call ds:__imp__USBD_ParseConfigurationDescriptor@12 ; USBD_ParseConfigurationDescriptor(x,x,x)
.text:00011216 mov cl, [eax+5] ; 系统崩溃处!
.text:00011216 ; 返回值eax为零,造成内存访问错误,导致系统崩溃。

可以发现,usb8023x.sys在调用完解析配置描述符的函数之后,并没有判断是否解析成功。查询WDK文档可知,USBD_ParseConfigurationDescriptor的返回值是一个指针,指向要查询的接口描述符的首字节。如果找不到该接口的编号,则返回NULL。在这里,驱动没有找到我们的接口描述符,又没有进行失败检验,因此造成了内存访问错误,系统崩溃。可是我的接口描述符明明是有的啊,继续往上看:

.text:000111FA and [ebp+arg_0], 0 ; Interface number从0开始
.text:000111FE add ebx, 18h
.text:00011201 cmp byte ptr [esi+4], 0 ; 有几个Interface?
.text:00011205 mov [ebp+var_8], ebx
.text:00011208 jbe short loc_11233
.text:0001120A
.text:0001120A loc_1120A: ; CODE XREF: SelectConfiguration(x)+B5j
.text:0001120A push 0 ; AlternateSetting
.text:0001120C push [ebp+arg_0] ; Interface number
.text:0001120F push esi ; Configuration Descriptors
.text:00011210 call ds:__imp__USBD_ParseConfigurationDescriptor@12 ; USBD_ParseConfigurationDescriptor(x,x,x)
.text:00011216 mov cl, [eax+5] ; 系统崩溃处!
.text:00011216 ; 返回值eax为零,造成内存访问错误,导致系统崩溃。

可以知道,esi=870da958处保存着配置描述符。[ebp+arg_0]是接口的编号,usb8023x.sys将从接口0开始查找,可是我的RNDIS设备是接口2、3开始的,在WinDBG中看看esi处的数据:

0: kd> db 870da958
870da958  09 02 43 00 02 01 04 c0-00 09 04 02 00 01 02 02  ..C.............
870da968  ff 00 05 24 00 20 01 05-24 01 00 01 04 24 02 00  ...$. ..$....$..
870da978  05 24 06 02 03 07 05 83-03 08 00 01 09 04 03 00  .$..............
870da988  02 0a 00 00 00 07 05 84-02 40 00 00 07 05 04 02  .........@......
870da998  40 00 00 00 00 00 00 00-0a 00 05 0a 46 53 66 6d  @...........FSfm

其中前9个字节的配置描述符并不是从我的设备上传上来的,应该是Windows驱动在对复合设备进行描述符分析的时候自动生成的,可是后面的具体的接口、CDC、端点描述符确是直接memcpy过来的,而usb8023x.sys首先从接口0开始解析,然而接口0是不存在的,自然造成系统的崩溃。

解决:
这个问题主要是usb8023x.sys认定RNDIS的接口必须从0开始,而且驱动没有进行函数返回值的判断,造成的系统崩溃。解决方法也很简单,只需要把自己的设备的RNDIS的接口放到最前面即可。当然,如果USB设备不是复合设备,只有RNDIS一个功能,那么这些问题应该都不会遇到。

转载请注明出处,谢谢。



牙牙
2011年04月14日 21:23:34

看完之后我昏过去了。呵呵~~
很期待看看谁会第一个遇到同样的问题。

Rn
2011年05月3日 22:03:42

楼主好强,都反汇编了
是我们学习的典范

shengcm
2011年12月16日 11:35:30

楼主你好,我遇到的问题和你一样,不知道端口怎么改?楼主能帮忙吗

ssfighter
2011年12月16日 17:23:52

@shengcm
改USB配置描述符

shengcm
2011年12月19日 09:48:49

我的问题是unable to enumerate USB device on port 2,不能枚举到端口2,我是把开发板作为从机和PC通信,用的是RNDIS,不知道怎么把端口改到0或1?

ssfighter
2011年12月19日 15:32:04

@shengcm
我想你指的端口和我说的是两回事,请先看看USB的基本资料。

mj0011
2013年03月17日 17:22:21

最近我也遇到类似的问题,XP貌似就是这样了,WIN7里已经修复这个问题了,调Parse后会判断是否为空

aozima
2013年04月3日 18:12:16

膜拜一下楼主,记住了。

发表评论

昵称:  (必须)
邮件:  (必须)
网址: 
评论: