现在的位置: 首页 > 综合 > 正文

[转载]用汇编写个最小的WDM驱动程序进RING0

2013年11月17日 ⁄ 综合 ⁄ 共 3670字 ⁄ 字号 评论关闭
[ 字体: ]
2004年6月12日15:09星期六

原著:wowocock
http://www.luocong.com/bbs/dispbbs.asp?boardid=2&id=3176

用汇编写个最小的WDM驱动程序进RING0
在WINDOWS 2000/XP/2003里进入RING0是很多人的梦想,但实现这个梦想并不容易,
因为基于NT内核的 WINDOWS 2000/XP/2003对自己保护得非常好,这与WINDOWS 9X
有很大不同,WINDOWS 9X的GDT,IDT,LDT等重要的系统表格是可以被RING3的代码
轻易修改的,因此在WINDOWS 9X里任何程序都可以在GDT,IDT,LDT里构造自己的
调用门/中断门/陷阱门而进入RING0。在WINDOWS 2000/XP/2003里似乎只有写驱动
才能进入RING0了,当然还可以利用系统漏洞进入RING0,本人就发现了2个漏洞可
进入RING0,但本文只讨论写驱动的方式。

现在介绍用汇编语言写普通WINDOWS应用程序的书也不少,用汇编语言写WINDOWS
程序的程序员也越来越多,但遗憾的是这些书介绍的程序都是运行在RING3上的,
我等技术追求者怎能受制于RING3而无所作为?为了写出令人惊叹的程序(比如病毒,
反病毒,防火墙等等),就必须要进入RING0。但是有关用汇编语言写WDM驱动程序
的书和例子非常少,我找到的绝大多数都是用C写的,而且编译也很麻烦,对于汇编
语言这种最适合写系统程序的语言来说是很遗憾的。为此我特意用我所知道的知识
写出本文,意在抛砖引玉,共同进步。

好了,先来看看下面的例子:

.586p
.model flat,stdcall
option casemap:none

.code
start:
nop
nop
nop
nop

pushfd
pushad

push edx
sgdt [esp-2]
pop edx
mov eax,edx
mov ecx,3e0h

.if dword ptr [edx+ecx+2]!=0ec0003e8h
mov byte ptr [edx],0c3h

mov word ptr [edx+ecx],ax
shr eax,16
mov word ptr [edx+ecx+6],ax
mov dword ptr [edx+ecx+2],0ec0003e8h

mov dword ptr [edx+ecx+8],0000ffffh
mov dword ptr [edx+ecx+12],00cf9a00h
.endif

popad
popfd

xor eax,eax
ret 8

end start

把以上的汇编代码保存到文件mywdm.asm,然后用MASM 6.14按照下面的方法编译:

ml /c /coff /Cp mywdm.asm
link /subsystem:windows /driver:wdm /release /out:mywdm.sys mywdm.obj

就会在当前目录下生成一个mywdm.sys文件,这是一个没有导入导出和资源的纯代码
驱动程序,非常短小精悍。如果导入了ntoskrnl.exe和hal.dll里的函数,或者导出
给别人调用的函数,它将是一个功能全面的WDM驱动。

mywdm.sys的功能是在GDT中创建一个3级调用门和一个0级32位代码段描述符,3级调用
门的选择子是3E3,0级32位代码段的选择子是3E8。

好了,现在执行mywdm.sys,但驱动程序是不能直接执行的,所以要构造它的执行环境。
先在注册表HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services中建立一个mywdm
子键,然后建立3个DWORD型的键值:
ErrorControl=0
Start=3 (如果Start=1,mywdm.sys会随电脑启动而自动装入)
Type=1

再把mywdm.sys复制到system32/drivers目录里,然后重新启动(好象不重新启动也可以,
对这个我不太清楚),运行net start mywdm,结果屏幕上显示:

“发生系统错误 1058。

无法启动服务,原因可能是已被禁用或与其相关联的设备没有启动。”

如果你轻信微软的这句话的话,你就到此为止了!别被微软欺骗,因为我们的代码已经
执行过了,3级调用门和0级32位代码段描述符已经被建立在GDT中,而错误提示是在我们
的代码执行后弹出来的,此时不管是成功提示还是错误提示对我们来说都已太晚了。不信,
你可以用SOFT ICE看一看GDT就知道我所言不虚!如果Start=1,系统不会提示错误而运行
mywdm.sys。

好了,既然GDT中已经有了我们的调用门,那么意味着我们写的应用程序可以自由地在
RING3和RING0之间切换,但如何使用这个调用门还是有讲究的,它的使用方法是:

...

call 3e3:00000000 ;机器码 9A 00 00 00 00 E3 03
;此时已经进入RING0,CS=3E8,跟着要切换堆栈
mov eax,esp
mov esp,[esp+4]
push eax

;这里加入你要在RING0里执行的代码

;准备返回RING3
pop esp
push offset ring3
retf
ring3:
;此时回到RING3

...

为什么要切换堆栈?为什么要建立我们自己的0级32位代码段?直接使用操作系统的0级32位
代码段(选择子=8)不可以吗?在这里我要说明一下:WINDOWS 2000/XP/2003对自己保护得
非常好,就算普通应用程序能进入RING0,但还是在用户区(代码和数据的地址都小于80000000H),
所以WINDOWS 2000/XP/2003会认为是非法进入RING0的,就有可能产生页故障,表现是调用子程序
或访问内存时就会触发页故障,解决的方法是切换堆栈;不用操作系统的0级32位代码段(选择子
为8),用我们建立的0级32位代码段(选择子=3E8)。

本文例子生成的mywdm.sys大小是1536字节,如果你对PE文件很了解,可以手工给mywdm.sys减肥,
但要注意的是减肥后要把正确的CHECKSUM值填入PE头的CHECKSUM字段中,否则操作系统是不会装入
mywdm.sys执行的。我就把mywdm.sys减肥到只有368字节(只有DOS头,PE头+节表,60H字节的重定
位表和代码,应该是世界上最小的驱动程序了)。完全可以把这个只有368字节的驱动程序包含在
自己的程序中,在需要是把它还原到SYSTEM32/DRIVERS目录里。

计算CHECKSUM的方法是:把PE文件的所有字用ADC相加,然后得到的结果再和文件大小ADC相加,
这个结果就是CHECKSUM值。网上也可以找到计算CHECKSUM的工具。

RING3测试程序如下:
.386
.model flat, stdcall
option casemap:none
include /masm32/include/windows.inc
include /masm32/include/kernel32.inc
include /masm32/include/user32.inc
includelib /masm32/lib/user32.lib
includelib /masm32/lib/kernel32.lib

.data
szExceptionCaused db Exception Caused - could not switch to ring 0,0
szError    db Error,0
MsgCaption      db Test,0
MsgBoxText      db cr0=%8x,0
tmp db 50 dup(90)
Callgt dd 0
      dw 3e3h

.code
ExceptCallBack PROC
invoke    MessageBoxA, 0, addr szExceptionCaused,addr szError, 0
invoke ExitProcess, -1
ret
ExceptCallBack ENDP

start:  
push  offset ExceptCallBack
call   SetUnhandledExceptionFilter
call    fword ptr [Callgt]            ;use callgate to Ring0!

Ring0:
mov eax,esp   ;save ring0 esp
mov esp,[esp+4];->ring3 esp
push eax

mov eax,cr0

pop esp   ;restore ring0 esp
push offset Ring3
retf
Ring3:
invoke wsprintfA,addr tmp,addr MsgBoxText,eax
invoke MessageBox, NULL,addr tmp, addr MsgCaption, MB_OK

Exit:
invoke ExitProcess,NULL

end start
嘿嘿,大家以后就可以方便的写2K/XP/2003下的RING0病毒了。

抱歉!评论已关闭.