Skip to content

Commit

Permalink
visual: 'i' to invert conditional jumps in the decompilation mode
Browse files Browse the repository at this point in the history
  • Loading branch information
plasma-disassembler committed Nov 14, 2017
1 parent 3c3d16c commit f330547
Show file tree
Hide file tree
Showing 8 changed files with 61 additions and 15 deletions.
9 changes: 4 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
PLASMA
PL**ASM**A
======

The old project name was **Reverse**.
Expand Down Expand Up @@ -38,7 +38,7 @@ Optional :

./install.sh

Or if you have already installed requirements with the previous command :
Or if you have already installed requirements with the previous command:

./install.sh --update

Expand All @@ -61,10 +61,9 @@ Check tests :

![plasma](/images/visual.png?raw=true)

## Qt memory map (memmap)
Take the control of the flow graph by inverting conditional jumps:

The image is actually static.
![plasma](/images/qt_memory.png?raw=true)
![plasma](/images/invcond.png?raw=true)


## Scripting (Python API)
Expand Down
Binary file added images/invcond.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 3 additions & 2 deletions plasma/lib/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ def dump(self, o, tab=0):

class Ast_Ifelse:
def __init__(self, jump_inst, br_next_jump, br_next,
expected_next_addr, prefetch=None):
expected_next_addr, prefetch=None, force_inv_if=False):
self.jump_inst = jump_inst
self.br_next = br_next
self.br_next_jump = br_next_jump
Expand All @@ -122,6 +122,7 @@ def __init__(self, jump_inst, br_next_jump, br_next,
self.parent = None
self.level = 0
self.expected_next_addr = expected_next_addr
self.force_inv_if = force_inv_if

def dump(self, o, tab=0, print_else_keyword=False):
ARCH_UTILS = o.gctx.libarch.utils
Expand Down Expand Up @@ -163,7 +164,7 @@ def dump(self, o, tab=0, print_else_keyword=False):
o._keyword("if ")

# jump_inst is the condition to go to the else-part
if inv_if:
if inv_if ^ self.force_inv_if:
o._if_cond(ARCH_UTILS.get_cond(self.jump_inst),
self.fused_inst)
else:
Expand Down
10 changes: 9 additions & 1 deletion plasma/lib/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
from plasma.lib.memory import Memory


VERSION = 2.8
VERSION = 2.9
LAST_COMPATIBLE = 2.7


Expand Down Expand Up @@ -75,6 +75,7 @@ def __init_vars(self):
self.data_sub_xrefs = {} # data_address -> {addresses_with_xrefs: True}
self.imports = {} # ad -> flags
self.immediates = {} # insn_ad -> immediate result
self.inverted_cond = {} # addr -> arbitrary_value

self.raw_base = 0
self.raw_type = None
Expand Down Expand Up @@ -122,6 +123,7 @@ def load(self, filename):
self.__load_xrefs(data)
self.__load_imports(data)
self.__load_immediates(data)
self.__load_inverted_cond(data)

self.loaded = True

Expand Down Expand Up @@ -151,6 +153,7 @@ def save(self, history):
"raw_is_big_endian": self.raw_is_big_endian,
"imports": self.imports,
"immediates": self.immediates,
"inverted_cond": self.inverted_cond,
}

for j in self.jmptables.values():
Expand Down Expand Up @@ -253,3 +256,8 @@ def __load_functions(self, data):
self.func_id_counter = data["func_id_counter"]
except:
self.func_id_counter = max(self.func_id.keys()) + 1


def __load_inverted_cond(self, data):
if "inverted_cond" in data:
self.inverted_cond = data["inverted_cond"]
21 changes: 14 additions & 7 deletions plasma/lib/generate_ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,8 @@ def generate_ast(ctx__):
fake_br = Ast_Branch()
fake_br.level = sys.maxsize

libarch = ctx.gctx.libarch

while stack or waiting:

if not stack and waiting:
Expand Down Expand Up @@ -589,13 +591,13 @@ def generate_ast(ctx__):
if c1:
exit_loop = nxt[BRANCH_NEXT]
nxt_node_in_loop = nxt[BRANCH_NEXT_JUMP]
cond_id = ctx.gctx.libarch.utils.invert_cond(blk[0])
cond_id = libarch.utils.invert_cond(blk[0])
goto_set = True

if c2:
exit_loop = nxt[BRANCH_NEXT_JUMP]
nxt_node_in_loop = nxt[BRANCH_NEXT]
cond_id = ctx.gctx.libarch.utils.get_cond(blk[0])
cond_id = libarch.utils.get_cond(blk[0])
goto_set = True

# goto to exit a loop
Expand All @@ -614,7 +616,7 @@ def generate_ast(ctx__):
# and-if
if ctx.gctx.print_andif:
if else_addr == nxt[BRANCH_NEXT_JUMP]:
cond_id = ctx.gctx.libarch.utils.invert_cond(blk[0])
cond_id = libarch.utils.invert_cond(blk[0])
a = Ast_AndIf(blk[0], cond_id, nxt[BRANCH_NEXT], prefetch)
a.parent = ast
a.idx_in_parent = len(ast.nodes)
Expand All @@ -636,7 +638,7 @@ def generate_ast(ctx__):

# and-if
if else_addr == nxt[BRANCH_NEXT]:
cond_id = ctx.gctx.libarch.utils.get_cond(blk[0])
cond_id = libarch.utils.get_cond(blk[0])
a = Ast_AndIf(blk[0], cond_id, nxt[BRANCH_NEXT_JUMP], prefetch)
a.parent = ast
a.idx_in_parent = len(ast.nodes)
Expand All @@ -654,6 +656,11 @@ def generate_ast(ctx__):

endpoint = search_endpoint(ctx, ast, curr, l_set, l_prev_loop, l_start)

force_inv_if = False
if curr in ctx.gctx.db.inverted_cond:
nxt = list(reversed(nxt))
force_inv_if = True

ast_if = Ast_Branch()
ast_if.parent = ast
ast_if.level = level + 1
Expand Down Expand Up @@ -706,7 +713,7 @@ def generate_ast(ctx__):
ast.add(Ast_Goto(else_addr))

else:
a = Ast_Ifelse(blk[0], ast_else, ast_if, endpoint, prefetch)
a = Ast_Ifelse(blk[0], ast_else, ast_if, endpoint, prefetch, force_inv_if)
stack.append((ast_else, list(loops_stack), curr,
nxt[BRANCH_NEXT_JUMP], else_addr))

Expand Down Expand Up @@ -734,15 +741,15 @@ def generate_ast(ctx__):

start = time()

for func in ctx.gctx.libarch.registered:
for func in libarch.registered:
func(ctx, ast)

elapsed = time()
elapsed = elapsed - start
debug__("Functions for processing ast in %fs" % elapsed)

if ctx.gctx.color:
assign_colors(ctx.gctx.libarch, ctx, ast)
assign_colors(libarch, ctx, ast)

if waiting:
ast_head.nodes.insert(0, Ast_Comment(""))
Expand Down
2 changes: 2 additions & 0 deletions plasma/lib/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,8 @@ def _commented_inst(self, i, tab):
self._bytes(i.bytes)
self._comment(self.get_inst_str(i))
self._inline_comment(i)
if i.address in self.gctx.db.inverted_cond:
self._internal_comment(" ; manually inverted")
self._new_line()


Expand Down
1 change: 1 addition & 0 deletions plasma/lib/ui/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ def __init__(self, gctx):
"; edit inline comment (enter/escape to validate/cancel)",
"U undefine",
"F list of functions",
"i decompilation: invert a conditional jump",
"",
"Options:",
"I switch to traditional instruction string output (3 modes)",
Expand Down
28 changes: 28 additions & 0 deletions plasma/lib/ui/disasmbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ def __init__(self, x, y, w, h, gctx, ad, analyzer, api,
b"N": self.main_cmd_search_backward,
b"j": self.main_cmd_jump_to,
b"F": self.main_cmd_functions,
b"i": self.main_cmd_invert_conf,

b"c": self.main_cmd_set_code,
b"p": self.main_cmd_set_function,
Expand Down Expand Up @@ -1314,3 +1315,30 @@ def main_cmd_functions(self):
self.goto_address(ad)
self.main_cmd_line_middle()
return ret


def main_cmd_invert_conf(self):
if self.mode != MODE_DECOMPILE:
self.status_bar_message("only if decompilation mode")
return False

line = self.win_y + self.cursor_y
if line not in self.output.line_addr:
self.status_bar_message("error: move the cursor on a conditional jump")
return False

ad = self.output.line_addr[line]

i = self.gctx.dis.lazy_disasm(ad)
if i is not None and not self.gctx.libarch.utils.is_cond_jump(i):
self.status_bar_message("error: move the cursor on a conditional jump")
return False

if ad in self.db.inverted_cond:
del self.db.inverted_cond[ad]
else:
self.db.inverted_cond[ad] = 1

self.reload_asm()
self.db.modified = True
return True

0 comments on commit f330547

Please sign in to comment.