写在前面
此系列是本人一个字一个字码出来的,包括示例和实验截图。由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新。 如有好的建议,欢迎反馈。码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作。如想转载,请把我的转载信息附在文章后面,并声明我的个人信息和本人博客地址即可,但必须事先通知我。
你如果是从中间插过来看的,请仔细阅读 羽夏看Win系统内核——简述 ,方便学习本教程。
看此教程之前,问几个问题,基础知识储备好了吗?上一节教程学会了吗?上一节课的练习做了吗?没有的话就不要继续了。
华丽的分割线
练习及参考
本次答案均为参考,可以与我的答案不一致,但必须成功通过。
1️⃣ 实现一个工具,利用未导出的函数PspTerminateProcess
杀死软件(驱动的加载可不用代码实现,使用本教程工具进行加载)。
#include
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;
}
#include "stdafx.h"
#include
#include
#include
//操作码: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
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
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@(int SymbolLink@, WCHAR *drivername@, 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
发现是真的只读,那就是只读;如果记录的是写拷贝,那就是写拷贝。因此,我们只需要改一下物理页的属性,我们就可以绕过写拷贝。
本篇就介绍这么多,这个项目的源码分析将在下一篇进行介绍。
下一篇
驱动篇——项目源码分析