reverse-engineering-assistant 1.0.1__tar.gz → 1.0.3__tar.gz
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.
- {reverse-engineering-assistant-1.0.1 → reverse-engineering-assistant-1.0.3}/PKG-INFO +3 -1
- {reverse-engineering-assistant-1.0.1 → reverse-engineering-assistant-1.0.3}/README.md +2 -0
- {reverse-engineering-assistant-1.0.1 → reverse-engineering-assistant-1.0.3}/pyproject.toml +1 -1
- {reverse-engineering-assistant-1.0.1 → reverse-engineering-assistant-1.0.3}/reverse_engineering_assistant/api_server_tools/llm_tools.py +31 -3
- reverse-engineering-assistant-1.0.1/reverse_engineering_assistant/api_server_tools/function_tools.py → reverse-engineering-assistant-1.0.3/reverse_engineering_assistant/api_server_tools/re_tools.py +39 -5
- {reverse-engineering-assistant-1.0.1 → reverse-engineering-assistant-1.0.3}/reverse_engineering_assistant/assistant_api_server.py +2 -2
- {reverse-engineering-assistant-1.0.1 → reverse-engineering-assistant-1.0.3}/reverse_engineering_assistant/tool_protocol.py +55 -6
- {reverse-engineering-assistant-1.0.1 → reverse-engineering-assistant-1.0.3}/reverse_engineering_assistant.egg-info/PKG-INFO +3 -1
- {reverse-engineering-assistant-1.0.1 → reverse-engineering-assistant-1.0.3}/reverse_engineering_assistant.egg-info/SOURCES.txt +2 -2
- {reverse-engineering-assistant-1.0.1 → reverse-engineering-assistant-1.0.3}/reverse_engineering_assistant/__init__.py +0 -0
- {reverse-engineering-assistant-1.0.1 → reverse-engineering-assistant-1.0.3}/reverse_engineering_assistant/api_server_tools/__init__.py +0 -0
- {reverse-engineering-assistant-1.0.1 → reverse-engineering-assistant-1.0.3}/reverse_engineering_assistant/assistant.py +0 -0
- {reverse-engineering-assistant-1.0.1 → reverse-engineering-assistant-1.0.3}/reverse_engineering_assistant/configuration.py +0 -0
- {reverse-engineering-assistant-1.0.1 → reverse-engineering-assistant-1.0.3}/reverse_engineering_assistant/documents.py +0 -0
- {reverse-engineering-assistant-1.0.1 → reverse-engineering-assistant-1.0.3}/reverse_engineering_assistant/model.py +0 -0
- {reverse-engineering-assistant-1.0.1 → reverse-engineering-assistant-1.0.3}/reverse_engineering_assistant/reva_exceptions.py +0 -0
- {reverse-engineering-assistant-1.0.1 → reverse-engineering-assistant-1.0.3}/reverse_engineering_assistant/tool.py +0 -0
- {reverse-engineering-assistant-1.0.1 → reverse-engineering-assistant-1.0.3}/reverse_engineering_assistant.egg-info/dependency_links.txt +0 -0
- {reverse-engineering-assistant-1.0.1 → reverse-engineering-assistant-1.0.3}/reverse_engineering_assistant.egg-info/entry_points.txt +0 -0
- {reverse-engineering-assistant-1.0.1 → reverse-engineering-assistant-1.0.3}/reverse_engineering_assistant.egg-info/requires.txt +0 -0
- {reverse-engineering-assistant-1.0.1 → reverse-engineering-assistant-1.0.3}/reverse_engineering_assistant.egg-info/top_level.txt +0 -0
- {reverse-engineering-assistant-1.0.1 → reverse-engineering-assistant-1.0.3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: reverse-engineering-assistant
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.3
|
|
4
4
|
Summary: An AI assistant for reverse engineering tasks
|
|
5
5
|
Author: サイバーカイダ (cyberkaida)
|
|
6
6
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
@@ -145,6 +145,8 @@ revassistant --project ${NAME_OF_YOUR_PROJECT}
|
|
|
145
145
|
|
|
146
146
|
## Usage
|
|
147
147
|
|
|
148
|
+
The [ghidra-assistant](ghidra-assistant/README.md) plugin must be installed first.
|
|
149
|
+
|
|
148
150
|
After installation, enable the `ReVaPlugin` extension in the CodeBrowser tool (Open a file and click: File -> Configure -> Miscellaneous).
|
|
149
151
|
|
|
150
152
|
If you want ReVa enabled by default, click File -> Save Tool to save the configuration.
|
|
@@ -124,6 +124,8 @@ revassistant --project ${NAME_OF_YOUR_PROJECT}
|
|
|
124
124
|
|
|
125
125
|
## Usage
|
|
126
126
|
|
|
127
|
+
The [ghidra-assistant](ghidra-assistant/README.md) plugin must be installed first.
|
|
128
|
+
|
|
127
129
|
After installation, enable the `ReVaPlugin` extension in the CodeBrowser tool (Open a file and click: File -> Configure -> Miscellaneous).
|
|
128
130
|
|
|
129
131
|
If you want ReVa enabled by default, click File -> Save Tool to save the configuration.
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
from typing import Dict, List, Optional
|
|
2
2
|
from ..assistant_api_server import register_message_handler, RevaMessageHandler, RevaCallbackHandler
|
|
3
|
-
from ..tool_protocol import
|
|
3
|
+
from ..tool_protocol import (
|
|
4
|
+
RevaExplain,
|
|
5
|
+
RevaGetNewVariableName,
|
|
6
|
+
RevaGetNewVariableNameResponse,
|
|
7
|
+
RevaGetNewSymbolName,
|
|
8
|
+
RevaGetNewSymbolNameResponse,
|
|
9
|
+
RevaExplain,
|
|
10
|
+
RevaExplainResponse,
|
|
11
|
+
RevaLocation,
|
|
12
|
+
)
|
|
4
13
|
|
|
5
14
|
from ..reva_exceptions import RevaToolException
|
|
6
|
-
|
|
15
|
+
import threading
|
|
7
16
|
import logging
|
|
8
17
|
|
|
9
18
|
|
|
@@ -36,4 +45,23 @@ class HandleGetNewSymbolName(RevaMessageHandler):
|
|
|
36
45
|
# Block until ReVa finishes analysis.
|
|
37
46
|
_ = self.assistant.query(question)
|
|
38
47
|
response = RevaGetNewSymbolNameResponse(response_to=message.message_id)
|
|
39
|
-
return response
|
|
48
|
+
return response
|
|
49
|
+
|
|
50
|
+
@register_message_handler
|
|
51
|
+
class HandleExplain(RevaMessageHandler):
|
|
52
|
+
handles_type = RevaExplain
|
|
53
|
+
def run(self, callback_handler: RevaCallbackHandler) -> RevaExplainResponse:
|
|
54
|
+
# Extract the content and ask the LLM what it thinks...
|
|
55
|
+
assert isinstance(callback_handler.message, RevaExplain)
|
|
56
|
+
message: RevaExplain = callback_handler.message
|
|
57
|
+
question = f"""
|
|
58
|
+
Explain the following location in detail, leave comments as needed.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
location: RevaLocation = message.location
|
|
62
|
+
|
|
63
|
+
if message.location is not None:
|
|
64
|
+
question += f"\n{message.location}"
|
|
65
|
+
# Block until ReVa finishes analysis.
|
|
66
|
+
threading.Thread(target=self.assistant.query, args=(question,)).start()
|
|
67
|
+
return RevaExplainResponse(response_to=message.message_id)
|
|
@@ -91,13 +91,14 @@ class RevaDecompilationIndex(RevaRemoteTool):
|
|
|
91
91
|
# Now we can ask the tool
|
|
92
92
|
get_decompilation_message = RevaGetDecompilation(address=address, function=name)
|
|
93
93
|
response = self.submit_to_tool(get_decompilation_message)
|
|
94
|
-
assert isinstance(response, RevaMessageResponse), "Incorrect type returned from callback handler."
|
|
95
|
-
|
|
96
94
|
if response.error_message:
|
|
97
95
|
raise RevaToolException(response.error_message)
|
|
96
|
+
assert isinstance(response, RevaMessageResponse), "Incorrect type returned from callback handler."
|
|
97
|
+
|
|
98
|
+
|
|
98
99
|
|
|
99
100
|
if not isinstance(response, RevaGetDecompilationResponse):
|
|
100
|
-
raise
|
|
101
|
+
raise RevaToolException(f"Expected a RevaGetDecompilationResponse, got {response}")
|
|
101
102
|
|
|
102
103
|
respose: RevaGetDecompilationResponse = response
|
|
103
104
|
|
|
@@ -107,6 +108,7 @@ class RevaDecompilationIndex(RevaRemoteTool):
|
|
|
107
108
|
"function_signature": response.function_signature,
|
|
108
109
|
"address": hex(response.address),
|
|
109
110
|
"decompilation": response.decompilation,
|
|
111
|
+
"listing": response.listing,
|
|
110
112
|
"variables": response.variables, #type: ignore # We can ignore this because it can be serialised to a dict
|
|
111
113
|
}
|
|
112
114
|
|
|
@@ -226,7 +228,10 @@ class RevaCrossReferenceTool(RevaRemoteTool):
|
|
|
226
228
|
These might be calls from/to other functions, or data references from/to this address.
|
|
227
229
|
"""
|
|
228
230
|
from ..tool_protocol import RevaGetReferences, RevaGetReferencesResponse
|
|
229
|
-
|
|
231
|
+
if isinstance(address_or_symbol, int):
|
|
232
|
+
address_or_symbol = hex(address_or_symbol)
|
|
233
|
+
if not isinstance(address_or_symbol, str):
|
|
234
|
+
raise RevaToolException(f"address_or_symbol must be a string. Provided type was {type(address_or_symbol)}")
|
|
230
235
|
get_references_message = RevaGetReferences(address_or_symbol=address_or_symbol)
|
|
231
236
|
response = self.submit_to_tool(get_references_message)
|
|
232
237
|
assert isinstance(response, RevaMessageResponse), "Incorrect type returned from callback handler."
|
|
@@ -274,4 +279,33 @@ class RevaSetSymbolName(RevaRemoteTool):
|
|
|
274
279
|
return {
|
|
275
280
|
"old_name": old_name_or_address,
|
|
276
281
|
"new_name": new_name,
|
|
277
|
-
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
@register_tool
|
|
285
|
+
class RevaSetComment(RevaRemoteTool):
|
|
286
|
+
"""
|
|
287
|
+
A tool for setting comments on addresses, functions and symbols.
|
|
288
|
+
"""
|
|
289
|
+
|
|
290
|
+
def __init__(self, project: AssistantProject, llm: BaseLLM) -> None:
|
|
291
|
+
super().__init__(project, llm)
|
|
292
|
+
self.description = "Used for setting comments on addresses, functions and symbols"
|
|
293
|
+
|
|
294
|
+
self.tool_functions = [
|
|
295
|
+
self.set_comment,
|
|
296
|
+
]
|
|
297
|
+
|
|
298
|
+
def set_comment(self, comment: str, address_or_symbol: str) -> Dict[str, str]:
|
|
299
|
+
"""
|
|
300
|
+
Set the comment at the given address, function or symbol to `comment`.
|
|
301
|
+
Use this when you want to add an explanation or note to a specific part
|
|
302
|
+
of the code.
|
|
303
|
+
"""
|
|
304
|
+
from ..tool_protocol import RevaSetComment, RevaSetCommentResponse
|
|
305
|
+
set_comment_message: RevaMessageToTool = RevaSetComment(comment=comment, address_or_symbol=address_or_symbol)
|
|
306
|
+
|
|
307
|
+
response = self.submit_to_tool(set_comment_message)
|
|
308
|
+
assert isinstance(response, RevaSetCommentResponse), f"Expected a RevaSetCommentResponse, got {response}"
|
|
309
|
+
response: RevaSetCommentResponse = response
|
|
310
|
+
|
|
311
|
+
return response.model_dump()
|
|
@@ -281,7 +281,7 @@ def run_task(project_name: str):
|
|
|
281
281
|
|
|
282
282
|
reva_message = RevaMessage.to_specific_message(message) # type: ignore
|
|
283
283
|
assert isinstance(reva_message, RevaMessageToReva)
|
|
284
|
-
reva_message: RevaMessageToReva = reva_message
|
|
284
|
+
reva_message: RevaMessageToReva = reva_message # type: ignore
|
|
285
285
|
|
|
286
286
|
logger.debug(f"Processing message {reva_message}")
|
|
287
287
|
|
|
@@ -338,7 +338,7 @@ def run_server(port: int = REVA_PORT) -> None:
|
|
|
338
338
|
logging.getLogger("werkzeug").setLevel(logging.WARNING)
|
|
339
339
|
# Import these down here to avoid a circular import
|
|
340
340
|
# We need to have all the handlers registered before we start
|
|
341
|
-
from .api_server_tools import
|
|
341
|
+
from .api_server_tools import re_tools, llm_tools
|
|
342
342
|
app.run(host='localhost', port=port, debug=False)
|
|
343
343
|
|
|
344
344
|
def main():
|
|
@@ -33,8 +33,10 @@ import json
|
|
|
33
33
|
from pathlib import Path
|
|
34
34
|
import logging
|
|
35
35
|
|
|
36
|
+
from click import Option
|
|
36
37
|
from pydantic import BaseModel, Field
|
|
37
38
|
import pydantic
|
|
39
|
+
import pydantic_core
|
|
38
40
|
|
|
39
41
|
try:
|
|
40
42
|
from pydantic import validator
|
|
@@ -99,9 +101,11 @@ class RevaMessage(BaseModel, ABC):
|
|
|
99
101
|
logger.debug(f"Message class is {message_class}")
|
|
100
102
|
try:
|
|
101
103
|
return message_class.model_validate(thing)
|
|
102
|
-
except
|
|
104
|
+
except pydantic_core.ValidationError:
|
|
105
|
+
# If we fail to turn it into a specific type, make it a generic type
|
|
106
|
+
# This allows us to extract things like error messages.
|
|
103
107
|
if issubclass(message_class, RevaMessageResponse):
|
|
104
|
-
return RevaMessageResponse.
|
|
108
|
+
return RevaMessageResponse.model_validate(thing)
|
|
105
109
|
else:
|
|
106
110
|
logger.exception(f"Failed to parse {thing} as {message_class}")
|
|
107
111
|
return RevaMessage.model_validate(thing)
|
|
@@ -246,9 +250,9 @@ class RevaGetDecompilation(RevaMessageToTool):
|
|
|
246
250
|
|
|
247
251
|
class RevaVariable(BaseModel):
|
|
248
252
|
name: str = Field()
|
|
249
|
-
storage: Optional[str] = Field()
|
|
250
|
-
data_type: Optional[str] = Field()
|
|
251
|
-
size: Optional[int] = Field()
|
|
253
|
+
storage: Optional[str] = Field(default=None)
|
|
254
|
+
data_type: Optional[str] = Field(default=None)
|
|
255
|
+
size: Optional[int] = Field(default=None)
|
|
252
256
|
|
|
253
257
|
@register_message
|
|
254
258
|
class RevaGetDecompilationResponse(RevaMessageToReva, RevaMessageResponse):
|
|
@@ -264,6 +268,10 @@ class RevaGetDecompilationResponse(RevaMessageToReva, RevaMessageResponse):
|
|
|
264
268
|
"""
|
|
265
269
|
The decompilation of the given address
|
|
266
270
|
"""
|
|
271
|
+
listing: Optional[str] = Field(default=None)
|
|
272
|
+
"""
|
|
273
|
+
The disassebly listing of the function
|
|
274
|
+
"""
|
|
267
275
|
function: str = Field()
|
|
268
276
|
"""
|
|
269
277
|
The function this decompilation is for
|
|
@@ -457,4 +465,45 @@ class RevaRenameVariableResponse(RevaMessageToReva, RevaMessageResponse):
|
|
|
457
465
|
|
|
458
466
|
A simple yes/no, not much to respond with.
|
|
459
467
|
"""
|
|
460
|
-
message_type: str = "RevaRenameVariableResponse"
|
|
468
|
+
message_type: str = "RevaRenameVariableResponse"
|
|
469
|
+
|
|
470
|
+
class RevaLocation(BaseModel):
|
|
471
|
+
cursor_address: Optional[str] = Field(default=None)
|
|
472
|
+
function_name: Optional[str] = Field(default=None)
|
|
473
|
+
start_address: Optional[str] = Field(default=None)
|
|
474
|
+
end_address: Optional[str] = Field(default=None)
|
|
475
|
+
content: Optional[str] = Field(default=None)
|
|
476
|
+
|
|
477
|
+
@register_message
|
|
478
|
+
class RevaExplain(RevaMessageToReva):
|
|
479
|
+
"""
|
|
480
|
+
Ask the model to explain something
|
|
481
|
+
"""
|
|
482
|
+
message_type: str = "RevaExplain"
|
|
483
|
+
location: RevaLocation = Field()
|
|
484
|
+
"""
|
|
485
|
+
The location to explain
|
|
486
|
+
"""
|
|
487
|
+
|
|
488
|
+
@register_message
|
|
489
|
+
class RevaExplainResponse(RevaMessageToTool, RevaMessageResponse):
|
|
490
|
+
"""
|
|
491
|
+
Response to a RevaExplain message
|
|
492
|
+
"""
|
|
493
|
+
message_type: str = "RevaExplainResponse"
|
|
494
|
+
|
|
495
|
+
@register_message
|
|
496
|
+
class RevaSetComment(RevaMessageToTool):
|
|
497
|
+
"""
|
|
498
|
+
Set a comment at a given address
|
|
499
|
+
"""
|
|
500
|
+
message_type: str = "RevaSetComment"
|
|
501
|
+
address_or_symbol: str = Field()
|
|
502
|
+
comment: str = Field()
|
|
503
|
+
|
|
504
|
+
@register_message
|
|
505
|
+
class RevaSetCommentResponse(RevaMessageToReva, RevaMessageResponse):
|
|
506
|
+
"""
|
|
507
|
+
Response to a RevaSetComment message
|
|
508
|
+
"""
|
|
509
|
+
message_type: str = "RevaSetCommentResponse"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: reverse-engineering-assistant
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.3
|
|
4
4
|
Summary: An AI assistant for reverse engineering tasks
|
|
5
5
|
Author: サイバーカイダ (cyberkaida)
|
|
6
6
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
@@ -145,6 +145,8 @@ revassistant --project ${NAME_OF_YOUR_PROJECT}
|
|
|
145
145
|
|
|
146
146
|
## Usage
|
|
147
147
|
|
|
148
|
+
The [ghidra-assistant](ghidra-assistant/README.md) plugin must be installed first.
|
|
149
|
+
|
|
148
150
|
After installation, enable the `ReVaPlugin` extension in the CodeBrowser tool (Open a file and click: File -> Configure -> Miscellaneous).
|
|
149
151
|
|
|
150
152
|
If you want ReVa enabled by default, click File -> Save Tool to save the configuration.
|
|
@@ -16,5 +16,5 @@ reverse_engineering_assistant.egg-info/entry_points.txt
|
|
|
16
16
|
reverse_engineering_assistant.egg-info/requires.txt
|
|
17
17
|
reverse_engineering_assistant.egg-info/top_level.txt
|
|
18
18
|
reverse_engineering_assistant/api_server_tools/__init__.py
|
|
19
|
-
reverse_engineering_assistant/api_server_tools/
|
|
20
|
-
reverse_engineering_assistant/api_server_tools/
|
|
19
|
+
reverse_engineering_assistant/api_server_tools/llm_tools.py
|
|
20
|
+
reverse_engineering_assistant/api_server_tools/re_tools.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|