Skip to content

Commit

Permalink
Python3 bindings (#3)
Browse files Browse the repository at this point in the history
This PR adds Python 3 bindings and some new API:

- GetDirectoryTableBase
- TranslateVirtualToPhysical
- GetVirtualPage
- GetBugCheckParameters
  • Loading branch information
masthoon authored Aug 10, 2020
1 parent bac11ff commit 195f2b0
Show file tree
Hide file tree
Showing 11 changed files with 774 additions and 6 deletions.
5 changes: 4 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@
cmake_minimum_required (VERSION 3.8)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
set(CMAKE_POSITION_INDEPENDENT_CODE True)

OPTION(BUILD_TESTS "Build and run tests" OFF)

project(kdmp-parser)
add_subdirectory(src/lib)
add_subdirectory(src/parser)
add_subdirectory(src/testapp)
add_subdirectory(src/python)
if(BUILD_TESTS)
add_subdirectory(src/tests)
endif()
endif()

1 change: 1 addition & 0 deletions builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ def build(arch, configuration, tests_on):
cmake_config = (
'cmake',
f'-DCMAKE_RUNTIME_OUTPUT_DIRECTORY={output_dir}',
f'-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={output_dir}',
f'-DCMAKE_BUILD_TYPE={configuration}',
f'-DBUILD_TESTS={tests_on}'
)
Expand Down
76 changes: 73 additions & 3 deletions src/lib/kdmp-parser-structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,9 @@ static_assert(sizeof(KDMP_PARSER_PHYSMEM_DESC) == 0x20,
"PHYSICAL_MEMORY_DESCRIPTOR's size looks wrong.");

struct KDMP_PARSER_BMP_HEADER64 : public DisplayUtils {
static const uint32_t ExpectedSignature = 0x504D4453; // 'PMDS'
static const uint32_t ExpectedValidDump = 0x504D5544; // 'PMUD'
static const uint32_t ExpectedSignature = 0x504D4453; // 'PMDS'
static const uint32_t ExpectedSignature2 = 0x504D4446; // 'PMDF'
static const uint32_t ExpectedValidDump = 0x504D5544; // 'PMUD'

//
// Should be FDMP.
Expand Down Expand Up @@ -248,7 +249,7 @@ struct KDMP_PARSER_BMP_HEADER64 : public DisplayUtils {
// Integrity check the headers.
//

if (Signature != ExpectedSignature) {
if (Signature != ExpectedSignature && Signature != ExpectedSignature2) {
printf("KDMP_PARSER_BMP_HEADER64::Signature looks wrong.\n");
return false;
}
Expand Down Expand Up @@ -779,3 +780,72 @@ static_assert(offsetof(KDMP_PARSER_HEADER64, Comment) == 0xfb0,

static_assert(offsetof(KDMP_PARSER_HEADER64, BmpHeader) == 0x2000,
"The offset of BmpHeaders looks wrong.");


struct Page {

//
// Page size.
//

static const uint64_t Size = 0x1000;

//
// Page align an address.
//

static uint64_t Align(const uint64_t Address) { return Address & ~0xfff; }

//
// Extract the page offset off an address.
//

static uint64_t Offset(const uint64_t Address) { return Address & 0xfff; }
};


//
// Structure for parsing a PTE.
//

union MMPTE_HARDWARE {
struct {
uint64_t Present : 1;
uint64_t Write : 1;
uint64_t UserAccessible : 1;
uint64_t WriteThrough : 1;
uint64_t CacheDisable : 1;
uint64_t Accessed : 1;
uint64_t Dirty : 1;
uint64_t LargePage : 1;
uint64_t Available : 4;
uint64_t PageFrameNumber : 36;
uint64_t ReservedForHardware : 4;
uint64_t ReservedForSoftware : 11;
uint64_t NoExecute : 1;
};

uint64_t AsUINT64;

MMPTE_HARDWARE(const uint64_t Value) : AsUINT64(Value) {}
};

//
// Structure to parse a virtual address.
//

union VIRTUAL_ADDRESS {
struct {
uint64_t Offset : 12;
uint64_t PtIndex : 9;
uint64_t PdIndex : 9;
uint64_t PdPtIndex : 9;
uint64_t Pml4Index : 9;
uint64_t Reserved : 16;
};
uint64_t AsUINT64;
VIRTUAL_ADDRESS(const uint64_t Value) : AsUINT64(Value) {}
};

static_assert(sizeof(MMPTE_HARDWARE) == 8);
static_assert(sizeof(VIRTUAL_ADDRESS) == 8);
133 changes: 133 additions & 0 deletions src/lib/kdmp-parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,22 @@ const KDMP_PARSER_CONTEXT *KernelDumpParser::GetContext() {
return &DmpHdr_->ContextRecord;
}

const BugCheckParameters_t KernelDumpParser::GetBugCheckParameters() {

//
// Give the user a view of the bugcheck parameters.
//
BugCheckParameters_t Parameters = {
DmpHdr_->BugCheckCode,
DmpHdr_->BugCheckCodeParameter[0],
DmpHdr_->BugCheckCodeParameter[1],
DmpHdr_->BugCheckCodeParameter[2],
DmpHdr_->BugCheckCodeParameter[3],
};

return Parameters;
}

DumpType_t KernelDumpParser::GetDumpType() { return DmpHdr_->DumpType; }

bool KernelDumpParser::MapFile() { return FileMap_.MapFile(PathFile_); }
Expand Down Expand Up @@ -217,6 +233,23 @@ bool KernelDumpParser::BuildPhysmemFullDump() {
return true;
}

const uint64_t KernelDumpParser::PhyRead8(const uint64_t PhysicalAddress) const {

//
// Get the physical page and read from the offset.
//

const uint8_t * PhysicalPage = GetPhysicalPage(Page::Align(PhysicalAddress));

if (!PhysicalPage) {
printf("Internal page table parsing failed!\n");
return 0;
}

return *reinterpret_cast<const uint64_t*>(PhysicalPage + Page::Offset(PhysicalAddress));

}

const Physmem_t &KernelDumpParser::GetPhysmem() { return Physmem_; }

void KernelDumpParser::ShowContextRecord(const uint32_t Prefix = 0) const {
Expand Down Expand Up @@ -301,6 +334,10 @@ void KernelDumpParser::ShowAllStructures(const uint32_t Prefix = 0) const {
DmpHdr_->Show(Prefix);
}

const uint64_t KernelDumpParser::GetDirectoryTableBase() const {
return DmpHdr_->DirectoryTableBase;
}

const uint8_t *
KernelDumpParser::GetPhysicalPage(const uint64_t PhysicalAddress) const {

Expand All @@ -324,3 +361,99 @@ KernelDumpParser::GetPhysicalPage(const uint64_t PhysicalAddress) const {

return Pair->second;
}

const uint64_t
KernelDumpParser::VirtTranslate(const uint64_t VirtualAddress, const uint64_t DirectoryTableBase) const {

//
// If DirectoryTableBase is null ; use the one from the dump header and clear PCID bits (bits 11:0).
//

uint64_t LocalDTB = Page::Align(GetDirectoryTableBase());

if (DirectoryTableBase) {
LocalDTB = Page::Align(DirectoryTableBase);
}

//
// Stole code from @yrp604 and @0vercl0k.
//

const VIRTUAL_ADDRESS GuestAddress(VirtualAddress);
const MMPTE_HARDWARE Pml4(LocalDTB);
const uint64_t Pml4Base = Pml4.PageFrameNumber * Page::Size;
const uint64_t Pml4eGpa = Pml4Base + GuestAddress.Pml4Index * 8;
const MMPTE_HARDWARE Pml4e(PhyRead8(Pml4eGpa));
if (!Pml4e.Present) {
printf("Invalid page map level 4, address translation failed!\n");
return 0;
}

const uint64_t PdptBase = Pml4e.PageFrameNumber * Page::Size;
const uint64_t PdpteGpa = PdptBase + GuestAddress.PdPtIndex * 8;
const MMPTE_HARDWARE Pdpte(PhyRead8(PdpteGpa));
if (!Pdpte.Present) {
printf("Invalid page directory pointer table, address translation failed!\n");
return 0;
}

//
// huge pages:
// 7 (PS) - Page size; must be 1 (otherwise, this entry references a page
// directory; see Table 4-1
//

const uint64_t PdBase = Pdpte.PageFrameNumber * Page::Size;
if (Pdpte.LargePage) {
return (PdBase & 0xffff'ffff'c000'0000) + (VirtualAddress & 0x3fff'ffff);
}

const uint64_t PdeGpa = PdBase + GuestAddress.PdIndex * 8;
const MMPTE_HARDWARE Pde(PhyRead8(PdeGpa));
if (!Pde.Present) {
printf("Invalid page directory entry, address translation failed!\n");
return 0;
}

//
// large pages:
// 7 (PS) - Page size; must be 1 (otherwise, this entry references a page
// table; see Table 4-18
//

const uint64_t PtBase = Pde.PageFrameNumber * Page::Size;
if (Pde.LargePage) {
return (PtBase & 0xffff'ffff'ffe0'0000) + (VirtualAddress & 0x1f'ffff);
}

const uint64_t PteGpa = PtBase + GuestAddress.PtIndex * 8;
const MMPTE_HARDWARE Pte(PhyRead8(PteGpa));
if (!Pte.Present) {
printf("Invalid page table entry, address translation failed!\n");
return 0;
}

const uint64_t PageBase = Pte.PageFrameNumber * 0x1000;
return PageBase + GuestAddress.Offset;
}


const uint8_t *
KernelDumpParser::GetVirtualPage(const uint64_t VirtualAddress, const uint64_t DirectoryTableBase) const {

//
// First remove offset and translate the virtual address.
//

const uint64_t PhysicalAddress = VirtTranslate(Page::Align(VirtualAddress), DirectoryTableBase);

if (!PhysicalAddress) {
return nullptr;
}

//
// Then get the physical page.
//

return GetPhysicalPage(PhysicalAddress);
}
34 changes: 34 additions & 0 deletions src/lib/kdmp-parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@

using Physmem_t = std::unordered_map<uint64_t, const uint8_t *>;

struct BugCheckParameters_t {
uint32_t BugCheckCode;
uint64_t BugCheckCodeParameter[4];
};

class KernelDumpParser {
public:
KernelDumpParser();
Expand All @@ -25,6 +30,12 @@ class KernelDumpParser {

const KDMP_PARSER_CONTEXT *GetContext();

//
// Give the bugcheck parameters to the user.
//

const BugCheckParameters_t GetBugCheckParameters();

//
// Get the type of dump.
//
Expand Down Expand Up @@ -61,7 +72,30 @@ class KernelDumpParser {

const uint8_t *GetPhysicalPage(const uint64_t PhysicalAddress) const;

//
// Get the directory table base.
//

const uint64_t GetDirectoryTableBase() const;

//
// Translate a virtual address to physical address using a directory table base.
//

const uint64_t VirtTranslate(const uint64_t VirtualAddress, const uint64_t DirectoryTableBase = 0) const;

//
// Get the content of a virtual address.
//

const uint8_t *GetVirtualPage(const uint64_t VirtualAddress, const uint64_t DirectoryTableBase = 0) const;

private:
//
// Utility function to read an uint64_t from a physical address.
//

const uint64_t PhyRead8(const uint64_t PhysicalAddress) const;
//
// Build a map of physical addresses / page data pointers for full dump.
//
Expand Down
27 changes: 27 additions & 0 deletions src/python/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Mastho - 2020
link_libraries(kdmp-parser)
find_package (Python3 COMPONENTS Interpreter Development)

# Python on Windows Debug build only load $_d.pyd and require python_d.exe
if(CMAKE_BUILD_TYPE STREQUAL "Debug" AND WIN32)
set(PYMODULE kdmp_d CACHE INTERNAL "")
else()
set(PYMODULE kdmp CACHE INTERNAL "")
endif()

# Require Python3 interpreter and development libs
if(NOT Python3_Interpreter_FOUND OR (NOT Python3_Development_FOUND) OR (CMAKE_BUILD_TYPE STREQUAL "Debug" AND WIN32 AND NOT Python3_LIBRARY_DEBUG))
message(WARNING "Python3 development library not found")
set(PYMODULE "" CACHE INTERNAL "")
return()
endif()

Python3_add_library(${PYMODULE} SHARED python-kdmp.cc)

# On Windows Python load $.pyd :: On Linux remove lib$ prefix
if(WIN32)
set_target_properties(${PYMODULE} PROPERTIES SUFFIX ".pyd")
else()
set_target_properties(${PYMODULE} PROPERTIES PREFIX "")
endif()

Loading

0 comments on commit 195f2b0

Please sign in to comment.