[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 # 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) ## v0.1.0-beta.3 (2023/07/25)
- Added support for older Dolphin (e.g. Dolphin 5.0-114) - Added support for older Dolphin (e.g. Dolphin 5.0-114)
- Added support for MEM2 - Added support for MEM2

2
Cargo.lock generated
View file

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

View file

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

View file

@ -15,38 +15,51 @@ use process_memory::DolphinProcessMemory;
pub trait Dolphin { pub trait Dolphin {
/// # Safety /// # Safety
/// `maddr + size` must be in bound /// `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, &self, maddr: DolphinMemAddr, size: usize, operator: F,
) -> Option<T> ) -> 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] #[inline]
fn operate_memory<T, F>(&self, addr: Addr, size: usize, operator: F) -> Option<T> fn read_memory<T, F>(&self, addr: Addr, size: usize, operator: F) -> Option<T>
where F: FnOnce(*mut u8) -> T where F: FnOnce(*const u8) -> T
{ {
DolphinMemAddr::try_from(addr).ok().and_then(|maddr| { DolphinMemAddr::try_from(addr).ok().and_then(|maddr| {
if (maddr.space() as usize) < size {return None} 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] #[inline]
fn operate_memory_truncated<T, F>(&self, addr: Addr, max_size: usize, operator: F) -> Option<T> fn read_memory_truncated<T, F>(&self, addr: Addr, max_size: usize, operator: F) -> Option<T>
where F: FnOnce(*mut u8, usize) -> T where F: FnOnce(*const u8, usize) -> T
{ {
DolphinMemAddr::try_from(addr).ok().and_then(|maddr| { DolphinMemAddr::try_from(addr).ok().and_then(|maddr| {
let size = std::cmp::min(maddr.space() as usize, max_size); 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> { fn read<T: DecodeBE>(&self, addr: Addr) -> Option<T> {
let size = std::mem::size_of::<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> { fn read_str(&self, addr: Addr) -> Option<String> {
let maxlen = 256; // TODO let maxlen = 256; // TODO
self.operate_memory_truncated(addr, maxlen, |ptr, maxlen| { self.read_memory_truncated(addr, maxlen, |ptr, maxlen| {
let mut len = 0usize; let mut len = 0usize;
while len < maxlen && unsafe{*ptr.add(len)} != 0 { while len < maxlen && unsafe{*ptr.add(len)} != 0 {
len += 1; len += 1;
@ -57,19 +70,12 @@ pub trait Dolphin {
}).unwrap_or(None) }).unwrap_or(None)
} }
fn dump_hex(&self, addr: Addr, size: usize) -> Option<String> { fn dump_hex(&self, addr: Addr, size: usize) -> Option<String> {
self.operate_memory(addr, size, |ptr| { self.read_memory(addr, size, |ptr| {
(0..size) (0..size)
.map(|i| format!("{:02X}", unsafe {*ptr.add(i)})) .map(|i| format!("{:02X}", unsafe {*ptr.add(i)}))
.collect() .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 { pub enum DolphinMemory {
@ -77,12 +83,18 @@ pub enum DolphinMemory {
ProcessMemory(DolphinProcessMemory), ProcessMemory(DolphinProcessMemory),
} }
impl Dolphin for DolphinMemory { impl Dolphin for DolphinMemory {
unsafe fn operate_memory_unchecked<T, F>(&self, maddr: DolphinMemAddr, size: usize, operator: F) -> Option<T> 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
{ {
match self { match self {
DolphinMemory::SharedMemory(m) => m.operate_memory_unchecked(maddr, size, operator), DolphinMemory::SharedMemory(m) => m.read_memory_unchecked(maddr, size, operator),
DolphinMemory::ProcessMemory(m) => m.operate_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 core::ffi::c_void;
use windows::Win32::Foundation::{HANDLE, CloseHandle}; use windows::Win32::Foundation::{HANDLE, CloseHandle};
use windows::Win32::System::{ 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, Memory::MEM_MAPPED,
Diagnostics::Debug::ReadProcessMemory, Diagnostics::Debug::{ReadProcessMemory, WriteProcessMemory},
ProcessStatus::{PSAPI_WORKING_SET_EX_INFORMATION, QueryWorkingSetEx},
}; };
pub struct DolphinProcessMemory { pub struct DolphinProcessMemory {
@ -32,8 +33,8 @@ impl Drop for DolphinProcessMemory {
} }
} }
impl Dolphin for DolphinProcessMemory { impl Dolphin for DolphinProcessMemory {
unsafe fn operate_memory_unchecked<T, F>(&self, maddr: DolphinMemAddr, size: usize, operator: F) -> Option<T> 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
{ {
match maddr { match maddr {
DolphinMemAddr::MEM1(offset) => Some(self.base_addr_mem1 + (offset as usize)), 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 { pub enum DolphinProcessMemoryFindError {
@ -64,13 +82,26 @@ impl From<windows::core::Error> for DolphinProcessMemoryFindError {
impl DolphinProcessMemory { impl DolphinProcessMemory {
pub fn open_pid(pid: PidType) -> Result<DolphinProcessMemory, DolphinProcessMemoryFindError> { pub fn open_pid(pid: PidType) -> Result<DolphinProcessMemory, DolphinProcessMemoryFindError> {
unsafe { 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); let mut itr = ProcessMemoryIterator::with_handle(h_proc);
// find MEM1 // find MEM1
let Some(base_addr_mem1) = let Some(base_addr_mem1) =
itr.find(|meminfo| meminfo.RegionSize == 0x2000000 && meminfo.Type == MEM_MAPPED) itr.find(|meminfo| meminfo.RegionSize == 0x2000000 && meminfo.Type == MEM_MAPPED && {
.map(|meminfo| meminfo.BaseAddress) 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 { else {
CloseHandle(h_proc); CloseHandle(h_proc);
return Err(DolphinProcessMemoryFindError::MemoryNotFound); return Err(DolphinProcessMemoryFindError::MemoryNotFound);
@ -83,6 +114,7 @@ impl DolphinProcessMemory {
meminfo.BaseAddress == base_addr_mem2_check meminfo.BaseAddress == base_addr_mem2_check
&& meminfo.RegionSize == 0x4000000 && meminfo.RegionSize == 0x4000000
&& meminfo.Type == MEM_MAPPED && meminfo.Type == MEM_MAPPED
// TODO check valid
}).map(|_| base_addr_mem2_check as usize); }).map(|_| base_addr_mem2_check as usize);
Ok(DolphinProcessMemory { Ok(DolphinProcessMemory {

View file

@ -12,10 +12,8 @@ pub struct DolphinSharedMemory {
pub const MEM2_OFFSET: u32 = 0x4040000; pub const MEM2_OFFSET: u32 = 0x4040000;
impl Dolphin for DolphinSharedMemory { impl Dolphin for DolphinSharedMemory {
/// # Safety unsafe fn read_memory_unchecked<T, F>(&self, maddr: DolphinMemAddr, _size: usize, operator: F) -> Option<T>
/// `maddr + size` must be in bound where F: FnOnce(*const u8) -> T
unsafe fn operate_memory_unchecked<T, F>(&self, maddr: DolphinMemAddr, _size: usize, operator: F) -> Option<T>
where F: FnOnce(*mut u8) -> T
{ {
match maddr { match maddr {
DolphinMemAddr::MEM1(offset) => Some(offset), DolphinMemAddr::MEM1(offset) => Some(offset),
@ -27,6 +25,22 @@ impl Dolphin for DolphinSharedMemory {
operator(self.shared_memory.get_ptr().add(offset as usize)) 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 { impl DolphinSharedMemory {

View file

@ -20,10 +20,13 @@ pub struct SMSDolphin {
ver: SMSVersion, ver: SMSVersion,
} }
impl Dolphin for SMSDolphin { impl Dolphin for SMSDolphin {
unsafe fn operate_memory_unchecked<T, F>(&self, maddr: DolphinMemAddr, size: usize, operator: F) -> Option<T> 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
{ {
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() { for (pid, d) in DolphinMemory::list() {
match d { match d {
Some(d) => { Some(d) => {
if let Ok(o) = SMSDolphin::from_dolphin_memory(d, pid) { match SMSDolphin::from_dolphin_memory(d, pid) {
return Ok(o) 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"),
}
}
} }
game_running = true;
}, },
None => dolphin_running = true, None => dolphin_running = true,
} }

View file

@ -4687,7 +4687,7 @@ limitations under the License.
<h4>Used by:</h4> <h4>Used by:</h4>
<ul class="license-used-by"> <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/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/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-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> <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> <script src="index.js"></script>
</head> </head>
<body> <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> <header>
<div id="msg"></div> <div id="msg"></div>
<details> <details>