feat: add openwa-debug-proto crate with wire protocol types
Browse filesCo-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
crates/openwa-debug-proto/Cargo.toml
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[package]
|
| 2 |
+
name = "openwa-debug-proto"
|
| 3 |
+
version = "0.1.0"
|
| 4 |
+
edition = "2021"
|
| 5 |
+
description = "Wire protocol types for OpenWA debug server"
|
| 6 |
+
|
| 7 |
+
[dependencies]
|
| 8 |
+
serde = { version = "1", features = ["derive"] }
|
| 9 |
+
rmp-serde = "1"
|
crates/openwa-debug-proto/src/lib.rs
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
use serde::{Deserialize, Serialize};
|
| 2 |
+
use std::io::{self, Read, Write};
|
| 3 |
+
|
| 4 |
+
// --- Pointer classification ---
|
| 5 |
+
|
| 6 |
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
| 7 |
+
pub enum PointerKind {
|
| 8 |
+
Vtable,
|
| 9 |
+
Code,
|
| 10 |
+
Data,
|
| 11 |
+
Object,
|
| 12 |
+
Heap,
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
| 16 |
+
pub struct PointerInfo {
|
| 17 |
+
pub offset: u32,
|
| 18 |
+
pub raw_value: u32,
|
| 19 |
+
pub ghidra_value: u32,
|
| 20 |
+
pub kind: PointerKind,
|
| 21 |
+
pub detail: Option<String>,
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
// --- Protocol messages ---
|
| 25 |
+
|
| 26 |
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
| 27 |
+
pub enum Request {
|
| 28 |
+
Ping,
|
| 29 |
+
Help,
|
| 30 |
+
Read { addr: u32, len: u32 },
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
| 34 |
+
pub struct CommandHelp {
|
| 35 |
+
pub name: String,
|
| 36 |
+
pub usage: String,
|
| 37 |
+
pub description: String,
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
| 41 |
+
pub enum Response {
|
| 42 |
+
Pong,
|
| 43 |
+
Help { commands: Vec<CommandHelp> },
|
| 44 |
+
ReadResult {
|
| 45 |
+
ghidra_addr: u32,
|
| 46 |
+
runtime_addr: u32,
|
| 47 |
+
data: Vec<u8>,
|
| 48 |
+
pointers: Vec<PointerInfo>,
|
| 49 |
+
},
|
| 50 |
+
Error { message: String },
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
// --- Length-prefixed framing ---
|
| 54 |
+
|
| 55 |
+
pub const DEFAULT_PORT: u16 = 19840;
|
| 56 |
+
pub const MAX_READ_SIZE: u32 = 1024 * 1024; // 1 MB
|
| 57 |
+
pub const MAX_FRAME_SIZE: usize = 8 * 1024 * 1024; // 8 MB (read data + pointer metadata overhead)
|
| 58 |
+
|
| 59 |
+
/// Write a length-prefixed MessagePack frame.
|
| 60 |
+
pub fn write_frame<W: Write, T: Serialize>(writer: &mut W, msg: &T) -> io::Result<()> {
|
| 61 |
+
let payload =
|
| 62 |
+
rmp_serde::to_vec(msg).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
| 63 |
+
let len = payload.len() as u32;
|
| 64 |
+
writer.write_all(&len.to_le_bytes())?;
|
| 65 |
+
writer.write_all(&payload)?;
|
| 66 |
+
writer.flush()
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
/// Read a length-prefixed MessagePack frame.
|
| 70 |
+
pub fn read_frame<R: Read, T: for<'de> Deserialize<'de>>(reader: &mut R) -> io::Result<T> {
|
| 71 |
+
let mut len_buf = [0u8; 4];
|
| 72 |
+
reader.read_exact(&mut len_buf)?;
|
| 73 |
+
let len = u32::from_le_bytes(len_buf) as usize;
|
| 74 |
+
if len > MAX_FRAME_SIZE {
|
| 75 |
+
return Err(io::Error::new(
|
| 76 |
+
io::ErrorKind::InvalidData,
|
| 77 |
+
"frame too large",
|
| 78 |
+
));
|
| 79 |
+
}
|
| 80 |
+
let mut payload = vec![0u8; len];
|
| 81 |
+
reader.read_exact(&mut payload)?;
|
| 82 |
+
rmp_serde::from_slice(&payload)
|
| 83 |
+
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
|
| 84 |
+
}
|