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

介绍通用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
#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级查询得到的信息进行适时对比就能查出异常进程了。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索函数
, 寄存器
, object hook
, void
__hook
内核hook、detours内核hook、内核inline hook、linux内核函数 hook、内核hook消息发送,以便于您获取更多的相关知识。

时间: 2024-07-31 05:43:52

内核级利用通用Hook函数方法检测进程的相关文章

处理时长计算,请高手给个 通用的函数方法

问题描述 现有一个表aaaa 其中e是起始时间(包含日期) f 是终止时间(包含日期)要求 求出从起始时间到终止时间具体是多长时间,具体到小时,下班时间不包括在内,每天8:30上班,18点下班,多谢! 处理时长:关闭时间-发起时间(只计营业时间:8:30-18:00),单位:小时:7.5小时工作时长,中午2小时的休息时间12:00到14:00 .如: 发起时间:2012-12-13 15:40 关闭时间:2012-12-16 15:30发起时间:2012-12-13 15:40 -- 13号处理

[转贴]内核级HOOK的几种实现与应用

内核级HOOK的几种实现与应用 作者:sinister 内核级HOOK的几种实现与应用 Author  : sinister Email   : sinister@whitecell.org HomePage: http://www.whitecell.org       实现内核级 HOOK 对于拦截.分析.跟踪系统内核起着致关重要的作用.实现的方法不同意味着应用侧重点的不同.如想要拦截 NATIVE API 那么可能常用的就是 HOOK SERVICE TABLE 的方法.如果要分析一些系统

PHP实现通用alert函数的方法

 这篇文章主要介绍了PHP实现通用alert函数的方法,实例分析了php自定义alert函数实现提示信息的技巧,非常具有实用价值,需要的朋友可以参考下     本文实例讲述了PHP实现通用alert函数的方法.分享给大家供大家参考.具体如下: 函数:通用提示 参数:提示訊息,類型或網址,窗口名或函數名,延时毫秒 Alert("","function","close2",300); 代码如下: ? 1 2 3 4 5 6 7 8 9 10 11 1

PHP实现通用alert函数的方法_php技巧

本文实例讲述了PHP实现通用alert函数的方法.分享给大家供大家参考.具体如下: 函数:通用提示 参数:提示訊息,類型或網址,窗口名或函數名,延时毫秒 Alert("","function","close2",300); 代码如下: function Alert($Str,$Typ="back",$TopWindow="",$Tim=100){ echo "<script>"

利用PHP绘图函数实现简单验证码功能的方法_php实例

index.php <?php //===================================>>使用绘图技术绘制验证码 //1.随机产生4个随机数 $checkCode=""; for ($i=0;$i<4;$i++){ $checkCode.=dechex(rand(1, 15));// decheck()十进制转换为十六进制,即验证码上要显示的数字 } //2.存入列 session_start(); $_SESSION['checkCode'

linux内核空间与用户空间信息交互方法

     摘要:在进行设备驱动程序,内核功能模块等系统级开发时,通常需要在内核和用户程序之间交换信息.Linux提供了多种方法可以用来完成这些任务.本文总结了各种常用的信息交换方法,并用简单的例子演示这些方法各自的特点及用法.其中有大家非常熟悉的方法,也有特殊条件下方可使用的手段.通过对比明确这些方法,可以加深我们对Linux内核的认识,更重要的是,可以让我们更熟练驾御linux内核级的应用开发技术. 内核空间(kernel-space) VS 用户空间(user-space) 作为一个Linu

这种新型EDR工具可实现秒级对全IT资产的检测与修复

本文讲的是这种新型EDR工具可实现秒级对全IT资产的检测与修复,检测时间和修复时间,决定是安全事件还是数据泄露.目前,虽然市面上有很多新产品帮助安全团队检测事件,但是能够帮助IT运营团队快速修复事件影响的工具却极少见. 问题之一,就是检测和修复是两个相互独立的操作,而且它们分别由安全团队和IT团队执行.但安全并非IT的唯一客户--IT还要响应合规.审计和公司内几乎所有运营部门的改善或新增App请求. 结果就是,今天众多威胁检测系统产生的大量误报,让本已沉重不堪的工作量更加难以完成了.1000名I

form文本域的通用校验函数

名称:form文本域的通用校验函数作用:检测所有必须非空的input文本,比如姓名,账号,邮件地址等等.该校验现在只针对文本域,如果要针对form里面的其他域对象,可以改变判断条件.使用方法:在要检测的文本域中加入title文字.文字是在提示信息,你要提示给用户的该字段的中文名.比如要检测用户名html如下<input name="txt_1" title="姓名">,当然,最好用可视化工具比如dreamweaver什么的来编辑域.如果要检测数字类型数据

用HOOK函数自动关闭IE广告窗口

在用IE浏览某些网站的时候,网站主页会弹出一些广告网页.所以每当打开这样的网页时,总得手动关闭广告页,感觉比较麻烦.那么,是否可以编写程序来判断打开的网页是否是弹出广告,然后自动关闭这些广告,避免每次手动关闭弹出窗口的麻烦?在一些报刊上介绍了某些解决方法,以下就通过钩子(HOOK)函数截获消息的方法进行讨论: 1.弹出式广告框也是IE浏览窗口,一般来说,它是一个无菜单.无工具栏窗口.所以可以在桌面上打开一个窗口时,首先判断该窗口类型是否是"IEFrame",接着判断IEFrame的子窗