Skip to content
Open

v0.8 #13

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "kdmp-parser"
version = "0.7.0"
version = "0.8.0"
edition = "2024"
authors = ["Axel '0vercl0k' Souchet"]
categories = ["parser-implementations"]
Expand All @@ -9,18 +9,16 @@ 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.85"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bitflags = "2.9.2"
thiserror = "2"
rust-version = "1.91"

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

[lints.clippy]
pedantic = "warn"

[[example]]
name = "parser"
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<div align='center'>
<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>
<strong>A <a href="https://en.wikipedia.org/wiki/KISS_principle">KISS</a>, dependency free Rust crate to parse Windows kernel crash-dumps created by Windows & its debugger.</strong>
</p>
<p>
<a href="https://crates.io/crates/kdmp-parser"><img src="https://img.shields.io/crates/v/kdmp-parser.svg" /></a>
Expand Down
72 changes: 38 additions & 34 deletions examples/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ use std::path::PathBuf;

use anyhow::{Context, Result};
use clap::{Parser, ValueEnum};
use kdmp_parser::{Gpa, Gva, Gxa, KernelDumpParser, MappedFileReader};
use kdmp_parser::gxa::{Gpa, Gva, Gxa};
use kdmp_parser::map::MappedFileReader;
use kdmp_parser::parse::KernelDumpParser;
use kdmp_parser::{phys, virt};

#[derive(Debug, Default, Clone, Copy, ValueEnum)]
enum ReaderMode {
Expand All @@ -18,10 +21,12 @@ enum ReaderMode {
File,
}

#[expect(clippy::struct_excessive_bools)]
#[derive(Parser, Debug)]
#[command(version, about)]
struct Args {
/// The dump path.
#[arg(last = true)]
dump_path: PathBuf,
/// Dump the dump headers.
#[arg(long, default_value_t = false)]
Expand Down Expand Up @@ -65,37 +70,34 @@ fn hexdump(address: u64, data: &[u8], wanted_len: usize) {
let wanted_left = wanted_len - i;
// Do we need a full row or less?
let left_to_display = min(wanted_left, 16);
print!("{:016x}: ", address + (i as u64 * 16));
print!("{:016x}: ", address + i as u64);

// Iterate over the row now and populate it with the data. We do this because
// the output first displays the hexadecimal value of every bytes, and then its
// ASCII representation.
let mut row_it = row.iter_mut().enumerate().peekable();
while let Some((idx, item)) = row_it.next() {
// Drain the data iterator byte by byte and fill the row with the data.
match data_it.next() {
Some(c) => {
// If we have a byte, then easy peasy.
*item = Some(*c);
print!("{:02x}", c);
}
None => {
*item = None;
// If we don't have a byte, then we need to figure out what to do. There are two
// cases to take care of:
let displayed_amount = i + idx;
if displayed_amount >= wanted_len {
// - either what is left to display is not a full row, in which case we need
// to display spaces to padd the output such that the upcoming ASCII
// representation stays aligned.
print!(" ");
} else {
// - either the user asked a larger length than what is mapped in memory, in
// which case we need to display `??` for those bytes.
print!("??");
}
if let Some(c) = data_it.next() {
// If we have a byte, then easy peasy.
*item = Some(*c);
print!("{c:02x}");
} else {
*item = None;
// If we don't have a byte, then we need to figure out what to do. There are two
// cases to take care of:
let displayed_amount = i + idx;
if displayed_amount >= wanted_len {
// - either what is left to display is not a full row, in which case we need to
// display spaces to padd the output such that the upcoming ASCII
// representation stays aligned.
print!(" ");
} else {
// - either the user asked a larger length than what is mapped in memory, in
// which case we need to display `??` for those bytes.
print!("??");
}
};
}

// We separate half of the row with a dash. But we only want to display it if
// there'll be at least one byte after it (so at least 9 bytes to display in
Expand All @@ -117,7 +119,7 @@ fn hexdump(address: u64, data: &[u8], wanted_len: usize) {
print!("?");
}
}
println!()
println!();
}
}

Expand Down Expand Up @@ -169,21 +171,23 @@ fn main() -> Result<()> {
if let Some(addr) = args.mem {
let mut buffer = vec![0; args.len];
let addr = to_hex(&addr)?;
let phys_reader = phys::Reader::new(&parser);
let virt_reader = virt::Reader::with_dtb(
&parser,
args.dtb
.unwrap_or(Gpa::new(parser.headers().directory_table_base)),
);

if addr == u64::MAX {
for (gpa, _) in parser.physmem() {
parser.phys_read_exact(gpa, &mut buffer)?;
hexdump(gpa.u64(), &buffer, args.len)
phys_reader.read_exact(gpa, &mut buffer)?;
hexdump(gpa.u64(), &buffer, args.len);
}
} else {
let amount = if args.virt {
parser.virt_read_with_dtb(
Gva::new(addr),
&mut buffer,
args.dtb
.unwrap_or(Gpa::new(parser.headers().directory_table_base)),
)
virt_reader.read(Gva::new(addr), &mut buffer)
} else {
parser.phys_read(Gpa::new(addr), &mut buffer)
phys_reader.read(Gpa::new(addr), &mut buffer)
};

if let Ok(amount) = amount {
Expand Down
4 changes: 3 additions & 1 deletion src/bits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
//! //! # Examples
//!
//! ```
//! # use kdmp_parser::Bits;
//! # use kdmp_parser::bits::Bits;
//! let v = 0xAB_CD_EF_01_23_45_67_89u64;
//! assert_eq!(v.bits(0..=63), v);
//! assert_eq!(v.bits(0..=55), 0xCD_EF_01_23_45_67_89);
Expand All @@ -16,9 +16,11 @@ use std::ops::RangeInclusive;
/// Utility trait to make it easier to extract ranges of bits.
pub trait Bits: Sized {
/// Get a range of bits.
#[must_use]
fn bits(&self, r: RangeInclusive<usize>) -> Self;

/// Get a bit.
#[must_use]
fn bit(&self, n: usize) -> Self {
self.bits(n..=n)
}
Expand Down
Loading