寫在前面
此係列是本人一個字一個字碼出來的,包括示例和實驗截圖。由於系統核心的複雜性,故可能有錯誤或者不全面的地方,如有錯誤,歡迎批評指正,本教程將會長期更新。 如有好的建議,歡迎反饋。碼字不易,如果本篇文章有幫助你的,如有閒錢,可以打賞支援我的創作。如想轉載,請把我的轉載資訊附在文章後面,並宣告我的個人資訊和本人部落格地址即可,但必須事先通知我。
你如果是從中間插過來看的,請仔細閱讀 羽夏看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, ×);
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
本小節是對前面所學的一個總結,請學習本教程的同志們獨立完成,程式碼解析將會在下一篇進行講解。如下是你可能用到的知識點和要求:
- 自己寫程式碼載入,解除安裝驅動程式(必須)
- 段頁的知識:繞寫拷貝
- 3環與0環通訊
- 寫
HO0K
ShellCode
的使用- 不能使用
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
發現是真的只讀,那就是隻讀;如果記錄的是寫拷貝,那就是寫拷貝。因此,我們只需要改一下物理頁的屬性,我們就可以繞過寫拷貝。
本篇就介紹這麼多,這個專案的原始碼分析將在下一篇進行介紹。
下一篇
驅動篇——專案原始碼分析