驱动源码如下(每个函数及重要部份都标上了注释)
;@echo off
;goto make
;功能:驱动实现模拟键盘按键
;说明:模拟键盘按键就需要访问IO端口 但是系统默认是不允许访问的,所以我们利用驱动修改TSS中的IOPM(IO许可位图),允许我们的主程序访问IO端口(直接用in、out访问).
;by: 看雪--ericzw QQ--巴丫丫 37261550
.586
.model flat,stdcall
option casemap:none
include \masm32\include\w2k\ntddk.inc;驱动开发必备的头文件
include \masm32\include\w2k\ntstatus.inc;一些状态标识符
include \masm32\include\w2k\ntoskrnl.inc;将用到ntoskrnl很多未公开的函数
includelib \masm32\lib\w2k\ntoskrnl.lib
include \masm32\macros\strings.mac
;字符宏 声明字符串
$s MACRO txt:REQ
local d,sn
sn TEXTEQU @CurSeg
.const
d db txt,0
@CurSeg ENDS
sn SEGMENT
EXITM <offset d>
ENDM
.data
szUnicode WORD 'P','r','o','c','e','s','s','I','d',0
szUs UNICODE_STRING <sizeof szUnicode,sizeof szUnicode,offset szUnicode>;这2句可以使用$COUNTED_UNICODE_STRING('ProcessId',4)宏来代替
.code
;驱动程序入口函数 每次驱动加截都将调用此函数
DriverEntry proc _lpDeriverObject:PDRIVER_OBJECT,_lpRegisterString:PUNICODE_STRING;1参:当前驱动对象指针,由系统初始化这个驱动程序时分配的DRIVER_OBJECT结构 2参:指向定长的Unicode字符串,记录该驱动在注册表中的键路径
local @oa:OBJECT_ATTRIBUTES
local @hKey;注册表句柄
local @kvpi:KEY_VALUE_PARTIAL_INFORMATION
local @lp;分配的虚拟内存指针
local @kProcess:PVOID;KPROCESS对象指针
;int 3;需要调试此驱动的时候 把注释去掉,在softice里面开启捕捉int 3指令(使用“I3Here on“即可)
;初始化OBJECT_ATTRIBUTES供ZwOpenKey使用(使用这几个注册表函数就是为了得到调用程序的进程ID 我们保存在注册表里面)
lea esi,@oa
assume esi:ptr OBJECT_ATTRIBUTES
mov [esi]._Length,sizeof OBJECT_ATTRIBUTES;结构大小 结构字段原来是Length,但汇编Length是保留字,所以定义的时候在它前面加了'_'符号
mov [esi].RootDirectory,NULL;根目录路径 为NULL则结构的ObjectName必须指向一个完整路径对象名称 不为NULL则ObjectName指定对象名称即可
push _lpRegisterString;驱动在注册表中的键路径
pop [esi].ObjectName;指向Unicode字符串 指示对象是由哪个句柄打开
mov [esi].Attributes,NULL;属性
mov [esi].SecurityDescriptor,NULL;指示对象安全描述 为NULL则为默认设置
mov [esi].SecurityQualityOfService,NULL;一般为NULL
assume esi:nothing
invoke ZwOpenKey,addr @hKey,KEY_READ,esi;打开现有注册表 参1:函数在此返回取得的注册表句柄 参2:操作权限 参3:指向已初始化的OBJECT_ATTRIBUTES
.if eax==STATUS_SUCCESS
push eax
invoke ZwQueryValueKey,@hKey,addr szUs,KeyValuePartialInformation,addr @kvpi,sizeof @kvpi,esp;取注册表中的键值 参1:打开的句柄 参2:定长的UNICODE_STRING字符串注册表中键的名字 参3:需要取得注册表键的值使用KeyValuePartialInformation 参4:指针指向结构KEY_VALUE_PARTIAL_INFORMATON(最终取得的值返回在结构中字段Data里面) 参5:结构大小 参6:在此返回取得的数据大小
pop ecx
.if eax==STATUS_SUCCESS && ecx!=0;函数成功 同时取得的数据不为0(ecx就是上个函数堆栈上的值pop弹出的)
invoke MmAllocateNonCachedMemory,8192;我们总共有8192个许可位图 所以分配1个不被cache的虚拟内存
.if eax
mov @lp,eax
lea ecx,@kvpi
invoke PsLookupProcessByProcessId,DWORD ptr (KEY_VALUE_PARTIAL_INFORMATION PTR [ecx]).Data,addr @kProcess;利用进程ID取得进程对象 参1:进程id 参2:在此处返回取得的进程对象 使用此函数会使进程对象的引用计数加1
.if eax==STATUS_SUCCESS
invoke Ke386QueryIoAccessMap,1,@lp;从TSS中取得许可位图 参1:为0将所有位置1 为1普通的拷贝 参2:虚拟内存指针 许可位图的数据返回到此指针指向的虚拟内存
.if al;成功al非0
;把60 64端口都置位 注:由于IO许可位图使用的是bit位 我们正常表示都是字节 所以他们需要除8
mov esi,@lp
add esi,60h / 8
mov eax,[esi]
btr eax, 60h mod 8;btr把指定的位清0 也就是把60h端口位设置成0 允许访问60h端口
mov [esi],eax
mov esi,@lp
add esi,64h / 8
mov eax,[esi]
btr eax,64h mod 8
mov [esi],eax
invoke Ke386SetIoAccessMap,1,@lp;拷贝新的IOPM(IO许可位图)到TSS中 参1:必须为1 参2:指向不低于2000h的虚拟内存
.if al;成功al非0
invoke Ke386IoSetAccessProcess,@kProcess,1;上面的数据拷贝进TSS后 必须使TSS中的偏移定位到新的IOPM 参1:KPROCESS进程对象 参2:为0表示禁址对I/O端口进行存取将IOPM的偏移指到TSS段外面 为1表示允许存取I/O端口
.if al;成功al非0
invoke DbgPrint,$s('driver ok...')
.endif
.endif
.endif
invoke ObDereferenceObject,@kProcess;将对象引用计数减1
.endif
invoke MmFreeNonCachedMemory,@lp,8192;释放内存
.endif
invoke ZwClose,@hKey;释放句柄
.endif
.endif
mov eax,STATUS_DEVICE_CONFIGURATION_ERROR;入口函数返回值,这里将使系统返回设备配置错误,系统将直接把驱动从内存中清除
ret
DriverEntry endp
end DriverEntry
:make
set drv=myDriver
\masm32\bin\ml /nologo /c /coff %drv%.bat
\masm32\bin\link /nologo /driver /base:0x10000 /align:32 /out:%drv%.sys /subsystem:native %drv%.obj
del %drv%.obj
echo.
pause
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
上传的附件: