阅读:2861回复:18
用汇编写个最小的WDM驱动程序在2000/XP/2003里进入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 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 |
|
最新喜欢:![]() |
沙发#
发布于:2004-08-10 21:06
好贴!好多的疑惑顿然的解 :D
|
|
板凳#
发布于:2004-08-10 20:45
谢谢wowocock!!!!!!!!!谢谢!!!
|
|
地板#
发布于:2004-08-10 09:19
RET 后返回的是CALLGATE调用的下一条指令,不过其运行在RING0中,CS是8,后面返回RING3是将RING3返回地址入堆栈,通过RETF返回。
|
|
|
地下室#
发布于: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] |
|
5楼#
发布于:2004-08-09 22:38
难得一见的好贴,顶。
|
|
6楼#
发布于:2004-08-09 09:23
这里用了个TRICK,调用门里OFFSET指向GDT的NULL DESCRIPTOR,X86系列处理器规定NULL DESCRIPTOR不能用来进行内存访问,但里面可以放些自己的东西,我在里面放了条RET指令,这样CALLGATE就会执行到他,然后RET到CALLGATE的下一条指令初,就是RING0所在的地方,所以最好退出前必须往堆栈里再压一条RING3返回地址才行,自己调试下就很清楚了,嘿嘿。。。。。。
|
|
|
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 这里的呢?我是菜鸟啊,谁帮帮忙?? |
|
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 |
|
9楼#
发布于:2003-10-31 20:37
大家看了以后帮顶一下呀!这么好的帖子难道就这么让它沉下去?
|
|
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◄♀ Ü |
|
11楼#
发布于:2003-10-27 12:01
呵呵,我都测试了好几个月了!
没有经过测试,我哪敢发到网上呀! |
|
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 |
|
|
13楼#
发布于:2003-10-25 16:08
好文章,难得啊!
mxx |
|
|
14楼#
发布于:2003-10-24 13:52
wowocock在GDT里看到调用门没有?能够进入RING0吗?
如果不切换堆栈,不使用自己的0级32位代码段,就会经常遭遇 页异常。使用了自己的堆栈和0级32位代码段,几个月来我还没 有碰到过页异常,以后会不会碰到,还不清楚。 如果你能把你的代码移到80000000H以上的地址,你就可以使用 WINDOWS的堆栈和0级32位代码段了。我也不清楚WINDOWS是如何 这样保护自己的,好象是设了CR0或CR4的某个位吧! |
|
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,不用的话好象有时候可能有问题,是不是用了自己的,在任何情况下都不会有页异常?? |
|
|
16楼#
发布于:2003-10-24 09:58
/subsystem:windows
不是native?怎么是windows? |
|
17楼#
发布于:2003-10-23 23:17
这边也很多 !!
http://www.yates2k.net/syscode.htm |
|
18楼#
发布于:2003-10-23 22:44
不错,难得找到汇编的志同道合者,支持啊。
|
|
|