隐形的翅膀 发表于 2017-6-1 17:26:36

内核态下基于动态感染技术的应用程序执行保护(三 获取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)&amp;&amp; !(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)&amp;&amp; !(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]
查看完整版本: 内核态下基于动态感染技术的应用程序执行保护(三 获取SSDT)