2007年7月8日星期日

一个汇编版的NDIS驱动程序模板

一个汇编版的NDIS驱动程序模板(by mgf @2007.02.23); 写驱动程序已经很久了,但我一直用汇编来写驱动程序,原因是我不喜欢DDK,因为DDK和汇编一样,不是象VC,C#那样的“可视化”的开发工具,;DDK和汇编一样看不见结构或对象的成员,也看不到各种函数的原型,这些东西和各种常量定义还必须去查看相关的包含文件,不仅如此,DDK还会;“自作聪明”地把很多垃圾代码加到我的驱动里,虽然没有影响驱动程序的功能,但是还是不爽,不如直接用汇编,生成的驱动程序是什么样的自己;心里有数。其实这也怪不得MS,它不想大家了解它的操作系统核心技术,但是又不得不开放一些技术细节给大家写驱动,所以只有提高门槛,搞出这;么一个BT的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,stdcalloption casemap:noneassume fs:nothing,gs:nothingincludelib ntoskrnl.libincludelib hal.libincludelib ndis.libincludelib tdi.libincludelib wdm.lib;以上LIB文件要从DDK里复制到MASM32的LIB目录里NdisQueryBufferSafe proto :dword,:dword,:dword,:dwordNdisInitializeEvent proto :dwordNdisSetEvent proto :dwordNdisResetEvent proto :dwordNdisWaitEvent proto :dword,:dwordNdisRegisterProtocol proto :dword,:dword,:dword,:dwordNdisDeregisterProtocol proto :dword,:dwordIoCreateDevice proto :dword,:dword,:dword,:dword,:dword,:dword,:dwordIoDeleteDevice proto :dwordIoCreateSymbolicLink proto :dword,:dwordIoDeleteSymbolicLink proto :dwordIoCompleteRequest proto :dword,:dwordIoCreateNotificationEvent proto :dword,:dwordPsGetVersion proto :dword,:dword,:dword,:dwordRtlCompareUnicodeString proto :dword,:dword,:dwordZwClose proto :dword;由于MASM32没有相关的INC文件,所以要使用的API要自己定义原型.datalpProtocolHandle 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 0dwTempVar dd 0obSendEvent 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 ;接收副本大小.conststTcpip dw 5*2,6*2dd offset szTcpipszTcpip dw 'T','c','p','i','p',0stProtName dw 7*2,8*2dd offset szProtNameszProtName dw 'N','d','i','s','D','r','v',0stDeviceName dw 15*2,16*2dd offset szDeviceNameszDeviceName dw '\','D','e','v','i','c','e','\','N','d','i','s','D','r','v',0stSymbolicLinkName dw 19*2,20*2dd offset szSymbolicLinkNameszSymbolicLinkName dw '\','D','o','s','D','e','v','i','c','e','s','\','N','D','I','S','D','R','V',0stSend3Event dw 28*2,29*2dd offset szSend3EventszSend3Event 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',0stRecv3Event dw 28*2,29*2dd offset szRecv3EventszRecv3Event 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.codestart proc DriverObject,RegisterPathpushadinvoke PsGetVersion,offset dwTempVar,0,0,0 ;获取操作系统版本,如果不是5.0(2000,XP,2003)就退出,因为不能使用NDIS5.0.if dwTempVar!=5jmp _exit.endifmov byte ptr stProtocolChar,5mov byte ptr stProtocolChar+1,0mov eax,dword ptr stProtNamemov dword ptr stProtocolChar+30h,eaxmov eax,dword ptr stProtName+4mov dword ptr stProtocolChar+34h,eaxmov dword ptr stProtocolChar+08h,offset PtOpenAdapterCompletemov dword ptr stProtocolChar+0ch,offset PtCloseAdapterCompletemov dword ptr stProtocolChar+10h,offset PtSendCompletemov dword ptr stProtocolChar+14h,offset PtTransferDataCompletemov dword ptr stProtocolChar+18h,offset PtResetCompletemov dword ptr stProtocolChar+1ch,offset PtRequestCompletemov dword ptr stProtocolChar+20h,offset PtReceivemov dword ptr stProtocolChar+24h,offset PtReceiveCompletemov dword ptr stProtocolChar+28h,offset PtStatusmov dword ptr stProtocolChar+2ch,offset PtStatusCompletemov dword ptr stProtocolChar+3ch,offset PtBindAdaptermov dword ptr stProtocolChar+40h,offset PtUnbindAdaptermov dword ptr stProtocolChar+38h,offset PtReceivePacketmov dword ptr stProtocolChar+44h,offset PtPNPHandler;以上是填充NDIS_PROTOCOL_CHARACTERISTIC结构invoke NdisRegisterProtocol,offset dwStatus,offset lpProtocolHandle,offset stProtocolChar,6ch ;获取系统协议链表的地址,前面做了这么多工作都是为了这个地址.if dwStatusjmp _exit.endifmov ebx,lpProtocolHandlemov ebx,[ebx+10h] ;跳过我的假协议,保存协议链里下一项的地址invoke NdisDeregisterProtocol,offset dwStatus,lpProtocolHandle ;我注册的假协议已经没有用了,注销mov lpProtocolHandle,ebx ;真正的系统协议链的地址mov esi,offset stTcpip.repeatlea edi,[ebx+44h]invoke RtlCompareUnicodeString,edi,esi,1.break .if eax==0mov ebx,[ebx+10h].until ebx==0 ;这个循环是搜索TCPIP协议的PROTOCOL_HANDLE.if eaxjmp _exit.endifmov 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_transferinvoke IoCreateSymbolicLink,offset stSymbolicLinkName,offset stDeviceNamemov eax,lpDeviceObjector dword ptr [eax+1ch],10h ;把device.flag设置为DO_DIRECT_IO,使驱动程序的READ、WRITE例程直接映射用户缓冲区到本驱动mov edi,DriverObjectadd edi,38hmov ecx,1chmov eax,offset _CommonIoControlrep stosd ;填充共用例程,必须,否则CreateFile()不能打开本驱动mov eax,DriverObjectmov dword ptr [eax+34h],offset _Unloadmov dword ptr [eax+44h],offset _Readmov dword ptr [eax+48h],offset _Write ;注册驱动例程mov byte ptr [eax+8],2 ;强行修改Driver.flag为legacy driver,否则DriverEntry返回时系统就会卸载本驱动(因为本驱动默认编译为WDM drvier)invoke NdisInitializeEvent,offset obSendEventinvoke NdisInitializeEvent,offset obRecvEventinvoke NdisResetEvent,offset obSendEventinvoke NdisResetEvent,offset obRecvEvent;建立RING0事件invoke IoCreateNotificationEvent,offset stSend3Event,offset hSend3Eventmov lpSend3Event,eaxinvoke NdisResetEvent,eaxinvoke IoCreateNotificationEvent,offset stRecv3Event,offset hRecv3Eventmov lpRecv3Event,eaxinvoke NdisResetEvent,eax;建立RING3事件,在RING3用OpenEvent()打开mov ebx,lpProtocolHandlemov dword ptr [ebx+30h],offset _mySend ;HOOK TCPIP协议的SENDmov dword ptr [ebx+40h],offset _myRecv ;HOOK TCPIP协议的RECV_exit:popadxor eax,eaxretstart endp_Unload proc DriverObjectmov edx,lpProtocolHandlemov eax,lpOldSendmov [edx+30h],eaxmov eax,lpOldRecvmov [edx+40h],eax;恢复TCPIP协议的OPEN_BLOCK里原来的SEND/RECV派发例程invoke ZwClose,hSend3Eventinvoke ZwClose,hRecv3Eventinvoke NdisSetEvent,offset obSendEventinvoke NdisSetEvent,offset obRecvEventinvoke IoDeleteSymbolicLink,offset stSymbolicLinkNameinvoke IoDeleteDevice,lpDeviceObjectxor eax,eaxret_Unload endp_CommonIoControl proc DeviceObject,pIrpmov eax,pIrpmov dword ptr [eax+18h],0mov dword ptr [eax+1ch],0invoke IoCompleteRequest,pIrp,0xor eax,eaxret_CommonIoControl endp_Read proc DeviceObject,pIrppushadmov ebx,pIrpmov edi,[ebx+4]mov ecx,[edi+18h]mov edi,[edi+10h]add edi,ecx ;EDI=用户缓冲区mov ecx,lpSend3Eventmov edx,lpRecv3Event.if dword ptr [ecx+4]invoke NdisResetEvent,lpSend3Event ;RING3事件复位,防止再次放行mov esi,offset szSendBuffermov ecx,dwSendSizemov eax,[ebx+60h].if ecx>[eax+4]mov ecx,[eax+4].endif.elseinvoke NdisResetEvent,lpRecv3Eventmov esi,offset szRecvBuffermov ecx,dwRecvSizemov eax,[ebx+60h].if ecx>[eax+4]mov ecx,[eax+4].endif.endifmov dword ptr [ebx+18h],0mov dword ptr [ebx+1ch],ecx ;设置ReadFile()的读取字节数rep movsb ;把数据包复制到ReadFile()提供的缓冲区invoke IoCompleteRequest,pIrp,0popadxor eax,eaxret_Read endp_Write proc DeviceObject,pIrpmov eax,pIrpmov dword ptr [eax+18h],0mov dword ptr [eax+1ch],0mov 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,0xor eax,eaxret_Write endp_mySend proc _lpAdapt,_lpPacketlocal @valocal @sizepushadmov ebx,_lpPacketmov ebx,[ebx+8]mov edi,offset szSendBuffer.repeatinvoke NdisQueryBufferSafe,ebx,addr @va,addr @size,20hmov esi,@vamov ecx,@sizerep movsbmov ebx,[ebx].until ebx==0sub edi,offset szSendBuffermov dwSendSize,edi;以上循环复制将要发送的数据包到本驱动invoke NdisSetEvent,lpSend3Event ;放行RING3的WaitForSingleObject(),通知RING3用ReadFile来读数据包内容(重要)popadleavejmp lpOldSend ;转到系统原来的Send例程执行ret_mySend endp_myRecv proc _lpAdapt,_lpPacketlocal @valocal @sizepushadmov ebx,_lpPacketmov ebx,[ebx+8]mov edi,offset szRecvBuffer.repeatinvoke NdisQueryBufferSafe,ebx,addr @va,addr @size,20hmov esi,@vamov ecx,@sizerep movsbmov ebx,[ebx].until ebx==0sub edi,offset szRecvBuffermov dwRecvSize,edi;以上循环复制将要接收的数据包到本驱动invoke NdisSetEvent,lpRecv3Event ;放行RING3的WaitForSingleObject(),通知RING3用ReadFile来读数据包内容(重要)popadleavejmp lpOldRecv ;转到系统原来的Recv例程执行ret_myRecv endp;以下空函数是为了填充NDIS_PROTOCOL_CHARACTERISTIC结构而设置的,实际下基本不会被系统调用,没有又不行。PtOpenAdapterComplete proc pAdapt,Status,OpenErrorStatusxor eax,eaxretPtOpenAdapterComplete endpPtCloseAdapterComplete proc pAdapt,Statusxor eax,eaxretPtCloseAdapterComplete endpPtSendComplete proc pAdapt,Packet,Statusxor eax,eaxretPtSendComplete endpPtTransferDataComplete proc pAdapt,Packet,Status,BytesTransferredxor eax,eaxretPtTransferDataComplete endpPtResetComplete proc pAdapt,Statusxor eax,eaxretPtResetComplete endpPtRequestComplete proc pAdapt,_NdisRequest,Statusxor eax,eaxretPtRequestComplete endpPtReceive proc pAdapt,MacReceiveContext,HeaderBuffer,HeaderBufferSize,LookAheadBuffer,LookAheadBufferSize,PacketSizexor eax,eaxretPtReceive endpPtReceiveComplete proc pAdaptxor eax,eaxretPtReceiveComplete endpPtStatus proc pAdapt,GeneralStatus,StatusBuffer,StatusBufferSizexor eax,eaxretPtStatus endpPtStatusComplete proc pAdaptxor eax,eaxretPtStatusComplete endpPtBindAdapter proc Status,BindContext,DeviceName,SystemSpecific1,SystemSpecific2xor eax,eaxretPtBindAdapter endpPtUnbindAdapter proc Status,pAdapt,UnbindContextxor eax,eaxretPtUnbindAdapter endpPtReceivePacket proc pAdapt,Packetxor eax,eaxretPtReceivePacket endpPtPNPHandler proc pAdapt,pNetPnPEventxor eax,eaxretPtPNPHandler endpend start

没有评论: