内核态下基于动态感染技术的应用程序执行保护(三 获取SSDT)
imageheaderstringdoshookfile(转载请注明博客地址:http://blog.csdn.net/hitetoshi)
前面我们经常提到SSDT,那么什么是SSDT呢?SSDT(SystemServices Descriptor Table):系统服务描述符表。要对它有清楚的认识,我们先以用户态下调用CreateThread来举个例子。
如果你有兴趣,可以用OllyDbg在CreateThread上下断点,你会发现,经过一些简单的处理,CreateThread会调用CreateRemoteThread,按F7进入CreateRemoteThread,再经过一段跟踪,会发现CreateRemoteThread实际上又调用ZwCreateThread,再按F7跟踪进去,ZwCreateThread的代码非常简单:
7C92D1AE > B8 35000000 MOV EAX,35
7C92D1B3 BA 0003FE7F MOVEDX,7FFE0300
7C92D1B8 FF12 CALL DWORD PTR DS:
7C92D1BA C2 2000 RETN 20
注意MOV EDX,7FFE0300/CALL DWORD PTRDS:这两条指令,从OD上可以看到,这实际上是调用ntdll.dll中的KiFastSystemCall,KiFastSystemCall的代码也很简单:
7C92E510 > 8BD4 MOV EDX,ESP
7C92E512 0F34 SYSENTER
7C92E514 > C3 RETN
如果这时候你再用F7一步一步的跟,你会发现,到SYSENTER这条指令时,OllyDbg无法跟踪进去,而当这条指令执行完时,实际上ZwCreateThread的功能已经执行完成――用户态的一切玄机似乎都在SYSENTER这条指令上。SYSENTER指令在用户态下向内核态发起系统调用,此时代码切换到内核,因此OllyDbg这样的用户态调试器是无法调试的。和KiFastSystemCall类似,ntdll.dll中也有一个KiIntSystemCall。PII以前的处理器并不支持SYSENTER这条指令,因此KiIntSystemCall以一条INT
2E指令切换到内核。
SYSENTER进入内核的KiFastCallEntry(该函数没有被ntoskrnl.exe导出,你可能需要下载ntoskrnl.exe的Symbol才能看到它),KiFastCallEntry大致进行的工作就是按照SSDT表找到对应服务函数的地址,并转入到服务函数,即内核NtCreateThread中去。SSDT包含在一个叫SDT(服务描述表)的结构中,SDT由ntoskrnl.exe(某些系统下可能不是这个文件名,例如多核处理器下可能是ntkrnlpa.exe,但为简便起见,下文都用ntoskrnl.exe来表述)以KeServiceDescriptorTable为名称导出,SDT的定义如下:
ServiceDescriptorEntry STRUCT
pvSSDTBase dd ? ;SSDT基地址
pvServiceCounterTable dd ? ;SSDT中每个服务被调用次数的表
ulNumberOfServices dd ? ;描述的服务的数目
pvParamTableBase dd ? ;每个系统服务参数的字节数的表
ServiceDescriptorEntry ENDS
我们可以在LiveKd中用d nt!KeServiceDescriptorTable来观察内核中这个结构的数据:
可见系统中有0x11C个SSDT项,基地址为0x80505480,我们来看看SSDT表的内容,用d 80505480命令:
从0x80505480开始,存放每个系统服务项的函数地址,那么怎么确定内核NtCreateThread的地址呢?答案就在用户态下ZwCreateThread的mov eax,0x35这条指令,它指明了NtCreateThread在SSDT中是第0x35项,计算一下0x80505480+0x35*4=0x80505554,看一看:
大约NtCreateThread应该在0x805D2018这个地址,用u nt!NtCreateThread命令看一下:
正是这个地址!(大家肯定会比较奇怪NtCreateThread的第一条指令怎么会是Jmp,我也是今天写这篇稳当时才发现我的NtCreateThread居然被Inline hook了!NND!一看模块,IsDrv122.sys,原来是开着冰刃……)。
出了冰刃这个事件也好,也就是我想跟大家说的Hook SSDT,刚才大家已经看到,冰刃对SSDT的NtCreateThread做了Inline hook,我曾经说过,我极不推荐做Inline hook,除非你技术实力非常强大。原因我会在后面的文章介绍。这里我们有一个思路,如果把0x80505554这个地址的内容替换了,换成我们的函数,不是一样达到HookNtCreateThread的效果了吗?这就是SSDT Hook。在SSDT上做Hook或者是InlineHook是非常强大的,因为用户态上所有CreateThread的调用最终都会进入这个函数。很多杀毒软件会Hook
SSDT的这个位置,用来监视是否有进程试图用CreateRemoteThread向其它进程启动远程线程,这是防止远程线程的一个很有效的方法。不过我们Hook NtCreateThread的目的不是这个,我前面已经说了,我们要用它来监视进程的创建,并向进程中注入代码。
今天的这篇文章我并不会完成Hook SSDT的代码,因为在我们的内核程序中还有很多事情要做:获取NtCreateThread的服务序号、获取保存NtCreateThread地址的位置以及获取NtCreateThread的地址。
我们在上次的DynamicHook.asm的DriverEntry中加一些代码(红色部分是新加的代码):
DriverEntry proc pDriverObject:PDRIVER_OBJECT,pusRegistryPath:PUNICODE_STRING
local status:NTSTATUS
local pDeviceObject:PDEVICE_OBJECT
mov status,STATUS_DEVICE_CONFIGURATION_ERROR
invoke IoCreateDevice,pDriverObject,0,addrg_usDeviceName,FILE_DEVICE_UNKNOWN,0,FALSE,addr pDeviceObject
.if eax==STATUS_SUCCESS
invoke IoCreateSymbolicLink,addrg_usSymbolicLinkName,addr g_usDeviceName
.if eax==STATUS_SUCCESS
mov eax,pDriverObject
assume eax:ptr DRIVER_OBJECT
mov .DriverUnload,offset DriverUnload
mov .MajorFunction,offsetDispatchCreateClose
mov .MajorFunction,offsetDispatchCreateClose
invoke DbgPrint,$CTA0("Driver entry")
invoke HookNtCreateThread
NT_SUCCESS eax
.if eax
invoke DbgPrint,$CTA0("Hook success")
mov status,STATUS_SUCCESS
.else
invoke DbgPrint,$CTA0("Hook failure")
.endif
assume eax:nothing
.else
invoke IoDeleteDevice,pDeviceObject
.endif
.endif
@@:
mov eax,status
ret
DriverEntry endp
在.const节中加入:
CCOUNTED_UNICODE_STRING "NtCreateThread",g_usNtCreateThread,4
完成HookNtCreateThread函数:
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
HookNtCreateThread proc
local dwNtCreateThreadIndex
local dwNtCreateThreadAddress
local status:NTSTATUS
mov status,STATUS_UNSUCCESSFUL
;获取NtCreateThread服务序号
invoke GetSysCallIndex,addrg_usNtCreateThread
.if eax
mov dwNtCreateThreadIndex,eax
;获取NtCreteThread在内核中的地址
invoke GetSSDTOrigValue,dwNtCreateThreadIndex
.if eax
mov dwNtCreateThreadAddress,eax
invoke DbgPrint,$CTA0("NtCreateThreadindex:%08X,address:%08X"),dwNtCreateThreadIndex,dwNtCreateThreadAddress
mov status,STATUS_SUCCESS
.endif
.endif
mov eax,status
ret
HookNtCreateThread endp
为了完成GetSysCallIndex和GetSSDTOrigValue这两个函数,我增加了PEFile.asm和SSDT.asm两个文件以及对应的PEFile.inc、SSDT.inc和DyanmicHook.inc,你把这些文件加入进去时,注意要向上次那样设置PEFile.asm和SSDT.asm的汇编选项,设置各个文件的依赖项(自己看include去设置,就不详述了)。先把这五个文件的内容贴出来:
;PEFile.asm
.386
.model flat,stdcall
option casemap:none
include ntddk.inc
include native.inc
include hal.inc
include ntoskrnl.inc
include strings.mac
include DynamicHook.inc
include PEFile.inc
.code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
GetAlignedSize proc uses edx ecx dwSize,dwAlignment
xor edx,edx
mov eax,dwSize
mov ecx,dwAlignment
div ecx
.if edx==0
mov eax,dwSize
.else
inc eax
mul dwAlignment
.endif
ret
GetAlignedSize endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
GetTotalImageSize proc uses edi edx ecx pDosHeader:PIMAGE_DOS_HEADER,pNtHeaders:PIMAGE_NT_HEADERS
local dwResult:DWORD
local dwAlignment:DWORD
local dwSections:DWORD
and dwResult,0
mov edi,pNtHeaders
assume edi:ptrIMAGE_NT_HEADERS
M2M dwAlignment,.OptionalHeader.SectionAlignment
xor edx,edx
mov eax,.OptionalHeader.SizeOfHeaders
mov ecx,dwAlignment
div ecx
.if edx==0
mov eax,.OptionalHeader.SizeOfHeaders
add dwResult,eax
.else
inc eax
mul dwAlignment
add dwResult,eax
.endif
mov edi,pNtHeaders
assume edi:ptrIMAGE_NT_HEADERS
movzx eax,.FileHeader.NumberOfSections
mov dwSections,eax
add edi,sizeofIMAGE_NT_HEADERS
assume edi:ptrIMAGE_SECTION_HEADER
xor edx,edx
.while edx<dwSections
push edx
mov eax,.Misc.VirtualSize
.if eax
xor edx,edx
mov ecx,dwAlignment
div ecx
.if edx==0
mov eax,.Misc.VirtualSize
add dwResult,eax
.else
inc eax
mul dwAlignment
add dwResult,eax
.endif
.endif
pop edx
add edi,sizeofIMAGE_SECTION_HEADER
inc edx
.endw
assume edi:nothing
mov eax,dwResult
ret
GetTotalImageSize endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
GetPEInfo proc uses edi esi pBase:PVOID,ppDosHeader:ptr PIMAGE_DOS_HEADER,ppNtHeaders:ptrPIMAGE_NT_HEADERS
local status:NTSTATUS
mov status,STATUS_UNSUCCESSFUL
mov edi,pBase
assume edi:ptrIMAGE_DOS_HEADER
.if .e_magic!=IMAGE_DOS_SIGNATURE
jmp @F
.endif
mov esi,ppDosHeader
.if esi
mov dwordptr ,edi
.endif
add edi,.e_lfanew
assume edi:ptrIMAGE_NT_HEADERS
.if .Signature!=IMAGE_NT_SIGNATURE
jmp @F
.endif
mov esi,ppNtHeaders
.if esi
mov dwordptr ,edi
.endif
mov status,STATUS_SUCCESS
@@:
mov eax,status
ret
GetPEInfo endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
KLoadPEFile proc uses edi esi pBaseAddress:PVOID,pLoadAddress:PVOID
local status:NTSTATUS
local pDosHeader:PIMAGE_DOS_HEADER
local pNtHeaders:PIMAGE_NT_HEADERS
local pPtr:PVOID
local dwSections:DWORD
local dwAlignment:DWORD
mov status,STATUS_UNSUCCESSFUL
invoke KeGetCurrentIrql
.if eax!=PASSIVE_LEVEL
mov status,STATUS_INVALID_LEVEL
jmp @F
.endif
M2M pPtr,pLoadAddress
invoke GetPEInfo,pBaseAddress,addrpDosHeader,addr pNtHeaders
.if eax==STATUS_SUCCESS
mov edi,pNtHeaders
assume edi:ptrIMAGE_NT_HEADERS
invoke memcpy,pPtr,pBaseAddress,.OptionalHeader.SizeOfHeaders
M2M dwAlignment,.OptionalHeader.SectionAlignment
invoke GetAlignedSize,.OptionalHeader.SizeOfHeaders,dwAlignment
add pPtr,eax
movzx eax,.FileHeader.NumberOfSections
mov dwSections,eax
add edi,sizeofIMAGE_NT_HEADERS
assume edi:ptrIMAGE_SECTION_HEADER
xor edx,edx
.while edx<dwSections
push edx
mov eax,.SizeOfRawData
.if eax>0
.if eax>.Misc.VirtualSize
mov eax,.Misc.VirtualSize
.endif
mov esi,pBaseAddress
add esi,.PointerToRawData
invoke memcpy,pPtr,esi,eax
invoke GetAlignedSize,.Misc.VirtualSize,dwAlignment
add pPtr,eax
.endif
pop edx
add edi,sizeof IMAGE_SECTION_HEADER
inc edx
.endw
mov status,STATUS_SUCCESS
assume edi:nothing
.endif
@@:
mov eax,status
ret
KLoadPEFile endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;装载PE文件到内存并对齐
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
KLoadLibrary proc pusFileName:PUNICODE_STRING
local oa:OBJECT_ATTRIBUTES
local iosb:IO_STATUS_BLOCK
local hFile:HANDLE
local dwFileSize:DWORD
local fsi:FILE_STANDARD_INFORMATION
local pFile:PVOID
local pDosHeader:PIMAGE_DOS_HEADER
local pNtHeaders:PIMAGE_NT_HEADERS
local dwImageSize
local pImage:PVOID
and pImage,0
invoke KeGetCurrentIrql
.if eax!=PASSIVE_LEVEL
jmp @F
.endif
InitializeObjectAttributes addr oa,pusFileName,OBJ_CASE_INSENSITIVE orOBJ_KERNEL_HANDLE,NULL,NULL
invoke ZwOpenFile,addrhFile,FILE_READ_DATA or FILE_EXECUTE or SYNCHRONIZE,addr oa,addriosb,FILE_SHARE_READ,FILE_SYNCHRONOUS_IO_NONALERT
NT_SUCCESS eax
.if eax
invoke RtlZeroMemory,addrfsi,sizeof fsi
invoke ZwQueryInformationFile,hFile,addriosb,addr fsi,sizeof fsi,FileStandardInformation
NT_SUCCESS eax
.if eax
M2M dwFileSize,fsi.EndOfFile.LowPart
;分配内存
invoke ExAllocatePool,PagedPool,dwFileSize
.if eax
mov pFile,eax
invoke ZwReadFile,hFile,0,NULL,NULL,addr iosb,pFile,dwFileSize,0,NULL
NT_SUCCESS eax
.if eax
invoke GetPEInfo,pFile,addr pDosHeader,addrpNtHeaders
.if eax==STATUS_SUCCESS
invoke GetTotalImageSize,pDosHeader,pNtHeaders
.if eax
mov dwImageSize,eax
invoke ExAllocatePool,PagedPool,dwImageSize
.if eax
mov pImage,eax
invoke KLoadPEFile,pFile,pImage
.if eax!=STATUS_SUCCESS
invoke ExFreePool,pImage
and pImage,0
.endif
.endif
.endif
.endif
.endif
invoke ExFreePool,pFile
.endif
.endif
invoke ZwClose,hFile
.endif
@@:
mov eax,pImage
ret
KLoadLibrary endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
GetPEImageBase proc uses edi pBaseAddress:PVOID
local dwReturn:DWORD
and dwReturn,0
mov edi,pBaseAddress
assume edi:ptrIMAGE_DOS_HEADER
.if .e_magic!=IMAGE_DOS_SIGNATURE
jmp @F
.endif
add edi,.e_lfanew
assume edi:ptrIMAGE_NT_HEADERS
.if .Signature!=IMAGE_NT_SIGNATURE
jmp @F
.endif
M2M dwReturn,.OptionalHeader.ImageBase
assume edi:nothing
@@:
mov eax,dwReturn
ret
GetPEImageBase endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
GetFuncInfoByName proc uses esi edi ecx pusFuncName:PUNICODE_STRING,pBaseAddress:PVOID,pOrdinal:PDWORD
local dwAddress
local lpAddressOfNames
local lpAddressOfNameOrdinals
local dwIndex
local usExportFunctionName:UNICODE_STRING
local ansExportFunctionName:ANSI_STRING
xor eax,eax
mov dwAddress,eax
mov esi,pOrdinal
.if esi
mov dwordptr ,0
.endif
invoke KeGetCurrentIrql
.if eax!=PASSIVE_LEVEL
jmp _RETURN
.endif
mov esi,pBaseAddress
assume esi:ptrIMAGE_DOS_HEADER
add esi,.e_lfanew
assume esi:ptrIMAGE_NT_HEADERS
mov eax,.OptionalHeader.DataDirectory.VirtualAddress
.if !eax
jmp _RETURN
.endif
add eax,pBaseAddress
mov edi,eax
assume edi:ptrIMAGE_EXPORT_DIRECTORY
mov eax,.AddressOfNames
add eax,pBaseAddress
mov lpAddressOfNames,eax
mov eax,.AddressOfNameOrdinals
add eax,pBaseAddress
mov lpAddressOfNameOrdinals,eax
mov eax,.AddressOfFunctions
add eax,pBaseAddress
mov esi,eax
mov ecx,.NumberOfFunctions
mov dwIndex,0
@@:
pushad
mov eax,dwIndex
push edi
mov ecx,.NumberOfNames
cld
mov edi,lpAddressOfNameOrdinals
repnz scasw
.if ZERO?
sub edi,lpAddressOfNameOrdinals
sub edi,2
shl edi,1
add edi,lpAddressOfNames
mov eax,dwordptr
add eax,pBaseAddress
invoke RtlInitAnsiString,addransExportFunctionName,eax
invoke RtlAnsiStringToUnicodeString,addrusExportFunctionName,addr ansExportFunctionName,TRUE
invoke RtlCompareUnicodeString,addrusExportFunctionName,pusFuncName,FALSE
push eax
invoke RtlFreeUnicodeString,addrusExportFunctionName
pop eax
.if eax==0
M2M dwAddress,dwordptr
pop edi
mov esi,pOrdinal
.if esi
mov ecx,dwIndex
add ecx,.nBase
mov dword ptr ,ecx
.endif
popad
jmp _RETURN
.endif
.endif
pop edi
popad
add esi,4
inc dwIndex
loop @B
_RETURN:
assume esi:nothing
mov eax,dwAddress
ret
GetFuncInfoByName endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;获取DLL导出函数信息
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
GetFunctionInformation proc uses esi edi pusFuncName:PUNICODE_STRING,pusDllName:PUNICODE_STRING,pFuncInfo:PFUNCTION_INFORMATION
local status:NTSTATUS
local pBaseAddress:PVOID
xor eax,eax
mov pBaseAddress,eax
mov status,STATUS_UNSUCCESSFUL
invoke KeGetCurrentIrql
.if eax!=PASSIVE_LEVEL
mov status,STATUS_INVALID_LEVEL
jmp @F
.endif
invoke KLoadLibrary,pusDllName
.if eax
mov pBaseAddress,eax
mov esi,pFuncInfo
assume esi:ptrFUNCTION_INFORMATION
lea edi,.dwOridnal
invoke GetFuncInfoByName,pusFuncName,pBaseAddress,edi
.if eax
lea edi,.dwAddress
mov dword ptr ,eax
lea edi,.byEntryCode
mov esi,.dwAddress
add esi,pBaseAddress
invoke memcpy,edi,esi,16
mov status,STATUS_SUCCESS
.else
mov status,STATUS_UNSUCCESSFUL
.endif
invoke ExFreePool,pBaseAddress
assume esi:nothing
.endif
@@:
mov eax,status
ret
GetFunctionInformation endp
end
;SSDT.asm
.386
.model flat,stdcall
option casemap:none
include ntddk.inc
include native.inc
include ntoskrnl.inc
include hal.inc
include strings.mac
includePEFile.inc
include DynamicHook.inc
include SSDT.inc
.const
CCOUNTED_UNICODE_STRING "\\Systemroot\\System32\\ntdll.dll",g_usntdllFileName,4
CCOUNTED_UNICODE_STRING "KeServiceDescriptorTable",g_usKeServiceDescriptorTable,4
.code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;获取内核文件名
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
GetKernel proc uses edi pusFileName:PUNICODE_STRING
local dwLength:DWORD
local dwAddress:DWORD
local pBuffer
local szFileName:byte
local ansFileName:ANSI_STRING
local dwBase:DWORD
and dwLength,0
and pBuffer,0
and dwAddress,0
invoke KeGetCurrentIrql
.if eax!=PASSIVE_LEVEL
jmp @F
.endif
invoke ZwQuerySystemInformation,SystemModuleInformation,NULL,0,addrdwLength
.if dwLength
invoke ExAllocatePool,PagedPool,dwLength
.if eax
mov pBuffer,eax
invoke ZwQuerySystemInformation,SystemModuleInformation,pBuffer,dwLength,addrdwLength
NT_SUCCESS eax
.if eax
mov eax,pBuffer
add eax,4
assume eax:ptr SYSTEM_MODULE_INFORMATION
M2M dwBase,.Base
lea edi,.ImageName
movzx eax,.ModuleNameOffset
add edi,eax
assume eax:nothing
invoke RtlZeroMemory,addr szFileName,260
invoke strcpy,addr szFileName,$CTA0("\\Systemroot\\System32\\")
invoke strcat,addr szFileName,edi
invoke RtlInitAnsiString,addr ansFileName,addr szFileName
.if pusFileName!=NULL
invoke RtlAnsiStringToUnicodeString,pusFileName,addransFileName,TRUE
M2M dwAddress,dwBase
.endif
.endif
invoke ExFreePool,pBuffer
.endif
.endif
@@:
mov eax,dwAddress
ret
GetKernel endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
FindKiServiceTableEx proc uses ecx edi esi edx hModule,dwKSDT
local dwReturn:DWORD
local bFirstChunk:BOOL
local dwCount:DWORD
local dwFixups:DWORD
local dwImageBase:DWORD
and dwReturn,0
and dwFixups,0
mov edi,hModule
assume edi:ptrIMAGE_DOS_HEADER
.if .e_magic!=IMAGE_DOS_SIGNATURE
jmp @F
.endif
add edi,.e_lfanew
assume edi:ptrIMAGE_NT_HEADERS
.if .Signature!=IMAGE_NT_SIGNATURE
jmp @F
.endif
push .OptionalHeader.ImageBase
pop dwImageBase
mov eax,.OptionalHeader.DataDirectory.VirtualAddress
movzx ecx,.FileHeader.Characteristics
and ecx,IMAGE_FILE_RELOCS_STRIPPED
.if (eax)&& !(ecx)
mov edi,hModule
add edi,eax
assume edi:ptrIMAGE_BASE_RELOCATION
mov bFirstChunk,TRUE
.while bFirstChunk|| .VirtualAddress
mov bFirstChunk,FALSE
mov esi,edi
add esi,sizeof IMAGE_BASE_RELOCATION
assume esi:ptr IMAGE_FIXUP_ENTRY
mov edx,.SizeOfBlock
sub edx,sizeof IMAGE_BASE_RELOCATION
ror edx,1
mov dwCount,edx
xor edx,edx
.while edx<dwCount
push edx
mov ax,word ptr
and ax,0F000h
ror ax,12
.if ax==IMAGE_REL_BASED_HIGHLOW
inc dwFixups
mov ax,word ptr
and ax,0FFFh
movzx eax,ax
add eax,.VirtualAddress
add eax,hModule
mov ecx,eax
mov eax,dword ptr
sub eax,dwImageBase
.if eax==dwKSDT
mov eax,ecx
sub eax,2
mov ax,word ptr
.if ax==5C7h
mov eax,ecx
add eax,4
mov eax,dword ptr
sub eax,dwImageBase
mov dwReturn,eax
jmp @F
.endif
.endif
.endif
pop edx
inc edx
add esi,sizeof WORD
.endw
add edi,.SizeOfBlock
assume esi:nothing
.endw
.endif
assume edi:nothing
@@:
mov eax,dwReturn
ret
FindKiServiceTableEx endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;获取SSDT原始值
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
GetSSDTOrigValue proc uses edi dwIndex
local dwReturn:DWORD
local usKernelFileName:UNICODE_STRING
local dwKernelBase:DWORD
local pBaseAddress:PVOID
local dwImageBase:DWORD
xor eax,eax
mov dwReturn,eax
mov pBaseAddress,eax
mov dwKernelBase,eax
invoke KeGetCurrentIrql
.if eax!=PASSIVE_LEVEL
jmp @F
.endif
invoke GetKernel,addrusKernelFileName
.if eax
mov dwKernelBase,eax
;映射PE文件
invoke KLoadLibrary,addrusKernelFileName
.if eax
mov pBaseAddress,eax
invoke GetPEImageBase,pBaseAddress
.if eax
mov dwImageBase,eax
invoke GetFuncInfoByName,addrg_usKeServiceDescriptorTable,pBaseAddress,NULL
.if eax
invoke FindKiServiceTableEx,pBaseAddress,eax
.if eax
add eax,pBaseAddress
mov edi,eax
mov eax,dwIndex
rol eax,2
add edi,eax
mov eax,dword ptr
sub eax,dwImageBase
add eax,dwKernelBase
mov dwReturn,eax
.endif
.endif
.endif
invoke ExFreePool,pBaseAddress
.endif
invoke RtlFreeUnicodeString,addrusKernelFileName
.endif
@@:
mov eax,dwReturn
ret
GetSSDTOrigValue endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;获取函数的Service Index
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
GetSysCallIndex proc pusFuncName:PUNICODE_STRING
local FuncInfo:FUNCTION_INFORMATION
invoke GetFunctionInformation,pusFuncName,addrg_usntdllFileName,addr FuncInfo
.if eax==STATUS_SUCCESS
lea eax,FuncInfo.byEntryCode
.if byteptr ==0B8h
inc eax
mov eax,dword ptr
ret
.endif
.endif
xor eax,eax
ret
GetSysCallIndex endp
end
;SSDT.inc
IFNDEF __SSDT_INC__
__SSDT_INC__ equ <1>
ServiceDescriptorEntry STRUCT
pvSSDTBase dd ?
pvServiceCounterTable dd ?
ulNumberOfServices dd ?
pvParamTableBase dd ?
ServiceDescriptorEntry ENDS
GetSysCallIndex proto :PUNICODE_STRING
GetSSDTOrigValue proto :DWORD
GetKernel proto :PUNICODE_STRING
ENDIF
;PEFile.inc
IFNDEF __PEFILE_INC__
__PEFILE_INC__ equ <1>
FUNCTION_INFORMATION STRUCT
byEntryCode db 16 dup (?) ;前16字节
dwOridnal dd ?
dwAddress dd ?
FUNCTION_INFORMATION ENDS
PFUNCTION_INFORMATION typedef ptr FUNCTION_INFORMATION
IMAGE_DOS_SIGNATURE equ 5A4Dh
IMAGE_NT_SIGNATURE equ 00004550h
IMAGE_SIZEOF_SHORT_NAME equ 8
IMAGE_FILE_RELOCS_STRIPPED equ 0001h
IMAGE_REL_BASED_HIGHLOW equ 3
IMAGE_DIRECTORY_ENTRY_EXPORT equ 0
IMAGE_DIRECTORY_ENTRY_BASERELOC equ 5
IMAGE_NUMBEROF_DIRECTORY_ENTRIES equ 16
IMAGE_DOS_HEADERSTRUCT
e_magic WORD ?
e_cblp WORD ?
e_cp WORD ?
e_crlc WORD ?
e_cparhdr WORD ?
e_minalloc WORD ?
e_maxalloc WORD ?
e_ss WORD ?
e_sp WORD ?
e_csum WORD ?
e_ip WORD ?
e_cs WORD ?
e_lfarlc WORD ?
e_ovno WORD ?
e_res WORD 4 dup(?)
e_oemid WORD ?
e_oeminfo WORD ?
e_res2 WORD 10 dup(?)
e_lfanew DWORD ?
IMAGE_DOS_HEADERENDS
PIMAGE_DOS_HEADER typedef ptr IMAGE_DOS_HEADER
IMAGE_DATA_DIRECTORYSTRUCT
VirtualAddress DWORD ?
isize DWORD ?
IMAGE_DATA_DIRECTORYENDS
IMAGE_OPTIONAL_HEADER32STRUCT
Magic WORD ?
MajorLinkerVersion BYTE ?
MinorLinkerVersion BYTE ?
SizeOfCode DWORD ?
SizeOfInitializedData DWORD ?
SizeOfUninitializedData DWORD ?
AddressOfEntryPoint DWORD ?
BaseOfCode DWORD ?
BaseOfData DWORD ?
ImageBase DWORD ?
SectionAlignment DWORD ?
FileAlignment DWORD ?
MajorOperatingSystemVersion WORD ?
MinorOperatingSystemVersion WORD ?
MajorImageVersion WORD ?
MinorImageVersion WORD ?
MajorSubsystemVersion WORD ?
MinorSubsystemVersion WORD ?
Win32VersionValue DWORD ?
SizeOfImage DWORD ?
SizeOfHeaders DWORD ?
CheckSum DWORD ?
Subsystem WORD ?
DllCharacteristics WORD ?
SizeOfStackReserve DWORD ?
SizeOfStackCommit DWORD ?
SizeOfHeapReserve DWORD ?
SizeOfHeapCommit DWORD ?
LoaderFlags DWORD ?
NumberOfRvaAndSizes DWORD ?
DataDirectory IMAGE_DATA_DIRECTORYIMAGE_NUMBEROF_DIRECTORY_ENTRIES dup(<>)
IMAGE_OPTIONAL_HEADER32ENDS
IMAGE_OPTIONAL_HEADER equ <IMAGE_OPTIONAL_HEADER32>
PIMAGE_OPTIONAL_HEADER typedef ptr IMAGE_OPTIONAL_HEADER
IMAGE_EXPORT_DIRECTORYSTRUCT
Characteristics DWORD ?
TimeDateStamp DWORD ?
MajorVersion WORD ?
MinorVersion WORD ?
nName DWORD ?
nBase DWORD ?
NumberOfFunctions DWORD ?
NumberOfNames DWORD ?
AddressOfFunctions DWORD ?
AddressOfNames DWORD ?
AddressOfNameOrdinals DWORD ?
IMAGE_EXPORT_DIRECTORYENDS
IMAGE_FILE_HEADERSTRUCT
Machine WORD ?
NumberOfSections WORD ?
TimeDateStamp DWORD ?
PointerToSymbolTable DWORD ?
NumberOfSymbols DWORD ?
SizeOfOptionalHeader WORD ?
Characteristics WORD ?
IMAGE_FILE_HEADERENDS
IMAGE_NT_HEADERSSTRUCT
Signature DWORD ?
FileHeader IMAGE_FILE_HEADER <>
OptionalHeader IMAGE_OPTIONAL_HEADER32 <>
IMAGE_NT_HEADERSENDS
PIMAGE_NT_HEADERS typedef ptr IMAGE_NT_HEADERS
IMAGE_SECTION_HEADERSTRUCT
Name1 db IMAGE_SIZEOF_SHORT_NAME dup(?)
union Misc
PhysicalAddress dd ?
VirtualSize dd ?
ends
VirtualAddress dd ?
SizeOfRawData dd ?
PointerToRawData dd ?
PointerToRelocations dd ?
PointerToLinenumbers dd ?
NumberOfRelocations dw ?
NumberOfLinenumbers dw ?
Characteristics dd ?
IMAGE_SECTION_HEADERENDS
PIMAGE_SECTION_HEADER typedef ptr IMAGE_SECTION_HEADER
IMAGE_BASE_RELOCATIONSTRUCT
VirtualAddress dd ?
SizeOfBlock dd ?
IMAGE_BASE_RELOCATIONENDS
PIMAGE_BASE_RELOCATION typedef ptr IMAGE_BASE_RELOCATION
GetFunctionInformation proto :PUNICODE_STRING,:PUNICODE_STRING,:PFUNCTION_INFORMATION
GetFuncInfoByName proto :PUNICODE_STRING,:PVOID,:PDWORD
GetPEImageBase proto :PVOID
GetPEImageLength proto :PUNICODE_STRING
KLoadLibrary proto :PUNICODE_STRING
ENDIF
;SSDT.inc
;DyanmicHook.inc
IFNDEF __DYNAMICHOOK_INC__
__DYNAMICHOOK_INC__ equ <1>
OPEN_EXISTING equ 3
INVALID_HANDLE_VALUE equ -1
FILE_MAP_READ equ SECTION_MAP_READ
MAX_PATH equ 260
M2M macro M1,M2
push M2
pop M1
ENDM
NT_SUCCESS macro status
mov eax,status
and eax,80000000h
.if eax
mov eax,FALSE
.else
mov eax,TRUE
.endif
ENDM
ENDIF
先贴代码,再来讲原理。根据我们刚才对SSDT的认识,要顺利找到SSDT的位置,首先要确定NtCreateThread的服务序号。不幸的是我尚未找到有效准确的获取方式。我唯一的思路是在ntdll.dll中找到ZwCreateThread,判断如果它的第一字节为0xB8(汇编代码MOV EAX,XXXXXXXX)那么它第二字节开始的双字就是其服务序号。用C语言描述应该是:SysCallIndex = *( (WORD*)((DWORD)GetProcAddress(ntdll,ZwCreateThread)+
1) );
为此,专门写了PEFile.asm这个文件,这个文件提供了一些函数:
GetFunctionInformation proto :PUNICODE_STRING,:PUNICODE_STRING,:PFUNCTION_INFORMATION
GetFuncInfoByName proto :PUNICODE_STRING,:PVOID,:PDWORD
GetPEImageBase proto :PVOID
GetPEImageLength proto :PUNICODE_STRING
KLoadLibrary proto :PUNICODE_STRING
其中,KLoadLibrary将PE文件按加载到内存中(不是读取到内存中,类似PE加载器一样按照PE结构中的描述对其进行加载,以便通过导出表分析导出函数在内存中的位置),其它几个函数就顾名思义了。这些函数的意思我就不再详细解释,因为这也是我3年前的代码,我也记不得太清楚。有了这几个函数,你就能够理解SSDT.asm中的GetSysCallIndex这个函数的原理。当然,我讲了这是我唯一的思路,也希望大家不吝赐教,把你知道的方法告诉我(你别说用0x35硬编码就行)
确定了NtCreateThread的服务序号,下面我们就要在SSDT中寻找NtCreateThread的存放位置以及原始的函数地址。其实这比获取服务序号简单多了,因为一切秘密都在KeDescriptorTable这个导出符号中。需要注意的是前面我们已经说了,并不是所有系统中内核文件名都叫ntoskrnl.exe,所以先用ZwQuerySystemInformation获取一下当前系统中的内核文件名,通过对导出符号KeDescriptorTable的分析,顺利获得NtCreateThread的真实地址。具体的代码在SSDT.asm中,函数比较少,看名字就懂意思,具体我也不详细解释了,再解释又扯得很远,而且这是我3年前的代码。
好了,打开DebugView,用KmdManager加载一下我们生成的内核程序:
与前面LiveKd看到的一样,貌似一切都很顺利。
这一章就讲完了,下一章我们就完成HookSSDT、UnHookSSDT这两个函数,我们把NtCreateThread函数Hook住,监视一下进程的创建,同时我在下一章告诉大家为什么我极不推荐普通人对SSDT使用Inline Hook,虽然它的功能更加强大!顺便希望大家检查下我的代码,谢谢。
分类: 技术--汇编
imageheaderstringdoshookfile
(转载请注明博客地址:http://blog.csdn.net/hitetoshi)
前面我们经常提到SSDT,那么什么是SSDT呢?SSDT(SystemServices Descriptor Table):系统服务描述符表。要对它有清楚的认识,我们先以用户态下调用CreateThread来举个例子。
如果你有兴趣,可以用OllyDbg在CreateThread上下断点,你会发现,经过一些简单的处理,CreateThread会调用CreateRemoteThread,按F7进入CreateRemoteThread,再经过一段跟踪,会发现CreateRemoteThread实际上又调用ZwCreateThread,再按F7跟踪进去,ZwCreateThread的代码非常简单:
7C92D1AE > B8 35000000 MOV EAX,35
7C92D1B3 BA 0003FE7F MOVEDX,7FFE0300
7C92D1B8 FF12 CALL DWORD PTR DS:
7C92D1BA C2 2000 RETN 20
注意MOV EDX,7FFE0300/CALL DWORD PTRDS:这两条指令,从OD上可以看到,这实际上是调用ntdll.dll中的KiFastSystemCall,KiFastSystemCall的代码也很简单:
7C92E510 > 8BD4 MOV EDX,ESP
7C92E512 0F34 SYSENTER
7C92E514 > C3 RETN
如果这时候你再用F7一步一步的跟,你会发现,到SYSENTER这条指令时,OllyDbg无法跟踪进去,而当这条指令执行完时,实际上ZwCreateThread的功能已经执行完成――用户态的一切玄机似乎都在SYSENTER这条指令上。SYSENTER指令在用户态下向内核态发起系统调用,此时代码切换到内核,因此OllyDbg这样的用户态调试器是无法调试的。和KiFastSystemCall类似,ntdll.dll中也有一个KiIntSystemCall。PII以前的处理器并不支持SYSENTER这条指令,因此KiIntSystemCall以一条INT
2E指令切换到内核。
SYSENTER进入内核的KiFastCallEntry(该函数没有被ntoskrnl.exe导出,你可能需要下载ntoskrnl.exe的Symbol才能看到它),KiFastCallEntry大致进行的工作就是按照SSDT表找到对应服务函数的地址,并转入到服务函数,即内核NtCreateThread中去。SSDT包含在一个叫SDT(服务描述表)的结构中,SDT由ntoskrnl.exe(某些系统下可能不是这个文件名,例如多核处理器下可能是ntkrnlpa.exe,但为简便起见,下文都用ntoskrnl.exe来表述)以KeServiceDescriptorTable为名称导出,SDT的定义如下:
ServiceDescriptorEntry STRUCT
pvSSDTBase dd ? ;SSDT基地址
pvServiceCounterTable dd ? ;SSDT中每个服务被调用次数的表
ulNumberOfServices dd ? ;描述的服务的数目
pvParamTableBase dd ? ;每个系统服务参数的字节数的表
ServiceDescriptorEntry ENDS
我们可以在LiveKd中用d nt!KeServiceDescriptorTable来观察内核中这个结构的数据:
可见系统中有0x11C个SSDT项,基地址为0x80505480,我们来看看SSDT表的内容,用d 80505480命令:
从0x80505480开始,存放每个系统服务项的函数地址,那么怎么确定内核NtCreateThread的地址呢?答案就在用户态下ZwCreateThread的mov eax,0x35这条指令,它指明了NtCreateThread在SSDT中是第0x35项,计算一下0x80505480+0x35*4=0x80505554,看一看:
大约NtCreateThread应该在0x805D2018这个地址,用u nt!NtCreateThread命令看一下:
正是这个地址!(大家肯定会比较奇怪NtCreateThread的第一条指令怎么会是Jmp,我也是今天写这篇稳当时才发现我的NtCreateThread居然被Inline hook了!NND!一看模块,IsDrv122.sys,原来是开着冰刃……)。
出了冰刃这个事件也好,也就是我想跟大家说的Hook SSDT,刚才大家已经看到,冰刃对SSDT的NtCreateThread做了Inline hook,我曾经说过,我极不推荐做Inline hook,除非你技术实力非常强大。原因我会在后面的文章介绍。这里我们有一个思路,如果把0x80505554这个地址的内容替换了,换成我们的函数,不是一样达到HookNtCreateThread的效果了吗?这就是SSDT Hook。在SSDT上做Hook或者是InlineHook是非常强大的,因为用户态上所有CreateThread的调用最终都会进入这个函数。很多杀毒软件会Hook
SSDT的这个位置,用来监视是否有进程试图用CreateRemoteThread向其它进程启动远程线程,这是防止远程线程的一个很有效的方法。不过我们Hook NtCreateThread的目的不是这个,我前面已经说了,我们要用它来监视进程的创建,并向进程中注入代码。
今天的这篇文章我并不会完成Hook SSDT的代码,因为在我们的内核程序中还有很多事情要做:获取NtCreateThread的服务序号、获取保存NtCreateThread地址的位置以及获取NtCreateThread的地址。
我们在上次的DynamicHook.asm的DriverEntry中加一些代码(红色部分是新加的代码):
DriverEntry proc pDriverObject:PDRIVER_OBJECT,pusRegistryPath:PUNICODE_STRING
local status:NTSTATUS
local pDeviceObject:PDEVICE_OBJECT
mov status,STATUS_DEVICE_CONFIGURATION_ERROR
invoke IoCreateDevice,pDriverObject,0,addrg_usDeviceName,FILE_DEVICE_UNKNOWN,0,FALSE,addr pDeviceObject
.if eax==STATUS_SUCCESS
invoke IoCreateSymbolicLink,addrg_usSymbolicLinkName,addr g_usDeviceName
.if eax==STATUS_SUCCESS
mov eax,pDriverObject
assume eax:ptr DRIVER_OBJECT
mov .DriverUnload,offset DriverUnload
mov .MajorFunction,offsetDispatchCreateClose
mov .MajorFunction,offsetDispatchCreateClose
invoke DbgPrint,$CTA0("Driver entry")
invoke HookNtCreateThread
NT_SUCCESS eax
.if eax
invoke DbgPrint,$CTA0("Hook success")
mov status,STATUS_SUCCESS
.else
invoke DbgPrint,$CTA0("Hook failure")
.endif
assume eax:nothing
.else
invoke IoDeleteDevice,pDeviceObject
.endif
.endif
@@:
mov eax,status
ret
DriverEntry endp
在.const节中加入:
CCOUNTED_UNICODE_STRING "NtCreateThread",g_usNtCreateThread,4
完成HookNtCreateThread函数:
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
HookNtCreateThread proc
local dwNtCreateThreadIndex
local dwNtCreateThreadAddress
local status:NTSTATUS
mov status,STATUS_UNSUCCESSFUL
;获取NtCreateThread服务序号
invoke GetSysCallIndex,addrg_usNtCreateThread
.if eax
mov dwNtCreateThreadIndex,eax
;获取NtCreteThread在内核中的地址
invoke GetSSDTOrigValue,dwNtCreateThreadIndex
.if eax
mov dwNtCreateThreadAddress,eax
invoke DbgPrint,$CTA0("NtCreateThreadindex:%08X,address:%08X"),dwNtCreateThreadIndex,dwNtCreateThreadAddress
mov status,STATUS_SUCCESS
.endif
.endif
mov eax,status
ret
HookNtCreateThread endp
为了完成GetSysCallIndex和GetSSDTOrigValue这两个函数,我增加了PEFile.asm和SSDT.asm两个文件以及对应的PEFile.inc、SSDT.inc和DyanmicHook.inc,你把这些文件加入进去时,注意要向上次那样设置PEFile.asm和SSDT.asm的汇编选项,设置各个文件的依赖项(自己看include去设置,就不详述了)。先把这五个文件的内容贴出来:
;PEFile.asm
.386
.model flat,stdcall
option casemap:none
include ntddk.inc
include native.inc
include hal.inc
include ntoskrnl.inc
include strings.mac
include DynamicHook.inc
include PEFile.inc
.code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
GetAlignedSize proc uses edx ecx dwSize,dwAlignment
xor edx,edx
mov eax,dwSize
mov ecx,dwAlignment
div ecx
.if edx==0
mov eax,dwSize
.else
inc eax
mul dwAlignment
.endif
ret
GetAlignedSize endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
GetTotalImageSize proc uses edi edx ecx pDosHeader:PIMAGE_DOS_HEADER,pNtHeaders:PIMAGE_NT_HEADERS
local dwResult:DWORD
local dwAlignment:DWORD
local dwSections:DWORD
and dwResult,0
mov edi,pNtHeaders
assume edi:ptrIMAGE_NT_HEADERS
M2M dwAlignment,.OptionalHeader.SectionAlignment
xor edx,edx
mov eax,.OptionalHeader.SizeOfHeaders
mov ecx,dwAlignment
div ecx
.if edx==0
mov eax,.OptionalHeader.SizeOfHeaders
add dwResult,eax
.else
inc eax
mul dwAlignment
add dwResult,eax
.endif
mov edi,pNtHeaders
assume edi:ptrIMAGE_NT_HEADERS
movzx eax,.FileHeader.NumberOfSections
mov dwSections,eax
add edi,sizeofIMAGE_NT_HEADERS
assume edi:ptrIMAGE_SECTION_HEADER
xor edx,edx
.while edx<dwSections
push edx
mov eax,.Misc.VirtualSize
.if eax
xor edx,edx
mov ecx,dwAlignment
div ecx
.if edx==0
mov eax,.Misc.VirtualSize
add dwResult,eax
.else
inc eax
mul dwAlignment
add dwResult,eax
.endif
.endif
pop edx
add edi,sizeofIMAGE_SECTION_HEADER
inc edx
.endw
assume edi:nothing
mov eax,dwResult
ret
GetTotalImageSize endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
GetPEInfo proc uses edi esi pBase:PVOID,ppDosHeader:ptr PIMAGE_DOS_HEADER,ppNtHeaders:ptrPIMAGE_NT_HEADERS
local status:NTSTATUS
mov status,STATUS_UNSUCCESSFUL
mov edi,pBase
assume edi:ptrIMAGE_DOS_HEADER
.if .e_magic!=IMAGE_DOS_SIGNATURE
jmp @F
.endif
mov esi,ppDosHeader
.if esi
mov dwordptr ,edi
.endif
add edi,.e_lfanew
assume edi:ptrIMAGE_NT_HEADERS
.if .Signature!=IMAGE_NT_SIGNATURE
jmp @F
.endif
mov esi,ppNtHeaders
.if esi
mov dwordptr ,edi
.endif
mov status,STATUS_SUCCESS
@@:
mov eax,status
ret
GetPEInfo endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
KLoadPEFile proc uses edi esi pBaseAddress:PVOID,pLoadAddress:PVOID
local status:NTSTATUS
local pDosHeader:PIMAGE_DOS_HEADER
local pNtHeaders:PIMAGE_NT_HEADERS
local pPtr:PVOID
local dwSections:DWORD
local dwAlignment:DWORD
mov status,STATUS_UNSUCCESSFUL
invoke KeGetCurrentIrql
.if eax!=PASSIVE_LEVEL
mov status,STATUS_INVALID_LEVEL
jmp @F
.endif
M2M pPtr,pLoadAddress
invoke GetPEInfo,pBaseAddress,addrpDosHeader,addr pNtHeaders
.if eax==STATUS_SUCCESS
mov edi,pNtHeaders
assume edi:ptrIMAGE_NT_HEADERS
invoke memcpy,pPtr,pBaseAddress,.OptionalHeader.SizeOfHeaders
M2M dwAlignment,.OptionalHeader.SectionAlignment
invoke GetAlignedSize,.OptionalHeader.SizeOfHeaders,dwAlignment
add pPtr,eax
movzx eax,.FileHeader.NumberOfSections
mov dwSections,eax
add edi,sizeofIMAGE_NT_HEADERS
assume edi:ptrIMAGE_SECTION_HEADER
xor edx,edx
.while edx<dwSections
push edx
mov eax,.SizeOfRawData
.if eax>0
.if eax>.Misc.VirtualSize
mov eax,.Misc.VirtualSize
.endif
mov esi,pBaseAddress
add esi,.PointerToRawData
invoke memcpy,pPtr,esi,eax
invoke GetAlignedSize,.Misc.VirtualSize,dwAlignment
add pPtr,eax
.endif
pop edx
add edi,sizeof IMAGE_SECTION_HEADER
inc edx
.endw
mov status,STATUS_SUCCESS
assume edi:nothing
.endif
@@:
mov eax,status
ret
KLoadPEFile endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;装载PE文件到内存并对齐
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
KLoadLibrary proc pusFileName:PUNICODE_STRING
local oa:OBJECT_ATTRIBUTES
local iosb:IO_STATUS_BLOCK
local hFile:HANDLE
local dwFileSize:DWORD
local fsi:FILE_STANDARD_INFORMATION
local pFile:PVOID
local pDosHeader:PIMAGE_DOS_HEADER
local pNtHeaders:PIMAGE_NT_HEADERS
local dwImageSize
local pImage:PVOID
and pImage,0
invoke KeGetCurrentIrql
.if eax!=PASSIVE_LEVEL
jmp @F
.endif
InitializeObjectAttributes addr oa,pusFileName,OBJ_CASE_INSENSITIVE orOBJ_KERNEL_HANDLE,NULL,NULL
invoke ZwOpenFile,addrhFile,FILE_READ_DATA or FILE_EXECUTE or SYNCHRONIZE,addr oa,addriosb,FILE_SHARE_READ,FILE_SYNCHRONOUS_IO_NONALERT
NT_SUCCESS eax
.if eax
invoke RtlZeroMemory,addrfsi,sizeof fsi
invoke ZwQueryInformationFile,hFile,addriosb,addr fsi,sizeof fsi,FileStandardInformation
NT_SUCCESS eax
.if eax
M2M dwFileSize,fsi.EndOfFile.LowPart
;分配内存
invoke ExAllocatePool,PagedPool,dwFileSize
.if eax
mov pFile,eax
invoke ZwReadFile,hFile,0,NULL,NULL,addr iosb,pFile,dwFileSize,0,NULL
NT_SUCCESS eax
.if eax
invoke GetPEInfo,pFile,addr pDosHeader,addrpNtHeaders
.if eax==STATUS_SUCCESS
invoke GetTotalImageSize,pDosHeader,pNtHeaders
.if eax
mov dwImageSize,eax
invoke ExAllocatePool,PagedPool,dwImageSize
.if eax
mov pImage,eax
invoke KLoadPEFile,pFile,pImage
.if eax!=STATUS_SUCCESS
invoke ExFreePool,pImage
and pImage,0
.endif
.endif
.endif
.endif
.endif
invoke ExFreePool,pFile
.endif
.endif
invoke ZwClose,hFile
.endif
@@:
mov eax,pImage
ret
KLoadLibrary endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
GetPEImageBase proc uses edi pBaseAddress:PVOID
local dwReturn:DWORD
and dwReturn,0
mov edi,pBaseAddress
assume edi:ptrIMAGE_DOS_HEADER
.if .e_magic!=IMAGE_DOS_SIGNATURE
jmp @F
.endif
add edi,.e_lfanew
assume edi:ptrIMAGE_NT_HEADERS
.if .Signature!=IMAGE_NT_SIGNATURE
jmp @F
.endif
M2M dwReturn,.OptionalHeader.ImageBase
assume edi:nothing
@@:
mov eax,dwReturn
ret
GetPEImageBase endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
GetFuncInfoByName proc uses esi edi ecx pusFuncName:PUNICODE_STRING,pBaseAddress:PVOID,pOrdinal:PDWORD
local dwAddress
local lpAddressOfNames
local lpAddressOfNameOrdinals
local dwIndex
local usExportFunctionName:UNICODE_STRING
local ansExportFunctionName:ANSI_STRING
xor eax,eax
mov dwAddress,eax
mov esi,pOrdinal
.if esi
mov dwordptr ,0
.endif
invoke KeGetCurrentIrql
.if eax!=PASSIVE_LEVEL
jmp _RETURN
.endif
mov esi,pBaseAddress
assume esi:ptrIMAGE_DOS_HEADER
add esi,.e_lfanew
assume esi:ptrIMAGE_NT_HEADERS
mov eax,.OptionalHeader.DataDirectory.VirtualAddress
.if !eax
jmp _RETURN
.endif
add eax,pBaseAddress
mov edi,eax
assume edi:ptrIMAGE_EXPORT_DIRECTORY
mov eax,.AddressOfNames
add eax,pBaseAddress
mov lpAddressOfNames,eax
mov eax,.AddressOfNameOrdinals
add eax,pBaseAddress
mov lpAddressOfNameOrdinals,eax
mov eax,.AddressOfFunctions
add eax,pBaseAddress
mov esi,eax
mov ecx,.NumberOfFunctions
mov dwIndex,0
@@:
pushad
mov eax,dwIndex
push edi
mov ecx,.NumberOfNames
cld
mov edi,lpAddressOfNameOrdinals
repnz scasw
.if ZERO?
sub edi,lpAddressOfNameOrdinals
sub edi,2
shl edi,1
add edi,lpAddressOfNames
mov eax,dwordptr
add eax,pBaseAddress
invoke RtlInitAnsiString,addransExportFunctionName,eax
invoke RtlAnsiStringToUnicodeString,addrusExportFunctionName,addr ansExportFunctionName,TRUE
invoke RtlCompareUnicodeString,addrusExportFunctionName,pusFuncName,FALSE
push eax
invoke RtlFreeUnicodeString,addrusExportFunctionName
pop eax
.if eax==0
M2M dwAddress,dwordptr
pop edi
mov esi,pOrdinal
.if esi
mov ecx,dwIndex
add ecx,.nBase
mov dword ptr ,ecx
.endif
popad
jmp _RETURN
.endif
.endif
pop edi
popad
add esi,4
inc dwIndex
loop @B
_RETURN:
assume esi:nothing
mov eax,dwAddress
ret
GetFuncInfoByName endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;获取DLL导出函数信息
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
GetFunctionInformation proc uses esi edi pusFuncName:PUNICODE_STRING,pusDllName:PUNICODE_STRING,pFuncInfo:PFUNCTION_INFORMATION
local status:NTSTATUS
local pBaseAddress:PVOID
xor eax,eax
mov pBaseAddress,eax
mov status,STATUS_UNSUCCESSFUL
invoke KeGetCurrentIrql
.if eax!=PASSIVE_LEVEL
mov status,STATUS_INVALID_LEVEL
jmp @F
.endif
invoke KLoadLibrary,pusDllName
.if eax
mov pBaseAddress,eax
mov esi,pFuncInfo
assume esi:ptrFUNCTION_INFORMATION
lea edi,.dwOridnal
invoke GetFuncInfoByName,pusFuncName,pBaseAddress,edi
.if eax
lea edi,.dwAddress
mov dword ptr ,eax
lea edi,.byEntryCode
mov esi,.dwAddress
add esi,pBaseAddress
invoke memcpy,edi,esi,16
mov status,STATUS_SUCCESS
.else
mov status,STATUS_UNSUCCESSFUL
.endif
invoke ExFreePool,pBaseAddress
assume esi:nothing
.endif
@@:
mov eax,status
ret
GetFunctionInformation endp
end
;SSDT.asm
.386
.model flat,stdcall
option casemap:none
include ntddk.inc
include native.inc
include ntoskrnl.inc
include hal.inc
include strings.mac
includePEFile.inc
include DynamicHook.inc
include SSDT.inc
.const
CCOUNTED_UNICODE_STRING "\\Systemroot\\System32\\ntdll.dll",g_usntdllFileName,4
CCOUNTED_UNICODE_STRING "KeServiceDescriptorTable",g_usKeServiceDescriptorTable,4
.code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;获取内核文件名
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
GetKernel proc uses edi pusFileName:PUNICODE_STRING
local dwLength:DWORD
local dwAddress:DWORD
local pBuffer
local szFileName:byte
local ansFileName:ANSI_STRING
local dwBase:DWORD
and dwLength,0
and pBuffer,0
and dwAddress,0
invoke KeGetCurrentIrql
.if eax!=PASSIVE_LEVEL
jmp @F
.endif
invoke ZwQuerySystemInformation,SystemModuleInformation,NULL,0,addrdwLength
.if dwLength
invoke ExAllocatePool,PagedPool,dwLength
.if eax
mov pBuffer,eax
invoke ZwQuerySystemInformation,SystemModuleInformation,pBuffer,dwLength,addrdwLength
NT_SUCCESS eax
.if eax
mov eax,pBuffer
add eax,4
assume eax:ptr SYSTEM_MODULE_INFORMATION
M2M dwBase,.Base
lea edi,.ImageName
movzx eax,.ModuleNameOffset
add edi,eax
assume eax:nothing
invoke RtlZeroMemory,addr szFileName,260
invoke strcpy,addr szFileName,$CTA0("\\Systemroot\\System32\\")
invoke strcat,addr szFileName,edi
invoke RtlInitAnsiString,addr ansFileName,addr szFileName
.if pusFileName!=NULL
invoke RtlAnsiStringToUnicodeString,pusFileName,addransFileName,TRUE
M2M dwAddress,dwBase
.endif
.endif
invoke ExFreePool,pBuffer
.endif
.endif
@@:
mov eax,dwAddress
ret
GetKernel endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
FindKiServiceTableEx proc uses ecx edi esi edx hModule,dwKSDT
local dwReturn:DWORD
local bFirstChunk:BOOL
local dwCount:DWORD
local dwFixups:DWORD
local dwImageBase:DWORD
and dwReturn,0
and dwFixups,0
mov edi,hModule
assume edi:ptrIMAGE_DOS_HEADER
.if .e_magic!=IMAGE_DOS_SIGNATURE
jmp @F
.endif
add edi,.e_lfanew
assume edi:ptrIMAGE_NT_HEADERS
.if .Signature!=IMAGE_NT_SIGNATURE
jmp @F
.endif
push .OptionalHeader.ImageBase
pop dwImageBase
mov eax,.OptionalHeader.DataDirectory.VirtualAddress
movzx ecx,.FileHeader.Characteristics
and ecx,IMAGE_FILE_RELOCS_STRIPPED
.if (eax)&& !(ecx)
mov edi,hModule
add edi,eax
assume edi:ptrIMAGE_BASE_RELOCATION
mov bFirstChunk,TRUE
.while bFirstChunk|| .VirtualAddress
mov bFirstChunk,FALSE
mov esi,edi
add esi,sizeof IMAGE_BASE_RELOCATION
assume esi:ptr IMAGE_FIXUP_ENTRY
mov edx,.SizeOfBlock
sub edx,sizeof IMAGE_BASE_RELOCATION
ror edx,1
mov dwCount,edx
xor edx,edx
.while edx<dwCount
push edx
mov ax,word ptr
and ax,0F000h
ror ax,12
.if ax==IMAGE_REL_BASED_HIGHLOW
inc dwFixups
mov ax,word ptr
and ax,0FFFh
movzx eax,ax
add eax,.VirtualAddress
add eax,hModule
mov ecx,eax
mov eax,dword ptr
sub eax,dwImageBase
.if eax==dwKSDT
mov eax,ecx
sub eax,2
mov ax,word ptr
.if ax==5C7h
mov eax,ecx
add eax,4
mov eax,dword ptr
sub eax,dwImageBase
mov dwReturn,eax
jmp @F
.endif
.endif
.endif
pop edx
inc edx
add esi,sizeof WORD
.endw
add edi,.SizeOfBlock
assume esi:nothing
.endw
.endif
assume edi:nothing
@@:
mov eax,dwReturn
ret
FindKiServiceTableEx endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;获取SSDT原始值
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
GetSSDTOrigValue proc uses edi dwIndex
local dwReturn:DWORD
local usKernelFileName:UNICODE_STRING
local dwKernelBase:DWORD
local pBaseAddress:PVOID
local dwImageBase:DWORD
xor eax,eax
mov dwReturn,eax
mov pBaseAddress,eax
mov dwKernelBase,eax
invoke KeGetCurrentIrql
.if eax!=PASSIVE_LEVEL
jmp @F
.endif
invoke GetKernel,addrusKernelFileName
.if eax
mov dwKernelBase,eax
;映射PE文件
invoke KLoadLibrary,addrusKernelFileName
.if eax
mov pBaseAddress,eax
invoke GetPEImageBase,pBaseAddress
.if eax
mov dwImageBase,eax
invoke GetFuncInfoByName,addrg_usKeServiceDescriptorTable,pBaseAddress,NULL
.if eax
invoke FindKiServiceTableEx,pBaseAddress,eax
.if eax
add eax,pBaseAddress
mov edi,eax
mov eax,dwIndex
rol eax,2
add edi,eax
mov eax,dword ptr
sub eax,dwImageBase
add eax,dwKernelBase
mov dwReturn,eax
.endif
.endif
.endif
invoke ExFreePool,pBaseAddress
.endif
invoke RtlFreeUnicodeString,addrusKernelFileName
.endif
@@:
mov eax,dwReturn
ret
GetSSDTOrigValue endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;获取函数的Service Index
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
GetSysCallIndex proc pusFuncName:PUNICODE_STRING
local FuncInfo:FUNCTION_INFORMATION
invoke GetFunctionInformation,pusFuncName,addrg_usntdllFileName,addr FuncInfo
.if eax==STATUS_SUCCESS
lea eax,FuncInfo.byEntryCode
.if byteptr ==0B8h
inc eax
mov eax,dword ptr
ret
.endif
.endif
xor eax,eax
ret
GetSysCallIndex endp
end
;SSDT.inc
IFNDEF __SSDT_INC__
__SSDT_INC__ equ <1>
ServiceDescriptorEntry STRUCT
pvSSDTBase dd ?
pvServiceCounterTable dd ?
ulNumberOfServices dd ?
pvParamTableBase dd ?
ServiceDescriptorEntry ENDS
GetSysCallIndex proto :PUNICODE_STRING
GetSSDTOrigValue proto :DWORD
GetKernel proto :PUNICODE_STRING
ENDIF
;PEFile.inc
IFNDEF __PEFILE_INC__
__PEFILE_INC__ equ <1>
FUNCTION_INFORMATION STRUCT
byEntryCode db 16 dup (?) ;前16字节
dwOridnal dd ?
dwAddress dd ?
FUNCTION_INFORMATION ENDS
PFUNCTION_INFORMATION typedef ptr FUNCTION_INFORMATION
IMAGE_DOS_SIGNATURE equ 5A4Dh
IMAGE_NT_SIGNATURE equ 00004550h
IMAGE_SIZEOF_SHORT_NAME equ 8
IMAGE_FILE_RELOCS_STRIPPED equ 0001h
IMAGE_REL_BASED_HIGHLOW equ 3
IMAGE_DIRECTORY_ENTRY_EXPORT equ 0
IMAGE_DIRECTORY_ENTRY_BASERELOC equ 5
IMAGE_NUMBEROF_DIRECTORY_ENTRIES equ 16
IMAGE_DOS_HEADERSTRUCT
e_magic WORD ?
e_cblp WORD ?
e_cp WORD ?
e_crlc WORD ?
e_cparhdr WORD ?
e_minalloc WORD ?
e_maxalloc WORD ?
e_ss WORD ?
e_sp WORD ?
e_csum WORD ?
e_ip WORD ?
e_cs WORD ?
e_lfarlc WORD ?
e_ovno WORD ?
e_res WORD 4 dup(?)
e_oemid WORD ?
e_oeminfo WORD ?
e_res2 WORD 10 dup(?)
e_lfanew DWORD ?
IMAGE_DOS_HEADERENDS
PIMAGE_DOS_HEADER typedef ptr IMAGE_DOS_HEADER
IMAGE_DATA_DIRECTORYSTRUCT
VirtualAddress DWORD ?
isize DWORD ?
IMAGE_DATA_DIRECTORYENDS
IMAGE_OPTIONAL_HEADER32STRUCT
Magic WORD ?
MajorLinkerVersion BYTE ?
MinorLinkerVersion BYTE ?
SizeOfCode DWORD ?
SizeOfInitializedData DWORD ?
SizeOfUninitializedData DWORD ?
AddressOfEntryPoint DWORD ?
BaseOfCode DWORD ?
BaseOfData DWORD ?
ImageBase DWORD ?
SectionAlignment DWORD ?
FileAlignment DWORD ?
MajorOperatingSystemVersion WORD ?
MinorOperatingSystemVersion WORD ?
MajorImageVersion WORD ?
MinorImageVersion WORD ?
MajorSubsystemVersion WORD ?
MinorSubsystemVersion WORD ?
Win32VersionValue DWORD ?
SizeOfImage DWORD ?
SizeOfHeaders DWORD ?
CheckSum DWORD ?
Subsystem WORD ?
DllCharacteristics WORD ?
SizeOfStackReserve DWORD ?
SizeOfStackCommit DWORD ?
SizeOfHeapReserve DWORD ?
SizeOfHeapCommit DWORD ?
LoaderFlags DWORD ?
NumberOfRvaAndSizes DWORD ?
DataDirectory IMAGE_DATA_DIRECTORYIMAGE_NUMBEROF_DIRECTORY_ENTRIES dup(<>)
IMAGE_OPTIONAL_HEADER32ENDS
IMAGE_OPTIONAL_HEADER equ <IMAGE_OPTIONAL_HEADER32>
PIMAGE_OPTIONAL_HEADER typedef ptr IMAGE_OPTIONAL_HEADER
IMAGE_EXPORT_DIRECTORYSTRUCT
Characteristics DWORD ?
TimeDateStamp DWORD ?
MajorVersion WORD ?
MinorVersion WORD ?
nName DWORD ?
nBase DWORD ?
NumberOfFunctions DWORD ?
NumberOfNames DWORD ?
AddressOfFunctions DWORD ?
AddressOfNames DWORD ?
AddressOfNameOrdinals DWORD ?
IMAGE_EXPORT_DIRECTORYENDS
IMAGE_FILE_HEADERSTRUCT
Machine WORD ?
NumberOfSections WORD ?
TimeDateStamp DWORD ?
PointerToSymbolTable DWORD ?
NumberOfSymbols DWORD ?
SizeOfOptionalHeader WORD ?
Characteristics WORD ?
IMAGE_FILE_HEADERENDS
IMAGE_NT_HEADERSSTRUCT
Signature DWORD ?
FileHeader IMAGE_FILE_HEADER <>
OptionalHeader IMAGE_OPTIONAL_HEADER32 <>
IMAGE_NT_HEADERSENDS
PIMAGE_NT_HEADERS typedef ptr IMAGE_NT_HEADERS
IMAGE_SECTION_HEADERSTRUCT
Name1 db IMAGE_SIZEOF_SHORT_NAME dup(?)
union Misc
PhysicalAddress dd ?
VirtualSize dd ?
ends
VirtualAddress dd ?
SizeOfRawData dd ?
PointerToRawData dd ?
PointerToRelocations dd ?
PointerToLinenumbers dd ?
NumberOfRelocations dw ?
NumberOfLinenumbers dw ?
Characteristics dd ?
IMAGE_SECTION_HEADERENDS
PIMAGE_SECTION_HEADER typedef ptr IMAGE_SECTION_HEADER
IMAGE_BASE_RELOCATIONSTRUCT
VirtualAddress dd ?
SizeOfBlock dd ?
IMAGE_BASE_RELOCATIONENDS
PIMAGE_BASE_RELOCATION typedef ptr IMAGE_BASE_RELOCATION
GetFunctionInformation proto :PUNICODE_STRING,:PUNICODE_STRING,:PFUNCTION_INFORMATION
GetFuncInfoByName proto :PUNICODE_STRING,:PVOID,:PDWORD
GetPEImageBase proto :PVOID
GetPEImageLength proto :PUNICODE_STRING
KLoadLibrary proto :PUNICODE_STRING
ENDIF
;SSDT.inc
;DyanmicHook.inc
IFNDEF __DYNAMICHOOK_INC__
__DYNAMICHOOK_INC__ equ <1>
OPEN_EXISTING equ 3
INVALID_HANDLE_VALUE equ -1
FILE_MAP_READ equ SECTION_MAP_READ
MAX_PATH equ 260
M2M macro M1,M2
push M2
pop M1
ENDM
NT_SUCCESS macro status
mov eax,status
and eax,80000000h
.if eax
mov eax,FALSE
.else
mov eax,TRUE
.endif
ENDM
ENDIF
先贴代码,再来讲原理。根据我们刚才对SSDT的认识,要顺利找到SSDT的位置,首先要确定NtCreateThread的服务序号。不幸的是我尚未找到有效准确的获取方式。我唯一的思路是在ntdll.dll中找到ZwCreateThread,判断如果它的第一字节为0xB8(汇编代码MOV EAX,XXXXXXXX)那么它第二字节开始的双字就是其服务序号。用C语言描述应该是:SysCallIndex = *( (WORD*)((DWORD)GetProcAddress(ntdll,ZwCreateThread)+
1) );
为此,专门写了PEFile.asm这个文件,这个文件提供了一些函数:
GetFunctionInformation proto :PUNICODE_STRING,:PUNICODE_STRING,:PFUNCTION_INFORMATION
GetFuncInfoByName proto :PUNICODE_STRING,:PVOID,:PDWORD
GetPEImageBase proto :PVOID
GetPEImageLength proto :PUNICODE_STRING
KLoadLibrary proto :PUNICODE_STRING
其中,KLoadLibrary将PE文件按加载到内存中(不是读取到内存中,类似PE加载器一样按照PE结构中的描述对其进行加载,以便通过导出表分析导出函数在内存中的位置),其它几个函数就顾名思义了。这些函数的意思我就不再详细解释,因为这也是我3年前的代码,我也记不得太清楚。有了这几个函数,你就能够理解SSDT.asm中的GetSysCallIndex这个函数的原理。当然,我讲了这是我唯一的思路,也希望大家不吝赐教,把你知道的方法告诉我(你别说用0x35硬编码就行)
确定了NtCreateThread的服务序号,下面我们就要在SSDT中寻找NtCreateThread的存放位置以及原始的函数地址。其实这比获取服务序号简单多了,因为一切秘密都在KeDescriptorTable这个导出符号中。需要注意的是前面我们已经说了,并不是所有系统中内核文件名都叫ntoskrnl.exe,所以先用ZwQuerySystemInformation获取一下当前系统中的内核文件名,通过对导出符号KeDescriptorTable的分析,顺利获得NtCreateThread的真实地址。具体的代码在SSDT.asm中,函数比较少,看名字就懂意思,具体我也不详细解释了,再解释又扯得很远,而且这是我3年前的代码。
好了,打开DebugView,用KmdManager加载一下我们生成的内核程序:
与前面LiveKd看到的一样,貌似一切都很顺利。
这一章就讲完了,下一章我们就完成HookSSDT、UnHookSSDT这两个函数,我们把NtCreateThread函数Hook住,监视一下进程的创建,同时我在下一章告诉大家为什么我极不推荐普通人对SSDT使用Inline Hook,虽然它的功能更加强大!顺便希望大家检查下我的代码,谢谢。
页:
[1]