阅读:1786回复:10
一个汇编版的NDIS驱动程序模板
;文章作者:mgf
;一个汇编版的NDIS驱动程序模板(by mgf @2007.02.23) ; 写驱动程序已经很久了,但我一直用汇编来写驱动程序,原因是我不喜欢DDK,因为DDK和汇编一样,不是象VC,C#那样的“可视化”的开发工具, ;DDK和汇编一样看不见结构或对象的成员,也看不到各种函数的原型,这些东西和各种常量定义还必须去查看相关的包含文件,不仅如此,DDK还会 ;“自作聪明”地把很多垃圾代码加到我的驱动里,虽然没有影响驱动程序的功能,但是还是不爽,不如直接用汇编,生成的驱动程序是什么样的自己 ;心里有数。其实这也怪不得MS,它不想大家了解它的操作系统核心技术,但是又不得不开放一些技术细节给大家写驱动,所以只有提高门槛,搞出这 ;么一个变态的DDK来吓走很多人,它的目的也就达到了,就是让尽可能少的人了解它的核心技术。能够跨过DDK这道槛的,必定不是一般的高手。呵呵! ; 很多书都建议写驱动最好不用汇编,因为写出来的程序不可移植,但是我不这么认为,因为现在90%的人写出的驱动都是在PC上运行的,没有必要 ;为了这10%而放弃这么好的汇编语言。而且计算机都是相通的,如果你会写PC下的驱动了,再写MAC下的驱动不就容易多了吗? ; 这是一个标准的legacy驱动程序,虽然编译时用WDM的方法编译,它有Unload,Read,Write例程,而且还用NDIS HOOK来部分实现了防火墙的功能。 ;NDIS HOOK技术被广泛应用于瑞星、江民、天网等众多流行的防火墙里,它的原理是使用NdisRegisterProtocol()注册假协议来获取系统的协议链地址, ;再搜索这个单向协议链,找到TCPIP协议的NDIS_OPEN_BLOCK(NDIS_BINDING_HANDLE)结构,里面就有TCPIP协议的各种派发函数地址,比如SEND在结构 ;偏移30H的地方,RECV在结构偏移40H的地方,还有其他很多重要的函数地址都在这个表里。把表里的地址替换成我们的函数地址,就可以拦截操作系统 ;收发的所有数据包了。本驱动只是实现了嗅探数据包的功能,还没有实现拦截恶意数据包的功能,如果想实现这个功能,在_Write函数、_mySend函数、 ;_myRecv函数里加些代码,使它们配合完成拦截数据包的功能。本驱动如果IoAttachDevice其他驱动,可以改成过滤器;做些修改,还可以实现更多功能, ;可以用来过滤缓冲区溢出攻击包,也可以用来定制自己的SQL INJECTION防护网。 ; 本驱动嗅探数据包的流程是这样的:当系统收发数据包时,被_mySend或_myRecv拦截,这两个函数就用NdisSetEvent()置位一个RING3事件(这个事 ;件在DriverEntry里建立,在RING3里打开),这样在RING3应用层的WaitForSingleObject()因为事件被置位而返回,于是RING3代码用ReadFile()读取数 ;据包的内容,判断是放行还是拦截,再用WriteFile来通知驱动做相应的操作。当然为了提高效率,不是每个数据包都提示RING3应用程序该不该放行, ;有些可以直接在RING0驱动里判断的就不必传到RING3去判断了。处于RING3的代码大概是这样的: ;#include "stdafx.h" ;#include "windows.h" ;#include "stdlib.h" ;int _tmain(int argc, _TCHAR* argv[]) ;{ ; HANDLE hdrv=CreateFile("\\\\.\\NDISDRV",0xc0000000,0,0,3,0,0); ; HANDLE hEvent=OpenEvent(0x100000,0,"Send3Event"); //注意Send3Event必须和驱动里的名字对应 ; //HANDLE hEvent=OpenEvent(0x100000,0,"Recv3Event"); //注意Recv3Event必须和驱动里的名字对应 ; ; int dwTempVar=1;char szTempBuffer[0x800]; ; while(dwTempVar) ; { ; WaitForSingleObject(hEvent,-1); ; ReadFile(hdrv,szTempBuffer,0x800,(LPDWORD)&dwTempVar,0); ; } ; ; return 0; ;} ;把下面的代码保存到ndisdrv.asm里,在MASM32 6.14及以上的编译器里编译: ;ml /c /coff /Cp ndisdrv.asm ;link /subsystem:native /driver:wdm /release /align:16 /base:0x10000 /out:ndisdrv.sys ndisdrv.obj ;由于驱动程序涉及的知识太广而深奥了,本文难免有错漏的地方,希望大家指出,以免一错再错,误导别人。 ;下面是驱动的代码: .586p .model flat,stdcall option casemap:none assume fs:nothing,gs:nothing includelib ntoskrnl.lib includelib hal.lib includelib ndis.lib includelib tdi.lib includelib wdm.lib ;以上LIB文件要从DDK里复制到MASM32的LIB目录里 NdisQueryBufferSafe proto :dword,:dword,:dword,:dword NdisInitializeEvent proto :dword NdisSetEvent proto :dword NdisResetEvent proto :dword NdisWaitEvent proto :dword,:dword NdisRegisterProtocol proto :dword,:dword,:dword,:dword NdisDeregisterProtocol proto :dword,:dword IoCreateDevice proto :dword,:dword,:dword,:dword,:dword,:dword,:dword IoDeleteDevice proto :dword IoCreateSymbolicLink proto :dword,:dword IoDeleteSymbolicLink proto :dword IoCompleteRequest proto :dword,:dword IoCreateNotificationEvent proto :dword,:dword PsGetVersion proto :dword,:dword,:dword,:dword RtlCompareUnicodeString proto :dword,:dword,:dword ZwClose proto :dword ;由于MASM32没有相关的INC文件,所以要使用的API要自己定义原型 .data lpProtocolHandle dd 0 ;协议句柄的指针 lpDeviceObject dd 0 ;设备对象的指针 lpOldSend dd 0 ;保存TCPIP协议驱动的OPEN_BLOCK(BINDING HANDLE)里的SEND派发函数地址 lpOldRecv dd 0 ;保存TCPIP协议驱动的OPEN_BLOCK(BINDING HANDLE)里的RECV派发函数地址 lpSend3Event dd 0 ;RING3发送事件在本驱动的指针 hSend3Event dd 0 ;RING3发送事件在本驱动的句柄 lpRecv3Event dd 0 ;RING3接收事件在本驱动的指针 hRecv3Event dd 0 ;RING3接收事件在本驱动的句柄 dwStatus dd 0 dwTempVar dd 0 obSendEvent db 16 dup(0) ;RING0 SEND对象 obRecvEvent db 16 dup(0) ;RING0 RECV对象 stProtocolChar db 70h dup(0) ;NdisRegisterProtocol()要使用的NDIS_PROTOCOL_CHARACTERISTIC结构 szSendBuffer db 800h dup(0) ;系统将要发送的数据包的副本 szRecvBuffer db 800h dup(0) ;系统将要接收的数据包的副本 dwSendSize dd 0 ;发送副本大小 dwRecvSize dd 0 ;接收副本大小 .const stTcpip dw 5*2,6*2 dd offset szTcpip szTcpip dw 'T','c','p','i','p',0 stProtName dw 7*2,8*2 dd offset szProtName szProtName dw 'N','d','i','s','D','r','v',0 stDeviceName dw 15*2,16*2 dd offset szDeviceName szDeviceName dw '\','D','e','v','i','c','e','\','N','d','i','s','D','r','v',0 stSymbolicLinkName dw 19*2,20*2 dd offset szSymbolicLinkName szSymbolicLinkName dw '\','D','o','s','D','e','v','i','c','e','s','\','N','D','I','S','D','R','V',0 stSend3Event dw 28*2,29*2 dd offset szSend3Event szSend3Event dw '\','B','a','s','e','N','a','m','e','d','O','b','j','e','c','t','s','\','S','e','n','d','3','E','v','e','n','t',0 stRecv3Event dw 28*2,29*2 dd offset szRecv3Event szRecv3Event dw '\','B','a','s','e','N','a','m','e','d','O','b','j','e','c','t','s','\','R','e','c','v','3','E','v','e','n','t',0 .code start proc DriverObject,RegisterPath pushad invoke PsGetVersion,offset dwTempVar,0,0,0 ;获取操作系统版本,如果不是5.0(2000,XP,2003)就退出,因为不能使用NDIS5.0 .if dwTempVar!=5 jmp _exit .endif mov byte ptr stProtocolChar,5 mov byte ptr stProtocolChar+1,0 mov eax,dword ptr stProtName mov dword ptr stProtocolChar+30h,eax mov eax,dword ptr stProtName+4 mov dword ptr stProtocolChar+34h,eax mov dword ptr stProtocolChar+08h,offset PtOpenAdapterComplete mov dword ptr stProtocolChar+0ch,offset PtCloseAdapterComplete mov dword ptr stProtocolChar+10h,offset PtSendComplete mov dword ptr stProtocolChar+14h,offset PtTransferDataComplete mov dword ptr stProtocolChar+18h,offset PtResetComplete mov dword ptr stProtocolChar+1ch,offset PtRequestComplete mov dword ptr stProtocolChar+20h,offset PtReceive mov dword ptr stProtocolChar+24h,offset PtReceiveComplete mov dword ptr stProtocolChar+28h,offset PtStatus mov dword ptr stProtocolChar+2ch,offset PtStatusComplete mov dword ptr stProtocolChar+3ch,offset PtBindAdapter mov dword ptr stProtocolChar+40h,offset PtUnbindAdapter mov dword ptr stProtocolChar+38h,offset PtReceivePacket mov dword ptr stProtocolChar+44h,offset PtPNPHandler ;以上是填充NDIS_PROTOCOL_CHARACTERISTIC结构 invoke NdisRegisterProtocol,offset dwStatus,offset lpProtocolHandle,offset stProtocolChar,6ch ;获取系统协议链表的地址,前面做了这么多工作都是为了这个地址 .if dwStatus jmp _exit .endif mov ebx,lpProtocolHandle mov ebx,[ebx+10h] ;跳过我的假协议,保存协议链里下一项的地址 invoke NdisDeregisterProtocol,offset dwStatus,lpProtocolHandle ;我注册的假协议已经没有用了,注销 mov lpProtocolHandle,ebx ;真正的系统协议链的地址 mov esi,offset stTcpip .repeat lea edi,[ebx+44h] invoke RtlCompareUnicodeString,edi,esi,1 .break .if eax==0 mov ebx,[ebx+10h] .until ebx==0 ;这个循环是搜索TCPIP协议的PROTOCOL_HANDLE .if eax jmp _exit .endif mov ebx,[ebx] ;取出PROTOCOL_HANDLE里的OPEN_BLOCK指针,每个版本的NDIS的PROTOCOL_HANDLE结构都可能不同,要注意 mov lpProtocolHandle,ebx ;保存TCPIP协议的OPEN_BLOCK(BINDING_HANDLE) mov eax,[ebx+30h] mov lpOldSend,eax ;保存TCPIP协议的OPEN_BLOCK(BINDING_HANDLE)里的SEND派发函数地址 mov eax,[ebx+40h] mov lpOldRecv,eax ;保存TCPIP协议的OPEN_BLOCK(BINDING_HANDLE)里的RECV派发函数地址 invoke IoCreateDevice,DriverObject,18h,offset stDeviceName,21h,0,0,offset lpDeviceObject ;建立设备,DeviceExtension size=18h, type= device_transfer invoke IoCreateSymbolicLink,offset stSymbolicLinkName,offset stDeviceName mov eax,lpDeviceObject or dword ptr [eax+1ch],10h ;把device.flag设置为DO_DIRECT_IO,使驱动程序的READ、WRITE例程直接映射用户缓冲区到本驱动 mov edi,DriverObject add edi,38h mov ecx,1ch mov eax,offset _CommonIoControl rep stosd ;填充共用例程,必须,否则CreateFile()不能打开本驱动 mov eax,DriverObject mov dword ptr [eax+34h],offset _Unload mov dword ptr [eax+44h],offset _Read mov dword ptr [eax+48h],offset _Write ;注册驱动例程 mov byte ptr [eax+8],2 ;强行修改Driver.flag为legacy driver,否则DriverEntry返回时系统就会卸载本驱动(因为本驱动默认编译为WDM drvier) invoke NdisInitializeEvent,offset obSendEvent invoke NdisInitializeEvent,offset obRecvEvent invoke NdisResetEvent,offset obSendEvent invoke NdisResetEvent,offset obRecvEvent ;建立RING0事件 invoke IoCreateNotificationEvent,offset stSend3Event,offset hSend3Event mov lpSend3Event,eax invoke NdisResetEvent,eax invoke IoCreateNotificationEvent,offset stRecv3Event,offset hRecv3Event mov lpRecv3Event,eax invoke NdisResetEvent,eax ;建立RING3事件,在RING3用OpenEvent()打开 mov ebx,lpProtocolHandle mov dword ptr [ebx+30h],offset _mySend ;HOOK TCPIP协议的SEND mov dword ptr [ebx+40h],offset _myRecv ;HOOK TCPIP协议的RECV _exit: popad xor eax,eax ret start endp _Unload proc DriverObject mov edx,lpProtocolHandle mov eax,lpOldSend mov [edx+30h],eax mov eax,lpOldRecv mov [edx+40h],eax ;恢复TCPIP协议的OPEN_BLOCK里原来的SEND/RECV派发例程 invoke ZwClose,hSend3Event invoke ZwClose,hRecv3Event invoke NdisSetEvent,offset obSendEvent invoke NdisSetEvent,offset obRecvEvent invoke IoDeleteSymbolicLink,offset stSymbolicLinkName invoke IoDeleteDevice,lpDeviceObject xor eax,eax ret _Unload endp _CommonIoControl proc DeviceObject,pIrp mov eax,pIrp mov dword ptr [eax+18h],0 mov dword ptr [eax+1ch],0 invoke IoCompleteRequest,pIrp,0 xor eax,eax ret _CommonIoControl endp _Read proc DeviceObject,pIrp pushad mov ebx,pIrp mov edi,[ebx+4] mov ecx,[edi+18h] mov edi,[edi+10h] add edi,ecx ;EDI=用户缓冲区 mov ecx,lpSend3Event mov edx,lpRecv3Event .if dword ptr [ecx+4] invoke NdisResetEvent,lpSend3Event ;RING3事件复位,防止再次放行 mov esi,offset szSendBuffer mov ecx,dwSendSize mov eax,[ebx+60h] .if ecx>[eax+4] mov ecx,[eax+4] .endif .else invoke NdisResetEvent,lpRecv3Event mov esi,offset szRecvBuffer mov ecx,dwRecvSize mov eax,[ebx+60h] .if ecx>[eax+4] mov ecx,[eax+4] .endif .endif mov dword ptr [ebx+18h],0 mov dword ptr [ebx+1ch],ecx ;设置ReadFile()的读取字节数 rep movsb ;把数据包复制到ReadFile()提供的缓冲区 invoke IoCompleteRequest,pIrp,0 popad xor eax,eax ret _Read endp _Write proc DeviceObject,pIrp mov eax,pIrp mov dword ptr [eax+18h],0 mov dword ptr [eax+1ch],0 mov edx,[eax+4] mov ecx,[eax+60h] mov ecx,[ecx+4] mov eax,[edx+18h] mov edx,[edx+10h] add edx,eax ;EDX=用户缓冲区 invoke IoCompleteRequest,pIrp,0 xor eax,eax ret _Write endp _mySend proc _lpAdapt,_lpPacket local @va local @size pushad mov ebx,_lpPacket mov ebx,[ebx+8] mov edi,offset szSendBuffer .repeat invoke NdisQueryBufferSafe,ebx,addr @va,addr @size,20h mov esi,@va mov ecx,@size rep movsb mov ebx,[ebx] .until ebx==0 sub edi,offset szSendBuffer mov dwSendSize,edi ;以上循环复制将要发送的数据包到本驱动 invoke NdisSetEvent,lpSend3Event ;放行RING3的WaitForSingleObject(),通知RING3用ReadFile来读数据包内容(重要) popad leave jmp lpOldSend ;转到系统原来的Send例程执行 ret _mySend endp _myRecv proc _lpAdapt,_lpPacket local @va local @size pushad mov ebx,_lpPacket mov ebx,[ebx+8] mov edi,offset szRecvBuffer .repeat invoke NdisQueryBufferSafe,ebx,addr @va,addr @size,20h mov esi,@va mov ecx,@size rep movsb mov ebx,[ebx] .until ebx==0 sub edi,offset szRecvBuffer mov dwRecvSize,edi ;以上循环复制将要接收的数据包到本驱动 invoke NdisSetEvent,lpRecv3Event ;放行RING3的WaitForSingleObject(),通知RING3用ReadFile来读数据包内容(重要) popad leave jmp lpOldRecv ;转到系统原来的Recv例程执行 ret _myRecv endp ;以下空函数是为了填充NDIS_PROTOCOL_CHARACTERISTIC结构而设置的,实际下基本不会被系统调用,没有又不行。 PtOpenAdapterComplete proc pAdapt,Status,OpenErrorStatus xor eax,eax ret PtOpenAdapterComplete endp PtCloseAdapterComplete proc pAdapt,Status xor eax,eax ret PtCloseAdapterComplete endp PtSendComplete proc pAdapt,Packet,Status xor eax,eax ret PtSendComplete endp PtTransferDataComplete proc pAdapt,Packet,Status,BytesTransferred xor eax,eax ret PtTransferDataComplete endp PtResetComplete proc pAdapt,Status xor eax,eax ret PtResetComplete endp PtRequestComplete proc pAdapt,_NdisRequest,Status xor eax,eax ret PtRequestComplete endp PtReceive proc pAdapt,MacReceiveContext,HeaderBuffer,HeaderBufferSize,LookAheadBuffer,LookAheadBufferSize,PacketSize xor eax,eax ret PtReceive endp PtReceiveComplete proc pAdapt xor eax,eax ret PtReceiveComplete endp PtStatus proc pAdapt,GeneralStatus,StatusBuffer,StatusBufferSize xor eax,eax ret PtStatus endp PtStatusComplete proc pAdapt xor eax,eax ret PtStatusComplete endp PtBindAdapter proc Status,BindContext,DeviceName,SystemSpecific1,SystemSpecific2 xor eax,eax ret PtBindAdapter endp PtUnbindAdapter proc Status,pAdapt,UnbindContext xor eax,eax ret PtUnbindAdapter endp PtReceivePacket proc pAdapt,Packet xor eax,eax ret PtReceivePacket endp PtPNPHandler proc pAdapt,pNetPnPEvent xor eax,eax ret PtPNPHandler endp end start |
|
|
沙发#
发布于:2007-03-08 10:05
pf,但不鼓励...
汇编写的没移植性.ms也不建议大家使用汇编代码写驱动 |
|
|
板凳#
发布于:2007-03-14 13:35
感觉实用价值不大。
事实上,但从效率来讲,ddk2k3里带的cl,远比想象的要优秀,可以ida一下它优化编译的驱动代码,比手工汇编要合理的多。cl会充分考虑x86cpu的特性,使指令流程更合理,命中率更高,x86cpu对命不中的惩罚是很严厉的。 他也不会把没有调用的函数编译到module了,即使你的代码里有该函数的实现,这个好象是最近才有的功能。 |
|
地板#
发布于:2007-04-10 16:59
好像你说的就像真的样.....用汇编写..是最好学习用的了..最好了解它的工作的了...人家的开头写得很清楚..我觉得作者真是一位高人....老大我们是中国人呢? 不要什么就说MS怎么样..就要怎么样....没出息的家伙...如果没有汇编...好多深层的破解、内核修改等很难实现。。。注意不是不可以实现 。。是很难实现。。。。。。我不是有心和你争的。。相信你也是高手吧。。对于我们这些初学者来说。。。他用汇编写真的是很容易学习。。。因为高人们总是把高深的说的更高深。。我觉得也不是什么显示自己能力。。也许这就是高人的作风吧。。就好比文学家高人总爱说点儿我们难懂的字、词。。也许这样才能促进人们学习。。。。。。。。。。。。。。无聊哈。。发起耍的。。。。呵呵~!~·!
|
|
|
地下室#
发布于:2007-04-11 01:00
兄台..你看下你的HOOK接收数据哪儿..应该是[ebx+50h]哟..加40h的接收我试过的..试不行的哟...我也不知道为什么会在50h的行的哈..我以前对这个ndis驱动调试了很长段时间...
|
|
|
5楼#
发布于:2007-04-11 08:49
怎么不在的啊??
|
|
|
6楼#
发布于:2007-04-11 08:50
怎么没在的啊??我请教下。。在这个hook里面能不能发送自己构造的包啊。。。能的话。。怎么发啊。。。。。??
|
|
|
7楼#
发布于:2007-04-12 10:55
不错,用汇编写程序不难,难的是一辈子只用汇编写程序,哈哈。。。。。。
顶MGF老兄 ![]() |
|
|
8楼#
发布于:2007-04-12 13:48
wowocock老弟呀,我也好久没有跟你联系了,因为忙的关系,我的QQ一直隐身,不过你还是可以留言给我的,呵呵!
下面是NDIS_OPEN_BLOCK结构的定义(最左边是偏移): typedef struct _NDIS_COMMON_OPEN_BLOCK / _NdisBindingHandle { 00 PVOID MacHandle; // for backward compatibility 04 NDIS_HANDLE BindingHandle; // Miniport's open context 08 PNDIS_MINIPORT_BLOCK MiniportHandle; // pointer to the miniport 0C PNDIS_PROTOCOL_BLOCK ProtocolHandle; // pointer to our protocol 10 NDIS_HANDLE ProtocolBindingContext; // context when calling ProtXX 14 PNDIS_OPEN_BLOCK MiniportNextOpen; // used by adapter's OpenQueue 18 PNDIS_OPEN_BLOCK ProtocolNextOpen; // used by protocol's OpenQueue 1C NDIS_HANDLE MiniportAdapterContext; // context for miniport 20 BOOLEAN Reserved1; 21 BOOLEAN Reserved2; 22 BOOLEAN Reserved3; 23 BOOLEAN Reserved4; 24 PNDIS_STRING BindDeviceName; 28 KSPIN_LOCK Reserved5; 2C PNDIS_STRING RootDeviceName; 30 SEND_HANDLER SendHandler / WanSendHandler; 34 TRANSFER_DATA_HANDLER TransferDataHandler; 38 SEND_COMPLETE_HANDLER SendCompleteHandler; 3C TRANSFER_DATA_COMPLETE_HANDLER TransferDataCompleteHandler; 40 RECEIVE_HANDLER ReceiveHandler; 44 RECEIVE_COMPLETE_HANDLER ReceiveCompleteHandler; 48 WAN_RECEIVE_HANDLER WanReceiveHandler; 4C REQUEST_COMPLETE_HANDLER RequestCompleteHandler; 50 RECEIVE_PACKET_HANDLER ReceivePacketHandler; 54 SEND_PACKETS_HANDLER SendPacketsHandler; 58 RESET_HANDLER ResetHandler; 5C REQUEST_HANDLER RequestHandler; 60 RESET_COMPLETE_HANDLER ResetCompleteHandler; 64 STATUS_HANDLER StatusHandler; 68 STATUS_COMPLETE_HANDLER StatusCompleteHandler; 6C ULONG Flags; 70 LONG References; 74 KSPIN_LOCK SpinLock; 78 NDIS_HANDLE FilterHandle; 7C ULONG ProtocolOptions; 80 USHORT CurrentLookahead; 82 USHORT ConnectDampTicks; 84 USHORT DisconnectDampTicks; 86 USHORT reserved; 88 W_SEND_HANDLER WSendHandler; 8C W_TRANSFER_DATA_HANDLER WTransferDataHandler; 90 W_SEND_PACKETS_HANDLER WSendPacketsHandler; 94 W_CANCEL_SEND_PACKETS_HANDLER CancelSendPacketsHandler; 98 ULONG WakeUpEnable; 9C PKEVENT CloseCompleteEvent; A0 QUEUED_CLOSE QC; A4 LONG AfReferences; A8 PNDIS_OPEN_BLOCK NextGlobalOpen; } NDIS_COMMON_OPEN_BLOCK; 注意偏移40和50都是RECEIVE,只不过偏移40一次只能收一个包,偏移50一次可以收多个包, 有些系统用偏移40收包,有些系统用偏移50收包。4楼的兄弟应该属于偏移50收包的情况。 还有如果想发包,需要用NdisOpenAdapter()绑定网卡,比较麻烦。 |
|
9楼#
发布于:2007-04-12 14:10
如果用DDK来改写这个程序,绝对要比MS提供的passthru简单明了很多,呵呵
|
|
10楼#
发布于:2007-04-17 15:43
呵呵,支持,我现在也都是用汇编写东西
![]() |
|