首页
社区
课程
招聘
[分享]自己的kmd驱动ring0 参考的wowocock前辈的方法,做了一点注释
发表于: 2008-1-28 01:39 14414

[分享]自己的kmd驱动ring0 参考的wowocock前辈的方法,做了一点注释

2008-1-28 01:39
14414

为了尊重wowocock前辈 我把wowocock的代码贴在我代码后面。
毕竟是发表我自己的吧.  在看完wowocock大哥的代码后。因为有一些地方
说得不是很明白 所以自己动手测试了一次 并自己写了一个类试的代码。
加上了比较详细的注释 希望大家看起来不费力
废话不多说了。代码开始 大家可以保存为.bat文件.调试是设置了编译器环境变量。
代码分为驱动代码 装载驱动的程序代码 和测试代码
下面为驱动部分。
;@echo off
;goto make

.386
.model flat, stdcall
option casemap:none
;>>>>>>>>>>>>>>>STRUCT>>>>>>>>>>>>>>>>>>>>>>>>>>>>
CALLGATE                STRUCT
OFFSETL                DW                0
SELECTOR                DW                0
DCOUNT                DB                0
GTYPE                        DB                0
OFFSETH                DW                0
CALLGATE                ENDS

DESCRIPTOR        STRUCT
LIMITL                DW                0
BASEL                        DW                0
BASEM                        DB                0
ATTRIBUTES        DW                0
BASEH                        DB                0
DESCRIPTOR        ENDS
;>>>>>>>>>>>>>>>>>equ>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
CODES_SEL        =        3E8H
CODE_TYPE        =        0CF9AH
GATE_TYPE        =        0ECH

.code
DriverEntry:
                                pushfd
                                pushad
                               
                                push edx
                                sgdt [esp-2]
                                pop edx                                ;全局描述符表基地址
                                mov eax,edx                        ;存放全局描述符开始地址
                                mov BYTE ptr [edx],0C3H
                                ;在全句描述符表开始地址
                                ;存放retn指令机器码,全
                                ;句描述符表第一个描述符
                                ;为空表述符,被保留,没被用
                                ;书上说这个描述符应该全为0
                                ;这里看出 可以写东西进去。
                                mov ecx,3E0h                ;定位自己的门描述符
                                add edx,ecx                        ;让描述位置在全局描述
                                assume edx:ptr CALLGATE;符的3E0这个位置
                                cmp [edx].SELECTOR,CODES_SEL
                                jz ToEnd
                               
                                mov [edx].OFFSETL,ax                        ;创建门描述符
                                mov [edx].SELECTOR,CODES_SEL
                                mov [edx].DCOUNT,0
                                mov [edx].GTYPE,GATE_TYPE
                                shr eax,16
                                mov [edx].OFFSETH,ax
                                ;描述符的偏移地址其实就是上面说到的
                                ;那个retn指令地址,当call 这个门的时候
                                ;只从堆栈弹出EIP地址,所以CS寄存器没被
                                ;覆盖,所以还是ting0权限
                               
                                add edx,8
                                assume edx:ptr DESCRIPTOR
                                mov [edx].LIMITL,0FFFFH         ;创建自己的代码段描述符
                                mov [edx].BASEL,0
                                mov [edx].BASEM,0
                                mov [edx].ATTRIBUTES,CODE_TYPE
                                mov [edx].BASEH,0
ToEnd:
                                popad
                                popfd
                                mov eax,0C0000182h
                                ret 8
                                end DriverEntry
;如果设置了环境变量 就把代码直接保存为mykmd.bat 然后双击 就OK
;没有设置环境变量的可以按下面方法编译,具体编译方法不用我介绍了吧
                               
:make

set drv=mykmd

ml /nologo /c /coff %drv%.bat

link /nologo /driver /base:0x10000 /align:32 /out:%drv%.sys /subsystem:native %drv%.obj

del %drv%.obj

echo.

pause

;这里开始为测试代码部分
;@echo off
;goto make

.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
szOK                                db                '成功访问rc0寄存器',0
szFormat                        db                'cr0=%X',0
szBuffer                        db                50 dup (?)
CallGate_Sel        dd                0
                                        dw                03E3H        ;调用门的选择子
.code
start:
                call FWORD ptr CallGate_Sel
               
                ;进入ring0
                ;有兴趣自己也可以创建一个0级堆栈 呵呵。。
                ;我这里就用ring3的堆栈吧
                mov eax,esp  ;保存ring0堆栈
                mov esp,[esp+4] ;装入ring3堆栈地址
                ;解释下这里为什么是+4 因为call 调用门会把
                ;ss esp cs eip分别放入堆栈 在驱动程序里面
                ;已经有一句retn指令把eip弹出来了。所以这里
                ;就是加4 得到ring3堆栈地址
               
                push eax
               
                mov eax,cr0        ;ring0下才可以访问的寄存器
               
                pop esp
                ;;;开发返回ring3
                push offset ring3
                ;正如刚刚所说 已经弹出了eip地址。
                ;用retf返回肯定出错。所以还是手
                ;动帮忙来恢复堆栈 压入ring3下的
                ;代码地址吧
                retf
ring3:
                invoke        wsprintf,addr szBuffer,addr szFormat,eax
                invoke        MessageBox,0,addr szBuffer,addr szOK,0
                invoke        ExitProcess,0
                end start

:make
set drv=s
ml /nologo /c /coff %drv%.bat
link /nologo /subsystem:windows %drv%.obj
del %drv%.obj
echo.
pause

最后 前辈这里没有提到的。.装载驱动的程序。
;@echo off
;goto make

.386
.model flat, stdcall
option casemap:none

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

.data
szError                        db                        '装载驱动失败',0
szOK1                                db                        '驱动安装成功',0
szOK                                db                        'OH YEAH.~~!',0
szMyWdm                        db                        'mykmd.sys',0
szMyWdnName                db                        'mykmd',0
szBuffer                        db                        MAX_PATH dup (0)

hSCManager                dd                        ?
hService                        dd                        ?
.code
start:
                invoke        OpenSCManager,NULL,NULL,SC_MANAGER_CREATE_SERVICE
                .if !eax
                        invoke        MessageBox,0,addr szError,0,MB_ICONERROR or MB_OK
                        jmp Err
                .endif
                mov hSCManager,eax
                push ecx
                invoke        GetFullPathName,addr szMyWdm,sizeof szBuffer,addr szBuffer,esp
                pop ecx
                invoke        CreateService,hSCManager,addr szMyWdnName,addr szMyWdm,SERVICE_START or DELETE,SERVICE_KERNEL_DRIVER,SERVICE_DEMAND_START,SERVICE_ERROR_IGNORE,addr szBuffer,0,0,0,0,0
                .if !eax
                        invoke        MessageBox,0,addr szError,0,MB_ICONERROR
                        jmp Err
                .endif
                mov hService,eax
                invoke        StartService, hService, 0, NULL
                invoke        DeleteService, hService
                invoke        CloseServiceHandle, hService
                invoke        CloseServiceHandle, hSCManager
                invoke        MessageBox,0,addr szOK1,addr szOK,0
Err:
                invoke        ExitProcess,0
                end start

;如果设置了环境变量 就把代码直接保存为LoadDriver.bat 然后双击 就OK
;没有设置环境变量的可以按下面方法编译,具体编译方法不用我介绍了吧
                               
:make
set drv=LoadDriver
ml /nologo /c /coff %drv%.bat
link /nologo /subsystem:windows %drv%.obj
del %drv%.obj
echo.
pause

;说明下 如果用bat文件自动编译  把驱动代码改成mykmd.bat 测试代码改为s.bat  装载驱动程序代码改为LoadDriver.bat 分别双击就OK


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 7
支持
分享
最新回复 (15)
雪    币: 18
活跃值: (2079)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
2
下面给出wowocock大哥的代码.
其中测试部分的几个字符串定义可能忘了加' ' 两个单引号。
为了尊重作者..我还是没改动代码。请大家自己加上吧.
顺便说下。如果转载 我的代码给大家参考 所以随便怎么都可以
但是 wowocock的文章 请尊重下作者 把文章COPY全
谢谢

原著: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病毒了。

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=337881

[点击此处收藏本文]   jarodlau发表于 2005年04月06日 05:44:00

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=890537
2008-1-28 01:42
0
雪    币: 321
活跃值: (271)
能力值: ( LV13,RANK:1050 )
在线值:
发帖
回帖
粉丝
3
好文,收藏,感谢楼主分享
2008-1-28 09:29
0
雪    币: 321
活跃值: (271)
能力值: ( LV13,RANK:1050 )
在线值:
发帖
回帖
粉丝
4
写的精彩,禁不住再顶,青出于蓝而胜于蓝。。。
2008-1-28 09:36
0
雪    币: 102
活跃值: (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
5
wowocock 是楚狂人... 为人谦逊..不知道居住在上海何处
2008-1-28 23:35
0
雪    币: 6075
活跃值: (2236)
能力值: (RANK:1060 )
在线值:
发帖
回帖
粉丝
6
我多年前没有看懂这个代码,今天终于看懂了,顶
2008-1-29 02:46
0
雪    币: 709
活跃值: (2420)
能力值: ( LV12,RANK:1010 )
在线值:
发帖
回帖
粉丝
7
谢谢分享呀.
2008-1-29 09:31
0
雪    币: 332
活跃值: (30)
能力值: ( LV12,RANK:460 )
在线值:
发帖
回帖
粉丝
8
这么多大虾都说好,当然一读
2008-1-29 12:25
0
雪    币: 18
活跃值: (2079)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
9
谢谢大家的支持。感谢给我的这篇文章的加精. 给我带来了更大的学习动力
.
2008-1-29 20:50
0
雪    币: 248
活跃值: (42)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
很感动呀!!!
2008-1-30 11:28
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
好,谢谢----
2008-1-31 10:28
0
雪    币: 372
活跃值: (36)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
12
还是不懂 努力努力~~~
2008-3-5 17:43
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
好东西,顶!!!
2008-3-5 23:15
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
看到好贴,忍不住要顶一下的!
2008-3-6 01:18
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
dem
15
不知道10年后能看懂不
2008-3-21 16:14
0
雪    币: 104
活跃值: (73)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
太好了 呵呵 深入浅出 顶一个
2008-7-30 11:07
0
游客
登录 | 注册 方可回帖
返回
//