Skip to content

Commit

Permalink
example, fromstr
Browse files Browse the repository at this point in the history
  • Loading branch information
0vercl0k committed Jun 12, 2024
1 parent 8cf4418 commit 7a9aba8
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 17 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/symbolizer-rs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
- name: cargo clippy
env:
RUSTFLAGS: "-Dwarnings"
run: cargo clippy --workspace --tests
run: cargo clippy --workspace --tests --examples

doc:
name: doc
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ clap = { version = "4.5", features = ["derive"] }
symbolizer = { path = "crates/symbolizer" }
env_logger = "0.11"
itoa = "1.0"
kdmp-parser = "0.2"
kdmp-parser = "0.3"

[profile.release]
debug = true
Expand Down
3 changes: 2 additions & 1 deletion crates/symbolizer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ thiserror = "1.0.61"
anyhow = "1.0.86"

[dev-dependencies]
kdmp-parser = "0.2"
kdmp-parser = "0.3"
udmp-parser = "0.2"
object = { version = "0.36.0", default-features = false, features = [
"read",
"read_core",
"pe",
"std",
] }
clap = { version = "4.5", features = ["derive"] }
160 changes: 160 additions & 0 deletions crates/symbolizer/examples/symbolize-dump.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
// Axel '0vercl0k' Souchet
use std::cmp::min;
use std::env;
use std::io::{self, Write};
use std::path::PathBuf;

use anyhow::Result;
use clap::{Parser, Subcommand};
use kdmp_parser::KernelDumpParser;
use symbolizer::{AddrSpace, Builder, Module};
use udmp_parser::UserDumpParser;

/// The command line arguments.
#[derive(Debug, Parser)]
#[command(about = "Symbolize an address from a user or kernel dump file.")]
enum CliArgs {
User { dump: PathBuf, addr: String },
Kernel { dump: PathBuf, addr: String },
}

/// Parse the `_NT_SYMBOL_PATH` environment variable to try the path of a symbol
/// cache.
fn sympath() -> Option<PathBuf> {
let env = env::var("_NT_SYMBOL_PATH").ok()?;

if !env.starts_with("srv*") {
return None;
}

let sympath = env.strip_prefix("srv*").unwrap();
let sympath = PathBuf::from(sympath.split('*').next().unwrap());

if sympath.is_dir() {
Some(sympath)
} else {
None
}
}

fn user(dmp: UserDumpParser, addr: u64) -> Result<()> {
#[derive(Debug)]
struct UserDumpAddrSpace<'a>(UserDumpParser<'a>);
impl<'a> AddrSpace for UserDumpAddrSpace<'a> {
fn read_at(&mut self, addr: u64, mut buf: &mut [u8]) -> io::Result<usize> {
let mut cur_addr = addr;
let mut read_len = 0;
while read_len < buf.len() {
let Some(block) = self.0.get_mem_block(addr) else {
return Err(io::Error::new(
io::ErrorKind::Unsupported,
format!("no mem block found for {addr:#x}"),
));
};

let Some(data) = block.data_from(cur_addr) else {
panic!();
};

let left = buf.len() - read_len;
let len = min(data.len(), left);
buf.write_all(&data[..len]).unwrap();
cur_addr += u64::try_from(len).unwrap();
read_len += len;
}

Ok(read_len)
}

fn try_read_at(&mut self, addr: u64, buf: &mut [u8]) -> io::Result<Option<usize>> {
match self.read_at(addr, buf) {
Ok(sz) => Ok(Some(sz)),
Err(_) => Ok(None),
}
}
}

let modules = dmp
.modules()
.values()
.map(|module| {
Module::new(
module.path.file_name().unwrap().to_string_lossy(),
module.start_addr(),
module.end_addr(),
)
})
.collect::<Vec<_>>();

let mut wrapper = UserDumpAddrSpace(dmp);
let mut symb = Builder::default()
.modules(&modules)
.msft_symsrv()
.symcache(&sympath().expect("define a _NT_SYMBOL_PATH"))
.build(&mut wrapper)?;

let mut s = Vec::new();
symb.full(addr, &mut s)?;
println!("{addr:#x}: {}", String::from_utf8(s)?);

Ok(())
}

fn kernel(dmp: KernelDumpParser, addr: u64) -> Result<()> {
#[derive(Debug)]
struct KernelDumpAdrSpace<'a>(&'a KernelDumpParser);
impl<'a> AddrSpace for KernelDumpAdrSpace<'a> {
fn read_at(&mut self, addr: u64, buf: &mut [u8]) -> io::Result<usize> {
self.0
.virt_read(addr.into(), buf)
.map_err(|e| io::Error::new(io::ErrorKind::Unsupported, e))
}

fn try_read_at(&mut self, addr: u64, buf: &mut [u8]) -> io::Result<Option<usize>> {
self.0
.try_virt_read(addr.into(), buf)
.map_err(|e| io::Error::new(io::ErrorKind::Unsupported, e))
}
}

let mut modules = Vec::new();
for (at, name) in dmp.user_modules().chain(dmp.kernel_modules()) {
let (_, filename) = name.rsplit_once('\\').unwrap_or((name, name));
modules.push(Module::new(
filename.to_string(),
at.start.into(),
at.end.into(),
));
}

let mut wrapper = KernelDumpAdrSpace(&dmp);
let mut symb = Builder::default()
.modules(&modules)
.msft_symsrv()
.symcache(&sympath().expect("define a _NT_SYMBOL_PATH"))
.build(&mut wrapper)?;

let mut s = Vec::new();
symb.full(addr, &mut s)?;
println!("{addr:#x}: {}", String::from_utf8(s)?);

Ok(())
}

fn hex(x: &str) -> Result<u64> {
let no_backtick = x.replace('`', "");
let no_prefix = no_backtick.strip_prefix("0x").unwrap_or(x);

Ok(u64::from_str_radix(no_prefix, 16)?)
}

fn main() -> Result<()> {
// Parse the CLI arguments.
let args = CliArgs::parse();
match args {
CliArgs::User { dump, addr } => user(UserDumpParser::new(dump)?, hex(&addr)?),
CliArgs::Kernel { dump, addr } => kernel(KernelDumpParser::new(dump)?, hex(&addr)?),
}?;

Ok(())
}
24 changes: 13 additions & 11 deletions crates/symbolizer/src/guid.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Axel '0vercl0k' Souchet - February 20 2024
//! This module contains the implementation of the [`Guid`] type.
use std::fmt::Display;
use std::str::FromStr;

use anyhow::anyhow;

Expand All @@ -15,16 +16,16 @@ pub struct Guid {
d3: [u8; 8],
}

impl TryFrom<&str> for Guid {
type Error = Error;
impl FromStr for Guid {
type Err = Error;

fn try_from(value: &str) -> Result<Self, Self::Error> {
if value.len() != 32 {
return Err(anyhow!("the guid str ({value:?}) should be 32 bytes long").into());
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() != 32 {
return Err(anyhow!("the guid str ({s:?}) should be 32 bytes long").into());
}

let mut bytes = [0; 16];
for (n, chunk) in value.as_bytes().chunks_exact(2).enumerate() {
for (n, chunk) in s.as_bytes().chunks_exact(2).enumerate() {
let s = std::str::from_utf8(chunk)?;
bytes[n] = u8::from_str_radix(s, 16)?;
}
Expand Down Expand Up @@ -70,6 +71,8 @@ impl Display for Guid {

#[cfg(test)]
mod tests {
use std::str::FromStr;

use crate::Guid;

const NTDLL_GUID: Guid = Guid {
Expand All @@ -81,14 +84,13 @@ mod tests {

#[test]
fn malformed_guids() {
assert!(Guid::try_from("8D5D5ED5D5B8AA609A82600C14E3004D1").is_err());

assert!(Guid::try_from("8D5D5ED5D5B8AA609A82600C14E3004").is_err());
assert!(Guid::from_str("8D5D5ED5D5B8AA609A82600C14E3004D1").is_err());
assert!(Guid::from_str("8D5D5ED5D5B8AA609A82600C14E3004").is_err());
}

#[test]
fn non_hex_guids() {
assert!(Guid::try_from("8D5D5ED5D5B8AA609A82600C14E3004Z").is_err());
assert!(Guid::from_str("8D5D5ED5D5B8AA609A82600C14E3004Z").is_err());
}

#[test]
Expand All @@ -99,7 +101,7 @@ mod tests {
// 00007ff9`aa450000 00007ff9`aa667000 ntdll (pdb symbols)
// c:\dbg\sym\ntdll.pdb\8D5D5ED5D5B8AA609A82600C14E3004D1\ntdll.pdb
assert_eq!(
Guid::try_from("8D5D5ED5D5B8AA609A82600C14E3004D").unwrap(),
"8D5D5ED5D5B8AA609A82600C14E3004D".parse::<Guid>().unwrap(),
NTDLL_GUID
)
}
Expand Down
6 changes: 3 additions & 3 deletions crates/symbolizer/tests/basics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ fn raw_virt() {
assert!(stats.did_download(
PdbId::new(
"mrt100.pdb",
"A20DA44BF08DB27D2BA0928F79447C7D".try_into().unwrap(),
"A20DA44BF08DB27D2BA0928F79447C7D".parse().unwrap(),
1
)
.unwrap()
Expand Down Expand Up @@ -191,7 +191,7 @@ fn raw_file() {
assert!(stats.did_download(
PdbId::new(
"mrt100.pdb",
"A20DA44BF08DB27D2BA0928F79447C7D".try_into().unwrap(),
"A20DA44BF08DB27D2BA0928F79447C7D".parse().unwrap(),
1
)
.unwrap()
Expand Down Expand Up @@ -273,7 +273,7 @@ fn user_dump() {
assert!(stats.did_download(
PdbId::new(
"ntdll.pdb",
"8D5D5ED5D5B8AA609A82600C14E3004D".try_into().unwrap(),
"8D5D5ED5D5B8AA609A82600C14E3004D".parse().unwrap(),
1
)
.unwrap()
Expand Down

0 comments on commit 7a9aba8

Please sign in to comment.