zoukankan      html  css  js  c++  java
  • 内核中利用系统事件通知监控和拦截进程,线程和模块

    监控进程

    NTSTATUS PsSetCreateProcessNotifyRoutineEx(
      PCREATE_PROCESS_NOTIFY_ROUTINE_EX NotifyRoutine,
      BOOLEAN                           Remove
    );
    

    通过PsSetCreateProcessNotifyRoutineEx注册进程通知回调函数,函数的第一个参数传入进程通知回调函数的地址,第二个参数为FALSE表示注册回调函数,为TRUE表示卸载回调函数。

    void PcreateProcessNotifyRoutineEx(
      PEPROCESS Process,
      HANDLE ProcessId,
      PPS_CREATE_NOTIFY_INFO CreateInfo
    )
    

    上面的函数是回调函数原型,当CreateInfo为NULL时表示进程正在退出,当不为NULL则表示进程正在创建。我们可以通过为最后一个参数来的CreationStatus赋值为STATUS_UNSUCCESSFUL让进程无法创建,达到拦截指定进程的目的。

    监控线程

    NTSTATUS PsSetCreateThreadNotifyRoutine(
      PSCREATETHREADNOTIFYTYPE NotifyType,
    );
    

    通过PsSetCreateThreadNotifyRoutine注册线程通知回调函数,函数的参数传入线程通知回调函数的地址。通过PsRemoteCreateThreadNotifyRoutine来卸载回调函数。

    void PcreateThreadNotifyRoutine(
      HANDLE ProcessId,
      HANDLE ThreadId,
      BOOLEAN Create
    )
    
    

    上面的函数为线程通知回调函数的原型,其中Create为NULL表示线程正在退出,不为NULL表示线程正在创建。

    在对线程进行拦截时要注意几个问题,我们的思路是通过找到线程的ETHREAD,通过其Win32StartAddress字段得到其线程回调函数的地址,然后向线程回调函数入口写入ret指令(0xC3),从而达到使线程直接退出拦截线程的目的。

    • 注意写保护问题
      在保护模式下当控制寄存器CR0的wp位为1时系统不允许内核代码直接修改用户地址空间的只读数据,所以我们需要在修改地址数据前先关闭保护,修改完之后在打开保护。x86平台就直接利用内联汇编写就ok了,但是x64平台vs不支持内联汇编只能通过手写汇编编译成obj文件来调用了。

    并在源程序文件中声明函数

    //去除写保护
    extern "C"
    VOID DisableWriteProtect(PULONG pOldAttr);
    
    
    //还原写保护
    extern "C"
    VOID EnableWriteProtect(ULONG uOldAttr);
    
    • 注意进程上下文的问题
      因为回调函数被调用的时机是当A线程创建B线程时,这时切换到内核中去调用我们的回调函数,此时进程上下文在A线程对应的进程中。此时进程地址空间都为A线程对应的进程的地址空间。一般我们双击图标运行程序时应用程序都是通过文件管理器进程(explorer.exe),而当应用程序第一次运行时其主线程就是由explorer.exe的线程创建的,当主线程运行前会调用我们的线程通知回调函数,此时进程上下文在explorer.exe进程中。如果我们想要修改B进程主线程回调函数入口点地址的内存就需要使进程上下文先切换到B进程的进程地址空间中。修改后再切换回原来的进程上下文中。利用KeAttachProcess和KeDetachProcess。
    void KeAttachProcess(
      PRKPROCESS Process
    );
    
    void KeDetachProcess();
    
    • 注意地址有效性问题

    实际上我们是无法拦截进程的主线程的,因为当explorer.exe在创建进程的主线程时,在新创建进程的地址空间中windows加载器只将PE文件的文件头映射到虚拟地址空间中并为其分配物理内存,而其余的各个区段都还没有映射到虚拟地址空间中,当然也没有为其分配物理内存,此时如果直接先主线程入口点(主线程回调函数的函数入口)处写入数据因为地址是无效的,所以会直接发生异常导致蓝屏!

    我们在向用户地址空间中任意地址处写入数据时都应该向判断地址的有效性防止蓝屏。

    NTKERNELAPI
    BOOLEAN
    MmIsAddressValid (
      PVOID VirtualAddress
    );
    

    监控模块

    NTSTATUS PsSetLoadImageNotifyRoutine(
      PLOAD_IMAGE_NOTIFY_ROUTINE NotifyRoutine
    );
    

    通过PsSetLoadImageNotifyRoutine注册模块通知回调函数,我们的思路是通过修改模块初次加载时映射到进程地址空间的PE文件头来达到拦截模块的目的。

    void PloadImageNotifyRoutine(
      PUNICODE_STRING FullImageName,
      HANDLE ProcessId,
      PIMAGE_INFO ImageInfo
    )
    
    

    上面的函数是模块通知回调函数的原型,其中ImageInfo参数的SystemModeImage字段为1表示加载的sys模块,为0表示加载的dll模块。
    如果是sys模块我们可以通过向sys模块的入口点写入shellcode,"mov eax,0xc0000022, ret"返回拒绝访问错误码让其无法加载、。

  • 相关阅读:
    SlideShowExtender制作相册
    Response.Redirect(),Server.Transfer(),Server.Execute()的区别
    虚方法,抽象类,多态性
    gridview获取当前行索引的方法
    AutoQueryTextBox(AjaxPro.dll)非常值得研究的javascript代码
    abstract & virtual & override & new比较(转)
    Asp.net技巧:gridview获取当前行索引的方法
    js 获取浏览器高度和宽度值
    深入理解abstract class和interface
    c++ 静态数据成员和静态成员函数
  • 原文地址:https://www.cnblogs.com/revercc/p/14614734.html
Copyright © 2011-2022 走看看