首页
论坛
课程
招聘
[原创]不用CR0或MDL修改内核非分页写保护内存的一种思路(x64)
2020-10-3 21:42 3497

[原创]不用CR0或MDL修改内核非分页写保护内存的一种思路(x64)

2020-10-3 21:42
3497

    开门见山,本文的核心思路就是通过填充页表项,将一块连续的虚拟地址映射到新的地址,同时将需要修改的只读内存对应页表项的Dirty位置位。在Windows操作系统下,写保护是通过保护特定虚拟地址实现的,若不建立新映射,则即使将Dirty位置位,尝试写只读内存照样会触发BugCheck,若建立了新映射但不置位Dirty则触发PAGE_FAULT的BugCheck,两个步骤缺一不可。

    填充页表项首先需要动态定位PTEBase,国际惯例是采用大表哥的页表自映射法,代码如下:

ULONG_PTR PTEBase = 0;
BOOLEAN hzqstGetPTEBase()
{
	BOOLEAN Result = FALSE;
	ULONG_PTR PXEPA = __readcr3() & 0xFFFFFFFFF000;
	PHYSICAL_ADDRESS PXEPAParam;
	PXEPAParam.QuadPart = (LONGLONG)PXEPA;
	ULONG_PTR PXEVA = (ULONG_PTR)MmGetVirtualForPhysical(PXEPAParam);
	if (PXEVA)
	{
		ULONG_PTR PXEOffset = 0;
		do
		{
			if ((*(PULONGLONG)(PXEVA + PXEOffset) & 0xFFFFFFFFF000) == PXEPA)
			{
				PTEBase = (PXEOffset + 0xFFFF000) << 36;
				Result = TRUE;
				break;
			}
			PXEOffset += 8;
		} while (PXEOffset < PAGE_SIZE);
	}
	return Result;
}

    这里我顺便也给出另一种获取方案,基本思路是通过NT导出函数

NTKERNELAPI ULONG NTAPI KeCapturePersistentThreadState(PCONTEXT Context, PKTHREAD Thread, ULONG BugCheckCode, ULONG_PTR BugCheckParameter1, ULONG_PTR BugCheckParameter2, ULONG_PTR BugCheckParameter3, ULONG_PTR BugCheckParameter4, PVOID VirtualAddress);

    Dump下0x40000大小的数据,里面就存放有MmPfnDataBase,MmPfnDataBase是一个以物理地址页帧号为索引的内存信息数组,其中就有物理页对应的PTE项的地址PteAddress,我们传一个有效的物理地址进去(如当前CR3: __readcr3() & 0xFFFFFFFFF000)取出其中的PteAddress,由于PTEBase必定是0x8000000000的倍数,因此可由PteAddress直接算出PTEBase。另外,从Win10 RS1周年预览版开始,KeCapturePersistentThreadState开始受全局变量ForceDumpDisabled的控制,若注册表“HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CrashControl”中有关子项满足条件,此变量会在开机启动时置为1,导致KeCapturePersistentThreadState调用失败。综上所述我们得到第二种获取PTEBase的代码如下:

//若在Win10上测试,需要在这里加上"#define _WIN10 1"

#ifdef _WIN10
#define OFFSET_PTEADDRESS 0x8
#elif
#define OFFSET_PTEADDRESS 0x10
#endif
ULONG_PTR PTEBase = 0;

PBOOLEAN LocateForceDumpDisabledInRange(ULONG_PTR StartAddress, ULONG MaximumBytesToSearch)
{
	PBOOLEAN Result = 0;
	ULONG_PTR p = StartAddress;
	ULONG_PTR pEnd = p + MaximumBytesToSearch;
	do
	{
		//cmp cs:ForceDumpDisabled, al
		//jnz ...
		if ((*(PULONGLONG)p & 0xFFFF00000000FFFF) == 0x850F000000000538)
		{
			Result = p + 6 + *(PLONG)(p + 2);
			break;
		}
		p++;
	} while (p < pEnd);
	return Result;
}

BOOLEAN GetPTEBase()
{
	BOOLEAN Result = FALSE;
	CONTEXT Context = { 0 };
	Context.Rcx = (ULONG64)&Context;
	PUCHAR DumpData = ExAllocatePool(NonPagedPool, 0x40000);
	if (DumpData)
	{
		PBOOLEAN pForceDumpDisabled = LocateForceDumpDisabledInRange((ULONG_PTR)KeCapturePersistentThreadState, 0x300);
		if (pForceDumpDisabled) *pForceDumpDisabled = FALSE;
		if (KeCapturePersistentThreadState(&Context, 0, 0, 0, 0, 0, 0, DumpData) == 0x40000)
		{
			ULONG_PTR MmPfnDataBase = *(PULONG_PTR)(DumpData + 0x18);
			PTEBase = *(PULONG_PTR)(MmPfnDataBase + OFFSET_PTEADDRESS + (((ULONG_PTR)(__readcr3() & 0xFFFFFFFFF000) >> 8) * 3)) & 0xFFFFFF8000000000;
			Result = TRUE;
		}
		ExFreePool(DumpData);
	}
	return Result;
}

    获取到PTEBase后,现在进入正题,下面代码的主要思路是:首先从某个有效的按512G对齐的内核虚拟地址开始,一直到0xFFFFFFFFFFFFFFFF,查找未被占用的PML4T(下文统称PXE)子项,即PXE的Valid位为0的子项。

    对于有效起始内核虚拟地址,一开始笔者选用的是MmSystemRangeStart,虚拟机测试发现对于8.1/10映射成功了,而Vista/7/8下CPU并没有承认映射地址的有效性触发了BugCheck,调试器发现Vista/7/8下MmSystemRangeStart=0xFFFF080000000000,而8.1/10下MmSystemRangeStart=0xFFFF800000000000,并且Vista/7/8下映射地址范围为[0xFFFF080000000000, 0xFFFF800000000000)时,调试器的CrashDump提示为Noncanonical Virtual Address。查阅了Intel手册后,笔者发现当前的Intel CPU支持的虚拟地址寻址最大位数限制为48位,对于64位Windows来说,第47位被用来区分用户层虚拟地址和内核层虚拟地址,即内核层地址实际上只有47位的有效位,于是得出有效起始内核虚拟地址为0xFFFF800000000000。当然严谨起见,可以使用CPUID的0x80000008号功能,此时eax寄存器的ah即为处理器支持的虚拟地址最大有效位数,设其为x,则对64位地址,只要将最高的(65-x)位全部置1,剩余的(x-1)位全部置0,即得有效起始内核虚拟地址。

    找到未使用的PXE子项后,申请一段连续的物理内存,初始大小为PXE子项以及PXE子项的512个PPE项所描述的页面大小,即(1 + 0x200) * PAGE_SIZE,若申请失败,则将申请的PPE项减半,以此类推……由于是连续物理内存,因此最好的方案是通过MmAllocateContiguousMemory申请,若使用ExAllocatePool,则当申请的页面不是2M大页面时,其虚拟地址对应的物理地址很可能不是连续的,这会给我们后续填充512个PPE项的物理页帧号徒增不少麻烦。申请到连续物理地址后,第一个页面填充至目标PXE子项,第2到513个页面的物理页帧号按顺序填充到PXE子项页面描述的512个PPE项中。随后给定任一个需要映射的虚拟地址,我们先将它按0x8000000000(512G)进行对齐,再依次检索其PXE、PPE、PDE项,若PXE项Valid为0或LargePage为1则不映射,否则开始依次检索PPE和PDE。若PPE项Valid为0,则把映射地址对应的PPE页面清零。若Valid为1则分别处理是否LargePage的情形,若为1G大页面,则将其等分成512个2M大页面,再把对应的物理页帧号按顺序填充到PPE描述的512个PDE项中;若不是大页面,则将被映射地址的PPE项对应的一页全部复制到映射地址对应的PPE页面中。从这里开始,基本的地址映射已经完成,接下来对欲修改的页面只要把其对应的PTE或大页面PDE或大页面PPE项的Dirty位置1即可。

ULONG_PTR AllocateSinglePXEDirectory(OUT PULONG_PTR BaseAddress, OUT PULONG SizeOfPPEPages)
{
	ULONG_PTR Result = 0;
	ULONG_PTR PteBase = PTEBase;
	ULONG_PTR OffsetMask = 0x7FFFFFFFF8;
	ULONG_PTR PXEVA = PteBase + (((PteBase + (((PteBase + ((PteBase >> 9) & OffsetMask)) >> 9) & OffsetMask)) >> 9) & OffsetMask) + 0x800; //有效起始内核虚拟地址0xFFFF800000000000对应的PXE子项
	ULONG_PTR PXEVAEnd = PXEVA + 0x800; //0x800 + 0x800 == 0x1000 == PAGE_SIZE
	do
	{
		if (!(*(PULONGLONG)PXEVA & 0xFFFFFFFFF001))
		{
			PHYSICAL_ADDRESS Alloc;
			Alloc.QuadPart = MAXULONG64;
			ULONG TotalSizeOfValidPPEPages = 0x200 * PAGE_SIZE;
			while (TotalSizeOfValidPPEPages >= PAGE_SIZE && !(Result = (ULONG_PTR)MmAllocateContiguousMemory(TotalSizeOfValidPPEPages + PAGE_SIZE, Alloc)))
				TotalSizeOfValidPPEPages >>= 1;
			if (Result)
			{
				if (SizeOfPPEPages) *SizeOfPPEPages = TotalSizeOfValidPPEPages;
				ULONG64 OringinalIRQL = __readcr8();
				__writecr8(DISPATCH_LEVEL);
				if (BaseAddress) *BaseAddress = ((PXEVA & 0xFF8) + 0xFFFF000) << 36;
				ULONG_PTR PTEVA = PteBase + ((Result >> 9) & OffsetMask);
				ULONG_PTR PDEVA = PteBase + ((PTEVA >> 9) & OffsetMask);
				ULONG_PTR PPEVA = PteBase + ((PDEVA >> 9) & OffsetMask);
				ULONGLONG StartValue = *(PULONGLONG)PPEVA;
				if (StartValue & 0x80)
				{
					StartValue &= ~0x80;
					StartValue += (Result & 0x3FFFF000);
				}
				else
				{
					StartValue = *(PULONGLONG)PDEVA;
					if (StartValue & 0x80)
					{
						StartValue &= ~0x80;
						StartValue += (Result & 0x1FF000);
					}
					else StartValue = *(PULONGLONG)PTEVA;
				}
				*(PULONGLONG)PXEVA = StartValue;
				ULONG PPEOffset = 0;
				ULONG PPEOffsetEnd = TotalSizeOfValidPPEPages >> 9;
				ULONG_PTR PPEBase = Result;
				do
				{
					*(PULONGLONG)(PPEBase + PPEOffset) = StartValue + ((PPEOffset + 8) << 9);
					PPEOffset += 8;
				} while (PPEOffset < PPEOffsetEnd);
				RtlZeroMemory((PVOID)(PPEBase + PPEOffset), PAGE_SIZE - PPEOffset);
				__writecr8(OringinalIRQL);
			}
			break;
		}
		PXEVA += 8;
	} while (PXEVA < PXEVAEnd);
	return Result;
}

ULONG_PTR FillPDEArrayForAllValidPPEs(ULONG_PTR PagePointer, ULONG_PTR BaseAddress, ULONG SizeOfPPEPages, ULONG_PTR VirtualAddress)
{
	ULONG_PTR Result = 0;
	ULONG_PTR PteBase = PTEBase;
	ULONG_PTR OffsetMask = 0x7FFFFFFFF8;
	ULONG_PTR PDEVA = PteBase + (((PteBase + (((VirtualAddress & 0xFFFFFF8000000000) >> 9) & OffsetMask)) >> 9) & OffsetMask);
	ULONG_PTR PPEVA = PteBase + ((PDEVA >> 9) & OffsetMask);
	ULONG_PTR PXEVA = PteBase + ((PPEVA >> 9) & OffsetMask);
	if ((*(PUCHAR)PXEVA & 0x81) == 1) //不支持512G超大页面
	{
		if (SizeOfPPEPages >= PAGE_SIZE)
		{
			ULONG_PTR NewPDEVA = PagePointer + PAGE_SIZE;
			ULONG_PTR NewPDEVAEnd = NewPDEVA + SizeOfPPEPages;
			ULONG64 OringinalIRQL = __readcr8();
			__writecr8(DISPATCH_LEVEL);
			do
			{
				UCHAR ByteFlag = *(PUCHAR)PPEVA;
				if (ByteFlag & 1)
				{
					if ((CHAR)ByteFlag < 0) //为了不改变原先填充的PPE项,将1G大页面等分成512个2M大页面
					{
						ULONGLONG PDEStartValue = *(PULONGLONG)PPEVA;
						ULONG PDEOffset = 0;
						do
						{
							*(PULONGLONG)(NewPDEVA + PDEOffset) = PDEStartValue + (PDEOffset << 9);
							PDEOffset += 8;
						} while (PDEOffset < PAGE_SIZE);
					}
					else memcpy((PVOID)NewPDEVA, (PVOID)PDEVA, PAGE_SIZE);
				}
				else RtlZeroMemory((PVOID)NewPDEVA, PAGE_SIZE);				
				PDEVA += PAGE_SIZE;
				PPEVA += 8;
				NewPDEVA += PAGE_SIZE;
			} while (NewPDEVA < NewPDEVAEnd);
			__writecr8(OringinalIRQL);
			__writecr3(__readcr3());
			Result = BaseAddress + (VirtualAddress & 0x7FFFFFFFFF);
		}
	}	
	return Result;
}

BOOLEAN MakeDirtyPage(ULONG_PTR VirtualAddress)
{
	BOOLEAN Result = FALSE;
	ULONG_PTR PteBase = PTEBase;
	ULONG_PTR OffsetMask = 0x7FFFFFFFF8;
	ULONG_PTR PTEVA = PteBase + ((VirtualAddress >> 9) & OffsetMask);
	ULONG_PTR PDEVA = PteBase + ((PTEVA >> 9) & OffsetMask);
	ULONG_PTR PPEVA = PteBase + ((PDEVA >> 9) & OffsetMask);
	ULONG_PTR PXEVA = PteBase + ((PPEVA >> 9) & OffsetMask);
	if ((*(PUCHAR)PXEVA & 0x81) == 1) //不支持512G超大页面
	{
		UCHAR ByteFlag = *(PUCHAR)PPEVA;
		if (ByteFlag & 1)
		{
			if ((CHAR)ByteFlag < 0)
			{
				*(PUCHAR)PPEVA |= 0x42; //Dirty1 & Dirty
				Result = TRUE;
			}
			else
			{
				ByteFlag = *(PUCHAR)PDEVA;
				if (ByteFlag & 1)
				{
					if ((CHAR)ByteFlag < 0)
					{
						*(PUCHAR)PDEVA |= 0x42;
						Result = TRUE;
					}
					else
					{
						if (_bittest((PLONG)PTEVA, 0))
						{
							*(PUCHAR)PTEVA |= 0x42;
							Result = TRUE;
						}
					}
				}
			}
		}
	}
	__invlpg((PVOID)VirtualAddress);
	return Result;
}

void FreeSinglePXEDirectory(ULONG_PTR PagePointer, ULONG_PTR BaseAddress)
{
	ULONG_PTR PteBase = PTEBase;
	ULONG_PTR OffsetMask = 0x7FFFFFFFF8;
	*(PULONGLONG)(PteBase + (((PteBase + (((PteBase + (((PteBase + ((BaseAddress >> 9) & OffsetMask)) >> 9) & OffsetMask)) >> 9) & OffsetMask)) >> 9) & OffsetMask)) = 0;
	MmFreeContiguousMemory((PVOID)PagePointer);
	__writecr3(__readcr3());
}

    

    在Win10 18362.207 x64上的测试代码与结果:

NTKERNELAPI NTSTATUS ObReferenceObjectByName(IN PUNICODE_STRING ObjectName, IN ULONG Attributes, IN PACCESS_STATE PassedAccessState OPTIONAL, IN ACCESS_MASK DesiredAccess OPTIONAL, IN POBJECT_TYPE ObjectType, IN KPROCESSOR_MODE AccessMode, IN OUT PVOID ParseContext OPTIONAL, OUT PVOID *Object);
extern POBJECT_TYPE *IoDriverObjectType;

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegistryString)
{
	ULONG_PTR PagePointer = 0;
	ULONG_PTR BaseAddress = 0;
	ULONG SizeOfValidPPEPages = 0;
	if (GetPTEBase())
	{
		UNICODE_STRING UDrvName;
		PDRIVER_OBJECT DrvObj = 0;
		if (NT_SUCCESS(RtlInitUnicodeString(&UDrvName, L"\\Driver\\kbdclass")) && NT_SUCCESS(ObReferenceObjectByName(&UDrvName, OBJ_CASE_INSENSITIVE, 0, 0, *IoDriverObjectType, KernelMode, 0, (PVOID*)&DrvObj)))
		{
			ObDereferenceObject(DrvObj);
			PagePointer = AllocateSinglePXEDirectory(&BaseAddress, &SizeOfValidPPEPages);
			ULONG_PTR MappedKbdClassBase = FillPDEArrayForAllValidPPEs(PagePointer, BaseAddress, SizeOfValidPPEPages, (ULONG_PTR)DrvObj->DriverStart);
			if (MakeDirtyPage(MappedKbdClassBase)) *(PULONG)(MappedKbdClassBase + 4) = 0x78563412;
			FreeSinglePXEDirectory(PagePointer, BaseAddress);
		}
	}
	return STATUS_UNSUCCESSFUL;
}

    另外附上Win10的_MMPTE_HARDWARE以供参考

typedef struct _MMPTE_HARDWARE
{
	ULONGLONG Valid : 1;
	ULONGLONG Dirty1 : 1;
	ULONGLONG Owner : 1;
	ULONGLONG WriteThrough : 1;
	ULONGLONG CacheDisable : 1;
	ULONGLONG Accessed : 1;
	ULONGLONG Dirty : 1;
	ULONGLONG LargePage : 1;
	ULONGLONG Global : 1;
	ULONGLONG CopyOnWrite : 1;
	ULONGLONG Unused : 1;
	ULONGLONG Write : 1;
	ULONGLONG PageFrameNumber : 36; //Vista SP0为28,Vista SP1--10通用36
	ULONGLONG ReservedForHardware : 4;
	ULONGLONG ReservedForSoftware : 4;
	ULONGLONG WsleAge : 4;
	ULONGLONG WsleProtection : 3;
	ULONGLONG NoExecute : 1;
} MMPTE_HARDWARE, *PMMPTE_HARDWARE;

    总结一下,以上实现的代码大概类似弟中弟中弟版MmBuildMdlForNonPagedPool和MmMapLockedPagesSpecifyCache,不同的是本文的代码更多地相当于一份512G的虚拟地址空间物理内存的分布快照,这其中可能存在部分分页内存在快照过程中被放入物理内存而快照后再次被分回硬盘的情况,因此在映射地址空间使用MmIsAddressValid远远不如在原地址空间使用靠谱。

    10-7更新:感谢@syser老铁的关于刷新TLB的建议,测试发现刷TLB的确不需要KeFlushTb,只要对要访问或修改的映射页面invlpg刷新单个PTE对应的TLB项即可。以上代码在Vista SP0至Win10 18363均测试有效。


[看雪官方]走出瓶颈,《安卓高级研修班(网课)》月薪三万[秋季班],12月1日开班,仅剩2位名额!

最后于 2020-10-7 23:22 被hhkqqs编辑 ,原因:
收藏
点赞7
打赏
分享
最新回复 (37)
雪    币: 4054
活跃值: 活跃值 (282)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
如斯咩咩咩 活跃值 2020-10-3 23:34
2
0
插个眼
雪    币: 2450
活跃值: 活跃值 (321)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
syser 活跃值 2020-10-3 23:41
3
0

1.KeCapturePersistentThreadState调用的结构里直接就有PteBase,不需要用MmPfnDataBase计算
2.你这个没处理有Shadow的情况
3.不需要定位KeFlushTb,自己Ipi插过去直接自己刷就行,而且中断拉高因为自己当前用刷自己当前的就够了,不需要完整刷

最后于 2020-10-3 23:42 被syser编辑 ,原因:
雪    币: 4257
活跃值: 活跃值 (1327)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
hhkqqs 活跃值 1 2020-10-3 23:44
4
0
syser 1.KeCapturePersistentThreadState调用的结构里直接就有PteBase,不需要用MmPfnDataBase计算2.你这个没处理有Shadow的情况3.不需要定位KeFlus ...
PteBase是在某个RS1预览版才被加进去的,但MmPfnDataBase是全版本通用。我只考虑了内核页表的映射,所以就没管Shadow。至于lpi怎么刷倒是要请教一下,我已经在刷TLB这里卡了几天了
雪    币: 295
活跃值: 活跃值 (266)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
yy虫子yy 活跃值 2020-10-4 18:26
5
0
其实这样跟MDL的实现方式差不多,都是映射到新的地址
只是LZ不直接调用MDL,而是做了跟MDL同样的事
雪    币: 4257
活跃值: 活跃值 (1327)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
hhkqqs 活跃值 1 2020-10-4 18:41
6
0
yy虫子yy 其实这样跟MDL的实现方式差不多,都是映射到新的地址 只是LZ不直接调用MDL,而是做了跟MDL同样的事
我写这个纯粹吃饱了撑的,之前很好奇MDL的MmMapLockedPages是如何做到把只读内存映射到可写内存的,现在做个弟中弟版MDL才发现原来写保护只是基于虚拟地址而非物理地址
雪    币: 295
活跃值: 活跃值 (266)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
yy虫子yy 活跃值 2020-10-4 19:11
7
0
hhkqqs 我写这个纯粹吃饱了撑的,之前很好奇MDL的MmMapLockedPages是如何做到把只读内存映射到可写内存的,现在做个弟中弟版MDL才发现原来写保护只是基于虚拟地址而非物理地址
吃饱了撑的也是一种值得发扬的精神
雪    币: 4257
活跃值: 活跃值 (1327)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
hhkqqs 活跃值 1 2020-10-7 13:47
8
0
自顶一波
雪    币: 614
活跃值: 活跃值 (179)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Caim Astraea 活跃值 2020-10-7 22:47
9
0

mark

最后于 2020-10-8 12:16 被Caim Astraea编辑 ,原因:
雪    币: 21
活跃值: 活跃值 (50)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
ipnu 活跃值 2020-10-7 23:18
10
0
mark
雪    币: 424
活跃值: 活跃值 (553)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
萌克力 活跃值 2020-10-8 01:53
11
0
走一条没有人走过的路..才是最好的...
雪    币: 2575
活跃值: 活跃值 (396)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
淡然他徒弟 活跃值 2020-10-8 06:33
12
0
mark
雪    币: 211
活跃值: 活跃值 (264)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
叽叽‘and1=1 活跃值 2020-10-8 10:00
13
0
mark
雪    币: 998
活跃值: 活跃值 (91)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Rookietp 活跃值 2020-10-8 16:19
14
0
插眼
雪    币: 234
活跃值: 活跃值 (84)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
kakasasa 活跃值 2020-10-11 20:32
15
0
mark,虽然不搞内核
雪    币: 401
活跃值: 活跃值 (176)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
GhostValley 活跃值 2020-10-15 16:52
16
0

操作的其实是R/W位、bit6的Dirty位,其含义并非控制可读可写。

YY(估计是受最后放出的那个结构的影响)

雪    币: 401
活跃值: 活跃值 (176)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
GhostValley 活跃值 2020-10-15 16:59
17
0
“笔者发现当前的Intel CPU支持的虚拟地址寻址最大位数限制为48位,对于64位Windows来说,第47位被用来区分用户层虚拟地址和内核层虚拟地址,即内核层地址实际上只有47位的有效”
说明:
这有点渊源、Intel CPU其实有支持64位的、就是所谓的IA64架构。只不过用的不多、而且也和32位完全不兼容。而当时AMD出的AMD64架构、就是目前被广泛采用的结构、其规定最高的那几位为符号位。由此还引出一个规范性地址的说法。之所以能够流行、主要是因为此结构兼容32位地址结构、而且,即使有几位没用,也已经可以访问足够大的地址空间了。故此,现在所说的x64之类的、几乎都是这种架构。
所以、Intel有些手册上没有的一部分、AMD手册上则都能找到讷
雪    币: 4257
活跃值: 活跃值 (1327)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
hhkqqs 活跃值 1 2020-10-15 17:12
18
0
GhostValley “笔者发现当前的Intel CPU支持的虚拟地址寻址最大位数限制为48位,对于64位Windows来说,第47位被用来区分用户层虚拟地址和内核层虚拟地址,即内核层地址实际上只有47位的有效” 说明: ...
从报道来看,48位虚拟地址这种设计是Intel跟风AMD的结果,9999-12这种结构也确实完美兼容了32位的299-12,至于Intel有没有后悔这波跟风就是另一回事了。
雪    币: 4257
活跃值: 活跃值 (1327)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
hhkqqs 活跃值 1 2020-10-15 18:11
19
0
GhostValley 操作的其实是R/W位、bit6的Dirty位,其含义并非控制可读可写。YY(估计是受最后放出的那个结构的影响)
感谢提醒,之前查了些资料,也大概了解这两个位跟可写没什么关系,只是在linux中,新分配的页若没有置位Dirty,尝试写入会触发异常,后来发现在Windows里也是一样,我是挺好奇这个异常是从哪里分发的,请大佬不吝赐教
雪    币: 401
活跃值: 活跃值 (176)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
GhostValley 活跃值 2020-10-15 18:25
20
0
hhkqqs 感谢提醒,之前查了些资料,也大概了解这两个位跟可写没什么关系,只是在linux中,新分配的页若没有置位Dirty,尝试写入会触发异常,后来发现在Windows里也是一样,我是挺好奇这个异常是从哪里分发 ...
“这两个位跟可写没什么关系” 0x42,bit1是R/W位、bit6是Dirty位,bit1就是控制当前物理页是否可读可写的。我意在说明您说的Dirty其实已经是包含了对R/W位的操作的。
雪    币: 401
活跃值: 活跃值 (176)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
GhostValley 活跃值 2020-10-15 18:27
21
0
GhostValley “这两个位跟可写没什么关系” 0x42,bit1是R/W位、bit6是Dirty位,bit1就是控制当前物理页是否可读可写的。我意在说明您说的Dirty其实已经是包含了对R/W位的操作的。
只写入0x2应该也行吧
雪    币: 401
活跃值: 活跃值 (176)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
GhostValley 活跃值 2020-10-15 18:43
22
0
yy虫子yy 其实这样跟MDL的实现方式差不多,都是映射到新的地址 只是LZ不直接调用MDL,而是做了跟MDL同样的事
兄弟说的不大准确:
MDL结构和这个比起来、属于"上层应用"的范畴、MDL最后未文档化的PFN是其本质;
这篇文章则不同、其层面要靠下。
雪    币: 4257
活跃值: 活跃值 (1327)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
hhkqqs 活跃值 1 2020-10-15 19:24
23
0
GhostValley 只写入0x2应该也行吧
试了一下确实没必要改bit6,关键步骤就两步,改bit1+刷tlb
雪    币: 195
活跃值: 活跃值 (144)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
qdjytony 活跃值 2020-10-19 04:08
24
0
插眼
雪    币: 243
活跃值: 活跃值 (70)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
saloyun 活跃值 2020-10-21 14:08
25
0
mark,敬佩楼主。
游客
登录 | 注册 方可回帖
返回