发新话题
打印

[转载]内核级利用通用Hook函数方法检测进程

[转载]内核级利用通用Hook函数方法检测进程

文章作者:LionD8
信息来源:绿盟科技

内核级利用通用Hook函数方法检测进程
作者:   LionD8
QQ:   10415468
Email: LionD8@126.com
Blog:   http://blog.csdn.net/LionD8    or http://liond8.126.com

介绍通用Hook的一点思想:
   在系统内核级中,MS的很多信息都没公开,包括函数的参数数目,每个参数的类型等。在系统内核中,访问了大量的寄存器,而很多寄存器的值,是上层调用者提供的。如果值改变系统就会变得不稳定。很可能出现不可想象的后果。另外有时候对需要Hook的函数的参数不了解,所以不能随便就去改变它的堆栈,如果不小心也有可能导致蓝屏。所以Hook的最佳原则是在自己的Hook函数中呼叫原函数的时候,所有的寄存器值,堆栈里面的值和Hook前的信息一样。这样就能保证在原函数中不会出错。一般我们自己的Hook的函数都是写在C文件里面的。例如Hook的目标函数KiReadyThread。
那么一般就自己实现一个:
MyKiReadyThread(...)
{
   ......
   call KiReadyThread
   ......
}
但是用C编译器编译出来的代码会出现一个堆栈帧:
Push ebp
mov  ebp,esp
这就和我们的初衷不改变寄存器的数违背了。所以我们可以自己用汇编来实现MyKiReadyThread。

_func@0 proc
   pushad      ;保存通用寄存器
   call _cfunc@0  ;这里是在进入原来函数前进行的一些处理。
   popad      ;恢复通用寄存器
   push eax  
   mov eax,[esp+4] ;得到系统在call 目标函数时入栈的返回地址。
   mov ds:_OrgRet,eax ;保存在一个临时变量中
   pop eax
   mov [esp],retaddr  ;把目标函数的返回地址改成自己的代码空间的返回地址,使其返回           后能接手继续的处理
   jmp _OrgDestFunction ;跳到原目标函数中
retaddr:
   pushad      ;原函数处理完后保存寄存器
   call _HookDestFunction@0 ;再处理
   popad    ;回复寄存器
   jmp ds:_OrgRet ;跳到系统调用目标函数的下一条指令。
_func@0 endp

当我们要拦截目标API的时候,只要修改原函数头5个字节的机器为一个JMP _func就行了。
然后把原来的5字节保存。在跳入原函数时,恢复那5个字节即可。

Hook KiReadyThread检测系统中的进程:
在线程调度抢占的的时候会调用KiReadyThread,它的原型为
VOID FASTCALL KiReadyThread (IN PRKTHREAD Thread)
在进入KiReadyThread时,ecx指向Thread。
所以完全可以Hook KiReadyThread 然后用ecx的值得到但前线程的进程信息。
KiReadyThread没被ntosknrl.exe导出,所以通过硬编码来。在2000Sp4中地址为0x8043141f

具体实现:
////////////////////////////////
// 1.cpp
////////////////////////////////
#ifdef __cplusplus
extern "C" {
#endif

#include "ntddk.h"
#include "string.h"
#include "ntifs.h"
#include "stdio.h"

#define FILE_DEVICE_EVENT  0x8000

#define IOCTL_PASSBUF \
   CTL_CODE(FILE_DEVICE_EVENT, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)

void DriverUnload (IN PDRIVER_OBJECT pDriverObject);

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath);

void cfunc ();

void HookDestFunction();
NTSTATUS DeviceIoControlDispatch(IN  PDEVICE_OBJECT  DeviceObject,
                      IN  PIRP        pIrp);
extern void func();

void ResumeDestFunction();

const WCHAR devLink[]  = L"\\??\\MyEvent";
const WCHAR devName[]  = L"\\Device\\MyEvent";
UNICODE_STRING       devNameUnicd;
UNICODE_STRING       devLinkUnicd;   

ULONG OrgDestFunction = (ULONG)0x8043141f; //KiReadyThread

char JmpMyCode [] = {0xE9,0x00,0x00,0x00,0x00};
char OrgCode [5];

char OutBuf[128][16];

int Count = 0;

ULONG orgcr0;
#ifdef __cplusplus
}
#endif

VOID DisableWriteProtect( PULONG pOldAttr)
{

    ULONG uAttr;

    _asm
   {
       push eax;
       mov  eax, cr0;
       mov  uAttr, eax;
       and  eax, 0FFFEFFFFh; // CR0 16 BIT = 0
       mov  cr0, eax;
       pop  eax;
   };

    *pOldAttr = uAttr; //保存原有的 CRO 属性

}

VOID EnableWriteProtect( ULONG uOldAttr )
{

  _asm
  {
     push eax;
     mov  eax, uOldAttr; //恢复原有 CR0 属性
     mov  cr0, eax;
     pop  eax;
  };

}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING RegistryPath)
{
   NTSTATUS           Status;
   PDEVICE_OBJECT        pDevice;

   DbgPrint("DriverEntry called!\n");
   RtlInitUnicodeString (&devNameUnicd, devName );
   RtlInitUnicodeString (&devLinkUnicd, devLink );
   Status = IoCreateDevice ( pDriverObject,
   0,
      &devNameUnicd,
   FILE_DEVICE_UNKNOWN,
     0,
     TRUE,
     &pDevice );
      if( !NT_SUCCESS(Status))
      {
      DbgPrint(("Can not create device.\n"));
      return Status;
   }
     Status = IoCreateSymbolicLink (&devLinkUnicd, &devNameUnicd);
     if( !NT_SUCCESS(Status))
     {
        DbgPrint(("Cannot create link.\n"));
        return Status;
     }
     pDriverObject->DriverUnload  = DriverUnload;
     pDriverObject->MajorFunction[IRP_MJ_CREATE] =
   pDriverObject->MajorFunction[IRP_MJ_CLOSE] =
   pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =   DeviceIoControlDispatch;
   
   pDriverObject->DriverUnload = DriverUnload;
   * ( (ULONG*) (JmpMyCode+1) ) = (ULONG)func - (ULONG)OrgDestFunction - 5;
   memcpy(OrgCode,(char*)OrgDestFunction,5);
   HookDestFunction();
   
   return STATUS_SUCCESS;
}

void DriverUnload (IN PDRIVER_OBJECT pDriverObject)
{
   NTSTATUS        status;
   ResumeDestFunction();
   if(pDriverObject->DeviceObject != NULL)
      {
        status=IoDeleteSymbolicLink( &devLinkUnicd );
      if ( !NT_SUCCESS( status ) )
        {
              DbgPrint((  "IoDeleteSymbolicLink() failed\n" ));
        }
        IoDeleteDevice( pDriverObject->DeviceObject );
      }
}

void DisplayName(PKTHREAD Thread)
{
   PKPROCESS Process = Thread->ApcState.Process;
   PEPROCESS pEprocess = (PEPROCESS)Process;
   DbgPrint("ImageFileName = %s \n",pEprocess->ImageFileName);
   sprintf(OutBuf[Count++],"%s",pEprocess->ImageFileName);
}

void cfunc (void)
{
   ULONG PKHeader=0;
   __asm
   {
      mov PKHeader,ecx  //ecx寄存器是KiReadyThread中的PRKTHREAD参数
   }
   ResumeDestFunction();
   
   if ( PKHeader != 0 && Count < 128 )
   {
      DisplayName((PKTHREAD)PKHeader);   
   }   
}

void HookDestFunction()
{
   DisableWriteProtect(&orgcr0);
   memcpy((char*)OrgDestFunction,JmpMyCode,5);
   EnableWriteProtect(orgcr0);   
}

void ResumeDestFunction()
{
   DisableWriteProtect(&orgcr0);
   memcpy((char*)OrgDestFunction,OrgCode,5);
   EnableWriteProtect(orgcr0);
}

NTSTATUS DeviceIoControlDispatch(
                      IN  PDEVICE_OBJECT  DeviceObject,
                      IN  PIRP        pIrp
                      )
{
   PIO_STACK_LOCATION          irpStack;
   NTSTATUS                status;
   PVOID                  inputBuffer;
   ULONG                  inputLength;
   PVOID                  outputBuffer;
   ULONG                  outputLength;
   OBJECT_HANDLE_INFORMATION     objHandleInfo;

   status = STATUS_SUCCESS;
   // 取出IOCTL请求代码
   irpStack = IoGetCurrentIrpStackLocation(pIrp);

   switch (irpStack->MajorFunction)
   {
   case IRP_MJ_CREATE :
      DbgPrint("Call IRP_MJ_CREATE\n");
      break;
   case IRP_MJ_CLOSE:
      DbgPrint("Call IRP_MJ_CLOSE\n");
      break;
   case IRP_MJ_DEVICE_CONTROL:
      DbgPrint("IRP_MJ_DEVICE_CONTROL\n");
      inputLength=irpStack->Parameters.DeviceIoControl.InputBufferLength;
      outputLength=irpStack->Parameters.DeviceIoControl.OutputBufferLength;
      switch (irpStack->Parameters.DeviceIoControl.IoControlCode)
      {
        case   IOCTL_PASSBUF:
        {
           RtlCopyMemory(pIrp->UserBuffer, OutBuf, 20*16);
           
           memset(OutBuf,0,128*16);
           Count = 0;
           break;
        }
        default:
           break;
      }

   default:
      DbgPrint("Call IRP_MJ_UNKNOWN\n");
      break;
   }
   pIrp->IoStatus.Status = status;
   pIrp->IoStatus.Information = 0;
   IoCompleteRequest (pIrp, IO_NO_INCREMENT);
   return status;
}

////////////////////////////////
// 1.asm
////////////////////////////////
.386
.model small

.data
_OrgRet dd 0

.code
public _func@0
extrn _cfunc@0:near
extrn _HookDestFunction@0:near
extrn _OrgDestFunction:DWORD

_func@0 proc
   pushad
   call _cfunc@0
   popad
   push eax
   mov eax,[esp+4]
   mov ds:_OrgRet,eax
   pop eax
   mov [esp],retaddr
   jmp _OrgDestFunction
retaddr:
   pushad
   call _HookDestFunction@0
   popad
   jmp ds:_OrgRet
_func@0 endp
END

//////////////////////////////////////////
// app.cpp
//////////////////////////////////////////

#include <windows.h>
#include <stdio.h>

#define FILE_DEVICE_EVENT  0x8000
#define CTL_CODE( DeviceType, Function, Method, Access ) (            \
   ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \
)

#define FILE_ANY_ACCESS            0
#define METHOD_BUFFERED            0
#define FILE_DEVICE_UNKNOWN         0x00000022

#define IOCTL_PASSBUF \
   CTL_CODE(FILE_DEVICE_EVENT, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)

int main()
{
     HANDLE    hDevice;   
      bool      status;
     ULONG     dwReturn;
      char      outbuf[129][16];
      hDevice = NULL;
      m_hCommEvent = NULL;
      hDevice = CreateFile( "\\\\.\\MyEvent",
              GENERIC_READ|GENERIC_WRITE,
              FILE_SHARE_READ | FILE_SHARE_WRITE,
              NULL,
              OPEN_EXISTING,
              FILE_ATTRIBUTE_NORMAL,
              NULL);
      if(hDevice == INVALID_HANDLE_VALUE)
      {
        printf("createfile wrong\n");
        getchar();
        return 0;
      }
   while(1)
   {
      memset(outbuf,0,129*16);
      status =DeviceIoControl(hDevice,
              IOCTL_PASSBUF,
              NULL,
              0,
              &outbuf,
              128*16,
              &dwReturn,NULL);
      if( !status)
      {
        printf("IO wrong+%d\n", GetLastError());
        getchar();
        return 0;
      }
      int c=0;
      while( *((char*)(&outbuf)+c*16) )
      {
        //把csrss.exe和自身进程信息跳过,因为会产生有大量的信息。
        if ( strcmp((char*)(&outbuf)+c*16,"app.exe") && \
            strcmp((char*)(&outbuf)+c*16,"csrss.exe")  )
           printf("%s\n",(char*)(&outbuf)+c*16);
        c++;
      }
      Sleep(1);
   }
}

试验结果:
......
TTPlayer.exe
System
TTPlayer.exe
vrvmon.exe
TTPlayer.exe
System
System
Explorer.EXE
Explorer.EXE
Explorer.EXE
......
测试,编译环境 2000 Sp4 2000 DDK
没写出线程的隐藏进程代码。不过基本上实现得差不多了,只需要把返回的信息,和Ring3级查询得到的信息进行适时对比就能查出异常进程了。

本人水平有限,如哪里有错误,欢迎高手不吝赐教。
感谢:sinister大哥对小弟的指点及其鼓励
毕业在即将次愚作献给我的母校---重庆科技学院
http://iittss.com/ kijs与牛人在一起不是有理由的让自己变懒,那是为了让视野更开阔

TOP

发新话题