zoukankan      html  css  js  c++  java
  • Win64 驱动内核编程-13.回调监控模块加载

    回调监控模块加载

        模块加载包括用户层模块(.DLL)和内核模块(.SYS)的加载。传统方法要监控这两者加在必须 HOOK 好几个函数,比如 NtCreateSection 和 NtLoadDriver

    等,而且这些方法还不能监控未知的驱动加载方法。其实为了监控模块加载而HOOK API 是非常傻的,因为微软已经提供了一对标准的 API 实现此功能。它们

    分别是 PsSetLoadImageNotifyRoutine 和 PsRemoveLoadImageNotifyRoutine可以设置/取消一个“映像加载通告例程”,当有驱动或者 DLL 被加载时,回调函

    数就会被调用。有人可能认为这个标准方法的监控非常表层,其实恰恰相反,这个方法非常底层,大部分隐秘的加载驱动的方法都可以绕过 NtLoadDriver,但

    是无法绕过“映像加载通告例程”。所以用此方法监控驱动加载是最合适的了。

        之前说过,这个通告例程不仅仅管加载驱动,连进程加载 DLL 也管,那我们怎么判断到底是加载驱动还是加载 DLL 呢?如果说根据后缀名判断则很明显是一个挫方法。我的方

    法是, 根据回调函数 e LoadImageNotifyRoutine  的第二个参数判断,如果 D PID  0 0 ,则表示加载驱动,如果 D PID  位非零,则 表示加载  DLL 。原因很简单,我之前说过这个函数很底层,到了一定的深度之后就无法判断到底是谁主动引发的行为了,一切都是系统的行为。当然,你也可以认为这是通过回调来监控驱动加载的缺点。判断了是驱动后,就通过 ImageInfo->ImageBase 来获取驱动的映像基址。过 如果不想让这个驱动加载,就通过 e ImageBase 得 来获得 y DriverEntry  的地址(ImageBase 就是 DOS 头,根据 DOS 头找到 NT 头,然后在 NT 头的 OptionalHeader里就能找到入口点了。入口点的数据就是 DriverEntry 的地址) , 并 写入 “拒绝访问 ” 的机器码 即可。

    //添加:
    PsSetLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)LoadImageNotifyRoutine);
    //删除:
    PsRemoveLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)LoadImageNotifyRoutine);
     
    其中 NotifyRoutine 是一个函数指针,此回调函数的原型是:
    VOID (*PLOAD_IMAGE_NOTIFY_ROUTINE)
    (
    __in_opt PUNICODE_STRING FullImageName,
    __in HANDLE ProcessId,
    __in PIMAGE_INFO ImageInfo
    );
    下面是实现模块监控,并且拒绝Powertool的驱动加载的例子代码:
    #include <ntddk.h>
    #include <ntimage.h>
     
    #define dprintf	DbgPrint
     
    BOOLEAN VxkCopyMemory( PVOID pDestination, PVOID pSourceAddress, SIZE_T SizeOfCopy )
    {
        PMDL pMdl = NULL;
        PVOID pSafeAddress = NULL;
        pMdl = IoAllocateMdl( pSourceAddress, (ULONG)SizeOfCopy, FALSE, FALSE, NULL );
        if( !pMdl ) return FALSE;
        __try
        {
            MmProbeAndLockPages( pMdl, KernelMode, IoReadAccess );
        }
        __except(EXCEPTION_EXECUTE_HANDLER)
        {
            IoFreeMdl( pMdl );
            return FALSE;
        }
        pSafeAddress = MmGetSystemAddressForMdlSafe( pMdl, NormalPagePriority );
        if( !pSafeAddress ) return FALSE;
        RtlCopyMemory( pDestination, pSafeAddress, SizeOfCopy );
        MmUnlockPages( pMdl );
        IoFreeMdl( pMdl );
        return TRUE;
    }
     
    VOID UnicodeToChar(PUNICODE_STRING dst, char *src)
    {
        ANSI_STRING string;
        RtlUnicodeStringToAnsiString(&string,dst, TRUE);
        strcpy(src,string.Buffer);
        RtlFreeAnsiString(&string);
    }
     
    void DenyLoadDriver(PVOID DriverEntry)
    {
    UCHAR fuck[]="xB8x22x00x00xC0xC3";
    VxkCopyMemory(DriverEntry,fuck,sizeof(fuck));
    }
     
    PVOID GetDriverEntryByImageBase(PVOID ImageBase)
    {
    PIMAGE_DOS_HEADER pDOSHeader;
    PIMAGE_NT_HEADERS64 pNTHeader;
    PVOID pEntryPoint;
    pDOSHeader = (PIMAGE_DOS_HEADER)ImageBase;
    pNTHeader = (PIMAGE_NT_HEADERS64)((ULONG64)ImageBase + pDOSHeader->e_lfanew);
    pEntryPoint = (PVOID)((ULONG64)ImageBase + pNTHeader->OptionalHeader.AddressOfEntryPoint);
    return pEntryPoint;
    }
     
    VOID LoadImageNotifyRoutine
    (
        __in_opt PUNICODE_STRING  FullImageName,
        __in HANDLE  ProcessId,
        __in PIMAGE_INFO  ImageInfo
    )
    {
    PVOID pDrvEntry;
    char szFullImageName[260]={0};
    if(FullImageName!=NULL && MmIsAddressValid(FullImageName))
    {
    if(ProcessId==0)
    {
    DbgPrint("[MyDriver]%wZ
    ", FullImageName);
    pDrvEntry=GetDriverEntryByImageBase(ImageInfo->ImageBase);
    DbgPrint("[MyDriver]DriverEntry: %p
    ",pDrvEntry);
    UnicodeToChar(FullImageName,szFullImageName);
    if(strstr(_strlwr(szFullImageName),"kevp64.sys"))
    {
    DbgPrint("[MyDriver]Deny load [WIN64AST.SYS]");
    //禁止加载win64ast.sys
    DenyLoadDriver(pDrvEntry);
    }
    }
    }
    }
     
    加载:
    PsSetLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)LoadImageNotifyRoutine);
    注销:
    PsRemoveLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)LoadImageNotifyRoutine);


    
    

        执行结果,通过Pchunter看监控当前驱动信息,PowerTool驱动被拒绝加载之后不但自己没有提示,而且还在桌面上留下了自己的驱动文件,这相当于是你双击了一个exe,结果在exe入口函数的地方内存编程不可操作了,这种很难检测出问题来:



    禁止加载驱动的方式也可以用来禁止加载dll


  • 相关阅读:
    getElement方法封装
    使用Ajax (put delete ) django原生CBV 出现csrf token解决办法
    (IO模型介绍,阻塞IO,非阻塞IO,多路复用IO,异步IO,IO模型比较分析,selectors模块,垃圾回收机制)
    协程介绍, Greenlet模块,Gevent模块,Genvent之同步与异步
    Thread类的其他方法,同步锁,死锁与递归锁,信号量,事件,条件,定时器,队列,Python标准模块--concurrent.futures
    线程概念( 线程的特点,进程与线程的关系, 线程和python理论知识,线程的创建)
    进程同步控制(锁,信号量,事件), 进程通讯(队列和管道,生产者消费者模型) 数据共享(进程池和mutiprocess.Pool模块)
    在Python程序中的进程操作,multiprocess.Process模块
    进程前戏 (操作系统简述 什么是进程)
    django ModelForm
  • 原文地址:https://www.cnblogs.com/csnd/p/12062022.html
Copyright © 2011-2022 走看看