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,686 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from unidecompiler.core.effects import (
|
|
4
|
+
Binary,
|
|
5
|
+
BuildArrayCall,
|
|
6
|
+
BuildCall,
|
|
7
|
+
CallTopAs,
|
|
8
|
+
DuplicateTop,
|
|
9
|
+
InvokeMember,
|
|
10
|
+
LoadIndirect,
|
|
11
|
+
LoadAttr,
|
|
12
|
+
LoadItem,
|
|
13
|
+
LoadItemAddress,
|
|
14
|
+
LoadLocal,
|
|
15
|
+
Pop,
|
|
16
|
+
Push,
|
|
17
|
+
RaiseTop,
|
|
18
|
+
ReturnTop,
|
|
19
|
+
StoreAttr,
|
|
20
|
+
StoreIndirect,
|
|
21
|
+
StoreItemEffect,
|
|
22
|
+
StoreLocal,
|
|
23
|
+
StoreStaticMember,
|
|
24
|
+
UnknownOpcode,
|
|
25
|
+
)
|
|
26
|
+
from unidecompiler.core.ir import BinaryOp, Const, Expr, Global, IndirectRef, SourceRef, Var
|
|
27
|
+
from unidecompiler.core.vm_bytecode import VMBytecodeStep
|
|
28
|
+
from unidecompiler.core.vm_effect_table import VMEffectRule, VMEffectTable
|
|
29
|
+
from unidecompiler.core.vm_function import VMFunctionSpec, lift_steps, lift_vm_step_function, recover_vm_function
|
|
30
|
+
from unidecompiler.core.vm_hints import VMHint
|
|
31
|
+
from unidecompiler.core.vm_module import assemble_vm_module
|
|
32
|
+
from unidecompiler.core.vm_operands import VMDecodedInstruction, VMOperand
|
|
33
|
+
from unidecompiler.core.vm_region import (
|
|
34
|
+
VMLinearState,
|
|
35
|
+
VMRegionOpcodeClasses,
|
|
36
|
+
VMRegionProfile,
|
|
37
|
+
VMStatefulCallbacks,
|
|
38
|
+
build_hint_region_profile,
|
|
39
|
+
)
|
|
40
|
+
from unidecompiler_plugin_dotnet_cli.assembly import (
|
|
41
|
+
DotNetAssembly,
|
|
42
|
+
DotNetInstruction,
|
|
43
|
+
DotNetMethodListing,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
DOTNET_FRONTEND_ID = "dotnet-cli"
|
|
48
|
+
|
|
49
|
+
BINARY_OPS = {
|
|
50
|
+
"add": "+",
|
|
51
|
+
"add.ovf": "+",
|
|
52
|
+
"add.ovf.un": "+",
|
|
53
|
+
"sub": "-",
|
|
54
|
+
"sub.ovf": "-",
|
|
55
|
+
"sub.ovf.un": "-",
|
|
56
|
+
"mul": "*",
|
|
57
|
+
"mul.ovf": "*",
|
|
58
|
+
"mul.ovf.un": "*",
|
|
59
|
+
"div": "/",
|
|
60
|
+
"div.un": "/",
|
|
61
|
+
"rem": "%",
|
|
62
|
+
"rem.un": "%",
|
|
63
|
+
"and": "&",
|
|
64
|
+
"or": "|",
|
|
65
|
+
"xor": "^",
|
|
66
|
+
"shl": "<<",
|
|
67
|
+
"shr": ">>",
|
|
68
|
+
"shr.un": ">>",
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
COMPARE_OPS = {
|
|
72
|
+
"ceq": "==",
|
|
73
|
+
"cgt": ">",
|
|
74
|
+
"cgt.un": ">",
|
|
75
|
+
"clt": "<",
|
|
76
|
+
"clt.un": "<",
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
CONTROL_OPS = {
|
|
80
|
+
"br",
|
|
81
|
+
"br.s",
|
|
82
|
+
"brfalse",
|
|
83
|
+
"brfalse.s",
|
|
84
|
+
"brtrue",
|
|
85
|
+
"brtrue.s",
|
|
86
|
+
"beq",
|
|
87
|
+
"beq.s",
|
|
88
|
+
"bge",
|
|
89
|
+
"bge.s",
|
|
90
|
+
"bge.un",
|
|
91
|
+
"bge.un.s",
|
|
92
|
+
"bgt",
|
|
93
|
+
"bgt.s",
|
|
94
|
+
"bgt.un",
|
|
95
|
+
"bgt.un.s",
|
|
96
|
+
"ble",
|
|
97
|
+
"ble.s",
|
|
98
|
+
"ble.un",
|
|
99
|
+
"ble.un.s",
|
|
100
|
+
"blt",
|
|
101
|
+
"blt.s",
|
|
102
|
+
"blt.un",
|
|
103
|
+
"blt.un.s",
|
|
104
|
+
"bne.un",
|
|
105
|
+
"bne.un.s",
|
|
106
|
+
"leave",
|
|
107
|
+
"leave.s",
|
|
108
|
+
"switch",
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
DOTNET_CONDITIONAL_OPS = {
|
|
112
|
+
"brfalse": "truthy",
|
|
113
|
+
"brfalse.s": "truthy",
|
|
114
|
+
"brtrue": "falsey",
|
|
115
|
+
"brtrue.s": "falsey",
|
|
116
|
+
"beq": "!=",
|
|
117
|
+
"beq.s": "!=",
|
|
118
|
+
"bne.un": "==",
|
|
119
|
+
"bne.un.s": "==",
|
|
120
|
+
"bge": "<",
|
|
121
|
+
"bge.s": "<",
|
|
122
|
+
"bge.un": "<",
|
|
123
|
+
"bge.un.s": "<",
|
|
124
|
+
"bgt": "<=",
|
|
125
|
+
"bgt.s": "<=",
|
|
126
|
+
"bgt.un": "<=",
|
|
127
|
+
"bgt.un.s": "<=",
|
|
128
|
+
"ble": ">",
|
|
129
|
+
"ble.s": ">",
|
|
130
|
+
"ble.un": ">",
|
|
131
|
+
"ble.un.s": ">",
|
|
132
|
+
"blt": ">=",
|
|
133
|
+
"blt.s": ">=",
|
|
134
|
+
"blt.un": ">=",
|
|
135
|
+
"blt.un.s": ">=",
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
DOTNET_REGION_OPCODE_CLASSES = VMRegionOpcodeClasses(
|
|
139
|
+
control=frozenset(CONTROL_OPS),
|
|
140
|
+
jumps=frozenset({"br", "br.s", "leave", "leave.s"}),
|
|
141
|
+
forward_jumps=frozenset({"br", "br.s", "leave", "leave.s"}),
|
|
142
|
+
backward_jumps=frozenset({"br", "br.s", "leave", "leave.s"}),
|
|
143
|
+
conditional_jumps=frozenset(DOTNET_CONDITIONAL_OPS),
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
IGNORED_OPS = {
|
|
147
|
+
"nop",
|
|
148
|
+
"constrained.",
|
|
149
|
+
"readonly.",
|
|
150
|
+
"tail.",
|
|
151
|
+
"volatile.",
|
|
152
|
+
"unaligned.",
|
|
153
|
+
"prefix",
|
|
154
|
+
"castclass",
|
|
155
|
+
"box",
|
|
156
|
+
"unbox",
|
|
157
|
+
"unbox.any",
|
|
158
|
+
"conv.i",
|
|
159
|
+
"conv.i1",
|
|
160
|
+
"conv.i2",
|
|
161
|
+
"conv.i4",
|
|
162
|
+
"conv.i8",
|
|
163
|
+
"conv.u",
|
|
164
|
+
"conv.u1",
|
|
165
|
+
"conv.u2",
|
|
166
|
+
"conv.u4",
|
|
167
|
+
"conv.u8",
|
|
168
|
+
"conv.r4",
|
|
169
|
+
"conv.r8",
|
|
170
|
+
"conv.r.un",
|
|
171
|
+
"conv.ovf.i",
|
|
172
|
+
"conv.ovf.i.un",
|
|
173
|
+
"conv.ovf.i1.un",
|
|
174
|
+
"conv.ovf.i2.un",
|
|
175
|
+
"conv.ovf.i4.un",
|
|
176
|
+
"conv.ovf.i8.un",
|
|
177
|
+
"conv.ovf.u",
|
|
178
|
+
"conv.ovf.u.un",
|
|
179
|
+
"conv.ovf.u1.un",
|
|
180
|
+
"conv.ovf.u2.un",
|
|
181
|
+
"conv.ovf.u4.un",
|
|
182
|
+
"conv.ovf.u8.un",
|
|
183
|
+
"endfinally",
|
|
184
|
+
"endfilter",
|
|
185
|
+
"rethrow",
|
|
186
|
+
*CONTROL_OPS,
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _dotnet_no_effect(_method: DotNetMethodListing, _instruction: DotNetInstruction, _source: SourceRef) -> tuple:
|
|
191
|
+
return ()
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _dotnet_unknown_opcode_effect(
|
|
195
|
+
_method: DotNetMethodListing,
|
|
196
|
+
instruction: DotNetInstruction,
|
|
197
|
+
source: SourceRef,
|
|
198
|
+
) -> tuple:
|
|
199
|
+
return (UnknownOpcode(source=source, opcode=instruction.opcode, raw=_dotnet_raw_instruction_line(instruction)),)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _dotnet_binary(op: str):
|
|
203
|
+
def factory(_method: DotNetMethodListing, _instruction: DotNetInstruction, source: SourceRef) -> tuple:
|
|
204
|
+
return (Binary(source=source, op=op, semantics="static"),)
|
|
205
|
+
|
|
206
|
+
return factory
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _dotnet_compare(op: str):
|
|
210
|
+
def factory(_method: DotNetMethodListing, _instruction: DotNetInstruction, source: SourceRef) -> tuple:
|
|
211
|
+
return (Binary(source=source, op=op, semantics="static"),)
|
|
212
|
+
|
|
213
|
+
return factory
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def _dotnet_constant_effect(_method: DotNetMethodListing, instruction: DotNetInstruction, source: SourceRef) -> tuple | None:
|
|
217
|
+
value = _constant_value(instruction)
|
|
218
|
+
if value is None:
|
|
219
|
+
return None
|
|
220
|
+
return (Push(source=source, value=Const(value=value, source=source)),)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def _dotnet_load_local_effect(method: DotNetMethodListing, instruction: DotNetInstruction, source: SourceRef) -> tuple | None:
|
|
224
|
+
index = _load_index(instruction.opcode, instruction.operands, "ldloc", include_address=True)
|
|
225
|
+
if index is None:
|
|
226
|
+
return None
|
|
227
|
+
name = _local_name(method, index)
|
|
228
|
+
return (LoadLocal(source=source, name=name, fallback=Var(name=name, source=source)),)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def _dotnet_store_local_effect(method: DotNetMethodListing, instruction: DotNetInstruction, source: SourceRef) -> tuple | None:
|
|
232
|
+
index = _load_index(instruction.opcode, instruction.operands, "stloc")
|
|
233
|
+
if index is None:
|
|
234
|
+
return None
|
|
235
|
+
name = _local_name(method, index)
|
|
236
|
+
return (StoreLocal(source=source, name=name, target=Var(name=name, source=source)),)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def _dotnet_load_arg_effect(method: DotNetMethodListing, instruction: DotNetInstruction, source: SourceRef) -> tuple | None:
|
|
240
|
+
index = _load_index(instruction.opcode, instruction.operands, "ldarg", include_address=True)
|
|
241
|
+
if index is None:
|
|
242
|
+
return None
|
|
243
|
+
name = _argument_name(method, index)
|
|
244
|
+
if instruction.opcode.startswith("ldarga"):
|
|
245
|
+
return (Push(source=source, value=IndirectRef(source=source, target=Var(name=name, source=source))),)
|
|
246
|
+
return (LoadLocal(source=source, name=name, fallback=Var(name=name, source=source)),)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def _dotnet_store_arg_effect(method: DotNetMethodListing, instruction: DotNetInstruction, source: SourceRef) -> tuple | None:
|
|
250
|
+
index = _load_index(instruction.opcode, instruction.operands, "starg")
|
|
251
|
+
if index is None:
|
|
252
|
+
return None
|
|
253
|
+
name = _argument_name(method, index)
|
|
254
|
+
return (StoreLocal(source=source, name=name, target=IndirectRef(source=source, target=Var(name=name, source=source))),)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def _dotnet_call(_method: DotNetMethodListing, instruction: DotNetInstruction, source: SourceRef) -> tuple:
|
|
258
|
+
if instruction.opcode in {"call", "callvirt", "newobj", "jmp"} and not instruction.member_name:
|
|
259
|
+
return _dotnet_unknown_opcode_effect(_method, instruction, source)
|
|
260
|
+
arg_count = instruction.arg_count or 0
|
|
261
|
+
returns = 0 if instruction.returns_void else "unknown"
|
|
262
|
+
if instruction.opcode == "newobj":
|
|
263
|
+
return (
|
|
264
|
+
BuildCall(
|
|
265
|
+
source=source,
|
|
266
|
+
callee=Global(name=instruction.member_name or _member_token_name(instruction.operands), source=source),
|
|
267
|
+
arg_count=arg_count,
|
|
268
|
+
returns="unknown",
|
|
269
|
+
),
|
|
270
|
+
)
|
|
271
|
+
if instruction.is_static is False or instruction.opcode == "callvirt":
|
|
272
|
+
return (
|
|
273
|
+
InvokeMember(
|
|
274
|
+
source=source,
|
|
275
|
+
owner=instruction.owner_name or "",
|
|
276
|
+
member=instruction.member_name or _member_token_name(instruction.operands),
|
|
277
|
+
arg_count=arg_count,
|
|
278
|
+
static=False,
|
|
279
|
+
returns=returns,
|
|
280
|
+
),
|
|
281
|
+
)
|
|
282
|
+
if instruction.owner_name and instruction.member_name:
|
|
283
|
+
return (
|
|
284
|
+
InvokeMember(
|
|
285
|
+
source=source,
|
|
286
|
+
owner=instruction.owner_name,
|
|
287
|
+
member=instruction.member_name,
|
|
288
|
+
arg_count=arg_count,
|
|
289
|
+
static=True,
|
|
290
|
+
returns=returns,
|
|
291
|
+
),
|
|
292
|
+
)
|
|
293
|
+
return (
|
|
294
|
+
BuildCall(
|
|
295
|
+
source=source,
|
|
296
|
+
callee=Global(name=_member_token_name(instruction.operands), source=source),
|
|
297
|
+
arg_count=arg_count,
|
|
298
|
+
returns=returns,
|
|
299
|
+
),
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
DOTNET_EFFECT_TABLE = VMEffectTable(
|
|
304
|
+
opcode_attr="opcode",
|
|
305
|
+
ignored=frozenset(IGNORED_OPS),
|
|
306
|
+
exact={
|
|
307
|
+
**{opcode: _dotnet_binary(op) for opcode, op in BINARY_OPS.items()},
|
|
308
|
+
**{opcode: _dotnet_compare(op) for opcode, op in COMPARE_OPS.items()},
|
|
309
|
+
"ldnull": lambda _method, _instruction, source: (Push(source=source, value=Const(value=None, source=source)),),
|
|
310
|
+
"dup": lambda _method, instruction, source: (DuplicateTop(source=source, materialized_name=f"local_stack_{instruction.offset}"),),
|
|
311
|
+
"pop": lambda _method, _instruction, source: (Pop(source=source, count=1, emit_calls=True),),
|
|
312
|
+
"neg": lambda _method, _instruction, source: (CallTopAs(source=source, callee_name="neg"),),
|
|
313
|
+
"not": lambda _method, _instruction, source: (CallTopAs(source=source, callee_name="bitnot"),),
|
|
314
|
+
"call": _dotnet_call,
|
|
315
|
+
"callvirt": _dotnet_call,
|
|
316
|
+
"newobj": _dotnet_call,
|
|
317
|
+
"jmp": _dotnet_call,
|
|
318
|
+
"ldftn": lambda _method, instruction, source: (Push(source=source, value=Global(name=_member_token_name(instruction.operands), source=source)),),
|
|
319
|
+
"ldvirtftn": lambda _method, instruction, source: (Push(source=source, value=Global(name=_member_token_name(instruction.operands), source=source)),),
|
|
320
|
+
"initobj": lambda _method, _instruction, source: (Pop(source=source, count=1, allow_missing=True),),
|
|
321
|
+
"arglist": lambda _method, _instruction, source: (Push(source=source, value=Global(name="arglist", source=source)),),
|
|
322
|
+
"ckfinite": lambda _method, _instruction, source: (CallTopAs(source=source, callee_name="check_finite"),),
|
|
323
|
+
"cpblk": lambda _method, _instruction, source: (Pop(source=source, count=3, allow_missing=True),),
|
|
324
|
+
"cpobj": lambda _method, _instruction, source: (Pop(source=source, count=2, allow_missing=True),),
|
|
325
|
+
"initblk": lambda _method, _instruction, source: (Pop(source=source, count=3, allow_missing=True),),
|
|
326
|
+
"localloc": lambda _method, _instruction, source: (BuildCall(source=source, callee=Global(name="localloc", source=source), arg_count=1),),
|
|
327
|
+
"mkrefany": lambda _method, instruction, source: (BuildCall(source=source, callee=Global(name=f"mkrefany<{instruction.operands or 'type'}>", source=source), arg_count=1),),
|
|
328
|
+
"refanytype": lambda _method, _instruction, source: (CallTopAs(source=source, callee_name="refanytype"),),
|
|
329
|
+
"refanyval": lambda _method, instruction, source: (BuildCall(source=source, callee=Global(name=f"refanyval<{instruction.operands or 'type'}>", source=source), arg_count=1),),
|
|
330
|
+
"sizeof": lambda _method, instruction, source: (Push(source=source, value=Global(name=f"sizeof<{instruction.operands or 'type'}>", source=source)),),
|
|
331
|
+
"ldstr": lambda _method, instruction, source: (Push(source=source, value=Const(value=instruction.operands, source=source)),),
|
|
332
|
+
"ldtoken": lambda _method, instruction, source: (Push(source=source, value=Global(name=instruction.operands, source=source)),),
|
|
333
|
+
"isinst": lambda _method, instruction, source: (BuildCall(source=source, callee=Global(name="instanceof", source=source), arg_count=1, returns=1),),
|
|
334
|
+
"ldobj": lambda _method, instruction, source: (Pop(source=source, count=1, allow_missing=True), Push(source=source, value=Global(name=_member_token_name(instruction.operands), source=source))),
|
|
335
|
+
"ldfld": lambda _method, instruction, source: (LoadAttr(source=source, attr=_member_name(instruction)),),
|
|
336
|
+
"ldflda": lambda _method, instruction, source: (LoadAttr(source=source, attr=_member_name(instruction)),),
|
|
337
|
+
"stfld": lambda _method, instruction, source: (StoreAttr(source=source, attr=_member_name(instruction)),),
|
|
338
|
+
"ldsfld": lambda _method, instruction, source: (Push(source=source, value=Global(name=_member_token_name(instruction.operands), source=source)),),
|
|
339
|
+
"ldsflda": lambda _method, instruction, source: (Push(source=source, value=Global(name=_member_token_name(instruction.operands), source=source)),),
|
|
340
|
+
"stsfld": lambda _method, instruction, source: (
|
|
341
|
+
StoreStaticMember(
|
|
342
|
+
source=source,
|
|
343
|
+
owner=_member_owner(instruction),
|
|
344
|
+
field_name=_member_name(instruction),
|
|
345
|
+
),
|
|
346
|
+
),
|
|
347
|
+
"ldlen": lambda _method, _instruction, source: (LoadAttr(source=source, attr="length"),),
|
|
348
|
+
"newarr": lambda _method, instruction, source: (BuildArrayCall(source=source, kind=instruction.operands or "array"),),
|
|
349
|
+
"throw": lambda _method, _instruction, source: (RaiseTop(source=source),),
|
|
350
|
+
"ret": lambda _method, _instruction, source: (ReturnTop(source=source, empty_is_void=True),),
|
|
351
|
+
},
|
|
352
|
+
rules=(
|
|
353
|
+
VMEffectRule(matches=lambda opcode, instruction: _constant_value(instruction) is not None, factory=_dotnet_constant_effect),
|
|
354
|
+
VMEffectRule(matches=lambda opcode, instruction: _load_index(opcode, instruction.operands, "ldloc", include_address=True) is not None, factory=_dotnet_load_local_effect),
|
|
355
|
+
VMEffectRule(matches=lambda opcode, instruction: _load_index(opcode, instruction.operands, "stloc") is not None, factory=_dotnet_store_local_effect),
|
|
356
|
+
VMEffectRule(matches=lambda opcode, instruction: _load_index(opcode, instruction.operands, "ldarg", include_address=True) is not None, factory=_dotnet_load_arg_effect),
|
|
357
|
+
VMEffectRule(matches=lambda opcode, instruction: _load_index(opcode, instruction.operands, "starg") is not None, factory=_dotnet_store_arg_effect),
|
|
358
|
+
VMEffectRule(matches=lambda opcode, _instruction: opcode.startswith("ldind."), factory=lambda _method, _instruction, source: (LoadIndirect(source=source),)),
|
|
359
|
+
VMEffectRule(matches=lambda opcode, _instruction: opcode.startswith("stind."), factory=lambda _method, _instruction, source: (StoreIndirect(source=source),)),
|
|
360
|
+
VMEffectRule(matches=lambda opcode, _instruction: opcode.startswith("ldelema"), factory=lambda _method, _instruction, source: (LoadItemAddress(source=source),)),
|
|
361
|
+
VMEffectRule(matches=lambda opcode, _instruction: opcode.startswith("ldelem"), factory=lambda _method, _instruction, source: (LoadItem(source=source),)),
|
|
362
|
+
VMEffectRule(matches=lambda opcode, _instruction: opcode.startswith("stelem"), factory=lambda _method, _instruction, source: (StoreItemEffect(source=source),)),
|
|
363
|
+
),
|
|
364
|
+
fallback=_dotnet_unknown_opcode_effect,
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def lift_dotnet_assembly(assembly: DotNetAssembly, metadata: dict) -> "ModuleIR":
|
|
369
|
+
return assemble_vm_module(
|
|
370
|
+
name=assembly.name,
|
|
371
|
+
source_language="dotnet",
|
|
372
|
+
metadata={"frontend": metadata, "bytecode_format": "cli-assembly"},
|
|
373
|
+
functions=tuple(_recover_dotnet_method(method) for method in assembly.methods),
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def _recover_dotnet_method(method: DotNetMethodListing) -> "FunctionIR":
|
|
378
|
+
spec = VMFunctionSpec(
|
|
379
|
+
name=method.name,
|
|
380
|
+
params=tuple(_parameter_name(method, index) for index in range(method.param_count)),
|
|
381
|
+
frontend=DOTNET_FRONTEND_ID,
|
|
382
|
+
instruction_count=len(method.instructions),
|
|
383
|
+
metadata={"token": f"0x{method.token:08x}", "rva": method.rva},
|
|
384
|
+
)
|
|
385
|
+
return recover_vm_function(
|
|
386
|
+
spec,
|
|
387
|
+
lambda: lift_dotnet_method(method),
|
|
388
|
+
raw=tuple(_dotnet_raw_instruction_line(instruction) for instruction in method.instructions),
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def lift_dotnet_method(method: DotNetMethodListing) -> "FunctionIR":
|
|
393
|
+
steps = _dotnet_bytecode_steps(method)
|
|
394
|
+
return lift_vm_step_function(
|
|
395
|
+
VMFunctionSpec(
|
|
396
|
+
name=method.name,
|
|
397
|
+
params=tuple(_parameter_name(method, index) for index in range(method.param_count)),
|
|
398
|
+
frontend=DOTNET_FRONTEND_ID,
|
|
399
|
+
instruction_count=len(method.instructions),
|
|
400
|
+
metadata={"token": f"0x{method.token:08x}", "rva": method.rva},
|
|
401
|
+
),
|
|
402
|
+
steps,
|
|
403
|
+
profile=_dotnet_region_profile(steps, method.instructions),
|
|
404
|
+
stateful_callbacks=VMStatefulCallbacks(
|
|
405
|
+
initial_locals=lambda: _initial_locals(method),
|
|
406
|
+
lift_linear=lambda start, end, locals, stack: _dotnet_linear_state(method, method.instructions, start, end, locals, stack),
|
|
407
|
+
branch_condition=lambda branch, stack: _dotnet_branch_condition(_dotnet_instruction_for_step(method.instructions, branch), stack),
|
|
408
|
+
branch_stack_width=lambda branch: _dotnet_branch_stack_width(_dotnet_instruction_for_step(method.instructions, branch)),
|
|
409
|
+
),
|
|
410
|
+
initial_locals=_initial_locals(method),
|
|
411
|
+
raw_window=lambda index: _dotnet_raw_instruction_window(method.instructions, index),
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
def _dotnet_region_profile(
|
|
416
|
+
steps: tuple[VMBytecodeStep, ...],
|
|
417
|
+
instructions: tuple[DotNetInstruction, ...],
|
|
418
|
+
) -> VMRegionProfile[VMBytecodeStep]:
|
|
419
|
+
return build_hint_region_profile(
|
|
420
|
+
steps,
|
|
421
|
+
frontend=DOTNET_FRONTEND_ID,
|
|
422
|
+
opcode_classes=DOTNET_REGION_OPCODE_CLASSES,
|
|
423
|
+
raw_window=lambda index: _dotnet_raw_instruction_window(instructions, index),
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def _dotnet_linear_state(
|
|
428
|
+
method: DotNetMethodListing,
|
|
429
|
+
instructions: tuple[DotNetInstruction, ...],
|
|
430
|
+
start: int,
|
|
431
|
+
end: int,
|
|
432
|
+
initial_locals: dict[str, Expr],
|
|
433
|
+
initial_stack: tuple[Expr, ...],
|
|
434
|
+
) -> VMLinearState | None:
|
|
435
|
+
steps = tuple(_dotnet_bytecode_step(method, instruction) for instruction in instructions[start:end])
|
|
436
|
+
result = lift_steps(steps, initial_locals=initial_locals, initial_stack=initial_stack)
|
|
437
|
+
if result.state.diagnostics:
|
|
438
|
+
return None
|
|
439
|
+
if result.stopped_at is not None and result.state.terminator is None:
|
|
440
|
+
return None
|
|
441
|
+
if result.state.terminator is not None and result.stopped_at != steps[-1]:
|
|
442
|
+
return None
|
|
443
|
+
return VMLinearState(
|
|
444
|
+
locals=result.state.locals,
|
|
445
|
+
stack=tuple(result.state.stack),
|
|
446
|
+
statements=tuple(result.state.statements),
|
|
447
|
+
terminator=result.state.terminator,
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def _dotnet_instruction_for_step(
|
|
452
|
+
instructions: tuple[DotNetInstruction, ...],
|
|
453
|
+
step: VMBytecodeStep,
|
|
454
|
+
) -> DotNetInstruction:
|
|
455
|
+
for instruction in instructions:
|
|
456
|
+
if instruction.offset == step.source.offset:
|
|
457
|
+
return instruction
|
|
458
|
+
return instructions[0]
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
def _dotnet_branch_stack_width(instruction: DotNetInstruction) -> int:
|
|
462
|
+
if instruction.opcode == "switch":
|
|
463
|
+
return 1
|
|
464
|
+
op = DOTNET_CONDITIONAL_OPS.get(instruction.opcode)
|
|
465
|
+
return 2 if op in {"!=", "==", "<", "<=", ">", ">="} else 1
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
def _dotnet_branch_condition(instruction: DotNetInstruction, stack: tuple[Expr, ...]) -> Expr:
|
|
469
|
+
source = SourceRef(frontend=DOTNET_FRONTEND_ID, offset=instruction.offset)
|
|
470
|
+
op = DOTNET_CONDITIONAL_OPS[instruction.opcode]
|
|
471
|
+
if op == "truthy":
|
|
472
|
+
return BinaryOp(source=source, op="!=", left=stack[0], right=Const(value=0, source=source), semantics="static")
|
|
473
|
+
if op == "falsey":
|
|
474
|
+
return BinaryOp(source=source, op="==", left=stack[0], right=Const(value=0, source=source), semantics="static")
|
|
475
|
+
return BinaryOp(source=source, op=op, left=stack[0], right=stack[1], semantics="static")
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
def _dotnet_bytecode_step(method: DotNetMethodListing, instruction: DotNetInstruction) -> VMBytecodeStep:
|
|
479
|
+
source = SourceRef(frontend=DOTNET_FRONTEND_ID, offset=instruction.offset)
|
|
480
|
+
decoded = _dotnet_decoded_instruction(instruction, source)
|
|
481
|
+
return VMBytecodeStep(
|
|
482
|
+
opcode=decoded.opcode,
|
|
483
|
+
source=source,
|
|
484
|
+
effects=_dotnet_instruction_effects(method, instruction, source),
|
|
485
|
+
raw=decoded.raw,
|
|
486
|
+
decoded=decoded,
|
|
487
|
+
hints=_dotnet_instruction_hints(instruction, source),
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
def _dotnet_bytecode_steps(method: DotNetMethodListing) -> tuple[VMBytecodeStep, ...]:
|
|
492
|
+
steps: list[VMBytecodeStep] = []
|
|
493
|
+
instructions = method.instructions
|
|
494
|
+
for index, instruction in enumerate(instructions):
|
|
495
|
+
step = _dotnet_bytecode_step(method, instruction)
|
|
496
|
+
if _is_materialized_condition_branch(instructions, index):
|
|
497
|
+
step = VMBytecodeStep(
|
|
498
|
+
opcode=step.opcode,
|
|
499
|
+
source=step.source,
|
|
500
|
+
effects=step.effects,
|
|
501
|
+
raw=step.raw,
|
|
502
|
+
decoded=step.decoded,
|
|
503
|
+
hints=(*step.hints, VMHint(kind="materialized-condition", source=step.source, label=instruction.opcode)),
|
|
504
|
+
)
|
|
505
|
+
steps.append(step)
|
|
506
|
+
return tuple(steps)
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
def _is_materialized_condition_branch(instructions: tuple[DotNetInstruction, ...], index: int) -> bool:
|
|
510
|
+
if instructions[index].opcode not in {"brtrue", "brtrue.s", "brfalse", "brfalse.s"} or index < 2:
|
|
511
|
+
return False
|
|
512
|
+
return instructions[index - 1].opcode.startswith("ldloc") and instructions[index - 2].opcode.startswith("stloc")
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
def _dotnet_decoded_instruction(instruction: DotNetInstruction, source: SourceRef) -> VMDecodedInstruction:
|
|
516
|
+
operands = ()
|
|
517
|
+
if instruction.operands:
|
|
518
|
+
operands = (
|
|
519
|
+
VMOperand(
|
|
520
|
+
role=_dotnet_operand_role(instruction.opcode),
|
|
521
|
+
value=instruction.operands,
|
|
522
|
+
text=instruction.operands,
|
|
523
|
+
),
|
|
524
|
+
)
|
|
525
|
+
return VMDecodedInstruction(
|
|
526
|
+
opcode=instruction.opcode,
|
|
527
|
+
source=source,
|
|
528
|
+
operands=operands,
|
|
529
|
+
raw=_dotnet_raw_instruction_line(instruction),
|
|
530
|
+
)
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
def _dotnet_instruction_effects(
|
|
534
|
+
method: DotNetMethodListing,
|
|
535
|
+
instruction: DotNetInstruction,
|
|
536
|
+
source: SourceRef,
|
|
537
|
+
) -> tuple[object, ...] | None:
|
|
538
|
+
return DOTNET_EFFECT_TABLE.effects_for(method, instruction, source)
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
def _dotnet_operand_role(opcode: str):
|
|
542
|
+
if opcode in CONTROL_OPS or opcode == "switch":
|
|
543
|
+
return "target"
|
|
544
|
+
if "arg" in opcode or "loc" in opcode:
|
|
545
|
+
return "local"
|
|
546
|
+
if opcode.startswith(("call", "newobj", "ldfld", "stfld", "ldsfld", "stsfld")):
|
|
547
|
+
return "member"
|
|
548
|
+
if opcode.startswith("ldc") or opcode == "ldstr":
|
|
549
|
+
return "constant"
|
|
550
|
+
return "raw"
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
def _dotnet_instruction_hints(instruction: DotNetInstruction, source: SourceRef) -> tuple[VMHint, ...]:
|
|
554
|
+
if instruction.opcode not in CONTROL_OPS:
|
|
555
|
+
return ()
|
|
556
|
+
if instruction.opcode == "switch":
|
|
557
|
+
targets = _branch_targets(instruction)
|
|
558
|
+
default_target = _dotnet_switch_default_target(instruction)
|
|
559
|
+
hints = [VMHint(kind="default-target", source=source, target=default_target, label=instruction.opcode, flow="multiway")]
|
|
560
|
+
hints.extend(
|
|
561
|
+
VMHint(kind="case-target", source=source, target=target, value=index, label=instruction.opcode, flow="multiway")
|
|
562
|
+
for index, target in enumerate(targets)
|
|
563
|
+
)
|
|
564
|
+
return tuple(hints)
|
|
565
|
+
targets = _branch_targets(instruction)
|
|
566
|
+
flow = "unconditional" if instruction.opcode in {"br", "br.s", "leave", "leave.s"} else "conditional"
|
|
567
|
+
return tuple(
|
|
568
|
+
VMHint(
|
|
569
|
+
kind="loop-backedge" if target <= instruction.offset else "branch-target",
|
|
570
|
+
source=source,
|
|
571
|
+
target=target,
|
|
572
|
+
label=instruction.opcode,
|
|
573
|
+
flow=flow,
|
|
574
|
+
)
|
|
575
|
+
for target in targets
|
|
576
|
+
)
|
|
577
|
+
|
|
578
|
+
|
|
579
|
+
def _branch_targets(instruction: DotNetInstruction) -> tuple[int, ...]:
|
|
580
|
+
targets: list[int] = []
|
|
581
|
+
for raw in instruction.operands.split(","):
|
|
582
|
+
try:
|
|
583
|
+
targets.append(int(raw.strip()))
|
|
584
|
+
except ValueError:
|
|
585
|
+
continue
|
|
586
|
+
return tuple(targets)
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
def _dotnet_switch_default_target(instruction: DotNetInstruction) -> int | None:
|
|
590
|
+
if instruction.operand_kind != "InlineSwitch":
|
|
591
|
+
return None
|
|
592
|
+
count = len(_branch_targets(instruction))
|
|
593
|
+
return instruction.offset + 1 + 4 + (count * 4)
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
def _constant_value(instruction: DotNetInstruction):
|
|
597
|
+
opcode = instruction.opcode
|
|
598
|
+
if opcode == "ldc.i4.m1":
|
|
599
|
+
return -1
|
|
600
|
+
if opcode in {"ldc.i4.s", "ldc.i4", "ldc.i8"}:
|
|
601
|
+
try:
|
|
602
|
+
return int(instruction.operands)
|
|
603
|
+
except ValueError:
|
|
604
|
+
return None
|
|
605
|
+
if opcode.startswith("ldc.i4."):
|
|
606
|
+
try:
|
|
607
|
+
return int(opcode.removeprefix("ldc.i4."))
|
|
608
|
+
except ValueError:
|
|
609
|
+
return None
|
|
610
|
+
if opcode in {"ldc.r4", "ldc.r8"}:
|
|
611
|
+
try:
|
|
612
|
+
return float(instruction.operands)
|
|
613
|
+
except ValueError:
|
|
614
|
+
return None
|
|
615
|
+
return None
|
|
616
|
+
|
|
617
|
+
|
|
618
|
+
def _load_index(opcode: str, operands: str, family: str, *, include_address: bool = False) -> int | None:
|
|
619
|
+
if opcode == family or opcode == f"{family}.s":
|
|
620
|
+
try:
|
|
621
|
+
return int(operands.split()[0])
|
|
622
|
+
except (IndexError, ValueError):
|
|
623
|
+
return None
|
|
624
|
+
if include_address and (opcode == f"{family}a" or opcode == f"{family}a.s"):
|
|
625
|
+
try:
|
|
626
|
+
return int(operands.split()[0])
|
|
627
|
+
except (IndexError, ValueError):
|
|
628
|
+
return None
|
|
629
|
+
prefix = f"{family}."
|
|
630
|
+
if opcode.startswith(prefix):
|
|
631
|
+
suffix = opcode.removeprefix(prefix)
|
|
632
|
+
if suffix.isdigit():
|
|
633
|
+
return int(suffix)
|
|
634
|
+
return None
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
def _argument_name(method: DotNetMethodListing, index: int) -> str:
|
|
638
|
+
if not method.is_static and index == 0:
|
|
639
|
+
return "this"
|
|
640
|
+
parameter_index = index if method.is_static else index - 1
|
|
641
|
+
return _parameter_name(method, parameter_index) if 0 <= parameter_index < method.param_count else f"arg{index}"
|
|
642
|
+
|
|
643
|
+
|
|
644
|
+
def _parameter_name(_method: DotNetMethodListing, index: int) -> str:
|
|
645
|
+
return f"arg{index}"
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
def _local_name(_method: DotNetMethodListing, index: int) -> str:
|
|
649
|
+
return f"local{index}"
|
|
650
|
+
|
|
651
|
+
|
|
652
|
+
def _initial_locals(method: DotNetMethodListing) -> dict[str, Expr]:
|
|
653
|
+
locals_: dict[str, Expr] = {}
|
|
654
|
+
if not method.is_static:
|
|
655
|
+
locals_["this"] = Var(name="this", source=SourceRef(frontend=DOTNET_FRONTEND_ID, detail="arg:0"))
|
|
656
|
+
for index in range(method.param_count):
|
|
657
|
+
name = _parameter_name(method, index)
|
|
658
|
+
locals_[name] = Var(name=name, source=SourceRef(frontend=DOTNET_FRONTEND_ID, detail=f"arg:{index}"))
|
|
659
|
+
return locals_
|
|
660
|
+
|
|
661
|
+
|
|
662
|
+
def _member_token_name(operand: str) -> str:
|
|
663
|
+
return operand or "<metadata-token>"
|
|
664
|
+
|
|
665
|
+
|
|
666
|
+
def _member_name(instruction: DotNetInstruction) -> str:
|
|
667
|
+
return instruction.member_name or _member_token_name(instruction.operands)
|
|
668
|
+
|
|
669
|
+
|
|
670
|
+
def _member_owner(instruction: DotNetInstruction) -> str:
|
|
671
|
+
return instruction.owner_name or "<static>"
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
def _dotnet_raw_instruction_window(
|
|
675
|
+
instructions: tuple[DotNetInstruction, ...],
|
|
676
|
+
index: int,
|
|
677
|
+
radius: int = 3,
|
|
678
|
+
) -> tuple[str, ...]:
|
|
679
|
+
start = max(0, index - radius)
|
|
680
|
+
end = min(len(instructions), index + radius + 1)
|
|
681
|
+
return tuple(_dotnet_raw_instruction_line(instruction) for instruction in instructions[start:end])
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
def _dotnet_raw_instruction_line(instruction: DotNetInstruction) -> str:
|
|
685
|
+
operands = f" {instruction.operands}" if instruction.operands else ""
|
|
686
|
+
return f"IL_{instruction.offset:04x}: {instruction.opcode}{operands}"
|