paavohtl Claude Opus 4.6 commited on
Commit
77457b4
·
1 Parent(s): 9d55850

feat: add mem module with pointer classification to openwa-core

Browse files

Add DATA_END constant and mem module with can_read, classify_pointer,
and classify_region functions extracted from the validation dump logic.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

crates/openwa-core/Cargo.toml CHANGED
@@ -7,6 +7,7 @@ description = "Core library for Worms Armageddon reverse engineering — types,
7
  [dependencies]
8
  heapless = "0.9"
9
  hound = "3"
 
10
 
11
  [target.'cfg(target_os = "windows")'.dependencies]
12
  windows = { version = "0.62", features = [
 
7
  [dependencies]
8
  heapless = "0.9"
9
  hound = "3"
10
+ openwa-debug-proto = { path = "../openwa-debug-proto" }
11
 
12
  [target.'cfg(target_os = "windows")'.dependencies]
13
  windows = { version = "0.62", features = [
crates/openwa-core/src/address.rs CHANGED
@@ -17,6 +17,7 @@ pub mod va {
17
  pub const TEXT_END: u32 = 0x0061_9FFF;
18
  pub const RDATA_START: u32 = 0x0061_A000;
19
  pub const DATA_START: u32 = 0x0069_4000;
 
20
 
21
  // === Vtables (in .rdata) ===
22
 
 
17
  pub const TEXT_END: u32 = 0x0061_9FFF;
18
  pub const RDATA_START: u32 = 0x0061_A000;
19
  pub const DATA_START: u32 = 0x0069_4000;
20
+ pub const DATA_END: u32 = 0x008C_5000; // .rsrc starts here; .data/.bss ends just before
21
 
22
  // === Vtables (in .rdata) ===
23
 
crates/openwa-core/src/lib.rs CHANGED
@@ -14,6 +14,7 @@ pub mod render;
14
  pub mod address;
15
  pub mod fixed;
16
  pub mod log;
 
17
  pub mod rebase;
18
  pub mod task;
19
  pub mod vtable;
 
14
  pub mod address;
15
  pub mod fixed;
16
  pub mod log;
17
+ pub mod mem;
18
  pub mod rebase;
19
  pub mod task;
20
  pub mod vtable;
crates/openwa-core/src/mem.rs ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ //! Memory inspection utilities — pointer classification and safe read checks.
2
+
3
+ use crate::address::va;
4
+ use openwa_debug_proto::{PointerInfo, PointerKind};
5
+
6
+ #[cfg(target_os = "windows")]
7
+ extern "system" {
8
+ fn IsBadReadPtr(lp: *const u8, ucb: usize) -> i32;
9
+ }
10
+
11
+ /// Check if a pointer is safe to read.
12
+ #[cfg(target_os = "windows")]
13
+ #[inline]
14
+ pub unsafe fn can_read(ptr: u32, size: u32) -> bool {
15
+ ptr >= 0x10000 && IsBadReadPtr(ptr as *const u8, size as usize) == 0
16
+ }
17
+
18
+ /// Stub for non-Windows (always returns false).
19
+ #[cfg(not(target_os = "windows"))]
20
+ pub unsafe fn can_read(_ptr: u32, _size: u32) -> bool {
21
+ false
22
+ }
23
+
24
+ /// Classify a DWORD value as a pointer kind, if applicable.
25
+ ///
26
+ /// Returns `None` for zero values, small values (< 0x10000), and
27
+ /// values that don't point into any known section or readable memory.
28
+ ///
29
+ /// `delta` is `runtime_base - 0x400000` (the ASLR offset).
30
+ #[cfg(target_os = "windows")]
31
+ pub unsafe fn classify_pointer(value: u32, delta: u32) -> Option<PointerInfo> {
32
+ if value == 0 || value < 0x10000 {
33
+ return None;
34
+ }
35
+
36
+ let ghidra_val = value.wrapping_sub(delta);
37
+
38
+ // .rdata → Vtable (likely vtable or function pointer table)
39
+ if ghidra_val >= va::RDATA_START && ghidra_val < va::DATA_START {
40
+ let detail = if can_read(value, 4) {
41
+ let vt0 = *(value as *const u32);
42
+ Some(format!("vt[0]=ghidra:0x{:08X}", vt0.wrapping_sub(delta)))
43
+ } else {
44
+ Some("(unreadable)".to_string())
45
+ };
46
+ return Some(PointerInfo {
47
+ offset: 0, // caller fills this in
48
+ raw_value: value,
49
+ ghidra_value: ghidra_val,
50
+ kind: PointerKind::Vtable,
51
+ detail,
52
+ });
53
+ }
54
+
55
+ // .text → Code
56
+ if ghidra_val >= va::TEXT_START && ghidra_val <= va::TEXT_END {
57
+ return Some(PointerInfo {
58
+ offset: 0,
59
+ raw_value: value,
60
+ ghidra_value: ghidra_val,
61
+ kind: PointerKind::Code,
62
+ detail: None,
63
+ });
64
+ }
65
+
66
+ // .data/.bss → Data
67
+ if ghidra_val >= va::DATA_START && ghidra_val < va::DATA_END {
68
+ return Some(PointerInfo {
69
+ offset: 0,
70
+ raw_value: value,
71
+ ghidra_value: ghidra_val,
72
+ kind: PointerKind::Data,
73
+ detail: None,
74
+ });
75
+ }
76
+
77
+ // Heap pointer checks
78
+ if can_read(value, 4) {
79
+ let first = *(value as *const u32);
80
+ let ghidra_first = first.wrapping_sub(delta);
81
+
82
+ // Object — heap pointer whose first DWORD is a vtable
83
+ if ghidra_first >= va::RDATA_START && ghidra_first < va::DATA_START {
84
+ let detail = if can_read(first, 4) {
85
+ let vt0 = *(first as *const u32);
86
+ Some(format!(
87
+ "vtable=ghidra:0x{:08X} vt[0]=ghidra:0x{:08X}",
88
+ ghidra_first,
89
+ vt0.wrapping_sub(delta)
90
+ ))
91
+ } else {
92
+ Some(format!("vtable=ghidra:0x{:08X} vt[0]=?", ghidra_first))
93
+ };
94
+ return Some(PointerInfo {
95
+ offset: 0,
96
+ raw_value: value,
97
+ ghidra_value: ghidra_val,
98
+ kind: PointerKind::Object,
99
+ detail,
100
+ });
101
+ }
102
+
103
+ // Generic heap pointer
104
+ return Some(PointerInfo {
105
+ offset: 0,
106
+ raw_value: value,
107
+ ghidra_value: ghidra_val,
108
+ kind: PointerKind::Heap,
109
+ detail: Some(format!("*=0x{:08X}", first)),
110
+ });
111
+ }
112
+
113
+ None
114
+ }
115
+
116
+ /// Classify all DWORD-aligned values in a byte slice.
117
+ ///
118
+ /// Returns a vec of `PointerInfo` with offsets filled in relative to `base_offset`.
119
+ #[cfg(target_os = "windows")]
120
+ pub unsafe fn classify_region(
121
+ data: &[u8],
122
+ base_offset: u32,
123
+ delta: u32,
124
+ ) -> Vec<PointerInfo> {
125
+ let mut pointers = Vec::new();
126
+ let dword_count = data.len() / 4;
127
+ for i in 0..dword_count {
128
+ let byte_offset = i * 4;
129
+ let value = u32::from_le_bytes([
130
+ data[byte_offset],
131
+ data[byte_offset + 1],
132
+ data[byte_offset + 2],
133
+ data[byte_offset + 3],
134
+ ]);
135
+ if let Some(mut info) = classify_pointer(value, delta) {
136
+ info.offset = base_offset + byte_offset as u32;
137
+ pointers.push(info);
138
+ }
139
+ }
140
+ pointers
141
+ }