驅動篇——總結與提升

寂靜的羽夏發表於2021-11-21

寫在前面

  此係列是本人一個字一個字碼出來的,包括示例和實驗截圖。由於系統核心的複雜性,故可能有錯誤或者不全面的地方,如有錯誤,歡迎批評指正,本教程將會長期更新。 如有好的建議,歡迎反饋。碼字不易,如果本篇文章有幫助你的,如有閒錢,可以打賞支援我的創作。如想轉載,請把我的轉載資訊附在文章後面,並宣告我的個人資訊和本人部落格地址即可,但必須事先通知我

你如果是從中間插過來看的,請仔細閱讀 羽夏看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. HO0K
  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發現是真的只讀,那就是隻讀;如果記錄的是寫拷貝,那就是寫拷貝。因此,我們只需要改一下物理頁的屬性,我們就可以繞過寫拷貝。
  本篇就介紹這麼多,這個專案的原始碼分析將在下一篇進行介紹。

下一篇

  驅動篇——專案原始碼分析

相關文章