找回密码
 开放注册

QQ登录

只需一步,快速开始

微信登录

微信扫码,快速开始

搜索
查看: 1133|回复: 0

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

[复制链接]

739

主题

468

回帖

4307

牛毛

论坛管理员

狼群

积分
4347
发表于 2008-1-16 19:47:46 | 显示全部楼层 |阅读模式
介绍通用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大哥对小弟的指点及其鼓励
您需要登录后才可以回帖 登录 | 开放注册

本版积分规则

帮助|Archiver|小黑屋|通信管理局专项备案号:[2008]238号|NB5用户社区 ( 皖ICP备08004151号;皖公网安备34010402700514号 )

GMT+8, 2025-1-11 08:45 , Processed in 0.118062 second(s), 23 queries , Yac On.

Powered by Discuz! X3.5

快速回复 返回顶部 返回列表