-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathmemory_ops.rs
More file actions
139 lines (127 loc) · 4.08 KB
/
Copy pathmemory_ops.rs
File metadata and controls
139 lines (127 loc) · 4.08 KB
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
// Copyright (c) 2024-2026 Elias Bachaalany
// SPDX-License-Identifier: LicenseRef-Human-Origin-Source-1.0
//
// This file is licensed under the Human-Origin Source License v1.0.
// See LICENSE.
//
// memory_ops: Memory block inspection, byte reads, writes, and batch patching.
//
// Usage: memory_ops [host_url]
use libghidra as ghidra;
fn main() {
let args: Vec<String> = std::env::args().collect();
let url = args.get(1).map_or("http://127.0.0.1:18080", |s| s.as_str());
let client = ghidra::connect(url);
// 1. List memory blocks
let blocks = match client.list_memory_blocks(50, 0) {
Ok(b) => b,
Err(e) => {
eprintln!("ListMemoryBlocks failed (is a program open?): {e}");
std::process::exit(1);
}
};
println!("Memory blocks ({}):", blocks.blocks.len());
for blk in &blocks.blocks {
println!(
" {:<16} 0x{:08x}..0x{:08x} size={:<8} R={} W={} X={}",
blk.name,
blk.start_address,
blk.end_address,
blk.size,
blk.is_read as u8,
blk.is_write as u8,
blk.is_execute as u8,
);
}
// 2. Read the first 64 bytes from the first block
let first = match blocks.blocks.first() {
Some(b) => b,
None => {
eprintln!("No memory blocks found.");
std::process::exit(1);
}
};
let read_len: u32 = std::cmp::min(64, first.size as u32);
let read = client
.read_bytes(first.start_address, read_len)
.unwrap_or_else(|e| {
eprintln!("ReadBytes failed: {e}");
std::process::exit(1);
});
println!(
"\nFirst {} bytes of '{}' at 0x{:08x}:",
read.data.len(),
first.name,
first.start_address
);
hex_dump(&read.data, first.start_address);
// 3. Write a small marker (4 bytes) at the block start
let marker: Vec<u8> = vec![0xDE, 0xAD, 0xBE, 0xEF];
let write = client
.write_bytes(first.start_address, &marker)
.unwrap_or_else(|e| {
eprintln!("WriteBytes failed: {e}");
std::process::exit(1);
});
println!(
"\nWriteBytes: {} bytes written at 0x{:08x}",
write.bytes_written, first.start_address
);
// 4. Verify the write by reading back
let verify = client.read_bytes(first.start_address, 4).unwrap();
println!("Read-back after write:");
hex_dump(&verify.data, first.start_address);
// 5. Batch-patch two locations: start+8 and start+16
let patches = vec![
ghidra::BytePatch {
address: first.start_address + 8,
data: vec![0xCA, 0xFE],
},
ghidra::BytePatch {
address: first.start_address + 16,
data: vec![0xBA, 0xBE],
},
];
let batch = client.patch_bytes_batch(&patches).unwrap_or_else(|e| {
eprintln!("PatchBytesBatch failed: {e}");
std::process::exit(1);
});
println!(
"\nPatchBytesBatch: {} patches, {} bytes written",
batch.patch_count, batch.bytes_written
);
// 6. Final read to see all modifications
let final_read = client.read_bytes(first.start_address, 32).unwrap();
println!("\nFinal state (first 32 bytes):");
hex_dump(&final_read.data, first.start_address);
}
/// Print a classic hex dump: offset, hex bytes, ASCII.
fn hex_dump(data: &[u8], base: u64) {
for (i, chunk) in data.chunks(16).enumerate() {
let addr = base + (i * 16) as u64;
print!(" {:08x} ", addr);
for (j, &b) in chunk.iter().enumerate() {
if j == 8 {
print!(" ");
}
print!("{:02x} ", b);
}
// Pad if less than 16 bytes
for j in chunk.len()..16 {
if j == 8 {
print!(" ");
}
print!(" ");
}
print!(" |");
for &b in chunk {
let c = if b.is_ascii_graphic() || b == b' ' {
b as char
} else {
'.'
};
print!("{c}");
}
println!("|");
}
}