Skip to content

Commit

Permalink
v0.2.0 (#4)
Browse files Browse the repository at this point in the history
The main change is exposing the list of kernel drivers / the list of user modules loaded when the dump was generated.
  • Loading branch information
0vercl0k committed Mar 29, 2024
1 parent 2fa8a36 commit f61d0bd
Show file tree
Hide file tree
Showing 12 changed files with 3,354 additions and 144 deletions.
16 changes: 6 additions & 10 deletions Cargo.toml
@@ -1,30 +1,26 @@
[package]
name = "kdmp-parser"
version = "0.1.1"
version = "0.2.0"
edition = "2021"
authors = ["Axel '0vercl0k' Souchet"]
categories = ["parser-implementations"]
description = "A KISS Rust crate to parse Windows kernel crash-dumps created by Windows & its debugger."
include = [
"/Cargo.toml",
"/LICENSE",
"/src/**",
"/examples/**",
"README.md",
]
include = ["/Cargo.toml", "/LICENSE", "/src/**", "/examples/**", "README.md"]
keywords = ["windows", "kernel", "crashdump"]
license = "MIT"
repository = "https://github.com/0vercl0k/kdmp-parser-rs"
rust-version = "1.70"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bitflags = "2.4.2"
thiserror = "1.0.58"
bitflags = "2.5.0"
thiserror = "1.0"

[dev-dependencies]
anyhow = "1.0.80"
clap = { version = "4.5.1", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

[[example]]
name = "parser"
18 changes: 14 additions & 4 deletions README.md
@@ -1,5 +1,5 @@
<div align='center'>
<h1><code>kdmp-parser-rs</code></h1>
<h1><code>kdmp-parser</code></h1>
<p>
<strong>A <a href="https://en.wikipedia.org/wiki/KISS_principle">KISS</a> Rust crate to parse Windows kernel crash-dumps created by Windows & its debugger.</strong>
</p>
Expand All @@ -22,7 +22,7 @@ The [parser](src/examples/parser.rs) application is a small utility to show-case

Here are the options supported:
```text
A Rust crate for parsing Windows kernel crashdumps
A KISS Rust crate to parse Windows kernel crash-dumps created by Windows & its debugger.
Usage: parser.exe [OPTIONS] <DUMP_PATH>
Expand All @@ -31,11 +31,14 @@ Arguments:
The dump path
Options:
--dump-headers
Dump the dump headers
-c, --context-record
Show the context record
Dump the context record
-e, --exception-record
Show the exception record
Dump the exception record
-m, --mem[=<MEM>]
Dump the first `len` bytes of every physical pages, unless an address is specified
Expand All @@ -44,15 +47,22 @@ Options:
The address specified is interpreted as a virtual address, not a physical address
--len <LEN>
The number of bytes to dump out
[default: 16]
-r, --reader <READER>
Reader mode
[default: mmap]
Possible values:
- mmap: The crash-dump is memory-mapped
- file: The crash-dump is read as a file on disk
--modules
Dump the list of kernel & user modules
-h, --help
Print help (see a summary with '-h')
Expand Down
28 changes: 21 additions & 7 deletions examples/parser.rs
Expand Up @@ -3,7 +3,7 @@ use core::default::Default;
use std::fs::File;
use std::path::PathBuf;

use anyhow::{anyhow, Context, Result};
use anyhow::{Context, Result};
use clap::{Parser, ValueEnum};
use kdmp_parser::{Gpa, Gva, Gxa, KernelDumpParser, MappedFileReader};

Expand All @@ -21,10 +21,13 @@ enum ReaderMode {
struct Args {
/// The dump path.
dump_path: PathBuf,
/// Show the context record.
/// Dump the dump headers.
#[arg(long, default_value_t = false)]
dump_headers: bool,
/// Dump the context record.
#[arg(short, long)]
context_record: bool,
/// Show the exception record.
/// Dump the exception record.
#[arg(short, long)]
exception_record: bool,
/// Dump the first `len` bytes of every physical pages, unless an address is
Expand All @@ -41,6 +44,9 @@ struct Args {
/// Reader mode.
#[arg(short, long, value_enum, default_value_t = ReaderMode::Mmap)]
reader: ReaderMode,
/// Dump the list of kernel & user modules.
#[arg(long, default_value_t = false)]
modules: bool,
}

/// Print a hexdump of data that started at `address`.
Expand Down Expand Up @@ -90,6 +96,10 @@ fn main() -> Result<()> {
}
.context("failed to parse the kernel dump")?;

if args.dump_headers {
println!("{:?}", parser.headers());
}

if args.context_record {
println!("{:#x?}", parser.context_record());
}
Expand All @@ -98,14 +108,18 @@ fn main() -> Result<()> {
println!("{:#x?}", parser.exception_record());
}

if args.modules {
for (at, module) in parser.user_modules().chain(parser.kernel_modules()) {
println!("{:#x}-{:#x}: {module}", at.start.u64(), at.end.u64());
}
}

if let Some(addr) = args.mem {
let mut buffer = vec![0; args.len];
let addr = to_hex(&addr)?;
if addr == u64::MAX {
for (gpa, _) in parser.physmem() {
parser
.phys_read_exact(gpa, &mut buffer)
.ok_or_else(|| anyhow!("failed to read {gpa}"))?;
parser.phys_read_exact(gpa, &mut buffer)?;
hexdump(gpa.u64(), &buffer)
}
} else {
Expand All @@ -115,7 +129,7 @@ fn main() -> Result<()> {
parser.phys_read(Gpa::new(addr), &mut buffer)
};

if let Some(amount) = amount {
if let Ok(amount) = amount {
hexdump(addr, &buffer[..amount]);
} else {
println!(
Expand Down
48 changes: 43 additions & 5 deletions src/error.rs
@@ -1,15 +1,47 @@
// Axel '0vercl0k' Souchet - March 19 2024
//! This is the error type used across the codebase.
use std::io;
use std::fmt::Display;
use std::{io, string};

use thiserror::Error;

use crate::structs::{HEADER64_EXPECTED_SIGNATURE, HEADER64_EXPECTED_VALID_DUMP};
use crate::Gpa;
use crate::structs::{DUMP_HEADER64_EXPECTED_SIGNATURE, DUMP_HEADER64_EXPECTED_VALID_DUMP};
use crate::{Gpa, Gva};
pub type Result<R> = std::result::Result<R, KdmpParserError>;

#[derive(Debug)]
pub enum PxeNotPresent {
Pml4e,
Pdpte,
Pde,
Pte,
}

#[derive(Debug, Error)]
pub enum AddrTranslationError {
Virt(Gva, PxeNotPresent),
Phys(Gpa),
}

impl Display for AddrTranslationError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AddrTranslationError::Virt(gva, not_pres) => f.write_fmt(format_args!(
"virt to phys translation of {gva}: {not_pres:?}"
)),
AddrTranslationError::Phys(gpa) => {
f.write_fmt(format_args!("phys to offset translation of {gpa}"))
}
}
}
}

#[derive(Error, Debug)]
pub enum KdmpParserError {
#[error("invalid UNICODE_STRING")]
InvalidUnicodeString,
#[error("utf16: {0}")]
Utf16(#[from] string::FromUtf16Error),
#[error("overflow: {0}")]
Overflow(&'static str),
#[error("io: {0}")]
Expand All @@ -20,14 +52,20 @@ pub enum KdmpParserError {
UnknownDumpType(u32),
#[error("duplicate gpa found in physmem map for {0}")]
DuplicateGpa(Gpa),
#[error("header's signature looks wrong: {0:#x} vs {HEADER64_EXPECTED_SIGNATURE:#x}")]
#[error("header's signature looks wrong: {0:#x} vs {DUMP_HEADER64_EXPECTED_SIGNATURE:#x}")]
InvalidSignature(u32),
#[error("header's valid dump looks wrong: {0:#x} vs {HEADER64_EXPECTED_VALID_DUMP:#x}")]
#[error("header's valid dump looks wrong: {0:#x} vs {DUMP_HEADER64_EXPECTED_VALID_DUMP:#x}")]
InvalidValidDump(u32),
#[error("overflow for phys addr w/ run {0} page {1}")]
PhysAddrOverflow(u32, u64),
#[error("overflow for page offset w/ run {0} page {1}")]
PageOffsetOverflow(u32, u64),
#[error("overflow for page offset w/ bitmap_idx {0} bit_idx {1}")]
BitmapPageOffsetOverflow(u64, usize),
#[error("partial physical memory read")]
PartialPhysRead,
#[error("partial virtual memory read")]
PartialVirtRead,
#[error("memory translation: {0}")]
AddrTranslation(#[from] AddrTranslationError),
}
2 changes: 1 addition & 1 deletion src/lib.rs
Expand Up @@ -9,7 +9,7 @@ mod pxe;
mod structs;

pub use bits::Bits;
pub use error::KdmpParserError;
pub use error::{AddrTranslationError, KdmpParserError, PxeNotPresent, Result};
pub use gxa::{Gpa, Gva, Gxa};
pub use map::{MappedFileReader, Reader};
pub use parse::KernelDumpParser;
Expand Down

0 comments on commit f61d0bd

Please sign in to comment.