Skip to content

Commit 2ad4563

Browse files
committed
Tanto 2.0!
1 parent 4b29b8b commit 2ad4563

16 files changed

+2031
-774
lines changed

LICENSE.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright (c) 2021-2024 Vector 35 Inc
1+
Copyright (c) 2021-2025 Vector 35 Inc
22

33
Permission is hereby granted, free of charge, to any person obtaining a copy
44
of this software and associated documentation files (the "Software"), to

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Author: **Vector 35, Inc.**
33

44
## Description:
5-
Tantō is a plugin for [Binary Ninja](https://binary.ninja/) that slices functions so you can understand them faster.
5+
Tantō is a plugin for [Binary Ninja](https://binary.ninja/) that slices programs so you can understand them faster.
66

77
<img align="center" src="https://github.com/Vector35/tanto/blob/master/tanto_preview.png?raw=true">
88

@@ -12,7 +12,7 @@ For a more detailed explanation of what Tantō does, please check out [this blog
1212

1313
This plugin requires the following minimum version of Binary Ninja:
1414

15-
* 3.0.3223
15+
* 4.0.5336
1616

1717
## License
1818

__init__.py

Lines changed: 11 additions & 768 deletions
Large diffs are not rendered by default.

helpers.py

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
# Copyright(c) 2022-2025 Vector 35 Inc
2+
#
3+
# Permission is hereby granted, free of charge, to any person obtaining a copy
4+
# of this software and associated documentation files (the "Software"), to
5+
# deal in the Software without restriction, including without limitation the
6+
# rights to use, copy, modify, merge, publish, distribute, sublicense, and / or
7+
# sell copies of the Software, and to permit persons to whom the Software is
8+
# furnished to do so, subject to the following conditions:
9+
#
10+
# The above copyright notice and this permission notice shall be included in
11+
# all copies or substantial portions of the Software.
12+
#
13+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19+
# IN THE SOFTWARE.
20+
21+
from binaryninjaui import UIContext
22+
23+
from binaryninja import BinaryView
24+
from binaryninja import Function, LowLevelILFunction, MediumLevelILFunction, HighLevelILFunction
25+
from binaryninja import BasicBlock, LowLevelILBasicBlock, MediumLevelILBasicBlock, HighLevelILBasicBlock
26+
from binaryninja import LowLevelILInstruction, MediumLevelILInstruction, HighLevelILInstruction
27+
from binaryninja.log import log_error
28+
from binaryninja.enums import FunctionGraphType
29+
30+
from typing import Union, Optional
31+
32+
33+
BN_INVALID_EXPR = 0xffffffffffffffff
34+
35+
AnyFunction = Union[Function, LowLevelILFunction, MediumLevelILFunction, HighLevelILFunction]
36+
ILFunction = Union[LowLevelILFunction, MediumLevelILFunction, HighLevelILFunction]
37+
AnyBasicBlock = Union[BasicBlock, LowLevelILBasicBlock, MediumLevelILBasicBlock, HighLevelILBasicBlock]
38+
ILBasicBlock = Union[LowLevelILBasicBlock, MediumLevelILBasicBlock, HighLevelILBasicBlock]
39+
ILInstruction = Union[LowLevelILInstruction, MediumLevelILInstruction, HighLevelILInstruction]
40+
41+
42+
def get_disassembly_settings():
43+
view_context = UIContext.activeContext()
44+
view = view_context.getCurrentView().widget()
45+
return view.getDisassemblySettings()
46+
47+
48+
def get_insts(bb: BasicBlock) -> list[str]: # TODO Results are not str...
49+
if bb is None:
50+
return None
51+
if isinstance(bb, BasicBlock):
52+
return bb.disassembly_text
53+
else:
54+
return bb
55+
56+
57+
def get_basic_block_of_type(instr: LowLevelILInstruction, il_form: FunctionGraphType) -> Optional[AnyBasicBlock]:
58+
assert isinstance(il_form, FunctionGraphType)
59+
if il_form == FunctionGraphType.NormalFunctionGraph:
60+
return instr.il_basic_block.source_block
61+
elif il_form == FunctionGraphType.LiftedILFunctionGraph:
62+
return instr.function.source_function.get_lifted_il_at(instr.address).il_basic_block # Seemingly bugged
63+
elif il_form == FunctionGraphType.LowLevelILFunctionGraph:
64+
return instr.il_basic_block
65+
elif il_form == FunctionGraphType.LowLevelILSSAFormFunctionGraph:
66+
return instr.ssa_form.il_basic_block
67+
elif il_form == FunctionGraphType.MappedMediumLevelILFunctionGraph:
68+
return instr.mapped_medium_level_il.il_basic_block
69+
elif il_form == FunctionGraphType.MappedMediumLevelILSSAFormFunctionGraph:
70+
return instr.mapped_medium_level_il.ssa_form.il_basic_block
71+
elif il_form == FunctionGraphType.MediumLevelILFunctionGraph:
72+
return instr.mlil.il_basic_block
73+
elif il_form == FunctionGraphType.MediumLevelILSSAFormFunctionGraph:
74+
return instr.mlil.ssa_form.il_basic_block
75+
elif il_form == FunctionGraphType.HighLevelILFunctionGraph:
76+
return instr.hlil.il_basic_block
77+
elif il_form == FunctionGraphType.HighLevelILSSAFormFunctionGraph:
78+
return instr.hlil.ssa_form.il_basic_block
79+
elif il_form == FunctionGraphType.InvalidILViewType:
80+
return None
81+
else:
82+
log_error(f"IL form {il_form.name} not supported in Tanto")
83+
return None
84+
85+
86+
def get_function_of_type(func: Function, il_form: FunctionGraphType) -> Optional[AnyFunction]:
87+
assert isinstance(il_form, FunctionGraphType)
88+
if il_form == FunctionGraphType.NormalFunctionGraph:
89+
return func
90+
elif il_form == FunctionGraphType.LowLevelILFunctionGraph:
91+
return func.llil
92+
elif il_form == FunctionGraphType.LiftedILFunctionGraph:
93+
return func.lifted_il
94+
elif il_form == FunctionGraphType.LowLevelILSSAFormFunctionGraph:
95+
return func.llil.ssa_form
96+
elif il_form == FunctionGraphType.MediumLevelILFunctionGraph:
97+
return func.mlil
98+
elif il_form == FunctionGraphType.MediumLevelILSSAFormFunctionGraph:
99+
return func.mlil.ssa_form
100+
elif il_form == FunctionGraphType.MappedMediumLevelILFunctionGraph:
101+
return func.llil.mapped_medium_level_il
102+
elif il_form == FunctionGraphType.MappedMediumLevelILSSAFormFunctionGraph:
103+
return func.llil.mapped_medium_level_il.ssa_form
104+
elif il_form == FunctionGraphType.HighLevelILFunctionGraph:
105+
return func.hlil
106+
elif il_form == FunctionGraphType.HighLevelILSSAFormFunctionGraph:
107+
return func.hlil.ssa_form
108+
elif il_form == FunctionGraphType.InvalidILViewType:
109+
return None
110+
else:
111+
log_error(f"IL form {il_form.name} not supported in Tanto")
112+
return None
113+
114+
115+
def get_current_binary_view() -> Optional[BinaryView]:
116+
view_context = UIContext.activeContext()
117+
if view_context is None or view_context.getCurrentView() is None:
118+
return
119+
120+
return view_context.getCurrentView().getData()
121+
122+
123+
def get_current_source_function() -> Optional[Function]:
124+
view_context = UIContext.activeContext()
125+
if view_context is None or view_context.getCurrentView() is None:
126+
return
127+
128+
if (function := view_context.getCurrentView().getCurrentFunction()) is not None:
129+
return function
130+
131+
if (addr := get_current_address()) is not None and (bv := get_current_binary_view()) is not None and len(functions := bv.get_functions_containing(addr)) > 0:
132+
return functions[0]
133+
134+
135+
def get_current_il_function() -> Optional[AnyFunction]:
136+
view_context = UIContext.activeContext()
137+
if view_context is None or view_context.getCurrentViewFrame() is None:
138+
return None
139+
if (function := view_context.getCurrentView().getCurrentFunction()) is None:
140+
return None
141+
142+
return get_function_of_type(function, view_context.getCurrentViewFrame().getViewLocation().getILViewType().view_type)
143+
144+
145+
def get_current_il_basic_block() -> Optional[BasicBlock]:
146+
func = get_current_il_function()
147+
addr = get_current_address()
148+
149+
if func is None or addr is None or addr == 0:
150+
# log_error(f"Could not find function for location {hex(addr)}, {func}")
151+
return
152+
153+
if isinstance(func, Function):
154+
llil = func.get_low_level_il_at(addr)
155+
bb = llil.il_basic_block.source_block
156+
else:
157+
llil = func.source_function.get_low_level_il_at(addr)
158+
bb = None
159+
if llil is not None and bb is None:
160+
try:
161+
bb = get_basic_block_of_type(llil, func.il_form)
162+
except:
163+
# Fail silently because this most often happens at points where the user isn't trying to perform the action (switching tabs, etc)
164+
return
165+
166+
if llil is None or bb is None:
167+
log_error("Couldn't recover basic block. Please try again.")
168+
return
169+
170+
return bb
171+
172+
173+
def get_selected_inst() -> ILInstruction:
174+
view_context = UIContext.activeContext()
175+
if view_context is not None and view_context.getCurrentView() is not None:
176+
if (instr_index := view_context.getCurrentView().getCurrentILInstructionIndex()) != BN_INVALID_EXPR:
177+
return get_current_il_function()[instr_index]
178+
179+
180+
def get_selected_expr() -> ILInstruction:
181+
def traverser(inst, text):
182+
if text in str(inst):
183+
return inst
184+
185+
expr = None
186+
view_context = UIContext.activeContext()
187+
if view_context is not None and view_context.getCurrentView() is not None and (hts := view_context.getCurrentView().getHighlightTokenState()) is not None and hts.valid:
188+
if (inst := get_selected_inst()) is not None:
189+
for expr in inst.traverse(traverser, hts.token.text):
190+
pass
191+
if expr is not None:
192+
return expr
193+
return get_selected_inst()
194+
195+
196+
def instruction_contains_var(var_list, inst):
197+
for var in var_list:
198+
if inst.function.il_form in [FunctionGraphType.LowLevelILSSAFormFunctionGraph, FunctionGraphType.MediumLevelILSSAFormFunctionGraph, FunctionGraphType.MappedMediumLevelILSSAFormFunctionGraph, FunctionGraphType.HighLevelILSSAFormFunctionGraph]:
199+
for i in inst.function.get_ssa_var_uses(var) + [inst.function.get_ssa_var_definition(var)]:
200+
if inst.instr_index == i.instr_index:
201+
return True
202+
else:
203+
for i in inst.function.get_var_uses(var) + inst.function.get_var_definitions(var):
204+
if inst.instr_index == i.instr_index:
205+
return True
206+
return False
207+
208+
209+
def get_current_address() -> Optional[int]:
210+
view_context = UIContext.activeContext()
211+
if view_context is None or view_context.getCurrentView() is None:
212+
return
213+
214+
return view_context.getCurrentView().getCurrentOffset()

0 commit comments

Comments
 (0)