panda/api/
mem.rs

1use crate::enums::MemRWStatus;
2use crate::prelude::*;
3use crate::GuestType;
4use crate::{sys, Error};
5use crate::{GuestReadFail, GuestWriteFail};
6
7use std::ffi::CString;
8use std::os::raw::c_char;
9
10// Public API ----------------------------------------------------------------------------------------------------------
11
12/// Read a structure or value from guest memory using the guest endianess and
13/// layout for the given type.
14///
15/// ## Example
16///
17/// ```
18/// use panda::mem::read_guest_type;
19/// use panda::prelude::*;
20///
21/// # let cpu: &mut CPUState = todo!();
22/// let pid: u32 = read_guest_type(cpu, 0x55550000).unwrap();
23/// ```
24///
25/// To use custom structures and types, derive the [`GuestType`] trait for your
26/// given structure.
27pub fn read_guest_type<T: GuestType>(
28    cpu: &mut CPUState,
29    addr: target_ptr_t,
30) -> Result<T, GuestReadFail> {
31    T::read_from_guest(cpu, addr)
32}
33
34/// Write a given type to guest memory using the guest endianess and layout for the
35/// given type.
36///
37/// ## Example
38///
39/// ```
40/// use panda::mem::write_guest_type;
41/// use panda::prelude::*;
42///
43/// # let cpu: &mut CPUState = todo!();
44/// let pid = 1234_u32;
45///
46/// write_guest_type(cpu, 0x55550000, pid);
47/// ```
48pub fn write_guest_type<T: GuestType>(
49    cpu: &mut CPUState,
50    addr: target_ptr_t,
51    val: &T,
52) -> Result<(), GuestWriteFail> {
53    val.write_to_guest(cpu, addr)
54}
55
56/// Read a structure or value from physical guest memory using the guest endianess and layout
57/// for the given type.
58///
59/// ## Example
60///
61/// ```
62/// use panda::mem::read_guest_type_phys;
63/// use panda::prelude::*;
64///
65/// # let cpu: &mut CPUState = todo!();
66/// let ptr = 0xF8000010;
67/// let pid: u32 = read_guest_type_phys(ptr).unwrap();
68/// ```
69pub fn read_guest_type_phys<T: GuestType>(addr: target_ptr_t) -> Result<T, GuestReadFail> {
70    T::read_from_guest_phys(addr)
71}
72
73/// Write a given type to guest physical memory using the guest endianess and layout for the
74/// given type.
75///
76/// ## Example
77///
78/// ```
79/// use panda::mem::write_guest_type_phys;
80/// use panda::prelude::*;
81///
82/// # let cpu: &mut CPUState = todo!();
83/// let pid = 1234_u32;
84///
85/// write_guest_type_phys(0xF8000010, pid);
86/// ```
87pub fn write_guest_type_phys<T: GuestType>(
88    addr: target_ptr_t,
89    val: &T,
90) -> Result<(), GuestWriteFail> {
91    val.write_to_guest_phys(addr)
92}
93
94/// Read from guest virtual memory
95pub fn virtual_memory_read(
96    cpu: &mut CPUState,
97    addr: target_ulong,
98    len: usize,
99) -> Result<Vec<u8>, MemRWStatus> {
100    let mut buf: Vec<c_char> = Vec::with_capacity(len);
101
102    unsafe {
103        let res =
104            panda_sys::panda_virtual_memory_read_external(cpu, addr, buf.as_mut_ptr(), len as i32)
105                .into();
106
107        match res {
108            MemRWStatus::MemTxOk => {
109                buf.set_len(len);
110                Ok(vec_i8_into_u8(buf))
111            }
112            _ => Err(res),
113        }
114    }
115}
116
117/// Read from guest virtual memory into a buffer
118pub fn virtual_memory_read_into(
119    cpu: &mut CPUState,
120    addr: target_ulong,
121    buf: &mut [u8],
122) -> Result<(), MemRWStatus> {
123    let res = unsafe {
124        panda_sys::panda_virtual_memory_read_external(
125            cpu,
126            addr,
127            buf.as_mut_ptr() as _,
128            buf.len() as i32,
129        )
130        .into()
131    };
132
133    match res {
134        MemRWStatus::MemTxOk => Ok(()),
135        _ => Err(res),
136    }
137}
138
139/// Read from guest physical memory
140pub fn physical_memory_read(addr: target_ulong, len: usize) -> Result<Vec<u8>, MemRWStatus> {
141    let mut buf: Vec<u8> = Vec::with_capacity(len);
142
143    unsafe {
144        let res = panda_sys::panda_physical_memory_read_external(
145            addr as u64,
146            buf.as_mut_ptr(),
147            len as i32,
148        )
149        .into();
150
151        match res {
152            MemRWStatus::MemTxOk => {
153                buf.set_len(len);
154                Ok(buf)
155            }
156            _ => Err(res),
157        }
158    }
159}
160
161/// Read from guest physical memory into a pre-allocated buffer
162pub fn physical_memory_read_into(addr: target_ulong, buf: &mut [u8]) -> Result<(), MemRWStatus> {
163    let res = unsafe {
164        panda_sys::panda_physical_memory_read_external(
165            addr as u64,
166            buf.as_mut_ptr() as _,
167            buf.len() as i32,
168        )
169        .into()
170    };
171
172    match res {
173        MemRWStatus::MemTxOk => Ok(()),
174        _ => Err(res),
175    }
176}
177
178/// Write to guest virtual memory
179pub fn virtual_memory_write(cpu: &mut CPUState, addr: target_ulong, data: &[u8]) -> MemRWStatus {
180    let mut c_data = data.to_vec(); // Alloc b/c C API wants mut
181    unsafe {
182        panda_sys::panda_virtual_memory_write_external(
183            cpu,
184            addr,
185            c_data.as_mut_ptr() as *mut c_char,
186            c_data.len() as i32,
187        )
188        .into()
189    }
190}
191
192/// Write to guest physical memory
193pub fn physical_memory_write(addr: target_ulong, data: &[u8]) -> MemRWStatus {
194    let mut c_data = data.to_vec(); // Alloc b/c C API wants mut
195    unsafe {
196        panda_sys::panda_physical_memory_write_external(
197            addr as _,
198            c_data.as_mut_ptr(),
199            c_data.len() as i32,
200        )
201        .into()
202    }
203}
204
205/// Translate guest virtual address to physical address, returning `None` if no mapping
206/// can be found.
207pub fn virt_to_phys(cpu: &mut CPUState, addr: target_ulong) -> Option<target_ulong> {
208    match unsafe { panda_sys::panda_virt_to_phys_external(cpu, addr) } {
209        target_ulong::MAX => None,
210        phys_addr => Some(phys_addr),
211    }
212}
213
214pub const PAGE_SIZE: target_ulong = 1024;
215
216/// Map RAM into the system at a given physical address
217pub fn map_memory(name: &str, size: target_ulong, addr: target_ptr_t) -> Result<(), Error> {
218    let name = CString::new(name)?;
219
220    if size % PAGE_SIZE != 0 {
221        Err(Error::UnalignedPageSize)
222    } else {
223        unsafe {
224            sys::map_memory(name.as_ptr() as _, size as _, addr as _);
225        }
226
227        drop(name);
228
229        Ok(())
230    }
231}
232
233const IS_32_BIT: bool = std::mem::size_of::<target_ptr_t>() == 4;
234const TARGET_BITS: usize = std::mem::size_of::<target_ptr_t>() * 8;
235
236fn hex_addr(addr: target_ptr_t) -> impl std::fmt::Display {
237    if IS_32_BIT {
238        format!("{:08x}", addr)
239    } else {
240        format!("{:016x}", addr)
241    }
242}
243
244pub fn virt_memory_dump(cpu: &mut CPUState, addr: target_ptr_t, len: usize) {
245    let memory = virtual_memory_read(cpu, addr, len).unwrap();
246
247    let start_addr_aligned = addr & !0xf;
248    let end_addr_aligned = (addr + (len as target_ptr_t)) & !0xf;
249
250    let bytes_offset = addr - start_addr_aligned;
251
252    let hex_dump = (start_addr_aligned..=end_addr_aligned)
253        .step_by(0x10)
254        .enumerate()
255        .map(|(line_num, line_addr)| {
256            let hex_data = (0..0x10)
257                .map(|offset_in_line| {
258                    if line_num == 0 && offset_in_line < (bytes_offset as usize) {
259                        "  ".into()
260                    } else {
261                        let byte_index =
262                            ((line_num * 0x10) + offset_in_line) - (bytes_offset as usize);
263
264                        if let Some(byte) = memory.get(byte_index) {
265                            format!("{:02x}", byte).into()
266                        } else {
267                            "  ".into()
268                        }
269                    }
270                })
271                .collect::<Vec<std::borrow::Cow<'static, str>>>()
272                .join(" ");
273
274            format!("{}║{}\n", hex_addr(line_addr), hex_data)
275        })
276        .collect::<String>();
277
278    println!(
279        "{} 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f",
280        " ".repeat(TARGET_BITS / 4)
281    );
282    println!(
283        "{}╦═══════════════════════════════════════════════",
284        "═".repeat(TARGET_BITS / 4)
285    );
286    println!("{}", hex_dump);
287}
288
289// Private API ---------------------------------------------------------------------------------------------------------
290
291// https://stackoverflow.com/questions/59707349/cast-vector-of-i8-to-vector-of-u8-in-rust/59707887#59707887
292// TODO: replace with https://doc.rust-lang.org/std/vec/struct.Vec.html#method.into_raw_parts, once on stable
293fn vec_i8_into_u8(v: Vec<c_char>) -> Vec<u8> {
294    // Make sure v's destructor doesn't free the data it thinks it owns when it goes out of scope
295    let mut v = std::mem::ManuallyDrop::new(v);
296
297    // Pick apart the existing Vec
298    let p = v.as_mut_ptr();
299    let len = v.len();
300    let cap = v.capacity();
301
302    // Adopt the data into a new Vec
303    unsafe { Vec::from_raw_parts(p as *mut u8, len, cap) }
304}