use crate::backend::MemoryOps;
use crate::error::{Error, Result};
use crate::symbols::{TypeInfo, le_uint};
use crate::target::Target;
use crate::types::VirtAddr;
pub const KTRAP_FRAME_TYPE: &str = "_KTRAP_FRAME";
#[derive(Clone, Debug)]
pub struct KtrapFrame {
pub address: u64,
pub rax: u64,
pub rbx: u64,
pub rcx: u64,
pub rdx: u64,
pub rsi: u64,
pub rdi: u64,
pub rbp: u64,
pub rsp: u64,
pub r8: u64,
pub r9: u64,
pub r10: u64,
pub r11: u64,
pub rip: u64,
pub cs: u16,
pub ss: u16,
pub eflags: u32,
pub error_code: u64,
pub previous_mode: u8,
pub previous_irql: u8,
}
impl KtrapFrame {
pub fn decode(layout: &TypeInfo, address: u64, buf: &[u8]) -> Result<Self> {
let field = |name: &str| -> Result<u64> {
let f = layout
.fields
.get(name)
.ok_or_else(|| Error::FieldNotFound(name.to_string()))?;
let off = f.offset as usize;
let size = (f.size as usize).min(8);
if size == 0 || off + size > buf.len() {
return Err(Error::FieldNotFound(name.to_string()));
}
Ok(le_uint(&buf[off..off + size]))
};
Ok(Self {
address,
rax: field("Rax")?,
rbx: field("Rbx")?,
rcx: field("Rcx")?,
rdx: field("Rdx")?,
rsi: field("Rsi")?,
rdi: field("Rdi")?,
rbp: field("Rbp")?,
rsp: field("Rsp")?,
r8: field("R8")?,
r9: field("R9")?,
r10: field("R10")?,
r11: field("R11")?,
rip: field("Rip")?,
cs: field("SegCs")? as u16,
ss: field("SegSs")? as u16,
eflags: field("EFlags")? as u32,
error_code: field("ErrorCode")?,
previous_mode: field("PreviousMode")? as u8,
previous_irql: field("PreviousIrql")? as u8,
})
}
}
pub fn read_ktrap_frame(debugger: &Target, addr: VirtAddr) -> Result<KtrapFrame> {
let kernel_dtb = debugger.guest.ntoskrnl.dtb();
let layout = debugger
.symbols
.find_type_across_modules(kernel_dtb, KTRAP_FRAME_TYPE)
.ok_or_else(|| Error::StructNotFound(KTRAP_FRAME_TYPE.to_string()))?;
let mut buf = vec![0u8; layout.size];
debugger
.guest
.ntoskrnl
.memory()
.read_bytes(addr, &mut buf)?;
KtrapFrame::decode(&layout, addr.0, &buf)
}
pub fn read_ktrap_frame_at_or_current(
debugger: &Target,
addr: Option<VirtAddr>,
) -> Result<KtrapFrame> {
let addr = match addr {
Some(addr) => addr,
None => debugger
.current_thread_pseudo_register("trapframe")
.map(VirtAddr)
.ok_or_else(|| {
Error::DebugInfo(
"current thread has no saved trap frame (or no Windows thread context)".into(),
)
})?,
};
read_ktrap_frame(debugger, addr)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::symbols::{FieldInfo, ParsedType};
use std::collections::HashMap;
fn test_layout() -> TypeInfo {
let mut fields = HashMap::new();
let mut add = |name: &str, offset: u32, size: u64| {
fields.insert(
name.to_string(),
FieldInfo {
offset,
size,
type_data: ParsedType::Primitive("test".into()),
},
);
};
add("PreviousMode", 0x00, 1);
add("PreviousIrql", 0x01, 1);
add("Rax", 0x08, 8);
add("Rcx", 0x10, 8);
add("Rdx", 0x18, 8);
add("R8", 0x20, 8);
add("R9", 0x28, 8);
add("R10", 0x30, 8);
add("R11", 0x38, 8);
add("Rbx", 0x40, 8);
add("Rdi", 0x48, 8);
add("Rsi", 0x50, 8);
add("Rbp", 0x58, 8);
add("ErrorCode", 0x60, 8);
add("Rip", 0x68, 8);
add("SegCs", 0x70, 2);
add("EFlags", 0x74, 4);
add("Rsp", 0x78, 8);
add("SegSs", 0x80, 2);
TypeInfo {
name: KTRAP_FRAME_TYPE.to_string(),
size: 0x88,
fields,
}
}
#[test]
fn decodes_fields_at_pdb_offsets() {
let layout = test_layout();
let mut buf = vec![0u8; layout.size];
buf[0x00] = 1; buf[0x01] = 2; buf[0x08..0x10].copy_from_slice(&0x1111u64.to_le_bytes()); buf[0x60..0x68].copy_from_slice(&0x2u64.to_le_bytes()); buf[0x68..0x70].copy_from_slice(&0xffff_f800_1234_5678u64.to_le_bytes()); buf[0x70..0x72].copy_from_slice(&0x10u16.to_le_bytes()); buf[0x74..0x78].copy_from_slice(&0x0004_0246u32.to_le_bytes()); buf[0x78..0x80].copy_from_slice(&0xffff_b001_0000_0000u64.to_le_bytes()); buf[0x80..0x82].copy_from_slice(&0x18u16.to_le_bytes());
let frame = KtrapFrame::decode(&layout, 0xffff_b000_dead_0000, &buf).unwrap();
assert_eq!(frame.address, 0xffff_b000_dead_0000);
assert_eq!(frame.rax, 0x1111);
assert_eq!(frame.rip, 0xffff_f800_1234_5678);
assert_eq!(frame.rsp, 0xffff_b001_0000_0000);
assert_eq!(frame.cs, 0x10);
assert_eq!(frame.ss, 0x18);
assert_eq!(frame.eflags, 0x0004_0246);
assert_eq!(frame.error_code, 2);
assert_eq!(frame.previous_mode, 1);
assert_eq!(frame.previous_irql, 2);
}
#[test]
fn missing_field_is_an_error_not_a_zero() {
let mut layout = test_layout();
layout.fields.remove("Rip");
let buf = vec![0u8; layout.size];
assert!(matches!(
KtrapFrame::decode(&layout, 0, &buf),
Err(Error::FieldNotFound(name)) if name == "Rip"
));
}
#[test]
fn short_buffer_is_an_error() {
let layout = test_layout();
let buf = vec![0u8; 0x70];
assert!(KtrapFrame::decode(&layout, 0, &buf).is_err());
}
}