reverse-engineering-assistant 2.9.1__tar.gz → 2.9.4__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-2.9.1 → reverse_engineering_assistant-2.9.4}/PKG-INFO +76 -35
- {reverse_engineering_assistant-2.9.1 → reverse_engineering_assistant-2.9.4}/README.md +71 -34
- {reverse_engineering_assistant-2.9.1 → reverse_engineering_assistant-2.9.4}/pyproject.toml +6 -2
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/api_server_tools/__init__.py +91 -0
- {reverse_engineering_assistant-2.9.1 → reverse_engineering_assistant-2.9.4}/reverse_engineering_assistant/api_server_tools/connection.py +3 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/api_server_tools/llm_tools.py +134 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/api_server_tools/re_tool_box/__init__.py +6 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/api_server_tools/re_tool_box/bookmarks.py +65 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/api_server_tools/re_tool_box/comment.py +65 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/api_server_tools/re_tool_box/cross_reference.py +69 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/api_server_tools/re_tool_box/cursor.py +45 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/api_server_tools/re_tool_box/data.py +154 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/api_server_tools/re_tool_box/decompilation.py +204 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/api_server_tools/re_tool_box/symbols.py +209 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/api_server_tools/re_tools.py +17 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/assistant.py +503 -0
- {reverse_engineering_assistant-2.9.1 → reverse_engineering_assistant-2.9.4}/reverse_engineering_assistant/assistant_api_server.py +66 -15
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/chat_client.py +292 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/crash_dump.py +112 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/model.py +72 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaBookmark_pb2.py +35 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaBookmark_pb2.pyi +33 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaBookmark_pb2_grpc.py +145 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaChat_pb2.py +39 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaChat_pb2.pyi +55 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaChat_pb2_grpc.py +235 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaComment_pb2.py +31 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaComment_pb2.pyi +19 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaComment_pb2_grpc.py +102 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaData_pb2.py +47 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaData_pb2.pyi +94 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaData_pb2_grpc.py +231 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaGetCursor_pb2.py +31 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaGetCursor_pb2.pyi +21 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaGetCursor_pb2_grpc.py +102 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaGetDecompilation_pb2.py +44 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaGetDecompilation_pb2.pyi +83 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaGetDecompilation_pb2_grpc.py +231 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaGetSymbols_pb2.py +47 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaGetSymbols_pb2.pyi +70 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaGetSymbols_pb2_grpc.py +260 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaHandshake_pb2.py +31 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaHandshake_pb2.pyi +21 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaHandshake_pb2_grpc.py +102 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaHeartbeat_pb2.py +31 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaHeartbeat_pb2.pyi +23 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaHeartbeat_pb2_grpc.py +102 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaReferences_pb2.py +31 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaReferences_pb2.pyi +20 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaReferences_pb2_grpc.py +102 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaVariable_pb2.py +27 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaVariable_pb2.pyi +17 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/RevaVariable_pb2_grpc.py +29 -0
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/protocol/__init__.py +0 -0
- {reverse_engineering_assistant-2.9.1 → reverse_engineering_assistant-2.9.4}/reverse_engineering_assistant.egg-info/PKG-INFO +76 -35
- reverse_engineering_assistant-2.9.4/reverse_engineering_assistant.egg-info/SOURCES.txt +64 -0
- {reverse_engineering_assistant-2.9.1 → reverse_engineering_assistant-2.9.4}/reverse_engineering_assistant.egg-info/requires.txt +4 -0
- reverse_engineering_assistant-2.9.1/reverse_engineering_assistant/api_server_tools/__init__.py +0 -16
- reverse_engineering_assistant-2.9.1/reverse_engineering_assistant/api_server_tools/llm_tools.py +0 -111
- reverse_engineering_assistant-2.9.1/reverse_engineering_assistant/api_server_tools/re_tools.py +0 -354
- reverse_engineering_assistant-2.9.1/reverse_engineering_assistant/assistant.py +0 -453
- reverse_engineering_assistant-2.9.1/reverse_engineering_assistant/chat_client.py +0 -179
- reverse_engineering_assistant-2.9.1/reverse_engineering_assistant/configuration.py +0 -151
- reverse_engineering_assistant-2.9.1/reverse_engineering_assistant/model.py +0 -109
- reverse_engineering_assistant-2.9.1/reverse_engineering_assistant.egg-info/SOURCES.txt +0 -22
- {reverse_engineering_assistant-2.9.1 → reverse_engineering_assistant-2.9.4}/reverse_engineering_assistant/.gitignore +0 -0
- {reverse_engineering_assistant-2.9.1 → reverse_engineering_assistant-2.9.4}/reverse_engineering_assistant/__init__.py +0 -0
- {reverse_engineering_assistant-2.9.1 → reverse_engineering_assistant-2.9.4}/reverse_engineering_assistant/documents.py +0 -0
- {reverse_engineering_assistant-2.9.1 → reverse_engineering_assistant-2.9.4}/reverse_engineering_assistant/reva_exceptions.py +0 -0
- {reverse_engineering_assistant-2.9.1 → reverse_engineering_assistant-2.9.4}/reverse_engineering_assistant/tool.py +0 -0
- {reverse_engineering_assistant-2.9.1 → reverse_engineering_assistant-2.9.4}/reverse_engineering_assistant.egg-info/dependency_links.txt +0 -0
- {reverse_engineering_assistant-2.9.1 → reverse_engineering_assistant-2.9.4}/reverse_engineering_assistant.egg-info/entry_points.txt +0 -0
- {reverse_engineering_assistant-2.9.1 → reverse_engineering_assistant-2.9.4}/reverse_engineering_assistant.egg-info/top_level.txt +0 -0
- {reverse_engineering_assistant-2.9.1 → reverse_engineering_assistant-2.9.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: reverse-engineering-assistant
|
|
3
|
-
Version: 2.9.
|
|
3
|
+
Version: 2.9.4
|
|
4
4
|
Summary: An AI assistant for reverse engineering tasks
|
|
5
5
|
Author: サイバーカイダ (cyberkaida)
|
|
6
6
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
@@ -11,6 +11,9 @@ Description-Content-Type: text/markdown
|
|
|
11
11
|
Requires-Dist: langchain
|
|
12
12
|
Requires-Dist: langchain-core
|
|
13
13
|
Requires-Dist: langchain-openai
|
|
14
|
+
Requires-Dist: langchain-community
|
|
15
|
+
Requires-Dist: langchain-experimental
|
|
16
|
+
Requires-Dist: langgraph
|
|
14
17
|
Requires-Dist: prompt_toolkit
|
|
15
18
|
Requires-Dist: sentence_transformers
|
|
16
19
|
Requires-Dist: PyYAML
|
|
@@ -18,6 +21,7 @@ Requires-Dist: pydantic
|
|
|
18
21
|
Requires-Dist: rich
|
|
19
22
|
Requires-Dist: grpcio
|
|
20
23
|
Requires-Dist: protobuf
|
|
24
|
+
Requires-Dist: flask
|
|
21
25
|
|
|
22
26
|
# ReVA - Reverse Engineering Assistant
|
|
23
27
|
|
|
@@ -46,13 +50,14 @@ information from the tools, but when there is no information it can still respon
|
|
|
46
50
|
questions from its training.
|
|
47
51
|
|
|
48
52
|
You can ask questions like:
|
|
53
|
+
- What are the interesting strings in this program?
|
|
49
54
|
- Does this program use encryption? Write a markdown report on the encryption and where it is used.
|
|
50
55
|
- Draw a class diagram using plantuml syntax.
|
|
51
56
|
- Start from main, examine the program in detail. Rename variables as you go and provide a summary of the program.
|
|
52
57
|
- Explain the purpose of the `__mod_init` segment.
|
|
53
58
|
- What does `mmap` return?
|
|
54
59
|
- What does the function at address 0x80000 do?
|
|
55
|
-
- This is a CTF problem.
|
|
60
|
+
- This is a CTF problem. Write a pwntools script to get the flag.
|
|
56
61
|
|
|
57
62
|
An important part of reverse engineering is the process. Many other tools simply ask a single question of the LLM,
|
|
58
63
|
this means it is difficult to determine _why_ a thing happened. In ReVa we break all actions down into small parts
|
|
@@ -68,33 +73,40 @@ Built in support is provided for:
|
|
|
68
73
|
- [OpenAI](https://platform.openai.com/overview) for online inference and easy setup (Needs an OpenAI API key)
|
|
69
74
|
- [Ollama](https://ollama.ai) and any model it supports for local on-device inference or connecting to a self hosted remote inference server.
|
|
70
75
|
|
|
76
|
+
See [Configuration](#configuration) for more information about settings for the providers.
|
|
77
|
+
|
|
71
78
|
Adding additional inference servers is easy if it is supported by langchain.
|
|
72
79
|
|
|
73
80
|
## Configuration
|
|
74
81
|
|
|
75
|
-
|
|
76
|
-
|
|
82
|
+
Configuration for ReVa is in the CodeBrowser Tool options.
|
|
83
|
+
Open a program and go to Edit -> Tool Options -> ReVa.
|
|
84
|
+
|
|
85
|
+
There are options for:
|
|
86
|
+
- Selecting a provider (OpenAI or Ollama, others coming soon!)
|
|
87
|
+
- Enabling "Follow", this will move the Ghidra view to the location of
|
|
88
|
+
things ReVa is examining or changing.
|
|
89
|
+
- Enabling "Auto-allow", ReVa will log her actions for the user to accept
|
|
90
|
+
in the "ReVa Actions Log" window.
|
|
91
|
+
|
|
92
|
+
There are sections for the providers.
|
|
93
|
+
|
|
94
|
+
### OpenAI
|
|
77
95
|
|
|
78
|
-
|
|
79
|
-
`~/.config/reverse-engineering-assistant/config.yaml`. If this
|
|
80
|
-
is not present on first start, a default configuration using
|
|
81
|
-
OpenAI for inference and the `OPENAI_API_TOKEN` environment
|
|
82
|
-
variable will be used.
|
|
96
|
+
By default, the OpenAI key is loaded from the environment variable `OPENAI_API_KEY`. You can also set your key inside Ghidra. Setting the key back to the `OPENAI_API_KEY` value will clear the key from the Ghidra configuration and load it from the environment.
|
|
83
97
|
|
|
84
|
-
|
|
85
|
-
This controls what inference service you use. These are the
|
|
86
|
-
same as the configuration keys, for example to use Ollama,
|
|
87
|
-
set type to `ollama` and configure the settings in the `ollama:`
|
|
88
|
-
section.
|
|
98
|
+
You can also select the model. By default `gpt-4o` is selected. This model works best with the tools and the prompt provided by ReVa.
|
|
89
99
|
|
|
90
|
-
|
|
91
|
-
If you use Ollama or OpenAI these will be processed to fit the
|
|
92
|
-
model specific prompt pattern (placing the system prompt in the
|
|
93
|
-
correct tags, etc).
|
|
100
|
+
`gpt-4` also works well, but is slow and needs more prompting by the user to explore a binary.
|
|
94
101
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
102
|
+
### Ollama
|
|
103
|
+
|
|
104
|
+
Ollama is a local inference server. The default server is set to localhost, with the default Ollama port. You can change this to a remote server if you want to perform inference on a remote machine. This is useful for organisations that self host.
|
|
105
|
+
|
|
106
|
+
You can also select a model. The model must alread be loaded on the server. Good performance has been seen with:
|
|
107
|
+
- `mixtral`
|
|
108
|
+
- `llama3`
|
|
109
|
+
- `phi`
|
|
98
110
|
|
|
99
111
|
## Workflow
|
|
100
112
|
|
|
@@ -112,24 +124,41 @@ If you have more than one Ghidra open, you can select the right one with
|
|
|
112
124
|
`reva-chat --project ${project-name}`, if it is not set, `reva-chat` will
|
|
113
125
|
ask you which project you want to connect to.
|
|
114
126
|
|
|
115
|
-
##
|
|
127
|
+
## Protocol Build
|
|
116
128
|
|
|
117
|
-
|
|
118
|
-
sure that `reva-server` and `reva-chat` are on your path.
|
|
119
|
-
The Ghidra extension will need to start `reva-server`, and you will need to
|
|
120
|
-
run `reva-chat`.
|
|
129
|
+
To communicate between `reva-server` and the extension, [gRPC](https://grpc.io) is used. You can read more about that (here)[./DEVELOPER.md]. Building the source files from those protocol definitions is driven from the [Makefile](/Makefile). To build the protocol source code files, run this command in the project's root:
|
|
121
130
|
|
|
122
|
-
|
|
123
|
-
|
|
131
|
+
```sh
|
|
132
|
+
make protocol
|
|
133
|
+
```
|
|
124
134
|
|
|
125
|
-
|
|
135
|
+
## Python Project (reva-server and reva-chat) Installation
|
|
136
|
+
|
|
137
|
+
First install the python component, I like to use `pipx`. Install it with something like:
|
|
126
138
|
|
|
127
139
|
```sh
|
|
128
|
-
|
|
140
|
+
pip install pipx
|
|
129
141
|
```
|
|
130
142
|
|
|
131
|
-
|
|
132
|
-
|
|
143
|
+
In the `reverse-engineering-assistant` folder, run:
|
|
144
|
+
|
|
145
|
+
```sh
|
|
146
|
+
pipx install .
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
After installing the python project, pipx may warn you that you need to add a folder to your PATH environment variable. Make sure that the folder (now containing `reva-server` and `reva-chat`) are in your PATH variable. pipx can do it for you with this command:
|
|
150
|
+
|
|
151
|
+
```sh
|
|
152
|
+
pipx ensurepath
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
The extension will need to start `reva-server`, and you will need to run `reva-chat`. In case you do not want to add them to your PATH, see the [Configuration](#configuration) section for how to set the path to the executables.
|
|
156
|
+
|
|
157
|
+
Once the `reva-server` has been started by the extension the chat can be started with:
|
|
158
|
+
|
|
159
|
+
```sh
|
|
160
|
+
reva-chat
|
|
161
|
+
```
|
|
133
162
|
|
|
134
163
|
# Ghidra Support
|
|
135
164
|
|
|
@@ -139,12 +168,16 @@ reva-chat
|
|
|
139
168
|
|
|
140
169
|
Follow the instructions in the [ghidra-assistant](ghidra-assistant/README.md) plugin.
|
|
141
170
|
|
|
142
|
-
After installation, enable the `
|
|
171
|
+
After installation, enable the `ReVa Plugin` extension in the CodeBrowser tool (Open a file and click: File -> Configure -> Miscellaneous).
|
|
143
172
|
|
|
144
173
|
If you want ReVa enabled by default, click File -> Save Tool to save the configuration.
|
|
145
174
|
|
|
146
175
|
If everything is working correctly you will see a ReVa menu on your menu bar.
|
|
147
176
|
|
|
177
|
+
## Configuration
|
|
178
|
+
|
|
179
|
+
You can modify the plugin configuration in `Edit -> Tool Options -> ReVa`.
|
|
180
|
+
|
|
148
181
|
## Undo
|
|
149
182
|
|
|
150
183
|
Whenever ReVa performs an action it will create an undo point for each action. If ReVa renames 5 variables, this will be
|
|
@@ -152,9 +185,17 @@ one undo.
|
|
|
152
185
|
|
|
153
186
|
## Menus
|
|
154
187
|
|
|
155
|
-
|
|
188
|
+
ReVa adds an option to the CodeBrowser Tool's Window menu.
|
|
189
|
+
Select Window -> ReVa Action Log to open the ReVa Action Log window.
|
|
190
|
+
|
|
191
|
+
This window shows actions ReVa has performed and would like to perform.
|
|
192
|
+
You can accept or reject a change by double clicking the ✅ or ❌ icon. You can also go to the location the action will be performed by double clicking the address.
|
|
193
|
+
|
|
194
|
+
If you reject an action, ReVa will be told and she will move on.
|
|
195
|
+
|
|
196
|
+
You can also enable "Auto-allow" in the ReVa options. This will automatically accept all actions ReVa wants to perform.
|
|
156
197
|
|
|
157
|
-
ReVa adds some elements to the Ghidra UI. You can either ask ReVa to do something in the chat window,
|
|
198
|
+
ReVa also adds some elements to the Ghidra UI. You can either ask ReVa to do something in the chat window,
|
|
158
199
|
"Examine the variable usage in `main` in detail, rename the variables with more descriptive names.",
|
|
159
200
|
or use the menu system.
|
|
160
201
|
|
|
@@ -25,13 +25,14 @@ information from the tools, but when there is no information it can still respon
|
|
|
25
25
|
questions from its training.
|
|
26
26
|
|
|
27
27
|
You can ask questions like:
|
|
28
|
+
- What are the interesting strings in this program?
|
|
28
29
|
- Does this program use encryption? Write a markdown report on the encryption and where it is used.
|
|
29
30
|
- Draw a class diagram using plantuml syntax.
|
|
30
31
|
- Start from main, examine the program in detail. Rename variables as you go and provide a summary of the program.
|
|
31
32
|
- Explain the purpose of the `__mod_init` segment.
|
|
32
33
|
- What does `mmap` return?
|
|
33
34
|
- What does the function at address 0x80000 do?
|
|
34
|
-
- This is a CTF problem.
|
|
35
|
+
- This is a CTF problem. Write a pwntools script to get the flag.
|
|
35
36
|
|
|
36
37
|
An important part of reverse engineering is the process. Many other tools simply ask a single question of the LLM,
|
|
37
38
|
this means it is difficult to determine _why_ a thing happened. In ReVa we break all actions down into small parts
|
|
@@ -47,33 +48,40 @@ Built in support is provided for:
|
|
|
47
48
|
- [OpenAI](https://platform.openai.com/overview) for online inference and easy setup (Needs an OpenAI API key)
|
|
48
49
|
- [Ollama](https://ollama.ai) and any model it supports for local on-device inference or connecting to a self hosted remote inference server.
|
|
49
50
|
|
|
51
|
+
See [Configuration](#configuration) for more information about settings for the providers.
|
|
52
|
+
|
|
50
53
|
Adding additional inference servers is easy if it is supported by langchain.
|
|
51
54
|
|
|
52
55
|
## Configuration
|
|
53
56
|
|
|
54
|
-
|
|
55
|
-
|
|
57
|
+
Configuration for ReVa is in the CodeBrowser Tool options.
|
|
58
|
+
Open a program and go to Edit -> Tool Options -> ReVa.
|
|
59
|
+
|
|
60
|
+
There are options for:
|
|
61
|
+
- Selecting a provider (OpenAI or Ollama, others coming soon!)
|
|
62
|
+
- Enabling "Follow", this will move the Ghidra view to the location of
|
|
63
|
+
things ReVa is examining or changing.
|
|
64
|
+
- Enabling "Auto-allow", ReVa will log her actions for the user to accept
|
|
65
|
+
in the "ReVa Actions Log" window.
|
|
66
|
+
|
|
67
|
+
There are sections for the providers.
|
|
68
|
+
|
|
69
|
+
### OpenAI
|
|
56
70
|
|
|
57
|
-
|
|
58
|
-
`~/.config/reverse-engineering-assistant/config.yaml`. If this
|
|
59
|
-
is not present on first start, a default configuration using
|
|
60
|
-
OpenAI for inference and the `OPENAI_API_TOKEN` environment
|
|
61
|
-
variable will be used.
|
|
71
|
+
By default, the OpenAI key is loaded from the environment variable `OPENAI_API_KEY`. You can also set your key inside Ghidra. Setting the key back to the `OPENAI_API_KEY` value will clear the key from the Ghidra configuration and load it from the environment.
|
|
62
72
|
|
|
63
|
-
|
|
64
|
-
This controls what inference service you use. These are the
|
|
65
|
-
same as the configuration keys, for example to use Ollama,
|
|
66
|
-
set type to `ollama` and configure the settings in the `ollama:`
|
|
67
|
-
section.
|
|
73
|
+
You can also select the model. By default `gpt-4o` is selected. This model works best with the tools and the prompt provided by ReVa.
|
|
68
74
|
|
|
69
|
-
|
|
70
|
-
If you use Ollama or OpenAI these will be processed to fit the
|
|
71
|
-
model specific prompt pattern (placing the system prompt in the
|
|
72
|
-
correct tags, etc).
|
|
75
|
+
`gpt-4` also works well, but is slow and needs more prompting by the user to explore a binary.
|
|
73
76
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
+
### Ollama
|
|
78
|
+
|
|
79
|
+
Ollama is a local inference server. The default server is set to localhost, with the default Ollama port. You can change this to a remote server if you want to perform inference on a remote machine. This is useful for organisations that self host.
|
|
80
|
+
|
|
81
|
+
You can also select a model. The model must alread be loaded on the server. Good performance has been seen with:
|
|
82
|
+
- `mixtral`
|
|
83
|
+
- `llama3`
|
|
84
|
+
- `phi`
|
|
77
85
|
|
|
78
86
|
## Workflow
|
|
79
87
|
|
|
@@ -91,24 +99,41 @@ If you have more than one Ghidra open, you can select the right one with
|
|
|
91
99
|
`reva-chat --project ${project-name}`, if it is not set, `reva-chat` will
|
|
92
100
|
ask you which project you want to connect to.
|
|
93
101
|
|
|
94
|
-
##
|
|
102
|
+
## Protocol Build
|
|
95
103
|
|
|
96
|
-
|
|
97
|
-
sure that `reva-server` and `reva-chat` are on your path.
|
|
98
|
-
The Ghidra extension will need to start `reva-server`, and you will need to
|
|
99
|
-
run `reva-chat`.
|
|
104
|
+
To communicate between `reva-server` and the extension, [gRPC](https://grpc.io) is used. You can read more about that (here)[./DEVELOPER.md]. Building the source files from those protocol definitions is driven from the [Makefile](/Makefile). To build the protocol source code files, run this command in the project's root:
|
|
100
105
|
|
|
101
|
-
|
|
102
|
-
|
|
106
|
+
```sh
|
|
107
|
+
make protocol
|
|
108
|
+
```
|
|
103
109
|
|
|
104
|
-
|
|
110
|
+
## Python Project (reva-server and reva-chat) Installation
|
|
111
|
+
|
|
112
|
+
First install the python component, I like to use `pipx`. Install it with something like:
|
|
105
113
|
|
|
106
114
|
```sh
|
|
107
|
-
|
|
115
|
+
pip install pipx
|
|
108
116
|
```
|
|
109
117
|
|
|
110
|
-
|
|
111
|
-
|
|
118
|
+
In the `reverse-engineering-assistant` folder, run:
|
|
119
|
+
|
|
120
|
+
```sh
|
|
121
|
+
pipx install .
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
After installing the python project, pipx may warn you that you need to add a folder to your PATH environment variable. Make sure that the folder (now containing `reva-server` and `reva-chat`) are in your PATH variable. pipx can do it for you with this command:
|
|
125
|
+
|
|
126
|
+
```sh
|
|
127
|
+
pipx ensurepath
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
The extension will need to start `reva-server`, and you will need to run `reva-chat`. In case you do not want to add them to your PATH, see the [Configuration](#configuration) section for how to set the path to the executables.
|
|
131
|
+
|
|
132
|
+
Once the `reva-server` has been started by the extension the chat can be started with:
|
|
133
|
+
|
|
134
|
+
```sh
|
|
135
|
+
reva-chat
|
|
136
|
+
```
|
|
112
137
|
|
|
113
138
|
# Ghidra Support
|
|
114
139
|
|
|
@@ -118,12 +143,16 @@ reva-chat
|
|
|
118
143
|
|
|
119
144
|
Follow the instructions in the [ghidra-assistant](ghidra-assistant/README.md) plugin.
|
|
120
145
|
|
|
121
|
-
After installation, enable the `
|
|
146
|
+
After installation, enable the `ReVa Plugin` extension in the CodeBrowser tool (Open a file and click: File -> Configure -> Miscellaneous).
|
|
122
147
|
|
|
123
148
|
If you want ReVa enabled by default, click File -> Save Tool to save the configuration.
|
|
124
149
|
|
|
125
150
|
If everything is working correctly you will see a ReVa menu on your menu bar.
|
|
126
151
|
|
|
152
|
+
## Configuration
|
|
153
|
+
|
|
154
|
+
You can modify the plugin configuration in `Edit -> Tool Options -> ReVa`.
|
|
155
|
+
|
|
127
156
|
## Undo
|
|
128
157
|
|
|
129
158
|
Whenever ReVa performs an action it will create an undo point for each action. If ReVa renames 5 variables, this will be
|
|
@@ -131,9 +160,17 @@ one undo.
|
|
|
131
160
|
|
|
132
161
|
## Menus
|
|
133
162
|
|
|
134
|
-
|
|
163
|
+
ReVa adds an option to the CodeBrowser Tool's Window menu.
|
|
164
|
+
Select Window -> ReVa Action Log to open the ReVa Action Log window.
|
|
165
|
+
|
|
166
|
+
This window shows actions ReVa has performed and would like to perform.
|
|
167
|
+
You can accept or reject a change by double clicking the ✅ or ❌ icon. You can also go to the location the action will be performed by double clicking the address.
|
|
168
|
+
|
|
169
|
+
If you reject an action, ReVa will be told and she will move on.
|
|
170
|
+
|
|
171
|
+
You can also enable "Auto-allow" in the ReVa options. This will automatically accept all actions ReVa wants to perform.
|
|
135
172
|
|
|
136
|
-
ReVa adds some elements to the Ghidra UI. You can either ask ReVa to do something in the chat window,
|
|
173
|
+
ReVa also adds some elements to the Ghidra UI. You can either ask ReVa to do something in the chat window,
|
|
137
174
|
"Examine the variable usage in `main` in detail, rename the variables with more descriptive names.",
|
|
138
175
|
or use the menu system.
|
|
139
176
|
|
|
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "reverse-engineering-assistant"
|
|
7
7
|
readme = "README.md"
|
|
8
|
-
version = "2.9.
|
|
8
|
+
version = "2.9.4"
|
|
9
9
|
authors = [
|
|
10
10
|
{name="サイバーカイダ (cyberkaida)"},
|
|
11
11
|
]
|
|
@@ -20,6 +20,9 @@ dependencies = [
|
|
|
20
20
|
"langchain",
|
|
21
21
|
"langchain-core",
|
|
22
22
|
"langchain-openai",
|
|
23
|
+
"langchain-community",
|
|
24
|
+
"langchain-experimental",
|
|
25
|
+
"langgraph",
|
|
23
26
|
"prompt_toolkit",
|
|
24
27
|
"sentence_transformers",
|
|
25
28
|
"PyYAML",
|
|
@@ -27,8 +30,9 @@ dependencies = [
|
|
|
27
30
|
"rich",
|
|
28
31
|
"grpcio",
|
|
29
32
|
"protobuf",
|
|
33
|
+
"flask",
|
|
30
34
|
]
|
|
31
35
|
|
|
32
36
|
[project.scripts]
|
|
33
37
|
reva-server = "reverse_engineering_assistant.assistant_api_server:main"
|
|
34
|
-
reva-chat = "reverse_engineering_assistant.chat_client:main"
|
|
38
|
+
reva-chat = "reverse_engineering_assistant.chat_client:main"
|
reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/api_server_tools/__init__.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
from typing import List
|
|
4
|
+
from reverse_engineering_assistant.reva_exceptions import RevaToolException
|
|
5
|
+
from reverse_engineering_assistant.tool import AssistantProject
|
|
6
|
+
from reverse_engineering_assistant.assistant import RevaTool
|
|
7
|
+
from reverse_engineering_assistant.model import RevaModel
|
|
8
|
+
from reverse_engineering_assistant.api_server_tools.connection import get_channel
|
|
9
|
+
from typing import Optional, Tuple
|
|
10
|
+
import logging
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class RevaMessageHandler(object):
|
|
14
|
+
handles_type = None
|
|
15
|
+
|
|
16
|
+
_global_message_handlers: List[RevaMessageHandler] = []
|
|
17
|
+
|
|
18
|
+
__all__ = ['register_message_handler']
|
|
19
|
+
|
|
20
|
+
def register_message_handler(cls: RevaMessageHandler):
|
|
21
|
+
_global_message_handlers.append(cls)
|
|
22
|
+
return cls
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class RevaRemoteTool(RevaTool):
|
|
26
|
+
logger: logging.Logger
|
|
27
|
+
|
|
28
|
+
def __init__(self, project: AssistantProject, llm: RevaModel) -> None:
|
|
29
|
+
self.logger = logging.getLogger(f"reverse_engineering_assistant.RevaRemoteTool.{self.__class__.__name__}")
|
|
30
|
+
self.logger.addHandler(logging.FileHandler(project.project_path / "reva.log"))
|
|
31
|
+
super().__init__(project, llm)
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def channel(self):
|
|
35
|
+
return get_channel()
|
|
36
|
+
|
|
37
|
+
def resolve_to_address_and_symbol(self, thing: str) -> Tuple[str, Optional[str]]:
|
|
38
|
+
"""
|
|
39
|
+
Resolve a string to an address and symbol.
|
|
40
|
+
If it is an address it can be a namespaced address or a plain hex address.
|
|
41
|
+
|
|
42
|
+
This helps reduce hallucinations and catch issues with symbols and namespaces early.
|
|
43
|
+
|
|
44
|
+
Returns a tuple of (address, symbol).
|
|
45
|
+
"""
|
|
46
|
+
self.logger.debug(f"Resolving {thing} to address and symbol")
|
|
47
|
+
assert thing is not None
|
|
48
|
+
address: Optional[str] = None
|
|
49
|
+
symbol: Optional[str] = None
|
|
50
|
+
try:
|
|
51
|
+
# This is a plain address in the main namespace
|
|
52
|
+
address = hex(int(thing, 16))
|
|
53
|
+
self.logger.debug(f"Resolved {thing} to address: {address}")
|
|
54
|
+
except ValueError:
|
|
55
|
+
# This could also be a Ghidra namespaced address, so let's check that too!
|
|
56
|
+
if "::" in thing:
|
|
57
|
+
last_part = thing.split("::")[-1]
|
|
58
|
+
try:
|
|
59
|
+
hex(int(last_part, 16))
|
|
60
|
+
# If the last part of the address is a hex number, then we can assume it is an address
|
|
61
|
+
address = thing
|
|
62
|
+
except ValueError:
|
|
63
|
+
# Otherwise, it is a symbol
|
|
64
|
+
symbol = thing
|
|
65
|
+
else:
|
|
66
|
+
# This is a symbol
|
|
67
|
+
symbol = thing
|
|
68
|
+
|
|
69
|
+
# We can check if it is a symbol
|
|
70
|
+
from ..protocol import RevaGetSymbols_pb2_grpc, RevaGetSymbols_pb2
|
|
71
|
+
stub = RevaGetSymbols_pb2_grpc.RevaToolSymbolServiceStub(self.channel)
|
|
72
|
+
request = RevaGetSymbols_pb2.RevaSymbolRequest()
|
|
73
|
+
if address:
|
|
74
|
+
request.address = address
|
|
75
|
+
if symbol:
|
|
76
|
+
request.name = symbol
|
|
77
|
+
|
|
78
|
+
self.logger.debug(f"Getting symbol for {thing} request: {request}")
|
|
79
|
+
response = stub.GetSymbol(request)
|
|
80
|
+
self.logger.debug(f"Got symbol for {thing} response: {response}")
|
|
81
|
+
|
|
82
|
+
if response.name:
|
|
83
|
+
symbol = response.name
|
|
84
|
+
if response.address:
|
|
85
|
+
address = response.address
|
|
86
|
+
|
|
87
|
+
if address is None and symbol is None:
|
|
88
|
+
raise RevaToolException(message=f"Could not resolve {thing} to an address or symbol. Double check your symbol or address is correct.")
|
|
89
|
+
self.logger.debug(f"Resolved {thing} to address: {address}, symbol: {symbol}")
|
|
90
|
+
assert address is not None
|
|
91
|
+
return address, symbol
|
reverse_engineering_assistant-2.9.4/reverse_engineering_assistant/api_server_tools/llm_tools.py
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
from ast import Call
|
|
4
|
+
import queue
|
|
5
|
+
import threading
|
|
6
|
+
from typing import Callable, Dict, Optional
|
|
7
|
+
from uuid import uuid4
|
|
8
|
+
from venv import logger
|
|
9
|
+
|
|
10
|
+
from ..protocol.RevaChat_pb2_grpc import RevaChatServiceServicer
|
|
11
|
+
from ..protocol.RevaChat_pb2 import RevaChatMessageResponse
|
|
12
|
+
|
|
13
|
+
from functools import cache
|
|
14
|
+
from ..assistant import ReverseEngineeringAssistant
|
|
15
|
+
|
|
16
|
+
import logging
|
|
17
|
+
module_logger = logging.getLogger("reva-server")
|
|
18
|
+
|
|
19
|
+
from langchain_core.callbacks.base import BaseCallbackHandler
|
|
20
|
+
from langchain_core.agents import AgentAction, AgentFinish
|
|
21
|
+
|
|
22
|
+
from reverse_engineering_assistant.model import RevaModel
|
|
23
|
+
from reverse_engineering_assistant.model import get_llm_ollama, get_llm_openai
|
|
24
|
+
|
|
25
|
+
class RevaChat(RevaChatServiceServicer):
|
|
26
|
+
logger = logging.getLogger("reva-server.RevaChat")
|
|
27
|
+
|
|
28
|
+
def _model_from_request(self, request) -> RevaModel:
|
|
29
|
+
"""
|
|
30
|
+
Given a request, return the model associated with the request.
|
|
31
|
+
"""
|
|
32
|
+
if request.ollama.model:
|
|
33
|
+
return get_llm_ollama(base_url=request.ollama.url, model=request.ollama.model)
|
|
34
|
+
if request.openai.model:
|
|
35
|
+
return get_llm_openai(model=request.openai.model, api_key=request.openai.token)
|
|
36
|
+
raise ValueError("No model specified in request. Please file a bug.")
|
|
37
|
+
|
|
38
|
+
def chat(self, request, context):
|
|
39
|
+
self.logger.info(f"Received request: {request}")
|
|
40
|
+
assistant = ReverseEngineeringAssistant(
|
|
41
|
+
request.project,
|
|
42
|
+
model=self._model_from_request(request)
|
|
43
|
+
)
|
|
44
|
+
self.logger.info(f"Assistant: {assistant}")
|
|
45
|
+
llm_response = assistant.query(request.message)
|
|
46
|
+
self.logger.info(f"LLM Response: {llm_response}")
|
|
47
|
+
response = RevaChatMessageResponse()
|
|
48
|
+
response.message = llm_response
|
|
49
|
+
return response
|
|
50
|
+
|
|
51
|
+
def chatResponseStream(self, request, context):
|
|
52
|
+
"""
|
|
53
|
+
Given a request, return a stream of responses including
|
|
54
|
+
thoughts and a final message from the LLM.
|
|
55
|
+
"""
|
|
56
|
+
self.logger.info(f"Received request: {request}")
|
|
57
|
+
|
|
58
|
+
response_queue: queue.Queue = queue.Queue()
|
|
59
|
+
|
|
60
|
+
def callback(message: str):
|
|
61
|
+
# Called for intermediate thoughts
|
|
62
|
+
response = RevaChatMessageResponse()
|
|
63
|
+
response.thought = message
|
|
64
|
+
response_queue.put(response)
|
|
65
|
+
|
|
66
|
+
assistant = ReverseEngineeringAssistant(
|
|
67
|
+
request.project,
|
|
68
|
+
model=self._model_from_request(request),
|
|
69
|
+
logging_callbacks=[callback],
|
|
70
|
+
)
|
|
71
|
+
self.logger.info(f"Assistant: {assistant}")
|
|
72
|
+
|
|
73
|
+
def run_query(query: str):
|
|
74
|
+
llm_response = assistant.query(query)
|
|
75
|
+
self.logger.info(f"LLM Response: {llm_response}")
|
|
76
|
+
response = RevaChatMessageResponse()
|
|
77
|
+
response.message = llm_response
|
|
78
|
+
response_queue.put(response)
|
|
79
|
+
|
|
80
|
+
t = threading.Thread(target=run_query, args=[request.message])
|
|
81
|
+
t.start()
|
|
82
|
+
|
|
83
|
+
done = False
|
|
84
|
+
while not done:
|
|
85
|
+
response = response_queue.get()
|
|
86
|
+
# We stop when we get a message and not thoughts
|
|
87
|
+
if response.message and not response.thought:
|
|
88
|
+
done = True
|
|
89
|
+
yield response
|
|
90
|
+
t.join()
|
|
91
|
+
|
|
92
|
+
def chatStream(self, request_iterator, context):
|
|
93
|
+
assistant: Optional[ReverseEngineeringAssistant] = None
|
|
94
|
+
response_queue: queue.Queue = queue.Queue()
|
|
95
|
+
|
|
96
|
+
def callback(message: str):
|
|
97
|
+
# Called for intermediate thoughts
|
|
98
|
+
response = RevaChatMessageResponse()
|
|
99
|
+
response.thought = message
|
|
100
|
+
response_queue.put(response)
|
|
101
|
+
|
|
102
|
+
for request in request_iterator:
|
|
103
|
+
if not assistant:
|
|
104
|
+
assistant = ReverseEngineeringAssistant(
|
|
105
|
+
request.project,
|
|
106
|
+
model=self._model_from_request(request),
|
|
107
|
+
logging_callbacks=[callback],
|
|
108
|
+
)
|
|
109
|
+
self.logger.info(f"Received request: {request}")
|
|
110
|
+
def run_query(query: str):
|
|
111
|
+
assert assistant is not None
|
|
112
|
+
self.logger.info(f"Asking assistant: {query}")
|
|
113
|
+
llm_response = assistant.query(query)
|
|
114
|
+
self.logger.info(f"LLM Response: {llm_response}")
|
|
115
|
+
response = RevaChatMessageResponse()
|
|
116
|
+
response.message = llm_response
|
|
117
|
+
response_queue.put(response)
|
|
118
|
+
|
|
119
|
+
t = threading.Thread(target=run_query, args=[request.message])
|
|
120
|
+
t.start()
|
|
121
|
+
|
|
122
|
+
done = False
|
|
123
|
+
while not done:
|
|
124
|
+
response = response_queue.get()
|
|
125
|
+
# We stop when we get a message and not thoughts
|
|
126
|
+
if response.message and not response.thought:
|
|
127
|
+
done = True
|
|
128
|
+
yield response
|
|
129
|
+
t.join()
|
|
130
|
+
|
|
131
|
+
def shutdown(self, request, context):
|
|
132
|
+
self.logger.warning("Shutting down")
|
|
133
|
+
import sys
|
|
134
|
+
sys.exit(0)
|