├── .gitignore ├── README.md ├── debogus.py ├── example ├── example-1 ├── example-1_recovered ├── example-2 ├── example-2_recovered ├── example-3.cpp ├── example-3.exe └── example-3_recovered.exe ├── test.bat └── test.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | test.py 3 | debogus.py.bak -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # debogus 2 | Deobfuscate OLLVM Bogus Control Flow with angr 3 | ## Usage 4 | ``` 5 | python debogus.py [-h] [-f FILE] [-s START] 6 | ``` 7 | 8 | ### Arguments 9 | - `-h, --help` Show this help message and exit 10 | - `-f FILE, --file FILE` File to deobfuscate 11 | - `-s START, --start START` Starting address of target function (Optional, address of `main` function by default) 12 | 13 | ### Examples 14 | See [test.bat](test.bat) or [test.sh](test.sh) -------------------------------------------------------------------------------- /debogus.py: -------------------------------------------------------------------------------- 1 | import angr 2 | from angrmanagement.utils.graph import to_supergraph 3 | import argparse 4 | import logging 5 | import os 6 | 7 | # def patch_jmp(block, jmp_addr): 8 | # insn = block.capstone.insns[-1] 9 | # offset = insn.address - proj.loader.main_object.mapped_base 10 | # # Nop original jx/jnx instruction 11 | # binfile[offset : offset + insn.size] = b'\x90' * insn.size 12 | # # Patch jmp instruction that jumps to the real successor 13 | # binfile[offset : offset + 5] = b'\xE9' + (jmp_addr - (insn.address + 5)).to_bytes(4, 'little', signed=True) 14 | # print('Patch [%s\t%s] at %#x' % (insn.mnemonic, insn.op_str, insn.address)) 15 | 16 | def patch_nops(block): 17 | offset = block.addr - proj.loader.main_object.mapped_base 18 | binfile[offset : offset + block.size] = b'\x90' * block.size 19 | print('Patch nop at block %#x' % block.addr) 20 | 21 | def get_cfg(func_addr): 22 | cfg = proj.analyses.CFGFast(normalize=True, force_complete_scan=False) 23 | function_cfg = cfg.functions.get(func_addr).transition_graph 24 | super_cfg = to_supergraph(function_cfg) 25 | return super_cfg 26 | 27 | def deobfu_func(func_addr): 28 | blocks = set() 29 | cfg = get_cfg(func_addr) 30 | for node in cfg.nodes: 31 | blocks.add(node.addr) 32 | print([hex(b) for b in blocks]) 33 | # Symbolic execution 34 | state = proj.factory.blank_state(addr=func_addr) 35 | simgr = proj.factory.simgr(state) 36 | while len(simgr.active): 37 | for active in simgr.active: 38 | blocks.discard(active.addr) 39 | # hook call instructions 40 | block = proj.factory.block(active.addr) 41 | for insn in block.capstone.insns: 42 | if insn.mnemonic == 'call': 43 | next_func_addr = int(insn.op_str, 16) 44 | proj.hook(next_func_addr, angr.SIM_PROCEDURES["stubs"]["ReturnUnconstrained"](), replace=True) 45 | print('Hook [%s\t%s] at %#x' % (insn.mnemonic, insn.op_str, insn.address)) 46 | simgr.step() 47 | for block_addr in blocks: 48 | patch_nops(proj.factory.block(block_addr)) 49 | 50 | if __name__ == '__main__': 51 | # Disable warning 52 | logging.getLogger('cle').setLevel(logging.ERROR) 53 | logging.getLogger('angr').setLevel(logging.ERROR) 54 | parser = argparse.ArgumentParser() 55 | parser.add_argument('-f', '--file', required=True, help='File to deobfuscate') 56 | parser.add_argument('-s', '--start', type=lambda x : int(x, 0), help='Starting address of target function') 57 | args = parser.parse_args() 58 | # Load binary file ${file} into angr 59 | proj = angr.Project(args.file, load_options={"auto_load_libs": False}) 60 | start = args.start 61 | if start == None: 62 | main = proj.loader.find_symbol('main') 63 | if main == None: 64 | parser.error('Can\'t find the main function, please provide argument -s/--start') 65 | start = main.rebased_addr 66 | # Load binary file ${file} into memory 67 | with open(args.file, 'rb') as file: 68 | binfile = bytearray(file.read()) 69 | # Do deobfuscation on target function 70 | deobfu_func(func_addr=start) 71 | # Write the recovered binary file to ${file}_recovered 72 | fname, ext = os.path.splitext(args.file) 73 | with open(fname + '_recovered' + ext, 'wb') as file: 74 | file.write(binfile) 75 | print('Deobfuscation success!') -------------------------------------------------------------------------------- /example/example-1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluesadi/debogus/46020b44330d1d6be0fc9e84cede9ca486e9f1e9/example/example-1 -------------------------------------------------------------------------------- /example/example-1_recovered: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluesadi/debogus/46020b44330d1d6be0fc9e84cede9ca486e9f1e9/example/example-1_recovered -------------------------------------------------------------------------------- /example/example-2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluesadi/debogus/46020b44330d1d6be0fc9e84cede9ca486e9f1e9/example/example-2 -------------------------------------------------------------------------------- /example/example-2_recovered: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluesadi/debogus/46020b44330d1d6be0fc9e84cede9ca486e9f1e9/example/example-2_recovered -------------------------------------------------------------------------------- /example/example-3.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #define LL long long 3 | 4 | 5 | 6 | int main(){ 7 | int a; 8 | int f = 1; 9 | scanf("%d", &a); 10 | for(int i = 0;i < a && i < 10;i ++){ 11 | f *= 2; 12 | } 13 | if(f == 8){ 14 | printf("%s\n", "Correct!"); 15 | }else{ 16 | printf("%s\n", "Wrong!"); 17 | } 18 | } -------------------------------------------------------------------------------- /example/example-3.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluesadi/debogus/46020b44330d1d6be0fc9e84cede9ca486e9f1e9/example/example-3.exe -------------------------------------------------------------------------------- /example/example-3_recovered.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluesadi/debogus/46020b44330d1d6be0fc9e84cede9ca486e9f1e9/example/example-3_recovered.exe -------------------------------------------------------------------------------- /test.bat: -------------------------------------------------------------------------------- 1 | python debogus.py -f example/example-1 -s 0x404350 2 | python debogus.py -f example/example-2 -s 0x401150 3 | python debogus.py -f example/example-3.exe -s 0x401410 -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | python debogus.py -f example/example-1 -s 0x404350 2 | python debogus.py -f example/example-2 -s 0x401150 3 | python debogus.py -f example/example-3.exe -s 0x401410 --------------------------------------------------------------------------------