use crate::bugchecks::BugcheckAnalysis;
use crate::target::{
AddressDescription, DeviceObjectDetail, DriverObjectDetail, IoStackLocationInfo, IrpHit,
IrpInfo, NotifyCallback, ObjectHeaderDetail, PteLevel, SsdtTable, Target,
irp_major_function_name, kthread_state_name, wait_reason_name,
};
pub enum View {
Hex(u64),
OptHex(Option<u64>),
Num(u64),
OptNum(Option<u64>),
Int(i64),
Bool(bool),
OptBool(Option<bool>),
Str(String),
OptStr(Option<String>),
Null,
List(Vec<View>),
Object(Vec<(&'static str, View)>),
}
#[cfg(feature = "mcp")]
pub fn to_json(v: &View) -> serde_json::Value {
use serde_json::Value;
match v {
View::Hex(n) => Value::from(format!("{n:#x}")),
View::OptHex(o) => o.map_or(Value::Null, |n| Value::from(format!("{n:#x}"))),
View::Num(n) => Value::from(*n),
View::OptNum(o) => o.map_or(Value::Null, Value::from),
View::Int(n) => Value::from(*n),
View::Bool(b) => Value::from(*b),
View::OptBool(o) => o.map_or(Value::Null, Value::from),
View::Str(s) => Value::from(s.clone()),
View::OptStr(o) => o.clone().map_or(Value::Null, Value::from),
View::Null => Value::Null,
View::List(items) => Value::Array(items.iter().map(to_json).collect()),
View::Object(fields) => {
let mut map = serde_json::Map::new();
for (key, val) in fields {
map.insert((*key).to_string(), to_json(val));
}
Value::Object(map)
}
}
}
#[cfg(feature = "python")]
pub fn to_py<'py>(
py: pyo3::Python<'py>,
v: &View,
) -> pyo3::PyResult<pyo3::Bound<'py, pyo3::PyAny>> {
use pyo3::IntoPyObjectExt;
use pyo3::prelude::*;
use pyo3::types::{PyDict, PyList};
Ok(match v {
View::Hex(n) | View::Num(n) => n.into_bound_py_any(py)?,
View::OptHex(o) | View::OptNum(o) => match o {
Some(n) => n.into_bound_py_any(py)?,
None => py.None().into_bound(py),
},
View::Int(n) => n.into_bound_py_any(py)?,
View::Bool(b) => b.into_bound_py_any(py)?,
View::OptBool(o) => match o {
Some(b) => b.into_bound_py_any(py)?,
None => py.None().into_bound(py),
},
View::Str(s) => s.as_str().into_bound_py_any(py)?,
View::OptStr(o) => match o {
Some(s) => s.as_str().into_bound_py_any(py)?,
None => py.None().into_bound(py),
},
View::Null => py.None().into_bound(py),
View::List(items) => {
let list = PyList::empty(py);
for item in items {
list.append(to_py(py, item)?)?;
}
list.into_any()
}
View::Object(fields) => {
let dict = PyDict::new(py);
for (key, val) in fields {
dict.set_item(key, to_py(py, val)?)?;
}
dict.into_any()
}
})
}
fn io_stack(s: &IoStackLocationInfo) -> View {
View::Object(vec![
("address", View::Hex(s.address.0)),
("major_function", View::Num(s.major_function as u64)),
(
"major_function_name",
View::Str(format!("IRP_MJ_{}", irp_major_function_name(s.major_function))),
),
("minor_function", View::Num(s.minor_function as u64)),
("device_object", View::Hex(s.device_object.0)),
("file_object", View::Hex(s.file_object.0)),
("completion_routine", View::Hex(s.completion_routine.0)),
("context", View::Hex(s.context.0)),
])
}
pub fn irp(irp: &IrpInfo) -> View {
View::Object(vec![
("address", View::Hex(irp.address.0)),
("type", View::Num(irp.irp_type as u64)),
("size", View::Num(irp.size as u64)),
("stack_count", View::Num(irp.stack_count as u64)),
("current_location", View::Num(irp.current_location as u64)),
("pending_returned", View::Bool(irp.pending_returned)),
("requestor_mode", View::Num(irp.requestor_mode as u64)),
("io_status", View::OptHex(irp.io_status.map(|s| s as u64))),
("user_event", View::Hex(irp.user_event.0)),
("user_buffer", View::Hex(irp.user_buffer.0)),
("mdl_address", View::Hex(irp.mdl_address.0)),
("thread", View::Hex(irp.thread.0)),
(
"current_stack",
irp.current_stack.as_ref().map_or(View::Null, io_stack),
),
])
}
pub fn driver_object(target: &Target, d: &DriverObjectDetail) -> View {
let dtb = target.guest.ntoskrnl.dtb();
let devices = d
.device_chain
.iter()
.map(|x| {
View::Object(vec![
("device", View::Hex(x.device.0)),
("device_type", View::Num(x.device_type as u64)),
("flags", View::Num(x.flags as u64)),
("characteristics", View::Num(x.characteristics as u64)),
("attached", View::Hex(x.attached.0)),
("next", View::Hex(x.next.0)),
])
})
.collect();
let dispatch = d
.dispatch
.iter()
.enumerate()
.map(|(i, f)| {
View::Object(vec![
("index", View::Num(i as u64)),
(
"name",
View::Str(format!("IRP_MJ_{}", irp_major_function_name(i as u8))),
),
("routine", View::Hex(f.0)),
(
"symbol",
View::OptStr(target.symbols.format_closest_symbol_for_address(dtb, *f)),
),
])
})
.collect();
View::Object(vec![
("object", View::Hex(d.object.0)),
("via_pointer", View::Bool(d.via_pointer)),
("name", View::OptStr(d.name.clone())),
("driver_start", View::Hex(d.driver_start.0)),
("driver_size", View::Num(d.driver_size)),
("driver_section", View::Hex(d.driver_section.0)),
("driver_unload", View::Hex(d.driver_unload.0)),
("devices", View::List(devices)),
("dispatch", View::List(dispatch)),
])
}
pub fn device_object(d: &DeviceObjectDetail) -> View {
let stack = d
.attached_stack
.iter()
.map(|x| {
View::Object(vec![
("device", View::Hex(x.device.0)),
("driver_object", View::Hex(x.driver_object.0)),
("device_type", View::Num(x.device_type as u64)),
("flags", View::Num(x.flags as u64)),
])
})
.collect();
View::Object(vec![
("object", View::Hex(d.object.0)),
("via_pointer", View::Bool(d.via_pointer)),
("device_type", View::Num(d.device_type as u64)),
("flags", View::Num(d.flags as u64)),
("characteristics", View::Num(d.characteristics as u64)),
("driver_object", View::Hex(d.driver_object.0)),
("attached_device", View::Hex(d.attached_device.0)),
("next_device", View::Hex(d.next_device.0)),
("current_irp", View::Hex(d.current_irp.0)),
("device_extension", View::Hex(d.device_extension.0)),
("attached_stack", View::List(stack)),
])
}
pub fn object_header(o: &ObjectHeaderDetail) -> View {
View::Object(vec![
("input", View::Hex(o.input.0)),
("mode", View::Str(o.mode.to_string())),
("header", View::Hex(o.header.0)),
("body", View::Hex(o.body.0)),
("pointer_count", View::Int(o.pointer_count)),
("handle_count", View::Int(o.handle_count)),
("type_index", View::OptNum(o.type_index)),
("type_object", View::OptHex(o.type_object.map(|t| t.0))),
("type_name", View::OptStr(o.type_name.clone())),
("info_mask", View::OptNum(o.info_mask.map(u64::from))),
("name_info", View::OptHex(o.name_info.map(|n| n.0))),
("name", View::OptStr(o.name.clone())),
])
}
pub fn notify_callback(c: &NotifyCallback, symbol: Option<String>) -> View {
View::Object(vec![
("kind", View::Str(c.kind.to_string())),
("index", View::Num(c.index as u64)),
("function", View::Hex(c.function.0)),
("symbol", View::OptStr(symbol)),
("block", View::Hex(c.block.0)),
("raw", View::Hex(c.raw.0)),
("context", View::Hex(c.context.0)),
])
}
pub fn ssdt_table(t: &SsdtTable) -> View {
let entries = t
.entries
.iter()
.map(|e| {
View::Object(vec![
("index", View::Num(e.index as u64)),
("target", View::Hex(e.target.0)),
("symbol", View::OptStr(e.symbol.clone())),
("module", View::OptStr(e.module.clone())),
])
})
.collect();
View::Object(vec![
("label", View::Str(t.label.clone())),
("base", View::Hex(t.base.0)),
("limit", View::Num(t.limit as u64)),
("entries", View::List(entries)),
])
}
pub fn irp_hit(h: &IrpHit) -> View {
View::Object(vec![
("irp", View::Hex(h.irp.0)),
("source", View::Str(h.source.to_string())),
("stack_count", View::Num(h.stack_count as u64)),
("current_location", View::Num(h.current_location as u64)),
("pid", View::OptNum(h.pid)),
("tid", View::OptNum(h.tid)),
("ethread", View::OptHex(h.ethread.map(|e| e.0))),
(
"state",
View::OptStr(h.state.map(|s| kthread_state_name(s).to_string())),
),
(
"wait_reason",
View::OptStr(h.wait_reason.map(|r| wait_reason_name(r).to_string())),
),
("driver", View::OptStr(h.driver.clone())),
("device", View::OptHex(h.device.map(|d| d.0))),
])
}
pub fn address_description(d: &AddressDescription) -> View {
let module = d.module.as_ref().map_or(View::Null, |m| {
View::Object(vec![
("name", View::Str(m.name.clone())),
("base", View::Hex(m.base.0)),
("size", View::Num(m.size as u64)),
("offset", View::Hex(m.offset)),
])
});
let region = d.region.as_ref().map_or(View::Null, |r| {
View::Object(vec![
("start", View::Hex(r.start.0)),
("end", View::Hex(r.end.0)),
("protection", View::OptNum(r.protection)),
("vad_type", View::OptNum(r.vad_type)),
("private_memory", View::OptBool(r.private_memory)),
("commit_charge", View::OptNum(r.commit_charge)),
("details", View::OptStr(r.details.clone())),
])
});
View::Object(vec![
("address", View::Hex(d.address.0)),
("dtb", View::Hex(d.dtb)),
("kind", View::Str(d.kind.to_string())),
("module", module),
("section", View::OptStr(d.section.clone())),
("va_type", View::OptStr(d.va_type.clone())),
("region", region),
])
}
pub fn pte_level(pte: &PteLevel) -> View {
View::Object(vec![
("level", View::Str(pte.name.clone())),
("address", View::Hex(pte.address.0)),
("value", View::Hex(pte.value.0)),
("pfn", View::Hex(pte.value.pfn())),
("present", View::Bool(pte.value.is_present())),
("large_page", View::Bool(pte.value.is_large_page())),
("writable", View::Bool(pte.value.is_writable())),
("user", View::Bool(pte.value.is_user())),
("nx", View::Bool(pte.value.is_nx())),
("flags", View::Str(pte.value.flags())),
])
}
pub fn bugcheck(a: &BugcheckAnalysis) -> View {
let args = a
.args
.iter()
.enumerate()
.map(|(i, arg)| {
View::Object(vec![
("index", View::Num((i + 1) as u64)),
("value", View::Hex(arg.value)),
("description", View::Str(arg.description.clone())),
])
})
.collect();
let fault = a.fault.as_ref().map_or(View::Null, |f| {
View::Object(vec![
("ip", View::Hex(f.ip)),
("symbol", View::Str(f.symbol.clone())),
("driver", View::OptStr(f.driver.clone())),
])
});
View::Object(vec![
("code", View::Num(a.code as u64)),
("code_hex", View::Str(format!("{:#010x}", a.code))),
("name", View::Str(a.name.clone())),
("description", View::OptStr(a.description.clone())),
("driver", View::OptStr(a.driver.clone())),
("source", View::OptStr(a.source.clone())),
("args", View::List(args)),
("fault", fault),
])
}