Skip to content

Commit

Permalink
added chapter 26
Browse files Browse the repository at this point in the history
  • Loading branch information
ergot86 committed Sep 27, 2021
1 parent fb0b800 commit a097663
Show file tree
Hide file tree
Showing 22 changed files with 3,269 additions and 0 deletions.
9 changes: 9 additions & 0 deletions ch26/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM kalilinux/kali-rolling
COPY ./labs /labs
RUN apt update
RUN apt install -y build-essential nasm bsdmainutils
RUN apt install -y grub-pc-bin grub-efi-amd64-bin xorriso mtools
RUN apt install -y python3 pip
RUN pip install construct fixedint crc32c portion pypsrp capstone uuid
RUN /bin/bash -c "cd /labs && make"

26 changes: 26 additions & 0 deletions ch26/labs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
CFLAGS = -march=native -std=gnu99 -Wall -nostdlib -nostartfiles -nodefaultlibs -fno-stack-protector -mno-red-zone
MKRESCUE = /usr/bin/grub-mkrescue
GRUB_EFI_FLAGS = -d /usr/lib/grub/x86_64-efi/
GRUB_BIOS_FLAGS = -d /usr/lib/grub/i386-pc/
OBJECTS = main.o bootstrap.o common.o protocol.o

all: kernel_efi.iso kernel_bios.iso

%.o : %.c
$(CC) $(CFLAGS) -c $< -o $@

bootstrap.o : bootstrap.asm
nasm -felf64 $< -o $@

kernel_efi.iso: iso/boot/kernel
$(MKRESCUE) $(GRUB_EFI_FLAGS) -o $@ iso

kernel_bios.iso: iso/boot/kernel
$(MKRESCUE) $(GRUB_BIOS_FLAGS) -o $@ iso

iso/boot/kernel: $(OBJECTS)
$(LD) -melf_x86_64 $^ -o $@

clean:
rm -f *.o iso/boot/kernel *.iso

73 changes: 73 additions & 0 deletions ch26/labs/bootstrap.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
extern kmain
global _start
[bits 32]

[section .bss]
align 0x1000
resb 0x2000
stack_top:
pd: resb 0x1000 * 4 ; 4 PDs = maps 4GB
pdpt: resb 0x1000 ; 1 PDPT
pml4: resb 0x1000 ; 1 PML

[section .data]
gdt: ; minimal 64-bit GDT
dq 0x0000000000000000
dq 0x00A09b000000ffff ; kernel CS
dq 0x00C093000000ffff ; kernel DS
gdt_end: ; TODO: TSS
gdtr:
dw gdt_end - gdt - 1 ; GDT limit
dq gdt ; GDT base

[section .text]
align 8, db 0
;; multiboot2 header
mb_header_size equ (mb_header_end - mb_header)
mb_header:
dd 0xE85250D6 ; magic field
dd 0 ; architecture field: i386 32-bit protected-mode
dd mb_header_size ; header length field
dd 0xffffffff & -(0xE85250D6 + mb_header_size) ; checksum field
;; termination tag
dw 0 ; tag type
dw 0 ; tag flags
dd 8 ; tag size
mb_header_end:
;; kernel code starts here
_start:
mov esp, stack_top
mov edi, pd
mov ecx, 512*4
mov eax, 0x87
init_pde:
mov dword [edi], eax
add eax, 0x200000
add edi, 8
dec ecx
jnz init_pde
mov dword [pdpt], pd + 7
mov dword [pdpt+0x08], pd + 0x1007
mov dword [pdpt+0x10], pd + 0x2007
mov dword [pdpt+0x18], pd + 0x3007
mov eax, pml4
mov dword [eax], pdpt + 7
mov cr3, eax ; load page-tables
mov ecx, 0xC0000080
rdmsr
or eax, 0x101 ; LME | SCE
wrmsr ; set EFER
lgdt [gdtr] ; load 64-bit GDT
mov eax, 0x1ba ; PVI | DE | PSE | PAE | PGE | PCE
mov cr4, eax
mov eax, 0x8000003b ; PG | PE | MP | TS | ET | NE
mov cr0, eax
jmp 0x08:code64
[bits 64]
code64:
mov ax, 0x10
mov ds, ax
mov es, ax
mov ss, ax
mov rdi, rbx ; MULTIBOOT_MBI_REGISTER
call kmain
22 changes: 22 additions & 0 deletions ch26/labs/code.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import os
from subprocess import run
from tempfile import NamedTemporaryFile

class Code:
def __init__(self, code, sym):
self.code = '[bits 64]\n'
self.code += '\n'.join(f'{k} equ {v:#x}' for (k, v) in sym.items())
self.code += '\n%include "macros.asm"\n' + code

def build(self, base_address):
with NamedTemporaryFile('w') as f:
f.write(f'[org {base_address:#x}]\n' + self.code)
f.flush()
run(f'nasm -fbin -o {f.name}.bin {f.name}', shell=True)

with open(f'{f.name}.bin', 'rb') as fout:
ret = fout.read()

os.remove(f'{f.name}.bin')

return ret
80 changes: 80 additions & 0 deletions ch26/labs/common.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#include <stdint.h>
#include <stdbool.h>
#include <x86intrin.h>
#include "protocol.h"

static uint16_t SerialPort = 0x3f8; /* TODO: set it dynamically */

static void outb(uint16_t port, uint8_t val) {
__asm__ __volatile__("outb %0, %1" :: "a"(val), "Nd"(port));
}

static uint8_t inb(uint16_t port) {
uint8_t ret;
__asm__ __volatile__("inb %1, %0" : "=a"(ret) : "Nd"(port));
return ret;
}

void setup_serial() {
outb(SerialPort + 1, 0x00); /* disable interrupts */
outb(SerialPort + 3, 0x80); /* enable DLAB */
outb(SerialPort + 0, 0x01); /* divisor low = 1 (115200 baud) */
outb(SerialPort + 1, 0x00); /* divisor high = 0 */
outb(SerialPort + 3, 0x03); /* 8-bit, no parity, 1 stop bit */
outb(SerialPort + 2, 0xC7); /* FIFO, clear, 14-byte threshold */
outb(SerialPort + 4, 0x03); /* DTR/RTS */
}

void write_serial(const void *data, unsigned long len) {
const uint8_t *ptr = data;
while (len) {
if (!(inb(SerialPort + 5) & 0x20))
continue;
len -= 1;
outb(SerialPort, *ptr++);
}
}

void read_serial(void *data, unsigned long len) {
uint8_t *ptr = data;
while (len) {
if (!(inb(SerialPort + 5) & 1))
continue; /* TODO: yield CPU */
len -= 1;
*ptr++ = inb(SerialPort);
}
}

/* TODO: portable implementation supporting older CPUs */
uint32_t crc32(const void *data, unsigned long len) {
const uint8_t *ptr = data;
uint32_t ret = 0xffffffff;

while (len--)
ret = _mm_crc32_u8(ret, *ptr++);

return ret ^ 0xffffffff;
}

void reset() {
struct {
uint16_t limit;
unsigned long base;
} __attribute__((packed)) idtr = {0};

/* Hard-reset by triple-fault */
__asm__ __volatile__(
"lidt %0\n"
"int $1" :: "m" (idtr));
}

void _reset_oob_buffer();

void __assert(const char *msg, const char *file, int line) {
_reset_oob_buffer();
PUT_LIST(true, UInt32, OOBAssert, CString,
msg, CString, file, Int32, line
);
send_msg(MTOOB);
reset();
}
21 changes: 21 additions & 0 deletions ch26/labs/common.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#ifndef COMMON_H
#define COMMON_H
#include <stdint.h>

#define va_start(v,l) __builtin_va_start(v,l)
#define va_arg(v,l) __builtin_va_arg(v,l)
#define va_end(v) __builtin_va_end(v)
typedef __builtin_va_list va_list;

void setup_serial();
void write_serial(const void *data, unsigned long len);
void read_serial(void *data, unsigned long len);
uint32_t crc32(const void *data, unsigned long len);
void reset();
void __assert(const char *msg, const char *file, int line);
#define assert(EX) (void)((EX) || (__assert(#EX, __FILE__, __LINE__),0))

#define PTR_ADD(a, s) ((typeof(a))((unsigned long)a + s))
#define ALIGN_UP(a, s) ((a + (typeof(a))s - 1) & ~((typeof(a))s - 1))

#endif
88 changes: 88 additions & 0 deletions ch26/labs/guest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import protocol
from enum import Enum
from subprocess import Popen, PIPE
from remotemem import RemoteMemory

class OpType(Enum):
Write = 0
Exec = 1

class Guest:
guest_id = None

def __init__(self):
self.proc = None
self.memory = None
self.symbols = None
self._request = []

def __enter__(self):
self.proc = Popen(
('exec qemu-system-x86_64 '
'-display none '
'-boot d '
'-cdrom kernel_bios.iso '
'-m 300M '
'-serial stdio '
'-enable-kvm '
),
stdout=PIPE, stdin=PIPE, shell=True
)
return self

def __exit__(self, type, value, traceback):
self.proc.kill()

def _init_boot_info(self, symbols, mmap):
self.symbols = dict(symbols)
self.memory = RemoteMemory()

for entry in map(dict, mmap):
if entry['type'] == 1: # MULTIBOOT_MEMORY_AVAILABLE
self.memory.add_region(entry['address'], entry['length'])

kernel_end = (self.symbols['_end'] + 0x1000) & ~0xfff
self.memory.del_region(0, kernel_end)

def messages(self):
while self.proc.returncode is None:
msg = protocol.recv(self.proc.stdout)
msg_type, body = msg

if msg_type == protocol.MT.Boot:
self._init_boot_info(**dict(body))

yield msg

def op_write_data(self, data, address=None):
if address is None:
address = self.memory.alloc(len(data))

self._request += [
protocol.f.UInt32(OpType.Write.value),
protocol.f.UInt64(address),
tuple(protocol.f.UInt8(x) for x in data)
]
return address

def op_write(self, code, address=None):
if address is None:
address = self.memory.alloc(len(code.build(0)))

return self.op_write_data(code.build(address), address)

def op_exec(self, address):
self._request += [
protocol.f.UInt32(OpType.Exec.value),
protocol.f.UInt64(address)
]

def op_commit(self):
protocol.send(self.proc.stdin, self._request)
self._request.clear()

def execute(self, code):
address = self.op_write(code)
self.op_exec(address)
self.op_commit()
self.memory.free(address)
Loading

0 comments on commit a097663

Please sign in to comment.