Skip to content

Instantly share code, notes, and snippets.

@SWORDIntel
Last active February 21, 2025 17:10
Show Gist options
  • Save SWORDIntel/b12e004eb7daef0f7497160c191f7329 to your computer and use it in GitHub Desktop.
Save SWORDIntel/b12e004eb7daef0f7497160c191f7329 to your computer and use it in GitHub Desktop.
CAPA CLI
#!/usr/bin/env python3
import os
import subprocess
from datetime import datetime
import tkinter as tk
from tkinter import filedialog, ttk, messagebox, scrolledtext
class CapaGUI(tk.Tk):
def __init__(self):
super().__init__()
self.title("capa GUI Interface")
self.create_widgets()
def create_widgets(self):
# Row 0: Input file selection
tk.Label(self, text="Input File:").grid(row=0, column=0, sticky="w", padx=5, pady=5)
self.input_file_var = tk.StringVar()
self.input_entry = tk.Entry(self, textvariable=self.input_file_var, width=50)
self.input_entry.grid(row=0, column=1, padx=5, pady=5)
tk.Button(self, text="Browse", command=self.browse_file).grid(row=0, column=2, padx=5, pady=5)
# Verbosity (None, -v, -vv) - use radio buttons
tk.Label(self, text="Verbosity:").grid(row=1, column=0, sticky="w", padx=5, pady=5)
self.verbosity_var = tk.StringVar(value="none")
verbosity_frame = tk.Frame(self)
verbosity_frame.grid(row=1, column=1, padx=5, pady=5, sticky="w")
tk.Radiobutton(verbosity_frame, text="None", variable=self.verbosity_var, value="none").pack(side="left")
tk.Radiobutton(verbosity_frame, text="-v", variable=self.verbosity_var, value="-v").pack(side="left")
tk.Radiobutton(verbosity_frame, text="-vv", variable=self.verbosity_var, value="-vv").pack(side="left")
# Debug and Quiet flags
self.debug_var = tk.BooleanVar()
tk.Checkbutton(self, text="Debug (-d)", variable=self.debug_var).grid(row=2, column=0, sticky="w", padx=5, pady=5)
self.quiet_var = tk.BooleanVar()
tk.Checkbutton(self, text="Quiet (-q)", variable=self.quiet_var).grid(row=2, column=1, sticky="w", padx=5, pady=5)
# Color option
tk.Label(self, text="Color:").grid(row=3, column=0, sticky="w", padx=5, pady=5)
self.color_var = tk.StringVar(value="auto")
ttk.Combobox(self, textvariable=self.color_var, values=["auto", "always", "never"], state="readonly", width=10)\
.grid(row=3, column=1, sticky="w", padx=5, pady=5)
# -f file format
tk.Label(self, text="-f Format:").grid(row=4, column=0, sticky="w", padx=5, pady=5)
self.format_var = tk.StringVar(value="auto")
formats = ["auto", "pe", "dotnet", "elf", "sc32", "sc64", "cape", "drakvuf", "vmray", "freeze", "binexport2", "binja_database"]
ttk.Combobox(self, textvariable=self.format_var, values=formats, state="readonly", width=15)\
.grid(row=4, column=1, sticky="w", padx=5, pady=5)
# -b backend
tk.Label(self, text="-b Backend:").grid(row=5, column=0, sticky="w", padx=5, pady=5)
self.backend_var = tk.StringVar(value="auto")
backends = ["auto", "vivisect", "ida", "pefile", "binja", "dotnet", "binexport2", "freeze", "cape", "drakvuf", "vmray"]
ttk.Combobox(self, textvariable=self.backend_var, values=backends, state="readonly", width=15)\
.grid(row=5, column=1, sticky="w", padx=5, pady=5)
# Restrict-to functions
tk.Label(self, text="--restrict-to-functions:").grid(row=6, column=0, sticky="w", padx=5, pady=5)
self.restrict_func_var = tk.StringVar()
tk.Entry(self, textvariable=self.restrict_func_var, width=50).grid(row=6, column=1, padx=5, pady=5, columnspan=2)
# Restrict-to processes
tk.Label(self, text="--restrict-to-processes:").grid(row=7, column=0, sticky="w", padx=5, pady=5)
self.restrict_proc_var = tk.StringVar()
tk.Entry(self, textvariable=self.restrict_proc_var, width=50).grid(row=7, column=1, padx=5, pady=5, columnspan=2)
# OS option
tk.Label(self, text="--os:").grid(row=8, column=0, sticky="w", padx=5, pady=5)
self.os_var = tk.StringVar(value="auto")
ttk.Combobox(self, textvariable=self.os_var, values=["auto", "linux", "macos", "windows"], state="readonly", width=10)\
.grid(row=8, column=1, sticky="w", padx=5, pady=5)
# Rules, Signatures, Tag
tk.Label(self, text="-r RULES:").grid(row=9, column=0, sticky="w", padx=5, pady=5)
self.rules_var = tk.StringVar()
tk.Entry(self, textvariable=self.rules_var, width=50).grid(row=9, column=1, padx=5, pady=5, columnspan=2)
tk.Label(self, text="-s SIGNATURES:").grid(row=10, column=0, sticky="w", padx=5, pady=5)
self.signatures_var = tk.StringVar()
tk.Entry(self, textvariable=self.signatures_var, width=50).grid(row=10, column=1, padx=5, pady=5, columnspan=2)
tk.Label(self, text="-t TAG:").grid(row=11, column=0, sticky="w", padx=5, pady=5)
self.tag_var = tk.StringVar()
tk.Entry(self, textvariable=self.tag_var, width=50).grid(row=11, column=1, padx=5, pady=5, columnspan=2)
# -j flag (JSON mode) checkbox (though we default to JSON output from capa)
self.json_var = tk.BooleanVar(value=True)
tk.Checkbutton(self, text="-j (JSON output)", variable=self.json_var).grid(row=12, column=0, sticky="w", padx=5, pady=5)
# Run button
tk.Button(self, text="Run Analysis", command=self.run_analysis).grid(row=13, column=0, columnspan=3, pady=10)
# Output area (scrolled text)
tk.Label(self, text="Output:").grid(row=14, column=0, sticky="nw", padx=5, pady=5)
self.output_text = scrolledtext.ScrolledText(self, width=80, height=15)
self.output_text.grid(row=15, column=0, columnspan=3, padx=5, pady=5)
def browse_file(self):
file_path = filedialog.askopenfilename(title="Select file for capa analysis")
if file_path:
self.input_file_var.set(file_path)
def run_analysis(self):
input_file = self.input_file_var.get().strip()
if not input_file or not os.path.isfile(input_file):
messagebox.showerror("Error", "Please select a valid input file.")
return
# Build the command based on selections.
command = ["capa"] # assuming capa is in PATH or same directory
# Verbosity:
verbosity = self.verbosity_var.get()
if verbosity != "none":
command.append(verbosity)
# Debug and Quiet flags
if self.debug_var.get():
command.append("-d")
if self.quiet_var.get():
command.append("-q")
# Color option
if self.color_var.get():
command.extend(["--color", self.color_var.get()])
# -f format and -b backend
if self.format_var.get():
command.extend(["-f", self.format_var.get()])
if self.backend_var.get():
command.extend(["-b", self.backend_var.get()])
# Restrict options
if self.restrict_func_var.get().strip():
command.extend(["--restrict-to-functions", self.restrict_func_var.get().strip()])
if self.restrict_proc_var.get().strip():
command.extend(["--restrict-to-processes", self.restrict_proc_var.get().strip()])
# OS option
if self.os_var.get():
command.extend(["--os", self.os_var.get()])
# Rules, Signatures, Tag
if self.rules_var.get().strip():
command.extend(["-r", self.rules_var.get().strip()])
if self.signatures_var.get().strip():
command.extend(["-s", self.signatures_var.get().strip()])
if self.tag_var.get().strip():
command.extend(["-t", self.tag_var.get().strip()])
# JSON flag
if self.json_var.get():
command.append("-j")
# Finally, add the input file
command.append(input_file)
# Prepare the output file name: analysis_<basename>_<timestamp>.json
base_name = os.path.splitext(os.path.basename(input_file))[0]
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output_filename = f"analysis_{base_name}_{timestamp}.json"
self.output_text.insert(tk.END, f"Running command: {' '.join(command)}\n")
self.output_text.insert(tk.END, f"Output will be saved to: {output_filename}\n")
self.update()
# Run capa and capture stderr for error reporting.
try:
with open(output_filename, "w") as outfile:
result = subprocess.run(command, stdout=outfile, stderr=subprocess.PIPE, text=True)
if result.returncode == 0:
self.output_text.insert(tk.END, "Analysis complete.\n")
else:
self.output_text.insert(tk.END, f"Error running capa:\n{result.stderr}\n")
except Exception as e:
self.output_text.insert(tk.END, f"Exception occurred: {e}\n")
self.output_text.insert(tk.END, "-" * 60 + "\n")
self.output_text.see(tk.END)
if __name__ == "__main__":
app = CapaGUI()
app.mainloop()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment