驱动篇——总结与提升

写在前面

  此系列是本人一个字一个字码出来的,包括示例和实验截图。由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新。 如有好的建议,欢迎反馈。码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作。如想转载,请把我的转载信息附在文章后面,并声明我的个人信息和本人博客地址即可,但必须事先通知我

你如果是从中间插过来看的,请仔细阅读 羽夏看Win系统内核——简述 ,方便学习本教程。

  看此教程之前,问几个问题,基础知识储备好了吗?上一节教程学会了吗?上一节课的练习做了吗?没有的话就不要继续了。


🔒 华丽的分割线 🔒


练习及参考

本次答案均为参考,可以与我的答案不一致,但必须成功通过。

1️⃣ 实现一个工具,利用未导出的函数PspTerminateProcess杀死软件(驱动的加载可不用代码实现,使用本教程工具进行加载)。

🔒 点击查看答案 🔒


  效果和上篇教程一样,就给个效果图来看看吧:


🔒 点击查看驱动代码 🔒
#include <ntddk.h>

typedef  NTSTATUS (__stdcall *PspTerminateProcess)(INT32,INT32);
PspTerminateProcess pspTerminateProcess;

UNICODE_STRING Devicename;
UNICODE_STRING SymbolLink;

#define DEVICE_NAME L"\\Device\\APPKiller"
#define SYMBOL_LINK L"\\??\\APPKiller"

//操作码:0x0-0x7FF 被保留,0x800-0xFFF 可用
#define KillAPP CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS)

NTSTATUS DEVICE_CONTROL_Dispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp);
NTSTATUS DEVICE_CREATE_Dispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp);
NTSTATUS DEVICE_CLOSE_Dispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp);

NTSTATUS UnloadDriver(PDRIVER_OBJECT DriverObject)
{
    DbgPrint("卸载成功!!!\n");
}

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{

    DriverObject->DriverUnload = UnloadDriver;

    UNICODE_STRING ntkrnl;
    RtlInitUnicodeString(&ntkrnl, L"ntoskrnl.exe");

    LIST_ENTRY* lethis = (LIST_ENTRY*)DriverObject->DriverSection;
    LIST_ENTRY* item = lethis;
    DRIVER_OBJECT obj;

    UINT32 DllBase = 0;
    while (1)
    {
        PUNICODE_STRING name = (PUNICODE_STRING)(((UINT32)item) + 0x2c);

        if (!RtlCompareUnicodeString(name,&ntkrnl,FALSE))
        {
            DllBase = *(UINT32*)(((UINT32)item) + 0x18);
            break;
        }

        item = item->Blink;
        if (item == lethis)
        {
            break;
        }
    }

    if (!DllBase)
    {
        DbgPrint("获取内核文件失败!");
        return STATUS_UNSUCCESSFUL;
    }

    pspTerminateProcess = (PspTerminateProcess)(DllBase + 0xF1DA4);

    RtlInitUnicodeString(&Devicename, DEVICE_NAME);

    DEVICE_OBJECT dobj;
    IoCreateDevice(DriverObject, 0, &Devicename, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE,&dobj);

    RtlInitUnicodeString(&SymbolLink, SYMBOL_LINK);

    IoCreateSymbolicLink(&SymbolLink, &Devicename);

    DriverObject->Flags |= DO_BUFFERED_IO;

    DriverObject->MajorFunction[IRP_MJ_CREATE] = DEVICE_CREATE_Dispatch;
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DEVICE_CONTROL_Dispatch;
    DriverObject->MajorFunction[IRP_MJ_CLOSE] = DEVICE_CLOSE_Dispatch;

    return STATUS_SUCCESS;
}

NTSTATUS DEVICE_CONTROL_Dispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{

    NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
    PIO_STACK_LOCATION pIrqlStack;
    ULONG uIoControlCode;
    PVOID pIoBuffer;
    ULONG uInLength;
    ULONG uOutLength;
    ULONG uRead;
    ULONG uWrite;

    //获取IRP教据
    pIrqlStack = IoGetCurrentIrpStackLocation(pIrp);
    //获取控制码
    uIoControlCode = pIrqlStack->Parameters.DeviceIoControl.IoControlCode;
    //获取缓冲区地址(输入和输出的缓冲区都是一个
    pIoBuffer = pIrp->AssociatedIrp.SystemBuffer;
    //3环发送的数据字节数
    uInLength = pIrqlStack->Parameters.DeviceIoControl.InputBufferLength;
    //0环发送的数据字节数
    uOutLength = pIrqlStack->Parameters.DeviceIoControl.OutputBufferLength;

    switch (uIoControlCode)
    {
        case KillAPP:
            RtlMoveMemory(&uRead, pIoBuffer, 4);
            status = pspTerminateProcess(uRead, 0);
            break;
        default:
            break;
    }

    //设置返回状态,否则默认是失败
    pIrp->IoStatus.Status = status;
    pIrp->IoStatus.Information = 0;    //返回给3环多少字节数据,没有填0
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    return STATUS_SUCCESS;
}

NTSTATUS DEVICE_CREATE_Dispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{

    DbgPrint("Create Success!\n");

    //设置返回状态,否则默认是失败
    pIrp->IoStatus.Status = STATUS_SUCCESS;
    pIrp->IoStatus.Information = 0;    //返回给3环多少字节数据,没有填0
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    return STATUS_SUCCESS;
}

NTSTATUS DEVICE_CLOSE_Dispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{

    DbgPrint("Close Success!\n");

    //设置返回状态,否则默认是失败
    pIrp->IoStatus.Status = STATUS_SUCCESS;
    pIrp->IoStatus.Information = 0;    //返回给3环多少字节数据,没有填0
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    return STATUS_SUCCESS;
}
🔒 点击查看3环代码 🔒
#include "stdafx.h"
#include <windows.h>
#include <winioctl.h>
#include <stdlib.h>

//操作码:0x0-0x7FF 被保留,0x800-0xFFF 可用
#define KillAPP CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define SYMBOL_LINK_NAME L"\\\\.\\APPKiller"

HANDLE g_Device;

int main(int argc, char* argv[])
{
    //获取驱动链接对象句柄
    g_Device=CreateFileW(SYMBOL_LINK_NAME,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
    if (g_Device==INVALID_HANDLE_VALUE)
    {
        puts("访问驱动符号链接失败!");
        goto endproc;
    }

    DWORD pEprocess;
    DWORD outBuffer;

    while (1)
    {
        puts("请输入需要关闭程序的 EPROCESS :");
        scanf("%x",&pEprocess);

        if (!pEprocess)
        {
            break;
        }

        if (DeviceIoControl(g_Device,KillAPP,&pEprocess,sizeof(DWORD),&outBuffer,sizeof(DWORD),NULL,NULL))
        {
            puts("关闭命令正在发送,请查看程序是否退出……");
        }
    }

    CloseHandle(g_Device);
endproc:
    system("pause");
    return 0;
}

篇章总结

  本篇章节我们介绍了学习驱动的目的,驱动的最基本的知识。比如什么是驱动空间?什么是驱动模块?驱动如何和3环进行常规的通信?学完这些东西,如果写驱动的话这些知识还是远远不够的,因为我只介绍了最基本的,如果实现高级功能还是需要多查资料。
  既然是总结。那么我们为了更好的总结。我们将写一个项目,即跨进程监控MessageBox这个函数。之前学Windows系统内核篇之前我们只能对自己的当前进程进行监控,现在我们可以利用我们现有的知识,进行对MessageBox这个函数进行挂钩。
  对于这个项目,你可能会用到的知识点如下:

1. 段页的知识,过写保护
2. 挂钩子
3. 写ShellCode
4. 门的相关知识
5. 自行加载驱动

  我们之前一直都没讲解如何利用代码挂载驱动,都是使用工具。本篇将会介绍如何用代码进行挂载和卸载驱动,并且对挂载驱动的API的细节进行分析。

前言

  关于提升模块,我们将介绍驱动相关的如下知识:

1. 进一步隐藏驱动
2. 用代码实现驱动的加载和卸载
3. 写保护是什么?如何过写保护?

  注意,学习提升模块。你必须具有羽夏看Win系统内核——保护模式篇的所有基本知识点、使用使用C语言开发软件的经验、如何调试驱动源码的知识,并且把之前的练习逐个按要求做完。否则学习本篇就是对你时间的浪费,后面讲解的系统调用篇更是举步维艰。

进一步隐藏驱动

  我们之前有一个练习,使用断链的方式隐藏驱动。但不幸的是,还是被PCHunter这工具发现了。这是为什么呢?是由于我们每一个驱动,他都有自己固有的特征,我们需要分析一下。
  先来看看我们的驱动是如何被加载到内存的,我们用下面的代码配合WinDbg进行检测:

#include <ntddk.h>

NTSTATUS UnloadDriver(PDRIVER_OBJECT DriverObject)
{
}

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
    DriverObject->DriverUnload = UnloadDriver;
    DbgBreakPoint();    //一个宏,就是 int 3 断点。
    return STATUS_SUCCESS;
}

  代码是不是很简单,编译一下,拖到虚拟机中加载,然后WinDbg接到了断点,然后我们再输入k命令,这个指令的意思是查看当前的调用堆栈:

kd> k
 ## ChildEBP RetAddr
00 bad03c7c 8057777f     HelloDriver!DriverEntry+0xd [E:\HelloDriver\main.c @ 10]
01 bad03d4c 8057788f     nt!IopLoadDriver+0x66d
02 bad03d74 80535c02     nt!IopLoadUnloadDriver+0x45
03 bad03dac 805c7160     nt!ExpWorkerThread+0x100
04 bad03ddc 80542dd2     nt!PspSystemThreadStartup+0x34
05 00000000 00000000     nt!KiThreadStartup+0x16

  我们看到调用DriverEntry之前调用了IopLoadDriver这个函数,到IDA定位到这个函数看看。

  可以看出这个函数相当复杂,我们可以F5一下,但是肯定信息不全。我们可以参考WRK源码进行分析。毕竟WRK和真实拿到的内核文件是不一样的,但是对于这个函数,分析发现其实和WRK的源码几乎是一样的,不过由于有些局部变量对应的寄存器和堆栈位置是一样(猜测是优化的原因)或者不同伪变量其实是一个变量,导致分析重命名之类的操作IDA出现错误,感兴趣的自己打开折叠看看:

🔒 点击查看伪代码 🔒
// local variable allocation has failed, the output may be wrong!
NTSTATUS __stdcall IopLoadDriver(HANDLE KeyHandle, BOOLEAN CheckForSafeBoot, BOOLEAN IsFilter, NTSTATUS *DriverEntryStatus)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

  v29 = DriverEntryStatus;
  *DriverEntryStatus = NULL;
  Handle = KeyHandle;
  keyBasicInformation_1 = NULL;
  Length = NULL;
  MaximumLength_2 = NULL;
  serviceNameBuffer_1 = NULL;
  Destination.Buffer = NULL;
  baseName.Buffer = NULL;
  status = NtQueryKey(KeyHandle, KeyBasicInformation, NULL, NULL, &ResultLength);
  if ( status != STATUS_BUFFER_OVERFLOW && status != STATUS_BUFFER_TOO_SMALL )
  {
    status = STATUS_ILL_FORMED_SERVICE_ENTRY;
IopLoadExit0:
    HeadlessKernelAddLogEntry(3, NULL);
    goto IopLoadExit;
  }
  keyBasicInformation = ExAllocatePoolWithTag(NonPagedPool, ResultLength + 8, 0x20206F49u);
  keyBasicInformation_1 = keyBasicInformation;
  if ( !keyBasicInformation )
  {
LABEL_60:
    status = STATUS_INSUFFICIENT_RESOURCES;
    goto IopLoadExit0;
  }
  status = NtQueryKey(Handle, KeyBasicInformation, keyBasicInformation, ResultLength, &ResultLength);
  if ( status >= NULL )
  {
    baseName.Length = keyBasicInformation[6];
    baseName.MaximumLength = baseName.Length + 8;
    baseName.Buffer = keyBasicInformation + 8;
    serviceNameBuffer = ExAllocatePoolWithTag(PagedPool, baseName.Length + 2, 0x20206F49u);
    serviceNameBuffer_1 = serviceNameBuffer;
    if ( serviceNameBuffer )
    {
      Length = baseName.Length;
      MaximumLength_2 = baseName.Length + 2;
      qmemcpy(serviceNameBuffer, baseName.Buffer, baseName.Length);
      *(&serviceNameBuffer_1->Length + (Length >> 1)) = NULL;
    }
    RtlAppendUnicodeToString(&baseName, L".SYS");
    HeadlessKernelAddLogEntry(1, &baseName);
    if ( CheckForSafeBoot )
    {
      if ( InitSafeBootMode )
      {
        RtlInitUnicodeString(&DestinationString, L"Group");
        memset(&KeyValue, 0, 0x4Cu);
        if ( NtQueryValueKey(Handle, &DestinationString, KeyValuePartialInformation, &KeyValue, 0x4Cu, &length) < 0
          || (LOWORD(DestinationString) = LOWORD(KeyValue.DataLength) - 2,
              HIWORD(DestinationString) = LOWORD(KeyValue.DataLength) - 2,
              *&PreviousMode = KeyValue.Data,
              !IopSafebootDriverLoad(&DestinationString)) )
        {
          if ( !IopSafebootDriverLoad(&baseName) )
          {
            IopBootLog(&baseName, NULL);
            DbgPrint("SAFEBOOT: skipping device = %wZ(%wZ)\n", &baseName, &DestinationString);
            HeadlessKernelAddLogEntry(2, NULL);
            return xHalReferenceHandler(v49, v22, v23, v24, v25, v26, v27);
          }
        }
      }
    }
    ExAcquireResourceSharedLite(&PsLoadedModuleResource, 1u);
    for ( i = PsLoadedModuleList; i != &PsLoadedModuleList; i = *i )
    {
      if ( RtlEqualString(&baseName, (i + 36), 1u) )
      {
        status = STATUS_IMAGE_ALREADY_LOADED;
        ExReleaseResourceLite(&PsLoadedModuleResource);
        IopBootLog(&baseName, 1);
        baseName.Buffer = NULL;
        goto LABEL_21;
      }
    }
    ExReleaseResourceLite(&PsLoadedModuleResource);
    status = IopBuildFullDriverPath(&Length, Handle, &baseName);
    if ( status < NULL )
    {
      baseName.Buffer = NULL;
      goto LABEL_80;
    }
    status = IopGetDriverNameFromKeyNode(Handle, &Destination);
    if ( status >= NULL )
    {
      v24 = &Destination;
      v22 = 24;
      v23 = NULL;
      v25 = 16;
      v26 = NULL;
      v27 = NULL;
      v10 = MmLoadSystemImage(&baseName, NULL, NULL, NULL, &P, &BaseAddress);
      status = v10;
      if ( v10 < NULL )
      {
        if ( v10 == STATUS_IMAGE_ALREADY_LOADED )
        {
          status = ObOpenObjectByName(&v22, IoDriverObjectType, NULL, NULL, NULL, NULL, &v40);
          if ( status < NULL )
          {
            IopBootLog(&baseName, NULL);
            if ( status == STATUS_OBJECT_NAME_NOT_FOUND )
              status = STATUS_DRIVER_FAILED_PRIOR_UNLOAD;
LABEL_80:
            if ( status >= NULL )
              goto LABEL_21;
            goto LABEL_81;
          }
          PreviousMode = KeGetCurrentThread()->PreviousMode;
          status = ObReferenceObjectByHandle(v40, NULL, IoDriverObjectType, PreviousMode, &Object, NULL);
          NtClose(v40);
          if ( status >= NULL )
          {
            status = IopResurrectDriver(Object);
            ObfDereferenceObject(Object);
          }
        }
LABEL_55:
        IopBootLog(&baseName, NULL);
        goto LABEL_80;
      }
      RtlImageNtHeader(BaseAddress);
      status = IopPrepareDriverLoading(&Length, Handle, BaseAddress, IsFilter);
      if ( status < NULL )
      {
        MmUnloadSystemImage(P);
        goto LABEL_55;
      }
      PreviousMode = KeGetCurrentThread()->PreviousMode;
      status = ObCreateObject(PreviousMode, IoDriverObjectType, &v22, NULL, NULL, 196, NULL, NULL, &PreviousMode);
      Pdriver = *&PreviousMode;
      if ( status < NULL )
        goto LABEL_55;
      memset(*&PreviousMode, 0, 0xC4u);
      Pdriver->DriverExtension = &Pdriver[1];
      *&Pdriver[1].Type = Pdriver;
      for ( ReturnLength = NULL; ReturnLength <= 0x1B; ++ReturnLength )
        Pdriver->MajorFunction[ReturnLength] = IopInvalidDeviceRequest;
      Pdriver->Type = 4;
      Pdriver->Size = 168;
      v13 = RtlImageNtHeader(BaseAddress);
      v14 = (BaseAddress + v13->OptionalHeader.AddressOfEntryPoint);
      if ( (v13->OptionalHeader.DllCharacteristics & 0x2000) == 0 )
        Pdriver->Flags |= 2u;
      Pdriver->DriverInit = v14;
      Pdriver->DriverSection = P;
      Pdriver->DriverStart = BaseAddress;
      Pdriver->DriverSize = v13->OptionalHeader.SizeOfImage;
      status = ObInsertObject(Pdriver, NULL, 1u, NULL, NULL, &v40);
      if ( status < NULL )
        goto LABEL_55;
      ObReferenceObjectByHandle(v40, NULL, IoDriverObjectType, KeGetCurrentThread()->PreviousMode, &length, NULL);
      v15 = length;
      *&v35 = length;
      NtClose(v40);
      v15[4].Buffer = &CmRegistryMachineHardwareDescriptionSystemName;
      v16 = ExAllocatePoolWithTag(PagedPool, Destination.MaximumLength, 0x20206F49u);
      *&v15[4].Length = v16;
      if ( v16 )
      {
        HIWORD(v15[3].Buffer) = Destination.MaximumLength;
        LOWORD(v15[3].Buffer) = Destination.Length;
        qmemcpy(v16, Destination.Buffer, Destination.MaximumLength);
        v15 = *&v35;
      }
      v17 = ExAllocatePoolWithTag(NonPagedPool, 0x1000u, 0x20206F49u);
      Object = v17;
      if ( !v17 )
      {
        ObMakeTemporaryObject(v15);
        ObfDereferenceObject(v15);
        goto LABEL_60;
      }
      status = NtQueryObject(Handle, ObjectNameInformation, v17, 0x1000u, &ReturnLength);
      if ( status < NULL )
      {
        ObMakeTemporaryObject(v15);
        ObfDereferenceObject(v15);
        ExFreePoolWithTag(Object, NULL);
        goto LABEL_80;
      }
      if ( serviceNameBuffer_1 )
      {
        *(*&v15[3].Length + 16) = ExAllocatePoolWithTag(NonPagedPool, MaximumLength_2, 0x20206F49u);
        v18 = *&v15[3].Length;
        if ( *(v18 + 16) )
        {
          *(v18 + 14) = MaximumLength_2;
          *(*&v15[3].Length + 12) = Length;
          qmemcpy(*(*&v15[3].Length + 16), serviceNameBuffer_1, MaximumLength_2);
          v15 = *&v35;
        }
      }
      v19 = (v15[5].Buffer)(v15, Object);
      status = v19;
      *v29 = v19;
      if ( v19 < NULL )
        status = STATUS_FAILED_DRIVER_ENTRY;
      for ( ReturnLength = NULL; ReturnLength <= 0x1B; ++ReturnLength )
      {
        v20 = &v15[7].Length + 2 * ReturnLength;
        if ( !*v20 )
          *v20 = IopInvalidDeviceRequest;
      }
      ExFreePoolWithTag(Object, NULL);
      if ( status < NULL )
      {
LABEL_78:
        ObMakeTemporaryObject(v15);
        ObfDereferenceObject(v15);
        goto LABEL_80;
      }
      if ( !IopIsLegacyDriver(v15) )
      {
        status = IopPnpDriverStarted(v15, Handle, &Length);
        if ( status >= NULL )
          goto LABEL_79;
        v21 = v15[6].Buffer;
        if ( v21 )
        {
          *&v15[1].Length |= 1u;
          (v21)(v15);
          IopBootLog(&baseName, NULL);
        }
      }
      if ( status < NULL )
        goto LABEL_78;
LABEL_79:
      IopBootLog(&baseName, 1);
      MmFreeDriverInitialization(v15[2].Buffer);
      IopReadyDeviceObjects(v15);
      goto LABEL_80;
    }
  }
LABEL_81:
  if ( status != STATUS_IMAGE_ALREADY_LOADED )
    goto IopLoadExit0;
LABEL_21:
  HeadlessKernelAddLogEntry(2, NULL);
IopLoadExit:
  if ( Destination.Buffer )
    ExFreePoolWithTag(Destination.Buffer, NULL);
  if ( keyBasicInformation_1 )
    ExFreePoolWithTag(keyBasicInformation_1, NULL);
  if ( serviceNameBuffer_1 )
    ExFreePoolWithTag(serviceNameBuffer_1, NULL);
  if ( baseName.Buffer )
    ExFreePoolWithTag(baseName.Buffer, NULL);
  if ( status < NULL && status != STATUS_PLUGPLAY_NO_DEVICE && status != STATUS_IMAGE_ALREADY_LOADED )
  {
    IopDriverLoadingFailed(Handle, NULL);
    if ( IopGetRegistryValue(Handle, "E", &v29) >= NULL )
    {
      v8 = v29;
      if ( *(v29 + 3) )
        CmBootLastKnownGood(*(v29 + *(v29 + 2)));
      ExFreePoolWithTag(v8, NULL);
    }
  }
  ObCloseHandle(Handle, NULL);
  return xHalReferenceHandler(v49, v22, v23, v24, v25, v26, v27);
}

  所以我们直接看WRK更加直接明了。我们就拿着WRK中这个函数源码进行分析,尤其注意与我们驱动对象相关的代码。
  经过阅读代码可知,前期是读取注册表获取驱动信息,然后申请资源然后在PsLoadedModuleList这个全局变量插入一个驱动对象,也就是我们断链实现隐藏驱动的地方。然后进行一系列的操作和判断,最终到了初始化驱动对象信息的部分:

RtlZeroMemory( driverObject, sizeof( DRIVER_OBJECT ) + sizeof ( DRIVER_EXTENSION) );
driverObject->DriverExtension = (PDRIVER_EXTENSION) (driverObject + 1);
driverObject->DriverExtension->DriverObject = driverObject;

for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) {
    driverObject->MajorFunction[i] = IopInvalidDeviceRequest;
}

driverObject->Type = IO_TYPE_DRIVER;
driverObject->Size = sizeof( DRIVER_OBJECT );
ntHeaders = RtlImageNtHeader( imageBaseAddress );
entryPoint = ntHeaders->OptionalHeader.AddressOfEntryPoint;
entryPoint += (ULONG_PTR) imageBaseAddress;
if (!(ntHeaders->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_WDM_DRIVER)) {
    driverObject->Flags |= DRVO_LEGACY_DRIVER;
}
driverObject->DriverInit = (PDRIVER_INITIALIZE) entryPoint;
driverObject->DriverSection = sectionPointer;
driverObject->DriverStart = imageBaseAddress;
driverObject->DriverSize = ntHeaders->OptionalHeader.SizeOfImage;

status = ObInsertObject( driverObject,
                             (PACCESS_STATE) NULL,
                             FILE_READ_DATA,
                             0,
                             (PVOID *) NULL,
                             &driverHandle );

  PCHunter仍然能够发现我们的驱动模块,是因为它肯定不是用常规的遍历PsLoadedModuleList这个东西,当然这个是废话。它是通过特征码进行的,从上面的代码我们就看到了一些固有的“特征”,也就是可以拿来作为特征码搜内存的依据:

driverObject->Type = IO_TYPE_DRIVER;
driverObject->Size = sizeof( DRIVER_OBJECT );

  当然,特征不止上面写的。如果你真想知道PCHunter发现驱动模块的,可以自己逆向分析一下,毕竟逆向分析是我们学该教程的基本功。我们从结构体的角度来看看怎样把自己隐藏起来:

typedef struct _DRIVER_OBJECT {
    CSHORT Type;
    CSHORT Size;

    PDEVICE_OBJECT DeviceObject;
    ULONG Flags;

    PVOID DriverStart;
    ULONG DriverSize;
    PVOID DriverSection;
    PDRIVER_EXTENSION DriverExtension;

    UNICODE_STRING DriverName;
    PUNICODE_STRING HardwareDatabase;
    PFAST_IO_DISPATCH FastIoDispatch;

    PDRIVER_INITIALIZE DriverInit;
    PDRIVER_STARTIO DriverStartIo;
    PDRIVER_UNLOAD DriverUnload;
    PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];

} DRIVER_OBJECT;

  从结构体来看,加载完后暂时没用的结构有如下:

    CSHORT Type;
    CSHORT Size;
    PVOID DriverStart;
    ULONG DriverSize;
    PVOID DriverSection;
    PDRIVER_EXTENSION DriverExtension;
    UNICODE_STRING DriverName;
    PUNICODE_STRING HardwareDatabase;
    PFAST_IO_DISPATCH FastIoDispatch;
    PDRIVER_INITIALIZE DriverInit;
    PDRIVER_STARTIO DriverStartIo;
    PDRIVER_UNLOAD DriverUnload;

  如果我们把它们都抹掉,那么PCHunter还会找到我们吗?其实只要抹掉DriverSection,它就找不到我们了,代码如下:

#include <ntddk.h>

LIST_ENTRY* lethis;
LIST_ENTRY* fle;
LIST_ENTRY* ble;

HANDLE hThread;

NTSTATUS UnloadDriver(PDRIVER_OBJECT DriverObject)
{
  DbgPrint("卸载成功!!!");
}

VOID HideThread(_In_ PVOID StartContext)
{
  DbgPrint("开始隐藏执行……\n");
  LARGE_INTEGER times;
  times.QuadPart = -30 * 1000 * 1000;

  KeDelayExecutionThread(KernelMode, FALSE, &times);
  PDRIVER_OBJECT pDriver = (PDRIVER_OBJECT)StartContext;

  pDriver->DriverSection = NULL;

  ZwClose(hThread);

  DbgPrint("执行隐藏结束……\n");
}

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{

    DriverObject->DriverUnload = UnloadDriver;

    lethis = (LIST_ENTRY*)DriverObject->DriverSection;

    fle = lethis->Flink;
    ble = lethis->Blink;

    fle->Blink = lethis->Blink;
    ble->Flink = lethis->Flink;

    PsCreateSystemThread(&hThread, GENERIC_ALL, NULL, NULL, NULL, HideThread, DriverObject);

    DbgPrint("加载并隐藏成功!!!");

    return STATUS_SUCCESS;
}

  这里有点不好的地方就是,你无法卸载此驱动,就算你在UnloadDriver填写了恢复代码,可以自行分析IopUnloadDriver查看它什么时候调用该函数,也能明白为什么如果没有卸载函数就不能正常卸载驱动。

常规加载驱动方式

  根据我们使用工具加载驱动的时候,它有注册、加载、停止、卸载操作。那么如何通过常规的方式实现这几个步骤呢?我们将通过新建一个MFC项目详细介绍:

注册驱动

  我们先看一看注册代码:

void CDriverLoadDlg::OnBnRegister()
{
  CString path = this->getFilePath();

  this->scMageger = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
  if (this->scMageger == NULL)
  {
    MessageBox(__T("提示"), __T("服务管理打开失败"));
    return;
  }

  if (path.IsEmpty())
  {
    MessageBox(_T("没有选择文件"), _T("你要干什么"));
    return;
  }

  CString fileName = this->getFileName();

  SC_HANDLE serviceHandle = CreateService(this->scMageger, fileName, fileName, SERVICE_ALL_ACCESS,
    SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, path, NULL, NULL, NULL, NULL, NULL);

  if (serviceHandle == NULL)
  {
    DWORD error = GetLastError();
    if (error == ERROR_SERVICE_EXISTS)
    {
      MessageBox(_T("服务已经存在不需要创建了"), _T("提示"));
    }
    else
    {
      CString str;
      str.Format(L"错误号为:%d", error);
      MessageBox(str, _T("提示"));
      OutputDebugString(str);
    }
    return;
  }
  CloseServiceHandle(serviceHandle);
}

  首先,我们调用OpenSCManager建立了一个到服务控制管理器的连接,CreateService创建一个服务对象,通过SERVICE_KERNEL_DRIVER指明是管理内核驱动然后关联到我们创建的服务控制管理器上。用完关闭句柄,我们就实现了驱动的注册。

运行驱动

  先看代码:

void CDriverLoadDlg::OnBnRun()
{
  CString path = this->getFilePath();;

  if (path.IsEmpty())
  {
    MessageBox(_T("没有选择文件"), _T("你要干什么"));
    return;
  }

  if (this->scMageger == NULL)
  {
    MessageBox(_T("请重新启动,服务器打开失败"), _T("你要干什么"));
    return;
  }

  CString fileName = this->getFileName();

  SC_HANDLE serviceHandle =  OpenService(this->scMageger, fileName, SERVICE_ALL_ACCESS);
  if (serviceHandle == NULL)
  {
    DWORD error = GetLastError();
    if (error == ERROR_SERVICE_DOES_NOT_EXIST)
    {
      MessageBox( _T("服务已经不存在"),_T("提示"));
    }
    else
    {
      CString str("错误号为:"+error);
      MessageBox(str, _T("提示"));
    }
    return;
  }

  int result = StartService(serviceHandle, 0, NULL); 
  if (result == 0)
  {
    DWORD error = GetLastError();
    if (error == ERROR_SERVICE_ALREADY_RUNNING)
    {
      MessageBox(_T("已经运行"),_T("提示"));
      return;
    }
  }
  CloseServiceHandle(serviceHandle);
}

  我们先打开我们创建好的服务,然后启动它就成功运行驱动了,关闭不使用的句柄。

停止驱动

  看如下代码:

void CDriverLoadDlg::OnBnStop()
{
  CString path = this->getFilePath();

  if (path.IsEmpty())
  {
    MessageBox(_T("没有选择文件"), _T("你要干什么"));
    return;
  }

  if (this->scMageger == NULL)
  {
    MessageBox(_T("请重新启动,服务器打开失败"), _T("你要干什么"));
    return;
  }

  CString fileName = this->getFileName();
  SC_HANDLE serviceHandle = OpenService(this->scMageger, fileName, SERVICE_ALL_ACCESS);
  if (serviceHandle == NULL)
  {
    DWORD error = GetLastError();
    if (error == ERROR_SERVICE_DOES_NOT_EXIST)
    {
      MessageBox(_T("服务不存在"), _T("提示"));
    }
    else
    {
      MessageBox(_T("未知错误"), _T("提示"));
    }
    return;
  }
  SERVICE_STATUS error = { 0 };
  int result = ControlService(serviceHandle, SERVICE_CONTROL_STOP, &error);
  if (result == 0)
  {
    DWORD error = GetLastError();
    if (error == ERROR_SERVICE_NOT_ACTIVE)
    {
    MessageBox(_T("服务没有在运行"), _T("提示"));
    }
    else
    {
      CString str("错误号为:" + error);
      MessageBox(str, _T("提示"));
    }
  }
  CloseServiceHandle(serviceHandle);
}

  代码也挺简单的,先打开驱动然后调用ControlService函数控制驱动,使其停止。

卸载驱动

  代码如下:

void CDriverLoadDlg::OnBnUnload()
{
  CString path = this->getFilePath();

  if (path.IsEmpty())
  {
    MessageBox(_T("没有选择文件"), _T("你要干什么"));
    return;
  }

  if (this->scMageger == NULL)
  {
    MessageBox(_T("请重新启动,服务器打开失败"), _T("你要干什么"));
    return;
  }

  CString fileName = this->getFileName();
  SC_HANDLE serviceHandle = OpenService(this->scMageger, fileName, SERVICE_ALL_ACCESS);
  if (serviceHandle == NULL)
  {
    DWORD error = GetLastError();
    if (error == ERROR_SERVICE_DOES_NOT_EXIST)
    {
      MessageBox( _T("服务已经不存在"),_T("提示"));
    }
    else
    {
      CString str("错误号为:" + error);
      MessageBox(str, _T("提示"));
    }
    return;
  }

  if (!DeleteService(serviceHandle))
  {
    DWORD error = GetLastError();
    CString str;
    str.Format(L"错误号为:%d", error);
    MessageBox(str, _T("提示"));
    return;
  }

  CloseServiceHandle(serviceHandle);
  CloseServiceHandle(this->scMageger);
  this->scMageger = NULL;
}

  这块代码也挺简单,直接打开服务,找到然后删掉即可。

PCHunter 如何加载驱动

  在逆向分析PCHunter如何加载驱动的时候得到的,可以说它并没有按照常规的方式加载驱动,而是通过重写调用更底层的函数的方式来进行驱动的加载,代码如下,我就不单独分析了:

char __usercall LoadOrUnLoadDriver@<al>(int SymbolLink@<ecx>, WCHAR *drivername@<esi>, bool load, bool SafeBoot)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

  v20 = 0;
  v5 = GetModuleHandleW(&ModuleName);
  if ( v5 )
  {
    ZwLoadDriver = (load ? GetProcAddress(v5, "ZwLoadDriver") : GetProcAddress(v5, "ZwUnloadDriver"));
    v15 = ZwLoadDriver;
    if ( ZwLoadDriver )
    {
      wprintf(0x208u, Buffer, L"\\??\\%s", SymbolLink);
      wprintf(0x208u, buffer, L"System\\CurrentControlSet\\Services\\%s", drivername);
      LODWORD(v9) = &phkResult;
      if ( RegCreateKeyW(0x80000002, buffer, v9) )
        return v20;
      *Data = 1;
      RegSetValueExW(phkResult, L"Type", 0, 4u, Data, 4u);
      RegSetValueExW(phkResult, L"ErrorControl", 0, 4u, Data, 4u);
      RegSetValueExW(phkResult, L"Start", 0, 4u, Data, 4u);
      RegSetValueExW(phkResult, L"ImagePath", 0, 1u, Buffer, 2 * wcslen(Buffer));
      RegCloseKey(phkResult);
      if ( SafeBoot )
      {
        v18 = 0;
        wprintf(0x208u, buffer, L"System\\CurrentControlSet\\Control\\SafeBoot\\Minimal\\%s.sys", drivername);
        LODWORD(v10) = &phkResult;
        if ( !RegCreateKeyW(0x80000002, buffer, v10) )
        {
          v18 = 1;
          RegCloseKey(phkResult);
        }
        v19 = 0;
        wprintf(0x208u, buffer, L"System\\CurrentControlSet\\Control\\SafeBoot\\Network\\%s.sys", drivername);
        LODWORD(v11) = &phkResult;
        if ( !RegCreateKeyW(0x80000002, buffer, v11) )
        {
          v19 = 1;
          RegCloseKey(phkResult);
        }
      }
      wprintf(0x208u, buffer, L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\%s", drivername);
      v14 = buffer;
      v13 = 2 * wcslen(buffer);
      v12 = v13;
      if ( !v15(&v12) )
      {
        if ( !load )
        {
LABEL_16:
          v20 = 1;
          goto LABEL_17;
        }
        sub_4291C0(buffer, L"\\\\.\\%s", drivername);
        v7 = CreateFileW(buffer, GENERIC_READ, 0, 0, 3u, 0, 0);
        if ( v7 != -1 )
        {
          CloseHandle(v7);
          goto LABEL_16;
        }
      }
LABEL_17:
      if ( SafeBoot )
      {
        if ( v18 == 1 )
        {
          wprintf(0x208u, buffer, L"System\\CurrentControlSet\\Control\\SafeBoot\\Minimal\\%s.sys", drivername);
          RegDeleteKeyW(0x80000002, buffer);
        }
        if ( v19 == 1 )
        {
          wprintf(0x208u, buffer, L"System\\CurrentControlSet\\Control\\SafeBoot\\Network\\%s.sys", drivername);
          RegDeleteKeyW(0x80000002, buffer);
        }
      }
      wprintf(0x208u, buffer, aSy, drivername);
      RegDeleteKeyW(0x80000002, buffer);
      wprintf(0x208u, buffer, aSy_0, drivername);
      RegDeleteKeyW(0x80000002, buffer);
      wprintf(0x208u, buffer, L"System\\CurrentControlSet\\Services\\%s", drivername);
      RegDeleteKeyW(0x80000002, buffer);
      return v20;
    }
  }
  return 0;
}

int __cdecl LoadDriver(wchar_t *SymbolLink, int SafeBoot)
{
  wchar_t *v2; // eax
  const wchar_t *v3; // eax
  wchar_t *v4; // eax
  HANDLE v5; // eax
  int result; // eax
  bool v7; // zf
  wchar_t Drivername[32]; // [esp+8h] [ebp-44h] BYREF

  GetLoadDriverPri();
  v2 = wcsrchr(SymbolLink, '\\');
  if ( !v2 )
    return 0;
  v3 = v2 + 1;
  if ( !*v3 )
    return 0;
  wcsncpy_s(Drivername, 0x20u, v3, 0x1Fu);
  Drivername[31] = 0;
  v4 = wcsrchr(Drivername, 0x2Eu);
  if ( v4 )
  {
    if ( v4[1] )
      *v4 = 0;
  }
  sub_4291F0(Symbollink, L"\\\\.\\%s", Drivername);
  word_8FE856 = 0;
  v5 = CreateFileW(Symbollink, 0x80000000, 0, 0, 3u, 0, 0);
  if ( v5 != -1 )
  {
    CloseHandle(v5);
    return 1;
  }
  v7 = LoadOrUnLoadDriver(SymbolLink, Drivername, 1, SafeBoot != 0) == 1;
  result = 1;
  if ( !v7 )
    return 0;
  return result;
}

跨进程监控API

  本小节是对前面所学的一个总结,请学习本教程的同志们独立完成,代码解析将会在下一篇进行讲解。如下是你可能用到的知识点和要求:

  1. 自己写代码加载,卸载驱动程序(必须)
  2. 段页的知识:绕写拷贝
  3. 3环与0环通信
  4. HOOK
  5. ShellCode 的使用
  6. 不能使用SSDT HOOK等之后的知识

  由于写拷贝之前没有讲解,故讲解完什么是写拷贝之后,自行做这个项目。
  我们之前学保护模式中的段页中没有写保护这个属性吧?只有只读可写或者3环是否能够访问或者有效属性,唯独没有写保护属性。而我们在3环下断点的本质都是在写入内存0xCC实现,但是没有影响到别的程序,就是因为写拷贝。它会把你更改的那块页重新分配一个,重新指向。EPROCESS结构体有一个结构,在0x11c偏移处,名为VadRoot,它是一个二叉树,里面记录了哪些线性地址使用了,使用情况是什么,哪些线性地址没有使用,我们在虚拟机测试一下,先遍历进程结构体,找到VadRoot的值:

Failed to get VadRoot
PROCESS 89b102c0  SessionId: 0  Cid: 01d8    Peb: 7ffd8000  ParentCid: 05f4
    DirBase: 12fc0280  ObjectTable: e140a0c8  HandleCount:  44.
    Image: notepad.exe

kd> dt _EPROCESS 89b102c0
ntdll!_EPROCESS
   +0x000 Pcb              : _KPROCESS
   +0x06c ProcessLock      : _EX_PUSH_LOCK
   +0x070 CreateTime       : _LARGE_INTEGER 0x01d7ded9`051be59e
   +0x078 ExitTime         : _LARGE_INTEGER 0x0
   +0x080 RundownProtect   : _EX_RUNDOWN_REF
   +0x084 UniqueProcessId  : 0x000001d8 Void
   +0x088 ActiveProcessLinks : _LIST_ENTRY [ 0x8055b158 - 0x89bba538 ]
   +0x090 QuotaUsage       : [3] 0xa50
   +0x09c QuotaPeak        : [3] 0xc48
   +0x0a8 CommitCharge     : 0x184
   +0x0ac PeakVirtualSize  : 0x244d000
   +0x0b0 VirtualSize      : 0x1f87000
   +0x0b4 SessionProcessLinks : _LIST_ENTRY [ 0xbadce014 - 0x89bba564 ]
   +0x0bc DebugPort        : (null) 
   +0x0c0 ExceptionPort    : 0xe12b8eb0 Void
   +0x0c4 ObjectTable      : 0xe140a0c8 _HANDLE_TABLE
   +0x0c8 Token            : _EX_FAST_REF
   +0x0cc WorkingSetLock   : _FAST_MUTEX
   +0x0ec WorkingSetPage   : 0x1bbeb
   +0x0f0 AddressCreationLock : _FAST_MUTEX
   +0x110 HyperSpaceLock   : 0
   +0x114 ForkInProgress   : (null) 
   +0x118 HardwareTrigger  : 0
   +0x11c VadRoot          : 0x89bd1ea0 Void
   +0x120 VadHint          : 0x89b2f398 Void
   +0x124 CloneRoot        : (null) 
   (部分略)

  我们如何查看这个Vad二叉树这个,只需要使用如下指令:

kd> !vad 0x89bd1ea0
VAD   Level     Start       End Commit
89dbfa18  3        10        10      1 Private      READWRITE          
89d75dd0  2        20        20      1 Private      READWRITE          
89922598  5        30        3f      6 Private      READWRITE          
89cc27f0  4        40        7f     18 Private      READWRITE          
89dc78a8  3        80        82      0 Mapped       READONLY           Pagefile section, shared commit 0x3
89b248e8  4        90        91      0 Mapped       READONLY           Pagefile section, shared commit 0x2
89bcd658  1        a0       19f     21 Private      READWRITE          
89c430a0  4       1a0       1af      6 Private      READWRITE          
89b73178  3       1b0       1bf      0 Mapped       READWRITE          Pagefile section, shared commit 0x3
89bbcea0  4       1c0       1d5      0 Mapped       READONLY           \WINDOWS\system32\unicode.nls
89dc7878  2       1e0       220      0 Mapped       READONLY           \WINDOWS\system32\locale.nls
89b0bea0  4       230       270      0 Mapped       READONLY           \WINDOWS\system32\sortkey.nls
89bcfea0  3       280       285      0 Mapped       READONLY           \WINDOWS\system32\sorttbls.nls
89bd1ea0  0       290       2d0      0 Mapped       READONLY           Pagefile section, shared commit 0x41
89b2c0a8  5       2e0       3a7      0 Mapped       EXECUTE_READ       Pagefile section, shared commit 0x3
89cd10a8  6       3b0       3bf      8 Private      READWRITE          
89bccfa0  7       3c0       3c0      1 Private      READWRITE          
89bcd2f8  8       3d0       3d0      1 Private      READWRITE          
89b9a340 10       3e0       3e1      0 Mapped       READONLY           Pagefile section, shared commit 0x2
89bf2798  9       3f0       3f1      0 Mapped       READONLY           Pagefile section, shared commit 0x2
89bf8418 10       400       40f      3 Private      READWRITE          
89d5ce28  4       410       41f      8 Private      READWRITE          
89d81b18  3       420       42f      4 Private      READWRITE          
89c08ea8  4       430       432      0 Mapped       READONLY           \WINDOWS\system32\ctype.nls
89b27df0  2       440       47f      3 Private      READWRITE          
89b9ab20  5       480       582      0 Mapped       READONLY           Pagefile section, shared commit 0x103
89b9a600  4       590       88f      0 Mapped       EXECUTE_READ       Pagefile section, shared commit 0x1b
89b4b1d8  3       890       90f      1 Private      READWRITE          
899135b8  5       910       95f      0 Mapped       READONLY           Pagefile section, shared commit 0x50
898e4c90  4       960       960      0 Mapped       READWRITE          Pagefile section, shared commit 0x1
89bfb990  6       970       9af      0 Mapped       READWRITE          Pagefile section, shared commit 0x10
89bf2768  5       9b0       9bd      0 Mapped       READWRITE          Pagefile section, shared commit 0xe
89b29200  7       9c0       abf    123 Private      READWRITE          
89b0de80  6       ad0       b4f      0 Mapped       READWRITE          Pagefile section, shared commit 0x7
89bcd360  1      1000      1012      3 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\notepad.exe
89b08c18  7     58fb0     59179      9 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\AppPatch\AcGenral.dll
89b2f398  8     5adc0     5adf6      2 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\uxtheme.dll
89b2c0d8  6     5cc30     5cc55     20 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\shimeng.dll
89b9a5d0  7     62c20     62c28      1 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\lpk.dll
89b0f2a0  5     72f70     72f95      3 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\winspool.drv
89bba7f8  8     73640     7366d      2 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\MSCTFIME.IME
89b9a370  7     73fa0     7400a     16 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\usp10.dll
89bde880  8     74680     746cb      3 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\MSCTF.dll
89b2f3c8  6     759d0     75a7e      3 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\userenv.dll
89c08e78  7     76300     7631c      1 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\imm32.dll
89b05ea0  4     76320     76366      4 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\comdlg32.dll
89b2eb20  8     76990     76acc      8 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\ole32.dll
89b08be8  7     76b10     76b39      2 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\winmm.dll
89b2eaf0  8     770f0     7717a      4 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\oleaut32.dll
89be47c0  6     77180     77282      2 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\WinSxS\x86_Microsoft.Windows.Common-Controls_6595b64144ccf1df_6.0.2600.5512_x-ww_35d4ce83\comctl32.dll
89bac230  8     77bb0     77bc4      2 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\msacm32.dll
89bac200  9     77bd0     77bd7      1 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\version.dll
89babdb0  7     77be0     77c37      7 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\msvcrt.dll
8990d4d8  8     77d10     77d9f      2 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\user32.dll
89b33ea0  5     77da0     77e48      5 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\advapi32.dll
89d8eb70  6     77e50     77ee1      1 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\rpcrt4.dll
89babd80  8     77ef0     77f38      2 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\gdi32.dll
89b0f6c8  9     77f40     77fb5      2 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\shlwapi.dll
89be47f0  7     77fc0     77fd0      1 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\secur32.dll
89d06c08  3     7c800     7c91d      5 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\kernel32.dll
89d84838  2     7c920     7c9b2      5 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\ntdll.dll
89b0f2d0  5     7d590     7dd83     30 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\shell32.dll
89bcc0d8  4     7f6f0     7f7ef      0 Mapped       EXECUTE_READ       Pagefile section, shared commit 0x7
89d82ab0  3     7ffa0     7ffd2      0 Mapped       READONLY           Pagefile section, shared commit 0x33
89bccd38  4     7ffd8     7ffd8      1 Private      READWRITE          
89bccc68  5     7ffdf     7ffdf      1 Private      READWRITE          

Total VADs: 66, average level: 6, maximum depth: 10
Total private commit: 0x161 pages (1412 KB)
Total shared commit:  0x21e pages (2168 KB)

  VAD这个列表示节点,每一个二叉树都是由各个节点组成;Level是指二叉树的等级,也就是深度,通俗的说是第几层;Start记录是除去后12位的线性地址的开始部分,End记录是除去后12位的线性地址的结尾部分。通过这个二叉树,我们很清晰的能够看到每个内存的情况,也证实了在三环隐藏dll模块是几乎没作用的。
  我们先简单介绍一下页的属性:Private为私有物理页,也就是独占的;Mapped就是共享物理页;其他只读、写拷贝可执行看英文就能明白意思,就不介绍了。接下来我们看看到底写拷贝和只读到底有什么区别。
  我们从上面选取了一个只读和写拷贝的物理页,先查看它们的属性:

//只读
kd> !vtop 12fc0280 1c0000
X86VtoP: Virt 00000000001c0000, pagedir 0000000012fc0280
X86VtoP: PAE PDPE 0000000012fc0280 - 000000001ba26001
X86VtoP: PAE PDE 000000001ba26000 - 000000001ba09067
X86VtoP: PAE PTE 000000001ba09e00 - 000000000bb10025
X86VtoP: PAE Mapped phys 000000000bb10000
Virtual address 1c0000 translates to physical address bb10000.

//写拷贝
kd> !vtop 12fc0280 1000000
X86VtoP: Virt 0000000001000000, pagedir 0000000012fc0280
X86VtoP: PAE PDPE 0000000012fc0280 - 000000001ba26001
X86VtoP: PAE PDE 000000001ba26040 - 000000001bbfc067
X86VtoP: PAE PTE 000000001bbfc000 - 000000001bb52025
X86VtoP: PAE Mapped phys 000000001bb52000
Virtual address 1000000 translates to physical address 1bb52000.

  可以看出它们的属性都是一样的,都是只读的物理页。我们就可以明白操作系统是如何判断该页是只读还是写拷贝:先检查是不是只读,如果查Vad发现是真的只读,那就是只读;如果记录的是写拷贝,那就是写拷贝。因此,我们只需要改一下物理页的属性,我们就可以绕过写拷贝。
  本篇就介绍这么多,这个项目的源码分析将在下一篇进行介绍。

下一篇

  驱动篇——项目源码分析

posted @ 2021-11-21 21:41  寂静的羽夏  阅读(1440)  评论(0编辑  收藏  举报