gxmgf
驱动牛犊
驱动牛犊
  • 注册日期2003-10-17
  • 最后登录2007-03-08
  • 粉丝0
  • 关注0
  • 积分50分
  • 威望5点
  • 贡献值0点
  • 好评度5点
  • 原创分0分
  • 专家分0分
阅读:2861回复:18

用汇编写个最小的WDM驱动程序在2000/XP/2003里进入RING0

楼主#
更多 发布于:2003-10-23 22:10
在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
push dx
sgdt fword ptr [esp]
pop cx
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的工具。


                                                                  作者:   MGF
                                                                     mgf2001@163.net

最新喜欢:

juventusjuvent...
gantleman
驱动小牛
驱动小牛
  • 注册日期2003-10-03
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分13分
  • 威望127点
  • 贡献值1点
  • 好评度18点
  • 原创分0分
  • 专家分0分
沙发#
发布于:2004-08-10 21:06
好贴!好多的疑惑顿然的解 :D
yukinzh
驱动牛犊
驱动牛犊
  • 注册日期2004-08-07
  • 最后登录2004-09-12
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
板凳#
发布于:2004-08-10 20:45
谢谢wowocock!!!!!!!!!谢谢!!!
wowocock
VIP专家组
VIP专家组
  • 注册日期2002-04-08
  • 最后登录2016-01-09
  • 粉丝16
  • 关注2
  • 积分601分
  • 威望1651点
  • 贡献值1点
  • 好评度1227点
  • 原创分1分
  • 专家分0分
地板#
发布于:2004-08-10 09:19
RET 后返回的是CALLGATE调用的下一条指令,不过其运行在RING0中,CS是8,后面返回RING3是将RING3返回地址入堆栈,通过RETF返回。
花开了,然后又会凋零,星星是璀璨的,可那光芒也会消失。在这样 一瞬间,人降生了,笑者,哭着,战斗,伤害,喜悦,悲伤憎恶,爱。一切都只是刹那间的邂逅,而最后都要归入死亡的永眠
mptchenx
驱动牛犊
驱动牛犊
  • 注册日期2004-07-03
  • 最后登录2010-01-06
  • 粉丝0
  • 关注0
  • 积分18分
  • 威望2点
  • 贡献值0点
  • 好评度1点
  • 原创分0分
  • 专家分0分
地下室#
发布于:2004-08-10 03:14
32位代码段描述符中的段base被初始化成00000000

3级调用门中又指定代码段的偏移量~~ 就是GDT base 所以CALL之后就

执行GDT base中的ret指令~~~ 把CALL的下条指令地址PUSH到EIP中,就

然执行call的下条指令,不知道有没理解正确?可是ret只后不就返回

ring3了吗? 为什么call的下条指令会在ring0中?


去年的帖子。。~

[编辑 -  8/10/04 by  mptchenx]
chengtao
驱动牛犊
驱动牛犊
  • 注册日期2002-11-30
  • 最后登录2020-03-25
  • 粉丝0
  • 关注0
  • 积分11分
  • 威望78点
  • 贡献值0点
  • 好评度17点
  • 原创分0分
  • 专家分0分
  • 社区居民
5楼#
发布于:2004-08-09 22:38
难得一见的好贴,顶。
wowocock
VIP专家组
VIP专家组
  • 注册日期2002-04-08
  • 最后登录2016-01-09
  • 粉丝16
  • 关注2
  • 积分601分
  • 威望1651点
  • 贡献值1点
  • 好评度1227点
  • 原创分1分
  • 专家分0分
6楼#
发布于:2004-08-09 09:23
这里用了个TRICK,调用门里OFFSET指向GDT的NULL DESCRIPTOR,X86系列处理器规定NULL DESCRIPTOR不能用来进行内存访问,但里面可以放些自己的东西,我在里面放了条RET指令,这样CALLGATE就会执行到他,然后RET到CALLGATE的下一条指令初,就是RING0所在的地方,所以最好退出前必须往堆栈里再压一条RING3返回地址才行,自己调试下就很清楚了,嘿嘿。。。。。。
花开了,然后又会凋零,星星是璀璨的,可那光芒也会消失。在这样 一瞬间,人降生了,笑者,哭着,战斗,伤害,喜悦,悲伤憎恶,爱。一切都只是刹那间的邂逅,而最后都要归入死亡的永眠
yukinzh
驱动牛犊
驱动牛犊
  • 注册日期2004-08-07
  • 最后登录2004-09-12
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
7楼#
发布于:2004-08-07 13:01
我不懂这个调用门的offset为什么是gdt的base,call了之后是怎么执行到
Ring0:
mov eax,esp ;save ring0 esp
mov esp,[esp+4];->ring3 esp
push eax
这里的呢?我是菜鸟啊,谁帮帮忙??
gxmgf
驱动牛犊
驱动牛犊
  • 注册日期2003-10-17
  • 最后登录2007-03-08
  • 粉丝0
  • 关注0
  • 积分50分
  • 威望5点
  • 贡献值0点
  • 好评度5点
  • 原创分0分
  • 专家分0分
8楼#
发布于:2003-11-04 12:25
不重新启动装载.sys的例子:

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


include windows.inc
include kernel32.inc
include user32.inc
include gdi32.inc
include advapi32.inc
includelib kernel32.lib
includelib user32.lib
includelib gdi32.lib
includelib advapi32.lib


.data
szServiceName db \'mywdm25\',0            ;只要这个名字不重复,驱动程序装载成功的希望就很大!
szDisplayName db \'mywdm25 Driver\',0     ;只要这个名字不重复,驱动程序装载成功的希望就很大!
szFileName db \'c:\\windows\\system32\\drivers\\mywdm.sys\',0
hSCManager dd 0
hService dd 0


.code
start:
nop
nop
nop
nop

invoke OpenSCManager,0,0,000f000fh
mov hSCManager,eax

;00010030H=SERVICE_START OR SERVICE_STOP OR DELETE的组合码,用SERVICE_ALL_ACCESS
;好象不行
invoke CreateService,eax,offset szServiceName,offset szDisplayName,00010030h,1,1,0,offset szFileName,0,0,0,0,0
mov hService,eax

invoke StartService,eax,0,0


invoke DeleteService,hService

invoke CloseServiceHandle,hService

invoke CloseServiceHandle,hSCManager

invoke ExitProcess,0


end start

gxmgf
驱动牛犊
驱动牛犊
  • 注册日期2003-10-17
  • 最后登录2007-03-08
  • 粉丝0
  • 关注0
  • 积分50分
  • 威望5点
  • 贡献值0点
  • 好评度5点
  • 原创分0分
  • 专家分0分
9楼#
发布于:2003-10-31 20:37
大家看了以后帮顶一下呀!这么好的帖子难道就这么让它沉下去?
gxmgf
驱动牛犊
驱动牛犊
  • 注册日期2003-10-17
  • 最后登录2007-03-08
  • 粉丝0
  • 关注0
  • 积分50分
  • 威望5点
  • 贡献值0点
  • 好评度5点
  • 原创分0分
  • 专家分0分
10楼#
发布于:2003-10-27 21:40
这就是我手工减肥后的mywdm.sys驱动程序,只保留了最基本的东西,其他的一律去掉!
大小400字节。

FileHex v1.42              (■ File Hex Fixor ■)                 Nov 26, 1994

Filename : mywdm.sys [400 bytes]
Sector [000000]
- Offset - ---------------- Hex codes --------------------  ----- ASCII ----
0000(0000) 4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00  MZÉ ♥   ♦    
0016(0010) B8 00 00 00 00 00 00 00 50 45 00 00 4C 01 01 00  ㄙ       PE  L☺☺
0032(0020) 00 00 00 00 00 00 00 00 00 00 00 00 E0 00 0E 01              α ♫☺
0048(0030) 0B 01 05 0C 50 00 00 00 50 00 00 00 18 00 00 00  ♂☺♣♀P   P   ↑
0064(0040) 40 01 00 00 40 01 00 00 40 01 00 00 00 00 40 00  @☺  @☺  @☺    @
0080(0050) 01 00 00 00 01 00 00 00 04 00 00 00 00 00 00 00  ☺   ☺   ♦
0096(0060) 04 00 00 00 00 00 00 00 00 10 00 00 40 01 00 00  ♦        ►  @☺
0112(0070) 95 46 00 00 01 00 00 20 00 00 10 00 00 10 00 00  òF  ☺     ►  ►
0128(0080) 00 00 10 00 00 10 00 00 00 00 00 00 10 00 00 00    ►  ►      ►
0144(0090) 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0160(00A0) 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0176(00B0) 00 00 00 00 00 00 00 00 88 01 00 00 08 00 00 00          ê☺  ◘
0192(00C0) 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0208(00D0) 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0224(00E0) 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0240(00F0) 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0256(0100) 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0272(0110) 2E 74 65 78 74 00 00 00 50 00 00 00 40 01 00 00  .text   P   @☺
0288(0120) 50 00 00 00 40 01 00 00 00 00 00 00 00 00 00 00  P   @☺
0304(0130) 00 00 00 00 20 00 00 68 00 00 00 00 00 00 00 00         h
0320(0140) 90 9C 60 52 66 52 0F 01 04 24 66 59 5A 8B C2 0F  É£`RfR☼☺♦$fYZï┬☼
0336(0150) B7 C9 80 E1 F0 C6 00 C3 66 89 04 11 C1 E8 10 66  ㄚㄘÇß≡ㄢ ├fë♦◄┴Φ►f
0352(0160) 89 44 11 06 C7 44 11 02 08 00 00 EC 01 4C 11 02  ëD◄♠ㄣD◄☻◘  ∞☺L◄☻
0368(0170) C7 44 11 08 FF FF 00 00 C7 44 11 0C 00 9A CF 00  ㄣD◄◘    ㄣD◄♀ Ü
gxmgf
驱动牛犊
驱动牛犊
  • 注册日期2003-10-17
  • 最后登录2007-03-08
  • 粉丝0
  • 关注0
  • 积分50分
  • 威望5点
  • 贡献值0点
  • 好评度5点
  • 原创分0分
  • 专家分0分
11楼#
发布于:2003-10-27 12:01
呵呵,我都测试了好几个月了!
没有经过测试,我哪敢发到网上呀!
wowocock
VIP专家组
VIP专家组
  • 注册日期2002-04-08
  • 最后登录2016-01-09
  • 粉丝16
  • 关注2
  • 积分601分
  • 威望1651点
  • 贡献值1点
  • 好评度1227点
  • 原创分1分
  • 专家分0分
12楼#
发布于:2003-10-27 09:17
GDT里是可以看到的,老大,别说你自己没测试过??
顺便帮你做了利用IDT来实现的例子。
.586p
.model flat,stdcall
option casemap:none

HOOKINT EQU 20H
.code
start:
nop
nop
nop
nop

pushfd
pushad

push edx
sgdt  [esp-2]
pop edx
mov eax,edx ;EDX.EAX->GDT BASE
mov ecx,3e8h

.if dword ptr [eax+ecx+4]!=00cf9a00h ;3e8 ring0 code32
mov byte ptr [eax],0c3h ;ret
mov dword ptr [eax+ecx],0000ffffh
mov dword ptr [eax+ecx+4],00cf9a00h
.endif

push edx
sidt  [esp-2]
pop edx
add edx,HOOKINT*8 ;edx->int 20h

.if dword ptr [edx+2]!=0ee0003e8h;int 20 gate ->gdt base(ret)
mov dword ptr[edx+2],0ee0003e8h
mov word ptr [edx],ax
shr eax,16
mov word ptr [edx+6],ax
.endif

popad
popfd

xor eax,eax
ret 8


end start
RIN3测试程序
.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)

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

start:
push  offset ExceptCallBack
call   SetUnhandledExceptionFilter
int 20h           ;use intgate to Ring0!

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

mov eax,cr0

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

Exit:
invoke ExitProcess,NULL

end start
花开了,然后又会凋零,星星是璀璨的,可那光芒也会消失。在这样 一瞬间,人降生了,笑者,哭着,战斗,伤害,喜悦,悲伤憎恶,爱。一切都只是刹那间的邂逅,而最后都要归入死亡的永眠
mxxlj
驱动牛犊
驱动牛犊
  • 注册日期2003-10-22
  • 最后登录2003-10-25
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
13楼#
发布于:2003-10-25 16:08
好文章,难得啊!
mxx
mxx
gxmgf
驱动牛犊
驱动牛犊
  • 注册日期2003-10-17
  • 最后登录2007-03-08
  • 粉丝0
  • 关注0
  • 积分50分
  • 威望5点
  • 贡献值0点
  • 好评度5点
  • 原创分0分
  • 专家分0分
14楼#
发布于:2003-10-24 13:52
wowocock在GDT里看到调用门没有?能够进入RING0吗?

如果不切换堆栈,不使用自己的0级32位代码段,就会经常遭遇
页异常。使用了自己的堆栈和0级32位代码段,几个月来我还没
有碰到过页异常,以后会不会碰到,还不清楚。

如果你能把你的代码移到80000000H以上的地址,你就可以使用
WINDOWS的堆栈和0级32位代码段了。我也不清楚WINDOWS是如何
这样保护自己的,好象是设了CR0或CR4的某个位吧!
wowocock
VIP专家组
VIP专家组
  • 注册日期2002-04-08
  • 最后登录2016-01-09
  • 粉丝16
  • 关注2
  • 积分601分
  • 威望1651点
  • 贡献值1点
  • 好评度1227点
  • 原创分1分
  • 专家分0分
15楼#
发布于:2003-10-24 10:24
写了个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

还有想问下,是否有必要切换堆栈和使用自己的CS,不用的话好象有时候可能有问题,是不是用了自己的,在任何情况下都不会有页异常??
花开了,然后又会凋零,星星是璀璨的,可那光芒也会消失。在这样 一瞬间,人降生了,笑者,哭着,战斗,伤害,喜悦,悲伤憎恶,爱。一切都只是刹那间的邂逅,而最后都要归入死亡的永眠
arthurtu
驱动巨牛
驱动巨牛
  • 注册日期2001-11-08
  • 最后登录2020-12-19
  • 粉丝0
  • 关注0
  • 积分26分
  • 威望161点
  • 贡献值0点
  • 好评度35点
  • 原创分0分
  • 专家分0分
  • 社区居民
16楼#
发布于:2003-10-24 09:58
/subsystem:windows
不是native?怎么是windows?
KMK
KMK
驱动大牛
驱动大牛
  • 注册日期2001-09-12
  • 最后登录2017-10-06
  • 粉丝2
  • 关注0
  • 积分42分
  • 威望404点
  • 贡献值2点
  • 好评度58点
  • 原创分1分
  • 专家分1分
  • 社区居民
17楼#
发布于:2003-10-23 23:17
这边也很多 !!
http://www.yates2k.net/syscode.htm
wowocock
VIP专家组
VIP专家组
  • 注册日期2002-04-08
  • 最后登录2016-01-09
  • 粉丝16
  • 关注2
  • 积分601分
  • 威望1651点
  • 贡献值1点
  • 好评度1227点
  • 原创分1分
  • 专家分0分
18楼#
发布于:2003-10-23 22:44
不错,难得找到汇编的志同道合者,支持啊。
花开了,然后又会凋零,星星是璀璨的,可那光芒也会消失。在这样 一瞬间,人降生了,笑者,哭着,战斗,伤害,喜悦,悲伤憎恶,爱。一切都只是刹那间的邂逅,而最后都要归入死亡的永眠
游客

返回顶部