fromsoftware_shared/
rtti.rs1use std::ptr::NonNull;
2
3use pelite::pe64::{Pe, Rva, Va, msvc::RTTICompleteObjectLocator};
4use undname::Flags;
5
6use crate::program::Program;
7
8const VA_SIZE: u32 = size_of::<Va>() as u32;
10
11pub fn find_rtti_classes<'a, T: Pe<'a>>(program: &'a T) -> impl Iterator<Item = Class<'a, T>> + 'a {
14 let text = program
15 .section_headers()
16 .by_name(".text")
17 .expect("no .text section found");
18
19 let rdata = program
20 .section_headers()
21 .by_name(".rdata")
22 .expect("no .rdata section found");
23
24 rdata
25 .virtual_range()
26 .step_by(size_of::<Va>())
27 .filter_map(move |candidate_rva| {
28 let vftable_meta_rva = candidate_rva;
29 let vftable_rva = candidate_rva + VA_SIZE;
30
31 let vftable_meta_rva = program
32 .derva(vftable_meta_rva)
33 .and_then(|va| program.va_to_rva(*va))
34 .ok()?;
35
36 let vftable_entry_rva = program
37 .derva(vftable_rva)
38 .and_then(|va| program.va_to_rva(*va))
39 .ok()?;
40
41 if rdata.virtual_range().contains(&vftable_meta_rva)
42 && text.virtual_range().contains(&vftable_entry_rva)
43 {
44 let _: &RTTICompleteObjectLocator = program.derva(vftable_meta_rva).ok()?;
45
46 Some((vftable_meta_rva, vftable_rva))
47 } else {
48 None
49 }
50 })
51 .filter_map(|(meta, vftable)| {
52 let col: &RTTICompleteObjectLocator = program.derva(meta).ok()?;
53
54 let ty_name = program
55 .derva_c_str(col.type_descriptor + 16)
56 .ok()?
57 .to_string();
58 if !ty_name
59 .chars()
60 .all(|ch| (0x20..=0x7e).contains(&(ch as u8)))
61 {
62 return None;
63 }
64
65 let demangled = undname::demangle(ty_name.as_str(), Flags::NAME_ONLY)
66 .map(|s| s.to_string())
67 .ok()?;
68
69 Some(Class {
70 program,
71 name: demangled,
72 vftable,
73 })
74 })
75}
76
77pub fn vftable_classname(program: &Program, vftable_va: usize) -> Option<String> {
80 let vftable_rva = program.va_to_rva(vftable_va as u64).ok()?;
81 let vftable_meta_rva = vftable_rva - VA_SIZE;
82
83 let rdata = program
84 .section_headers()
85 .by_name(".rdata")
86 .expect("no .rdata section found");
87
88 let vftable_meta_rva = program
89 .derva(vftable_meta_rva)
90 .and_then(|va| program.va_to_rva(*va))
91 .ok()?;
92
93 if !rdata.virtual_range().contains(&vftable_meta_rva) {
94 return None;
95 }
96
97 let col: &RTTICompleteObjectLocator = program.derva(vftable_meta_rva).ok()?;
98 let ty_name = program
99 .derva_c_str(col.type_descriptor + 16)
100 .ok()?
101 .to_string();
102 if !ty_name
103 .chars()
104 .all(|ch| (0x20..=0x7e).contains(&(ch as u8)))
105 {
106 return None;
107 }
108
109 let demangled = undname::demangle(ty_name.as_str(), Flags::NAME_ONLY)
110 .map(|s| s.to_string())
111 .ok()?;
112
113 Some(demangled)
114}
115
116#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
117#[allow(dead_code)]
118struct RttiCandidate {
119 vftable_meta_rva: Rva,
120 vftable_rva: Rva,
121}
122
123pub struct Class<'a, T: Pe<'a>> {
124 program: &'a T,
125 pub name: String,
126 pub vftable: Rva,
127}
128
129impl<'a, T: Pe<'a>> Class<'a, T> {
130 pub unsafe fn vmt_fn(&self, index: u32) -> Option<Va> {
135 Some(*self.program.derva(self.vftable + VA_SIZE * index).ok()?)
136 }
137
138 pub unsafe fn vmt_index(&self, index: u32) -> Option<NonNull<Va>> {
143 let ptr = self
144 .program
145 .rva_to_va(self.vftable + VA_SIZE * index)
146 .ok()? as *const u64 as *mut u64;
147
148 NonNull::new(ptr)
149 }
150}