Skip to content

Commit 31c7d79

Browse files
committed
Implement renaming individual variables.
- Works only with HLIL Variable Initializations. Assignments would also be appropriate, but I found that the model typically only returned "result" as the string. This was not useful.
1 parent 00ab48e commit 31c7d79

4 files changed

Lines changed: 91 additions & 22 deletions

File tree

__init__.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from binaryninja import PluginCommand
22
from . src.settings import OpenAISettings
3-
from . src.entry import check_function
4-
from . src.entry import rename_expression
3+
from . src.entry import check_function, rename_variable, \
4+
rename_all_variables_in_function
55

66
# Register the settings group in Binary Ninja to store the API key and model.
77
OpenAISettings()
@@ -20,10 +20,13 @@
2020
"OPENAI_API_KEY or modify the path in entry.py.",
2121
check_function)
2222

23-
PluginCommand.register_for_high_level_il_instruction("OpenAI\Rename Variable",
23+
PluginCommand.register_for_high_level_il_instruction("OpenAI\Rename Variable (HLIL)",
2424
"If the current expression is a HLIL Initialization " \
2525
"(HighLevelILVarInit), then query OpenAI to rename the " \
2626
"variable to what it believes is correct. If the expression" \
2727
"is not an HighLevelILVarInit, then do nothing. Requires " \
2828
"an internet connection and an API key. ",
29-
rename_expression)
29+
rename_variable)
30+
31+
PluginCommand.register_for_high_level_il_function("OpenAI\Rename All Variables (HLIL)",
32+
"", rename_all_variables_in_function)

src/agent.py

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import os
2-
from typing import Optional, Union
2+
from typing import Callable, Optional, Union
33
from pathlib import Path
44

55
import openai
@@ -9,7 +9,8 @@
99
from binaryninja.function import Function
1010
from binaryninja.lowlevelil import LowLevelILFunction
1111
from binaryninja.mediumlevelil import MediumLevelILFunction
12-
from binaryninja.highlevelil import HighLevelILFunction, HighLevelILInstruction
12+
from binaryninja.highlevelil import HighLevelILFunction, HighLevelILInstruction, \
13+
HighLevelILVarInit
1314
from binaryninja.settings import Settings
1415
from binaryninja import log, BinaryView
1516

@@ -24,10 +25,10 @@ class Agent:
2425
It is in IL_FORM. What does this function do?
2526
'''
2627

27-
rename_variable_question: str = '''
28-
In one word, what should the variable name be for the variable that is
29-
assigned to the result of the C expression:\n
30-
'''
28+
rename_variable_question: str = "In one word, what should the variable " \
29+
"be for the variable that is assigned to the result of the C " \
30+
"expression:\n"
31+
3132

3233
# A mapping of IL forms to their names.
3334
il_name: dict[type, str] = {
@@ -48,6 +49,8 @@ def __init__(self,
4849
# Set instance attributes.
4950
self.bv = bv
5051
self.model = self.get_model()
52+
# Used for the callback function.
53+
self.instruction = None
5154

5255
def read_api_key(self, filename: Optional[Path]=None) -> str:
5356
'''Checks for the API key in three locations.
@@ -157,16 +160,46 @@ def generate_query(self, function: Union[Function,
157160
prompt += '\n'.join(self.instruction_list(function))
158161
return prompt
159162

160-
def generate_rename_expression_query(
161-
instruction: HighLevelILInstruction) -> str:
163+
def generate_rename_variable_query(self,
164+
instruction: HighLevelILInstruction) -> Optional[str]:
162165
'''Generates a query string given a BNIL instruction. Returns the query
163166
as a string.
164167
'''
165-
pass
168+
if not isinstance(instruction, HighLevelILVarInit):
169+
raise TypeError(f'Expected a BNIL instruction of type '
170+
f'HighLevelILVarInit got {type(instruction)}.')
171+
# Assign the instruction to the Agent instance. This is used for the
172+
# callback function so we don't need to pass in the instruction to the
173+
# Query instance. This is kind of janky and should be examined in future
174+
# versions.
175+
self.instruction = instruction
176+
177+
prompt: str = self.rename_variable_question
178+
# Get the disassembly lines and add them to the prompt.
179+
for line in instruction.instruction_operands:
180+
prompt += str(line)
166181

167-
def send_query(self, query: str) -> None:
182+
return prompt
183+
184+
def rename_variable(self, response: str) -> None:
185+
'''Renames the variable of the instruction saved in the Agent instance
186+
to the response passed in as an argument.
187+
'''
188+
if self.instruction is None:
189+
raise TypeError('No instruction was saved in the Agent instance.')
190+
if response is None or response == '':
191+
raise TypeError(f'No response was returned from OpenAI; got type {type(response)}.')
192+
# Get just one word from the response.
193+
response = response.split()[0]
194+
# Assign the variable name to the response.
195+
log.log_debug(f'Renaming variable in expression {self.instruction} to {response}.')
196+
self.instruction.dest.name = response
197+
198+
199+
def send_query(self, query: str, callback: Optional[Callable]=None) -> None:
168200
'''Sends a query to the engine and prints the response.'''
169201
query = Query(query_string=query,
170202
model=self.model,
171-
max_token_count=self.get_token_count())
203+
max_token_count=self.get_token_count(),
204+
callback_function=callback)
172205
query.start()

src/entry.py

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from pathlib import Path
22
from binaryninja import BinaryView, Function
3-
from binaryninja.highlevelil import HighLevelILInstruction
3+
from binaryninja.highlevelil import HighLevelILInstruction, HighLevelILVarInit, \
4+
HighLevelILFunction
5+
from binaryninja.log import log_error
46
from . agent import Agent
57

68
API_KEY_PATH = Path.home() / Path('.openai/api_key.txt')
@@ -13,9 +15,31 @@ def check_function(bv: BinaryView, func: Function) -> bool:
1315
query: str = agent.generate_query(func)
1416
agent.send_query(query)
1517

16-
def rename_expression(bv: BinaryView, instruction: HighLevelILInstruction) -> bool:
18+
def rename_variable(bv: BinaryView, instruction: HighLevelILInstruction) -> bool:
19+
20+
if not isinstance(instruction, HighLevelILVarInit):
21+
log_error(f'Instruction must be of type HighLevelILVarInit, got type: ' \
22+
f'{type(instruction)}')
23+
return False
24+
1725
agent: Agent = Agent(
1826
bv=bv,
1927
path_to_api_key=API_KEY_PATH
2028
)
21-
query: str = agent.generate_rename_expression_query(instruction)
29+
query: str = agent.generate_rename_variable_query(instruction)
30+
agent.send_query(query=query, callback=agent.rename_variable)
31+
32+
# Difficult to test without a payment method added, given that the rate limits
33+
# are so low. This should also probably take place in a background task of its
34+
# own.
35+
# def rename_all_variables_in_function(bv: BinaryView, func: HighLevelILFunction) -> None:
36+
# # Get each instruction in the High Level IL Function.
37+
# for instruction in func.instructions:
38+
# match instruction:
39+
# # Rename the variable if it is a HighLevelILVarInit.
40+
# case HighLevelILVarInit():
41+
# rename_variable(bv, instruction)
42+
# # Explicit pass for all other cases.
43+
# case _ :
44+
# pass
45+

src/query.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
import openai
2+
from typing import Callable
23
from binaryninja.plugin import BackgroundTaskThread
3-
4+
from binaryninja.highlevelil import HighLevelILInstruction
5+
from binaryninja.log import log_info
46

57
class Query(BackgroundTaskThread):
68

79
def __init__(self, query_string: str, model: str,
8-
max_token_count: int) -> None:
10+
max_token_count: int, callback_function: Callable=None) -> None:
911
BackgroundTaskThread.__init__(self,
1012
initial_progress_text="",
1113
can_cancel=False)
1214
self.query_string: str = query_string
1315
self.model: str = model
1416
self.max_token_count: int = max_token_count
17+
self.callback = callback_function
1518

1619
def run(self) -> None:
1720
self.progress = "Submitting query to OpenAI."
@@ -21,5 +24,11 @@ def run(self) -> None:
2124
prompt=self.query_string,
2225
max_tokens=self.max_token_count,
2326
)
24-
# Notify the user.
25-
print(response.choices[0].text)
27+
# Get the response text.
28+
response: str = response.choices[0].text
29+
# If there is a callback, do something with it.
30+
if self.callback:
31+
self.callback(response)
32+
# Otherwise, assume we just want to log it.
33+
else:
34+
log_info(response)

0 commit comments

Comments
 (0)