1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
//! Metadata table queries for the emulation context.
//!
//! Provides access to MemberRef, MethodSpec, standalone signatures, TypeSpec
//! signatures, and P/Invoke import resolution.
use std::sync::Arc;
use crate::{
emulation::engine::context::EmulationContext,
metadata::{
imports::{Import, ImportSourceId, ImportType},
signatures::SignatureTypeSpec,
tables::{
MemberRef, MethodSpec, MethodSpecRc, StandAloneSigRaw, StandAloneSignature,
TableAccess, TypeSpecRaw,
},
token::Token,
typesystem::CilTypeReference,
},
};
impl EmulationContext {
/// Gets a MemberRef by its token.
///
/// MemberRef entries represent external method or field references
/// (e.g., calls to BCL methods like `System.String.Concat`).
///
/// # Returns
///
/// The MemberRef if found, or `None` if the token doesn't exist.
#[must_use]
pub fn get_member_ref(&self, token: Token) -> Option<Arc<MemberRef>> {
self.assembly
.refs_members()
.get(&token)
.map(|e| e.value().clone())
}
/// Gets a method specification by its token.
///
/// MethodSpec entries represent generic method instantiations with concrete
/// type arguments (e.g., calls to `List<T>.Add` with `T = int`).
///
/// # Arguments
///
/// * `token` - A MethodSpec metadata token (table 0x2B)
///
/// # Returns
///
/// The MethodSpec if found, or `None` if the token doesn't exist.
#[must_use]
pub fn get_method_spec(&self, token: Token) -> Option<MethodSpecRc> {
self.assembly
.method_specs()
.get(&token)
.map(|e| e.value().clone())
}
/// Resolves a MethodSpec token to its underlying method token.
///
/// For generic method instantiations, this extracts the token of the actual
/// method being called (either a MethodDef or MemberRef).
///
/// # Arguments
///
/// * `method_spec` - The MethodSpec to resolve
///
/// # Returns
///
/// The token of the underlying method, or `None` if resolution fails.
#[must_use]
pub fn resolve_method_spec_to_token(method_spec: &MethodSpec) -> Option<Token> {
match &method_spec.method {
CilTypeReference::MethodDef(method_ref) => method_ref.upgrade().map(|m| m.token),
CilTypeReference::MemberRef(member_ref) => Some(member_ref.token),
_ => None,
}
}
/// Gets a standalone signature by its token.
///
/// Standalone signatures are used for:
/// - `calli` instructions (method call site signatures)
/// - Local variable declarations
/// - Function pointer types
///
/// # Arguments
///
/// * `token` - A StandAloneSig metadata token (table 0x11)
///
/// # Returns
///
/// The parsed signature if found, or `None` if the token doesn't exist.
#[must_use]
pub fn get_standalone_signature(&self, token: Token) -> Option<StandAloneSignature> {
let tables = self.assembly.tables()?;
let blob = self.assembly.blob()?;
let table = <_ as TableAccess<StandAloneSigRaw>>::table(tables)?;
let row = token.row();
let raw_sig = table.get(row)?;
// Parse the raw signature using the blob heap
let owned_sig = raw_sig.to_owned(blob).ok()?;
Some(owned_sig.parsed_signature.clone())
}
/// Gets the signature of a TypeSpec by its token.
///
/// TypeSpec entries represent complex types through signatures in the blob heap,
/// including generic instantiations, array types, and type parameters.
///
/// # Arguments
///
/// * `token` - A TypeSpec metadata token (table 0x1B)
///
/// # Returns
///
/// The parsed TypeSpec signature if found, or `None` if the token doesn't exist.
#[must_use]
pub fn get_typespec_signature(&self, token: Token) -> Option<SignatureTypeSpec> {
// Only handle TypeSpec tokens (table 0x1B)
if token.table() != 0x1B {
return None;
}
let tables = self.assembly.tables()?;
let blob = self.assembly.blob()?;
let table = <_ as TableAccess<TypeSpecRaw>>::table(tables)?;
let row = token.row();
let raw_typespec = table.get(row)?;
// Parse the raw signature using the blob heap
let owned_typespec = raw_typespec.to_owned(blob).ok()?;
Some(owned_typespec.signature.clone())
}
/// Finds a P/Invoke import by the method it's associated with.
///
/// P/Invoke imports are used for calling native code from managed code.
/// This searches the assembly's imports to find one matching the given
/// method token.
///
/// # Arguments
///
/// * `method_token` - Token of the method to find imports for
///
/// # Returns
///
/// The import information if found, or `None` if no import exists
/// for this method.
#[must_use]
pub fn find_import_by_method(&self, method_token: Token) -> Option<Arc<Import>> {
for import_entry in self.assembly.imports().cil() {
let import = import_entry.value();
if let ImportType::Method(import_method) = &import.import {
if import_method.token == method_token {
return Some(import.clone());
}
}
}
None
}
/// Gets the DLL/module name from a P/Invoke import.
///
/// For imports from external modules (P/Invoke), this returns the DLL name
/// (e.g., "kernel32.dll"). For other import types, returns `None`.
///
/// # Arguments
///
/// * `import` - The import to get the DLL name from
///
/// # Returns
///
/// The DLL name if this is a ModuleRef-sourced import, or `None` otherwise.
#[must_use]
pub fn get_import_dll_name(&self, import: &Import) -> Option<String> {
match import.source_id {
ImportSourceId::ModuleRef(token) => self
.assembly
.imports()
.cil()
.get_module_ref(token)
.map(|module_ref| module_ref.name.clone()),
_ => None,
}
}
/// Extracts type name and namespace from a MemberRef's parent type reference.
///
/// This is useful for identifying the declaring type of external method
/// references without having to manually handle weak reference upgrades.
///
/// # Arguments
///
/// * `member_ref` - The MemberRef to extract type info from
///
/// # Returns
///
/// A tuple of (namespace, name), or `None` if the type reference is invalid.
#[must_use]
pub fn get_member_ref_type_info(member_ref: &MemberRef) -> Option<(String, String)> {
match &member_ref.declaredby {
CilTypeReference::TypeRef(cil_type_ref)
| CilTypeReference::TypeDef(cil_type_ref)
| CilTypeReference::TypeSpec(cil_type_ref) => cil_type_ref.upgrade().map(|t| {
// Resolve namespace from enclosing type for nested types.
// Nested types have empty namespace in metadata but inherit their
// enclosing type's namespace (e.g., List`1/Enumerator → "System.Collections.Generic").
let namespace = if t.namespace.is_empty() {
t.enclosing_type()
.map(|enc| enc.namespace.clone())
.filter(|ns| !ns.is_empty())
.unwrap_or_default()
} else {
t.namespace.clone()
};
(namespace, t.name.clone())
}),
_ => None,
}
}
/// Gets the full type name from a MemberRef's parent type reference.
///
/// Returns the fully qualified type name (e.g., "System.String") or
/// extracts what's available from the reference.
///
/// # Arguments
///
/// * `member_ref` - The MemberRef to extract type name from
///
/// # Returns
///
/// The type name string, or "Unknown" if the reference is invalid.
#[must_use]
pub fn get_member_ref_type_name(member_ref: &MemberRef) -> String {
match &member_ref.declaredby {
CilTypeReference::ModuleRef(m) => m.name.clone(),
_ => member_ref
.declaredby
.fullname()
.unwrap_or_else(|| "Unknown".to_string()),
}
}
/// Gets the token from a MemberRef's parent type reference.
///
/// # Arguments
///
/// * `member_ref` - The MemberRef to extract token from
///
/// # Returns
///
/// The type token if the reference is valid, or `None` otherwise.
#[must_use]
pub fn get_member_ref_type_token(member_ref: &MemberRef) -> Option<Token> {
match &member_ref.declaredby {
CilTypeReference::TypeRef(cil_type_ref)
| CilTypeReference::TypeDef(cil_type_ref)
| CilTypeReference::TypeSpec(cil_type_ref) => cil_type_ref.upgrade().map(|t| t.token),
CilTypeReference::ModuleRef(m) => Some(m.token),
_ => None,
}
}
}