unidecompiler-plugin-dotnet-cli 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- unidecompiler_plugin_dotnet_cli/__init__.py +1 -0
- unidecompiler_plugin_dotnet_cli/assembly.py +675 -0
- unidecompiler_plugin_dotnet_cli/lifter.py +686 -0
- unidecompiler_plugin_dotnet_cli/plugin.py +52 -0
- unidecompiler_plugin_dotnet_cli/support.py +11 -0
- unidecompiler_plugin_dotnet_cli-0.1.0.dist-info/METADATA +21 -0
- unidecompiler_plugin_dotnet_cli-0.1.0.dist-info/RECORD +10 -0
- unidecompiler_plugin_dotnet_cli-0.1.0.dist-info/WHEEL +5 -0
- unidecompiler_plugin_dotnet_cli-0.1.0.dist-info/entry_points.txt +2 -0
- unidecompiler_plugin_dotnet_cli-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Frontend adapter for .NET CLI assemblies."""
|
|
@@ -0,0 +1,675 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
import struct
|
|
5
|
+
from typing import Protocol
|
|
6
|
+
|
|
7
|
+
from unidecompiler.plugins import FrontendDecodeError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
PE_MAGIC = b"MZ"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class DotNetDecodeError(FrontendDecodeError):
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True)
|
|
18
|
+
class DotNetInstruction:
|
|
19
|
+
offset: int
|
|
20
|
+
opcode: str
|
|
21
|
+
operands: str = ""
|
|
22
|
+
token: int | None = None
|
|
23
|
+
operand_kind: str | None = None
|
|
24
|
+
member_name: str | None = None
|
|
25
|
+
owner_name: str | None = None
|
|
26
|
+
arg_count: int | None = None
|
|
27
|
+
returns_void: bool | None = None
|
|
28
|
+
is_static: bool | None = None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass(frozen=True)
|
|
32
|
+
class DotNetMethodListing:
|
|
33
|
+
name: str
|
|
34
|
+
token: int
|
|
35
|
+
rva: int
|
|
36
|
+
is_static: bool = False
|
|
37
|
+
param_count: int = 0
|
|
38
|
+
max_stack: int | None = None
|
|
39
|
+
code_size: int = 0
|
|
40
|
+
instructions: tuple[DotNetInstruction, ...] = ()
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass(frozen=True)
|
|
44
|
+
class DotNetAssembly:
|
|
45
|
+
name: str
|
|
46
|
+
filename: str | None = None
|
|
47
|
+
methods: tuple[DotNetMethodListing, ...] = ()
|
|
48
|
+
decoder_id: str | None = None
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class DotNetAssemblyDecoder(Protocol):
|
|
52
|
+
id: str
|
|
53
|
+
|
|
54
|
+
def can_decode(self, data: bytes, filename: str | None = None) -> bool:
|
|
55
|
+
...
|
|
56
|
+
|
|
57
|
+
def decode(self, data: bytes, filename: str | None = None) -> DotNetAssembly:
|
|
58
|
+
...
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def looks_like_dotnet(data: bytes) -> bool:
|
|
62
|
+
if not data.startswith(PE_MAGIC):
|
|
63
|
+
return False
|
|
64
|
+
try:
|
|
65
|
+
import dnfile
|
|
66
|
+
|
|
67
|
+
pe = dnfile.dnPE(data=data)
|
|
68
|
+
return pe.net is not None
|
|
69
|
+
except Exception:
|
|
70
|
+
return False
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class DnfileAssemblyDecoder:
|
|
74
|
+
id = "dnfile"
|
|
75
|
+
|
|
76
|
+
def can_decode(self, data: bytes, filename: str | None = None) -> bool:
|
|
77
|
+
return looks_like_dotnet(data) and _dnfile_module() is not None
|
|
78
|
+
|
|
79
|
+
def decode(self, data: bytes, filename: str | None = None) -> DotNetAssembly:
|
|
80
|
+
dnfile = _dnfile_module()
|
|
81
|
+
if dnfile is None:
|
|
82
|
+
raise DotNetDecodeError("dnfile is not installed")
|
|
83
|
+
try:
|
|
84
|
+
pe = dnfile.dnPE(data=data)
|
|
85
|
+
except Exception as error: # pragma: no cover - parser owns details.
|
|
86
|
+
raise DotNetDecodeError(f"dnfile failed to decode assembly: {error}") from error
|
|
87
|
+
if pe.net is None:
|
|
88
|
+
raise DotNetDecodeError("missing .NET metadata")
|
|
89
|
+
return DotNetAssembly(
|
|
90
|
+
name=_assembly_name(pe, filename),
|
|
91
|
+
filename=filename,
|
|
92
|
+
methods=tuple(_method_listing(pe, row_index, row) for row_index, row in enumerate(_method_rows(pe), start=1)),
|
|
93
|
+
decoder_id=self.id,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _dnfile_module():
|
|
98
|
+
try:
|
|
99
|
+
import dnfile
|
|
100
|
+
except ImportError:
|
|
101
|
+
return None
|
|
102
|
+
return dnfile
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _method_rows(pe) -> tuple:
|
|
106
|
+
tables = getattr(pe.net, "mdtables", None)
|
|
107
|
+
method_def = getattr(tables, "MethodDef", None)
|
|
108
|
+
return tuple(getattr(method_def, "rows", ()) or ())
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _assembly_name(pe, filename: str | None) -> str:
|
|
112
|
+
tables = getattr(pe.net, "mdtables", None)
|
|
113
|
+
assembly = getattr(tables, "Assembly", None)
|
|
114
|
+
rows = getattr(assembly, "rows", ()) or ()
|
|
115
|
+
if rows:
|
|
116
|
+
name = _heap_text(rows[0].Name)
|
|
117
|
+
if name:
|
|
118
|
+
return name
|
|
119
|
+
if filename:
|
|
120
|
+
return filename
|
|
121
|
+
return "<dotnet-assembly>"
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _method_listing(pe, row_index: int, row) -> DotNetMethodListing:
|
|
125
|
+
rva = int(getattr(row, "Rva", 0) or 0)
|
|
126
|
+
body = _read_method_body(pe, rva) if rva else None
|
|
127
|
+
return DotNetMethodListing(
|
|
128
|
+
name=_heap_text(row.Name) or f"<method_{row_index}>",
|
|
129
|
+
token=0x06000000 | row_index,
|
|
130
|
+
rva=rva,
|
|
131
|
+
is_static=bool(getattr(row.Flags, "mdStatic", False)),
|
|
132
|
+
param_count=_signature_param_count(row.Signature),
|
|
133
|
+
max_stack=None if body is None else body.max_stack,
|
|
134
|
+
code_size=0 if body is None else len(body.code),
|
|
135
|
+
instructions=() if body is None else _decode_il(pe, body.code),
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@dataclass(frozen=True)
|
|
140
|
+
class _MethodBody:
|
|
141
|
+
max_stack: int
|
|
142
|
+
code: bytes
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _read_method_body(pe, rva: int) -> _MethodBody | None:
|
|
146
|
+
data = pe.get_data(rva, 16)
|
|
147
|
+
if not data:
|
|
148
|
+
return None
|
|
149
|
+
first = data[0]
|
|
150
|
+
kind = first & 0x3
|
|
151
|
+
if kind == 0x2:
|
|
152
|
+
code_size = first >> 2
|
|
153
|
+
return _MethodBody(max_stack=8, code=pe.get_data(rva + 1, code_size))
|
|
154
|
+
if kind == 0x3:
|
|
155
|
+
header = pe.get_data(rva, 12)
|
|
156
|
+
if len(header) < 12:
|
|
157
|
+
return None
|
|
158
|
+
_flags_size, max_stack, code_size, _local_sig = struct.unpack_from("<HHII", header, 0)
|
|
159
|
+
header_size = (_flags_size >> 12) * 4
|
|
160
|
+
if header_size < 12:
|
|
161
|
+
return None
|
|
162
|
+
return _MethodBody(max_stack=max_stack, code=pe.get_data(rva + header_size, code_size))
|
|
163
|
+
return None
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _heap_text(value) -> str | None:
|
|
167
|
+
if value is None:
|
|
168
|
+
return None
|
|
169
|
+
if isinstance(value, str):
|
|
170
|
+
return value
|
|
171
|
+
text = getattr(value, "value", None)
|
|
172
|
+
return text if isinstance(text, str) else None
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _signature_param_count(signature) -> int:
|
|
176
|
+
data = bytes(getattr(signature, "value", b"") or b"")
|
|
177
|
+
if not data:
|
|
178
|
+
return 0
|
|
179
|
+
reader = _BlobReader(data)
|
|
180
|
+
calling_convention = reader.byte()
|
|
181
|
+
if calling_convention & 0x10:
|
|
182
|
+
reader.compressed_uint()
|
|
183
|
+
if reader.eof:
|
|
184
|
+
return 0
|
|
185
|
+
return reader.compressed_uint()
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class _BlobReader:
|
|
189
|
+
def __init__(self, data: bytes) -> None:
|
|
190
|
+
self.data = data
|
|
191
|
+
self.offset = 0
|
|
192
|
+
|
|
193
|
+
@property
|
|
194
|
+
def eof(self) -> bool:
|
|
195
|
+
return self.offset >= len(self.data)
|
|
196
|
+
|
|
197
|
+
def byte(self) -> int:
|
|
198
|
+
if self.eof:
|
|
199
|
+
return 0
|
|
200
|
+
value = self.data[self.offset]
|
|
201
|
+
self.offset += 1
|
|
202
|
+
return value
|
|
203
|
+
|
|
204
|
+
def compressed_uint(self) -> int:
|
|
205
|
+
first = self.byte()
|
|
206
|
+
if first & 0x80 == 0:
|
|
207
|
+
return first
|
|
208
|
+
if first & 0xC0 == 0x80:
|
|
209
|
+
return ((first & 0x3F) << 8) | self.byte()
|
|
210
|
+
return ((first & 0x1F) << 24) | (self.byte() << 16) | (self.byte() << 8) | self.byte()
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
_ONE_BYTE_OPCODES = {
|
|
214
|
+
0x00: ("nop", "InlineNone"),
|
|
215
|
+
0x02: ("ldarg.0", "InlineNone"),
|
|
216
|
+
0x03: ("ldarg.1", "InlineNone"),
|
|
217
|
+
0x04: ("ldarg.2", "InlineNone"),
|
|
218
|
+
0x05: ("ldarg.3", "InlineNone"),
|
|
219
|
+
0x06: ("ldloc.0", "InlineNone"),
|
|
220
|
+
0x07: ("ldloc.1", "InlineNone"),
|
|
221
|
+
0x08: ("ldloc.2", "InlineNone"),
|
|
222
|
+
0x09: ("ldloc.3", "InlineNone"),
|
|
223
|
+
0x0A: ("stloc.0", "InlineNone"),
|
|
224
|
+
0x0B: ("stloc.1", "InlineNone"),
|
|
225
|
+
0x0C: ("stloc.2", "InlineNone"),
|
|
226
|
+
0x0D: ("stloc.3", "InlineNone"),
|
|
227
|
+
0x0E: ("ldarg.s", "ShortInlineVar"),
|
|
228
|
+
0x0F: ("ldarga.s", "ShortInlineVar"),
|
|
229
|
+
0x10: ("starg.s", "ShortInlineVar"),
|
|
230
|
+
0x11: ("ldloc.s", "ShortInlineVar"),
|
|
231
|
+
0x12: ("ldloca.s", "ShortInlineVar"),
|
|
232
|
+
0x13: ("stloc.s", "ShortInlineVar"),
|
|
233
|
+
0x14: ("ldnull", "InlineNone"),
|
|
234
|
+
0x15: ("ldc.i4.m1", "InlineNone"),
|
|
235
|
+
0x16: ("ldc.i4.0", "InlineNone"),
|
|
236
|
+
0x17: ("ldc.i4.1", "InlineNone"),
|
|
237
|
+
0x18: ("ldc.i4.2", "InlineNone"),
|
|
238
|
+
0x19: ("ldc.i4.3", "InlineNone"),
|
|
239
|
+
0x1A: ("ldc.i4.4", "InlineNone"),
|
|
240
|
+
0x1B: ("ldc.i4.5", "InlineNone"),
|
|
241
|
+
0x1C: ("ldc.i4.6", "InlineNone"),
|
|
242
|
+
0x1D: ("ldc.i4.7", "InlineNone"),
|
|
243
|
+
0x1E: ("ldc.i4.8", "InlineNone"),
|
|
244
|
+
0x1F: ("ldc.i4.s", "ShortInlineI"),
|
|
245
|
+
0x20: ("ldc.i4", "InlineI"),
|
|
246
|
+
0x21: ("ldc.i8", "InlineI8"),
|
|
247
|
+
0x22: ("ldc.r4", "ShortInlineR"),
|
|
248
|
+
0x23: ("ldc.r8", "InlineR"),
|
|
249
|
+
0x25: ("dup", "InlineNone"),
|
|
250
|
+
0x26: ("pop", "InlineNone"),
|
|
251
|
+
0x27: ("jmp", "InlineMethod"),
|
|
252
|
+
0x28: ("call", "InlineMethod"),
|
|
253
|
+
0x2A: ("ret", "InlineNone"),
|
|
254
|
+
0x2B: ("br.s", "ShortInlineBrTarget"),
|
|
255
|
+
0x2C: ("brfalse.s", "ShortInlineBrTarget"),
|
|
256
|
+
0x2D: ("brtrue.s", "ShortInlineBrTarget"),
|
|
257
|
+
0x2E: ("beq.s", "ShortInlineBrTarget"),
|
|
258
|
+
0x2F: ("bge.s", "ShortInlineBrTarget"),
|
|
259
|
+
0x30: ("bgt.s", "ShortInlineBrTarget"),
|
|
260
|
+
0x31: ("ble.s", "ShortInlineBrTarget"),
|
|
261
|
+
0x32: ("blt.s", "ShortInlineBrTarget"),
|
|
262
|
+
0x33: ("bne.un.s", "ShortInlineBrTarget"),
|
|
263
|
+
0x34: ("bge.un.s", "ShortInlineBrTarget"),
|
|
264
|
+
0x35: ("bgt.un.s", "ShortInlineBrTarget"),
|
|
265
|
+
0x36: ("ble.un.s", "ShortInlineBrTarget"),
|
|
266
|
+
0x37: ("blt.un.s", "ShortInlineBrTarget"),
|
|
267
|
+
0x38: ("br", "InlineBrTarget"),
|
|
268
|
+
0x39: ("brfalse", "InlineBrTarget"),
|
|
269
|
+
0x3A: ("brtrue", "InlineBrTarget"),
|
|
270
|
+
0x3B: ("beq", "InlineBrTarget"),
|
|
271
|
+
0x3C: ("bge", "InlineBrTarget"),
|
|
272
|
+
0x3D: ("bgt", "InlineBrTarget"),
|
|
273
|
+
0x3E: ("ble", "InlineBrTarget"),
|
|
274
|
+
0x3F: ("blt", "InlineBrTarget"),
|
|
275
|
+
0x40: ("bne.un", "InlineBrTarget"),
|
|
276
|
+
0x41: ("bge.un", "InlineBrTarget"),
|
|
277
|
+
0x42: ("bgt.un", "InlineBrTarget"),
|
|
278
|
+
0x43: ("ble.un", "InlineBrTarget"),
|
|
279
|
+
0x44: ("blt.un", "InlineBrTarget"),
|
|
280
|
+
0x45: ("switch", "InlineSwitch"),
|
|
281
|
+
0x46: ("ldind.i1", "InlineNone"),
|
|
282
|
+
0x47: ("ldind.u1", "InlineNone"),
|
|
283
|
+
0x48: ("ldind.i2", "InlineNone"),
|
|
284
|
+
0x49: ("ldind.u2", "InlineNone"),
|
|
285
|
+
0x4A: ("ldind.i4", "InlineNone"),
|
|
286
|
+
0x4B: ("ldind.u4", "InlineNone"),
|
|
287
|
+
0x4C: ("ldind.i8", "InlineNone"),
|
|
288
|
+
0x4D: ("ldind.i", "InlineNone"),
|
|
289
|
+
0x4E: ("ldind.r4", "InlineNone"),
|
|
290
|
+
0x4F: ("ldind.r8", "InlineNone"),
|
|
291
|
+
0x50: ("ldind.ref", "InlineNone"),
|
|
292
|
+
0x51: ("stind.ref", "InlineNone"),
|
|
293
|
+
0x52: ("stind.i1", "InlineNone"),
|
|
294
|
+
0x53: ("stind.i2", "InlineNone"),
|
|
295
|
+
0x54: ("stind.i4", "InlineNone"),
|
|
296
|
+
0x55: ("stind.i8", "InlineNone"),
|
|
297
|
+
0x56: ("stind.r4", "InlineNone"),
|
|
298
|
+
0x57: ("stind.r8", "InlineNone"),
|
|
299
|
+
0x58: ("add", "InlineNone"),
|
|
300
|
+
0x59: ("sub", "InlineNone"),
|
|
301
|
+
0x5A: ("mul", "InlineNone"),
|
|
302
|
+
0x5B: ("div", "InlineNone"),
|
|
303
|
+
0x5C: ("div.un", "InlineNone"),
|
|
304
|
+
0x5D: ("rem", "InlineNone"),
|
|
305
|
+
0x5E: ("rem.un", "InlineNone"),
|
|
306
|
+
0x5F: ("and", "InlineNone"),
|
|
307
|
+
0x60: ("or", "InlineNone"),
|
|
308
|
+
0x61: ("xor", "InlineNone"),
|
|
309
|
+
0x62: ("shl", "InlineNone"),
|
|
310
|
+
0x63: ("shr", "InlineNone"),
|
|
311
|
+
0x64: ("shr.un", "InlineNone"),
|
|
312
|
+
0x65: ("neg", "InlineNone"),
|
|
313
|
+
0x66: ("not", "InlineNone"),
|
|
314
|
+
0x67: ("conv.i1", "InlineNone"),
|
|
315
|
+
0x68: ("conv.i2", "InlineNone"),
|
|
316
|
+
0x69: ("conv.i4", "InlineNone"),
|
|
317
|
+
0x6A: ("conv.i8", "InlineNone"),
|
|
318
|
+
0x6B: ("conv.r4", "InlineNone"),
|
|
319
|
+
0x6C: ("conv.r8", "InlineNone"),
|
|
320
|
+
0x6D: ("conv.u4", "InlineNone"),
|
|
321
|
+
0x6E: ("conv.u8", "InlineNone"),
|
|
322
|
+
0x6F: ("callvirt", "InlineMethod"),
|
|
323
|
+
0x70: ("cpobj", "InlineType"),
|
|
324
|
+
0x71: ("ldobj", "InlineType"),
|
|
325
|
+
0x72: ("ldstr", "InlineString"),
|
|
326
|
+
0x73: ("newobj", "InlineMethod"),
|
|
327
|
+
0x74: ("castclass", "InlineType"),
|
|
328
|
+
0x75: ("isinst", "InlineType"),
|
|
329
|
+
0x76: ("conv.r.un", "InlineNone"),
|
|
330
|
+
0x79: ("unbox", "InlineType"),
|
|
331
|
+
0x7A: ("throw", "InlineNone"),
|
|
332
|
+
0x7B: ("ldfld", "InlineField"),
|
|
333
|
+
0x7C: ("ldflda", "InlineField"),
|
|
334
|
+
0x7D: ("stfld", "InlineField"),
|
|
335
|
+
0x7E: ("ldsfld", "InlineField"),
|
|
336
|
+
0x7F: ("ldsflda", "InlineField"),
|
|
337
|
+
0x80: ("stsfld", "InlineField"),
|
|
338
|
+
0x81: ("ldelema", "InlineType"),
|
|
339
|
+
0x8C: ("box", "InlineType"),
|
|
340
|
+
0x8D: ("newarr", "InlineType"),
|
|
341
|
+
0x8E: ("ldlen", "InlineNone"),
|
|
342
|
+
0x8F: ("ldelema", "InlineType"),
|
|
343
|
+
0x90: ("ldelem.i1", "InlineNone"),
|
|
344
|
+
0x91: ("ldelem.u1", "InlineNone"),
|
|
345
|
+
0x92: ("ldelem.i2", "InlineNone"),
|
|
346
|
+
0x93: ("ldelem.u2", "InlineNone"),
|
|
347
|
+
0x94: ("ldelem.i4", "InlineNone"),
|
|
348
|
+
0x95: ("ldelem.u4", "InlineNone"),
|
|
349
|
+
0x96: ("ldelem.i8", "InlineNone"),
|
|
350
|
+
0x97: ("ldelem.i", "InlineNone"),
|
|
351
|
+
0x98: ("ldelem.r4", "InlineNone"),
|
|
352
|
+
0x99: ("ldelem.r8", "InlineNone"),
|
|
353
|
+
0x9A: ("ldelem.ref", "InlineNone"),
|
|
354
|
+
0x9B: ("stelem.i", "InlineNone"),
|
|
355
|
+
0x9C: ("stelem.i1", "InlineNone"),
|
|
356
|
+
0x9D: ("stelem.i2", "InlineNone"),
|
|
357
|
+
0x9E: ("stelem.i4", "InlineNone"),
|
|
358
|
+
0x9F: ("stelem.i8", "InlineNone"),
|
|
359
|
+
0xA0: ("stelem.r4", "InlineNone"),
|
|
360
|
+
0xA1: ("stelem.r8", "InlineNone"),
|
|
361
|
+
0xA2: ("stelem.ref", "InlineNone"),
|
|
362
|
+
0xA3: ("ldelem", "InlineType"),
|
|
363
|
+
0xA4: ("stelem", "InlineType"),
|
|
364
|
+
0xA5: ("unbox.any", "InlineType"),
|
|
365
|
+
0xB6: ("tail.", "InlineNone"),
|
|
366
|
+
0xB7: ("conv.u2", "InlineNone"),
|
|
367
|
+
0xB8: ("conv.u1", "InlineNone"),
|
|
368
|
+
0xB9: ("conv.i", "InlineNone"),
|
|
369
|
+
0xBA: ("conv.ovf.i", "InlineNone"),
|
|
370
|
+
0xBB: ("conv.ovf.u", "InlineNone"),
|
|
371
|
+
0xC2: ("refanyval", "InlineType"),
|
|
372
|
+
0xC3: ("ckfinite", "InlineNone"),
|
|
373
|
+
0xC6: ("mkrefany", "InlineType"),
|
|
374
|
+
0xD0: ("ldtoken", "InlineTok"),
|
|
375
|
+
0xD1: ("conv.u", "InlineNone"),
|
|
376
|
+
0xD2: ("add.ovf", "InlineNone"),
|
|
377
|
+
0xD3: ("add.ovf.un", "InlineNone"),
|
|
378
|
+
0xD4: ("mul.ovf", "InlineNone"),
|
|
379
|
+
0xD5: ("mul.ovf.un", "InlineNone"),
|
|
380
|
+
0xD6: ("sub.ovf", "InlineNone"),
|
|
381
|
+
0xD7: ("sub.ovf.un", "InlineNone"),
|
|
382
|
+
0xD8: ("endfinally", "InlineNone"),
|
|
383
|
+
0xDC: ("endfilter", "InlineNone"),
|
|
384
|
+
0xDD: ("leave", "InlineBrTarget"),
|
|
385
|
+
0xDE: ("leave.s", "ShortInlineBrTarget"),
|
|
386
|
+
0xDF: ("stind.i", "InlineNone"),
|
|
387
|
+
0xE0: ("conv.ovf.i1.un", "InlineNone"),
|
|
388
|
+
0xE1: ("conv.ovf.i2.un", "InlineNone"),
|
|
389
|
+
0xE2: ("conv.ovf.i4.un", "InlineNone"),
|
|
390
|
+
0xE3: ("conv.ovf.i8.un", "InlineNone"),
|
|
391
|
+
0xE4: ("conv.ovf.u1.un", "InlineNone"),
|
|
392
|
+
0xE5: ("conv.ovf.u2.un", "InlineNone"),
|
|
393
|
+
0xE6: ("conv.ovf.u4.un", "InlineNone"),
|
|
394
|
+
0xE7: ("conv.ovf.u8.un", "InlineNone"),
|
|
395
|
+
0xE8: ("conv.ovf.i.un", "InlineNone"),
|
|
396
|
+
0xE9: ("conv.ovf.u.un", "InlineNone"),
|
|
397
|
+
0xFE: ("prefix", "Prefix"),
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
_TWO_BYTE_OPCODES = {
|
|
401
|
+
0x00: ("arglist", "InlineNone"),
|
|
402
|
+
0x01: ("ceq", "InlineNone"),
|
|
403
|
+
0x02: ("cgt", "InlineNone"),
|
|
404
|
+
0x03: ("cgt.un", "InlineNone"),
|
|
405
|
+
0x04: ("clt", "InlineNone"),
|
|
406
|
+
0x05: ("clt.un", "InlineNone"),
|
|
407
|
+
0x06: ("ldftn", "InlineMethod"),
|
|
408
|
+
0x07: ("ldvirtftn", "InlineMethod"),
|
|
409
|
+
0x09: ("ldarg", "InlineVar"),
|
|
410
|
+
0x0A: ("ldarga", "InlineVar"),
|
|
411
|
+
0x0B: ("starg", "InlineVar"),
|
|
412
|
+
0x0C: ("ldloc", "InlineVar"),
|
|
413
|
+
0x0D: ("ldloca", "InlineVar"),
|
|
414
|
+
0x0E: ("stloc", "InlineVar"),
|
|
415
|
+
0x0F: ("localloc", "InlineNone"),
|
|
416
|
+
0x11: ("endfilter", "InlineNone"),
|
|
417
|
+
0x12: ("unaligned.", "ShortInlineI"),
|
|
418
|
+
0x13: ("volatile.", "InlineNone"),
|
|
419
|
+
0x14: ("tail.", "InlineNone"),
|
|
420
|
+
0x15: ("initobj", "InlineType"),
|
|
421
|
+
0x16: ("constrained.", "InlineType"),
|
|
422
|
+
0x17: ("cpblk", "InlineNone"),
|
|
423
|
+
0x18: ("initblk", "InlineNone"),
|
|
424
|
+
0x1A: ("rethrow", "InlineNone"),
|
|
425
|
+
0x1C: ("sizeof", "InlineType"),
|
|
426
|
+
0x1D: ("refanytype", "InlineNone"),
|
|
427
|
+
0x1E: ("readonly.", "InlineNone"),
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
def _decode_il(pe, code: bytes) -> tuple[DotNetInstruction, ...]:
|
|
432
|
+
instructions: list[DotNetInstruction] = []
|
|
433
|
+
offset = 0
|
|
434
|
+
while offset < len(code):
|
|
435
|
+
start = offset
|
|
436
|
+
op = code[offset]
|
|
437
|
+
offset += 1
|
|
438
|
+
if op == 0xFE and offset < len(code):
|
|
439
|
+
op2 = code[offset]
|
|
440
|
+
offset += 1
|
|
441
|
+
opcode, operand_kind = _TWO_BYTE_OPCODES.get(op2, (f"unknown.fe{op2:02x}", "InlineNone"))
|
|
442
|
+
else:
|
|
443
|
+
opcode, operand_kind = _ONE_BYTE_OPCODES.get(op, (f"unknown.{op:02x}", "InlineNone"))
|
|
444
|
+
operand, token, offset = _read_operand(pe, code, offset, start, operand_kind)
|
|
445
|
+
instructions.append(
|
|
446
|
+
DotNetInstruction(
|
|
447
|
+
offset=start,
|
|
448
|
+
opcode=opcode,
|
|
449
|
+
operands=operand.text,
|
|
450
|
+
token=token,
|
|
451
|
+
operand_kind=operand_kind,
|
|
452
|
+
member_name=operand.member_name,
|
|
453
|
+
owner_name=operand.owner_name,
|
|
454
|
+
arg_count=operand.arg_count,
|
|
455
|
+
returns_void=operand.returns_void,
|
|
456
|
+
is_static=operand.is_static,
|
|
457
|
+
)
|
|
458
|
+
)
|
|
459
|
+
return tuple(instructions)
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
@dataclass(frozen=True)
|
|
463
|
+
class _DecodedOperand:
|
|
464
|
+
text: str = ""
|
|
465
|
+
member_name: str | None = None
|
|
466
|
+
owner_name: str | None = None
|
|
467
|
+
arg_count: int | None = None
|
|
468
|
+
returns_void: bool | None = None
|
|
469
|
+
is_static: bool | None = None
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def _read_operand(pe, code: bytes, offset: int, instruction_offset: int, kind: str) -> tuple[_DecodedOperand, int | None, int]:
|
|
473
|
+
if kind in {"InlineNone", "Prefix"}:
|
|
474
|
+
return _DecodedOperand(), None, offset
|
|
475
|
+
if kind in {"ShortInlineI", "ShortInlineVar"}:
|
|
476
|
+
if offset >= len(code):
|
|
477
|
+
return _DecodedOperand(), None, offset
|
|
478
|
+
value = struct.unpack_from("<b" if kind == "ShortInlineI" else "<B", code, offset)[0]
|
|
479
|
+
return _DecodedOperand(str(value)), None, offset + 1
|
|
480
|
+
if kind == "InlineVar":
|
|
481
|
+
if offset + 2 > len(code):
|
|
482
|
+
return _DecodedOperand(), None, offset
|
|
483
|
+
return _DecodedOperand(str(struct.unpack_from("<H", code, offset)[0])), None, offset + 2
|
|
484
|
+
if kind == "InlineI":
|
|
485
|
+
if offset + 4 > len(code):
|
|
486
|
+
return _DecodedOperand(), None, offset
|
|
487
|
+
return _DecodedOperand(str(struct.unpack_from("<i", code, offset)[0])), None, offset + 4
|
|
488
|
+
if kind == "InlineI8":
|
|
489
|
+
if offset + 8 > len(code):
|
|
490
|
+
return _DecodedOperand(), None, offset
|
|
491
|
+
return _DecodedOperand(str(struct.unpack_from("<q", code, offset)[0])), None, offset + 8
|
|
492
|
+
if kind == "ShortInlineR":
|
|
493
|
+
if offset + 4 > len(code):
|
|
494
|
+
return _DecodedOperand(), None, offset
|
|
495
|
+
return _DecodedOperand(str(struct.unpack_from("<f", code, offset)[0])), None, offset + 4
|
|
496
|
+
if kind == "InlineR":
|
|
497
|
+
if offset + 8 > len(code):
|
|
498
|
+
return _DecodedOperand(), None, offset
|
|
499
|
+
return _DecodedOperand(str(struct.unpack_from("<d", code, offset)[0])), None, offset + 8
|
|
500
|
+
if kind == "ShortInlineBrTarget":
|
|
501
|
+
if offset >= len(code):
|
|
502
|
+
return _DecodedOperand(), None, offset
|
|
503
|
+
delta = struct.unpack_from("<b", code, offset)[0]
|
|
504
|
+
return _DecodedOperand(str(offset + 1 + delta)), None, offset + 1
|
|
505
|
+
if kind == "InlineBrTarget":
|
|
506
|
+
if offset + 4 > len(code):
|
|
507
|
+
return _DecodedOperand(), None, offset
|
|
508
|
+
delta = struct.unpack_from("<i", code, offset)[0]
|
|
509
|
+
return _DecodedOperand(str(offset + 4 + delta)), None, offset + 4
|
|
510
|
+
if kind == "InlineSwitch":
|
|
511
|
+
if offset + 4 > len(code):
|
|
512
|
+
return _DecodedOperand(), None, offset
|
|
513
|
+
count = struct.unpack_from("<I", code, offset)[0]
|
|
514
|
+
offset += 4
|
|
515
|
+
base = offset + count * 4
|
|
516
|
+
targets: list[str] = []
|
|
517
|
+
for _ in range(count):
|
|
518
|
+
if offset + 4 > len(code):
|
|
519
|
+
break
|
|
520
|
+
targets.append(str(base + struct.unpack_from("<i", code, offset)[0]))
|
|
521
|
+
offset += 4
|
|
522
|
+
return _DecodedOperand(",".join(targets)), None, offset
|
|
523
|
+
if kind in {"InlineMethod", "InlineField", "InlineType", "InlineTok", "InlineString", "InlineSig"}:
|
|
524
|
+
if offset + 4 > len(code):
|
|
525
|
+
return _DecodedOperand(), None, offset
|
|
526
|
+
token = struct.unpack_from("<I", code, offset)[0]
|
|
527
|
+
return _resolve_metadata_operand(pe, token, kind), token, offset + 4
|
|
528
|
+
return _DecodedOperand(), None, offset
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
def _resolve_metadata_operand(pe, token: int, kind: str) -> _DecodedOperand:
|
|
532
|
+
if kind == "InlineString":
|
|
533
|
+
value = _user_string(pe, token)
|
|
534
|
+
return _DecodedOperand(value if value is not None else f"0x{token:08x}")
|
|
535
|
+
table_id = token >> 24
|
|
536
|
+
row_index = token & 0x00FFFFFF
|
|
537
|
+
row = _metadata_row(pe, table_id, row_index)
|
|
538
|
+
if row is None:
|
|
539
|
+
return _DecodedOperand(f"0x{token:08x}")
|
|
540
|
+
if table_id == 0x06:
|
|
541
|
+
name = _heap_text(getattr(row, "Name", None)) or f"0x{token:08x}"
|
|
542
|
+
owner = _owner_for_method(pe, row_index)
|
|
543
|
+
arg_count, returns_void, _has_this = _signature_shape(getattr(row, "Signature", None))
|
|
544
|
+
is_static = bool(getattr(row.Flags, "mdStatic", False))
|
|
545
|
+
return _DecodedOperand(_qualified_member(owner, name), name, owner, arg_count, returns_void, is_static)
|
|
546
|
+
if table_id == 0x0A:
|
|
547
|
+
name = _heap_text(getattr(row, "Name", None)) or f"0x{token:08x}"
|
|
548
|
+
owner = _type_name_from_coded(getattr(row, "Class", None)) or "<member>"
|
|
549
|
+
arg_count, returns_void, has_this = _signature_shape(getattr(row, "Signature", None))
|
|
550
|
+
return _DecodedOperand(_qualified_member(owner, name), name, owner, arg_count, returns_void, not has_this)
|
|
551
|
+
if table_id == 0x2B:
|
|
552
|
+
method = _methodspec_target(pe, row_index)
|
|
553
|
+
if method is None:
|
|
554
|
+
return _DecodedOperand(f"0x{token:08x}")
|
|
555
|
+
return method
|
|
556
|
+
if table_id == 0x04:
|
|
557
|
+
name = _heap_text(getattr(row, "Name", None)) or f"0x{token:08x}"
|
|
558
|
+
owner = _owner_for_field(pe, row_index)
|
|
559
|
+
return _DecodedOperand(_qualified_member(owner, name), name, owner)
|
|
560
|
+
if table_id in {0x01, 0x02}:
|
|
561
|
+
name = _type_name(row) or f"0x{token:08x}"
|
|
562
|
+
return _DecodedOperand(name, owner_name=name)
|
|
563
|
+
return _DecodedOperand(f"0x{token:08x}")
|
|
564
|
+
|
|
565
|
+
|
|
566
|
+
def _metadata_row(pe, table_id: int, row_index: int):
|
|
567
|
+
table_names = {
|
|
568
|
+
0x01: "TypeRef",
|
|
569
|
+
0x02: "TypeDef",
|
|
570
|
+
0x04: "Field",
|
|
571
|
+
0x06: "MethodDef",
|
|
572
|
+
0x0A: "MemberRef",
|
|
573
|
+
0x2B: "MethodSpec",
|
|
574
|
+
}
|
|
575
|
+
table = getattr(getattr(pe.net, "mdtables", None), table_names.get(table_id, ""), None)
|
|
576
|
+
rows = getattr(table, "rows", ()) or ()
|
|
577
|
+
if row_index <= 0 or row_index > len(rows):
|
|
578
|
+
return None
|
|
579
|
+
return rows[row_index - 1]
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
def _user_string(pe, token: int) -> str | None:
|
|
583
|
+
heap = getattr(pe.net, "user_strings", None)
|
|
584
|
+
if heap is None:
|
|
585
|
+
return None
|
|
586
|
+
value = heap.get(token & 0x00FFFFFF)
|
|
587
|
+
return _heap_text(value)
|
|
588
|
+
|
|
589
|
+
|
|
590
|
+
def _type_name(row) -> str | None:
|
|
591
|
+
name = _heap_text(getattr(row, "TypeName", None))
|
|
592
|
+
namespace = _heap_text(getattr(row, "TypeNamespace", None))
|
|
593
|
+
if name and namespace:
|
|
594
|
+
return f"{namespace}.{name}"
|
|
595
|
+
return name
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
def _qualified_member(owner: str | None, name: str) -> str:
|
|
599
|
+
return f"{owner}.{name}" if owner else name
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
def _type_name_from_coded(value) -> str | None:
|
|
603
|
+
row = getattr(value, "row", None)
|
|
604
|
+
return _type_name(row)
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
def _owner_for_method(pe, row_index: int) -> str | None:
|
|
608
|
+
typedef = getattr(getattr(pe.net, "mdtables", None), "TypeDef", None)
|
|
609
|
+
for row in getattr(typedef, "rows", ()) or ():
|
|
610
|
+
method_indices = [getattr(index, "row_index", None) for index in getattr(row, "MethodList", ()) or ()]
|
|
611
|
+
if row_index in method_indices:
|
|
612
|
+
return _type_name(row)
|
|
613
|
+
return None
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
def _owner_for_field(pe, row_index: int) -> str | None:
|
|
617
|
+
typedef = getattr(getattr(pe.net, "mdtables", None), "TypeDef", None)
|
|
618
|
+
for row in getattr(typedef, "rows", ()) or ():
|
|
619
|
+
field_indices = [getattr(index, "row_index", None) for index in getattr(row, "FieldList", ()) or ()]
|
|
620
|
+
if row_index in field_indices:
|
|
621
|
+
return _type_name(row)
|
|
622
|
+
return None
|
|
623
|
+
|
|
624
|
+
|
|
625
|
+
def _methodspec_target(pe, row_index: int) -> _DecodedOperand | None:
|
|
626
|
+
methodspec = getattr(getattr(pe.net, "mdtables", None), "MethodSpec", None)
|
|
627
|
+
rows = getattr(methodspec, "rows", ()) or ()
|
|
628
|
+
if row_index <= 0 or row_index > len(rows):
|
|
629
|
+
return None
|
|
630
|
+
row = rows[row_index - 1]
|
|
631
|
+
method_ref = getattr(row, "Method", None)
|
|
632
|
+
if method_ref is None:
|
|
633
|
+
return None
|
|
634
|
+
target_row = getattr(method_ref, "table", None)
|
|
635
|
+
target_index = getattr(method_ref, "row_index", None)
|
|
636
|
+
if target_row is None or target_index is None:
|
|
637
|
+
return None
|
|
638
|
+
if target_row.__class__.__name__ == "MemberRef":
|
|
639
|
+
member_ref = _metadata_row(pe, 0x0A, target_index)
|
|
640
|
+
if member_ref is None:
|
|
641
|
+
return None
|
|
642
|
+
name = _heap_text(getattr(member_ref, "Name", None)) or f"0x{row_index:08x}"
|
|
643
|
+
owner = _type_name_from_coded(getattr(member_ref, "Class", None)) or "<member>"
|
|
644
|
+
arg_count, returns_void, has_this = _signature_shape(getattr(member_ref, "Signature", None))
|
|
645
|
+
return _DecodedOperand(_qualified_member(owner, name), name, owner, arg_count, returns_void, not has_this)
|
|
646
|
+
if target_row.__class__.__name__ == "MethodDef":
|
|
647
|
+
method_def = _metadata_row(pe, 0x06, target_index)
|
|
648
|
+
if method_def is None:
|
|
649
|
+
return None
|
|
650
|
+
name = _heap_text(getattr(method_def, "Name", None)) or f"0x{row_index:08x}"
|
|
651
|
+
owner = _owner_for_method(pe, target_index)
|
|
652
|
+
arg_count, returns_void, _has_this = _signature_shape(getattr(method_def, "Signature", None))
|
|
653
|
+
is_static = bool(getattr(method_def.Flags, "mdStatic", False))
|
|
654
|
+
return _DecodedOperand(_qualified_member(owner, name), name, owner, arg_count, returns_void, is_static)
|
|
655
|
+
return None
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
def _signature_shape(signature) -> tuple[int, bool | None, bool]:
|
|
659
|
+
data = bytes(getattr(signature, "value", b"") or b"")
|
|
660
|
+
if not data:
|
|
661
|
+
return 0, None, False
|
|
662
|
+
reader = _BlobReader(data)
|
|
663
|
+
calling_convention = reader.byte()
|
|
664
|
+
has_this = bool(calling_convention & 0x20)
|
|
665
|
+
is_generic = bool(calling_convention & 0x10)
|
|
666
|
+
calling_convention &= 0x0F
|
|
667
|
+
if calling_convention == 0x06:
|
|
668
|
+
return 0, None, has_this
|
|
669
|
+
if is_generic:
|
|
670
|
+
reader.compressed_uint()
|
|
671
|
+
if reader.eof:
|
|
672
|
+
return 0, None, has_this
|
|
673
|
+
param_count = reader.compressed_uint()
|
|
674
|
+
return_type = reader.byte()
|
|
675
|
+
return param_count, return_type == 0x01, has_this
|