Skip to content

Instantly share code, notes, and snippets.

@tandasat
Last active April 29, 2023 14:56
Show Gist options
  • Select an option

  • Save tandasat/960653187242cf4615f373f3d0853225 to your computer and use it in GitHub Desktop.

Select an option

Save tandasat/960653187242cf4615f373f3d0853225 to your computer and use it in GitHub Desktop.

Revisions

  1. tandasat revised this gist Apr 29, 2023. 2 changed files with 136 additions and 0 deletions.
    68 changes: 68 additions & 0 deletions GetPhysicalMemoryRanges.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,68 @@
    /**
    * @brief Returns an array of physical memory address ranges on the system.
    *
    * @param This - The pointer to the platform API interface.
    * @param RangeCount - The pointer to receive the number of entries in the returned
    * pointer on success.
    * @return The pointer to the array of ranges on success. This must be freed with
    * Freed with FREE_CONTIGUOUS_PAGES. On failure, NULL.
    */
    STATIC
    SNAPSHOT_MEMORY_RANGE*
    MVAPI
    GetPhysicalMemoryRanges (
    IN CONST PLATFORM_API_INTERFACE* This,
    OUT UINT64* RangeCount
    )
    {
    EFI_STATUS status;
    UINT64 memoryMapSize;
    EFI_MEMORY_DESCRIPTOR* memoryMaps;
    UINT64 descriptorSize;
    SNAPSHOT_MEMORY_RANGE* ranges;
    UINT64 rangeCount;
    //
    // Get the memory map using GetMemoryMap().
    //
    memoryMapSize = 0;
    status = gBS->GetMemoryMap(&memoryMapSize,
    NULL,
    NULL,
    NULL,
    NULL);
    memoryMaps = This->AllocateContiguousPages(This,
    EFI_SIZE_TO_PAGES(memoryMapSize),
    AllocationTypeData);
    if (memoryMaps == NULL)
    {
    return NULL;
    }
    memoryMapSize = EFI_PAGES_TO_SIZE(EFI_SIZE_TO_PAGES(memoryMapSize));
    status = gBS->GetMemoryMap(&memoryMapSize,
    memoryMaps,
    NULL,
    &descriptorSize,
    NULL);
    ASSERT_EFI_ERROR(status);
    //
    // For each entry returned from GetMemoryMap(), save when the entry indicates
    // DRAM backed memory, ie, exclude MMIO regions.
    //
    rangeCount = 0;
    ranges = (SNAPSHOT_MEMORY_RANGE*)memoryMaps;
    for (UINT64 i = 0; i < memoryMapSize / descriptorSize; ++i)
    {
    CONST EFI_MEMORY_DESCRIPTOR* memoryMap;
    memoryMap = (EFI_MEMORY_DESCRIPTOR*)((UINT8*)memoryMaps + descriptorSize * i);
    if ((memoryMap->Type < EfiLoaderCode) ||
    (memoryMap->Type > EfiACPIMemoryNVS))
    {
    continue;
    }
    ranges[rangeCount].PageBase = memoryMap->PhysicalStart;
    ranges[rangeCount].PageCount = memoryMap->NumberOfPages;
    rangeCount++;
    }
    *RangeCount = rangeCount;
    return ranges;
    }
    68 changes: 68 additions & 0 deletions structures.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,68 @@
    //
    // Snapshot related definitions
    //
    #define SNAPSHOT_SIGNATURE 0x544F485350414E53 // 'SNAPSHOT'
    #define SNAPSHOT_MAX_MEMORY_RANGE_COUNT 47 // arbitrary
    typedef struct
    {
    IA32_DESCRIPTOR Gdtr; // +0x0
    UINT8 Reserved1[0x10 - sizeof(IA32_DESCRIPTOR)];
    IA32_DESCRIPTOR Idtr; // +0x10
    UINT8 Reserved2[0x10 - sizeof(IA32_DESCRIPTOR)];
    UINT16 Es; // +0x20
    UINT16 Cs;
    UINT16 Ss;
    UINT16 Ds;
    UINT16 Fs;
    UINT16 Gs;
    UINT16 Ldtr;
    UINT16 Tr;
    UINT64 Efer; // +0x30
    UINT64 SysenterCs;
    UINT64 Cr0; // +0x40
    UINT64 Cr3;
    UINT64 Cr4; // +0x50
    UINT64 FsBase;
    UINT64 GsBase; // +0x60
    UINT64 LdtrBase;
    UINT64 TrBase; // +0x70
    UINT64 Rsp;
    UINT64 Rip; // +0x80
    UINT64 Rflags;
    UINT64 SysenterEsp; // +0x90
    UINT64 SysenterEip;
    UINT64 Rax; // +0xa0
    UINT64 Rbx;
    UINT64 Rcx; // +0xb0
    UINT64 Rdx;
    UINT64 Rdi; // +0xc0
    UINT64 Rsi;
    UINT64 Rbp; // +0xd0
    UINT64 R8;
    UINT64 R9; // +0xe0
    UINT64 R10;
    UINT64 R11; // +0xf0
    UINT64 R12;
    UINT64 R13; // +0x100
    UINT64 R14;
    UINT64 R15; // +0x110
    } SNAPSHOT_REGISTERS;

    typedef struct
    {
    UINT64 Magic;
    UINT64 Reserved1;
    SNAPSHOT_MEMORY_RANGE MemoryRanges[SNAPSHOT_MAX_MEMORY_RANGE_COUNT];
    SNAPSHOT_REGISTERS Registers;
    UINT8 Reserved2[SIZE_4KB -
    sizeof(UINT64) * 2 -
    sizeof(SNAPSHOT_MEMORY_RANGE) * SNAPSHOT_MAX_MEMORY_RANGE_COUNT -
    sizeof(SNAPSHOT_REGISTERS)];
    } SNAPSHOT_HEADER;
    STATIC_ASSERT(sizeof(SNAPSHOT_HEADER) == SIZE_4KB, "Must be 4KB");

    typedef struct
    {
    UINT64 PageBase;
    UINT64 PageCount;
    } SNAPSHOT_MEMORY_RANGE;
  2. tandasat created this gist Apr 29, 2023.
    151 changes: 151 additions & 0 deletions Ide.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,151 @@
    /**
    * @file Ide.c
    * @author Satoshi Tanda (tanda.sat@gmail.com)
    * @brief Write data into the IDE storage device.
    * @date 2022-01-16
    *
    * @copyright Copyright (c) 2022, Satoshi Tanda. All rights reserved.
    *
    */
    #include "Ide.h"
    #include "HostUtils.h"

    //
    // See
    // https://wiki.osdev.org/PCI_IDE_Controller#Read.2FWrite_From_ATA_Drive
    // https://wiki.osdev.org/ATA_PIO_Mode
    //
    #define IDE_SECTOR_SIZE 512

    #define IDE_IO_ADDR_BASE 0x1f0
    #define IDE_DATA (IDE_IO_ADDR_BASE + 0)
    #define IDE_SECCOUNT0 (IDE_IO_ADDR_BASE + 2)
    #define IDE_SECCOUNT1 (IDE_IO_ADDR_BASE + 2)
    #define IDE_LBA0 (IDE_IO_ADDR_BASE + 3)
    #define IDE_LBA3 (IDE_IO_ADDR_BASE + 3)
    #define IDE_LBA1 (IDE_IO_ADDR_BASE + 4)
    #define IDE_LBA4 (IDE_IO_ADDR_BASE + 4)
    #define IDE_LBA2 (IDE_IO_ADDR_BASE + 5)
    #define IDE_LBA5 (IDE_IO_ADDR_BASE + 5)
    #define IDE_HDDEVSEL (IDE_IO_ADDR_BASE + 6)
    #define IDE_COMMAND (IDE_IO_ADDR_BASE + 7)
    #define IDE_COMMAND_WRITE_PIO 0x30
    #define IDE_COMMAND_CACHE_FLUSH 0xe7
    #define IDE_STATUS (IDE_IO_ADDR_BASE + 7)
    #define IDE_STATUS_BUSY (1 << 7)

    #define IDE_CONTROL 0x3f6
    #define IDE_CONTROL_NIEN (1 << 1)
    #define IDE_CONTROL_SRST (1 << 2)

    /**
    * @brief Wait until the IDE complete its processing and ready for another request.
    */
    STATIC
    VOID
    IdeWait (
    VOID
    )
    {
    while (IoRead8(IDE_STATUS) & IDE_STATUS_BUSY);
    }

    /**
    * @brief Write data into an IDE-related IO port and wait for IDE to complete it.
    *
    * @param Port - The IO port address to write.
    * @param Value - The value to write to the IO port.
    */
    STATIC
    VOID
    IdeWriteRegister (
    IN UINT64 Port,
    IN UINT8 Value
    )
    {
    IoWrite8(Port, Value);
    IdeWait();
    }

    STATIC BOOLEAN mIdeHasBeenReset = FALSE;

    /**
    * @brief Writes one sector (512 bytes) at the specified LBA of the master IDE device.
    *
    * @param LogicalBlockAddress - The logical block address to start writing.
    * @param Data - The pointer to the data to write.
    * @param DataSize - The size of the data to write in bytes.
    */
    STATIC
    VOID
    IdeWriteDisk (
    UINT64 LogicalBlockAddress,
    CONST UINT8* Data,
    UINT64 DataSize
    )
    {
    MV_HOST_ASSERT((DataSize % 2) == 0);

    //
    // Reset the device on the first use, or when it is left as busy.
    //
    if ((mIdeHasBeenReset == FALSE) ||
    (IoRead8(IDE_STATUS) & IDE_STATUS_BUSY))
    {
    IoWrite8(IDE_CONTROL, IDE_CONTROL_SRST);
    IdeWriteRegister(IDE_CONTROL, 0);
    mIdeHasBeenReset = TRUE;
    }

    //
    // Disable interrupts.
    //
    IdeWriteRegister(IDE_CONTROL, IDE_CONTROL_NIEN);

    //
    // Select master drive with LBA addressing mode.
    //
    IdeWriteRegister(IDE_HDDEVSEL, 0xe0);

    //
    // Set up to write one sector with the LBA specified.
    //
    IdeWriteRegister(IDE_SECCOUNT1, 0);
    IdeWriteRegister(IDE_LBA5, (LogicalBlockAddress >> (8 * 5)) & 0xff);
    IdeWriteRegister(IDE_LBA4, (LogicalBlockAddress >> (8 * 4)) & 0xff);
    IdeWriteRegister(IDE_LBA3, (LogicalBlockAddress >> (8 * 3)) & 0xff);
    IdeWriteRegister(IDE_SECCOUNT0, 1);
    IdeWriteRegister(IDE_LBA2, (LogicalBlockAddress >> (8 * 2)) & 0xff);
    IdeWriteRegister(IDE_LBA1, (LogicalBlockAddress >> (8 * 1)) & 0xff);
    IdeWriteRegister(IDE_LBA0, (LogicalBlockAddress >> (8 * 0)) & 0xff);

    //
    // Send the 28-bit LBA PIO write request command and write data onto the disk.
    // Note that the large IDE devices cannot work with this. Change this to
    // IDE_COMMAND_WRITE_PIO_EX and IDE_COMMAND_CACHE_FLUSH_EX if desired.
    //
    IdeWriteRegister(IDE_COMMAND, IDE_COMMAND_WRITE_PIO);
    for (UINT64 i = 0; i < DataSize; i += sizeof(UINT16))
    {
    IoWrite16(IDE_DATA, *(UINT16*)(Data + i));
    }
    IdeWriteRegister(IDE_COMMAND, IDE_COMMAND_CACHE_FLUSH);
    }

    VOID
    IdeWrite4Kb (
    IN UINT64 Offset,
    IN CONST UINT8* Data
    )
    {
    //
    // Write sectors (512 bytes for each) one by one while convert the offset to
    // the LBA.
    //
    for (UINT64 i = 0; i < SIZE_4KB / IDE_SECTOR_SIZE; i++)
    {
    IdeWriteDisk(Offset / IDE_SECTOR_SIZE + i,
    Data + IDE_SECTOR_SIZE * i,
    IDE_SECTOR_SIZE);
    }
    }
    23 changes: 23 additions & 0 deletions Ide.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,23 @@
    /**
    * @file Ide.h
    * @author Satoshi Tanda (tanda.sat@gmail.com)
    * @brief Write data into the IDE storage device.
    * @date 2022-01-16
    *
    * @copyright Copyright (c) 2022, Satoshi Tanda. All rights reserved.
    *
    */
    #pragma once
    #include "Common/Common.h"

    /**
    * @brief Writes 4KB of data onto at the specified offset of the IDE master device.
    *
    * @param Offset - The offset in the device to start writing.
    * @param Data - The pointer to 4KB data to write to the device.
    */
    VOID
    IdeWrite4Kb (
    IN UINT64 Offset,
    IN CONST UINT8* Data
    );
    115 changes: 115 additions & 0 deletions Snapshot.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,115 @@
    /**
    * @file Snapshot.c
    * @author Satoshi Tanda (tanda.sat@gmail.com)
    * @brief Takes the snapshot of the guest onto a disk.
    * @date 2022-01-16
    *
    * @copyright Copyright (c) 2022, Satoshi Tanda. All rights reserved.
    *
    */
    #include "Snapshot.h"
    #include "Ide.h"
    #include "HostUtils.h"
    #include "Common/VtxUtils.h"

    CONST SNAPSHOT_MEMORY_RANGE* gMemoryRanges;
    UINT64 gRangeCount;

    VOID
    TakeSnapshot (
    IN CONST VMEXIT_CONTEXT* VmExitContext
    )
    {
    SNAPSHOT_HEADER snapshotHeader;
    UINT64 offset;
    UINT64 pageBase;

    //
    // The first 4KB of the snapshot file is a header containing memory ranges
    // and register values. Capture them and write it into the disk.
    //
    offset = 0;
    ZeroMem(&snapshotHeader, sizeof(snapshotHeader));
    snapshotHeader.Magic = SNAPSHOT_SIGNATURE;
    CopyMem(&snapshotHeader.MemoryRanges[0], gMemoryRanges, sizeof(*gMemoryRanges) * gRangeCount);
    snapshotHeader.Registers.Gdtr.Limit = (UINT16)VmxRead(VMCS_GUEST_GDTR_LIMIT);
    snapshotHeader.Registers.Gdtr.Base = VmxRead(VMCS_GUEST_GDTR_BASE);
    snapshotHeader.Registers.Idtr.Limit = (UINT16)VmxRead(VMCS_GUEST_IDTR_LIMIT);
    snapshotHeader.Registers.Idtr.Base = VmxRead(VMCS_GUEST_IDTR_BASE);
    snapshotHeader.Registers.Es = (UINT16)VmxRead(VMCS_GUEST_ES_SELECTOR);
    snapshotHeader.Registers.Cs = (UINT16)VmxRead(VMCS_GUEST_CS_SELECTOR);
    snapshotHeader.Registers.Ss = (UINT16)VmxRead(VMCS_GUEST_SS_SELECTOR);
    snapshotHeader.Registers.Ds = (UINT16)VmxRead(VMCS_GUEST_DS_SELECTOR);
    snapshotHeader.Registers.Fs = (UINT16)VmxRead(VMCS_GUEST_FS_SELECTOR);
    snapshotHeader.Registers.Gs = (UINT16)VmxRead(VMCS_GUEST_GS_SELECTOR);
    snapshotHeader.Registers.Ldtr = (UINT16)VmxRead(VMCS_GUEST_LDTR_SELECTOR);
    snapshotHeader.Registers.Tr = (UINT16)VmxRead(VMCS_GUEST_TR_SELECTOR);
    snapshotHeader.Registers.Efer = VmxRead(VMCS_GUEST_EFER);
    snapshotHeader.Registers.SysenterCs = VmxRead(VMCS_GUEST_SYSENTER_CS);
    snapshotHeader.Registers.Cr0 = VmxRead(VMCS_GUEST_CR0);
    snapshotHeader.Registers.Cr3 = VmxRead(VMCS_GUEST_CR3);
    snapshotHeader.Registers.Cr4 = VmxRead(VMCS_GUEST_CR4);
    snapshotHeader.Registers.FsBase = VmxRead(VMCS_GUEST_FS_BASE);
    snapshotHeader.Registers.GsBase = VmxRead(VMCS_GUEST_GS_BASE);
    snapshotHeader.Registers.LdtrBase = VmxRead(VMCS_GUEST_LDTR_BASE);
    snapshotHeader.Registers.TrBase = VmxRead(VMCS_GUEST_TR_BASE);
    snapshotHeader.Registers.Rsp = VmxRead(VMCS_GUEST_RSP);
    snapshotHeader.Registers.Rip = VmxRead(VMCS_GUEST_RIP);
    snapshotHeader.Registers.Rflags = VmxRead(VMCS_GUEST_RFLAGS);
    snapshotHeader.Registers.SysenterEsp = VmxRead(VMCS_GUEST_SYSENTER_ESP);
    snapshotHeader.Registers.SysenterEip = VmxRead(VMCS_GUEST_SYSENTER_EIP);
    snapshotHeader.Registers.Rax = VmExitContext->Guest.StackSaved->Rax;
    snapshotHeader.Registers.Rbx = VmExitContext->Guest.StackSaved->Rbx;
    snapshotHeader.Registers.Rcx = VmExitContext->Guest.StackSaved->Rcx;
    snapshotHeader.Registers.Rdx = VmExitContext->Guest.StackSaved->Rdx;
    snapshotHeader.Registers.Rdi = VmExitContext->Guest.StackSaved->Rdi;
    snapshotHeader.Registers.Rsi = VmExitContext->Guest.StackSaved->Rsi;
    snapshotHeader.Registers.Rbp = VmExitContext->Guest.StackSaved->Rbp;
    snapshotHeader.Registers.R8 = VmExitContext->Guest.StackSaved->R8;
    snapshotHeader.Registers.R9 = VmExitContext->Guest.StackSaved->R9;
    snapshotHeader.Registers.R10 = VmExitContext->Guest.StackSaved->R10;
    snapshotHeader.Registers.R11 = VmExitContext->Guest.StackSaved->R11;
    snapshotHeader.Registers.R12 = VmExitContext->Guest.StackSaved->R12;
    snapshotHeader.Registers.R13 = VmExitContext->Guest.StackSaved->R13;
    snapshotHeader.Registers.R14 = VmExitContext->Guest.StackSaved->R14;
    snapshotHeader.Registers.R15 = VmExitContext->Guest.StackSaved->R15;
    IdeWrite4Kb(offset, (void*)&snapshotHeader);
    offset += sizeof(snapshotHeader);

    //
    // According with the memory range information, write the contents of memory
    // for each 4KB onto the disk. This is not limited to the guest memory and
    // does include hypervisor-owned memory which is unused for the purpose of
    // fuzzing.
    //
    pageBase = 0;
    for (UINT64 i = 0; i < gRangeCount; ++i)
    {
    CONST SNAPSHOT_MEMORY_RANGE* range;

    range = &gMemoryRanges[i];
    MV_HOST_INFO("%16llx - %16llx : Processing %llx pages",
    range->PageBase,
    range->PageBase + range->PageCount * SIZE_4KB,
    range->PageCount);

    for (UINT64 pageCount = 0; pageCount < range->PageCount; ++pageCount)
    {
    if ((pageCount != 0) && (pageCount % 1000) == 0)
    {
    MV_HOST_INFO("... done with %llu / %llu pages",
    pageCount,
    range->PageCount);
    }

    pageBase = range->PageBase + (SIZE_4KB * pageCount);
    IdeWrite4Kb(pageBase + offset, (const void*)pageBase);
    }
    }

    //
    // Write the end marker for the ease of reviewing the contents of the snapshot
    // file.
    //
    IdeWrite4Kb(pageBase + offset + SIZE_4KB, (const void*)"END_OF_FILE");
    }
    22 changes: 22 additions & 0 deletions Snapshot.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,22 @@
    /**
    * @file Snapshot.h
    * @author Satoshi Tanda (tanda.sat@gmail.com)
    * @brief Takes the snapshot of the guest onto a disk.
    * @date 2022-01-16
    *
    * @copyright Copyright (c) 2022, Satoshi Tanda. All rights reserved.
    *
    */
    #pragma once
    #include "Common/Common.h"
    #include "VmExitContext.h"

    /**
    * @brief Writes the snapshot at the offset 0 of the IDE master device.
    *
    * @param VmExitContext - The pointer to the VM-exit context.
    */
    VOID
    TakeSnapshot (
    IN CONST VMEXIT_CONTEXT* VmExitContext
    );