#![allow(non_snake_case, non_camel_case_types)]
#[cfg(test)]
pub mod tests {
use crate::tests::util::local_debug;
use std::mem::offset_of;
use wdext::{
dbgeng::TimeoutQuery,
object::{traits::list::ListEntry, win_25h2::*},
util::ListEntryIterator,
zerocopy::*,
*,
};
mod util;
trait MyEntry: Sized + FromBytes + IntoBytes + Immutable {
type Ptr: TPtr<Self>;
type CharPtr: TPtr<u8>;
type Entry: ListEntry;
fn a(&self) -> i32;
fn entry(&self) -> Self::Entry;
fn s(&self) -> Self::CharPtr;
fn offset_of_entry() -> u64;
fn read_s(
&self,
ds: &WdDataSpaces,
mem: &Memory,
) -> WdResult<(String, ReadStringStatus)> {
ds.read_ansi_string(mem, self.s().offset(), 32, 932)
}
fn flink(&self) -> Self::Ptr {
(self.entry().flink().offset() - Self::offset_of_entry()).into()
}
fn blink(&self) -> Self::Ptr {
(self.entry().blink().offset() - Self::offset_of_entry()).into()
}
}
#[repr(C)]
#[derive(Debug, FromBytes, IntoBytes, Immutable)]
struct _MY_ENTRY64 {
a: i32,
_Pad1: [u8; 4],
entry: x64::list::_LIST_ENTRY,
s: TPtr64<u8>,
}
impl MyEntry for _MY_ENTRY64 {
type Ptr = TPtr64<Self>;
type CharPtr = TPtr64<u8>;
type Entry = x64::list::_LIST_ENTRY;
fn a(&self) -> i32 { self.a }
fn entry(&self) -> Self::Entry { self.entry }
fn s(&self) -> Self::CharPtr { self.s }
fn offset_of_entry() -> u64 { offset_of!(Self, entry) as u64 }
}
#[repr(C)]
#[derive(Debug, FromBytes, IntoBytes, Immutable)]
struct _MY_ENTRY32 {
a: i32,
entry: x86::list::_LIST_ENTRY,
s: TPtr32<u8>,
}
impl MyEntry for _MY_ENTRY32 {
type Ptr = TPtr32<Self>;
type CharPtr = TPtr32<u8>;
type Entry = x86::list::_LIST_ENTRY;
fn a(&self) -> i32 { self.a }
fn entry(&self) -> Self::Entry { self.entry }
fn s(&self) -> Self::CharPtr { self.s }
fn offset_of_entry() -> u64 { offset_of!(Self, entry) as u64 }
}
fn test_my_entries<E: MyEntry>(ctx: &TargetContext, timeout: TimeoutQuery) {
let ctrl = ctx.control();
let ds = ctx.data_spaces();
let mem = ctx.memory();
macro_rules! assert_my_entry {
($entry:expr, $a:expr, $s:expr) => {
assert_eq!($entry.a(), $a);
assert_eq!(
$entry.read_s(ds, mem).unwrap(),
($s.to_string(), ReadStringStatus::NulTerminated)
);
};
}
let offset_head_list: E::Ptr =
ctx.evaluate_pointer("ListEntry!Head").unwrap().into();
let offset_head: E::Ptr =
(offset_head_list.offset() - E::offset_of_entry()).into();
let head = offset_head.read(ctx, mem).unwrap();
assert_eq!(head.flink(), offset_head);
assert_eq!(head.blink(), offset_head);
let mut it = ListEntryIterator::<'_, E::Entry>::new(
ds,
mem,
offset_head_list.offset(),
None,
true,
false,
true,
false,
)
.unwrap();
assert!(it.next().is_none());
let mut it = ListEntryIterator::<'_, E::Entry>::new(
ds,
mem,
offset_head_list.offset(),
None,
true,
true,
true,
false,
)
.unwrap();
let tobj = it.next().unwrap().unwrap();
assert_eq!(tobj.ptr().offset(), offset_head_list.offset());
assert!(it.next().is_none());
ctrl.go(timeout).unwrap();
ctrl.go(timeout).unwrap();
ctrl.go_upper(timeout).unwrap();
let head = offset_head.read(ctx, mem).unwrap();
let offset_entry1 = head.flink();
let entry1 = offset_entry1.read(ctx, mem).unwrap();
let offset_entry2 = entry1.flink();
let entry2 = offset_entry2.read(ctx, mem).unwrap();
let offset_entry3 = entry2.flink();
let entry3 = offset_entry3.read(ctx, mem).unwrap();
assert_my_entry!(entry1, 1, "test 1");
assert_my_entry!(entry2, 2, "test 2");
assert_my_entry!(entry3, 3, "test 3");
let s = entry1.s().read_objects(ctx, mem, 7).unwrap();
assert_eq!(&s, b"test 1\x00".as_ref());
assert_eq!(entry3.flink(), offset_head);
assert_eq!(entry1.blink(), offset_head);
let head = offset_head.read(ctx, mem).unwrap();
let offset_entry3 = head.blink();
let entry3 = offset_entry3.read(ctx, mem).unwrap();
let offset_entry2 = entry3.blink();
let entry2 = offset_entry2.read(ctx, mem).unwrap();
let offset_entry1 = entry2.blink();
let entry1 = offset_entry1.read(ctx, mem).unwrap();
assert_my_entry!(entry1, 1, "test 1");
assert_my_entry!(entry2, 2, "test 2");
assert_my_entry!(entry3, 3, "test 3");
let mut it = ListEntryIterator::<'_, E::Entry>::new(
ds,
mem,
offset_head_list.offset(),
None,
true,
false,
true,
false,
)
.unwrap();
let tobj = it.next().unwrap().unwrap();
assert_eq!(
tobj.ptr().offset(),
offset_entry1.offset() + E::offset_of_entry()
);
let tobj = it.next().unwrap().unwrap();
assert_eq!(
tobj.ptr().offset(),
offset_entry2.offset() + E::offset_of_entry()
);
let tobj = it.next().unwrap().unwrap();
assert_eq!(
tobj.ptr().offset(),
offset_entry3.offset() + E::offset_of_entry()
);
assert!(it.next().is_none());
let mut it = ListEntryIterator::<'_, E::Entry>::new(
ds,
mem,
offset_head_list.offset(),
None,
false,
false,
true,
false,
)
.unwrap();
let tobj = it.next().unwrap().unwrap();
assert_eq!(
tobj.ptr().offset(),
offset_entry3.offset() + E::offset_of_entry()
);
let addr = it.next().unwrap().unwrap();
assert_eq!(
addr.ptr().offset(),
offset_entry2.offset() + E::offset_of_entry()
);
let addr = it.next().unwrap().unwrap();
assert_eq!(
addr.ptr().offset(),
offset_entry1.offset() + E::offset_of_entry()
);
assert!(it.next().is_none());
}
#[test]
fn test_list_entry() {
#[cfg(target_arch = "x86")]
let exe_path = r#"./tests/bin/x86/ListEntry.exe"#;
#[cfg(target_arch = "x86_64")]
let exe_path = r#"./tests/bin/x64/ListEntry.exe"#;
let ctx = local_debug(exe_path);
let ctrl = ctx.control();
let timeout = TimeoutQuery::from_secs(10);
let sym = ctx.symbols();
let offset_insert = sym
.get_symbol_offset_by_name("ListEntry!InsertTailListFunc")
.unwrap();
let _ = ctrl.add_software_breakpoint(offset_insert).unwrap();
ctrl.go(timeout).unwrap();
match ctx.pointer_width() {
PointerWidth::Ptr32 => {
test_my_entries::<_MY_ENTRY32>(&ctx, timeout)
}
PointerWidth::Ptr64 => {
test_my_entries::<_MY_ENTRY64>(&ctx, timeout)
}
}
}
}