mirror of
https://github.com/cyber-defence-campus/mole
synced 2026-06-20 13:19:21 +00:00
Add max_slice_depth config parameter (#55)
This commit is contained in:
@@ -5,6 +5,12 @@ settings:
|
||||
value: 3
|
||||
min_value: -1
|
||||
max_value: 99
|
||||
max_slice_depth:
|
||||
name: max_slice_depth
|
||||
help: maximum slice depth to stop the search
|
||||
value: -1
|
||||
min_value: -1
|
||||
max_value: 9999
|
||||
highlight_color:
|
||||
name: highlight_color
|
||||
help: color used to highlight paths
|
||||
|
||||
+27
-4
@@ -174,6 +174,23 @@ class Controller:
|
||||
max_value=mfd_max_value
|
||||
)
|
||||
})
|
||||
msd_name = "max_slice_depth"
|
||||
msd_settings = settings.get(msd_name, None)
|
||||
if msd_settings:
|
||||
msd_value = int(msd_settings.get("value", None))
|
||||
msd_min_value = int(msd_settings.get("min_value", None))
|
||||
msd_max_value = int(msd_settings.get("max_value", None))
|
||||
msd_value = min(max(msd_value, msd_min_value), msd_max_value)
|
||||
msd_help = msd_settings.get("help", "")
|
||||
parsed_conf["settings"].update({
|
||||
msd_name: SpinboxSetting(
|
||||
name=msd_name,
|
||||
value=msd_value,
|
||||
help=msd_help,
|
||||
min_value=msd_min_value,
|
||||
max_value=msd_max_value
|
||||
)
|
||||
})
|
||||
col_name = "highlight_color"
|
||||
col_settings = settings.get(col_name, None)
|
||||
if col_settings:
|
||||
@@ -394,6 +411,7 @@ class Controller:
|
||||
self,
|
||||
bv: bn.BinaryView,
|
||||
max_call_level: int = None,
|
||||
max_slice_depth: int = None,
|
||||
enable_all_funs: bool = False,
|
||||
but: qtw.QPushButton = None,
|
||||
tbl: qtw.QTableWidget = None
|
||||
@@ -426,6 +444,7 @@ class Controller:
|
||||
ctr=self,
|
||||
runs_headless=self._runs_headless,
|
||||
max_call_level=max_call_level,
|
||||
max_slice_depth=max_slice_depth,
|
||||
enable_all_funs=enable_all_funs,
|
||||
log=self._log
|
||||
)
|
||||
@@ -794,6 +813,7 @@ class MediumLevelILBackwardSlicerThread(bn.BackgroundTaskThread):
|
||||
ctr: Controller,
|
||||
runs_headless: bool = False,
|
||||
max_call_level: int = None,
|
||||
max_slice_depth: int = None,
|
||||
enable_all_funs: bool = False,
|
||||
tag: str = "BackSlicer",
|
||||
log: Logger = Logger()
|
||||
@@ -806,6 +826,7 @@ class MediumLevelILBackwardSlicerThread(bn.BackgroundTaskThread):
|
||||
self._ctr: Controller = ctr
|
||||
self._runs_headless: bool = runs_headless
|
||||
self._max_call_level: int = max_call_level
|
||||
self._max_slice_depth: int = max_slice_depth
|
||||
self._enable_all_funs: bool = enable_all_funs
|
||||
self._tag: str = tag
|
||||
self._log: Logger = log
|
||||
@@ -833,11 +854,12 @@ class MediumLevelILBackwardSlicerThread(bn.BackgroundTaskThread):
|
||||
if not snk_funs:
|
||||
self._log.warn(self._tag, "No sink functions configured")
|
||||
|
||||
# Find paths
|
||||
max_call_level = self._max_call_level
|
||||
if max_call_level is None:
|
||||
# Settings
|
||||
settings = self._ctr.get_settings()
|
||||
max_call_level = settings.get("max_call_level").value
|
||||
max_call_level = self._max_call_level if self._max_call_level is not None else settings.get("max_call_level").value
|
||||
max_slice_depth = self._max_slice_depth if self._max_slice_depth is not None else settings.get("max_slice_depth").value
|
||||
|
||||
# Find paths
|
||||
if src_funs and snk_funs:
|
||||
for i, snk_fun in enumerate(snk_funs):
|
||||
if self.cancelled: break
|
||||
@@ -846,6 +868,7 @@ class MediumLevelILBackwardSlicerThread(bn.BackgroundTaskThread):
|
||||
bv=self._bv,
|
||||
sources=src_funs,
|
||||
max_call_level=max_call_level,
|
||||
max_slice_depth=max_slice_depth,
|
||||
found_path=self._ctr.add_path_to_view,
|
||||
canceled=lambda:self.cancelled,
|
||||
tag=self._tag,
|
||||
|
||||
+2
-1
@@ -249,6 +249,7 @@ class SinkFunction(Function):
|
||||
bv: bn.BinaryView,
|
||||
sources: List[SourceFunction],
|
||||
max_call_level: int,
|
||||
max_slice_depth: int,
|
||||
found_path: Callable[[Path], None],
|
||||
canceled: Callable[[], bool],
|
||||
tag: str = None,
|
||||
@@ -309,7 +310,7 @@ class SinkFunction(Function):
|
||||
for src_inst in src_insts:
|
||||
if canceled(): break
|
||||
# Find paths
|
||||
for insts, call_graph in slicer.find_paths(par_var, src_inst):
|
||||
for insts, call_graph in slicer.find_paths(par_var, src_inst, max_slice_depth):
|
||||
# Prepend sink instruction
|
||||
insts.insert(0, snk_inst)
|
||||
# Find split between sink and source originating instructions
|
||||
|
||||
+9
-6
@@ -467,19 +467,22 @@ class MediumLevelILBackwardSlicer:
|
||||
def find_paths(
|
||||
self,
|
||||
snk_inst: bn.MediumLevelILInstruction,
|
||||
src_inst: bn.MediumLevelILInstruction
|
||||
src_inst: bn.MediumLevelILInstruction,
|
||||
max_slice_depth: int
|
||||
) -> List[Tuple[List[bn.MediumLevelILInstruction], MediumLevelILFunctionGraph]]:
|
||||
"""
|
||||
This method finds all simple paths from `snk_inst` to `src_inst`. For each found path, the
|
||||
following is returned: First, a list of instructions belonging to the path. And second, a
|
||||
function call graph, where nodes and edges belonging to the path, have an attribute
|
||||
`in_path` set to `True`.
|
||||
This method finds all simple paths from `snk_inst` to `src_inst`, with optionally limiting
|
||||
path length by `max_slice_depth`. For each found path, the following is returned: First, a
|
||||
list of instructions belonging to the path. And second, a function call graph, where nodes
|
||||
and edges belonging to the path, have an attribute `in_path` set to `True`.
|
||||
"""
|
||||
paths = []
|
||||
# Find all simple paths
|
||||
try:
|
||||
if max_slice_depth is not None and max_slice_depth < 0:
|
||||
max_slice_depth = None
|
||||
simple_paths: List[List[bn.MediumLevelILInstruction]] = list(
|
||||
nx.all_simple_paths(self._inst_graph, snk_inst, src_inst)
|
||||
nx.all_simple_paths(self._inst_graph, snk_inst, src_inst, max_slice_depth)
|
||||
)
|
||||
except (nx.NodeNotFound, nx.NetworkXNoPath):
|
||||
return paths
|
||||
|
||||
@@ -323,6 +323,19 @@ class SidebarWidget(bnui.SidebarWidget):
|
||||
mcl_lbl = qtw.QLabel(f"{mcl_name:s}:")
|
||||
mcl_lbl.setToolTip(mcl.help)
|
||||
com_lay.addRow(mcl_lbl, mcl.widget)
|
||||
msd_name = "max_slice_depth"
|
||||
msd = settings.get(msd_name, None)
|
||||
if msd:
|
||||
msd.widget = qtw.QSpinBox()
|
||||
msd.widget.setRange(msd.min_value, msd.max_value)
|
||||
msd.widget.setValue(msd.value)
|
||||
msd.widget.setToolTip(msd.help)
|
||||
msd.widget.valueChanged.connect(
|
||||
lambda value, setting=msd: self._ctr.spinbox_change_value(setting, value)
|
||||
)
|
||||
msd_lbl = qtw.QLabel(f"{msd_name:s}:")
|
||||
msd_lbl.setToolTip(msd.help)
|
||||
com_lay.addRow(msd_lbl, msd.widget)
|
||||
com_wid.setLayout(com_lay)
|
||||
com_box_lay = qtw.QVBoxLayout()
|
||||
com_box_lay.addWidget(com_wid)
|
||||
|
||||
+6
-1
@@ -33,6 +33,11 @@ def main() -> None:
|
||||
type=int, default=None,
|
||||
help="backward slicing visits called functions up to the given level"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--max_slice_depth",
|
||||
type=int, default=None,
|
||||
help="maximum slice depth to stop the search"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export_paths_to_json_file",
|
||||
help="export identified paths in JSON format"
|
||||
@@ -51,7 +56,7 @@ def main() -> None:
|
||||
bv = bn.load(args.file)
|
||||
bv.update_analysis_and_wait()
|
||||
# Analyze binary with Mole
|
||||
paths = ctr.find_paths(bv, args.max_call_level)
|
||||
paths = ctr.find_paths(bv, args.max_call_level, args.max_slice_depth)
|
||||
# Export identified paths
|
||||
if args.export_paths_to_yml_file or args.export_paths_to_json_file:
|
||||
# Calculate SHA1 hash of binary
|
||||
|
||||
+14
-3
@@ -65,6 +65,13 @@ class TestData(unittest.TestCase):
|
||||
max_value=99,
|
||||
help="backward slicing visits called functions up to the given level"
|
||||
),
|
||||
"max_slice_depth": SpinboxSetting(
|
||||
name="max_slice_depth",
|
||||
value=-1,
|
||||
min_value=-1,
|
||||
max_value=9999,
|
||||
help="maximum slice depth to stop the search"
|
||||
),
|
||||
"highlight_color": ComboboxSetting(
|
||||
name="highlight_color",
|
||||
value="Red",
|
||||
@@ -176,10 +183,14 @@ class TestData(unittest.TestCase):
|
||||
return
|
||||
|
||||
def test_serialize_spinbox_settings(self) -> None:
|
||||
setting = self.conf.settings["max_call_level"]
|
||||
settings = {
|
||||
"max_call_level": self.conf.settings["max_call_level"].to_dict(),
|
||||
"max_slice_depth": self.conf.settings["max_slice_depth"].to_dict()
|
||||
}
|
||||
|
||||
# Serialize
|
||||
yaml.safe_dump(
|
||||
setting.to_dict(),
|
||||
settings,
|
||||
self.tf,
|
||||
sort_keys=False,
|
||||
default_style=None,
|
||||
@@ -190,7 +201,7 @@ class TestData(unittest.TestCase):
|
||||
self.tf.seek(0)
|
||||
ydoc = yaml.safe_load(self.tf)
|
||||
# Assert
|
||||
self.assertEqual(ydoc, setting, "Serialization error of 'SpinboxSetting'")
|
||||
self.assertEqual(ydoc, settings, "Serialization error of 'SpinboxSetting'")
|
||||
return
|
||||
|
||||
def test_serialize_combobox_settings(self) -> None:
|
||||
|
||||
Reference in New Issue
Block a user