0x0 前言
本来是想学一下MiniFilter,后面觉得没什么意思,就顺道将文件这一块逆了几天,越逆越觉得有意思,特将过程记录下来,从最开始CreateFile C盘开始,卷设备、NTFS文件系统、缓存管理、IO管理、到15个重要的元文件($MFT、$BOOT等),因为文件系统较复杂和繁琐,并且是以逆向为主,所以只记录关键流程,并争取简单明了,如果有错误还请指出,借此抛砖引玉。(参考书籍:windows内核原理与实现 windows内核设计思想)
0x1 简单基础介绍

我们在0环中断下来,知道内核NtCreateFile的第三个参数放的就是我们要打开的对象名:"\??\C:",至于这里是怎么转过来的,可以自已去三环跟一下,没什么难度。

在解释"\??\"是个什么玩意的时候,得先初步了解一下对象管理的一些知识。
windows内部维护了一个对象层次目录(即系统全局名字空间)。
如下图:根目录:“\”(用全局变量ObpRootDirectoryObject来定义),下面的ArcName、Device、Driver等都是子目录。

对象管理器在第1阶段初始化时,会先创建"\"根目录,在第一阶段完成后根目录对象会插入以下对象。

这里Name里面并没有显示??,但实际上,是已经创建了??这个目录,并且还创建了DosDevices这个符号链接来指向它,它的创建过程:
ObInitSystem---->ObpInitializeRootNamespace---->ObpCreateDosDevicesDirectory,感兴趣的同学可以逆。
0x2 解析过程
1。对象管理器在遇到“\??\”字符串时,会进入到EPROCESS的DeviceMAP(设备表)里,找到他需要的目录来解析:


DosDevicesDirectory:指向局部空间的目录。 "\??"
GlobalDosDevicesDirectory:指向全局空间的目录。"\GLOBAL??"
注:先找局部空间目录,如果局部空间目录没找到,则进入全局空间目录查找。
2。 解析DosDevicesDirectory这个对象,找到ParseProcedure或者ParseProcedureEx的值,判断是否为NULL,如果为NULL为目录,再通过剩余的名字来解析。(后面会多次需要得到ParseProcedure,具体过程这里会详细一点,后面直接略过)
1>得到Object_Header的TypeIndex.

2>Object_Header右移8位,得到低2字节。

3>得到ObHeaderCookie(我把他理解为随机值)

4>取他们的异或0xC0 ^ 0x1D ^ 0xDE = 0x3;
5>全局变量ObTypeIndexTable是一个对象类型数组,从这里取出上面异或出来的第3个,找到ParseProcedure或者ParseProcedureEx。

3。接着解析剩余的”C:“,因为目录的对象体是:_OBJECT_DIRECTORY,里面主要是用哈希表。

1>先计算”C:“的哈希值,在计算的时候,小写c也会转成大写。 C:的ASCII码:0x43 0x00 0x3A 0x00 [WCHAR,内存16进制表示]
HashValue=(0x43*3)+(0x43/2)+0x3A=0x124
HashIndex=0x124 % 37 = 33 [为什么是37,因为他上面一个目录对象只有37个Object_Directory_Entry]
2>在DosDevicesDirectory或者GlobalDosDevicesDirectory的哈希表中查找,如果DosDevicesDirectory没有,则去GlobalDosDevicesDirectory哈希表中查找。

3>ChainLink是一个单向链表,我们一直往下判断HashValue和我们的HashValue=(0x43*3)+(0x43/2)+0x3A=0x124是否相等。

4>取出相等的Object

5>同上解析得到Object的ParseProcedure或者ParseProcedureEx的值,是否为NULL,如果不为NULL,就调用他。具体过程上面已有详细说明,则简化。

6>设置好参数,调用ParseProcedure:这里也就是ObpParseSymbolicLinkEx(解析符号链接)。
4。ObpParseSymbolicLinkEx处理非常简单。
上面得到的Object是一个_OBJECT_SYMBOLIC_LINK结构体,里面记录了符号链接的目标设备对象,ObpParseSymbolicLinkEx的主要功能是将LinkTarget这个字符串替换了前面那个"\??\C:",然后返回STATUS_REPARSE,ObpLookupObjectName再接着解析,很多过滤就是用的这种机制。

0x3 简单实验
(不经文件过滤,直接利用上述符号知识,让E盘重定位到C盘)
未实现前:

实现后:======================================================================================

代码写的比较简单,没有去考虑锁之类的:
#include <ntddk.h>
#include "KernelStruct.h"
POBJECT_DIRECTORY_ENTRY GetDirectoryEntry(PDEVICE_MAP pDeviceMap, PWCHAR pDriverLetter);
BOOLEAN isReloc = FALSE;
PUNICODE_STRING OldAddr;
PWCHAR OldBuffer;
VOID Unload(PDRIVER_OBJECT pDriverObject)
{
//还原
if (isReloc) OldAddr->Buffer = OldBuffer;
KdPrint(("Over\n"));
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pReg)
{
KdPrint(("Start\n"));
NTSTATUS status = STATUS_SUCCESS;
pDriverObject->DriverUnload = Unload;
//第一步得到当前进程的EPROCESS
PEPROCESS CurrentProcess = PsGetCurrentProcess();
//第二步得到进程的设备表DeviceMap
PDEVICE_MAP DeviceMap = (PDEVICE_MAP)(*(PUINT64)((UINT64)CurrentProcess + DEVICEMAP));
//第三步计算将E盘重定位到C盘的两个盘符的HashValue,并得到目录对象。
//这里要用大写,在windows里,会将小写转为大写计算Hash
POBJECT_DIRECTORY_ENTRY pDirectoryEntry0 = GetDirectoryEntry(DeviceMap, L"C:");
POBJECT_DIRECTORY_ENTRY pDirectoryEntry1 = GetDirectoryEntry(DeviceMap, L"E:");
if (!pDirectoryEntry0 | !pDirectoryEntry1)
{
KdPrint(("参数错误\n"));
return status;
}
//第四步
POBJECT_SYMBOLIC_LINK pSymbolicLink0 = (POBJECT_SYMBOLIC_LINK)pDirectoryEntry0->Object;
POBJECT_SYMBOLIC_LINK pSymbolicLink1 = (POBJECT_SYMBOLIC_LINK)pDirectoryEntry1->Object;
OldAddr = &pSymbolicLink1->LinkTarget;
OldBuffer = *(&pSymbolicLink1->LinkTarget.Buffer);
*(&pSymbolicLink1->LinkTarget.Buffer) = *(&pSymbolicLink0->LinkTarget.Buffer);
isReloc = TRUE;
return status;
}
POBJECT_DIRECTORY_ENTRY GetDirectoryEntry(PDEVICE_MAP pDeviceMap,PWCHAR pDriverLetter)
{
PCHAR var = (PCHAR)pDriverLetter;
UINT32 vHashValue = (*var * 3) + (*var / 2) + *(var+2);
UINT32 vHashIndex = vHashValue % 0x25;
POBJECT_DIRECTORY pDirectoryObject = pDeviceMap->DosDevicesDirectory;
if (!pDirectoryObject)
{
pDirectoryObject = pDeviceMap->GlobalDosDevicesDirectory;
}
POBJECT_DIRECTORY_ENTRY pDirectoryEntry = pDirectoryObject->HashBuckets[vHashIndex];
if (!pDirectoryEntry)
{
//打印直接返回
KdPrint(("没有找到目录项\n"));
return NULL;
}
do
{
if (pDirectoryEntry->HashValue == vHashValue)
{
break;
}
pDirectoryEntry = pDirectoryEntry->ChainLink;
} while (pDirectoryEntry);
return pDirectoryEntry;
}
KernelStruct.h
#include <ntddk.h>
#define DEVICEMAP 0x430
typedef struct _EX_PUSH_LOCK
{
union
{
struct
{
ULONGLONG Locked : 1; //0x0
ULONGLONG Waiting : 1; //0x0
ULONGLONG Waking : 1; //0x0
ULONGLONG MultipleShared : 1; //0x0
ULONGLONG Shared : 60; //0x0
};
ULONGLONG Value; //0x0
VOID* Ptr; //0x0
};
}EX_PUSH_LOCK, *PEX_PUSH_LOCK;
typedef struct _OBJECT_DIRECTORY_ENTRY
{
struct _OBJECT_DIRECTORY_ENTRY* ChainLink; //0x0
VOID* Object; //0x8
ULONG HashValue; //0x10
}OBJECT_DIRECTORY_ENTRY, *POBJECT_DIRECTORY_ENTRY;
typedef struct _OBJECT_DIRECTORY
{
struct _OBJECT_DIRECTORY_ENTRY* HashBuckets[37]; //0x0
struct _EX_PUSH_LOCK Lock; //0x128
struct _DEVICE_MAP* DeviceMap; //0x130
struct _OBJECT_DIRECTORY* ShadowDirectory; //0x138
VOID* NamespaceEntry; //0x140
VOID* SessionObject; //0x148
ULONG Flags; //0x150
ULONG SessionId; //0x154
}OBJECT_DIRECTORY, *POBJECT_DIRECTORY;
typedef struct _DEVICE_MAP
{
struct _OBJECT_DIRECTORY* DosDevicesDirectory; //0x0
struct _OBJECT_DIRECTORY* GlobalDosDevicesDirectory; //0x8
VOID* DosDevicesDirectoryHandle; //0x10
volatile LONG ReferenceCount; //0x18
ULONG DriveMap; //0x1c
UCHAR DriveType[32]; //0x20
struct _EJOB* ServerSilo; //0x40
}DEVICE_MAP,*PDEVICE_MAP;
typedef struct _OBJECT_SYMBOLIC_LINK
{
union _LARGE_INTEGER CreationTime; //0x0
union
{
struct _UNICODE_STRING LinkTarget; //0x8
struct
{
LONG(*Callback)(struct _OBJECT_SYMBOLIC_LINK* arg1, VOID* arg2, struct _UNICODE_STRING* arg3, VOID** arg4); //0x8
VOID* CallbackContext; //0x10
};
};
ULONG DosDeviceDriveIndex; //0x18
ULONG Flags; //0x1c
ULONG AccessMask; //0x20
}OBJECT_SYMBOLIC_LINK, *POBJECT_SYMBOLIC_LINK;
完,下周再见!
4h入门PHP代码审计之反序列化