Skip to content

Commit 3095189

Browse files
authored
Merge pull request #13 from renato-osec/main
Added write methods
2 parents 9c16ddc + 0764a12 commit 3095189

File tree

5 files changed

+269
-2
lines changed

5 files changed

+269
-2
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ The following table details which integrations with Binary Ninja are currently s
5353
| `function_at` | Retrive the name of the function the address belongs to. |
5454
| `code_references` | Retrive names and addresses of functions that call the given function. |
5555
| `get_user_defined_type` | Retrive definition of a user defined type (struct, enumeration, typedef, union). |
56+
| `rename_variable` | Rename variable inside a given function. |
57+
| `retype_variable` | Retype variable inside a given function. |
58+
| `define_types` | Add type definitions from a C string type definition. |
59+
| `edit_function_signature` | Edit signature of a given function, given as a type string. |
5660

5761
## Prerequisites
5862

bridge/binja_mcp_bridge.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,33 @@ def list_methods(offset: int = 0, limit: int = 100) -> list:
5555
"""
5656
return safe_get("methods", {"offset": offset, "limit": limit})
5757

58+
@mcp.tool()
59+
def retype_variable(function_name: str, variable_name: str, type_str: str) -> str:
60+
"""
61+
Retype a variable in a function.
62+
"""
63+
return safe_get("retypeVariable", {"functionName": function_name, "variableName": variable_name, "type": type_str})
64+
65+
@mcp.tool()
66+
def rename_variable(function_name: str, variable_name: str, new_name: str) -> str:
67+
"""
68+
Rename a variable in a function.
69+
"""
70+
return safe_get("renameVariable", {"functionName": function_name, "variableName": variable_name, "newName": new_name})
71+
72+
@mcp.tool()
73+
def define_types(c_code: str) -> str:
74+
"""
75+
Define types from a C code string.
76+
"""
77+
return safe_get("defineTypes", {"cCode": c_code})
78+
79+
@mcp.tool()
80+
def edit_function_signature(function_name: str, signature: str) -> str:
81+
"""
82+
Edit the signature of a function.
83+
"""
84+
return safe_get("editFunctionSignature", {"functionName": function_name, "signature": signature})
5885

5986
@mcp.tool()
6087
def list_classes(offset: int = 0, limit: int = 100) -> list:

plugin/api/endpoints.py

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,3 +171,137 @@ def get_assembly_function(self, identifier: str) -> Optional[str]:
171171
except Exception as e:
172172
bn.log_error(f"Error getting assembly for function: {e}")
173173
return None
174+
175+
def define_types(self, c_code: str) -> Dict[str, str]:
176+
"""Define types from C code string
177+
178+
Args:
179+
c_code: C code string containing type definitions
180+
181+
Returns:
182+
Dictionary mapping type names to their string representations
183+
184+
Raises:
185+
RuntimeError: If no binary is loaded
186+
ValueError: If parsing the types fails
187+
"""
188+
if not self.binary_ops.current_view:
189+
raise RuntimeError("No binary loaded")
190+
191+
try:
192+
# Parse the C code string to get type objects
193+
parse_result = self.binary_ops.current_view.parse_types_from_string(c_code)
194+
195+
# Define each type in the binary view
196+
defined_types = {}
197+
for name, type_obj in parse_result.types.items():
198+
self.binary_ops.current_view.define_user_type(name, type_obj)
199+
defined_types[str(name)] = str(type_obj)
200+
201+
return defined_types
202+
except Exception as e:
203+
raise ValueError(f"Failed to define types: {str(e)}")
204+
205+
def rename_variable(self, function_name: str, old_name: str, new_name: str) -> Dict[str, str]:
206+
"""Rename a variable inside a function
207+
208+
Args:
209+
function_name: Name of the function containing the variable
210+
old_name: Current name of the variable
211+
new_name: New name for the variable
212+
213+
Returns:
214+
Dictionary with status message
215+
216+
Raises:
217+
RuntimeError: If no binary is loaded
218+
ValueError: If the function is not found or variable cannot be renamed
219+
"""
220+
if not self.binary_ops.current_view:
221+
raise RuntimeError("No binary loaded")
222+
223+
# Find the function by name
224+
function = self.binary_ops.get_function_by_name_or_address(function_name)
225+
if not function:
226+
raise ValueError(f"Function '{function_name}' not found")
227+
228+
# Try to rename the variable
229+
try:
230+
# Get the variable by name and rename it
231+
variable = function.get_variable_by_name(old_name)
232+
if not variable:
233+
raise ValueError(f"Variable '{old_name}' not found in function '{function_name}'")
234+
235+
variable.name = new_name
236+
return {"status": f"Successfully renamed variable '{old_name}' to '{new_name}' in function '{function_name}'"}
237+
except Exception as e:
238+
raise ValueError(f"Failed to rename variable: {str(e)}")
239+
240+
241+
def retype_variable(self, function_name: str, name: str, type_str: str) -> Dict[str, str]:
242+
"""Retype a variable inside a function
243+
244+
Args:
245+
function_name: Name of the function containing the variable
246+
name: Current name of the variable
247+
type: C type for the variable
248+
249+
Returns:
250+
Dictionary with status message
251+
252+
Raises:
253+
RuntimeError: If no binary is loaded
254+
ValueError: If the function is not found or variable cannot be retyped
255+
"""
256+
if not self.binary_ops.current_view:
257+
raise RuntimeError("No binary loaded")
258+
259+
# Find the function by name
260+
function = self.binary_ops.get_function_by_name_or_address(function_name)
261+
if not function:
262+
raise ValueError(f"Function '{function_name}' not found")
263+
264+
# Try to rename the variable
265+
try:
266+
# Get the variable by name and rename it
267+
variable = function.get_variable_by_name(name)
268+
if not variable:
269+
raise ValueError(f"Variable '{name}' not found in function '{function_name}'")
270+
271+
variable.type = type_str
272+
return {"status": f"Successfully retyped variable '{name}' to '{type_str}' in function '{function_name}'"}
273+
except Exception as e:
274+
raise ValueError(f"Failed to rename variable: {str(e)}")
275+
276+
277+
def edit_function_signature(self, function_name: str, signature: str) -> Dict[str, str]:
278+
"""Rename a variable inside a function
279+
280+
Args:
281+
function_name: Name of the function to edit the signature of
282+
signature: new signature to apply
283+
284+
Returns:
285+
Dictionary with status message
286+
287+
Raises:
288+
RuntimeError: If no binary is loaded
289+
ValueError: If the function is not found or variable cannot be renamed
290+
"""
291+
if not self.binary_ops.current_view:
292+
raise RuntimeError("No binary loaded")
293+
294+
# Find the function by name
295+
function = self.binary_ops.get_function_by_name_or_address(function_name)
296+
if not function:
297+
raise ValueError(f"Function '{function_name}' not found")
298+
299+
function.type = self.binary_ops.current_view.parse_type_string(signature)[0]
300+
301+
function.reanalyze(bn.FunctionUpdateType.UserFunctionUpdate)
302+
303+
try:
304+
return {"status": f"Successfully"}
305+
except Exception as e:
306+
raise ValueError(f"Failed to rename variable: {str(e)}")
307+

plugin/core/binary_operations.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,10 @@ def decompile_function(self, identifier: Union[str, int]) -> Optional[str]:
360360
if not func:
361361
return None
362362

363+
# analyze func in case it was skipped
364+
func.analysis_skipped = False
365+
self._current_view.update_analysis_and_wait()
366+
363367
try:
364368
# Try high-level IL first for best readability
365369
if hasattr(func, "hlil"):

plugin/server/http_server.py

Lines changed: 100 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,106 @@ def do_GET(self):
639639
"message": "No comment found for this function",
640640
}
641641
)
642+
elif path == "/editFunctionSignature":
643+
function_name = params.get("functionName")
644+
if not function_name:
645+
self._send_json_response(
646+
{"error": "Missing function name parameter"}, 400
647+
)
648+
return
649+
650+
signature = params.get("signature")
651+
if not signature:
652+
self._send_json_response(
653+
{"error": "Missing signature parameter"}, 400
654+
)
655+
return
656+
657+
try:
658+
self._send_json_response(self.endpoints.edit_function_signature(function_name, signature))
659+
except Exception as e:
660+
bn.log_error(f"Error handling editFunctionSignature request: {e}")
661+
self._send_json_response(
662+
{"error": str(e)},
663+
500,
664+
)
665+
elif path == "/retypeVariable":
666+
function_name = params.get("functionName")
667+
if not function_name:
668+
self._send_json_response(
669+
{"error": "Missing function name parameter"}, 400
670+
)
671+
return
672+
673+
variable_name = params.get("variableName")
674+
if not variable_name:
675+
self._send_json_response(
676+
{"error": "Missing variable name parameter"}, 400
677+
)
678+
return
679+
680+
type_str = params.get("type")
681+
if not type_str:
682+
self._send_json_response(
683+
{"error": "Missing type parameter"}, 400
684+
)
685+
return
686+
687+
try:
688+
self._send_json_response(self.endpoints.retype_variable(function_name, variable_name, type_str))
689+
except Exception as e:
690+
bn.log_error(f"Error handling retypeVariable request: {e}")
691+
self._send_json_response(
692+
{"error": str(e)},
693+
500,
694+
)
695+
elif path == "/renameVariable":
696+
function_name = params.get("functionName")
697+
if not function_name:
698+
self._send_json_response(
699+
{"error": "Missing function name parameter"}, 400
700+
)
701+
return
702+
703+
variable_name = params.get("variableName")
704+
if not variable_name:
705+
self._send_json_response(
706+
{"error": "Missing variable name parameter"}, 400
707+
)
708+
return
709+
710+
new_name = params.get("newName")
711+
if not new_name:
712+
self._send_json_response(
713+
{"error": "Missing new name parameter"}, 400
714+
)
715+
return
716+
717+
try:
718+
self._send_json_response(self.endpoints.rename_variable(function_name, variable_name, new_name))
719+
except Exception as e:
720+
bn.log_error(f"Error handling renameVariable request: {e}")
721+
self._send_json_response(
722+
{"error": str(e)},
723+
500,
724+
)
642725

726+
elif path == "/defineTypes":
727+
c_code = params.get("cCode")
728+
if not c_code:
729+
self._send_json_response(
730+
{"error": "Missing cCode parameter"}, 400
731+
)
732+
return
733+
734+
try:
735+
self._send_json_response(self.endpoints.define_types(c_code))
736+
except Exception as e:
737+
bn.log_error(f"Error handling defineTypes request: {e}")
738+
self._send_json_response(
739+
{"error": str(e)},
740+
500,
741+
)
643742
else:
644743
self._send_json_response({"error": "Not found"}, 404)
645744

@@ -1042,7 +1141,7 @@ def do_POST(self):
10421141
self._send_json_response({"error": "Invalid address format"}, 400)
10431142

10441143
elif path == "/getFunctionComment":
1045-
function_name = params.get("name") or params.get("functionName")
1144+
function_name = params.get("functionName") or params.get("name")
10461145
if not function_name:
10471146
self._send_json_response(
10481147
{
@@ -1075,7 +1174,6 @@ def do_POST(self):
10751174

10761175
else:
10771176
self._send_json_response({"error": "Not found"}, 404)
1078-
10791177
except Exception as e:
10801178
bn.log_error(f"Error handling POST request: {e}")
10811179
self._send_json_response({"error": str(e)}, 500)

0 commit comments

Comments
 (0)