[v0.1.0-beta.4] Added validity check on MEM1 of DolphinProcessMemory; fixed write memory API
This commit is contained in:
parent
444618a42d
commit
3a4ba11520
9 changed files with 116 additions and 43 deletions
|
@ -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
2
Cargo.lock
generated
|
@ -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",
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue