Skip to content

Commit 8b69c24

Browse files
authored
Merge pull request #11 from moval0x1/main
Added new features
2 parents 19f224c + 286e64e commit 8b69c24

16 files changed

+4045
-234
lines changed

AskJOE.py

+223-226
Large diffs are not rendered by default.

AskJOE/__init__.py

Whitespace-only changes.

AskJOE/capa_explorer.py

+378
Large diffs are not rendered by default.

AskJOE/config.ini

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[KEY]
2+
OPEN_AI = KEY_HERE
3+
4+
[CAPA]
5+
GITHUB = https://raw.githubusercontent.com/mandiant/capa/master/capa/ghidra/capa_explorer.py

AskJOE/constants.py

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from enum import Enum
2+
3+
4+
class Operation(Enum):
5+
PROMPT = "User Prompt."
6+
AI_TRIAGE = "AI Triage."
7+
EXPLAIN_FUNCTION = "Explain Function."
8+
EXPLAIN_SELECTION = "Explain Selection."
9+
BETTER_NAME = "Better Name."
10+
SIMPLIFY_CODE = "Simplify Code."
11+
STACK_STRINGS = "Stack Strings."
12+
XORS = "Search XORs."
13+
CRYPTO_CONSTS = "Crypto Consts."
14+
CAPA_ANALYSIS = "Capa Analysis."

AskJOE/data.py

+3,250
Large diffs are not rendered by default.

AskJOE/db.json

+1
Large diffs are not rendered by default.

AskJOE/ghidra_utils.py

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import re
2+
import ghidra
3+
4+
# For type checking
5+
try:
6+
from ghidra.ghidra_builtins import *
7+
except ImportError:
8+
pass
9+
10+
from java.awt import Color
11+
from ghidra.util.exception import CancelledException
12+
from ghidra.app.plugin.core.colorizer import ColorizingService
13+
from ghidra.app.decompiler import DecompInterface
14+
from ghidra.util.task import ConsoleTaskMonitor
15+
from ghidra.program.model.data import StringDataType
16+
from ghidra.program.model.mem import MemoryAccessException
17+
18+
19+
def set_ghidra_plate_comment(address, comment):
20+
service = state().getTool().getService(ColorizingService)
21+
22+
listing = currentProgram().getListing()
23+
code_unit = listing.getCodeUnitAt(address)
24+
code_unit.setComment(code_unit.PLATE_COMMENT, comment)
25+
service.setBackgroundColor(address, address, Color(143, 27, 104))
26+
27+
28+
def rename_function(old_name, new_name):
29+
function_manager = currentProgram().getFunctionManager()
30+
core_func_obj = function_manager.getFunctionAt(old_name.getEntryPoint())
31+
core_func_obj.setName(new_name, ghidra.program.model.symbol.SourceType.DEFAULT)
32+
33+
34+
def recover_stack_string(address):
35+
stack_string = ""
36+
current_instruction = currentProgram().getListing().getInstructionAt(address)
37+
current_address = address
38+
39+
while current_instruction and current_instruction.getScalar(1):
40+
scalar_value = current_instruction.getScalar(1)
41+
if not scalar_value:
42+
break
43+
decimal_value = scalar_value.getValue()
44+
45+
try:
46+
next_instruction = current_instruction.getNext()
47+
if decimal_value == 0 and stack_string and next_instruction.getScalar(1).value == 0:
48+
return stack_string, str(current_address)
49+
except AttributeError:
50+
pass
51+
52+
stack_string_part = ""
53+
54+
while decimal_value > 0:
55+
if decimal_value > 126:
56+
try:
57+
hex_str = hex(decimal_value)[2:]
58+
bytes_array = bytes.fromhex(hex_str)
59+
ascii_str = bytes_array.decode()
60+
stack_string_part += ascii_str[::-1]
61+
decimal_value = 0
62+
except (ValueError, UnicodeDecodeError):
63+
decimal_value = 0
64+
else:
65+
stack_string_part += chr(decimal_value & 0xff)
66+
decimal_value >>= 8
67+
68+
stack_string += stack_string_part
69+
70+
try:
71+
next_instruction = current_instruction.getNext()
72+
if next_instruction.getScalar(1) is None and next_instruction.getNext().getScalar(1) is not None:
73+
current_instruction = next_instruction
74+
except AttributeError:
75+
pass
76+
77+
current_instruction = current_instruction.getNext()
78+
current_address = current_instruction.getAddress() if current_instruction else current_address
79+
80+
return stack_string, str(current_address)
81+
82+
83+
def print_joe_answer(option, open_ai_response=""):
84+
if open_ai_response:
85+
print(f"\n[+] AskJOE said: {option}\n{open_ai_response}\n")
86+
else:
87+
print(f"\n[+] AskJOE said: {option}\n")

AskJOE/openai_utils.py

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import configparser
2+
3+
# For type checking
4+
try:
5+
from ghidra.ghidra_builtins import *
6+
except ImportError:
7+
pass
8+
9+
from openai import OpenAI
10+
from ghidra.app.script import GhidraScriptUtil
11+
12+
import logging
13+
14+
# Suppress INFO messages from httpx
15+
logging.getLogger('httpx').setLevel(logging.WARNING)
16+
17+
def read_config(section_name, key_name) -> str:
18+
ghidra_script = GhidraScriptUtil()
19+
config = configparser.ConfigParser()
20+
config.read(f'{ghidra_script.USER_SCRIPTS_DIR}/AskJOE/config.ini')
21+
return str(config.get(section_name, key_name))
22+
23+
24+
def ask_open_ai(prompt, decompiled_function="") -> object:
25+
client = OpenAI(api_key=read_config('KEY', 'OPEN_AI'))
26+
27+
response = client.chat.completions.create(
28+
model="gpt-4o",
29+
messages=[{"role": "user", "content": f"{prompt}\n{decompiled_function}"}],
30+
temperature=0.8,
31+
max_tokens=2048,
32+
top_p=1,
33+
frequency_penalty=0.2,
34+
presence_penalty=0
35+
)
36+
37+
return response
38+
39+
40+
def parse_open_ai_response(response):
41+
try:
42+
return response.choices[0].message.content
43+
except AttributeError:
44+
raise Exception(response.choices[0].message.content)

AskJOE/utils.py

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import re
2+
import struct
3+
4+
5+
def pack_constant(const):
6+
byte_array = []
7+
if const["size"] == "B":
8+
byte_array = const["array"]
9+
elif const["size"] == "L":
10+
for val in const["array"]:
11+
byte_array += list(map(lambda x: x if isinstance(x, int) else ord(x), struct.pack("<L", val)))
12+
elif const["size"] == "Q":
13+
for val in const["array"]:
14+
byte_array += list(map(lambda x: x if isinstance(x, int) else ord(x), struct.pack("<Q", val)))
15+
return bytes(bytearray(byte_array))

README.md

+28-6
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,22 @@
44
AskJoe is a tool that utilizes [OpenAI](https://openai.com/) to assist researchers wanting to use [Ghidra](https://github.com/NationalSecurityAgency/ghidra) as their malware analysis tool. It was based on the [Gepetto](https://github.com/JusticeRage/Gepetto) idea.
55
With its capabilities, OpenAI highly simplifies the practice of reverse engineering, allowing researchers to better detect and mitigate threats.
66

7-
![AskJOE Running](/imgs/AskJOE-Running.gif "AskJOE Running")
7+
![AskJOE Running](/imgs/AskJOE-updated-running.gif "AskJOE Running")
88

99
The tool is free to use, under the limitations of Github.
1010

1111
Author: https://twitter.com/moval0x1 | Threat Researcher, Security Joes
1212

13+
## Updates - 07/20/2024
14+
- Ghidrathon added and removed pyhidra
15+
- Refactored Code
16+
- AI Triage added
17+
- Better Name added
18+
- Search for crypto constants added
19+
- Mandiant CAPA added
20+
1321
## Updates - 07/31/2023
14-
- Search XOR
22+
- Search XORs
1523
- Ask User Prompt (To OpenAI)
1624

1725
## Updates - 05/23/2023
@@ -34,17 +42,31 @@ Author: https://twitter.com/moval0x1 | Threat Researcher, Security Joes
3442
- Monitor messages added
3543

3644
## Dependencies
37-
- Requests: `pip install requests`
45+
- requests: `pip install requests`
46+
- flare-capa: `pip install flare-capa`
47+
- openai: `pip install openai`
3848
- [Ghidra](https://github.com/NationalSecurityAgency/ghidra)
39-
- [Pyhidra](https://github.com/dod-cyber-crime-center/pyhidra)
4049
- [Python3](https://www.python.org/downloads/)
50+
- [Ghidrathon](https://github.com/mandiant/Ghidrathon)
4151

4252

4353
## Limitations
4454
> OpenAI has a hard limit of 4096 tokens for each API call, so if your text is longer than that, you'll need to split it up. However, OpenAI currently does not support stateful conversations over multiple API calls, which means it does not remember the previous API call.
4555
56+
> By now, It only supports Linux OS.
4657
4758
## How to install?
48-
- Copy the AskJOE files to the ghidra_scripts folder
59+
Copy the ```AskJOE.py```, ```JOES.png``` and ```AskJOE folder```.
60+
61+
![ghidra_scripts home](/imgs/ghidra_scripts_home.png "ghidra_scripts home")
62+
63+
Within any of the ```ghidra_scripts``` folders.
64+
65+
![ghidra_scripts folders](/imgs/ghidra_scripts_folders.png "ghidra_scripts folders")
66+
67+
## Credits
68+
Some functions were added in the AskJOE, but we did not create them. Let us give the proper credit.
4969

50-
![ghidra_scripts](/imgs/ghidra_scripts_folder.png "ghidra_scripts")
70+
- [Stack Strings](https://github.com/reb311ion/replica/blob/fede41b9afd2ef5e33c860ce114e8a24193751ac/replica.py#L560)
71+
- [Crypto Constants](https://github.com/reb311ion/replica/blob/fede41b9afd2ef5e33c860ce114e8a24193751ac/replica.py#L527)
72+
- [Capa Explorer](https://github.com/mandiant/capa/blob/master/capa/ghidra/capa_explorer.py)

config.ini

-2
This file was deleted.

imgs/AskJOE-Running.gif

-861 KB
Binary file not shown.

imgs/AskJOE-updated-running.gif

1.15 MB
Loading
File renamed without changes.

imgs/ghidra_scripts_home.png

18.7 KB
Loading

0 commit comments

Comments
 (0)