科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网网络频道内核级利用通用Hook函数方法检测进程

内核级利用通用Hook函数方法检测进程

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

  在系统内核级中,MS的很多信息都没公开,包括函数的参数数目,每个参数的类型等。在系统内核中,访问了大量的寄存器,而很多寄存器的值,是上层调用者提供的。

作者:51cto 2007年10月20日

关键字: IDS IPS 入侵检测 入侵防御

  • 评论
  • 分享微博
  • 分享邮件

  在系统内核级中,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

  #include

  #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

  ......

  测试、编译环境Windows2000 Sp4、Windows2000 DDK。没写出线程的隐藏进程代码,不过基本上实现得差不多了,只需要把返回的信息,和Ring3级查询得到的信息进行适时对比就能查出异常进程了。

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章