内核态下基于动态感染技术的应用程序执行保护(三 获取SSDT)

(转载请注明博客地址: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:[EDX]

7C92D1BA C2 2000 RETN 20

注意MOV EDX,7FFE0300/CALL DWORD PTRDS:[EDX]这两条指令,从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来观察内核中这个结构的数据:

内核态下基于动态感染技术的应用程序执行保护(三 获取SSDT)

可见系统中有0x11C个SSDT项,基地址为0x80505480,我们来看看SSDT表的内容,用d 80505480命令:

内核态下基于动态感染技术的应用程序执行保护(三 获取SSDT)

从0x80505480开始,存放每个系统服务项的函数地址,那么怎么确定内核NtCreateThread的地址呢?答案就在用户态下ZwCreateThread的mov eax,0x35这条指令,它指明了NtCreateThread在SSDT中是第0x35项,计算一下0x80505480+0x35*4=0x80505554,看一看:


大约NtCreateThread应该在0x805D2018这个地址,用u nt!NtCreateThread命令看一下:

内核态下基于动态感染技术的应用程序执行保护(三 获取SSDT)

正是这个地址!(大家肯定会比较奇怪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 [eax].DriverUnload,offset DriverUnload

mov [eax].MajorFunction[IRP_MJ_CREATE*(sizeof PVOID)],offsetDispatchCreateClose

mov [eax].MajorFunction[IRP_MJ_CLOSE*(sizeof PVOID)],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,[edi].OptionalHeader.SectionAlignment

xor edx,edx

mov eax,[edi].OptionalHeader.SizeOfHeaders

mov ecx,dwAlignment

div ecx

.if edx==0

mov eax,[edi].OptionalHeader.SizeOfHeaders

add dwResult,eax

.else

inc eax

mul dwAlignment

add dwResult,eax

.endif

mov edi,pNtHeaders

assume edi:ptrIMAGE_NT_HEADERS

movzx eax,[edi].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,[edi].Misc.VirtualSize

.if eax

xor edx,edx

mov ecx,dwAlignment

div ecx

.if edx==0

mov eax,[edi].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 [edi].e_magic!=IMAGE_DOS_SIGNATURE

jmp @F

.endif

mov esi,ppDosHeader

.if esi

mov dwordptr [esi],edi

.endif

add edi,[edi].e_lfanew

assume edi:ptrIMAGE_NT_HEADERS

.if [edi].Signature!=IMAGE_NT_SIGNATURE

jmp @F

.endif

mov esi,ppNtHeaders

.if esi

mov dwordptr [esi],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,[edi].OptionalHeader.SizeOfHeaders

M2M dwAlignment,[edi].OptionalHeader.SectionAlignment

invoke GetAlignedSize,[edi].OptionalHeader.SizeOfHeaders,dwAlignment

add pPtr,eax

movzx eax,[edi].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,[edi].SizeOfRawData

.if eax>0

.if eax>[edi].Misc.VirtualSize

mov eax,[edi].Misc.VirtualSize

.endif

mov esi,pBaseAddress

add esi,[edi].PointerToRawData

invoke memcpy,pPtr,esi,eax

invoke GetAlignedSize,[edi].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 [edi].e_magic!=IMAGE_DOS_SIGNATURE

jmp @F

.endif

add edi,[edi].e_lfanew

assume edi:ptrIMAGE_NT_HEADERS

.if [edi].Signature!=IMAGE_NT_SIGNATURE

jmp @F

.endif

M2M dwReturn,[edi].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 [esi],0

.endif

invoke KeGetCurrentIrql

.if eax!=PASSIVE_LEVEL

jmp _RETURN

.endif

mov esi,pBaseAddress

assume esi:ptrIMAGE_DOS_HEADER

add esi,[esi].e_lfanew

assume esi:ptrIMAGE_NT_HEADERS

mov eax,[esi].OptionalHeader.DataDirectory.VirtualAddress

.if !eax

jmp _RETURN

.endif

add eax,pBaseAddress

mov edi,eax

assume edi:ptrIMAGE_EXPORT_DIRECTORY

mov eax,[edi].AddressOfNames

add eax,pBaseAddress

mov lpAddressOfNames,eax

mov eax,[edi].AddressOfNameOrdinals

add eax,pBaseAddress

mov lpAddressOfNameOrdinals,eax

mov eax,[edi].AddressOfFunctions

add eax,pBaseAddress

mov esi,eax

mov ecx,[edi].NumberOfFunctions

mov dwIndex,0

@@:

pushad

mov eax,dwIndex

push edi

mov ecx,[edi].NumberOfNames

cld

mov edi,lpAddressOfNameOrdinals

repnz scasw

.if ZERO?

sub edi,lpAddressOfNameOrdinals

sub edi,2

shl edi,1

add edi,lpAddressOfNames

mov eax,dwordptr [edi]

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 [esi]

pop edi

mov esi,pOrdinal

.if esi

mov ecx,dwIndex

add ecx,[edi].nBase

mov dword ptr [esi],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,[esi].dwOridnal

invoke GetFuncInfoByName,pusFuncName,pBaseAddress,edi

.if eax

lea edi,[esi].dwAddress

mov dword ptr [edi],eax

lea edi,[esi].byEntryCode

mov esi,[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[260]: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,[eax].Base

lea edi,[eax].ImageName

movzx eax,[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 [edi].e_magic!=IMAGE_DOS_SIGNATURE

jmp @F

.endif

add edi,[edi].e_lfanew

assume edi:ptrIMAGE_NT_HEADERS

.if [edi].Signature!=IMAGE_NT_SIGNATURE

jmp @F

.endif

push [edi].OptionalHeader.ImageBase

pop dwImageBase

mov eax,[edi].OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC*sizeofIMAGE_DATA_DIRECTORY].VirtualAddress

movzx ecx,[edi].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|| [edi].VirtualAddress

mov bFirstChunk,FALSE

mov esi,edi

add esi,sizeof IMAGE_BASE_RELOCATION

assume esi:ptr IMAGE_FIXUP_ENTRY

mov edx,[edi].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 [esi]

and ax,0F000h

ror ax,12

.if ax==IMAGE_REL_BASED_HIGHLOW

inc dwFixups

mov ax,word ptr [esi]

and ax,0FFFh

movzx eax,ax

add eax,[edi].VirtualAddress

add eax,hModule

mov ecx,eax

mov eax,dword ptr [eax]

sub eax,dwImageBase

.if eax==dwKSDT

mov eax,ecx

sub eax,2

mov ax,word ptr [eax]

.if ax==5C7h

mov eax,ecx

add eax,4

mov eax,dword ptr [eax]

sub eax,dwImageBase

mov dwReturn,eax

jmp @F

.endif

.endif

.endif

pop edx

inc edx

add esi,sizeof WORD

.endw

add edi,[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 [edi]

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 [eax]==0B8h

inc eax

mov eax,dword ptr [eax]

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?

isizeDWORD ?

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 ?

MajorVersionWORD ?

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加载一下我们生成的内核程序:

内核态下基于动态感染技术的应用程序执行保护(三 获取SSDT)

与前面LiveKd看到的一样,貌似一切都很顺利。

这一章就讲完了,下一章我们就完成HookSSDT、UnHookSSDT这两个函数,我们把NtCreateThread函数Hook住,监视一下进程的创建,同时我在下一章告诉大家为什么我极不推荐普通人对SSDT使用Inline Hook,虽然它的功能更加强大!顺便希望大家检查下我的代码,谢谢。

你可能感兴趣的:(应用程序)