File size: 6,040 Bytes
9d55850 5f22f7f 9d55850 1d3de83 05dc1e9 1d3de83 7795724 1d3de83 7795724 1d3de83 2c6f096 5f22f7f 1d3de83 5f22f7f 1d3de83 5f22f7f 1d3de83 05dc1e9 9d55850 1d3de83 9d55850 05dc1e9 7795724 1d3de83 7795724 1d3de83 7795724 1d3de83 2c6f096 1d3de83 5f22f7f 1d3de83 5f22f7f 1d3de83 9d55850 1d3de83 9d55850 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 | use serde::{Deserialize, Serialize};
use std::io::{self, Read, Write};
// --- Pointer classification ---
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum PointerKind {
Vtable,
Code,
Data,
Object,
Heap,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PointerInfo {
pub offset: u32,
pub raw_value: u32,
pub ghidra_value: u32,
pub kind: PointerKind,
pub detail: Option<String>,
}
// --- Typed inspection types ---
/// A formatted field value from a struct inspection.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FieldValue {
pub offset: u32,
pub name: String,
pub size: u32,
/// Raw hex representation.
pub hex: String,
/// Human-readable formatted value.
pub display: String,
}
/// A tracked live object in the DLL.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LiveObjectInfo {
pub runtime_addr: u32,
pub ghidra_addr: u32,
pub size: u32,
pub class_name: String,
pub field_count: u32,
}
/// Result of resolving a named alias (e.g., "ddgame") to an address.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResolvedAlias {
pub runtime_addr: u32,
pub class_name: String,
}
/// Result of resolving a field name to its offset.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResolvedField {
pub offset: u32,
pub size: u32,
}
// --- Protocol messages ---
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Request {
Ping,
Help,
Read {
addr: u32,
len: u32,
#[serde(default)]
absolute: bool,
},
/// Walk a pointer chain: start at `addr`, then for each offset in `chain`,
/// deref the current DWORD and add the offset. Read `len` bytes at the end.
ReadChain {
addr: u32,
chain: Vec<u32>,
len: u32,
absolute: bool,
},
/// Pause the game at the next frame boundary.
Suspend,
/// Resume the game.
Resume,
/// Advance `count` frames, then pause.
Step {
count: i32,
},
/// Query current frame number and pause state.
Frame,
/// Set a frame breakpoint (-1 to clear).
Break {
frame: i32,
},
/// Capture a canonicalized game state snapshot.
Snapshot,
/// Typed struct inspection: read all named fields at an address.
Inspect {
class_name: String,
addr: u32,
chain: Vec<u32>,
absolute: bool,
},
/// List all tracked live objects.
ListObjects,
/// Resolve a named alias (e.g., "ddgame") to a runtime address.
ResolveAlias {
name: String,
},
/// Resolve a field name to its offset within a struct.
ResolveField {
class_name: String,
field_name: String,
},
}
/// One step in a resolved pointer chain, for display.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChainStep {
/// Address we read from
pub deref_addr: u32,
/// Value (DWORD) we read
pub value: u32,
/// Offset added after deref
pub offset: u32,
/// Resulting address (value + offset)
pub result_addr: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CommandHelp {
pub name: String,
pub usage: String,
pub description: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Response {
Pong,
Help {
commands: Vec<CommandHelp>,
},
ReadResult {
ghidra_addr: u32,
runtime_addr: u32,
data: Vec<u8>,
pointers: Vec<PointerInfo>,
},
ReadChainResult {
/// Each deref step in the chain (for trace output)
steps: Vec<ChainStep>,
/// Final address (Ghidra VA)
ghidra_addr: u32,
/// Final address (runtime)
runtime_addr: u32,
/// Memory at the final address
data: Vec<u8>,
/// Pointer annotations in the final data
pointers: Vec<PointerInfo>,
},
/// Game is now suspended at this frame.
Suspended {
frame: i32,
},
/// Game resumed.
Resumed,
/// Current frame info.
FrameInfo {
frame: i32,
paused: bool,
breakpoint: i32,
},
/// Breakpoint set/cleared.
BreakSet {
frame: i32,
},
/// Game state snapshot.
Snapshot {
frame: i32,
text: String,
},
/// Typed struct inspection result.
InspectResult {
class_name: String,
ghidra_addr: u32,
runtime_addr: u32,
fields: Vec<FieldValue>,
},
/// List of tracked live objects.
ObjectList {
objects: Vec<LiveObjectInfo>,
},
/// Resolved named alias.
AliasResult(ResolvedAlias),
/// Resolved field offset.
FieldResult(ResolvedField),
Error {
message: String,
},
}
// --- Length-prefixed framing ---
pub const DEFAULT_PORT: u16 = 19840;
pub const MAX_READ_SIZE: u32 = 1024 * 1024; // 1 MB
pub const MAX_FRAME_SIZE: usize = 8 * 1024 * 1024; // 8 MB (read data + pointer metadata overhead)
/// Write a length-prefixed MessagePack frame.
pub fn write_frame<W: Write, T: Serialize>(writer: &mut W, msg: &T) -> io::Result<()> {
let payload =
rmp_serde::to_vec(msg).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
let len = payload.len() as u32;
writer.write_all(&len.to_le_bytes())?;
writer.write_all(&payload)?;
writer.flush()
}
/// Read a length-prefixed MessagePack frame.
pub fn read_frame<R: Read, T: for<'de> Deserialize<'de>>(reader: &mut R) -> io::Result<T> {
let mut len_buf = [0u8; 4];
reader.read_exact(&mut len_buf)?;
let len = u32::from_le_bytes(len_buf) as usize;
if len > MAX_FRAME_SIZE {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"frame too large",
));
}
let mut payload = vec![0u8; len];
reader.read_exact(&mut payload)?;
rmp_serde::from_slice(&payload).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
}
|