Skip to content

Commit 036b22e

Browse files
committed
Fix runtime vtable functions
1 parent b0cb73c commit 036b22e

File tree

5 files changed

+117
-45
lines changed

5 files changed

+117
-45
lines changed

s2binlib/src/module.rs

Lines changed: 89 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
use std::ffi::CString;
2-
31
#[derive(Debug)]
42
pub struct ModuleInfo {
53
pub base_address: usize,
@@ -9,31 +7,35 @@ pub struct ModuleInfo {
97
#[derive(Debug)]
108
pub enum ModuleError {
119
NotFound,
12-
InvalidName,
10+
InvalidBase,
1311
SystemError(String),
1412
}
1513

1614
impl std::fmt::Display for ModuleError {
1715
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1816
match self {
1917
ModuleError::NotFound => write!(f, "Module not found"),
20-
ModuleError::InvalidName => write!(f, "Invalid module name"),
18+
ModuleError::InvalidBase => write!(f, "Invalid module base address"),
2119
ModuleError::SystemError(msg) => write!(f, "System error: {}", msg),
2220
}
2321
}
2422
}
2523

2624
impl std::error::Error for ModuleError {}
2725

28-
pub fn get_module_info(module_name: &str) -> Result<ModuleInfo, ModuleError> {
26+
pub fn get_module_info(module_base: u64) -> Result<ModuleInfo, ModuleError> {
27+
if module_base == 0 || module_base > usize::MAX as u64 {
28+
return Err(ModuleError::InvalidBase);
29+
}
30+
2931
#[cfg(target_os = "windows")]
3032
{
31-
get_module_info_windows(module_name)
33+
get_module_info_windows(module_base as usize)
3234
}
3335

3436
#[cfg(target_os = "linux")]
3537
{
36-
get_module_info_linux(module_name)
38+
get_module_info_linux(module_base as usize)
3739
}
3840

3941
#[cfg(not(any(target_os = "windows", target_os = "linux")))]
@@ -43,21 +45,18 @@ pub fn get_module_info(module_name: &str) -> Result<ModuleInfo, ModuleError> {
4345
}
4446

4547
#[cfg(target_os = "windows")]
46-
fn get_module_info_windows(module_name: &str) -> Result<ModuleInfo, ModuleError> {
48+
fn get_module_info_windows(module_base: usize) -> Result<ModuleInfo, ModuleError> {
4749
use std::mem;
4850
use winapi::shared::minwindef::{FALSE, HMODULE};
49-
use winapi::um::libloaderapi::GetModuleHandleA;
5051
use winapi::um::processthreadsapi::GetCurrentProcess;
5152
use winapi::um::psapi::{GetModuleInformation, MODULEINFO};
5253

53-
let c_name = CString::new(module_name).map_err(|_| ModuleError::InvalidName)?;
54+
if module_base == 0 {
55+
return Err(ModuleError::InvalidBase);
56+
}
5457

5558
unsafe {
56-
let h_module: HMODULE = GetModuleHandleA(c_name.as_ptr());
57-
58-
if h_module.is_null() {
59-
return Err(ModuleError::NotFound);
60-
}
59+
let h_module: HMODULE = module_base as HMODULE;
6160

6261
let mut mod_info: MODULEINFO = mem::zeroed();
6362
let result = GetModuleInformation(
@@ -68,9 +67,14 @@ fn get_module_info_windows(module_name: &str) -> Result<ModuleInfo, ModuleError>
6867
);
6968

7069
if result == FALSE {
71-
return Err(ModuleError::SystemError(
72-
"GetModuleInformation failed".to_string(),
73-
));
70+
const ERROR_INVALID_HANDLE: i32 = 6;
71+
let error = std::io::Error::last_os_error();
72+
if error.raw_os_error() == Some(ERROR_INVALID_HANDLE) {
73+
return Err(ModuleError::NotFound);
74+
}
75+
return Err(ModuleError::SystemError(format!(
76+
"GetModuleInformation failed with error: {error}"
77+
)));
7478
}
7579

7680
Ok(ModuleInfo {
@@ -81,46 +85,97 @@ fn get_module_info_windows(module_name: &str) -> Result<ModuleInfo, ModuleError>
8185
}
8286

8387
#[cfg(target_os = "linux")]
84-
fn get_module_info_linux(module_name: &str) -> Result<ModuleInfo, ModuleError> {
88+
fn get_module_info_linux(module_base: usize) -> Result<ModuleInfo, ModuleError> {
8589
use std::fs::File;
8690
use std::io::{BufRead, BufReader};
8791

92+
if module_base == 0 {
93+
return Err(ModuleError::InvalidBase);
94+
}
95+
8896
let maps_file = File::open("/proc/self/maps")
8997
.map_err(|e| ModuleError::SystemError(format!("Cannot open /proc/self/maps: {}", e)))?;
9098

9199
let reader = BufReader::new(maps_file);
92100
let mut base_address: Option<usize> = None;
93101
let mut end_address: Option<usize> = None;
102+
let mut module_path: Option<String> = None;
103+
let mut module_dev: Option<String> = None;
104+
let mut module_inode: Option<String> = None;
94105

95106
for line in reader.lines() {
96107
let line = line.map_err(|e| ModuleError::SystemError(format!("Read error: {}", e)))?;
97108

98-
if line.contains(module_name) {
99-
let parts: Vec<&str> = line.split_whitespace().collect();
100-
if parts.is_empty() {
101-
continue;
109+
let trimmed = line.trim();
110+
if trimmed.is_empty() {
111+
continue;
112+
}
113+
114+
let parts: Vec<&str> = trimmed.split_whitespace().collect();
115+
if parts.is_empty() {
116+
continue;
117+
}
118+
119+
let addr_range: Vec<&str> = parts[0].split('-').collect();
120+
if addr_range.len() != 2 {
121+
continue;
122+
}
123+
124+
let start = usize::from_str_radix(addr_range[0], 16)
125+
.map_err(|_| ModuleError::SystemError("Invalid address format".to_string()))?;
126+
let end = usize::from_str_radix(addr_range[1], 16)
127+
.map_err(|_| ModuleError::SystemError("Invalid address format".to_string()))?;
128+
129+
let current_dev = parts.get(3).map(|s| s.to_string());
130+
let current_inode = parts.get(4).map(|s| s.to_string());
131+
let current_path = if parts.len() > 5 {
132+
Some(parts[5..].join(" "))
133+
} else {
134+
None
135+
};
136+
137+
if base_address.is_none() {
138+
if start == module_base {
139+
base_address = Some(start);
140+
end_address = Some(end);
141+
module_path = current_path;
142+
module_dev = current_dev;
143+
module_inode = current_inode;
102144
}
145+
continue;
146+
}
147+
148+
let matches_path = match (&module_path, &current_path) {
149+
(Some(expected), Some(actual)) => expected == actual,
150+
(None, None) => true,
151+
_ => false,
152+
};
103153

104-
let addr_range: Vec<&str> = parts[0].split('-').collect();
105-
if addr_range.len() != 2 {
106-
continue;
154+
let matches_identity = match (&module_dev, &module_inode, &current_dev, &current_inode) {
155+
(Some(expected_dev), Some(expected_inode), Some(dev), Some(inode)) => {
156+
expected_dev == dev && expected_inode == inode
107157
}
158+
_ => false,
159+
};
108160

109-
let start = usize::from_str_radix(addr_range[0], 16)
110-
.map_err(|_| ModuleError::SystemError("Invalid address format".to_string()))?;
111-
let end = usize::from_str_radix(addr_range[1], 16)
112-
.map_err(|_| ModuleError::SystemError("Invalid address format".to_string()))?;
161+
let contiguous = end_address.map_or(false, |current_end| start == current_end);
113162

114-
if base_address.is_none() {
115-
base_address = Some(start);
163+
if matches_path || matches_identity || contiguous {
164+
match end_address {
165+
Some(ref mut stored_end) if end > *stored_end => *stored_end = end,
166+
None => end_address = Some(end),
167+
_ => {}
116168
}
169+
continue;
170+
}
117171

118-
end_address = Some(end);
172+
if start > module_base {
173+
break;
119174
}
120175
}
121176

122177
match (base_address, end_address) {
123-
(Some(base), Some(end)) => Ok(ModuleInfo {
178+
(Some(base), Some(end)) if end > base => Ok(ModuleInfo {
124179
base_address: base,
125180
size: end - base,
126181
}),

s2binlib/src/s2binlib.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use object::{Object, ObjectSection, ObjectSymbol, read::pe::ImageOptionalHeader}
2626
use crate::{
2727
VTableInfo, find_pattern_simd, is_executable,
2828
jit::JitTrampoline,
29-
memory::{get_module_base_from_pointer, set_mem_access},
29+
memory::{get_module_base_from_pointer, module_from_pointer, set_mem_access},
3030
};
3131

3232
#[cfg(target_os = "windows")]
@@ -220,6 +220,24 @@ impl<'a> S2BinLib<'a> {
220220
self.manual_base_addresses.remove(lib_name);
221221
}
222222

223+
pub fn module_from_pointer(&self, ptr: u64) -> Result<u64> {
224+
let module = module_from_pointer(ptr);
225+
226+
if module.is_none() {
227+
bail!("Failed to get module from pointer.");
228+
}
229+
230+
let (module_name, module_base) = module.unwrap();
231+
// respect custom module base
232+
for (manual_module_name, manual_module_base) in &self.manual_base_addresses {
233+
let lib_name = self.get_os_lib_name(&manual_module_name);
234+
if module_name.contains(&lib_name) {
235+
return Ok(*manual_module_base);
236+
}
237+
}
238+
Ok(module_base)
239+
}
240+
223241
pub fn get_binary_path(&self, binary_name: &str) -> String {
224242
if self.os.to_lowercase() == "windows" {
225243
if let Some(path) = self.custom_binary_paths_windows.get(binary_name) {

s2binlib/src/view.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -412,14 +412,9 @@ impl<'a> S2BinLib<'a> {
412412
}
413413

414414
pub fn get_memory_view_from_ptr(&self, ptr: u64) -> Result<MemoryView<'_>> {
415-
let result = module_from_pointer(ptr);
415+
let module_base = self.module_from_pointer(ptr)?;
416416

417-
if result.is_none() {
418-
bail!("Failed to get module from pointer.");
419-
}
420-
421-
let (module_name, module_base) = result.unwrap();
422-
let module_info = get_module_info(&module_name)?;
417+
let module_info = get_module_info(module_base)?;
423418
let memory_view = unsafe {
424419
MemoryView::new(
425420
module_info.base_address as *const u8,

s2binlib/src/vtable.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,13 @@ impl<'a> S2BinLib<'a> {
168168
let memory_view = self.get_memory_view_from_ptr(vtable_ptr)?;
169169

170170
if !memory_view.contains(vtable_ptr - 8) {
171-
bail!("Vtable pointer is not in the memory view.");
171+
bail!("Vtable pointer {:X} is not in the memory view {:X}.", vtable_ptr - 8, memory_view.image_base());
172172
};
173-
let rtti_ptr: u64 = vtable_ptr - 8;
173+
let rtti_ptr = memory_view.read::<u64>(vtable_ptr - 8);
174+
if rtti_ptr.is_none() {
175+
bail!("Failed to read rtti pointer.");
176+
}
177+
let rtti_ptr = rtti_ptr.unwrap();
174178

175179
#[cfg(target_os = "windows")]
176180
{

s2binlib_binding/src/c_bindings.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2475,7 +2475,7 @@ pub extern "C" fn s2binlib_get_object_ptr_vtable_name(
24752475

24762476
0
24772477
}
2478-
Err(_) => return_error!(-4, "Failed to get vtable info"),
2478+
Err(err) => return_error!(-4, format!("Failed to get vtable info: {:?}", err)),
24792479
}
24802480
}
24812481
}

0 commit comments

Comments
 (0)