[v0.1.0-beta.4] Added validity check on MEM1 of DolphinProcessMemory; fixed write memory API

This commit is contained in:
sup39 2023-07-26 00:44:24 +09:00
parent 444618a42d
commit 3a4ba11520
No known key found for this signature in database
GPG key ID: 14D2E0D21140D260
9 changed files with 116 additions and 43 deletions

View file

@ -1,4 +1,8 @@
# Changelog
## v0.1.0-beta.4 (2023/07/26)
- Added validity check on MEM1 of DolphinProcessMemory
- Fixed write memory API
## v0.1.0-beta.3 (2023/07/25)
- Added support for older Dolphin (e.g. Dolphin 5.0-114)
- Added support for MEM2

2
Cargo.lock generated
View file

@ -854,7 +854,7 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "sup-smsac"
version = "0.1.0-beta.3"
version = "0.1.0-beta.4"
dependencies = [
"clap",
"encoding_rs",

View file

@ -1,6 +1,6 @@
[package]
name = "sup-smsac"
version = "0.1.0-beta.3"
version = "0.1.0-beta.4"
edition = "2021"
license = "MIT OR Apache-2.0"
authors = ["sup39 <sms@sup39.dev>"]
@ -16,6 +16,7 @@ windows = {version = "0.48.0", features = [
"Win32_System_Diagnostics_Debug",
"Win32_System_Memory",
"Win32_System_Threading",
"Win32_System_ProcessStatus",
]}
futures-util = "0.3.28"
hyper = { version = "0.14.27", features = ["full"] }

View file

@ -15,38 +15,51 @@ use process_memory::DolphinProcessMemory;
pub trait Dolphin {
/// # Safety
/// `maddr + size` must be in bound
unsafe fn operate_memory_unchecked<T, F>(
unsafe fn read_memory_unchecked<T, F>(
&self, maddr: DolphinMemAddr, size: usize, operator: F,
) -> Option<T>
where F: FnOnce(*mut u8) -> T;
where F: FnOnce(*const u8) -> T;
/// # Safety
/// `maddr + payload.len()` must be in bound
unsafe fn write_memory_unchecked(&self, maddr: DolphinMemAddr, payload: &[u8]) -> Option<()>;
#[inline]
fn operate_memory<T, F>(&self, addr: Addr, size: usize, operator: F) -> Option<T>
where F: FnOnce(*mut u8) -> T
fn read_memory<T, F>(&self, addr: Addr, size: usize, operator: F) -> Option<T>
where F: FnOnce(*const u8) -> T
{
DolphinMemAddr::try_from(addr).ok().and_then(|maddr| {
if (maddr.space() as usize) < size {return None}
unsafe {self.operate_memory_unchecked(maddr, size, operator)}
unsafe {self.read_memory_unchecked(maddr, size, operator)}
})
}
#[inline]
fn operate_memory_truncated<T, F>(&self, addr: Addr, max_size: usize, operator: F) -> Option<T>
where F: FnOnce(*mut u8, usize) -> T
fn read_memory_truncated<T, F>(&self, addr: Addr, max_size: usize, operator: F) -> Option<T>
where F: FnOnce(*const u8, usize) -> T
{
DolphinMemAddr::try_from(addr).ok().and_then(|maddr| {
let size = std::cmp::min(maddr.space() as usize, max_size);
unsafe {self.operate_memory_unchecked(maddr, size, |ptr| operator(ptr, size))}
unsafe {self.read_memory_unchecked(maddr, size, |ptr| operator(ptr, size))}
})
}
#[inline]
fn write_bytes(&self, addr: Addr, payload: &[u8]) -> Option<()> {
let size = payload.len();
DolphinMemAddr::try_from(addr).ok().and_then(|maddr| {
if (maddr.space() as usize) < size {return None}
unsafe {self.write_memory_unchecked(maddr, payload)}
})
}
fn read<T: DecodeBE>(&self, addr: Addr) -> Option<T> {
let size = std::mem::size_of::<T>();
self.operate_memory(addr, size, |ptr| unsafe {T::decode_be(ptr)})
self.read_memory(addr, size, |ptr| unsafe {T::decode_be(ptr)})
}
fn read_str(&self, addr: Addr) -> Option<String> {
let maxlen = 256; // TODO
self.operate_memory_truncated(addr, maxlen, |ptr, maxlen| {
self.read_memory_truncated(addr, maxlen, |ptr, maxlen| {
let mut len = 0usize;
while len < maxlen && unsafe{*ptr.add(len)} != 0 {
len += 1;
@ -57,19 +70,12 @@ pub trait Dolphin {
}).unwrap_or(None)
}
fn dump_hex(&self, addr: Addr, size: usize) -> Option<String> {
self.operate_memory(addr, size, |ptr| {
self.read_memory(addr, size, |ptr| {
(0..size)
.map(|i| format!("{:02X}", unsafe {*ptr.add(i)}))
.collect()
})
}
fn write_bytes(&self, addr: Addr, payload: &[u8]) -> Option<()> {
let size = payload.len();
self.operate_memory(addr, size, |ptr| unsafe {
std::ptr::copy(payload.as_ptr(), ptr, size);
})
}
}
pub enum DolphinMemory {
@ -77,12 +83,18 @@ pub enum DolphinMemory {
ProcessMemory(DolphinProcessMemory),
}
impl Dolphin for DolphinMemory {
unsafe fn operate_memory_unchecked<T, F>(&self, maddr: DolphinMemAddr, size: usize, operator: F) -> Option<T>
where F: FnOnce(*mut u8) -> T
unsafe fn read_memory_unchecked<T, F>(&self, maddr: DolphinMemAddr, size: usize, operator: F) -> Option<T>
where F: FnOnce(*const u8) -> T
{
match self {
DolphinMemory::SharedMemory(m) => m.operate_memory_unchecked(maddr, size, operator),
DolphinMemory::ProcessMemory(m) => m.operate_memory_unchecked(maddr, size, operator),
DolphinMemory::SharedMemory(m) => m.read_memory_unchecked(maddr, size, operator),
DolphinMemory::ProcessMemory(m) => m.read_memory_unchecked(maddr, size, operator),
}
}
unsafe fn write_memory_unchecked(&self, maddr: DolphinMemAddr, payload: &[u8]) -> Option<()> {
match self {
DolphinMemory::SharedMemory(m) => m.write_memory_unchecked(maddr, payload),
DolphinMemory::ProcessMemory(m) => m.write_memory_unchecked(maddr, payload),
}
}
}

View file

@ -14,9 +14,10 @@ use crate::sys::process_memory::ProcessMemoryIterator;
use core::ffi::c_void;
use windows::Win32::Foundation::{HANDLE, CloseHandle};
use windows::Win32::System::{
Threading::{PROCESS_QUERY_INFORMATION, PROCESS_VM_READ, PROCESS_VM_WRITE, OpenProcess},
Threading::{PROCESS_QUERY_INFORMATION, PROCESS_VM_OPERATION, PROCESS_VM_READ, PROCESS_VM_WRITE, OpenProcess},
Memory::MEM_MAPPED,
Diagnostics::Debug::ReadProcessMemory,
Diagnostics::Debug::{ReadProcessMemory, WriteProcessMemory},
ProcessStatus::{PSAPI_WORKING_SET_EX_INFORMATION, QueryWorkingSetEx},
};
pub struct DolphinProcessMemory {
@ -32,8 +33,8 @@ impl Drop for DolphinProcessMemory {
}
}
impl Dolphin for DolphinProcessMemory {
unsafe fn operate_memory_unchecked<T, F>(&self, maddr: DolphinMemAddr, size: usize, operator: F) -> Option<T>
where F: FnOnce(*mut u8) -> T
unsafe fn read_memory_unchecked<T, F>(&self, maddr: DolphinMemAddr, size: usize, operator: F) -> Option<T>
where F: FnOnce(*const u8) -> T
{
match maddr {
DolphinMemAddr::MEM1(offset) => Some(self.base_addr_mem1 + (offset as usize)),
@ -50,6 +51,23 @@ impl Dolphin for DolphinProcessMemory {
}
})
}
unsafe fn write_memory_unchecked(&self, maddr: DolphinMemAddr, payload: &[u8]) -> Option<()> {
match maddr {
DolphinMemAddr::MEM1(offset) => Some(self.base_addr_mem1 + (offset as usize)),
DolphinMemAddr::MEM2(offset) => self.base_addr_mem2.map(|base_addr| base_addr + (offset as usize)),
}.and_then(|base_addr| {
let size = payload.len();
let ptr = payload.as_ptr();
match unsafe {WriteProcessMemory(
self.h_proc, base_addr as *const c_void,
ptr as *mut c_void, size, None,
).as_bool()} {
true => Some(()),
false => None,
}
})
}
}
pub enum DolphinProcessMemoryFindError {
@ -64,13 +82,26 @@ impl From<windows::core::Error> for DolphinProcessMemoryFindError {
impl DolphinProcessMemory {
pub fn open_pid(pid: PidType) -> Result<DolphinProcessMemory, DolphinProcessMemoryFindError> {
unsafe {
let h_proc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_VM_WRITE, false, pid)?;
let h_proc = OpenProcess(
PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE,
false, pid,
)?;
let mut itr = ProcessMemoryIterator::with_handle(h_proc);
// find MEM1
let Some(base_addr_mem1) =
itr.find(|meminfo| meminfo.RegionSize == 0x2000000 && meminfo.Type == MEM_MAPPED)
.map(|meminfo| meminfo.BaseAddress)
itr.find(|meminfo| meminfo.RegionSize == 0x2000000 && meminfo.Type == MEM_MAPPED && {
let mut wsinfo = PSAPI_WORKING_SET_EX_INFORMATION {
VirtualAddress: meminfo.BaseAddress,
..PSAPI_WORKING_SET_EX_INFORMATION::default()
};
QueryWorkingSetEx(
h_proc,
&mut wsinfo as *mut PSAPI_WORKING_SET_EX_INFORMATION as *mut c_void,
std::mem::size_of::<PSAPI_WORKING_SET_EX_INFORMATION>() as u32
).as_bool()
&& wsinfo.VirtualAttributes.Flags & 1 != 0
}).map(|meminfo| meminfo.BaseAddress)
else {
CloseHandle(h_proc);
return Err(DolphinProcessMemoryFindError::MemoryNotFound);
@ -83,6 +114,7 @@ impl DolphinProcessMemory {
meminfo.BaseAddress == base_addr_mem2_check
&& meminfo.RegionSize == 0x4000000
&& meminfo.Type == MEM_MAPPED
// TODO check valid
}).map(|_| base_addr_mem2_check as usize);
Ok(DolphinProcessMemory {

View file

@ -12,10 +12,8 @@ pub struct DolphinSharedMemory {
pub const MEM2_OFFSET: u32 = 0x4040000;
impl Dolphin for DolphinSharedMemory {
/// # Safety
/// `maddr + size` must be in bound
unsafe fn operate_memory_unchecked<T, F>(&self, maddr: DolphinMemAddr, _size: usize, operator: F) -> Option<T>
where F: FnOnce(*mut u8) -> T
unsafe fn read_memory_unchecked<T, F>(&self, maddr: DolphinMemAddr, _size: usize, operator: F) -> Option<T>
where F: FnOnce(*const u8) -> T
{
match maddr {
DolphinMemAddr::MEM1(offset) => Some(offset),
@ -27,6 +25,22 @@ impl Dolphin for DolphinSharedMemory {
operator(self.shared_memory.get_ptr().add(offset as usize))
})
}
unsafe fn write_memory_unchecked(&self, maddr: DolphinMemAddr, payload: &[u8]) -> Option<()> {
match maddr {
DolphinMemAddr::MEM1(offset) => Some(offset),
DolphinMemAddr::MEM2(offset) => match self.has_mem2 {
true => Some(MEM2_OFFSET + offset),
false => None,
},
}.map(|offset| {
std::ptr::copy(
payload.as_ptr(),
self.shared_memory.get_ptr().add(offset as usize),
payload.len(),
);
})
}
}
impl DolphinSharedMemory {

View file

@ -20,10 +20,13 @@ pub struct SMSDolphin {
ver: SMSVersion,
}
impl Dolphin for SMSDolphin {
unsafe fn operate_memory_unchecked<T, F>(&self, maddr: DolphinMemAddr, size: usize, operator: F) -> Option<T>
where F: FnOnce(*mut u8) -> T
unsafe fn read_memory_unchecked<T, F>(&self, maddr: DolphinMemAddr, size: usize, operator: F) -> Option<T>
where F: FnOnce(*const u8) -> T
{
self.d.operate_memory_unchecked(maddr, size, operator)
self.d.read_memory_unchecked(maddr, size, operator)
}
unsafe fn write_memory_unchecked(&self, maddr: DolphinMemAddr, payload: &[u8]) -> Option<()> {
self.d.write_memory_unchecked(maddr, payload)
}
}
@ -81,10 +84,17 @@ impl SMSDolphin {
for (pid, d) in DolphinMemory::list() {
match d {
Some(d) => {
if let Ok(o) = SMSDolphin::from_dolphin_memory(d, pid) {
return Ok(o)
}
match SMSDolphin::from_dolphin_memory(d, pid) {
Ok(o) => return Ok(o),
Err(e) => {
game_running = true;
match e {
Some(e) => eprintln!("Unknown game (pid: {pid}): {}",
e.map(|c| format!("{c:02X}")).join("")),
None => eprintln!("Unknown game (pid: {pid}): fail to get version"),
}
}
}
},
None => dolphin_running = true,
}

View file

@ -4687,7 +4687,7 @@ limitations under the License.
<h4>Used by:</h4>
<ul class="license-used-by">
<li><a href=" https://github.com/serde-rs/serde ">serde_derive 1.0.174</a></li>
<li><a href=" https://github.com/sup39/sup-smsac ">sup-smsac 0.1.0-beta.3</a></li>
<li><a href=" https://github.com/sup39/sup-smsac ">sup-smsac 0.1.0-beta.4</a></li>
<li><a href=" https://github.com/sup39/sup-smsac ">sup-smsac-derive 0.1.0-beta.1</a></li>
<li><a href=" https://github.com/retep998/winapi-rs ">winapi-i686-pc-windows-gnu 0.4.0</a></li>
<li><a href=" https://github.com/retep998/winapi-rs ">winapi-x86_64-pc-windows-gnu 0.4.0</a></li>

View file

@ -8,7 +8,7 @@
<script src="index.js"></script>
</head>
<body>
<h1>SMS Web Object Viewer (v0.1.0-beta.3) (2023/07/25)</h1>
<h1>SMS Web Object Viewer (v0.1.0-beta.4) (2023/07/26)</h1>
<header>
<div id="msg"></div>
<details>