четверг, 21 мая 2020 г.

kernel pte in windows 10 64bit

Quarantine is a good time to re-read some old but useful papers and check if you can catch trick with making KUSER_SHARED_DATA writable again So lets see what we need:
  • some logic to extract PTE base from MiGetPteAddress
  • primitive to check if some address is valid - I used MmIsAddressValid
  • primitive to read page from kernel memory
Start with some system info:
pSystemRangeStart: FFFF800000000000
PTE base address: FFFF880000000000
PTE shift: 9

Do some calculation
function to get offset to PTE looks like:
return (address >> 9) & 0x7FFFFFFFF8;

for pSystemRangeStart PTE will be
4000000000 + pte_base = FFFF884000000000
You may notice that this address located above pte_base

for last available address in system FFFFFFFFFFFFFFFF PTE will be
7FFFFFFFF8 + pte_base = FFFF887FFFFFFFF8

Also we need some inverse function to convert from PTE to page address. First we need sub pte_base, then divide on sizeof(MMPTE_HARDWARE) and then multiply to size of page. And add some base address. Try to calculate this base for example for PTE of pSystemRangeStart FFFF884000000000:
(FFFF884000000000 - FFFF880000000000) / sizeof(MMPTE_HARDWARE) * 0x1000 = 800000000000

Our base is FFFF800000000000 (value of pSystemRangeStart) - 800000000000 = FFFF000000000000

Notice that this base does not match with pSystemRangeStart
So our inverse function may looks like:
return 0xFFFF000000000000 + (ptr_addr - pte_base) / sizeof(MMPTE_HARDWARE) * 0x1000;

Ok, it seems that we have all puzzle pieces and can scan all PTE from FFFF884000000000 to FFFF887FFFFFFFF8. Yes, all 0x4000000000 bytes - 256Gb. Sure, we can wait for results before retirement

Lets think if there are some ways to reduce amount of memory for scanning
You may notice that all of PTE not nailed in the middle of nothing but occupy some memory. And this memory also must have some valid PTE - lets call it secondary PTE. And if some page for this secondary PTE is not valid we can skip 512 * 0x1000 = 0x200000 PTE. So I just wrote some code to check first this secondary PTE and load only PTE located in valid memory


On my work machine it tooks only several seconds to scan all PTE.
Readed C75 pages for secondary PTE
183843 pages for PTE

Good news - yes, you can detect writable KUSER_SHARED_DATA. You could do it with reading of just 8 bytes if you know PTE base, mask and shift

Bad news - there are lots of writable pages without NX bit. On clear windows 10 build 19624 with 2Gb of RAM there are 4760 such pages and only 2 mapped to some drivers:
 [352] FFFFF80274360000 NX 0 -> \SystemRoot\System32\drivers\E1G6032E.sys
 [383] FFFFF8027437F000 NX 0 -> \SystemRoot\System32\drivers\E1G6032E.sys

On my work machines with 32Gb of RAM there are already 130698 such pages. I don`t know who allocated them and for what, but their very presence makes a threats detection virtually impossibly

Комментариев нет:

Отправка комментария