日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > linux >内容正文

linux

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

發(fā)布時(shí)間:2023/12/8 linux 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux内存写保护,[原创]不用CR0或MDL修改内核非分页写保护内存的一种思路(x64) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

開(kāi)門(mén)見(jiàn)山,本文的核心思路就是通過(guò)填充頁(yè)表項(xiàng),將一塊連續(xù)的虛擬地址映射到新的地址,同時(shí)將需要修改的只讀內(nèi)存對(duì)應(yīng)頁(yè)表項(xiàng)的Dirty位置位。在Windows操作系統(tǒng)下,寫(xiě)保護(hù)是通過(guò)保護(hù)特定虛擬地址實(shí)現(xiàn)的,若不建立新映射,則即使將Dirty位置位,嘗試寫(xiě)只讀內(nèi)存照樣會(huì)觸發(fā)BugCheck,若建立了新映射但不置位Dirty則觸發(fā)PAGE_FAULT的BugCheck,兩個(gè)步驟缺一不可。

填充頁(yè)表項(xiàng)首先需要?jiǎng)討B(tài)定位PTEBase,國(guó)際慣例是采用大表哥的頁(yè)表自映射法,代碼如下: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)?<

Result?=?TRUE;

break;

}

PXEOffset?+=?8;

}?while?(PXEOffset?

}

return?Result;

}

這里我順便也給出另一種獲取方案,基本思路是通過(guò)NT導(dǎo)出函數(shù)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大小的數(shù)據(jù),里面就存放有MmPfnDataBase,MmPfnDataBase是一個(gè)以物理地址頁(yè)幀號(hào)為索引的內(nèi)存信息數(shù)組,其中就有物理頁(yè)對(duì)應(yīng)的PTE項(xiàng)的地址PteAddress,我們傳一個(gè)有效的物理地址進(jìn)去(如當(dāng)前CR3: __readcr3() & 0xFFFFFFFFF000)取出其中的PteAddress,由于PTEBase必定是0x8000000000的倍數(shù),因此可由PteAddress直接算出PTEBase。另外,從Win10 RS1周年預(yù)覽版開(kāi)始,KeCapturePersistentThreadState開(kāi)始受全局變量ForceDumpDisabled的控制,若注冊(cè)表“HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CrashControl”中有關(guān)子項(xiàng)滿(mǎn)足條件,此變量會(huì)在開(kāi)機(jī)啟動(dòng)時(shí)置為1,導(dǎo)致KeCapturePersistentThreadState調(diào)用失敗。綜上所述我們得到第二種獲取PTEBase的代碼如下://若在Win10上測(cè)試,需要在這里加上"#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?

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后,現(xiàn)在進(jìn)入正題,下面代碼的主要思路是:首先從某個(gè)有效的按512G對(duì)齊的內(nèi)核虛擬地址開(kāi)始,一直到0xFFFFFFFFFFFFFFFF,查找未被占用的PML4T(下文統(tǒng)稱(chēng)PXE)子項(xiàng),即PXE的Valid位為0的子項(xiàng)。

對(duì)于有效起始內(nèi)核虛擬地址,一開(kāi)始筆者選用的是MmSystemRangeStart,虛擬機(jī)測(cè)試發(fā)現(xiàn)對(duì)于8.1/10映射成功了,而Vista/7/8下CPU并沒(méi)有承認(rèn)映射地址的有效性觸發(fā)了BugCheck,調(diào)試器發(fā)現(xiàn)Vista/7/8下MmSystemRangeStart=0xFFFF080000000000,而8.1/10下MmSystemRangeStart=0xFFFF800000000000,并且Vista/7/8下映射地址范圍為[0xFFFF080000000000,?0xFFFF800000000000)時(shí),調(diào)試器的CrashDump提示為Noncanonical Virtual Address。查閱了Intel手冊(cè)后,筆者發(fā)現(xiàn)當(dāng)前的Intel CPU支持的虛擬地址尋址最大位數(shù)限制為48位,對(duì)于64位Windows來(lái)說(shuō),第47位被用來(lái)區(qū)分用戶(hù)層虛擬地址和內(nèi)核層虛擬地址,即內(nèi)核層地址實(shí)際上只有47位的有效位,于是得出有效起始內(nèi)核虛擬地址為0xFFFF800000000000。當(dāng)然嚴(yán)謹(jǐn)起見(jiàn),可以使用CPUID的0x80000008號(hào)功能,此時(shí)eax寄存器的ah即為處理器支持的虛擬地址最大有效位數(shù),設(shè)其為x,則對(duì)64位地址,只要將最高的(65-x)位全部置1,剩余的(x-1)位全部置0,即得有效起始內(nèi)核虛擬地址。

找到未使用的PXE子項(xiàng)后,申請(qǐng)一段連續(xù)的物理內(nèi)存,初始大小為PXE子項(xiàng)以及PXE子項(xiàng)的512個(gè)PPE項(xiàng)所描述的頁(yè)面大小,即(1 + 0x200) * PAGE_SIZE,若申請(qǐng)失敗,則將申請(qǐng)的PPE項(xiàng)減半,以此類(lèi)推……由于是連續(xù)物理內(nèi)存,因此最好的方案是通過(guò)MmAllocateContiguousMemory申請(qǐng),若使用ExAllocatePool,則當(dāng)申請(qǐng)的頁(yè)面不是2M大頁(yè)面時(shí),其虛擬地址對(duì)應(yīng)的物理地址很可能不是連續(xù)的,這會(huì)給我們后續(xù)填充512個(gè)PPE項(xiàng)的物理頁(yè)幀號(hào)徒增不少麻煩。申請(qǐng)到連續(xù)物理地址后,第一個(gè)頁(yè)面填充至目標(biāo)PXE子項(xiàng),第2到513個(gè)頁(yè)面的物理頁(yè)幀號(hào)按順序填充到PXE子項(xiàng)頁(yè)面描述的512個(gè)PPE項(xiàng)中。隨后給定任一個(gè)需要映射的虛擬地址,我們先將它按0x8000000000(512G)進(jìn)行對(duì)齊,再依次檢索其PXE、PPE、PDE項(xiàng),若PXE項(xiàng)Valid為0或LargePage為1則不映射,否則開(kāi)始依次檢索PPE和PDE。若PPE項(xiàng)Valid為0,則把映射地址對(duì)應(yīng)的PPE頁(yè)面清零。若Valid為1則分別處理是否LargePage的情形,若為1G大頁(yè)面,則將其等分成512個(gè)2M大頁(yè)面,再把對(duì)應(yīng)的物理頁(yè)幀號(hào)按順序填充到PPE描述的512個(gè)PDE項(xiàng)中;若不是大頁(yè)面,則將被映射地址的PPE項(xiàng)對(duì)應(yīng)的一頁(yè)全部復(fù)制到映射地址對(duì)應(yīng)的PPE頁(yè)面中。從這里開(kāi)始,基本的地址映射已經(jīng)完成,接下來(lái)對(duì)欲修改的頁(yè)面只要把其對(duì)應(yīng)的PTE或大頁(yè)面PDE或大頁(yè)面PPE項(xiàng)的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;?//有效起始內(nèi)核虛擬地址0xFFFF800000000000對(duì)應(yīng)的PXE子項(xiàng)

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)?<

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)?<

PPEOffset?+=?8;

}?while?(PPEOffset?

RtlZeroMemory((PVOID)(PPEBase?+?PPEOffset),?PAGE_SIZE?-?PPEOffset);

__writecr8(OringinalIRQL);

}

break;

}

PXEVA?+=?8;

}?while?(PXEVA?

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超大頁(yè)面

{

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?

{

ULONGLONG?PDEStartValue?=?*(PULONGLONG)PPEVA;

ULONG?PDEOffset?=?0;

do

{

*(PULONGLONG)(NewPDEVA?+?PDEOffset)?=?PDEStartValue?+?(PDEOffset?<

PDEOffset?+=?8;

}?while?(PDEOffset?

}

else?memcpy((PVOID)NewPDEVA,?(PVOID)PDEVA,?PAGE_SIZE);

}

else?RtlZeroMemory((PVOID)NewPDEVA,?PAGE_SIZE);

PDEVA?+=?PAGE_SIZE;

PPEVA?+=?8;

NewPDEVA?+=?PAGE_SIZE;

}?while?(NewPDEVA?

__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超大頁(yè)面

{

UCHAR?ByteFlag?=?*(PUCHAR)PPEVA;

if?(ByteFlag?&?1)

{

if?((CHAR)ByteFlag?

{

*(PUCHAR)PPEVA?|=?0x42;?//Dirty1?&?Dirty

Result?=?TRUE;

}

else

{

ByteFlag?=?*(PUCHAR)PDEVA;

if?(ByteFlag?&?1)

{

if?((CHAR)ByteFlag?

{

*(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上的測(cè)試代碼與結(jié)果: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;

總結(jié)一下,以上實(shí)現(xiàn)的代碼大概類(lèi)似弟中弟中弟版MmBuildMdlForNonPagedPool和MmMapLockedPagesSpecifyCache,不同的是本文的代碼更多地相當(dāng)于一份512G的虛擬地址空間物理內(nèi)存的分布快照,這其中可能存在部分分頁(yè)內(nèi)存在快照過(guò)程中被放入物理內(nèi)存而快照后再次被分回硬盤(pán)的情況,因此在映射地址空間使用MmIsAddressValid遠(yuǎn)遠(yuǎn)不如在原地址空間使用靠譜。

10-7更新:感謝@syser老鐵的關(guān)于刷新TLB的建議,測(cè)試發(fā)現(xiàn)刷TLB的確不需要KeFlushTb,只要對(duì)要訪問(wèn)或修改的映射頁(yè)面invlpg刷新單個(gè)PTE對(duì)應(yīng)的TLB項(xiàng)即可。以上代碼在Vista SP0至Win10 18363均測(cè)試有效。

最后于 2020-10-7 23:22

被hhkqqs編輯

,原因:

總結(jié)

以上是生活随笔為你收集整理的linux内存写保护,[原创]不用CR0或MDL修改内核非分页写保护内存的一种思路(x64)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。