mirror of https://github.com/a2x/cs2-dumper.git
Merge cfb038485f
into a4b9e4abe6
This commit is contained in:
commit
b1d6187874
|
@ -30,5 +30,8 @@ features = [
|
|||
"Win32_System_Threading",
|
||||
]
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
procfs = "0.16.0"
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
{
|
||||
"signatures": [
|
||||
{
|
||||
"name": "dwBuildNumber",
|
||||
"module": "libengine2.so",
|
||||
"pattern": "89 15 ? ? ? ? 48 83 ? ? 48 8D 3D ? ? ? ?",
|
||||
"operations": [
|
||||
{
|
||||
"type": "rip",
|
||||
"offset": 2,
|
||||
"length": 6
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "dwNetworkGameClient_deltaTick",
|
||||
"module": "libengine2.so",
|
||||
"pattern": "89 83 ? ? ? ? B8 01",
|
||||
"operations": [
|
||||
{
|
||||
"type": "slice",
|
||||
"start": 2,
|
||||
"end": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "dwEntityList",
|
||||
"module": "libclient.so",
|
||||
"pattern": "48 8B 3D ? ? ? ? 44 89 E3 45 89 E6 C1 ? 0E",
|
||||
"operations": [
|
||||
{
|
||||
"type": "rip"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "dwForceAttack",
|
||||
"module": "libclient.so",
|
||||
"pattern": "4c 8d ? ? ? ? ? 4d 89 ? 48 89 ? ? ? ? ? eb",
|
||||
"operations": [
|
||||
{
|
||||
"type": "rip"
|
||||
},
|
||||
{
|
||||
"type": "add",
|
||||
"value": 48
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "dwForceAttack2",
|
||||
"module": "libclient.so",
|
||||
"pattern": "4c 8d ? ? ? ? ? 48 8d ? ? ? ? ? 48 89 ? ? ? ? ? e8 ? ? ? ? 49 ? ? ? 31 d2 48 c7 ? ? ? ? ? ? 08",
|
||||
"operations": [
|
||||
{
|
||||
"type": "rip"
|
||||
},
|
||||
{
|
||||
"type": "add",
|
||||
"value": 48
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "dwGameEntitySystem",
|
||||
"module": "libclient.so",
|
||||
"pattern": "48 89 3d ? ? ? ? e9 ? ? ? ? 55",
|
||||
"operations": [
|
||||
{
|
||||
"type": "rip"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "dwGameEntitySystem_getHighestEntityIndex",
|
||||
"module": "libclient.so",
|
||||
"pattern": "8b 87 ? ? ? ? c3 66 ? ? ? ? ? ? ? ? 8b 97",
|
||||
"operations": [
|
||||
{
|
||||
"type": "slice",
|
||||
"start": 2,
|
||||
"end": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "dwGameRules",
|
||||
"module": "libclient.so",
|
||||
"pattern": "48 89 3d ? ? ? ? 8b",
|
||||
"operations": [
|
||||
{
|
||||
"type": "rip"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "dwLocalPlayerController",
|
||||
"module": "libclient.so",
|
||||
"pattern": "48 8B 15 ? ? ? ? 31 C0 48 85 D2 74 5C",
|
||||
"operations": [
|
||||
{
|
||||
"type": "rip"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "dwLocalPlayerPawn",
|
||||
"module": "libclient.so",
|
||||
"pattern": "48 8D 05 ? ? ? ? C3 ? ? ? 00 00 00 00 00 C7 47 40",
|
||||
"operations": [
|
||||
{
|
||||
"type": "rip"
|
||||
},
|
||||
{
|
||||
"type": "add",
|
||||
"value": 56
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "dwViewMatrix",
|
||||
"module": "libclient.so",
|
||||
"pattern": "4c 8d 05 ? ? ? ? 48 ? ? 48 8d ? ? ? ? ? e8 ? ? ? ? 8b",
|
||||
"operations": [
|
||||
{
|
||||
"type": "rip"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -39,3 +39,42 @@ pub struct Signature {
|
|||
pub pattern: String,
|
||||
pub operations: Vec<Operation>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SchemaSystemConfig {
|
||||
pub module_name: &'static str,
|
||||
pub pattern: &'static str,
|
||||
pub type_scope_size_offset: usize,
|
||||
pub type_scope_data_offset: usize,
|
||||
pub declared_classes_offset: usize,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub const SCHEMA_CONF: SchemaSystemConfig = SchemaSystemConfig {
|
||||
module_name: "schemasystem.dll",
|
||||
pattern: "48 8D 0D ? ? ? ? E9 ? ? ? ? CC CC CC CC 48 8D 0D ? ? ? ? E9 ? ? ? ? CC CC CC CC 48 83 EC 28",
|
||||
type_scope_size_offset: 0x190,
|
||||
type_scope_data_offset: 0x198,
|
||||
declared_classes_offset: 0x5B8,
|
||||
};
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub const SCHEMA_CONF: SchemaSystemConfig = SchemaSystemConfig {
|
||||
module_name: "libschemasystem.so",
|
||||
pattern: "48 8D 05 ? ? ? ? c3 ? ? ? 00 00 00 00 00 48 8d 05 ? ? ? ? c3 ? ? ? 00 00 00 00 00 48 ? ? ? c3",
|
||||
type_scope_size_offset: 0x1f8,
|
||||
type_scope_data_offset: 0x200,
|
||||
declared_classes_offset: 0x620,
|
||||
};
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub const PROC_NAME: &str = "cs2.exe";
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub const PROC_NAME: &str = "cs2";
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub const OFFSETS_CONF: &str = "config.json";
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub const OFFSETS_CONF: &str = "config_linux.json";
|
||||
|
|
|
@ -58,8 +58,21 @@ pub fn dump_interfaces(
|
|||
if let Some(create_interface_export) = module.export_by_name("CreateInterface") {
|
||||
info!("Dumping interfaces in <blue>{}</>...", module.name);
|
||||
|
||||
let create_interface_address =
|
||||
let create_interface_address;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
create_interface_address =
|
||||
process.resolve_rip(create_interface_export, None, None)?;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
let create_interface_fn =
|
||||
process.resolve_jmp(create_interface_export, None, None)?;
|
||||
create_interface_address =
|
||||
process.resolve_rip(create_interface_fn + 0x10, None, None)?;
|
||||
}
|
||||
|
||||
let mut node = process.read_memory::<*mut InterfaceNode>(create_interface_address)?;
|
||||
|
||||
|
|
|
@ -7,8 +7,8 @@ use simplelog::{debug, error, info};
|
|||
use super::{generate_files, Entries, Entry};
|
||||
|
||||
use crate::builder::FileBuilderEnum;
|
||||
use crate::config::Config;
|
||||
use crate::config::Operation::*;
|
||||
use crate::config::{self, Config};
|
||||
use crate::os::Process;
|
||||
|
||||
pub fn dump_offsets(
|
||||
|
@ -17,7 +17,7 @@ pub fn dump_offsets(
|
|||
file_path: &str,
|
||||
indent: usize,
|
||||
) -> Result<()> {
|
||||
let file = File::open("config.json")?;
|
||||
let file = File::open(config::OFFSETS_CONF)?;
|
||||
|
||||
let config: Config = serde_json::from_reader(file)?;
|
||||
|
||||
|
@ -149,8 +149,6 @@ mod tests {
|
|||
fn setup() -> Result<Process> {
|
||||
let mut process = Process::new("cs2.exe")?;
|
||||
|
||||
process.initialize()?;
|
||||
|
||||
Ok(process)
|
||||
}
|
||||
|
||||
|
|
|
@ -86,14 +86,14 @@ fn main() -> Result<()> {
|
|||
|
||||
TermLogger::init(log_level, config, TerminalMode::Mixed, ColorChoice::Auto)?;
|
||||
|
||||
if !Path::new("config.json").exists() {
|
||||
bail!("Missing config.json file");
|
||||
if !Path::new(config::OFFSETS_CONF).exists() {
|
||||
bail!("Missing {} file", config::OFFSETS_CONF);
|
||||
}
|
||||
|
||||
// Create the output directory if it doesn't exist.
|
||||
fs::create_dir_all(&output)?;
|
||||
|
||||
let mut process = Process::new("cs2.exe")?;
|
||||
let mut process = Process::new(config::PROC_NAME)?;
|
||||
|
||||
let now = Instant::now();
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#[cfg(target_os = "linux")]
|
||||
pub use module::ModuleEntry;
|
||||
|
||||
pub use module::Module;
|
||||
pub use process::Process;
|
||||
|
||||
|
|
114
src/os/module.rs
114
src/os/module.rs
|
@ -1,18 +1,43 @@
|
|||
use anyhow::Result;
|
||||
|
||||
use goblin::pe::export::Export;
|
||||
use goblin::pe::import::Import;
|
||||
use goblin::pe::options::ParseOptions;
|
||||
use goblin::pe::section_table::SectionTable;
|
||||
use goblin::pe::PE;
|
||||
#[cfg(target_os = "windows")]
|
||||
use goblin::pe::{
|
||||
export::Export, import::Import, options::ParseOptions, section_table::SectionTable, PE,
|
||||
};
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
use goblin::elf::{sym, Elf, SectionHeader};
|
||||
#[cfg(target_os = "linux")]
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[derive(Debug)]
|
||||
pub struct ModuleEntry {
|
||||
pub path: PathBuf,
|
||||
pub start_addr: usize,
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
/// Represents a module loaded in a Windows process.
|
||||
pub struct Module<'a> {
|
||||
/// The name of the module.
|
||||
pub name: &'a str,
|
||||
|
||||
/// A reference to a slice of bytes containing the module data.
|
||||
#[cfg(target_os = "windows")]
|
||||
pub data: &'a [u8],
|
||||
#[cfg(target_os = "linux")]
|
||||
pub module_info: &'a ModuleEntry,
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
/// The PE file format representation of the module.
|
||||
pub pe: PE<'a>,
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub elf: Elf<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Module<'a> {
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn parse(name: &'a str, data: &'a [u8]) -> Result<Self> {
|
||||
let pe = PE::parse_with_opts(
|
||||
data,
|
||||
|
@ -25,22 +50,67 @@ impl<'a> Module<'a> {
|
|||
Ok(Self { name, data, pe })
|
||||
}
|
||||
|
||||
// parse the elf
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn parse(name: &'a str, module_entry: &'a ModuleEntry) -> Result<Self> {
|
||||
let elf = Elf::parse(&module_entry.data)?;
|
||||
Ok(Self {
|
||||
name,
|
||||
module_info: module_entry,
|
||||
elf,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn base(&self) -> usize {
|
||||
self.pe.image_base
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn base(&self) -> usize {
|
||||
self.module_info.start_addr
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn exports(&self) -> &[Export] {
|
||||
&self.pe.exports
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn exports(&self) -> Vec<sym::Sym> {
|
||||
let exports: Vec<sym::Sym> = self
|
||||
.elf
|
||||
.dynsyms
|
||||
.iter()
|
||||
.filter(|sym| sym.st_bind() == sym::STB_GLOBAL || sym.st_bind() == sym::STB_WEAK)
|
||||
.collect();
|
||||
exports
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn imports(&self) -> &[Import] {
|
||||
&self.pe.imports
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn imports(&self) -> Vec<sym::Sym> {
|
||||
let imports: Vec<sym::Sym> = self
|
||||
.elf
|
||||
.dynsyms
|
||||
.iter()
|
||||
.filter(|sym| sym.is_import())
|
||||
.collect();
|
||||
imports
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn export_by_name(&self, name: &str) -> Option<usize> {
|
||||
self.pe
|
||||
.exports
|
||||
|
@ -50,6 +120,21 @@ impl<'a> Module<'a> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn export_by_name(&self, name: &str) -> Option<usize> {
|
||||
let base_addr: usize = self.base();
|
||||
self.elf
|
||||
.dynsyms
|
||||
.iter()
|
||||
.find(|sym| {
|
||||
(sym.st_bind() == sym::STB_GLOBAL || sym.st_bind() == sym::STB_WEAK)
|
||||
&& self.elf.dynstrtab.get_at(sym.st_name) == Some(name)
|
||||
})
|
||||
.map(|sym| (base_addr as u64 + sym.st_value) as usize)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn import_by_name(&self, name: &str) -> Option<usize> {
|
||||
self.pe
|
||||
.imports
|
||||
|
@ -59,11 +144,30 @@ impl<'a> Module<'a> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn get_import_by_name(&self, name: &str) -> Option<usize> {
|
||||
let base_addr: usize = self.base().into();
|
||||
self.elf
|
||||
.dynsyms
|
||||
.iter()
|
||||
.find(|sym| sym.is_import() && self.elf.dynstrtab.get_at(sym.st_name) == Some(name))
|
||||
.map(|sym| (base_addr as u64 + sym.st_value) as usize)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn sections(&self) -> &[SectionTable] {
|
||||
&self.pe.sections
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn sections(&self) -> &[SectionHeader] {
|
||||
self.elf.section_headers.as_slice()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn size(&self) -> u32 {
|
||||
self.pe
|
||||
.header
|
||||
|
|
|
@ -1,25 +1,54 @@
|
|||
use std::collections::HashMap;
|
||||
use std::ffi::{c_void, CStr};
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use super::Module;
|
||||
#[cfg(target_os = "linux")]
|
||||
use super::ModuleEntry;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
|
||||
use windows::Win32::Foundation::{CloseHandle, HANDLE};
|
||||
use windows::Win32::System::Diagnostics::Debug::ReadProcessMemory;
|
||||
use windows::Win32::System::Diagnostics::ToolHelp::*;
|
||||
use windows::Win32::System::Threading::{OpenProcess, PROCESS_ALL_ACCESS};
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::c_void;
|
||||
use std::mem;
|
||||
|
||||
use super::Module;
|
||||
#[cfg(target_os = "windows")]
|
||||
use std::ffi::CStr;
|
||||
#[cfg(target_os = "windows")]
|
||||
use std::ptr;
|
||||
#[cfg(target_os = "windows")]
|
||||
use windows::Win32::{
|
||||
Foundation::{CloseHandle, HANDLE},
|
||||
System::Diagnostics::Debug::ReadProcessMemory,
|
||||
System::Diagnostics::ToolHelp::*,
|
||||
System::Threading::{OpenProcess, PROCESS_ALL_ACCESS},
|
||||
};
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
use procfs::process::{self, all_processes};
|
||||
#[cfg(target_os = "linux")]
|
||||
use std::fs::File;
|
||||
#[cfg(target_os = "linux")]
|
||||
use std::io::{Read, Seek, SeekFrom};
|
||||
#[cfg(target_os = "linux")]
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// Represents a Windows process.
|
||||
#[derive(Debug)]
|
||||
pub struct Process {
|
||||
/// ID of the process.
|
||||
id: u32,
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
/// Handle to the process.
|
||||
handle: HANDLE,
|
||||
|
||||
/// A HashMap containing the name of each module and its corresponding raw data.
|
||||
#[cfg(target_os = "windows")]
|
||||
modules: HashMap<String, Vec<u8>>,
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
modules: HashMap<String, ModuleEntry>,
|
||||
}
|
||||
|
||||
impl Process {
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn new(name: &str) -> Result<Self> {
|
||||
let id = Self::get_process_id_by_name(name)?;
|
||||
|
||||
|
@ -30,12 +59,21 @@ impl Process {
|
|||
handle,
|
||||
modules: HashMap::new(),
|
||||
};
|
||||
|
||||
process.parse_loaded_modules()?;
|
||||
|
||||
Ok(process)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn new(name: &str) -> Result<Self> {
|
||||
let id = Self::get_process_id_by_name(name)?;
|
||||
let mut process = Self {
|
||||
id,
|
||||
modules: HashMap::new(),
|
||||
};
|
||||
process.parse_loaded_modules()?;
|
||||
Ok(process)
|
||||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn find_pattern(&self, module_name: &str, pattern: &str) -> Option<usize> {
|
||||
let module = self.get_module_by_name(module_name)?;
|
||||
|
||||
|
@ -54,6 +92,30 @@ impl Process {
|
|||
None
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn find_pattern(&self, module_name: &str, pattern: &str) -> Option<usize> {
|
||||
let module = self.get_module_by_name(module_name)?;
|
||||
|
||||
let pattern_bytes = Self::pattern_to_bytes(pattern);
|
||||
|
||||
for (i, window) in module
|
||||
.module_info
|
||||
.data
|
||||
.windows(pattern_bytes.len())
|
||||
.enumerate()
|
||||
{
|
||||
if window
|
||||
.iter()
|
||||
.zip(&pattern_bytes)
|
||||
.all(|(&x, &y)| x == y as u8 || y == -1)
|
||||
{
|
||||
return Some(module.base() + i);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn get_module_by_name<'a>(&'a self, name: &'a str) -> Option<Module<'a>> {
|
||||
self.modules
|
||||
.get(name)
|
||||
|
@ -82,6 +144,7 @@ impl Process {
|
|||
Ok(buffer)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn read_memory_raw(&self, address: usize, buffer: *mut c_void, size: usize) -> Result<()> {
|
||||
unsafe {
|
||||
ReadProcessMemory(
|
||||
|
@ -95,6 +158,21 @@ impl Process {
|
|||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn read_memory_raw(&self, address: usize, buffer: *mut c_void, size: usize) -> Result<()> {
|
||||
let proc_mem_path = format!("/proc/{}/mem", self.id);
|
||||
let mut mem_file = File::open(proc_mem_path)?;
|
||||
|
||||
// Go to the start address
|
||||
mem_file.seek(SeekFrom::Start(address as u64))?;
|
||||
|
||||
let buffer_slice = unsafe { std::slice::from_raw_parts_mut(buffer as *mut u8, size) };
|
||||
|
||||
// Try to read the data
|
||||
mem_file.read_exact(buffer_slice)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read_string(&self, address: usize) -> Result<String> {
|
||||
let mut buffer = Vec::new();
|
||||
|
||||
|
@ -129,7 +207,12 @@ impl Process {
|
|||
// The displacement value can be negative.
|
||||
let displacement = self.read_memory::<i32>(address + offset.unwrap_or(0x1))?;
|
||||
|
||||
Ok((address + displacement as usize) + length.unwrap_or(0x5))
|
||||
let final_address = if displacement.is_negative() {
|
||||
address - displacement.wrapping_abs() as usize
|
||||
} else {
|
||||
address + displacement as usize
|
||||
} + length.unwrap_or(0x5);
|
||||
Ok(final_address)
|
||||
}
|
||||
|
||||
pub fn resolve_rip(
|
||||
|
@ -141,9 +224,15 @@ impl Process {
|
|||
// The displacement value can be negative.
|
||||
let displacement = self.read_memory::<i32>(address + offset.unwrap_or(0x3))?;
|
||||
|
||||
Ok((address + displacement as usize) + length.unwrap_or(0x7))
|
||||
let final_address = if displacement.is_negative() {
|
||||
address - displacement.wrapping_abs() as usize
|
||||
} else {
|
||||
address + displacement as usize
|
||||
} + length.unwrap_or(0x7);
|
||||
Ok(final_address)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn get_process_id_by_name(process_name: &str) -> Result<u32> {
|
||||
let snapshot = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) }?;
|
||||
|
||||
|
@ -167,6 +256,27 @@ impl Process {
|
|||
bail!("Process not found: {}", process_name)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn get_process_id_by_name(process_name: &str) -> Result<u32> {
|
||||
use std::io::{BufRead, BufReader};
|
||||
|
||||
for process_iter in all_processes()? {
|
||||
let Ok(process) = process_iter else { continue };
|
||||
let comm_path = format!("/proc/{}/comm", process.pid());
|
||||
if let Ok(comm_file) = File::open(Path::new(&comm_path)) {
|
||||
let mut comm = String::new();
|
||||
if BufReader::new(comm_file).read_line(&mut comm).is_ok() {
|
||||
comm.pop();
|
||||
if comm == process_name && process.pid() > 0 {
|
||||
return Ok(process.pid() as u32);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bail!("Process not found: {}", process_name);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn parse_loaded_modules(&mut self) -> Result<()> {
|
||||
let snapshot = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, self.id) }?;
|
||||
|
||||
|
@ -196,6 +306,58 @@ impl Process {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn parse_loaded_modules(&mut self) -> Result<()> {
|
||||
let process = process::Process::new(self.id as i32)?;
|
||||
|
||||
let mut modules_info: HashMap<String, ((u64, u64), PathBuf)> = HashMap::new();
|
||||
|
||||
for mmap in process.maps()? {
|
||||
let module_path = match mmap.pathname {
|
||||
process::MMapPath::Path(path) => path,
|
||||
_ => continue,
|
||||
};
|
||||
let get_module_name = |path: &PathBuf| -> Option<String> {
|
||||
path.file_name()
|
||||
.and_then(|name| name.to_str())
|
||||
.filter(|name| name.starts_with("lib") && name.ends_with(".so"))
|
||||
.map(|name| name.to_string())
|
||||
};
|
||||
if let Some(module_name) = get_module_name(&module_path) {
|
||||
let module_entry = modules_info
|
||||
.entry(module_name)
|
||||
.or_insert_with(|| (mmap.address, module_path));
|
||||
module_entry.0 = (
|
||||
std::cmp::min(mmap.address.0, module_entry.0 .0),
|
||||
std::cmp::max(mmap.address.1, module_entry.0 .1),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (module_name, (address_space, path)) in modules_info.into_iter() {
|
||||
let (start, end) = address_space;
|
||||
let read_elf_file = |path: &PathBuf| -> Result<Vec<u8>> {
|
||||
let mut file = File::open(path)?;
|
||||
let mut data = Vec::new();
|
||||
file.read_to_end(&mut data)?;
|
||||
|
||||
Ok(data)
|
||||
};
|
||||
if let Ok(data) = read_elf_file(&path) {
|
||||
self.modules.insert(
|
||||
module_name,
|
||||
ModuleEntry {
|
||||
path: path.clone(),
|
||||
start_addr: start as usize,
|
||||
data: data,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pattern_to_bytes(pattern: &str) -> Vec<i32> {
|
||||
pattern
|
||||
.split_whitespace()
|
||||
|
@ -210,6 +372,7 @@ impl Process {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
impl Drop for Process {
|
||||
fn drop(&mut self) {
|
||||
if !self.handle.is_invalid() {
|
||||
|
|
|
@ -6,6 +6,8 @@ use super::SchemaSystemTypeScope;
|
|||
|
||||
use crate::os::Process;
|
||||
|
||||
use crate::config::SCHEMA_CONF;
|
||||
|
||||
pub struct SchemaSystem<'a> {
|
||||
process: &'a Process,
|
||||
address: usize,
|
||||
|
@ -13,10 +15,9 @@ pub struct SchemaSystem<'a> {
|
|||
|
||||
impl<'a> SchemaSystem<'a> {
|
||||
pub fn new(process: &'a Process) -> Result<Self> {
|
||||
let mut address = process.find_pattern(
|
||||
"schemasystem.dll",
|
||||
"48 8D 0D ? ? ? ? E9 ? ? ? ? CC CC CC CC 48 8D 0D ? ? ? ? E9 ? ? ? ? CC CC CC CC 48 83 EC 28"
|
||||
).expect("unable to find schema system pattern");
|
||||
let mut address = process
|
||||
.find_pattern(SCHEMA_CONF.module_name, SCHEMA_CONF.pattern)
|
||||
.expect("unable to find schema system pattern");
|
||||
|
||||
address = process.resolve_rip(address, None, None)?;
|
||||
|
||||
|
@ -24,13 +25,17 @@ impl<'a> SchemaSystem<'a> {
|
|||
}
|
||||
|
||||
pub fn type_scopes(&self) -> Result<Vec<SchemaSystemTypeScope>> {
|
||||
let size = self.process.read_memory::<u32>(self.address + 0x190)?;
|
||||
let size = self
|
||||
.process
|
||||
.read_memory::<u32>(self.address + SCHEMA_CONF.type_scope_size_offset)?;
|
||||
|
||||
if size == 0 {
|
||||
bail!("no type scopes found");
|
||||
}
|
||||
|
||||
let data = self.process.read_memory::<usize>(self.address + 0x198)?;
|
||||
let data = self
|
||||
.process
|
||||
.read_memory::<usize>(self.address + SCHEMA_CONF.type_scope_data_offset)?;
|
||||
|
||||
let mut addresses = vec![0; size as usize];
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@ use super::{SchemaClassInfo, SchemaTypeDeclaredClass, UtlTsHash};
|
|||
|
||||
use crate::os::Process;
|
||||
|
||||
use crate::config::SCHEMA_CONF;
|
||||
|
||||
pub struct SchemaSystemTypeScope<'a> {
|
||||
process: &'a Process,
|
||||
address: usize,
|
||||
|
@ -17,7 +19,9 @@ impl<'a> SchemaSystemTypeScope<'a> {
|
|||
pub fn classes(&self) -> Result<Vec<SchemaClassInfo>> {
|
||||
let declared_classes = self
|
||||
.process
|
||||
.read_memory::<UtlTsHash<*mut SchemaTypeDeclaredClass>>(self.address + 0x5B8)?;
|
||||
.read_memory::<UtlTsHash<*mut SchemaTypeDeclaredClass>>(
|
||||
self.address + SCHEMA_CONF.declared_classes_offset,
|
||||
)?;
|
||||
|
||||
let classes: Vec<SchemaClassInfo> = declared_classes
|
||||
.elements(self.process)?
|
||||
|
|
Loading…
Reference in New Issue