LionD8' Blog

明志. 博学. 笃行

LionD8的公告
最近评论
mldstk:wow power leveling
mldstk:wow power leveling
zhegaozhouji:哇塞~~ 大牛啊!
safer:D8 要买房子了
sweet:不但有全局变量,还有改了入口5个字节的函数,如果可以重入,都会导致问题的。
文章分类
收藏
    相册
    黑黑
    生活中的朋友和同学
    我的相册
    杂物
    ebook
    chinaitlab
    netYi专业电脑电子书库
    中国E书网
    国外站1
    国外站2
    PHP
    php5研究室
    phpchina
    安全相关
    astalavista
    BlackHat
    ccert
    elitehackers
    frsirt
    h4cky0u
    intrusion
    marc.theaimsgroup
    milw0rm
    networkintrusion
    packetstormsecurity
    securityfocus
    securitytracker
    WhiteCell
    中华安全网
    中国讯息安全网
    安全焦点
    幻影旅团
    重庆网络安全小组
    留言本
    朋友的Blog【非IT】
    YY的博客
    无线网络
    无线网络安全
    系统/编程
    CVC
    jiurl Home
    Kendiv的专栏
    OSR Online
    RootKit
    sourceforge
    SysInternals
    TheCodeProject
    VC知识库
    Windows Developer's Journal
    陆麟的主页
    驱动开发网
    兄弟伙的Blog
    alpha兄的好东西
    CDrea' s Blog
    Darkness Blog
    ffantasyYD的专栏
    Gxter的Blog
    heiyeluren's Blog
    lanker 's Blog
    looooo 's blog
    shadow3 's Blog
    孤独's Blog
    慕容小雨的Blog
    梁上君子Blog
    翼帆Blog
    娱乐
    flyinufo的相册
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    原创 内核级利用通用Hook函数方法检测进程收藏

    新一篇: 查找某个目录下的所有文件(包括子目录) | 旧一篇: 5.1---快乐!!!!

    内核级利用通用Hook函数方法检测进程
    作者: LionD8
    QQ: 10415468
    Email: LionD8@126.com
    Blog: http://blog.csdn.net/LionD8 
             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, 128*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大哥对小弟的指点及其鼓励
    毕业在即将次愚作献给我的母校---重庆科技学院

    发表于 @ 2005年05月11日 00:42:00|评论(loading...)|编辑

    新一篇: 查找某个目录下的所有文件(包括子目录) | 旧一篇: 5.1---快乐!!!!

    评论

    #梁上君子 发表于2005-05-11 22:20:00  IP: 218.6.192.*
    女人的心的新宠 --- D8


    呵呵, 好文章, 收藏了
    #ostrich 发表于2005-07-13 11:40:00  IP: 61.186.252.*
    _func@0 使用了全局变量,NT内核是可以重入的,因此结果会兰屏!
    #ostrich 发表于2005-07-13 11:45:00  IP: 61.186.252.*
    _func@0 函数为什么不用嵌入汇编写?
    void cfunc (void)
    {
    ULONG PKHeader=0;
    __asm
    {
    mov PKHeader,ecx //ecx寄存器是KiReadyThread中的PRKTHREAD参数
    ...
    如何保证ecx没有被修改?编译器不能保证。从栈中读不是更安全吗?因为你有pushad.
    #EM 发表于2005-07-14 18:37:00  IP: 61.186.252.*
    编程习惯不是很好,有待改进
    #D8 发表于2005-07-14 21:36:00  IP: 61.186.252.*
    非常感谢ostrich和EM的建议。小弟以后会注意的
    #sweet 发表于2006-10-04 20:53:00  IP: 221.2.77.*
    不但有全局变量,还有改了入口5个字节的函数,如果可以重入,都会导致问题的。
    发表评论  


    当前用户设置只有注册用户才能发表评论。如果你没有登录,请点击登录
    Csdn Blog version 3.1a
    Copyright © LionD8