Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds the Binary Ninja front-end plugin (MVP) + simplifies patch space 0 #240

Merged
merged 47 commits into from
Dec 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
de978eb
Initial schema for a plugin
Nov 12, 2022
dd5e49f
Progress on higher vars
Nov 17, 2022
bf823aa
Check supported architecture
Nov 17, 2022
bc53b8f
Comment
Nov 17, 2022
165fedd
Frame range
Nov 17, 2022
62ee9b0
Use helper function for defined vars
Nov 17, 2022
cc3621c
Identify data symbols that can be accessed via frame
Nov 17, 2022
60e2bed
Add button to refresh higher vars
Nov 17, 2022
0ff2c3e
Fixes
Nov 17, 2022
3784097
Simplify
Nov 17, 2022
70250eb
Store hvars in the class
Nov 17, 2022
ae5c835
More improvements
Nov 17, 2022
8c0712d
Reject invalid patch names
Nov 17, 2022
6257e3e
Fix
Nov 17, 2022
345a6a0
Add patch var editor, fix some bugs
Nov 21, 2022
d7d65ab
Ability to add live vars, some fixes to live vars analysis
Nov 22, 2022
20eb746
OGRE editor
Nov 22, 2022
4d620e3
Merge the OGRE editor with the patch editor
Nov 22, 2022
88b3f88
Serialize to the binja database
Nov 23, 2022
2b87b74
Fix patch name regex
Nov 23, 2022
a638946
Add ability to load a C source file
Nov 23, 2022
eba4425
Better labels for the buttons
Nov 23, 2022
f36f087
Better labels
Nov 23, 2022
6fdc09f
Allow patch size of zero
Nov 23, 2022
6b80030
Split the plugin into multiple modules
Nov 23, 2022
6668696
Default patch code is empty string
Nov 23, 2022
ec31692
Handle higher var values for "pointer" types
Nov 23, 2022
bc99fb5
Set current row if we had existing patches
Nov 23, 2022
efb45f3
Fix ogre decls formatting
Nov 30, 2022
ead854c
Import and export configs
Nov 30, 2022
488c5da
Redundant
Nov 30, 2022
6ec1fd4
Clear patch editor when we change to a different binaryview
Nov 30, 2022
a2e44d4
OGRE editor doesn't need title
Nov 30, 2022
a5e395a
Patch space editor (WIP)
Nov 30, 2022
8e14ea1
Use the `QValidator`s
Nov 30, 2022
ce00f95
Export spaces
Nov 30, 2022
5ec8d5d
Handle some cases when the LLIL isn't available
Dec 1, 2022
e6e18c3
Synchronize patch spaces with the BNDB
Dec 1, 2022
ced47be
Stretch patch and live vars widget columns
Dec 1, 2022
f9a39cc
Comment
Dec 1, 2022
4e417a6
Support `constant` higher var type
Dec 1, 2022
4c837f6
Fix size conversions
Dec 1, 2022
75c86ec
Clamp value based on size
Dec 1, 2022
fba85ed
Fix incorrect `segment` spec
Dec 1, 2022
22d89d1
Update samples
Dec 2, 2022
4230ece
Don't add the `overwritten` block if there are no overwritten instruc…
Dec 2, 2022
6b3f955
Misc
Dec 2, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion vibes-tools/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,46 @@ VIBES_XDG_APP_DIR := $(HOME)/.vibes
MINIZINC_MODEL_LOCAL := $(VIBES_XDG_APP_DIR)/model.mzn
MINIZINC_MODEL_SRC := resources/minizinc/model.mzn

BINARY_NINJA_DIR := resources/binja
BINARY_NINJA_TARGET := $(HOME)/.binaryninja/plugins/vibes

###################################
# DEFAULT
###################################

.DEFAULT_GOAL := all

###################################
# MINIZINC
###################################

$(MINIZINC_MODEL_LOCAL): $(MINIZINC_MODEL_SRC)
mkdir -p $(VIBES_XDG_APP_DIR)
cp $(MINIZINC_MODEL_SRC) $(MINIZINC_MODEL_LOCAL)

###################################
# BINARY NINJA
###################################

.PHONY: binja
binja:
mkdir -p $(BINARY_NINJA_TARGET)
cp -r $(BINARY_NINJA_DIR)/* $(BINARY_NINJA_TARGET)

###################################
# ALL
###################################

all: $(MINIZINC_MODEL_LOCAL)
$(MAKE) binja
$(MAKE) tools.all

.PHONY: build
build:
$(MAKE) tools.build

.PHONY: install
install: $(MINIZINC_MODEL_LOCAL)
install: $(MINIZINC_MODEL_LOCAL) binja
$(MAKE) tools.install

.PHONY: uninstall
Expand Down
129 changes: 129 additions & 0 deletions vibes-tools/resources/binja/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import re

from . import db
from . import utils
from .patchinfo import PatchInfo
from .patcheditor import PatchEditor
from binaryninja import *

from binaryninjaui import (
Menu,
UIAction,
UIActionHandler,
)

PATCH_EDITOR = 'VIBES\\Patch Editor'
patch_editor = None

supported = ["armv7", "thumb2", "armv7eb", "thumb2eb"]

def check_arch(bv):
arch = bv.arch.name
if arch not in supported:
show_message_box("VIBES", "Unsupported architecture %s" % arch,
MessageBoxButtonSet.OKButtonSet,
MessageBoxIcon.ErrorIcon)
return False
return True

def check_bv_changed(bv):
global patch_editor
if patch_editor is None:
return
if patch_editor.data.file.filename == bv.file.filename:
return
patch_editor = None
db.clear_patches()
db.clear_spaces()

def launch_editor(context):
global patch_editor
bv = context.binaryView
if not bv:
show_message_box("VIBES", "No binary currently in view",
MessageBoxButtonSet.OKButtonSet,
MessageBoxIcon.ErrorIcon)
return
if not check_arch(bv):
return
check_bv_changed(bv)
if not patch_editor:
patch_editor = PatchEditor(context, parent=context.widget)
patch_editor.show()

UIAction.registerAction(PATCH_EDITOR)
UIActionHandler.globalActions().bindAction(PATCH_EDITOR, UIAction(launch_editor))
Menu.mainMenu('Plugins').addAction(PATCH_EDITOR, 'show')

patch_name_re = re.compile("[A-Za-z][A-Za-z-_0-9]*")

def valid_patch_name(name):
return patch_name_re.fullmatch(name) is not None

def prompt_patch_name(bv):
patches = db.get_patches(bv)
while True:
name = get_text_line_input("Please provide a patch name",
"VIBES: new patch")
if name is None:
return None
name = name.decode('ASCII')
if name is None:
return None
elif name in patches:
utils.eprint("A patch with the name '%s' already exists" % name)
elif not valid_patch_name(name):
utils.eprint("Invalid patch name '%s'" % name)
else:
return name

def patch_range(bv, addr, n):
if not check_arch(bv):
return
check_bv_changed(bv)
global patch_editor
name = prompt_patch_name(bv)
if name is None:
return
if patch_editor:
patch_editor.add_patch(name, addr, n)
else:
# Patch editor hasn't been created yet
p = PatchInfo(name, addr, n, bv)
patches = db.get_patches(bv)
patches[name] = p
db.save_patch(bv, p)

def patch_addr(bv, addr):
if not check_arch(bv):
return
check_bv_changed(bv)
global patch_editor
name = prompt_patch_name(bv)
if name is None:
return
if patch_editor:
patch_editor.add_patch(name, addr, 0)
else:
# Patch editor hasn't been created yet
p = PatchInfo(name, addr, 0, bv)
patches = db.get_patches(bv)
patches[name] = p
db.save_patch(bv, p)

def add_patch_space(bv, addr, n):
if not check_arch(bv):
return
check_bv_changed(bv)
global patch_editor
if patch_editor:
patch_editor.spaces.add_space(addr, n)
else:
space = AddressRange(start=addr, end=addr+n)
spaces = db.get_spaces(bv)
spaces.append(space)
db.save_space(bv, space)

PluginCommand.register_for_range("VIBES\\Patch highlighted instruction(s)", "", patch_range)
PluginCommand.register_for_address("VIBES\\Insert patch at this address", "", patch_addr)
PluginCommand.register_for_range("VIBES\\Add patch space", "", add_patch_space)
102 changes: 102 additions & 0 deletions vibes-tools/resources/binja/db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
from binaryninja import AddressRange
from .patchinfo import PatchInfo

patches = {}

def clear_patches():
global patches
patches.clear()

spaces = []

def clear_spaces():
global spaces
spaces.clear()

def get_patches(bv):
global patches
try:
ps = bv.query_metadata("vibes.patch-infos")
except KeyError:
ps = {}
bv.store_metadata("vibes.patch-infos", ps)
if not patches:
for k, v in ps.items():
patches[k] = PatchInfo.deserialize(bv, k, v)
return patches

def save_patch(bv, p):
ps = bv.query_metadata("vibes.patch-infos")
ps[p.name] = p.serialize(bv)
bv.store_metadata("vibes.patch-infos", ps)

def delete_patch(bv, name):
ps = bv.query_metadata("vibes.patch-infos")
del ps[name]
bv.store_metadata("vibes.patch-infos", ps)
try:
ps = bv.query_metadata("vibes.patch-codes")
del ps[name]
bv.store_metadata("vibes.patch-codes", ps)
except KeyError:
pass

def get_spaces(bv):
global spaces
try:
ss = bv.query_metadata("vibes.patch-spaces")
except KeyError:
ss = []
bv.store_metadata("vibes.patch-spaces", ss)
if not spaces:
for start, end in ss:
spaces.append(AddressRange(start, end))
return spaces

def save_space(bv, space):
ss = bv.query_metadata("vibes.patch-spaces")
ss.append((space.start, space.end))
bv.store_metadata("vibes.patch-spaces", ss)

def delete_space(bv, space):
ss = bv.query_metadata("vibes.patch-spaces")
try:
ss.remove(space)
bv.store_metadata("vibes.patch-spaces", ss)
except ValueError:
pass

def get_patch_code(bv, name):
try:
ps = bv.query_metadata("vibes.patch-codes")
except KeyError:
ps = {name: ""}
bv.store_metadata("vibes.patch-codes", ps)
return ps.get(name, "")

def save_patch_code(bv, name, code):
ps = bv.query_metadata("vibes.patch-codes")
ps[name] = code
bv.store_metadata("vibes.patch-codes", ps)

def get_ogre_functions(bv):
try:
fs = bv.query_metadata("vibes.ogre-functions")
except KeyError:
fs = []
bv.store_metadata("vibes.ogre-functions", fs)
return fs

def save_ogre_func(bv, f):
fs = bv.query_metadata("vibes.ogre-functions")
fs.append(f)
fs = list(set(fs))
bv.store_metadata("vibes.ogre-functions", fs)

def remove_ogre_func(bv, f):
fs = bv.query_metadata("vibes.ogre-functions")
try:
fs.remove(f)
bv.store_metadata("vibes.ogre-functions", fs)
except ValueError:
pass
Loading