smallworld-re 1.0.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.
Files changed (166) hide show
  1. smallworld/__init__.py +35 -0
  2. smallworld/analyses/__init__.py +14 -0
  3. smallworld/analyses/analysis.py +88 -0
  4. smallworld/analyses/code_coverage.py +31 -0
  5. smallworld/analyses/colorizer.py +682 -0
  6. smallworld/analyses/colorizer_summary.py +100 -0
  7. smallworld/analyses/field_detection/__init__.py +14 -0
  8. smallworld/analyses/field_detection/field_analysis.py +536 -0
  9. smallworld/analyses/field_detection/guards.py +26 -0
  10. smallworld/analyses/field_detection/hints.py +133 -0
  11. smallworld/analyses/field_detection/malloc.py +211 -0
  12. smallworld/analyses/forced_exec/__init__.py +3 -0
  13. smallworld/analyses/forced_exec/forced_exec.py +87 -0
  14. smallworld/analyses/underlays/__init__.py +4 -0
  15. smallworld/analyses/underlays/basic.py +13 -0
  16. smallworld/analyses/underlays/underlay.py +31 -0
  17. smallworld/analyses/unstable/__init__.py +4 -0
  18. smallworld/analyses/unstable/angr/__init__.py +0 -0
  19. smallworld/analyses/unstable/angr/base.py +12 -0
  20. smallworld/analyses/unstable/angr/divergence.py +274 -0
  21. smallworld/analyses/unstable/angr/model.py +383 -0
  22. smallworld/analyses/unstable/angr/nwbt.py +63 -0
  23. smallworld/analyses/unstable/angr/typedefs.py +170 -0
  24. smallworld/analyses/unstable/angr/utils.py +25 -0
  25. smallworld/analyses/unstable/angr/visitor.py +315 -0
  26. smallworld/analyses/unstable/angr_nwbt.py +106 -0
  27. smallworld/analyses/unstable/code_coverage.py +54 -0
  28. smallworld/analyses/unstable/code_reachable.py +44 -0
  29. smallworld/analyses/unstable/control_flow_tracer.py +71 -0
  30. smallworld/analyses/unstable/pointer_finder.py +90 -0
  31. smallworld/arch/__init__.py +0 -0
  32. smallworld/arch/aarch64_arch.py +286 -0
  33. smallworld/arch/amd64_arch.py +86 -0
  34. smallworld/arch/i386_arch.py +44 -0
  35. smallworld/emulators/__init__.py +14 -0
  36. smallworld/emulators/angr/__init__.py +7 -0
  37. smallworld/emulators/angr/angr.py +1652 -0
  38. smallworld/emulators/angr/default.py +15 -0
  39. smallworld/emulators/angr/exceptions.py +7 -0
  40. smallworld/emulators/angr/exploration/__init__.py +9 -0
  41. smallworld/emulators/angr/exploration/bounds.py +27 -0
  42. smallworld/emulators/angr/exploration/default.py +17 -0
  43. smallworld/emulators/angr/exploration/terminate.py +22 -0
  44. smallworld/emulators/angr/factory.py +55 -0
  45. smallworld/emulators/angr/machdefs/__init__.py +35 -0
  46. smallworld/emulators/angr/machdefs/aarch64.py +292 -0
  47. smallworld/emulators/angr/machdefs/amd64.py +192 -0
  48. smallworld/emulators/angr/machdefs/arm.py +387 -0
  49. smallworld/emulators/angr/machdefs/i386.py +221 -0
  50. smallworld/emulators/angr/machdefs/machdef.py +138 -0
  51. smallworld/emulators/angr/machdefs/mips.py +184 -0
  52. smallworld/emulators/angr/machdefs/mips64.py +189 -0
  53. smallworld/emulators/angr/machdefs/ppc.py +101 -0
  54. smallworld/emulators/angr/machdefs/riscv.py +261 -0
  55. smallworld/emulators/angr/machdefs/xtensa.py +255 -0
  56. smallworld/emulators/angr/memory/__init__.py +7 -0
  57. smallworld/emulators/angr/memory/default.py +10 -0
  58. smallworld/emulators/angr/memory/fixups.py +43 -0
  59. smallworld/emulators/angr/memory/memtrack.py +105 -0
  60. smallworld/emulators/angr/scratch.py +43 -0
  61. smallworld/emulators/angr/simos.py +53 -0
  62. smallworld/emulators/angr/utils.py +70 -0
  63. smallworld/emulators/emulator.py +1013 -0
  64. smallworld/emulators/hookable.py +252 -0
  65. smallworld/emulators/panda/__init__.py +5 -0
  66. smallworld/emulators/panda/machdefs/__init__.py +28 -0
  67. smallworld/emulators/panda/machdefs/aarch64.py +93 -0
  68. smallworld/emulators/panda/machdefs/amd64.py +71 -0
  69. smallworld/emulators/panda/machdefs/arm.py +89 -0
  70. smallworld/emulators/panda/machdefs/i386.py +36 -0
  71. smallworld/emulators/panda/machdefs/machdef.py +86 -0
  72. smallworld/emulators/panda/machdefs/mips.py +94 -0
  73. smallworld/emulators/panda/machdefs/mips64.py +91 -0
  74. smallworld/emulators/panda/machdefs/ppc.py +79 -0
  75. smallworld/emulators/panda/panda.py +575 -0
  76. smallworld/emulators/unicorn/__init__.py +13 -0
  77. smallworld/emulators/unicorn/machdefs/__init__.py +28 -0
  78. smallworld/emulators/unicorn/machdefs/aarch64.py +310 -0
  79. smallworld/emulators/unicorn/machdefs/amd64.py +326 -0
  80. smallworld/emulators/unicorn/machdefs/arm.py +321 -0
  81. smallworld/emulators/unicorn/machdefs/i386.py +137 -0
  82. smallworld/emulators/unicorn/machdefs/machdef.py +117 -0
  83. smallworld/emulators/unicorn/machdefs/mips.py +202 -0
  84. smallworld/emulators/unicorn/unicorn.py +684 -0
  85. smallworld/exceptions/__init__.py +5 -0
  86. smallworld/exceptions/exceptions.py +85 -0
  87. smallworld/exceptions/unstable/__init__.py +1 -0
  88. smallworld/exceptions/unstable/exceptions.py +25 -0
  89. smallworld/extern/__init__.py +4 -0
  90. smallworld/extern/ctypes.py +94 -0
  91. smallworld/extern/unstable/__init__.py +1 -0
  92. smallworld/extern/unstable/ghidra.py +129 -0
  93. smallworld/helpers.py +107 -0
  94. smallworld/hinting/__init__.py +8 -0
  95. smallworld/hinting/hinting.py +214 -0
  96. smallworld/hinting/hints.py +427 -0
  97. smallworld/hinting/unstable/__init__.py +2 -0
  98. smallworld/hinting/utils.py +19 -0
  99. smallworld/instructions/__init__.py +18 -0
  100. smallworld/instructions/aarch64.py +20 -0
  101. smallworld/instructions/arm.py +18 -0
  102. smallworld/instructions/bsid.py +67 -0
  103. smallworld/instructions/instructions.py +258 -0
  104. smallworld/instructions/mips.py +21 -0
  105. smallworld/instructions/x86.py +100 -0
  106. smallworld/logging.py +90 -0
  107. smallworld/platforms.py +95 -0
  108. smallworld/py.typed +0 -0
  109. smallworld/state/__init__.py +6 -0
  110. smallworld/state/cpus/__init__.py +32 -0
  111. smallworld/state/cpus/aarch64.py +563 -0
  112. smallworld/state/cpus/amd64.py +676 -0
  113. smallworld/state/cpus/arm.py +630 -0
  114. smallworld/state/cpus/cpu.py +71 -0
  115. smallworld/state/cpus/i386.py +239 -0
  116. smallworld/state/cpus/mips.py +374 -0
  117. smallworld/state/cpus/mips64.py +372 -0
  118. smallworld/state/cpus/powerpc.py +229 -0
  119. smallworld/state/cpus/riscv.py +357 -0
  120. smallworld/state/cpus/xtensa.py +80 -0
  121. smallworld/state/memory/__init__.py +7 -0
  122. smallworld/state/memory/code.py +70 -0
  123. smallworld/state/memory/elf/__init__.py +3 -0
  124. smallworld/state/memory/elf/elf.py +564 -0
  125. smallworld/state/memory/elf/rela/__init__.py +32 -0
  126. smallworld/state/memory/elf/rela/aarch64.py +27 -0
  127. smallworld/state/memory/elf/rela/amd64.py +32 -0
  128. smallworld/state/memory/elf/rela/arm.py +51 -0
  129. smallworld/state/memory/elf/rela/i386.py +32 -0
  130. smallworld/state/memory/elf/rela/mips.py +45 -0
  131. smallworld/state/memory/elf/rela/ppc.py +45 -0
  132. smallworld/state/memory/elf/rela/rela.py +63 -0
  133. smallworld/state/memory/elf/rela/riscv64.py +27 -0
  134. smallworld/state/memory/elf/rela/xtensa.py +15 -0
  135. smallworld/state/memory/elf/structs.py +55 -0
  136. smallworld/state/memory/heap.py +85 -0
  137. smallworld/state/memory/memory.py +181 -0
  138. smallworld/state/memory/stack/__init__.py +31 -0
  139. smallworld/state/memory/stack/aarch64.py +22 -0
  140. smallworld/state/memory/stack/amd64.py +42 -0
  141. smallworld/state/memory/stack/arm.py +66 -0
  142. smallworld/state/memory/stack/i386.py +22 -0
  143. smallworld/state/memory/stack/mips.py +34 -0
  144. smallworld/state/memory/stack/mips64.py +34 -0
  145. smallworld/state/memory/stack/ppc.py +34 -0
  146. smallworld/state/memory/stack/riscv.py +22 -0
  147. smallworld/state/memory/stack/stack.py +127 -0
  148. smallworld/state/memory/stack/xtensa.py +34 -0
  149. smallworld/state/models/__init__.py +6 -0
  150. smallworld/state/models/mmio.py +186 -0
  151. smallworld/state/models/model.py +163 -0
  152. smallworld/state/models/posix.py +455 -0
  153. smallworld/state/models/x86/__init__.py +2 -0
  154. smallworld/state/models/x86/microsoftcdecl.py +35 -0
  155. smallworld/state/models/x86/systemv.py +240 -0
  156. smallworld/state/state.py +962 -0
  157. smallworld/state/unstable/__init__.py +0 -0
  158. smallworld/state/unstable/elf.py +393 -0
  159. smallworld/state/x86_registers.py +30 -0
  160. smallworld/utils.py +935 -0
  161. smallworld_re-1.0.0.dist-info/LICENSE.txt +21 -0
  162. smallworld_re-1.0.0.dist-info/METADATA +189 -0
  163. smallworld_re-1.0.0.dist-info/RECORD +166 -0
  164. smallworld_re-1.0.0.dist-info/WHEEL +5 -0
  165. smallworld_re-1.0.0.dist-info/entry_points.txt +2 -0
  166. smallworld_re-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,63 @@
1
+ import angr
2
+
3
+ from ....emulators.angr.exploration import (
4
+ BoundedExplorationMixin,
5
+ TerminationExplorationMixin,
6
+ )
7
+ from ....emulators.angr.memory import TrackerMemoryMixin
8
+ from .divergence import DivergenceExplorationMixin, DivergenceMemoryMixin
9
+ from .model import ModelMemoryMixin
10
+ from .typedefs import TypeDefPlugin
11
+
12
+
13
+ class NWBTMemoryPlugin( # type: ignore[misc]
14
+ DivergenceMemoryMixin,
15
+ TrackerMemoryMixin,
16
+ ModelMemoryMixin,
17
+ angr.storage.DefaultMemory,
18
+ ):
19
+ pass
20
+
21
+
22
+ class NWBTExplorationTechnique(
23
+ TerminationExplorationMixin,
24
+ BoundedExplorationMixin,
25
+ DivergenceExplorationMixin,
26
+ angr.exploration_techniques.suggestions.Suggestions,
27
+ ):
28
+ pass
29
+
30
+
31
+ def configure_nwbt_plugins(emu):
32
+ """Configure NWBT analysis plugins.
33
+
34
+ This creates a new plugin preset that overrides
35
+ angr's default symbolic memory plugin.
36
+ This preset is passed to the entry state constructor,
37
+ and can't be changed afterward.
38
+ Thus, this needs to get called in a preinit callback.
39
+ """
40
+ preset = angr.SimState._presets["default"].copy()
41
+ preset.add_default_plugin("sym_memory", NWBTMemoryPlugin)
42
+ preset.add_default_plugin("typedefs", TypeDefPlugin)
43
+ emu._plugin_preset = preset
44
+
45
+
46
+ def configure_nwbt_strategy(emu):
47
+ """Configure NWBT analysis strategies
48
+
49
+ This overrides the default angr exploration strategy.
50
+ This needs to access the instantiated exploration manager,
51
+ so it needs to get called in an init callback.
52
+ """
53
+ emu.mgr.use_technique(NWBTExplorationTechnique())
54
+ # angr bug: states don't inherit plugin presets.
55
+ #
56
+ # Normally, they copy all plugins from their parents.
57
+ # If you define a custom plugin and don't touch it,
58
+ # it never gets initialized, and won't get inherited.
59
+ #
60
+ # If you try to touch that plugin on a successor,
61
+ # it can't be initialized, since it doesn't have
62
+ # your custom preset.
63
+ emu.state.get_plugin("typedefs")
@@ -0,0 +1,170 @@
1
+ import ctypes
2
+ import logging
3
+
4
+ import angr
5
+ import claripy
6
+
7
+ log = logging.getLogger(__name__)
8
+
9
+
10
+ class TypeDefPlugin(angr.SimStatePlugin):
11
+ """Angr state plugin for tracking typedefs
12
+
13
+ This captures two concepts for a state:
14
+
15
+ - Type bindings for addresses, registers and symbols
16
+ - Instance addresses for symbols
17
+ """
18
+
19
+ def __init__(self):
20
+ super().__init__()
21
+ self._structdefs = dict()
22
+
23
+ self._symbols = dict()
24
+ self._sym_values = dict()
25
+ self._registers = dict()
26
+ self._addrs = dict()
27
+ self._allocations = dict()
28
+
29
+ @angr.SimStatePlugin.memo
30
+ def copy(self, memo):
31
+ dup = TypeDefPlugin()
32
+ dup._structdefs.update(self._structdefs)
33
+ dup._symbols.update(self._symbols)
34
+ dup._sym_values.update(self._sym_values)
35
+ dup._registers.update(self._registers)
36
+
37
+ # heckin' multi-layer copy...
38
+ for typedef, addrs in self._allocations.items():
39
+ dup._allocations[typedef] = set(addrs)
40
+
41
+ return dup
42
+
43
+ def ctype_to_bv(self, typedef, prefix="sw"):
44
+ if hasattr(typedef, "_fields_"):
45
+ off = 0
46
+ fielddefs = list()
47
+ for name, fielddef in typedef._fields_:
48
+ field_off = getattr(typedef, name).offset
49
+ if field_off > off:
50
+ # handle padding
51
+ fielddefs.append(claripy.BVS("padding", (field_off - off) * 8))
52
+ off = field_off
53
+ fielddefs.append(self.ctype_to_bv(fielddef, f"{prefix}_{name}"))
54
+ off += ctypes.sizeof(fielddef)
55
+
56
+ return claripy.Concat(*fielddefs)
57
+ else:
58
+ res = claripy.BVS(prefix, ctypes.sizeof(typedef) * 8)
59
+ self.bind_symbol(res, typedef)
60
+ res = claripy.Reverse(res)
61
+ return res
62
+
63
+ def add_struct(self, structdef):
64
+ """Add a struct type to this context for easy lookup"""
65
+ if structdef._name in self._structdefs:
66
+ raise ValueError(f"Already have a struct def for {structdef._name}")
67
+ self._structdefs[structdef._name] = structdef
68
+
69
+ def get_struct(self, name):
70
+ """Look up a struct type by its name"""
71
+ return self._structdefs[name]
72
+
73
+ def bind_symbol(self, sym, typedef):
74
+ """Bind a symbol to a typedef"""
75
+ if sym in self._symbols:
76
+ raise ValueError(f"Already have a binding for {sym}")
77
+ self._symbols[sym.args[0]] = typedef
78
+
79
+ def get_symbol_binding(self, sym):
80
+ """Get the type binding for a symbol"""
81
+ if sym in self._symbols:
82
+ return self._symbols[sym]
83
+ return None
84
+
85
+ def set_symbol(self, sym, val):
86
+ """Set a concrete value for a symbol
87
+
88
+ Symbols are static-single-assignment,
89
+ so if they are collapsed to a concrete value,
90
+ that assignment will stick.
91
+ The assignment is normally bound up in the solver,
92
+ making it annoying and expensive to recover.
93
+ This feature lets us quickly recover our stuff
94
+ """
95
+ log.info(f"Setting {sym} to {val}")
96
+ if sym in self._sym_values:
97
+ raise ValueError(f"Already have value {self._sym_values[sym]} for {sym}")
98
+ self._sym_values[sym] = val
99
+
100
+ def get_symbol(self, sym):
101
+ """Get a concrete value for a symbol"""
102
+ if sym in self._sym_values:
103
+ return self._sym_values[sym]
104
+ return None
105
+
106
+ def bind_register(self, name, typedef):
107
+ """Bind a register to a typedef
108
+
109
+ Registers themselves don't have specific types.
110
+ However, this only gets used in uninitialized value analysis;
111
+ it only covers the type a register has before it's
112
+ written by the program.
113
+ """
114
+ if name in self._registers:
115
+ raise ValueError(f"Already have a binding for {name}")
116
+ self._registers[name] = typedef
117
+
118
+ def get_register_binding(self, name):
119
+ """Get the type binding for a register
120
+
121
+ Registers themselves don't have specific types.
122
+ However, this only gets used in uninitialized value analysis;
123
+ it only covers the type a register has before it's
124
+ written by the program.
125
+ """
126
+ if name in self._registers:
127
+ return self._registers[name]
128
+ return None
129
+
130
+ def bind_address(self, addr, typedef):
131
+ if addr in self._addrs:
132
+ raise ValueError(f"Already have a binding for {addr}")
133
+ self._addrs[addr] = typedef
134
+
135
+ def get_address_binding(self, addr):
136
+ if addr in self._addrs:
137
+ return self._addrs[addr]
138
+ return None
139
+
140
+ def allocate(self, typedef):
141
+ addr = self.state.heap.allocate(ctypes.sizeof(typedef))
142
+
143
+ rep = self.ctype_to_bv(typedef)
144
+
145
+ self._allocations.setdefault(typedef, set()).add(addr)
146
+ log.debug(f"Storing {rep} at {addr:x}")
147
+ self.state.memory.store(addr, rep)
148
+ return addr
149
+
150
+ def get_allocs_for_type(self, typedef):
151
+ if typedef in self._allocations:
152
+ return list(self._allocations[typedef])
153
+ return list()
154
+
155
+ def pp(self, log, all_allocs=True):
156
+ log("Register Bindings")
157
+ for reg_name, reg_def in self._registers.items():
158
+ log(f"\t{reg_name} -> {reg_def}")
159
+ log("Symbol Bindings")
160
+ for sym_name, sym_def in self._symbols.items():
161
+ if sym_name in self._sym_values:
162
+ log(f"\t{sym_name} -> {sym_def} ({self._sym_values[sym_name]:x})")
163
+ else:
164
+ log(f"\t{sym_name} -> {sym_def} (No Value)")
165
+ if all_allocs:
166
+ log("Allocations:")
167
+ for typedef, addrs in self._allocations.items():
168
+ log(f"- {typedef}")
169
+ for addr in addrs:
170
+ log(f"\t- {addr:x}")
@@ -0,0 +1,25 @@
1
+ import angr
2
+
3
+
4
+ def print_state(log, state, tag):
5
+ """Pretty-print data contained in an execution state
6
+
7
+ This prints useful registers, memory, and state constraints.
8
+ """
9
+ log(f"{state}: {tag}")
10
+ if not state.regs.rip.symbolic:
11
+ try:
12
+ block = state.block()
13
+ block.pp()
14
+ except angr.errors.SimEngineError:
15
+ log("(Invalid IP!)")
16
+ else:
17
+ log("(Exit state)")
18
+ log("Registers")
19
+ state.registers.pp(log)
20
+ log("Memory")
21
+ state.memory.pp(log)
22
+ log("Constraints:")
23
+ for expr in state.solver.constraints:
24
+ log(f"\t{expr}")
25
+ state.typedefs.pp(log)
@@ -0,0 +1,315 @@
1
+ import logging
2
+
3
+ import claripy
4
+
5
+ log = logging.getLogger(__name__)
6
+
7
+
8
+ class ClaripyVisitor:
9
+ """Abstract class for a claripy AST visitor"""
10
+
11
+ def visit_int(self, v, **kwargs):
12
+ raise NotImplementedError(f"{type(self)} missing Integer")
13
+
14
+ def visit_bvv(self, v, **kwargs):
15
+ raise NotImplementedError(f"{type(self)} missing BVV")
16
+
17
+ def visit_bvs(self, v, **kwargs):
18
+ raise NotImplementedError(f"{type(self)} missing BVS")
19
+
20
+ def visit_add(self, v, **kwargs):
21
+ raise NotImplementedError(f"{type(self)} missing Add")
22
+
23
+ def visit_mul(self, v, **kwargs):
24
+ raise NotImplementedError(f"{type(self)} missing Mul")
25
+
26
+ def visit_bwor(self, v, **kwargs):
27
+ raise NotImplementedError(f"{type(self)} missing BWOr")
28
+
29
+ def visit_concat(self, v, **kwargs):
30
+ raise NotImplementedError(f"{type(self)} missing Concat")
31
+
32
+ def visit_extract(self, v, **kwargs):
33
+ raise NotImplementedError(f"{type(self)} missing Extract")
34
+
35
+ def visit_if(self, v, **kwargs):
36
+ raise NotImplementedError(f"{type(self)} missing If")
37
+
38
+ def visit_reverse(self, v, **kwargs):
39
+ raise NotImplementedError(f"{type(self)} missing Reverse")
40
+
41
+ def visit_lshift(self, v, **kwargs):
42
+ raise NotImplementedError(f"{type(self)} missing LShift")
43
+
44
+ def visit_rshift(self, v, **kwargs):
45
+ raise NotImplementedError(f"{type(self)} missing RShift")
46
+
47
+ def visit_zeroext(self, v, **kwargs):
48
+ raise NotImplementedError(f"{type(self)} missing ZeroExt")
49
+
50
+ def visit(self, v, **kwargs):
51
+ """Perform the visitor pattern on a claripy expression."""
52
+ # TODO: This is very far from complete.
53
+ # claripy supports a dizzying array of bitvector operations.
54
+ # It's easier to populate them as I find them.
55
+ if isinstance(v, int):
56
+ # v is a python integer.
57
+ # I occasionally see an int instead of a BVV.
58
+ # I don't exactly know why.
59
+ return self.visit_int(v, **kwargs)
60
+ elif v.op == "BVV":
61
+ # v is a concrete value
62
+ return self.visit_bvv(v, **kwargs)
63
+ elif v.op == "BVS":
64
+ # v is a symbol
65
+ return self.visit_bvs(v, **kwargs)
66
+ elif v.op == "__add__":
67
+ # v = sum(*X), for two or more expressions x in X.
68
+ return self.visit_add(v, **kwargs)
69
+ elif v.op == "__mul__":
70
+ # v = prod(*X), for two or more expressions x in X.
71
+ return self.visit_mul(v, **kwargs)
72
+ elif v.op == "__or__":
73
+ # v = BWOr(*X) for two or more expressions x in X.
74
+ return self.visit_bwor(v, **kwargs)
75
+ elif v.op == "Concat":
76
+ # v = Concat(*X), for two or more expressions x in X.
77
+ return self.visit_concat(v, **kwargs)
78
+ elif v.op == "If":
79
+ # v is ITE(x, y, z)
80
+ return self.visit_if(v, **kwargs)
81
+ elif v.op == "Reverse":
82
+ # v is Reverse(x)
83
+ return self.visit_reverse(v, **kwargs)
84
+ elif v.op == "Extract":
85
+ # v is x[a:b], for expression x and ints a and b.
86
+ return self.visit_extract(v, **kwargs)
87
+ elif v.op == "__lshift__":
88
+ # v is x << y, for expressions x and y
89
+ return self.visit_lshift(v, **kwargs)
90
+ elif v.op == "LShR":
91
+ # v is x >>> y, for expressions x and y
92
+ return self.visit_rshift(v, **kwargs)
93
+ elif v.op == "ZeroExt":
94
+ # v is ZeroExt(v, a) for expression x and int a
95
+ return self.visit_zeroext(v, **kwargs)
96
+ else:
97
+ # v is something I haven't thought of yet.
98
+ raise NotImplementedError(f"Unknown op {v.op}")
99
+
100
+
101
+ class ConditionalVisitor(ClaripyVisitor):
102
+ """Visitor that splits a conditional expression into its possible evaluations."""
103
+
104
+ def visit_int(self, v):
105
+ # Integers are leaf ASTs, and never conditional.
106
+ return [(v, claripy.BoolV(True))]
107
+
108
+ def visit_bvv(self, v):
109
+ # BVVs are leaf ASTs, and never conditional.
110
+ return [(v, claripy.BoolV(True))]
111
+
112
+ def visit_bvs(self, v):
113
+ # BVSs are leaf ASTs, and never conditional.
114
+ return [(v, claripy.BoolV(True))]
115
+
116
+ def visit_add(self, v):
117
+ # Addition can produce all combinations of evaluations
118
+ # of the argument expressions.
119
+ out = self.visit(v.args[0])
120
+ for arg in v.args[1:]:
121
+ old_out = out
122
+ out = list()
123
+ for res2, guard2 in self.visit(arg):
124
+ for res1, guard1 in old_out:
125
+ res = res1 + res2
126
+ guard = claripy.And(guard1, guard2)
127
+ out.append((res, guard))
128
+ return out
129
+
130
+ def visit_mul(self, v):
131
+ # Multiplication can produce all combinations of evaluations
132
+ # of the argument expressions.
133
+ out = self.visit(v.args[0])
134
+ for arg in v.args[1:]:
135
+ old_out = out
136
+ out = list()
137
+ for res2, guard2 in self.visit(arg):
138
+ for res1, guard1 in old_out:
139
+ res = res1 * res2
140
+ guard = claripy.And(guard1, guard2)
141
+ out.append((res, guard))
142
+ return out
143
+
144
+ def visit_concat(self, v):
145
+ # Concatenation can produce all combinations of evaluations
146
+ # of the argument expressions.
147
+ out = self.visit(v.args[0])
148
+ for arg in v.args[1:]:
149
+ old_out = out
150
+ out = list()
151
+ for res2, guard2 in self.visit(arg):
152
+ for res1, guard1 in old_out:
153
+ res = res1.concat(res2)
154
+ guard = claripy.And(guard1, guard2)
155
+ out.append((res, guard))
156
+ return out
157
+
158
+ def visit_extract(self, v):
159
+ # Extraction produces one expression per evaluation
160
+ # of the main argument. The other two are always ints.
161
+ a = v.args[0]
162
+ b = v.args[1]
163
+ res = self.visit(v.args[2])
164
+ res = list(map(lambda x: (x[0][a:b], x[1]), res))
165
+ return res
166
+
167
+ def visit_if(self, v):
168
+ # ITE produces the union of the results of
169
+ # the "then" and "else" expressions.
170
+ # The condition itself is ignored.
171
+ out = list()
172
+ out.extend(
173
+ map(lambda x: (x[0], claripy.And(x[1], v.args[0])), self.visit(v.args[1]))
174
+ )
175
+ out.extend(
176
+ map(
177
+ lambda x: (x[0], claripy.And(x[1], claripy.Not(v.args[0]))),
178
+ self.visit(v.args[2]),
179
+ )
180
+ )
181
+ return out
182
+
183
+ def visit_reverse(self, v):
184
+ # Reversal produces one expression per evaluation of the argument.
185
+ return list(map(lambda x: (claripy.Reverse(x[0]), x[1]), self.visit(v.args[0])))
186
+
187
+
188
+ class EvalVisitor(ClaripyVisitor):
189
+ """Visitor that concretizes a symbolic expression.
190
+
191
+ Uses a mapping from variables to concrete values.
192
+ This implementation requires a complete mapping;
193
+ it will raise an exception if it encounters
194
+ a symbol for which it does not have a value.
195
+ """
196
+
197
+ def visit_int(self, v, bindings=None):
198
+ return v
199
+
200
+ def visit_bvv(self, v, bindings=None):
201
+ return v
202
+
203
+ def visit_bvs(self, v, bindings=None):
204
+ if v.args[0] not in bindings:
205
+ raise KeyError("Missing binding for {v}")
206
+ return bindings[v.args[0]]
207
+
208
+ def visit_add(self, v, bindings=None):
209
+ # Eval of sum is sum of evals
210
+ out = self.visit(v.args[0], bindings=bindings)
211
+ for x in v.args[1:]:
212
+ out += self.visit(x, bindings=bindings)
213
+ return out
214
+
215
+ def visit_concat(self, v, bindings=None):
216
+ # Eval of concat is concat of evals
217
+ return claripy.Concat(map(lambda x: self.visit(x, bindings=bindings), v.args))
218
+
219
+ def visit_extract(self, v, bindings=None):
220
+ # Extract only hase one BV argument; the range limits are ints.
221
+ a = v.args[0]
222
+ b = v.args[1]
223
+ return self.visit(v.args[2], bindings=bindings)[a:b]
224
+
225
+ def visit_if(self, v, bindings=None):
226
+ # Concretize all three args of the ITE expression.
227
+ i = self.visit(v.args[0], bindings=bindings)
228
+ t = self.visit(v.args[1], bindings=bindings)
229
+ e = self.visit(v.args[2], bindings=bindings)
230
+ return claripy.If(i, t, e)
231
+
232
+ def visit_reverse(self, v, bindings=None):
233
+ # Reverse is a simple unary; reverse the result from the arg.
234
+ res = claripy.Reverse(self.visit(v.args[0], bindings=bindings))
235
+ return res
236
+
237
+
238
+ class PPrintVisitor(ClaripyVisitor):
239
+ def visit_int(self, v, **kwargs):
240
+ raise NotImplementedError("Why?")
241
+
242
+ def visit_bvv(self, v, **kwargs):
243
+ indent = kwargs.get("indent", 0)
244
+ out = kwargs.get("out", print)
245
+ out(" " * indent + str(v))
246
+
247
+ def visit_bvs(self, v, **kwargs):
248
+ indent = kwargs.get("indent", 0)
249
+ out = kwargs.get("out", print)
250
+ out(" " * indent + str(v))
251
+
252
+ def visit_add(self, v, **kwargs):
253
+ indent = kwargs.get("indent", 0)
254
+ out = kwargs.get("out", print)
255
+ out(" " * indent + "Add:")
256
+ for arg in v.args:
257
+ self.visit(arg, indent=indent + 1, out=out)
258
+
259
+ def visit_bwor(self, v, **kwargs):
260
+ indent = kwargs.get("indent", 0)
261
+ out = kwargs.get("out", print)
262
+ out(" " * indent + "BWOr:")
263
+ for arg in v.args:
264
+ self.visit(arg, indent=indent + 1, out=out)
265
+
266
+ def visit_concat(self, v, **kwargs):
267
+ indent = kwargs.get("indent", 0)
268
+ out = kwargs.get("out", print)
269
+ out(" " * indent + "Concat:")
270
+ for arg in v.args:
271
+ self.visit(arg, indent=indent + 1, out=out)
272
+
273
+ def visit_extract(self, v, **kwargs):
274
+ indent = kwargs.get("indent", 0)
275
+ out = kwargs.get("out", print)
276
+ out(" " * indent + f"Extract [{v.args[0]}:{v.args[1]}]:")
277
+ self.visit(v.args[2], indent=indent + 1, out=out)
278
+
279
+ def visit_if(self, v, **kwargs):
280
+ indent = kwargs.get("indent", 0)
281
+ out = kwargs.get("out", print)
282
+ out(" " * indent + "If:")
283
+ self.visit(v.args[0], indent=indent + 1, out=out)
284
+ out(" " * indent + "Then:")
285
+ self.visit(v.args[1], indent=indent + 1, out=out)
286
+ out(" " * indent + "Else:")
287
+ self.visit(v.args[2], indent=indent + 1, out=out)
288
+
289
+ def visit_reverse(self, v, **kwargs):
290
+ indent = kwargs.get("indent", 0)
291
+ out = kwargs.get("out", print)
292
+ out(" " * indent + "Reverse:")
293
+ self.visit(v.args[0], indent=indent + 1, out=out)
294
+
295
+ def visit_lshift(self, v, **kwargs):
296
+ indent = kwargs.get("indent", 0)
297
+ out = kwargs.get("out", print)
298
+ out(" " * indent + "LShift:")
299
+ self.visit(v.args[0], indent=indent + 1, out=out)
300
+ out(" " * indent + "By:")
301
+ self.visit(v.args[1], indent=indent + 1, out=out)
302
+
303
+ def visit_rshift(self, v, **kwargs):
304
+ indent = kwargs.get("indent", 0)
305
+ out = kwargs.get("out", print)
306
+ out(" " * indent + "RShift (Logical):")
307
+ self.visit(v.args[0], indent=indent + 1, out=out)
308
+ out(" " * indent + "By:")
309
+ self.visit(v.args[1], indent=indent + 1, out=out)
310
+
311
+ def visit_zeroext(self, v, **kwargs):
312
+ indent = kwargs.get("indent", 0)
313
+ out = kwargs.get("out", print)
314
+ out(" " * indent + f"ZeroExt[{v.args[0]}]:")
315
+ self.visit(v.args[1], indent=indent + 1, out=out)
@@ -0,0 +1,106 @@
1
+ import logging
2
+
3
+ from ... import emulators, hinting, state
4
+ from .. import analysis
5
+ from .angr.nwbt import configure_nwbt_plugins, configure_nwbt_strategy
6
+ from .angr.utils import print_state
7
+
8
+ log = logging.getLogger(__name__)
9
+ hinter = hinting.get_hinter(__name__)
10
+
11
+
12
+ class AngrNWBTAnalysis(analysis.Analysis):
13
+ name = "angr-nwbt"
14
+ description = "Angr-based Not-Written-By-Trace detection and correction"
15
+ version = "0.0.1"
16
+
17
+ def __init__(self, *args, initfunc=None, max_steps=500, **kwargs):
18
+ self.steps_left = max_steps
19
+ self.initfunc = initfunc
20
+
21
+ def analysis_preinit(self, emu):
22
+ log.info("Registering plugins")
23
+ configure_nwbt_plugins(emu)
24
+
25
+ def analysis_init(self, emu):
26
+ configure_nwbt_strategy(emu)
27
+ if self.initfunc is not None:
28
+ self.initfunc(self, emu.entry)
29
+
30
+ def run(self, machine: state.Machine):
31
+ cpu = machine.get_cpu()
32
+ emu = emulators.AngrEmulator(
33
+ cpu.platform, preinit=self.analysis_preinit, init=self.analysis_init
34
+ )
35
+ machine.apply(emu)
36
+
37
+ # Extract typedef info from the CPU state,
38
+ # and bind it to the machine state
39
+ for item in cpu:
40
+ if isinstance(item, state.Register):
41
+ if item.get_type() is not None:
42
+ log.debug(f"Applying type for {item}")
43
+ emu.state.typedefs.bind_register(item.name, item.get_type())
44
+ for item in machine:
45
+ if isinstance(item, state.memory.code.Executable):
46
+ # TODO: Code is also memory, so it should have types...?
47
+ pass
48
+ elif isinstance(item, state.memory.Memory):
49
+ for offset, value in item.items():
50
+ if value.get_type() is not None:
51
+ addr = item.address + offset
52
+ log.debug(f"Applying type for {hex(addr)}")
53
+ emu.state.typedefs.bind_address(addr, value.get_type())
54
+ else:
55
+ if not isinstance(item, state.cpus.CPU) and item.type is not None:
56
+ raise NotImplementedError(
57
+ f"Applying typedef {item.get_type()} of type {type(item)} not implemented"
58
+ )
59
+
60
+ while (self.steps_left is None or self.steps_left > 0) and not self.step(emu):
61
+ if self.steps_left is not None:
62
+ self.steps_left -= 1
63
+
64
+ def _report_status(self, emu):
65
+ for st in emu.mgr.active:
66
+ dis = "\r".join(map(str, st.block().disassembly.insns))
67
+ log.debug(f"Active state: {dis}")
68
+ for st in emu.mgr.unconstrained:
69
+ hint = hinting.OutputHint(
70
+ message="State left the program",
71
+ registers=st.registers.create_hint(),
72
+ memory=st.memory.create_hint(),
73
+ )
74
+ hinter.info(hint)
75
+ for st in emu.mgr.deadended:
76
+ hint = hinting.OutputHint(
77
+ message="State exited due to breakpoint",
78
+ registers=st.registers.create_hint(),
79
+ memory=st.memory.create_hint(),
80
+ )
81
+ hinter.info(hint)
82
+ for st in emu.mgr.unsat:
83
+ hint = hinting.OutputHint(
84
+ message="State cannot continue; constraints unsat",
85
+ registers=st.registers.create_hint(),
86
+ memory=st.memory.create_hint(),
87
+ )
88
+ hinter.info(hint)
89
+ for err in emu.mgr.errored:
90
+ print_state(log.info, err.state, "error")
91
+ log.error(
92
+ "\tError:",
93
+ exc_info=(type(err.error), err.error, err.error.__traceback__),
94
+ )
95
+ log.info(f"Summary: {emu.mgr}")
96
+
97
+ def step(self, emu):
98
+ # Report our current status
99
+ self._report_status(emu)
100
+ # In our case, return states are unconstrained.
101
+ emu.mgr.move(from_stash="deadended", to_stash="done")
102
+ emu.mgr.move(from_stash="unconstrained", to_stash="done")
103
+ # Drop unsat states once we've logged them.
104
+ emu.mgr.drop(stash="unsat")
105
+ self._report_status(emu)
106
+ return emu.step()