├── .gitignore ├── D810_Extern ├── 1_show_patch_info.py ├── 1_unfla_ollvm.py ├── 2_unfla_ollvm.py ├── D810.py ├── LICENSE ├── README.md ├── UnflattenerFakeJumpDemo.py ├── UnflattenerOpt.py ├── d810 │ ├── __init__.py │ ├── ast.py │ ├── cfg_utils.py │ ├── conf │ │ ├── __init__.py │ │ ├── default_instruction_only.json │ │ ├── default_unflattening_ollvm.json │ │ ├── default_unflattening_switch_case.json │ │ ├── example_anel.json │ │ ├── example_libobfuscated.json │ │ └── options.json │ ├── docs │ │ └── source │ │ │ └── images │ │ │ └── gui_plugin_configuration.png │ ├── emulator.py │ ├── errors.py │ ├── hexrays_formatters.py │ ├── hexrays_helpers.py │ ├── hexrays_hooks.py │ ├── ida_ui.py │ ├── log.ini │ ├── log.py │ ├── manager.py │ ├── manager_info.json │ ├── optimizers │ │ ├── __init__.py │ │ ├── flow │ │ │ ├── __init__.py │ │ │ ├── flattening │ │ │ │ ├── __init__.py │ │ │ │ ├── fix_pred_cond_jump_block.py │ │ │ │ ├── generic.py │ │ │ │ ├── unflattener.py │ │ │ │ ├── unflattener_fake_jump.py │ │ │ │ ├── unflattener_indirect.py │ │ │ │ ├── unflattener_switch_case.py │ │ │ │ └── utils.py │ │ │ ├── handler.py │ │ │ └── jumps │ │ │ │ ├── __init__.py │ │ │ │ ├── handler.py │ │ │ │ ├── opaque.py │ │ │ │ └── tricks.py │ │ ├── handler.py │ │ └── instructions │ │ │ ├── __init__.py │ │ │ ├── analysis │ │ │ ├── __init__.py │ │ │ ├── handler.py │ │ │ ├── pattern_guess.py │ │ │ └── utils.py │ │ │ ├── chain │ │ │ ├── __init__.py │ │ │ ├── chain_rules.py │ │ │ └── handler.py │ │ │ ├── early │ │ │ ├── __init__.py │ │ │ ├── handler.py │ │ │ └── mem_read.py │ │ │ ├── handler.py │ │ │ ├── pattern_matching │ │ │ ├── __init__.py │ │ │ ├── experimental.py │ │ │ ├── handler.py │ │ │ ├── rewrite_add.py │ │ │ ├── rewrite_and.py │ │ │ ├── rewrite_bnot.py │ │ │ ├── rewrite_cst.py │ │ │ ├── rewrite_mov.py │ │ │ ├── rewrite_mul.py │ │ │ ├── rewrite_neg.py │ │ │ ├── rewrite_or.py │ │ │ ├── rewrite_predicates.py │ │ │ ├── rewrite_sub.py │ │ │ ├── rewrite_xor.py │ │ │ └── weird.py │ │ │ └── z3 │ │ │ ├── __init__.py │ │ │ ├── cst.py │ │ │ ├── handler.py │ │ │ └── predicates.py │ ├── tracker.py │ ├── utils.py │ └── z3_utils.py ├── dfview.py ├── dup_1.py ├── dup_hello_ollvm_split.py ├── genmy.py ├── hello_ollvm_fla │ ├── hello_ollvm_show_dup_info.py │ ├── hello_ollvm_show_patch_info.py │ ├── hello_world_patch.py │ └── un_hello_ollvm.py ├── show_ma.py ├── test.py └── un_hello_ollvm_split.py ├── D810_demo ├── DisPatchInfo.py ├── UnflattenerFakeJumpDemo.py ├── create_microcode.py ├── d810 │ ├── cfg_utils.py │ ├── emulator.py │ ├── errors.py │ ├── generic.py │ ├── hexrays_formatters.py │ ├── hexrays_helpers.py │ ├── hexrays_hooks.py │ ├── tracker.py │ └── utils.py ├── genmc.py ├── hexrays.hpp ├── ida_cfg_graph.py ├── ida_view.py ├── main.py ├── plug_menu.py ├── popemu.py ├── show_ma.py ├── trave_microcode.py ├── trave_microcode2.py ├── utils.py ├── vds15.py └── vds5.py ├── README.md ├── demo ├── 1.so ├── 2.so ├── d810_samples │ ├── README.md │ ├── binaries │ │ ├── anel.zip │ │ ├── blog_instruction_obfuscation_sub1.bin │ │ ├── blog_instruction_obfuscation_sub2.bin │ │ ├── libobfuscated.so │ │ └── qsynth_dataset_custom_ea.bin │ └── src │ │ ├── Makefile │ │ ├── blog_manual_obfuscated.c │ │ ├── blog_manual_ollvm.c │ │ └── blog_tigress_obfuscated.c ├── hello_ollvm_bcf ├── hello_ollvm_bcf2 ├── hello_ollvm_fla └── hello_ollvm_fla_split ├── doc ├── 1.md ├── D810.md ├── d810_bcf_microcode ├── d810_bcf_microcode_2 ├── explanation of terms.md ├── ida microcode Overview.md ├── ida microcode insn opcode list.md ├── ida microcode mop type.md ├── ida ud xrfe.md ├── libmsec ├── ollvm.md └── un_patch.md ├── example ├── addMicrocode.py ├── bcf_optimizer_insn.py ├── blockoptimizerDemo.py ├── blockoptimizerReturnText.py ├── call_genmy.py ├── create_insn.py ├── dfs.py ├── getMultiple_predecessors.py ├── graphviz_demo.py ├── hook.py ├── insnoptimizerDemo.py ├── miasm2.py ├── mlist.py ├── networkx_demo.py ├── optInstall.py ├── py_getitem.py ├── view_add_menu.py └── xrfe.py ├── plugs ├── z3Demo1.py └── z3_2.py └── python3 ├── ida_allins.py ├── ida_auto.py ├── ida_bitrange.py ├── ida_bytes.py ├── ida_dbg.py ├── ida_dirtree.py ├── ida_diskio.py ├── ida_entry.py ├── ida_enum.py ├── ida_expr.py ├── ida_fixup.py ├── ida_fpro.py ├── ida_frame.py ├── ida_funcs.py ├── ida_gdl.py ├── ida_graph.py ├── ida_hexrays.py ├── ida_ida.py ├── ida_idaapi.py ├── ida_idc.py ├── ida_idd.py ├── ida_idp.py ├── ida_ieee.py ├── ida_kernwin.py ├── ida_lines.py ├── ida_loader.py ├── ida_moves.py ├── ida_nalt.py ├── ida_name.py ├── ida_netnode.py ├── ida_offset.py ├── ida_pro.py ├── ida_problems.py ├── ida_range.py ├── ida_registry.py ├── ida_search.py ├── ida_segment.py ├── ida_segregs.py ├── ida_srclang.py ├── ida_strlist.py ├── ida_struct.py ├── ida_tryblks.py ├── ida_typeinf.py ├── ida_ua.py ├── ida_xref.py ├── idaapi.py ├── idadex.py ├── idautils.py ├── idc.py ├── init.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | .cxx 10 | local.properties 11 | -------------------------------------------------------------------------------- /D810_Extern/D810.py: -------------------------------------------------------------------------------- 1 | import os 2 | import idaapi 3 | import ida_hexrays 4 | import ida_kernwin 5 | 6 | 7 | from d810.conf import D810Configuration 8 | from d810.manager import D810State, D810_LOG_DIR_NAME 9 | from d810.log import configure_loggers, clear_logs 10 | 11 | 12 | D810_VERSION = "0.1" 13 | 14 | class D810Plugin(idaapi.plugin_t): 15 | # variables required by IDA 16 | flags = 0 # normal plugin 17 | wanted_name = "D-810" 18 | wanted_hotkey = "Ctrl-Shift-D" 19 | comment = "Interface to the D-810 plugin" 20 | help = "" 21 | initialized = False 22 | 23 | def __init__(self): 24 | super(D810Plugin, self).__init__() 25 | self.d810_config = None 26 | self.state = None 27 | self.initialized = False 28 | 29 | 30 | def reload_plugin(self): 31 | if self.initialized: 32 | self.term() 33 | 34 | self.d810_config = D810Configuration() 35 | 36 | #TO-DO: if [...].get raises an exception because log_dir is not found, handle exception 37 | real_log_dir = os.path.join(self.d810_config.get("log_dir"), D810_LOG_DIR_NAME) 38 | 39 | #TO-DO: if [...].get raises an exception because erase_logs_on_reload is not found, handle exception 40 | if self.d810_config.get("erase_logs_on_reload"): 41 | clear_logs(real_log_dir) 42 | 43 | configure_loggers(real_log_dir) 44 | self.state = D810State(self.d810_config) 45 | print("D-810 reloading...") 46 | self.state.start_plugin() 47 | self.initialized = True 48 | 49 | 50 | # IDA API methods: init, run, term 51 | def init(self): 52 | if not ida_hexrays.init_hexrays_plugin(): 53 | print("D-810 need Hex-Rays decompiler. Skipping") 54 | return idaapi.PLUGIN_SKIP 55 | 56 | kv = ida_kernwin.get_kernel_version().split(".") 57 | if (int(kv[0]) < 7) or ((int(kv[0]) == 7) and (int(kv[1]) < 5)): 58 | print("D-810 need IDA version >= 7.5. Skipping") 59 | return idaapi.PLUGIN_SKIP 60 | print("D-810 initialized (version {0})".format(D810_VERSION)) 61 | return idaapi.PLUGIN_OK 62 | 63 | 64 | def run(self, args): 65 | self.reload_plugin() 66 | 67 | 68 | def term(self): 69 | print("Terminating D-810...") 70 | if self.state is not None: 71 | self.state.stop_plugin() 72 | 73 | self.initialized = False 74 | 75 | 76 | def PLUGIN_ENTRY(): 77 | return D810Plugin() 78 | -------------------------------------------------------------------------------- /D810_Extern/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | ## What is D-810 4 | 5 | D-810 is an IDA Pro plugin which can be used to deobfuscate code at decompilation time by modifying IDA Pro microcode. 6 | It was designed with the following goals in mind: 7 | 8 | * It should have as least as possible impact on our standard reverse engineering workflow 9 | * Fully integrated to IDA Pro 10 | * It should be easily extensible and configurable 11 | * Fast creation of new deobfuscation rules 12 | * Configurable so that we don't have to modify the source code to use rules for a specific project 13 | * Performance impact should be reasonable 14 | * Our goal is to be transparent for the reverse engineer 15 | * But we don't care if the decompilation of a function takes 1 more second if the resulting code is much more simplier. 16 | 17 | 18 | # Installation 19 | 20 | **Only IDA v7.5 or later is supported with Python 3.7 and higher** (since we need the microcode Python API) 21 | 22 | Copy this repository in `.idapro/plugins` 23 | 24 | We recommend to install Z3 to be able to use several features of D-810: 25 | ```bash 26 | pip3 install z3-solver 27 | ``` 28 | 29 | # Using D-810 30 | 31 | * Load the plugin by using the `Ctrl-Shift-D` shortcut, you should see this configuration GUI 32 | 33 | ![](d810/docs/source/images/gui_plugin_configuration.png) 34 | 35 | * Choose or create your project configuration 36 | * If you are not sure what to do here, leave *default_instruction_only.json*. 37 | * Click on the `Start` button to enable deobfuscation 38 | * Decompile an obfuscated function, the code should be simplified (hopefully) 39 | * When you want to disable deobfuscation, just click on the `Stop` button. 40 | 41 | # Warnings 42 | 43 | This plugin is still in early stage of development, so issues ~~may~~ will happen. 44 | 45 | * Modifying incorrectly IDA microcode may lead IDA to crash. We try to detect that as much as possible to avoid crash, but since it may still happen **save you IDA database often** 46 | * We only tested this plugin on Linux, but it should work on Windows too. 47 | 48 | # Documentation 49 | 50 | Work in progress 51 | 52 | Currently, you can read our [blog post](https://eshard.com/posts/) to get some information. 53 | 54 | 55 | # Licenses 56 | 57 | This library is licensed under LGPL V3 license. See the [LICENSE](LICENSE) file for details. 58 | 59 | ## Authors 60 | 61 | See [AUTHORS](AUTHORS.md) for the list of contributors to the project. 62 | 63 | # Acknowledgement 64 | 65 | Rolf Rolles for the huge work he has done with his [HexRaysDeob plugin](https://github.com/RolfRolles/HexRaysDeob) and all the information about Hex-Rays microcode internals described in his [blog post](https://www.hex-rays.com/blog/hex-rays-microcode-api-vs-obfuscating-compiler/). We are still using some part of his plugin in D-810. 66 | 67 | Dennis Elser for the [genmc plugin](https://github.com/patois/genmc) plugin which was very helpful for debugging D-810 errors. 68 | -------------------------------------------------------------------------------- /D810_Extern/UnflattenerOpt.py: -------------------------------------------------------------------------------- 1 | import ida_hexrays 2 | import pydevd 3 | import ida_kernwin 4 | import idaapi 5 | import idc 6 | import ida_bytes 7 | import ida_idaapi 8 | from ida_hexrays import * 9 | import logging 10 | from d810.optimizers.flow.flattening.utils import NotResolvableFatherException, get_all_possibles_values 11 | 12 | FAKE_LOOP_OPCODES = [m_jz, m_jnz,m_jg] 13 | 14 | def format_minsn_t(ins: minsn_t) -> str: 15 | if ins is None: 16 | return "minsn_t is None" 17 | 18 | tmp = ins._print() 19 | pp_ins = "".join([c if 0x20 <= ord(c) <= 0x7e else "" for c in tmp]) 20 | return pp_ins 21 | 22 | 23 | 24 | class UnflattenerOpt(optblock_t): 25 | DEFAULT_UNFLATTENING_MATURITIES = [MMAT_CALLS, MMAT_GLBOPT1, MMAT_GLBOPT2] 26 | 27 | 28 | def __init__(self, *args): 29 | super().__init__(*args) 30 | self.mba = None 31 | self.cur_maturity = MMAT_ZERO 32 | self.cur_maturity_pass = 0 33 | self.last_pass_nb_patch_done = 0 34 | self.maturities = self.DEFAULT_UNFLATTENING_MATURITIES 35 | 36 | 37 | def func(self, blk): 38 | self.mba = blk.mba 39 | if not self.check_if_rule_should_be_used(blk): 40 | return 0 41 | if (blk.tail is None) or blk.tail.opcode not in FAKE_LOOP_OPCODES: 42 | return 0 43 | print(hex(blk.head.ea)) 44 | # if blk.get_reginsn_qty() != 1: 45 | # return 0 46 | # if blk.tail.r.t != mop_n: 47 | # return 0 48 | # logging.info("Checking if block {0} is fake loop: {1}".format(blk.serial, format_minsn_t(blk.tail))) 49 | op_compared = mop_t(blk.tail.l) 50 | blk_preset_list = [x for x in blk.predset] 51 | nb_change = 0 52 | for pred_serial in blk_preset_list: 53 | pred_blk = blk.mba.get_mblock(pred_serial) 54 | # pred_values = get_all_possibles_values(pred_histories, [op_compared]) 55 | 56 | logging.info("Pred {0} ".format(pred_blk.serial)) 57 | return 0 # report the number of changes 58 | 59 | def check_if_rule_should_be_used(self, blk: mblock_t) -> bool: 60 | if self.cur_maturity == self.mba.maturity: 61 | self.cur_maturity_pass += 1 62 | else: 63 | self.cur_maturity = self.mba.maturity 64 | self.cur_maturity_pass = 0 65 | if self.cur_maturity not in self.maturities: 66 | return False 67 | return True 68 | 69 | 70 | 71 | class my_plugin_t(ida_idaapi.plugin_t): 72 | flags = ida_idaapi.PLUGIN_HIDE 73 | wanted_name = "Optimize UnflattenerOpt" 74 | wanted_hotkey = "" 75 | comment = "Sample plugin of UnflattenerOpt" 76 | help = "" 77 | def init(self): 78 | if init_hexrays_plugin(): 79 | self.optimizer = UnflattenerOpt() 80 | self.optimizer.install() 81 | return ida_idaapi.PLUGIN_KEEP # keep us in the memory 82 | def term(self): 83 | self.optimizer.remove() 84 | def run(self, arg): 85 | if arg == 1: 86 | return self.optimizer.remove() 87 | elif arg == 2: 88 | return self.optimizer.install() 89 | 90 | # def PLUGIN_ENTRY(): #可以当插件用 91 | # # import pydevd_pycharm 92 | # # pydevd_pycharm.settrace('localhost', port=31235, stdoutToServer=True, stderrToServer=True) 93 | # return my_plugin_t() 94 | 95 | 96 | if __name__ == '__main__': #也可以直接在脚本里执行 97 | optimizer = UnflattenerOpt() 98 | optimizer.install() -------------------------------------------------------------------------------- /D810_Extern/d810/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thehepta/D810Learn/52ad29245037f279411a2ddd6b8b6e3f15e9bc9a/D810_Extern/d810/__init__.py -------------------------------------------------------------------------------- /D810_Extern/d810/conf/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | 4 | 5 | class D810Configuration(object): 6 | def __init__(self): 7 | self.config_dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) 8 | self.config_file = os.path.join(self.config_dir, "options.json") 9 | with open(self.config_file, "r") as fp: 10 | self._options = json.load(fp) 11 | 12 | def get(self, name): 13 | if (name == "log_dir") and (self._options[name] is None): 14 | return os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "..")) 15 | return self._options[name] 16 | 17 | def set(self, name, value): 18 | self._options[name] = value 19 | 20 | def save(self): 21 | with open(self.config_file, "w") as fp: 22 | json.dump(self._options, fp, indent=2) 23 | 24 | 25 | class RuleConfiguration(object): 26 | def __init__(self, name=None, is_activated=False, config=None): 27 | self.name = name 28 | self.is_activated = is_activated 29 | self.config = config if config is not None else {} 30 | 31 | def to_dict(self): 32 | return { 33 | "name": self.name, 34 | "is_activated": self.is_activated, 35 | "config": self.config 36 | } 37 | 38 | @staticmethod 39 | def from_dict(kwargs): 40 | return RuleConfiguration(**kwargs) 41 | 42 | 43 | class ProjectConfiguration(object): 44 | def __init__(self, path=None, description=None, ins_rules=None, blk_rules=None, conf_dir=None): 45 | self.path = path 46 | self.description = description 47 | self.conf_dir = conf_dir 48 | self.ins_rules = [] if ins_rules is None else ins_rules 49 | self.blk_rules = [] if blk_rules is None else blk_rules 50 | self.additional_configuration = {} 51 | 52 | def load(self): 53 | try: 54 | with open(self.path, "r") as fp: 55 | project_conf = json.load(fp) 56 | except FileNotFoundError as e: 57 | if self.conf_dir is not None: 58 | self.path = os.path.join(self.conf_dir, self.path) 59 | with open(self.path, "r") as fp: 60 | project_conf = json.load(fp) 61 | 62 | self.description = project_conf["description"] 63 | self.ins_rules = [RuleConfiguration.from_dict(x) for x in project_conf["ins_rules"]] 64 | self.blk_rules = [RuleConfiguration.from_dict(x) for x in project_conf["blk_rules"]] 65 | 66 | def save(self): 67 | project_conf = { 68 | "description": self.description, 69 | "ins_rules": [x.to_dict() for x in self.ins_rules], 70 | "blk_rules": [x.to_dict() for x in self.blk_rules], 71 | } 72 | with open(self.path, "w") as fp: 73 | json.dump(project_conf, fp, indent=2) 74 | -------------------------------------------------------------------------------- /D810_Extern/d810/conf/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "erase_logs_on_reload": true, 3 | "generate_z3_code": true, 4 | "dump_intermediate_microcode": true, 5 | "log_dir": null, 6 | "configurations": [ 7 | "default_instruction_only.json", 8 | "default_unflattening_ollvm.json", 9 | "default_unflattening_switch_case.json", 10 | "example_anel.json", 11 | "example_libobfuscated.json" 12 | ], 13 | "last_project_index": 0 14 | } -------------------------------------------------------------------------------- /D810_Extern/d810/docs/source/images/gui_plugin_configuration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thehepta/D810Learn/52ad29245037f279411a2ddd6b8b6e3f15e9bc9a/D810_Extern/d810/docs/source/images/gui_plugin_configuration.png -------------------------------------------------------------------------------- /D810_Extern/d810/errors.py: -------------------------------------------------------------------------------- 1 | class D810Exception(Exception): 2 | pass 3 | 4 | 5 | class AstException(D810Exception): 6 | pass 7 | 8 | 9 | class AstEvaluationException(AstException): 10 | pass 11 | 12 | 13 | class D810Z3Exception(D810Exception): 14 | pass 15 | 16 | 17 | class ControlFlowException(D810Exception): 18 | pass 19 | 20 | 21 | class EmulationException(D810Exception): 22 | pass 23 | 24 | 25 | class EmulationIndirectJumpException(EmulationException): 26 | def __init__(self, message, dest_ea, dest_serial_list): 27 | super().__init__(message) 28 | self.dest_ea = dest_ea 29 | self.dest_serial_list = dest_serial_list 30 | 31 | 32 | class UnresolvedMopException(EmulationException): 33 | pass 34 | 35 | 36 | class WritableMemoryReadException(EmulationException): 37 | pass 38 | 39 | 40 | class UnsupportedInstructionException(EmulationException): 41 | pass 42 | -------------------------------------------------------------------------------- /D810_Extern/d810/hexrays_formatters.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | from typing import List 4 | 5 | from d810.hexrays_helpers import OPCODES_INFO, MATURITY_TO_STRING_DICT, STRING_TO_MATURITY_DICT, MOP_TYPE_TO_STRING_DICT 6 | from ida_hexrays import minsn_t, mop_t, vd_printer_t, mbl_array_t 7 | 8 | 9 | logger = logging.getLogger('D810.helper') 10 | 11 | 12 | def format_minsn_t(ins: minsn_t) -> str: 13 | if ins is None: 14 | return "minsn_t is None" 15 | 16 | tmp = ins._print() 17 | pp_ins = "".join([c if 0x20 <= ord(c) <= 0x7e else "" for c in tmp]) 18 | return pp_ins 19 | 20 | 21 | def format_mop_t(mop_in: mop_t) -> str: 22 | if mop_in is None: 23 | return "mop_t is None" 24 | if mop_in.t > 15: 25 | # To avoid error 50581 26 | return "Unknown mop type {0}".format(mop_in.t) 27 | return mop_in.dstr() 28 | 29 | 30 | def format_mop_list(mop_list: List[mop_t]) -> str: 31 | return ", ".join([format_mop_t(x) for x in mop_list]) 32 | 33 | 34 | def maturity_to_string(maturity_level: int) -> str: 35 | return MATURITY_TO_STRING_DICT.get(maturity_level, "Unknown maturity: {0}".format(maturity_level)) 36 | 37 | 38 | def string_to_maturity(maturity_string: str) -> int: 39 | return STRING_TO_MATURITY_DICT.get(maturity_string) 40 | 41 | 42 | def mop_type_to_string(mop_type: int) -> str: 43 | return MOP_TYPE_TO_STRING_DICT.get(mop_type, "Unknown mop type: {0}".format(mop_type)) 44 | 45 | 46 | def opcode_to_string(opcode) -> str: 47 | try: 48 | return OPCODES_INFO[opcode]["name"] 49 | except KeyError: 50 | return "Unknown opcode: {0}".format(opcode) 51 | 52 | 53 | class mba_printer(vd_printer_t): 54 | def __init__(self): 55 | vd_printer_t.__init__(self) 56 | self.mc = [] 57 | 58 | def get_mc(self): 59 | return self.mc 60 | 61 | def _print(self, indent, line): 62 | self.mc.append("".join([c if 0x20 <= ord(c) <= 0x7e else "" for c in line])+"\n") 63 | return 1 64 | 65 | 66 | class block_printer(vd_printer_t): 67 | def __init__(self): 68 | vd_printer_t.__init__(self) 69 | self.block_ins = [] 70 | 71 | def get_block_mc(self): 72 | return "\n".join(self.block_ins) 73 | 74 | def _print(self, indent, line): 75 | self.block_ins.append("".join([c if 0x20 <= ord(c) <= 0x7e else "" for c in line])) 76 | return 1 77 | 78 | 79 | def write_mc_to_file(mba: mbl_array_t, filename: str, mba_flags: int = 0) -> bool: 80 | if not mba: 81 | return False 82 | 83 | vp = mba_printer() 84 | mba.set_mba_flags(mba_flags) 85 | mba._print(vp) 86 | 87 | with open(filename, "w") as f: 88 | f.writelines(vp.get_mc()) 89 | return True 90 | 91 | 92 | def dump_microcode_for_debug(mba: mbl_array_t, log_dir_path: str, name: str = ""): 93 | mc_filename = os.path.join(log_dir_path, "{0:x}_maturity_{1}_{2}.log".format(mba.entry_ea, mba.maturity, name)) 94 | logger.info("Dumping microcode in file {0}...".format(mc_filename)) 95 | write_mc_to_file(mba, mc_filename) 96 | -------------------------------------------------------------------------------- /D810_Extern/d810/log.ini: -------------------------------------------------------------------------------- 1 | [loggers] 2 | keys=root,D810,D810Ui,D810Optimizer,D810RulesChain,D810PatternSearch,D810BranchFixer,D810Unflat,D810Tracker,D810Emulator,D810Helper,D810Z3Test 3 | 4 | [handlers] 5 | keys=consoleHandler,defaultFileHandler,z3FileHandler 6 | 7 | [formatters] 8 | keys=defaultFormatter,rawFormatter 9 | 10 | [logger_root] 11 | level=DEBUG 12 | handlers=consoleHandler 13 | 14 | [logger_D810] 15 | level=DEBUG 16 | handlers=consoleHandler,defaultFileHandler 17 | qualname=D810 18 | propagate=0 19 | 20 | [logger_D810Ui] 21 | level=ERROR 22 | handlers=defaultFileHandler 23 | qualname=D810.ui 24 | propagate=0 25 | 26 | [logger_D810Optimizer] 27 | level=INFO 28 | handlers=defaultFileHandler 29 | qualname=D810.optimizer 30 | propagate=0 31 | 32 | [logger_D810RulesChain] 33 | level=INFO 34 | handlers=defaultFileHandler 35 | qualname=D810.chain 36 | propagate=0 37 | 38 | [logger_D810BranchFixer] 39 | level=INFO 40 | handlers=defaultFileHandler 41 | qualname=D810.branch_fixer 42 | propagate=0 43 | 44 | [logger_D810Unflat] 45 | level=INFO 46 | handlers=defaultFileHandler 47 | qualname=D810.unflat 48 | propagate=0 49 | 50 | [logger_D810Tracker] 51 | level=INFO 52 | handlers=defaultFileHandler 53 | qualname=D810.tracker 54 | propagate=0 55 | 56 | [logger_D810Emulator] 57 | level=WARNING 58 | handlers=defaultFileHandler 59 | qualname=D810.emulator 60 | propagate=0 61 | 62 | [logger_D810Helper] 63 | level=INFO 64 | handlers=defaultFileHandler 65 | qualname=D810.helper 66 | propagate=0 67 | 68 | [logger_D810PatternSearch] 69 | level=ERROR 70 | handlers=defaultFileHandler 71 | qualname=D810.pattern_search 72 | propagate=0 73 | 74 | [logger_D810Z3Test] 75 | level=INFO 76 | handlers=z3FileHandler 77 | qualname=D810.z3_test 78 | propagate=0 79 | 80 | [handler_consoleHandler] 81 | class=StreamHandler 82 | level=INFO 83 | formatter=defaultFormatter 84 | args=(sys.stdout,) 85 | 86 | [handler_defaultFileHandler] 87 | class=FileHandler 88 | level=DEBUG 89 | formatter=defaultFormatter 90 | args=('%(default_log_filename)s',) 91 | 92 | [handler_z3FileHandler] 93 | class=FileHandler 94 | level=DEBUG 95 | formatter=rawFormatter 96 | args=('%(z3_log_filename)s',) 97 | 98 | [formatter_defaultFormatter] 99 | format=%(asctime)s - %(name)s - %(levelname)s - %(message)s 100 | 101 | [formatter_rawFormatter] 102 | format=%(message)s -------------------------------------------------------------------------------- /D810_Extern/d810/log.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | import logging 4 | import logging.config 5 | from pathlib import Path 6 | 7 | LOG_CONFIG_FILENAME = "log.ini" 8 | LOG_FILENAME = "d810.log" 9 | Z3_TEST_FILENAME = "z3_check_instructions_substitution.py" 10 | 11 | 12 | def clear_logs(log_dir): 13 | shutil.rmtree(log_dir, ignore_errors=True) 14 | 15 | 16 | def configure_loggers(log_dir): 17 | os.makedirs(log_dir, exist_ok=True) 18 | log_main_file = Path(log_dir) / LOG_FILENAME 19 | z3_test_file = Path(log_dir) / Z3_TEST_FILENAME 20 | log_conf_file = Path(__file__).resolve().parent / LOG_CONFIG_FILENAME 21 | logging.config.fileConfig(log_conf_file.as_posix(), defaults={"default_log_filename": log_main_file.as_posix(), 22 | "z3_log_filename": z3_test_file.as_posix()}) 23 | z3_file_logger = logging.getLogger('D810.z3_test') 24 | z3_file_logger.info("from z3 import BitVec, BitVecVal, UDiv, URem, LShR, UGT, UGE, ULT, ULE, prove\n\n") 25 | -------------------------------------------------------------------------------- /D810_Extern/d810/manager_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "_comment": "Order of module in module list matters", 3 | "module_list": [ 4 | "d810.cfg_utils", 5 | "d810.emulator", 6 | "d810.ast", 7 | "d810.optimizers.handler", 8 | "d810.optimizers.instructions.handler", 9 | "d810.optimizers.instructions.pattern_matching.handler", 10 | "d810.optimizers.instructions.pattern_matching.rewrite_add", 11 | "d810.optimizers.instructions.pattern_matching.rewrite_and", 12 | "d810.optimizers.instructions.pattern_matching.rewrite_bnot", 13 | "d810.optimizers.instructions.pattern_matching.rewrite_cst", 14 | "d810.optimizers.instructions.pattern_matching.rewrite_mov", 15 | "d810.optimizers.instructions.pattern_matching.rewrite_mul", 16 | "d810.optimizers.instructions.pattern_matching.rewrite_neg", 17 | "d810.optimizers.instructions.pattern_matching.rewrite_or", 18 | "d810.optimizers.instructions.pattern_matching.rewrite_predicates", 19 | "d810.optimizers.instructions.pattern_matching.rewrite_sub", 20 | "d810.optimizers.instructions.pattern_matching.rewrite_xor", 21 | "d810.optimizers.instructions.pattern_matching.weird", 22 | "d810.optimizers.instructions.pattern_matching.experimental", 23 | "d810.optimizers.instructions.pattern_matching", 24 | "d810.optimizers.instructions.chain.handler", 25 | "d810.optimizers.instructions.chain.chain_rules", 26 | "d810.optimizers.instructions.chain", 27 | "d810.optimizers.instructions.z3.handler", 28 | "d810.optimizers.instructions.z3.cst", 29 | "d810.optimizers.instructions.z3.predicates", 30 | "d810.optimizers.instructions.z3", 31 | "d810.optimizers.instructions.analysis.utils", 32 | "d810.optimizers.instructions.analysis.handler", 33 | "d810.optimizers.instructions.analysis.pattern_guess", 34 | "d810.optimizers.instructions.analysis", 35 | "d810.optimizers.instructions.early.handler", 36 | "d810.optimizers.instructions.early.mem_read", 37 | "d810.optimizers.instructions.early", 38 | "d810.optimizers.instructions", 39 | "d810.optimizers.flow.handler", 40 | "d810.optimizers.flow.jumps.handler", 41 | "d810.optimizers.flow.jumps.opaque", 42 | "d810.optimizers.flow.jumps.tricks", 43 | "d810.optimizers.flow.jumps", 44 | "d810.optimizers.flow.flattening.utils", 45 | "d810.optimizers.flow.flattening.generic", 46 | "d810.optimizers.flow.flattening.unflattener", 47 | "d810.optimizers.flow.flattening.unflattener_fake_jump", 48 | "d810.optimizers.flow.flattening.unflattener_switch_case", 49 | "d810.optimizers.flow.flattening.unflattener_indirect", 50 | "d810.optimizers.flow.flattening.fix_pred_cond_jump_block", 51 | "d810.optimizers.flow.flattening", 52 | "d810.optimizers.flow", 53 | "d810.hexrays_helpers", 54 | "d810.hexrays_formatters", 55 | "d810.hexrays_hooks", 56 | "d810.ida_ui", 57 | "d810.log", 58 | "d810.tracker", 59 | "d810.utils", 60 | "d810.z3_utils" 61 | ] 62 | } 63 | -------------------------------------------------------------------------------- /D810_Extern/d810/optimizers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thehepta/D810Learn/52ad29245037f279411a2ddd6b8b6e3f15e9bc9a/D810_Extern/d810/optimizers/__init__.py -------------------------------------------------------------------------------- /D810_Extern/d810/optimizers/flow/__init__.py: -------------------------------------------------------------------------------- 1 | from d810.optimizers.flow.flattening import UNFLATTENING_BLK_RULES 2 | from d810.optimizers.flow.jumps import JUMP_OPTIMIZATION_BLOCK_RULES, JUMP_OPTIMIZATION_RULES 3 | 4 | KNOWN_BLK_RULES = UNFLATTENING_BLK_RULES + JUMP_OPTIMIZATION_BLOCK_RULES 5 | 6 | -------------------------------------------------------------------------------- /D810_Extern/d810/optimizers/flow/flattening/__init__.py: -------------------------------------------------------------------------------- 1 | from d810.optimizers.flow.flattening.unflattener import Unflattener 2 | from d810.optimizers.flow.flattening.unflattener_switch_case import UnflattenerSwitchCase 3 | from d810.optimizers.flow.flattening.unflattener_indirect import UnflattenerTigressIndirect 4 | from d810.optimizers.flow.flattening.unflattener_fake_jump import UnflattenerFakeJump 5 | from d810.optimizers.flow.flattening.fix_pred_cond_jump_block import FixPredecessorOfConditionalJumpBlock 6 | 7 | UNFLATTENING_BLK_RULES = [Unflattener(), UnflattenerSwitchCase(), UnflattenerTigressIndirect(), UnflattenerFakeJump(), 8 | FixPredecessorOfConditionalJumpBlock()] 9 | -------------------------------------------------------------------------------- /D810_Extern/d810/optimizers/flow/flattening/unflattener.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from typing import Tuple, List 3 | from ida_hexrays import * 4 | 5 | from d810.hexrays_helpers import extract_num_mop, append_mop_if_not_in_list 6 | from d810.optimizers.flow.flattening.generic import GenericDispatcherCollector, GenericDispatcherInfo, \ 7 | GenericDispatcherBlockInfo, GenericDispatcherUnflatteningRule 8 | 9 | 10 | unflat_logger = logging.getLogger('D810.unflat') 11 | FLATTENING_JUMP_OPCODES = [m_jnz, m_jz, m_jae, m_jb, m_ja, m_jbe, m_jg, m_jge, m_jl, m_jle] 12 | 13 | 14 | class OllvmDispatcherBlockInfo(GenericDispatcherBlockInfo): 15 | pass 16 | 17 | 18 | class OllvmDispatcherInfo(GenericDispatcherInfo): 19 | def explore(self, blk: mblock_t) -> bool: 20 | self.reset() 21 | if not self._is_candidate_for_dispatcher_entry_block(blk): 22 | return False 23 | self.entry_block = OllvmDispatcherBlockInfo(blk) 24 | self.entry_block.parse() 25 | for used_mop in self.entry_block.use_list: 26 | append_mop_if_not_in_list(used_mop, self.entry_block.assume_def_list) 27 | self.dispatcher_internal_blocks.append(self.entry_block) 28 | num_mop, self.mop_compared = self._get_comparison_info(self.entry_block.blk) 29 | self.comparison_values.append(num_mop.nnn.value) 30 | self._explore_children(self.entry_block) 31 | dispatcher_blk_with_external_father = self._get_dispatcher_blocks_with_external_father() 32 | # TODO: I think this can be wrong because we are too permissive in detection of dispatcher blocks 33 | if len(dispatcher_blk_with_external_father) != 0: 34 | return False 35 | return True 36 | 37 | def _is_candidate_for_dispatcher_entry_block(self, blk: mblock_t) -> bool: 38 | # blk must be a condition branch with one numerical operand 39 | num_mop, mop_compared = self._get_comparison_info(blk) 40 | if (num_mop is None) or (mop_compared is None): 41 | return False 42 | # Its fathers are not conditional branch with this mop 43 | for father_serial in blk.predset: 44 | father_blk = self.mba.get_mblock(father_serial) 45 | father_num_mop, father_mop_compared = self._get_comparison_info(father_blk) 46 | if (father_num_mop is not None) and (father_mop_compared is not None): 47 | if mop_compared.equal_mops(father_mop_compared, EQ_IGNSIZE): 48 | return False 49 | return True 50 | 51 | def _get_comparison_info(self, blk: mblock_t) -> Tuple[mop_t, mop_t]: 52 | # We check if blk is a good candidate for dispatcher entry block: blk.tail must be a conditional branch 53 | if (blk.tail is None) or (blk.tail.opcode not in FLATTENING_JUMP_OPCODES): 54 | return None, None 55 | # One operand must be numerical 56 | num_mop, mop_compared = extract_num_mop(blk.tail) 57 | if num_mop is None or mop_compared is None: 58 | return None, None 59 | return num_mop, mop_compared 60 | 61 | def is_part_of_dispatcher(self, block_info: OllvmDispatcherBlockInfo) -> bool: 62 | is_ok = block_info.does_only_need(block_info.father.assume_def_list) 63 | if not is_ok: 64 | return False 65 | if (block_info.blk.tail is not None) and (block_info.blk.tail.opcode not in FLATTENING_JUMP_OPCODES): 66 | return False 67 | return True 68 | 69 | def _explore_children(self, father_info: OllvmDispatcherBlockInfo): 70 | for child_serial in father_info.blk.succset: 71 | if child_serial in [blk_info.blk.serial for blk_info in self.dispatcher_internal_blocks]: 72 | return 73 | if child_serial in [blk_info.blk.serial for blk_info in self.dispatcher_exit_blocks]: 74 | return 75 | child_blk = self.mba.get_mblock(child_serial) 76 | child_info = OllvmDispatcherBlockInfo(child_blk, father_info) 77 | child_info.parse() 78 | if not self.is_part_of_dispatcher(child_info): 79 | self.dispatcher_exit_blocks.append(child_info) 80 | else: 81 | self.dispatcher_internal_blocks.append(child_info) 82 | if child_info.comparison_value is not None: 83 | self.comparison_values.append(child_info.comparison_value) 84 | self._explore_children(child_info) 85 | 86 | def _get_external_fathers(self, block_info: OllvmDispatcherBlockInfo) -> List[mblock_t]: 87 | internal_serials = [blk_info.blk.serial for blk_info in self.dispatcher_internal_blocks] 88 | external_fathers = [] 89 | for blk_father in block_info.blk.predset: 90 | if blk_father not in internal_serials: 91 | external_fathers.append(blk_father) 92 | return external_fathers 93 | 94 | def _get_dispatcher_blocks_with_external_father(self) -> List[mblock_t]: 95 | dispatcher_blocks_with_external_father = [] 96 | for blk_info in self.dispatcher_internal_blocks: 97 | if blk_info.blk.serial != self.entry_block.blk.serial: 98 | external_fathers = self._get_external_fathers(blk_info) 99 | if len(external_fathers) > 0: 100 | dispatcher_blocks_with_external_father.append(blk_info) 101 | return dispatcher_blocks_with_external_father 102 | 103 | 104 | class OllvmDispatcherCollector(GenericDispatcherCollector): 105 | DISPATCHER_CLASS = OllvmDispatcherInfo 106 | DEFAULT_DISPATCHER_MIN_INTERNAL_BLOCK = 2 107 | DEFAULT_DISPATCHER_MIN_EXIT_BLOCK = 3 108 | DEFAULT_DISPATCHER_MIN_COMPARISON_VALUE = 2 109 | 110 | 111 | class Unflattener(GenericDispatcherUnflatteningRule): 112 | DESCRIPTION = "Remove control flow flattening generated by OLLVM" 113 | DISPATCHER_COLLECTOR_CLASS = OllvmDispatcherCollector 114 | DEFAULT_UNFLATTENING_MATURITIES = [MMAT_CALLS, MMAT_GLBOPT1, MMAT_GLBOPT2] 115 | DEFAULT_MAX_DUPLICATION_PASSES = 20 116 | DEFAULT_MAX_PASSES = 5 117 | -------------------------------------------------------------------------------- /D810_Extern/d810/optimizers/flow/flattening/unflattener_fake_jump.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from typing import List 3 | from ida_hexrays import * 4 | 5 | from d810.tracker import MopTracker 6 | from d810.cfg_utils import change_1way_block_successor 7 | from d810.hexrays_formatters import format_minsn_t, dump_microcode_for_debug 8 | from d810.optimizers.flow.flattening.utils import get_all_possibles_values 9 | from d810.optimizers.flow.flattening.generic import GenericUnflatteningRule 10 | 11 | unflat_logger = logging.getLogger('D810.unflat') 12 | 13 | FAKE_LOOP_OPCODES = [m_jz, m_jnz] 14 | 15 | 16 | class UnflattenerFakeJump(GenericUnflatteningRule): 17 | DESCRIPTION = "Check if a jump is always taken for each father blocks and remove them" 18 | DEFAULT_UNFLATTENING_MATURITIES = [MMAT_CALLS, MMAT_GLBOPT1] 19 | DEFAULT_MAX_PASSES = None 20 | 21 | def analyze_blk(self, blk: mblock_t) -> int: 22 | if (blk.tail is None) or blk.tail.opcode not in FAKE_LOOP_OPCODES: 23 | return 0 24 | if blk.get_reginsn_qty() != 1: 25 | return 0 26 | if blk.tail.r.t != mop_n: 27 | return 0 28 | unflat_logger.info("Checking if block {0} is fake loop: {1}".format(blk.serial, format_minsn_t(blk.tail))) 29 | op_compared = mop_t(blk.tail.l) 30 | blk_preset_list = [x for x in blk.predset] 31 | nb_change = 0 32 | for pred_serial in blk_preset_list: 33 | cmp_variable_tracker = MopTracker([op_compared], max_nb_block=100, max_path=1000) 34 | cmp_variable_tracker.reset() 35 | pred_blk = blk.mba.get_mblock(pred_serial) 36 | pred_histories = cmp_variable_tracker.search_backward(pred_blk, pred_blk.tail) 37 | 38 | father_is_resolvable = all([father_history.is_resolved() for father_history in pred_histories]) 39 | if not father_is_resolvable: 40 | return 0 41 | pred_values = get_all_possibles_values(pred_histories, [op_compared]) 42 | pred_values = [x[0] for x in pred_values] 43 | if None in pred_values: 44 | unflat_logger.info("Some path are not resolved, can't fix jump") 45 | return 0 46 | unflat_logger.info("Pred {0} has {1} possible path ({2} different cst): {3}" 47 | .format(pred_blk.serial, len(pred_values), len(set(pred_values)), pred_values)) 48 | if self.fix_successor(blk, pred_blk, pred_values): 49 | nb_change += 1 50 | return nb_change 51 | 52 | def fix_successor(self, fake_loop_block: mblock_t, pred: mblock_t, pred_comparison_values: List[int]) -> bool: 53 | if len(pred_comparison_values) == 0: 54 | return False 55 | jmp_ins = fake_loop_block.tail 56 | compared_value = jmp_ins.r.nnn.value 57 | jmp_taken = False 58 | jmp_not_taken = False 59 | dst_serial = None 60 | if jmp_ins.opcode == m_jz: 61 | jmp_taken = all([possible_value == compared_value for possible_value in pred_comparison_values]) 62 | 63 | jmp_not_taken = all([possible_value != compared_value for possible_value in pred_comparison_values]) 64 | elif jmp_ins.opcode == m_jnz: 65 | jmp_taken = all([possible_value != compared_value for possible_value in pred_comparison_values]) 66 | jmp_not_taken = all([possible_value == compared_value for possible_value in pred_comparison_values]) 67 | # TODO: handles other jumps cases 68 | if jmp_taken: 69 | unflat_logger.info("It seems that '{0}' is always taken when coming from {1}: {2}" 70 | .format(format_minsn_t(jmp_ins), pred.serial, pred_comparison_values)) 71 | dst_serial = jmp_ins.d.b 72 | if jmp_not_taken: 73 | unflat_logger.info("It seems that '{0}' is never taken when coming from {1}: {2}" 74 | .format(format_minsn_t(jmp_ins), pred.serial, pred_comparison_values)) 75 | dst_serial = fake_loop_block.serial + 1 76 | if dst_serial is None: 77 | unflat_logger.debug("Jump seems legit '{0}' from {1}: {2}" 78 | .format(format_minsn_t(jmp_ins), pred.serial, pred_comparison_values)) 79 | return False 80 | dump_microcode_for_debug(self.mba, self.log_dir, "{0}_before_fake_jump".format(self.cur_maturity_pass)) 81 | unflat_logger.info("Making pred {0} with value {1} goto {2} ({3})" 82 | .format(pred.serial, pred_comparison_values, dst_serial, format_minsn_t(jmp_ins))) 83 | dump_microcode_for_debug(self.mba, self.log_dir, "{0}_after_fake_jump".format(self.cur_maturity_pass)) 84 | return change_1way_block_successor(pred, dst_serial) 85 | 86 | def optimize(self, blk: mblock_t) -> int: 87 | self.mba = blk.mba 88 | if not self.check_if_rule_should_be_used(blk): 89 | return 0 90 | self.last_pass_nb_patch_done = self.analyze_blk(blk) 91 | if self.last_pass_nb_patch_done > 0: 92 | self.mba.mark_chains_dirty() 93 | self.mba.optimize_local(0) 94 | self.mba.verify(True) 95 | return self.last_pass_nb_patch_done 96 | -------------------------------------------------------------------------------- /D810_Extern/d810/optimizers/flow/flattening/unflattener_indirect.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import idaapi 3 | from typing import List 4 | from ida_hexrays import * 5 | 6 | from d810.hexrays_helpers import append_mop_if_not_in_list, AND_TABLE, CONTROL_FLOW_OPCODES 7 | from d810.tracker import MopTracker, MopHistory 8 | from d810.optimizers.flow.flattening.generic import GenericDispatcherBlockInfo, GenericDispatcherInfo, \ 9 | GenericDispatcherCollector, GenericDispatcherUnflatteningRule, NotDuplicableFatherException, DispatcherUnflatteningException, NotResolvableFatherException 10 | from d810.optimizers.flow.flattening.utils import configure_mop_tracker_log_verbosity, restore_mop_tracker_log_verbosity 11 | from d810.tracker import duplicate_histories 12 | from d810.cfg_utils import create_block, change_1way_block_successor 13 | from d810.hexrays_formatters import format_minsn_t, format_mop_t 14 | from d810.emulator import MicroCodeEnvironment, MicroCodeInterpreter 15 | 16 | unflat_logger = logging.getLogger('D810.unflat') 17 | FLATTENING_JUMP_OPCODES = [m_jtbl] 18 | 19 | 20 | class TigressIndirectDispatcherBlockInfo(GenericDispatcherBlockInfo): 21 | pass 22 | 23 | 24 | class TigressIndirectDispatcherInfo(GenericDispatcherInfo): 25 | def explore(self, blk: mblock_t): 26 | self.reset() 27 | if not self._is_candidate_for_dispatcher_entry_block(blk): 28 | return False 29 | self.mop_compared = self._get_comparison_info(blk) 30 | self.entry_block = TigressIndirectDispatcherBlockInfo(blk) 31 | self.entry_block.parse() 32 | for used_mop in self.entry_block.use_list: 33 | append_mop_if_not_in_list(used_mop, self.entry_block.assume_def_list) 34 | self.dispatcher_internal_blocks.append(self.entry_block) 35 | 36 | self.dispatcher_exit_blocks = [] 37 | self.comparison_values = [] 38 | return True 39 | 40 | def _get_comparison_info(self, blk: mblock_t): 41 | if (blk.tail is None) or (blk.tail.opcode != m_ijmp): 42 | return None, None 43 | return blk.tail.l 44 | 45 | def _is_candidate_for_dispatcher_entry_block(self, blk: mblock_t): 46 | if (blk.tail is None) or (blk.tail.opcode != m_ijmp): 47 | return False 48 | return True 49 | 50 | def should_emulation_continue(self, cur_blk: mblock_t): 51 | if (cur_blk is not None) and (cur_blk.serial == self.entry_block.serial): 52 | return True 53 | return False 54 | 55 | 56 | class TigressIndirectDispatcherCollector(GenericDispatcherCollector): 57 | DISPATCHER_CLASS = TigressIndirectDispatcherInfo 58 | DEFAULT_DISPATCHER_MIN_INTERNAL_BLOCK = 0 59 | DEFAULT_DISPATCHER_MIN_EXIT_BLOCK = 0 60 | DEFAULT_DISPATCHER_MIN_COMPARISON_VALUE = 0 61 | 62 | 63 | class LabelTableInfo(object): 64 | def __init__(self, sp_offset, mem_offset, nb_elt, ptr_size=8): 65 | self.sp_offset = sp_offset 66 | self.mem_offset = mem_offset 67 | self.nb_elt = nb_elt 68 | self.ptr_size = ptr_size 69 | 70 | def update_mop_tracker(self, mba: mbl_array_t, mop_tracker: MopTracker): 71 | stack_array_base_address = mba.stkoff_ida2vd(self.sp_offset) 72 | for i in range(self.nb_elt): 73 | tmp_mop = mop_t() 74 | tmp_mop.erase() 75 | tmp_mop._make_stkvar(mba, stack_array_base_address + self.ptr_size * i) 76 | tmp_mop.size = self.ptr_size 77 | mem_val = idaapi.get_qword(self.mem_offset + self.ptr_size * i) & AND_TABLE[self.ptr_size] 78 | mop_tracker.add_mop_definition(tmp_mop, mem_val) 79 | 80 | 81 | class UnflattenerTigressIndirect(GenericDispatcherUnflatteningRule): 82 | DESCRIPTION = "" 83 | DISPATCHER_COLLECTOR_CLASS = TigressIndirectDispatcherCollector 84 | DEFAULT_UNFLATTENING_MATURITIES = [MMAT_LOCOPT] 85 | DEFAULT_MAX_DUPLICATION_PASSES = 20 86 | DEFAULT_MAX_PASSES = 1 87 | 88 | def __init__(self): 89 | super().__init__() 90 | self.label_info = None 91 | self.goto_table_info = {} 92 | 93 | def configure(self, kwargs): 94 | super().configure(kwargs) 95 | if "goto_table_info" in self.config.keys(): 96 | for ea_str, table_info in self.config["goto_table_info"].items(): 97 | self.goto_table_info[int(ea_str, 16)] = LabelTableInfo(sp_offset=int(table_info["stack_table_offset"], 16), 98 | mem_offset=int(table_info["table_address"], 16), 99 | nb_elt=table_info["table_nb_elt"]) 100 | 101 | def check_if_rule_should_be_used(self, blk: mblock_t): 102 | if not super().check_if_rule_should_be_used(blk): 103 | return False 104 | if self.mba.entry_ea not in self.goto_table_info: 105 | return False 106 | if (self.cur_maturity_pass >= 1) and (self.last_pass_nb_patch_done == 0): 107 | return False 108 | self.label_info = self.goto_table_info[self.mba.entry_ea] 109 | return True 110 | 111 | def register_initialization_variables(self, mop_tracker: MopTracker): 112 | self.label_info.update_mop_tracker(self.mba, mop_tracker) 113 | 114 | def check_if_histories_are_resolved(self, mop_histories: List[MopHistory]): 115 | return True 116 | -------------------------------------------------------------------------------- /D810_Extern/d810/optimizers/flow/flattening/unflattener_switch_case.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from ida_hexrays import * 3 | 4 | from d810.hexrays_helpers import append_mop_if_not_in_list 5 | from d810.optimizers.flow.flattening.generic import GenericDispatcherBlockInfo, GenericDispatcherInfo, \ 6 | GenericDispatcherCollector, GenericDispatcherUnflatteningRule 7 | 8 | 9 | unflat_logger = logging.getLogger('D810.unflat') 10 | FLATTENING_JUMP_OPCODES = [m_jtbl] 11 | 12 | 13 | class TigressSwitchDispatcherBlockInfo(GenericDispatcherBlockInfo): 14 | pass 15 | 16 | 17 | class TigressSwitchDispatcherInfo(GenericDispatcherInfo): 18 | def explore(self, blk: mblock_t): 19 | self.reset() 20 | if not self._is_candidate_for_dispatcher_entry_block(blk): 21 | return False 22 | self.mop_compared, mcases = self._get_comparison_info(blk) 23 | self.entry_block = TigressSwitchDispatcherBlockInfo(blk) 24 | self.entry_block.parse() 25 | for used_mop in self.entry_block.use_list: 26 | append_mop_if_not_in_list(used_mop, self.entry_block.assume_def_list) 27 | self.dispatcher_internal_blocks.append(self.entry_block) 28 | for possible_values, target_block_serial in zip(mcases.c.values, mcases.c.targets): 29 | if target_block_serial == self.entry_block.blk.serial: 30 | continue 31 | exit_block = TigressSwitchDispatcherBlockInfo(blk.mba.get_mblock(target_block_serial), self.entry_block) 32 | self.dispatcher_exit_blocks.append(exit_block) 33 | if len(possible_values) == 0: 34 | continue 35 | self.comparison_values.append(possible_values[0]) 36 | return True 37 | 38 | def _get_comparison_info(self, blk: mblock_t): 39 | # blk.tail must be a jtbl 40 | if (blk.tail is None) or (blk.tail.opcode != m_jtbl): 41 | return None, None 42 | return blk.tail.l, blk.tail.r 43 | 44 | def _is_candidate_for_dispatcher_entry_block(self, blk: mblock_t): 45 | if (blk.tail is None) or (blk.tail.opcode != m_jtbl): 46 | return False 47 | return True 48 | 49 | 50 | class TigressSwitchDispatcherCollector(GenericDispatcherCollector): 51 | DISPATCHER_CLASS = TigressSwitchDispatcherInfo 52 | DEFAULT_DISPATCHER_MIN_INTERNAL_BLOCK = 0 53 | DEFAULT_DISPATCHER_MIN_EXIT_BLOCK = 4 54 | DEFAULT_DISPATCHER_MIN_COMPARISON_VALUE = 4 55 | 56 | 57 | class UnflattenerSwitchCase(GenericDispatcherUnflatteningRule): 58 | DESCRIPTION = "Remove control flow flattening generated by Tigress with Switch case dispatcher" 59 | DISPATCHER_COLLECTOR_CLASS = TigressSwitchDispatcherCollector 60 | DEFAULT_UNFLATTENING_MATURITIES = [MMAT_GLBOPT1] 61 | DEFAULT_MAX_DUPLICATION_PASSES = 20 62 | DEFAULT_MAX_PASSES = 5 63 | -------------------------------------------------------------------------------- /D810_Extern/d810/optimizers/flow/flattening/utils.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | 4 | tracker_logger = logging.getLogger('D810.tracker') 5 | emulator_logger = logging.getLogger('D810.emulator') 6 | 7 | 8 | class UnflatteningException(Exception): 9 | pass 10 | 11 | 12 | class DispatcherUnflatteningException(UnflatteningException): 13 | pass 14 | 15 | 16 | class NotDuplicableFatherException(UnflatteningException): 17 | pass 18 | 19 | 20 | class NotResolvableFatherException(UnflatteningException): 21 | pass 22 | 23 | 24 | 25 | 26 | def configure_mop_tracker_log_verbosity(verbose=False): 27 | tracker_log_level = tracker_logger.getEffectiveLevel() 28 | emulator_log_level = emulator_logger.getEffectiveLevel() 29 | if not verbose: 30 | tracker_logger.setLevel(logging.ERROR) 31 | emulator_logger.setLevel(logging.ERROR) 32 | return [tracker_log_level, emulator_log_level] 33 | 34 | 35 | def restore_mop_tracker_log_verbosity(tracker_log_level, emulator_log_level): 36 | tracker_logger.setLevel(tracker_log_level) 37 | emulator_logger.setLevel(emulator_log_level) 38 | 39 | 40 | def get_all_possibles_values(mop_histories, searched_mop_list, verbose=False): 41 | log_levels = configure_mop_tracker_log_verbosity(verbose) 42 | mop_cst_values_list = [] 43 | for mop_history in mop_histories: 44 | mop_cst_values_list.append([mop_history.get_mop_constant_value(searched_mop) 45 | for searched_mop in searched_mop_list]) 46 | restore_mop_tracker_log_verbosity(*log_levels) 47 | return mop_cst_values_list 48 | 49 | 50 | def check_if_all_values_are_found(mop_cst_values_list): 51 | all_values_are_found = True 52 | for cst_list in mop_cst_values_list: 53 | if None in cst_list: 54 | all_values_are_found = False 55 | break 56 | return all_values_are_found 57 | -------------------------------------------------------------------------------- /D810_Extern/d810/optimizers/flow/handler.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import idc 3 | 4 | from d810.optimizers.handler import OptimizationRule, DEFAULT_FLOW_MATURITIES 5 | 6 | logger = logging.getLogger('D810.optimizer') 7 | 8 | 9 | class FlowOptimizationRule(OptimizationRule): 10 | def __init__(self): 11 | super().__init__() 12 | self.maturities = DEFAULT_FLOW_MATURITIES 13 | self.use_whitelist = False 14 | self.whitelisted_function_ea_list = [] 15 | self.use_blacklist = False 16 | self.blacklisted_function_ea_list = [] 17 | 18 | def configure(self, kwargs): 19 | super().configure(kwargs) 20 | self.use_whitelist = False 21 | self.whitelisted_function_ea_list = [] 22 | self.use_blacklist = False 23 | self.blacklisted_function_ea_list = [] 24 | if "whitelisted_functions" in self.config.keys(): 25 | self.use_whitelist = True 26 | for func_ea in self.config["whitelisted_functions"]: 27 | self.whitelisted_function_ea_list.append(int(func_ea, 16)) 28 | func_name_list = [idc.get_func_name(ea) for ea in self.whitelisted_function_ea_list] 29 | logger.info("Whitelisted functions for {0}: {1} -> {2}".format(self.__class__.__name__, 30 | self.whitelisted_function_ea_list, 31 | func_name_list)) 32 | if "blacklisted_functions" in self.config.keys(): 33 | self.use_blacklist = True 34 | for func_ea in self.config["whitelisted_functions"]: 35 | self.blacklisted_function_ea_list.append(int(func_ea, 16)) 36 | func_name_list = [idc.get_func_name(ea) for ea in self.blacklisted_function_ea_list] 37 | logger.info("Blacklisted functions for {0}: {1} -> {2}".format(self.__class__.__name__, 38 | self.blacklisted_function_ea_list, 39 | func_name_list)) 40 | -------------------------------------------------------------------------------- /D810_Extern/d810/optimizers/flow/jumps/__init__.py: -------------------------------------------------------------------------------- 1 | from d810.utils import get_all_subclasses 2 | from d810.optimizers.flow.jumps.handler import JumpOptimizationRule, JumpFixer 3 | from d810.optimizers.flow.jumps.opaque import * 4 | from d810.optimizers.flow.jumps.tricks import * 5 | 6 | 7 | JUMP_OPTIMIZATION_RULES = [x() for x in get_all_subclasses(JumpOptimizationRule)] 8 | jump_fixer = JumpFixer() 9 | for jump_optimization_rule in JUMP_OPTIMIZATION_RULES: 10 | jump_fixer.register_rule(jump_optimization_rule) 11 | JUMP_OPTIMIZATION_BLOCK_RULES = [jump_fixer] 12 | -------------------------------------------------------------------------------- /D810_Extern/d810/optimizers/flow/jumps/tricks.py: -------------------------------------------------------------------------------- 1 | from ida_hexrays import * 2 | 3 | from d810.ast import AstLeaf, AstConstant, AstNode 4 | from d810.hexrays_helpers import equal_mops_bypass_xdu, equal_bnot_mop 5 | from d810.optimizers.flow.jumps.handler import JumpOptimizationRule 6 | 7 | 8 | class CompareConstantRule1(JumpOptimizationRule): 9 | ORIGINAL_JUMP_OPCODES = [m_jge] 10 | LEFT_PATTERN = AstNode(m_and, 11 | AstNode(m_or, AstLeaf("xdu_x_0"), AstConstant("c_2")), 12 | AstNode(m_or, 13 | AstNode(m_xor, AstLeaf("x_0"), AstConstant("c_1")), 14 | AstNode(m_bnot, AstNode(m_sub, AstLeaf("x_0"), AstConstant("c_1"))))) 15 | RIGHT_PATTERN = AstConstant("0", 0) 16 | 17 | REPLACEMENT_OPCODE = m_jl 18 | REPLACEMENT_LEFT_PATTERN = AstLeaf("x_0") 19 | REPLACEMENT_RIGHT_PATTERN = AstLeaf("c_1") 20 | 21 | def check_candidate(self, opcode, left_candidate, right_candidate): 22 | if not equal_mops_bypass_xdu(left_candidate["xdu_x_0"].mop, left_candidate["x_0"].mop): 23 | return False 24 | if not equal_bnot_mop(left_candidate["c_2"].mop, left_candidate["c_1"].mop): 25 | return False 26 | self.jump_replacement_block_serial = self.jump_original_block_serial 27 | return True 28 | 29 | 30 | class CompareConstantRule2(JumpOptimizationRule): 31 | ORIGINAL_JUMP_OPCODES = [m_jge] 32 | LEFT_PATTERN = AstNode(m_or, 33 | AstNode(m_xdu, 34 | AstNode(m_and, 35 | AstNode(m_bnot, AstLeaf("x_0")), AstConstant("c_1"))), 36 | AstNode(m_and, 37 | AstNode(m_sub, AstLeaf('xdu_x_0'), AstConstant('xdu_c_1')), 38 | AstNode(m_bnot, AstNode(m_xdu, AstNode(m_xor, AstLeaf('xdu1_x_0'), AstConstant('xdu_c_1')))))) 39 | RIGHT_PATTERN = AstConstant("0", 0) 40 | 41 | REPLACEMENT_OPCODE = m_jge 42 | REPLACEMENT_LEFT_PATTERN = AstLeaf("x_0") 43 | REPLACEMENT_RIGHT_PATTERN = AstLeaf("c_1") 44 | 45 | def check_candidate(self, opcode, left_candidate, right_candidate): 46 | if not equal_mops_bypass_xdu(left_candidate["xdu_x_0"].mop, left_candidate["x_0"].mop): 47 | return False 48 | if not equal_mops_bypass_xdu(left_candidate["xdu1_x_0"].mop, left_candidate["x_0"].mop): 49 | return False 50 | self.jump_replacement_block_serial = self.jump_original_block_serial 51 | return True 52 | 53 | 54 | class CompareConstantRule3(JumpOptimizationRule): 55 | ORIGINAL_JUMP_OPCODES = [m_jge] 56 | LEFT_PATTERN = AstNode(m_and, 57 | AstNode(m_sub, AstLeaf('x_0'), AstConstant('c_1')), 58 | AstNode(m_bnot, AstLeaf("x_0"))) 59 | RIGHT_PATTERN = AstConstant("0", 0) 60 | 61 | REPLACEMENT_OPCODE = m_jg 62 | REPLACEMENT_LEFT_PATTERN = AstLeaf("x_0") 63 | REPLACEMENT_RIGHT_PATTERN = AstLeaf("c_1") 64 | 65 | def check_candidate(self, opcode, left_candidate, right_candidate): 66 | self.jump_replacement_block_serial = self.jump_original_block_serial 67 | return True 68 | 69 | 70 | class CompareConstantRule4(JumpOptimizationRule): 71 | ORIGINAL_JUMP_OPCODES = [m_jl, m_jge] 72 | LEFT_PATTERN = AstNode(m_and, 73 | AstNode(m_or, 74 | AstNode(m_bnot, 75 | AstNode(m_sub, 76 | AstLeaf('x_0'), 77 | AstConstant('c_1'))), 78 | AstNode(m_xor, 79 | AstLeaf('x_0'), 80 | AstConstant('c_1'))), 81 | AstNode(m_or, 82 | AstLeaf("xdu_x_0"), 83 | AstConstant('bnot_c_1'))) 84 | 85 | RIGHT_PATTERN = AstConstant("0", 0) 86 | 87 | REPLACEMENT_OPCODE = m_jge 88 | REPLACEMENT_LEFT_PATTERN = AstLeaf("x_0") 89 | REPLACEMENT_RIGHT_PATTERN = AstLeaf("c_1") 90 | 91 | def check_candidate(self, opcode, left_candidate, right_candidate): 92 | print("dflighdrth") 93 | if not equal_mops_bypass_xdu(left_candidate["xdu_x_0"].mop, left_candidate["x_0"].mop): 94 | return False 95 | if not equal_bnot_mop(left_candidate["c_1"].mop, left_candidate["bnot_c_1"].mop): 96 | return False 97 | self.jump_replacement_block_serial = self.jump_original_block_serial 98 | return True 99 | -------------------------------------------------------------------------------- /D810_Extern/d810/optimizers/handler.py: -------------------------------------------------------------------------------- 1 | from ida_hexrays import * 2 | 3 | from d810.hexrays_formatters import string_to_maturity 4 | 5 | DEFAULT_INSTRUCTION_MATURITIES = [MMAT_LOCOPT, MMAT_CALLS, MMAT_GLBOPT1] 6 | DEFAULT_FLOW_MATURITIES = [MMAT_CALLS, MMAT_GLBOPT1] 7 | 8 | 9 | class OptimizationRule(object): 10 | NAME = None 11 | DESCRIPTION = None 12 | 13 | def __init__(self): 14 | self.maturities = [] 15 | self.config = {} 16 | self.log_dir = None 17 | 18 | def set_log_dir(self, log_dir): 19 | self.log_dir = log_dir 20 | 21 | def configure(self, kwargs): 22 | self.config = kwargs if kwargs is not None else {} 23 | if "maturities" in self.config.keys(): 24 | self.maturities = [string_to_maturity(x) for x in self.config["maturities"]] 25 | 26 | @property 27 | def name(self): 28 | if self.NAME is not None: 29 | return self.NAME 30 | return self.__class__.__name__ 31 | 32 | @property 33 | def description(self): 34 | if self.DESCRIPTION is not None: 35 | return self.DESCRIPTION 36 | return "No description available" 37 | -------------------------------------------------------------------------------- /D810_Extern/d810/optimizers/instructions/__init__.py: -------------------------------------------------------------------------------- 1 | from d810.optimizers.instructions.chain import CHAIN_RULES, ChainOptimizer 2 | from d810.optimizers.instructions.pattern_matching import PATTERN_MATCHING_RULES, PatternOptimizer 3 | from d810.optimizers.instructions.z3 import Z3_RULES, Z3Optimizer 4 | from d810.optimizers.instructions.analysis import INSTRUCTION_ANALYSIS_RULES, InstructionAnalyzer 5 | from d810.optimizers.instructions.early import EARLY_RULES, EarlyOptimizer 6 | 7 | KNOWN_INS_RULES = PATTERN_MATCHING_RULES + CHAIN_RULES + Z3_RULES + EARLY_RULES + INSTRUCTION_ANALYSIS_RULES 8 | -------------------------------------------------------------------------------- /D810_Extern/d810/optimizers/instructions/analysis/__init__.py: -------------------------------------------------------------------------------- 1 | from d810.utils import get_all_subclasses 2 | from d810.optimizers.instructions.analysis.handler import InstructionAnalyzer, InstructionAnalysisRule 3 | from d810.optimizers.instructions.analysis.pattern_guess import * 4 | 5 | INSTRUCTION_ANALYSIS_RULES = CHAIN_RULES = [x() for x in get_all_subclasses(InstructionAnalysisRule)] 6 | -------------------------------------------------------------------------------- /D810_Extern/d810/optimizers/instructions/analysis/handler.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from ida_hexrays import * 3 | from d810.hexrays_formatters import format_minsn_t 4 | from d810.optimizers.instructions.handler import InstructionOptimizer, InstructionOptimizationRule 5 | 6 | 7 | optimizer_logger = logging.getLogger('D810.optimizer') 8 | 9 | 10 | class InstructionAnalysisRule(InstructionOptimizationRule): 11 | def analyze_instruction(self, blk, ins): 12 | raise NotImplementedError 13 | 14 | 15 | class InstructionAnalyzer(InstructionOptimizer): 16 | RULE_CLASSES = [InstructionAnalysisRule] 17 | 18 | def set_maturity(self, maturity: int): 19 | self.cur_maturity = maturity 20 | for rule in self.rules: 21 | rule.set_maturity(self.cur_maturity) 22 | 23 | def analyze(self, blk: mblock_t, ins: minsn_t): 24 | if blk is not None: 25 | self.cur_maturity = blk.mba.maturity 26 | 27 | if self.cur_maturity not in self.maturities: 28 | return None 29 | 30 | for rule in self.rules: 31 | try: 32 | rule.analyze_instruction(blk, ins) 33 | except RuntimeError: 34 | optimizer_logger.error("error during rule {0} for instruction {1}".format(rule, format_minsn_t(ins))) 35 | return None 36 | 37 | 38 | @property 39 | def name(self): 40 | if self.NAME is not None: 41 | return self.NAME 42 | return self.__class__.__name__ 43 | -------------------------------------------------------------------------------- /D810_Extern/d810/optimizers/instructions/analysis/pattern_guess.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from d810.ast import minsn_to_ast 4 | from d810.hexrays_formatters import format_minsn_t, format_mop_t, maturity_to_string 5 | 6 | from d810.optimizers.handler import DEFAULT_INSTRUCTION_MATURITIES 7 | from d810.optimizers.instructions.analysis.handler import InstructionAnalysisRule 8 | from d810.optimizers.instructions.analysis.utils import get_possible_patterns 9 | 10 | 11 | class ExampleGuessingRule(InstructionAnalysisRule): 12 | DESCRIPTION = "Detect pattern with variable used multiple times and with multiple different opcodes" 13 | 14 | def __init__(self): 15 | super().__init__() 16 | self.maturities = DEFAULT_INSTRUCTION_MATURITIES 17 | self.cur_maturity = None 18 | self.min_nb_var = 1 19 | self.max_nb_var = 3 20 | self.min_nb_diff_opcodes = 3 21 | self.max_nb_diff_opcodes = -1 22 | 23 | self.cur_index = 0 24 | self.max_index = 1000 25 | self.cur_ins_guessed = [""] * self.max_index 26 | self.pattern_filename_path = None 27 | 28 | def log_info(self, message): 29 | with open(self.pattern_filename_path, "a") as f: 30 | f.write('{0}\n'.format(message)) 31 | 32 | def set_maturity(self, maturity): 33 | self.log_info("Patterns guessed at maturity {0}".format(maturity_to_string(maturity))) 34 | self.cur_maturity = maturity 35 | 36 | def set_log_dir(self, log_dir): 37 | super().set_log_dir(log_dir) 38 | self.pattern_filename_path = os.path.join(self.log_dir, "pattern_guess.log") 39 | f = open(self.pattern_filename_path, "w") 40 | f.close() 41 | 42 | def configure(self, kwargs): 43 | super().configure(kwargs) 44 | if "min_nb_var" in kwargs.keys(): 45 | self.min_nb_var = kwargs["min_nb_var"] 46 | if "max_nb_var" in kwargs.keys(): 47 | self.max_nb_var = kwargs["max_nb_var"] 48 | if "min_nb_diff_opcodes" in kwargs.keys(): 49 | self.min_nb_diff_opcodes = kwargs["min_nb_diff_opcodes"] 50 | if "max_nb_diff_opcodes" in kwargs.keys(): 51 | self.max_nb_diff_opcodes = kwargs["max_nb_diff_opcodes"] 52 | 53 | if self.max_nb_var == -1: 54 | self.max_nb_var = 0xff 55 | if self.max_nb_diff_opcodes == -1: 56 | self.max_nb_diff_opcodes = 0xff 57 | 58 | def analyze_instruction(self, blk, ins): 59 | if self.cur_maturity not in self.maturities: 60 | return None 61 | formatted_ins = str(format_minsn_t(ins)) 62 | if formatted_ins in self.cur_ins_guessed: 63 | return False 64 | tmp = minsn_to_ast(ins) 65 | if tmp is None: 66 | return False 67 | is_good_candidate = self.check_if_possible_pattern(tmp) 68 | if is_good_candidate: 69 | self.cur_ins_guessed[self.cur_index] = formatted_ins 70 | self.cur_index = (self.cur_index + 1) % self.max_index 71 | return is_good_candidate 72 | 73 | def check_if_possible_pattern(self, test_ast): 74 | patterns = get_possible_patterns(test_ast, min_nb_use=2, ref_ast_info_by_index=None, max_nb_pattern=64) 75 | for pattern in patterns: 76 | leaf_info_list, cst_leaf_values, opcodes = pattern.get_information() 77 | leaf_nb_use = [leaf_info.number_of_use for leaf_info in leaf_info_list] 78 | if not(self.min_nb_var <= len(leaf_info_list) <= self.max_nb_var): 79 | continue 80 | if not(self.min_nb_diff_opcodes <= len(set(opcodes)) <= self.max_nb_diff_opcodes): 81 | continue 82 | if not(min(leaf_nb_use) >= 2): 83 | continue 84 | ins = pattern.mop.d 85 | self.log_info("IR: 0x{0:x} - {1}".format(ins.ea, format_minsn_t(ins))) 86 | for leaf_info in leaf_info_list: 87 | self.log_info(" {0} -> {1}".format(leaf_info.ast, format_mop_t(leaf_info.ast.mop))) 88 | self.log_info("Pattern: {0}".format(pattern)) 89 | self.log_info("AstNode: {0}\n".format(pattern.get_pattern())) 90 | return True 91 | return False 92 | -------------------------------------------------------------------------------- /D810_Extern/d810/optimizers/instructions/analysis/utils.py: -------------------------------------------------------------------------------- 1 | from d810.ast import AstNode, AstLeaf 2 | 3 | 4 | def get_possible_patterns(ast, min_nb_use=2, ref_ast_info_by_index=None, max_nb_pattern=64): 5 | # max_nb_pattern is used to prevent memory explosion when very large patterns are parsed 6 | if ast.is_leaf(): 7 | return [ast] 8 | if ref_ast_info_by_index is None: 9 | if ast.ast_index not in ast.sub_ast_info_by_index.keys(): 10 | ast.compute_sub_ast() 11 | ref_ast_info_by_index = ast.sub_ast_info_by_index 12 | possible_patterns = [] 13 | if ref_ast_info_by_index[ast.ast_index].number_of_use >= min_nb_use: 14 | node_as_leaf = AstLeaf("x_{0}".format(ast.ast_index)) 15 | node_as_leaf.mop = ast.mop 16 | node_as_leaf.ast_index = ast.ast_index 17 | possible_patterns.append(node_as_leaf) 18 | left_patterns = [] 19 | right_patterns = [] 20 | if ast.left is not None: 21 | left_patterns = get_possible_patterns(ast.left, min_nb_use, ref_ast_info_by_index, max_nb_pattern) 22 | if ast.right is not None: 23 | right_patterns = get_possible_patterns(ast.right, min_nb_use, ref_ast_info_by_index, max_nb_pattern) 24 | 25 | for left_pattern in left_patterns: 26 | if ast.right is not None: 27 | for right_pattern in right_patterns: 28 | node = AstNode(ast.opcode, left_pattern, right_pattern) 29 | node.mop = ast.mop 30 | node.ast_index = ast.ast_index 31 | if len(possible_patterns) < max_nb_pattern: 32 | possible_patterns.append(node) 33 | else: 34 | node = AstNode(ast.opcode, left_pattern) 35 | node.mop = ast.mop 36 | node.ast_index = ast.ast_index 37 | if len(possible_patterns) < max_nb_pattern: 38 | possible_patterns.append(node) 39 | return possible_patterns 40 | -------------------------------------------------------------------------------- /D810_Extern/d810/optimizers/instructions/chain/__init__.py: -------------------------------------------------------------------------------- 1 | from d810.utils import get_all_subclasses 2 | from d810.optimizers.instructions.chain.handler import ChainSimplificationRule, ChainOptimizer 3 | from d810.optimizers.instructions.chain.chain_rules import * 4 | 5 | CHAIN_RULES = [x() for x in get_all_subclasses(ChainSimplificationRule)] 6 | -------------------------------------------------------------------------------- /D810_Extern/d810/optimizers/instructions/chain/handler.py: -------------------------------------------------------------------------------- 1 | from d810.optimizers.instructions.handler import InstructionOptimizationRule, InstructionOptimizer 2 | 3 | 4 | class ChainSimplificationRule(InstructionOptimizationRule): 5 | pass 6 | 7 | 8 | class ChainOptimizer(InstructionOptimizer): 9 | RULE_CLASSES = [ChainSimplificationRule] 10 | -------------------------------------------------------------------------------- /D810_Extern/d810/optimizers/instructions/early/__init__.py: -------------------------------------------------------------------------------- 1 | from d810.utils import get_all_subclasses 2 | from d810.optimizers.instructions.early.handler import EarlyRule, EarlyOptimizer 3 | from d810.optimizers.instructions.early.mem_read import * 4 | 5 | EARLY_RULES = [x() for x in get_all_subclasses(EarlyRule)] 6 | -------------------------------------------------------------------------------- /D810_Extern/d810/optimizers/instructions/early/handler.py: -------------------------------------------------------------------------------- 1 | from d810.optimizers.instructions.handler import GenericPatternRule, InstructionOptimizer 2 | 3 | 4 | class EarlyRule(GenericPatternRule): 5 | pass 6 | 7 | 8 | class EarlyOptimizer(InstructionOptimizer): 9 | RULE_CLASSES = [EarlyRule] 10 | -------------------------------------------------------------------------------- /D810_Extern/d810/optimizers/instructions/early/mem_read.py: -------------------------------------------------------------------------------- 1 | from ida_hexrays import * 2 | from idaapi import SEGPERM_READ, SEGPERM_WRITE, xrefblk_t, getseg, segment_t, XREF_DATA, dr_W, is_loaded 3 | 4 | from d810.optimizers.instructions.early.handler import EarlyRule 5 | from d810.ast import AstLeaf, AstConstant, AstNode 6 | 7 | 8 | class SetGlobalVariablesToZero(EarlyRule): 9 | DESCRIPTION = "This rule can be used to patch memory read" 10 | 11 | PATTERN = AstNode(m_mov, AstLeaf("ro_dword")) 12 | REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_res")) 13 | 14 | def __init__(self): 15 | super().__init__() 16 | self.ro_dword_min_ea = None 17 | self.ro_dword_max_ea = None 18 | 19 | def configure(self, kwargs): 20 | super().configure(kwargs) 21 | self.ro_dword_min_ea = None 22 | self.ro_dword_max_ea = None 23 | if "ro_dword_min_ea" in kwargs.keys(): 24 | self.ro_dword_min_ea = int(kwargs["ro_dword_min_ea"], 16) 25 | if "ro_dword_max_ea" in kwargs.keys(): 26 | self.ro_dword_max_ea = int(kwargs["ro_dword_max_ea"], 16) 27 | 28 | def check_candidate(self, candidate): 29 | if (self.ro_dword_min_ea is None) or (self.ro_dword_max_ea is None): 30 | return False 31 | if candidate["ro_dword"].mop.t != mop_v: 32 | return False 33 | mem_read_address = candidate["ro_dword"].mop.g 34 | if not(self.ro_dword_min_ea <= mem_read_address <= self.ro_dword_max_ea): 35 | return False 36 | 37 | candidate.add_constant_leaf("val_res", 0, candidate["ro_dword"].mop.size) 38 | return True 39 | 40 | 41 | # This rule is from 42 | # https://www.carbonblack.com/blog/defeating-compiler-level-obfuscations-used-in-apt10-malware/ 43 | class SetGlobalVariablesToZeroIfDetectedReadOnly(EarlyRule): 44 | DESCRIPTION = "WARNING: Use it only if you know what you are doing as it may patch data not related to obfuscation" 45 | 46 | PATTERN = AstNode(m_mov, AstLeaf("ro_dword")) 47 | REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_res")) 48 | 49 | def __init__(self): 50 | super().__init__() 51 | # If we optimized too early (in MMAT_GENERATED), we may replace something like 52 | # 'mov &($dword_10020CC8).4, eoff.4' by 'mov #0.4, eoff.4' 53 | # and this will lead to incorrect decompilation where MEMORY[0] is used 54 | # Thus, we explicitly specify the MMAT_PREOPTIMIZED maturity. 55 | self.maturities = [MMAT_PREOPTIMIZED] 56 | 57 | def is_read_only_inited_var(self, address): 58 | s: segment_t = getseg(address) 59 | if s is None: 60 | return False 61 | if s.perm != (SEGPERM_READ | SEGPERM_WRITE): 62 | return False 63 | if is_loaded(address): 64 | return False 65 | ref_finder = xrefblk_t() 66 | is_ok = ref_finder.first_to(address, XREF_DATA) 67 | while is_ok: 68 | if ref_finder.type == dr_W: 69 | return False 70 | is_ok = ref_finder.next_to() 71 | return True 72 | 73 | def check_candidate(self, candidate): 74 | mem_read_address = None 75 | if candidate["ro_dword"].mop.t == mop_v: 76 | mem_read_address = candidate["ro_dword"].mop.g 77 | elif candidate["ro_dword"].mop.t == mop_a: 78 | if candidate["ro_dword"].mop.a.t == mop_v: 79 | mem_read_address = candidate["ro_dword"].mop.a.g 80 | 81 | if mem_read_address is None: 82 | return False 83 | 84 | if not self.is_read_only_inited_var(mem_read_address): 85 | return False 86 | candidate.add_constant_leaf("val_res", 0, candidate["ro_dword"].mop.size) 87 | return True 88 | 89 | -------------------------------------------------------------------------------- /D810_Extern/d810/optimizers/instructions/handler.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | import logging 3 | from typing import List 4 | from ida_hexrays import * 5 | 6 | from d810.optimizers.handler import OptimizationRule 7 | from d810.hexrays_formatters import format_minsn_t 8 | from d810.ast import minsn_to_ast, AstNode 9 | from d810.errors import D810Exception 10 | 11 | 12 | d810_logger = logging.getLogger('D810') 13 | optimizer_logger = logging.getLogger('D810.optimizer') 14 | 15 | 16 | class InstructionOptimizationRule(OptimizationRule): 17 | def __init__(self): 18 | super().__init__() 19 | self.maturities = [] 20 | 21 | def check_and_replace(self, blk, ins): 22 | return None 23 | 24 | 25 | class GenericPatternRule(InstructionOptimizationRule): 26 | PATTERN = None 27 | PATTERNS = None 28 | REPLACEMENT_PATTERN = None 29 | 30 | def __init__(self): 31 | super().__init__() 32 | self.pattern_candidates = [self.PATTERN] 33 | if self.PATTERNS is not None: 34 | self.pattern_candidates += self.PATTERNS 35 | 36 | def check_candidate(self, candidate: AstNode): 37 | # Perform rule specific checks 38 | return False 39 | 40 | def get_valid_candidates(self, instruction: minsn_t, stop_early=True): 41 | valid_candidates = [] 42 | tmp = minsn_to_ast(instruction) 43 | if tmp is None: 44 | return [] 45 | for candidate_pattern in self.pattern_candidates: 46 | if not candidate_pattern.check_pattern_and_copy_mops(tmp): 47 | continue 48 | if not self.check_candidate(candidate_pattern): 49 | continue 50 | valid_candidates.append(candidate_pattern) 51 | if stop_early: 52 | return valid_candidates 53 | return [] 54 | 55 | def get_replacement(self, candidate: AstNode): 56 | is_ok = self.REPLACEMENT_PATTERN.update_leafs_mop(candidate) 57 | if not is_ok: 58 | return None 59 | new_ins = self.REPLACEMENT_PATTERN.create_minsn(candidate.ea, candidate.dst_mop) 60 | return new_ins 61 | 62 | def check_and_replace(self, blk: mblock_t, instruction: minsn_t): 63 | valid_candidates = self.get_valid_candidates(instruction, stop_early=True) 64 | if len(valid_candidates) == 0: 65 | return None 66 | new_instruction = self.get_replacement(valid_candidates[0]) 67 | return new_instruction 68 | 69 | @property 70 | def description(self): 71 | if self.DESCRIPTION is not None: 72 | return self.DESCRIPTION 73 | if (self.PATTERN is None) or (self.REPLACEMENT_PATTERN is None): 74 | return "" 75 | self.PATTERN.reset_mops() 76 | self.REPLACEMENT_PATTERN.reset_mops() 77 | return "{0} => {1}".format(self.PATTERN, self.REPLACEMENT_PATTERN) 78 | 79 | 80 | class InstructionOptimizer(object): 81 | RULE_CLASSES = [] 82 | NAME = None 83 | 84 | def __init__(self, maturities: List[int], log_dir=None): 85 | self.rules = set() 86 | self.rules_usage_info = {} 87 | self.maturities = maturities 88 | self.log_dir = log_dir 89 | self.cur_maturity = MMAT_PREOPTIMIZED 90 | 91 | def add_rule(self, rule: InstructionOptimizationRule): 92 | is_valid_rule_class = False 93 | for rule_class in self.RULE_CLASSES: 94 | if isinstance(rule, rule_class): 95 | is_valid_rule_class = True 96 | break 97 | if not is_valid_rule_class: 98 | return False 99 | optimizer_logger.debug("Adding rule {0}".format(rule)) 100 | if len(rule.maturities) == 0: 101 | rule.maturities = self.maturities 102 | self.rules.add(rule) 103 | self.rules_usage_info[rule.name] = 0 104 | return True 105 | 106 | def reset_rule_usage_statistic(self): 107 | self.rules_usage_info = {} 108 | for rule in self.rules: 109 | self.rules_usage_info[rule.name] = 0 110 | 111 | def show_rule_usage_statistic(self): 112 | for rule_name, rule_nb_match in self.rules_usage_info.items(): 113 | if rule_nb_match > 0: 114 | d810_logger.info("Instruction Rule '{0}' has been used {1} times".format(rule_name, rule_nb_match)) 115 | 116 | def get_optimized_instruction(self, blk: mblock_t, ins: minsn_t): 117 | if blk is not None: 118 | self.cur_maturity = blk.mba.maturity 119 | # if self.cur_maturity not in self.maturities: 120 | # return None 121 | for rule in self.rules: 122 | if self.cur_maturity not in rule.maturities: 123 | continue 124 | try: 125 | new_ins = rule.check_and_replace(blk, ins) 126 | if new_ins is not None: 127 | self.rules_usage_info[rule.name] += 1 128 | optimizer_logger.info("Rule {0} matched:".format(rule.name)) 129 | optimizer_logger.info(" orig: {0}".format(format_minsn_t(ins))) 130 | optimizer_logger.info(" new : {0}".format(format_minsn_t(new_ins))) 131 | return new_ins 132 | except RuntimeError as e: 133 | optimizer_logger.error("Runtime error during rule {0} for instruction {1}: {2}".format(rule, format_minsn_t(ins), e)) 134 | except D810Exception as e: 135 | optimizer_logger.error("D810Exception during rule {0} for instruction {1}: {2}".format(rule, format_minsn_t(ins), e)) 136 | return None 137 | 138 | @property 139 | def name(self): 140 | if self.NAME is not None: 141 | return self.NAME 142 | return self.__class__.__name__ 143 | -------------------------------------------------------------------------------- /D810_Extern/d810/optimizers/instructions/pattern_matching/__init__.py: -------------------------------------------------------------------------------- 1 | from d810.utils import get_all_subclasses 2 | from d810.optimizers.instructions.pattern_matching.handler import PatternMatchingRule, PatternOptimizer 3 | from d810.optimizers.instructions.pattern_matching.rewrite_add import * 4 | from d810.optimizers.instructions.pattern_matching.rewrite_and import * 5 | from d810.optimizers.instructions.pattern_matching.rewrite_bnot import * 6 | from d810.optimizers.instructions.pattern_matching.rewrite_cst import * 7 | from d810.optimizers.instructions.pattern_matching.rewrite_mov import * 8 | from d810.optimizers.instructions.pattern_matching.rewrite_mul import * 9 | from d810.optimizers.instructions.pattern_matching.rewrite_neg import * 10 | from d810.optimizers.instructions.pattern_matching.rewrite_predicates import * 11 | from d810.optimizers.instructions.pattern_matching.rewrite_or import * 12 | from d810.optimizers.instructions.pattern_matching.rewrite_sub import * 13 | from d810.optimizers.instructions.pattern_matching.rewrite_xor import * 14 | from d810.optimizers.instructions.pattern_matching.weird import * 15 | from d810.optimizers.instructions.pattern_matching.experimental import * 16 | 17 | PATTERN_MATCHING_RULES = [x() for x in get_all_subclasses(PatternMatchingRule)] 18 | 19 | 20 | -------------------------------------------------------------------------------- /D810_Extern/d810/optimizers/instructions/pattern_matching/experimental.py: -------------------------------------------------------------------------------- 1 | from ida_hexrays import * 2 | 3 | from d810.optimizers.instructions.pattern_matching.handler import PatternMatchingRule 4 | from d810.ast import AstLeaf, AstConstant, AstNode 5 | from d810.hexrays_formatters import format_mop_t 6 | 7 | class ReplaceMovHigh(PatternMatchingRule): 8 | PATTERN = AstNode(m_mov, 9 | AstConstant('c_0')) 10 | REPLACEMENT_PATTERN = AstNode(m_or, AstConstant("new_c_0"), AstNode(m_and, AstLeaf("new_reg"), AstConstant("mask"))) 11 | 12 | def check_candidate(self, candidate): 13 | # IDA does not do constant propagation for pattern such as: 14 | # mov #0x65A4.2, r6.2 15 | # mov #0x210F.2, r6^2.2 16 | # jz r0.4, r6.4 17 | # Thus, we try to detect mov to r6^2 and replace by (or #0x210F0000.4, r6.4 & 0x0000ffff.4, r6.4 18 | # By doing that, IDA constant propagation will work again. 19 | 20 | if candidate.dst_mop.t != mop_r: 21 | return False 22 | dst_reg_name = format_mop_t(candidate.dst_mop) 23 | if dst_reg_name is None: 24 | return False 25 | if "^2" in dst_reg_name: 26 | if candidate["c_0"].mop.size != 2: 27 | return False 28 | candidate.add_constant_leaf("new_c_0", candidate["c_0"].value << 16, 4) 29 | candidate.add_constant_leaf("mask", 0xffff, 4) 30 | new_dst_reg = mop_t() 31 | new_dst_reg.make_reg(candidate.dst_mop.r - 2, 4) 32 | candidate.add_leaf("new_reg", new_dst_reg) 33 | candidate.dst_mop = new_dst_reg 34 | return True 35 | else: 36 | return False 37 | -------------------------------------------------------------------------------- /D810_Extern/d810/optimizers/instructions/pattern_matching/rewrite_mov.py: -------------------------------------------------------------------------------- 1 | from ida_hexrays import * 2 | 3 | 4 | from d810.optimizers.instructions.pattern_matching.handler import PatternMatchingRule 5 | from d810.ast import AstLeaf, AstConstant, AstNode 6 | from d810.hexrays_helpers import equal_bnot_mop, AND_TABLE 7 | 8 | 9 | # GetIdentRule1: ((x_0 & x_1) + (x_0 & ~x_1)) == x_0 10 | class GetIdentRule1(PatternMatchingRule): 11 | PATTERN = AstNode(m_add, 12 | AstNode(m_and, 13 | AstLeaf('x_0'), 14 | AstLeaf('x_1')), 15 | AstNode(m_and, 16 | AstLeaf('x_0'), 17 | AstLeaf('bnot_x_1'))) 18 | REPLACEMENT_PATTERN = AstNode(m_mov, AstLeaf("x_0")) 19 | 20 | def check_candidate(self, candidate): 21 | if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop): 22 | return False 23 | return True 24 | 25 | 26 | # GetIdentRule2: ((x_0 & x_1) ^ (x_0 & ~x_1)) == x_0 i 27 | class GetIdentRule2(PatternMatchingRule): 28 | PATTERN = AstNode(m_xor, 29 | AstNode(m_and, 30 | AstLeaf('x_0'), 31 | AstLeaf('x_1')), 32 | AstNode(m_and, 33 | AstLeaf('x_0'), 34 | AstLeaf('bnot_x_1'))) 35 | REPLACEMENT_PATTERN = AstNode(m_mov, AstLeaf("x_0")) 36 | 37 | def check_candidate(self, candidate): 38 | if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop): 39 | return False 40 | return True 41 | 42 | 43 | class GetIdentRule3(PatternMatchingRule): 44 | PATTERN = AstNode(m_and, 45 | AstLeaf("x_0"), 46 | AstNode(m_or, 47 | AstLeaf("x_0"), 48 | AstLeaf("x_1"))) 49 | 50 | REPLACEMENT_PATTERN = AstNode(m_mov, AstLeaf("x_0")) 51 | -------------------------------------------------------------------------------- /D810_Extern/d810/optimizers/instructions/pattern_matching/rewrite_neg.py: -------------------------------------------------------------------------------- 1 | from ida_hexrays import * 2 | 3 | from d810.optimizers.instructions.pattern_matching.handler import PatternMatchingRule 4 | from d810.hexrays_helpers import AND_TABLE 5 | from d810.ast import AstLeaf, AstConstant, AstNode 6 | 7 | 8 | class Neg_HackersDelightRule_1(PatternMatchingRule): 9 | PATTERN = AstNode(m_add, 10 | AstNode(m_bnot, 11 | AstLeaf("x_0")), 12 | AstConstant("1", 1)) 13 | REPLACEMENT_PATTERN = AstNode(m_neg, AstLeaf("x_0")) 14 | 15 | 16 | class Neg_HackersDelightRule_2(PatternMatchingRule): 17 | PATTERN = AstNode(m_bnot, 18 | AstNode(m_sub, 19 | AstLeaf("x_0"), 20 | AstConstant("1", 1))) 21 | REPLACEMENT_PATTERN = AstNode(m_neg, AstLeaf("x_0")) 22 | 23 | 24 | class NegAdd_HackersDelightRule_1(PatternMatchingRule): 25 | PATTERN = AstNode(m_sub, 26 | AstNode(m_xor, 27 | AstLeaf("x_0"), 28 | AstLeaf("x_1")), 29 | AstNode(m_mul, 30 | AstConstant("2", 2), 31 | AstNode(m_or, 32 | AstLeaf("x_0"), 33 | AstLeaf("x_1")))) 34 | REPLACEMENT_PATTERN = AstNode(m_neg, 35 | AstNode(m_add, 36 | AstLeaf("x_0"), 37 | AstLeaf("x_1"))) 38 | 39 | 40 | class NegAdd_HackersDelightRule_2(PatternMatchingRule): 41 | PATTERN = AstNode(m_sub, 42 | AstNode(m_xor, 43 | AstLeaf("x_0"), 44 | AstNode(m_or, 45 | AstLeaf("x_1"), 46 | AstLeaf("x_2"))), 47 | AstNode(m_mul, 48 | AstConstant("2", 2), 49 | AstNode(m_or, 50 | AstNode(m_or, 51 | AstLeaf("x_0"), 52 | AstLeaf("x_1")), 53 | AstLeaf("x_2")))) 54 | REPLACEMENT_PATTERN = AstNode(m_neg, 55 | AstNode(m_add, 56 | AstLeaf("x_0"), 57 | AstNode(m_or, 58 | AstLeaf("x_1"), 59 | AstLeaf("x_2")))) 60 | 61 | 62 | class NegAdd_HackersDelightRule_1(PatternMatchingRule): 63 | PATTERN = AstNode(m_add, 64 | AstNode(m_mul, 65 | AstConstant('val_fe'), 66 | AstNode(m_or, 67 | AstLeaf('x_0'), 68 | AstLeaf('x_1'))), 69 | AstNode(m_xor, 70 | AstLeaf('x_0'), 71 | AstLeaf('x_1'))) 72 | 73 | REPLACEMENT_PATTERN = AstNode(m_neg, 74 | AstNode(m_add, 75 | AstLeaf("x_0"), 76 | AstLeaf("x_1"))) 77 | 78 | 79 | def check_candidate(self, candidate): 80 | if (candidate["val_fe"].value + 2) & AND_TABLE[candidate["val_fe"].size] != 0: 81 | return False 82 | return True 83 | 84 | class NegOr_HackersDelightRule_1(PatternMatchingRule): 85 | PATTERN = AstNode(m_sub, 86 | AstNode(m_and, 87 | AstLeaf("x_0"), 88 | AstLeaf("x_1")), 89 | AstNode(m_add, 90 | AstLeaf("x_0"), 91 | AstLeaf("x_1"))) 92 | 93 | REPLACEMENT_PATTERN = AstNode(m_neg, 94 | AstNode(m_or, 95 | AstLeaf("x_0"), 96 | AstLeaf("x_1"))) 97 | 98 | 99 | class NegXor_HackersDelightRule_1(PatternMatchingRule): 100 | PATTERN = AstNode(m_sub, 101 | AstNode(m_and, 102 | AstLeaf('x_0'), 103 | AstLeaf('x_1')), 104 | AstNode(m_or, 105 | AstLeaf('x_0'), 106 | AstLeaf('x_1'))) 107 | REPLACEMENT_PATTERN = AstNode(m_neg, 108 | AstNode(m_xor, 109 | AstLeaf("x_0"), 110 | AstLeaf("x_1"))) 111 | 112 | 113 | class NegXor_HackersDelightRule_2(PatternMatchingRule): 114 | PATTERN = AstNode(m_sub, 115 | AstNode(m_add, 116 | AstLeaf('x_0'), 117 | AstLeaf('x_1')), 118 | AstNode(m_mul, 119 | AstConstant('2', 2), 120 | AstNode(m_or, 121 | AstLeaf('x_0'), 122 | AstLeaf('x_1')))) 123 | REPLACEMENT_PATTERN = AstNode(m_neg, 124 | AstNode(m_xor, 125 | AstLeaf("x_0"), 126 | AstLeaf("x_1"))) 127 | -------------------------------------------------------------------------------- /D810_Extern/d810/optimizers/instructions/pattern_matching/weird.py: -------------------------------------------------------------------------------- 1 | from ida_hexrays import * 2 | from d810.optimizers.instructions.pattern_matching.handler import PatternMatchingRule 3 | from d810.ast import AstLeaf, AstConstant, AstNode 4 | from d810.hexrays_helpers import equal_bnot_mop 5 | 6 | 7 | class WeirdRule1(PatternMatchingRule): 8 | PATTERN = AstNode(m_sub, 9 | AstLeaf("x_0"), 10 | AstNode(m_or, 11 | AstLeaf("x_0"), 12 | AstLeaf("x_1"))) 13 | REPLACEMENT_PATTERN = AstNode(m_add, 14 | AstNode(m_or, 15 | AstLeaf("x_0"), 16 | AstNode(m_bnot, AstLeaf("x_1"))), 17 | AstConstant("val_1")) 18 | 19 | def check_candidate(self, candidate): 20 | candidate.add_constant_leaf("val_1", 1, candidate.size) 21 | return True 22 | 23 | 24 | class WeirdRule2(PatternMatchingRule): 25 | PATTERN = AstNode(m_sub, 26 | AstNode(m_mul, 27 | AstConstant("2", 2), 28 | AstLeaf("x_0")), 29 | AstNode(m_and, 30 | AstLeaf("x_0"), 31 | AstNode(m_bnot, AstLeaf("x_1")))) 32 | REPLACEMENT_PATTERN = AstNode(m_add, 33 | AstLeaf("x_0"), 34 | AstNode(m_and, 35 | AstLeaf("x_0"), 36 | AstLeaf("x_1"))) 37 | 38 | 39 | class WeirdRule3(PatternMatchingRule): 40 | PATTERN = AstNode(m_sub, 41 | AstNode(m_and, 42 | AstLeaf("x_0"), 43 | AstNode(m_bnot, AstLeaf("x_1"))), 44 | AstNode(m_mul, 45 | AstConstant("2", 2), 46 | AstLeaf("x_0"))) 47 | REPLACEMENT_PATTERN = AstNode(m_neg, 48 | AstNode(m_add, 49 | AstLeaf("x_0"), 50 | AstNode(m_and, 51 | AstLeaf("x_0"), 52 | AstLeaf("x_1")))) 53 | 54 | 55 | class WeirdRule4(PatternMatchingRule): 56 | PATTERN = AstNode(m_sub, 57 | AstNode(m_and, 58 | AstLeaf("x_0"), 59 | AstLeaf("bnot_x_1")), 60 | AstNode(m_and, 61 | AstLeaf("x_0"), 62 | AstLeaf("x_1"))) 63 | REPLACEMENT_PATTERN = AstNode(m_sub, 64 | AstNode(m_xor, 65 | AstLeaf("x_0"), 66 | AstLeaf("x_1")), 67 | AstLeaf("x_1")) 68 | 69 | def check_candidate(self, candidate): 70 | if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop): 71 | return False 72 | return True 73 | 74 | 75 | class WeirdRule5(PatternMatchingRule): 76 | PATTERN = AstNode(m_sub, 77 | AstNode(m_add, 78 | AstNode(m_or, 79 | AstLeaf("bnot_x_0"), 80 | AstNode(m_and, 81 | AstLeaf("bnot_x_1"), 82 | AstLeaf("x_2"))), 83 | AstNode(m_add, 84 | AstLeaf("x_0"), 85 | AstNode(m_and, 86 | AstLeaf("x_1"), 87 | AstLeaf("x_2")))), 88 | AstLeaf("x_2")) 89 | REPLACEMENT_PATTERN = AstNode(m_or, 90 | AstLeaf("x_0"), 91 | AstNode(m_or, 92 | AstLeaf("x_1"), 93 | AstNode(m_bnot, 94 | AstLeaf("x_2")))) 95 | 96 | def check_candidate(self, candidate): 97 | if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop): 98 | return False 99 | if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop): 100 | return False 101 | return True 102 | 103 | 104 | class WeirdRule6(PatternMatchingRule): 105 | PATTERN = AstNode(m_add, 106 | AstNode(m_or, 107 | AstLeaf('x_0'), 108 | AstLeaf('x_1')), 109 | AstNode(m_and, 110 | AstLeaf('x_0'), 111 | AstNode(m_bnot, 112 | AstLeaf('x_1')))) 113 | REPLACEMENT_PATTERN = AstNode(m_add, 114 | AstNode(m_xor, 115 | AstLeaf("x_0"), 116 | AstLeaf("x_1")), 117 | AstLeaf('x_0')) 118 | -------------------------------------------------------------------------------- /D810_Extern/d810/optimizers/instructions/z3/__init__.py: -------------------------------------------------------------------------------- 1 | from d810.utils import get_all_subclasses 2 | from d810.optimizers.instructions.z3.handler import Z3Rule, Z3Optimizer 3 | from d810.optimizers.instructions.z3.cst import * 4 | from d810.optimizers.instructions.z3.predicates import * 5 | 6 | 7 | Z3_RULES = [x() for x in get_all_subclasses(Z3Rule)] 8 | -------------------------------------------------------------------------------- /D810_Extern/d810/optimizers/instructions/z3/cst.py: -------------------------------------------------------------------------------- 1 | from ida_hexrays import * 2 | from d810.optimizers.instructions.z3.handler import Z3Rule 3 | from d810.ast import AstConstant, AstNode 4 | from d810.ast import minsn_to_ast 5 | from d810.errors import AstEvaluationException 6 | from d810.z3_utils import z3_check_mop_equality 7 | 8 | 9 | class Z3ConstantOptimization(Z3Rule): 10 | DESCRIPTION = "Detect and replace obfuscated constants" 11 | REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("c_res")) 12 | 13 | def __init__(self): 14 | super().__init__() 15 | self.min_nb_opcode = 3 16 | self.min_nb_constant = 3 17 | 18 | def configure(self, kwargs): 19 | super().configure(kwargs) 20 | if "min_nb_opcode" in kwargs.keys(): 21 | self.min_nb_opcode = kwargs["min_nb_opcode"] 22 | if "min_nb_constant" in kwargs.keys(): 23 | self.min_nb_constant = kwargs["min_nb_constant"] 24 | 25 | def check_and_replace(self, blk, instruction): 26 | tmp = minsn_to_ast(instruction) 27 | if tmp is None: 28 | return None 29 | leaf_info_list, cst_leaf_values, opcodes = tmp.get_information() 30 | if len(leaf_info_list) == 1 and \ 31 | len(opcodes) >= self.min_nb_opcode and \ 32 | (len(cst_leaf_values) >= self.min_nb_constant): 33 | try: 34 | val_0 = tmp.evaluate_with_leaf_info(leaf_info_list, [0]) 35 | val_1 = tmp.evaluate_with_leaf_info(leaf_info_list, [0xffffffff]) 36 | 37 | if val_0 == val_1: 38 | c_res_mop = mop_t() 39 | c_res_mop.make_number(val_0, tmp.mop.size) 40 | is_ok = z3_check_mop_equality(tmp.mop, c_res_mop) 41 | if is_ok: 42 | tmp.add_leaf("c_res", c_res_mop) 43 | new_instruction = self.get_replacement(tmp) 44 | return new_instruction 45 | return None 46 | except ZeroDivisionError: 47 | pass 48 | except AstEvaluationException as e: 49 | print("Error while evaluating {0}: {1}".format(tmp, e)) 50 | pass 51 | return None 52 | -------------------------------------------------------------------------------- /D810_Extern/d810/optimizers/instructions/z3/handler.py: -------------------------------------------------------------------------------- 1 | from d810.optimizers.instructions.handler import GenericPatternRule, InstructionOptimizer 2 | 3 | 4 | class Z3Rule(GenericPatternRule): 5 | pass 6 | 7 | 8 | class Z3Optimizer(InstructionOptimizer): 9 | RULE_CLASSES = [Z3Rule] 10 | -------------------------------------------------------------------------------- /D810_Extern/d810/optimizers/instructions/z3/predicates.py: -------------------------------------------------------------------------------- 1 | from ida_hexrays import * 2 | 3 | from d810.optimizers.instructions.z3.handler import Z3Rule 4 | from d810.ast import AstLeaf, AstConstant, AstNode 5 | from d810.z3_utils import z3_check_mop_equality, z3_check_mop_inequality 6 | 7 | 8 | class Z3setzRuleGeneric(Z3Rule): 9 | DESCRIPTION = "Check with Z3 if a m_setz check is always True or False" 10 | PATTERN = AstNode(m_setz, 11 | AstLeaf("x_0"), 12 | AstLeaf("x_1")) 13 | REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_res")) 14 | 15 | def check_candidate(self, candidate): 16 | if z3_check_mop_equality(candidate["x_0"].mop, candidate["x_1"].mop): 17 | candidate.add_constant_leaf("val_res", 1, candidate.size) 18 | return True 19 | if z3_check_mop_inequality(candidate["x_0"].mop, candidate["x_1"].mop): 20 | candidate.add_constant_leaf("val_res", 0, candidate.size) 21 | return True 22 | return False 23 | 24 | 25 | class Z3setnzRuleGeneric(Z3Rule): 26 | DESCRIPTION = "Check with Z3 if a m_setnz check is always True or False" 27 | PATTERN = AstNode(m_setnz, 28 | AstLeaf("x_0"), 29 | AstLeaf("x_1")) 30 | REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_res")) 31 | 32 | def check_candidate(self, candidate): 33 | if z3_check_mop_equality(candidate["x_0"].mop, candidate["x_1"].mop): 34 | candidate.add_constant_leaf("val_res", 0, candidate.size) 35 | return True 36 | if z3_check_mop_inequality(candidate["x_0"].mop, candidate["x_1"].mop): 37 | candidate.add_constant_leaf("val_res", 1, candidate.size) 38 | return True 39 | return False 40 | 41 | 42 | class Z3lnotRuleGeneric(Z3Rule): 43 | DESCRIPTION = "Check with Z3 if a m_lnot check is always True or False" 44 | PATTERN = AstNode(m_lnot, 45 | AstLeaf("x_0")) 46 | REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_res")) 47 | 48 | def check_candidate(self, candidate): 49 | val_0_mop = mop_t() 50 | val_0_mop.make_number(0, candidate["x_0"].size) 51 | if z3_check_mop_equality(candidate["x_0"].mop, val_0_mop): 52 | candidate.add_constant_leaf("val_res", 1, candidate.size) 53 | return True 54 | if z3_check_mop_inequality(candidate["x_0"].mop, val_0_mop): 55 | candidate.add_constant_leaf("val_res", 0, candidate.size) 56 | return True 57 | return False 58 | 59 | 60 | class Z3SmodRuleGeneric(Z3Rule): 61 | DESCRIPTION = "Check with Z3 if a m_setz check is always True or False" 62 | PATTERN = AstNode(m_smod, 63 | AstLeaf("x_0"), 64 | AstConstant("2", 2)) 65 | REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_res")) 66 | 67 | def check_candidate(self, candidate): 68 | cst_0_mop = mop_t() 69 | cst_0_mop.make_number(0, candidate.size) 70 | if z3_check_mop_equality(candidate.mop, cst_0_mop): 71 | candidate.add_leaf("val_res", cst_0_mop) 72 | return True 73 | cst_1_mop = mop_t() 74 | cst_1_mop.make_number(1, candidate.size) 75 | if z3_check_mop_equality(candidate.mop, cst_1_mop): 76 | candidate.add_leaf("val_res", cst_1_mop) 77 | return True 78 | return False 79 | -------------------------------------------------------------------------------- /D810_Extern/d810/utils.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | 3 | from d810.hexrays_helpers import MSB_TABLE 4 | 5 | CTYPE_SIGNED_TABLE = {1: ctypes.c_int8, 2: ctypes.c_int16, 4: ctypes.c_int32, 8: ctypes.c_int64} 6 | CTYPE_UNSIGNED_TABLE = {1: ctypes.c_uint8, 2: ctypes.c_uint16, 4: ctypes.c_uint32, 8: ctypes.c_uint64} 7 | 8 | 9 | def get_all_subclasses(python_class): 10 | python_class.__subclasses__() 11 | 12 | subclasses = set() 13 | check_these = [python_class] 14 | 15 | while check_these: 16 | parent = check_these.pop() 17 | for child in parent.__subclasses__(): 18 | if child not in subclasses: 19 | subclasses.add(child) 20 | check_these.append(child) 21 | 22 | return sorted(subclasses, key=lambda x: x.__name__) 23 | 24 | 25 | def unsigned_to_signed(unsigned_value, nb_bytes): 26 | return CTYPE_SIGNED_TABLE[nb_bytes](unsigned_value).value 27 | 28 | 29 | def signed_to_unsigned(signed_value, nb_bytes): 30 | return CTYPE_UNSIGNED_TABLE[nb_bytes](signed_value).value 31 | 32 | 33 | def get_msb(value, nb_bytes): 34 | return (value & MSB_TABLE[nb_bytes]) >> (nb_bytes * 8 - 1) 35 | 36 | 37 | def get_add_cf(op1, op2, nb_bytes): 38 | res = op1 + op2 39 | return get_msb((((op1 ^ op2) ^ res) ^ ((op1 ^ res) & (~(op1 ^ op2)))), nb_bytes) 40 | 41 | 42 | def get_add_of(op1, op2, nb_bytes): 43 | res = op1 + op2 44 | return get_msb(((op1 ^ res) & (~(op1 ^ op2))), nb_bytes) 45 | 46 | 47 | def get_sub_cf(op1, op2, nb_bytes): 48 | res = op1 - op2 49 | return get_msb((((op1 ^ op2) ^ res) ^ ((op1 ^ res) & (op1 ^ op2))), nb_bytes) 50 | 51 | 52 | def get_sub_of(op1, op2, nb_bytes): 53 | res = op1 - op2 54 | return get_msb(((op1 ^ res) & (op1 ^ op2)), nb_bytes) 55 | 56 | 57 | def get_parity_flag(op1, op2, nb_bytes): 58 | tmp = CTYPE_UNSIGNED_TABLE[nb_bytes](op1 - op2).value 59 | return (bin(tmp).count("1") + 1) % 2 60 | 61 | 62 | def ror(x, n, nb_bits=32): 63 | mask = (2 ** n) - 1 64 | mask_bits = x & mask 65 | return (x >> n) | (mask_bits << (nb_bits - n)) 66 | 67 | 68 | def rol(x, n, nb_bits=32): 69 | return ror(x, nb_bits - n, nb_bits) 70 | -------------------------------------------------------------------------------- /D810_Extern/test.py: -------------------------------------------------------------------------------- 1 | """ 2 | summary: list instruction registers 3 | 4 | description: 5 | Shows a list of direct references to a register from the 6 | current instruction. 7 | """ 8 | 9 | import ida_pro 10 | import ida_hexrays 11 | import ida_kernwin 12 | import ida_funcs 13 | import ida_bytes 14 | import ida_lines 15 | 16 | def collect_block_xrefs(out, mlist, blk, ins, find_uses): 17 | p = ins 18 | while p and not mlist.empty(): 19 | use = blk.build_use_list(p, ida_hexrays.MUST_ACCESS); # things used by the insn 20 | _def = blk.build_def_list(p, ida_hexrays.MUST_ACCESS); # things defined by the insn 21 | plst = use if find_uses else _def 22 | if mlist.has_common(plst): 23 | if not p.ea in out: 24 | out.append(p.ea) # this microinstruction seems to use our operand 25 | mlist.sub(_def) 26 | p = p.next if find_uses else p.prev 27 | 28 | 29 | def collect_xrefs(out, ctx, mop, mlist, du, find_uses): 30 | # first collect the references in the current block 31 | start = ctx.topins.next if find_uses else ctx.topins.prev; 32 | collect_block_xrefs(out, mlist, ctx.blk, start, find_uses) 33 | 34 | # then find references in other blocks 35 | serial = ctx.blk.serial; # block number of the operand 36 | bc = du[serial] # chains of that block 37 | voff = ida_hexrays.voff_t(mop) 38 | ch = bc.get_chain(voff) # chain of the operand 39 | if not ch: 40 | return # odd 41 | for bn in ch: 42 | b = ctx.mba.get_mblock(bn) 43 | ins = b.head if find_uses else b.tail 44 | tmp = ida_hexrays.mlist_t() 45 | tmp.add(mlist) 46 | collect_block_xrefs(out, tmp, b, ins, find_uses) 47 | 48 | 49 | class xref_chooser_t(ida_kernwin.Choose): 50 | def __init__(self, xrefs, t, n, ea, gco): 51 | ida_kernwin.Choose.__init__( 52 | self, 53 | t, 54 | [["Type", 3], ["Address", 16], ["Instruction", 60]]) 55 | 56 | self.xrefs = xrefs 57 | self.ndefs = n 58 | self.curr_ea = ea 59 | self.gco = gco 60 | self.items = [ self._make_item(idx) for idx in range(len(xrefs)) ] 61 | 62 | def OnGetSize(self): 63 | return len(self.items) 64 | 65 | def OnGetLine(self, n): 66 | return self.items[n] 67 | 68 | def _make_item(self, idx): 69 | ea = self.xrefs[idx] 70 | both_mask = ida_hexrays.GCO_USE|ida_hexrays.GCO_DEF 71 | both = (self.gco.flags & both_mask) == both_mask 72 | if ea == self.curr_ea and both: 73 | type_str = "use/def" 74 | elif idx < self.ndefs: 75 | type_str = "def" 76 | else: 77 | type_str = "use" 78 | insn = ida_lines.generate_disasm_line(ea, ida_lines.GENDSM_REMOVE_TAGS) 79 | return [type_str, "%08x" % ea, insn] 80 | 81 | 82 | def show_xrefs(ea, gco, xrefs, ndefs): 83 | title = "xrefs to %s at %08x" % (gco.name, ea) 84 | xc = xref_chooser_t(xrefs, title, ndefs, ea, gco) 85 | i = xc.Show(True) 86 | if i >= 0: 87 | ida_kernwin.jumpto(xrefs[i]) 88 | 89 | 90 | if ida_hexrays.init_hexrays_plugin(): 91 | ea = ida_kernwin.get_screen_ea() 92 | pfn = ida_funcs.get_func(ea) 93 | w = ida_kernwin.warning 94 | if pfn: 95 | F = ida_bytes.get_flags(ea) 96 | if ida_bytes.is_code(F): 97 | gco = ida_hexrays.gco_info_t() 98 | if ida_hexrays.get_current_operand(gco): 99 | # generate microcode 100 | hf = ida_hexrays.hexrays_failure_t() 101 | mbr = ida_hexrays.mba_ranges_t(pfn) 102 | mba = ida_hexrays.gen_microcode( 103 | mbr, 104 | hf, 105 | None, 106 | ida_hexrays.DECOMP_WARNINGS | ida_hexrays.DECOMP_NO_CACHE, 107 | ida_hexrays.MMAT_PREOPTIMIZED) 108 | if mba: 109 | merr = mba.build_graph() 110 | if merr == ida_hexrays.MERR_OK: 111 | ncalls = mba.analyze_calls(ida_hexrays.ACFL_GUESS) 112 | if ncalls < 0: 113 | print("%08x: failed to determine some calling conventions", pfn.start_ea) 114 | mlist = ida_hexrays.mlist_t() 115 | if gco.append_to_list(mlist, mba): 116 | ctx = ida_hexrays.op_parent_info_t() 117 | mop = mba.find_mop(ctx, ea, gco.is_def(), mlist) 118 | if mop: 119 | xrefs = ida_pro.eavec_t() 120 | ndefs = 0 121 | graph = mba.get_graph() 122 | ud = graph.get_ud(ida_hexrays.GC_REGS_AND_STKVARS) 123 | du = graph.get_du(ida_hexrays.GC_REGS_AND_STKVARS) 124 | if gco.is_use(): 125 | collect_xrefs(xrefs, ctx, mop, mlist, ud, False) 126 | ndefs = xrefs.size() 127 | if ea not in xrefs: 128 | xrefs.append(ea) 129 | if gco.is_def(): 130 | if ea not in xrefs: 131 | xrefs.append(ea) 132 | ndefs = len(xrefs) 133 | collect_xrefs(xrefs, ctx, mop, mlist, du, True) 134 | show_xrefs(ea, gco, xrefs, ndefs) 135 | else: 136 | w("Could not find the operand in the microcode, sorry") 137 | else: 138 | w("Failed to represent %s as microcode list" % gco.name) 139 | else: 140 | w("%08x: %s" % (errea, ida_hexrays.get_merror_desc(merr, mba))) 141 | else: 142 | w("%08x: %s" % (hf.errea, hf.str)) 143 | else: 144 | w("Could not find a register or stkvar in the current operand") 145 | else: 146 | w("Please position the cursor on an instruction") 147 | else: 148 | w("Please position the cursor within a function") 149 | else: 150 | print('vds12: Hex-rays is not available.') 151 | 152 | -------------------------------------------------------------------------------- /D810_demo/d810/errors.py: -------------------------------------------------------------------------------- 1 | class D810Exception(Exception): 2 | pass 3 | 4 | 5 | class AstException(D810Exception): 6 | pass 7 | 8 | 9 | class AstEvaluationException(AstException): 10 | pass 11 | 12 | 13 | class D810Z3Exception(D810Exception): 14 | pass 15 | 16 | 17 | class ControlFlowException(D810Exception): 18 | pass 19 | 20 | 21 | class EmulationException(D810Exception): 22 | pass 23 | 24 | 25 | class EmulationIndirectJumpException(EmulationException): 26 | def __init__(self, message, dest_ea, dest_serial_list): 27 | super().__init__(message) 28 | self.dest_ea = dest_ea 29 | self.dest_serial_list = dest_serial_list 30 | 31 | 32 | class UnresolvedMopException(EmulationException): 33 | pass 34 | 35 | 36 | class WritableMemoryReadException(EmulationException): 37 | pass 38 | 39 | 40 | class UnsupportedInstructionException(EmulationException): 41 | pass 42 | -------------------------------------------------------------------------------- /D810_demo/d810/generic.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from typing import List 3 | 4 | from d810.hexrays_formatters import format_minsn_t, format_mop_list 5 | from d810.hexrays_helpers import append_mop_if_not_in_list, get_mop_index, CONDITIONAL_JUMP_OPCODES, extract_num_mop 6 | from d810.hexrays_hooks import InstructionDefUseCollector 7 | from d810.tracker import remove_segment_registers 8 | from ida_hexrays import mop_t, minsn_t 9 | 10 | unflat_logger = logging.getLogger('D810.unflat') 11 | 12 | 13 | class GenericDispatcherBlockInfo(object): 14 | 15 | def __init__(self, blk, father=None): 16 | self.blk = blk 17 | self.ins = [] 18 | self.use_list = [] 19 | self.use_before_def_list = [] 20 | self.def_list = [] 21 | self.assume_def_list = [] 22 | self.comparison_value = None 23 | self.compared_mop = None 24 | 25 | self.father = None 26 | if father is not None: 27 | self.register_father(father) 28 | 29 | @property 30 | def serial(self) -> int: 31 | return self.blk.serial 32 | 33 | def register_father(self, father: 'GenericDispatcherBlockInfo'): 34 | self.father = father 35 | self.assume_def_list = [x for x in father.assume_def_list] 36 | 37 | def update_use_def_lists(self, ins_mops_used: List[mop_t], ins_mops_def: List[mop_t]): 38 | for mop_used in ins_mops_used: 39 | append_mop_if_not_in_list(mop_used, self.use_list) 40 | mop_used_index = get_mop_index(mop_used, self.def_list) 41 | if mop_used_index == -1: 42 | append_mop_if_not_in_list(mop_used, self.use_before_def_list) 43 | for mop_def in ins_mops_def: 44 | append_mop_if_not_in_list(mop_def, self.def_list) 45 | 46 | def update_with_ins(self, ins: minsn_t): 47 | ins_mop_info = InstructionDefUseCollector() 48 | ins.for_all_ops(ins_mop_info) 49 | cleaned_unresolved_ins_mops = remove_segment_registers(ins_mop_info.unresolved_ins_mops) 50 | self.update_use_def_lists(cleaned_unresolved_ins_mops + ins_mop_info.memory_unresolved_ins_mops, 51 | ins_mop_info.target_mops) 52 | self.ins.append(ins) 53 | if ins.opcode in CONDITIONAL_JUMP_OPCODES: 54 | num_mop, other_mop = extract_num_mop(ins) 55 | if num_mop is not None: 56 | self.comparison_value = num_mop.nnn.value 57 | self.compared_mop = other_mop 58 | 59 | def parse(self): 60 | curins = self.blk.head 61 | while curins is not None: 62 | self.update_with_ins(curins) 63 | curins = curins.next 64 | for mop_def in self.def_list: 65 | append_mop_if_not_in_list(mop_def, self.assume_def_list) 66 | 67 | def does_only_need(self, prerequisite_mop_list: List[mop_t]) -> bool: 68 | for used_before_def_mop in self.use_before_def_list: 69 | mop_index = get_mop_index(used_before_def_mop, prerequisite_mop_list) 70 | if mop_index == -1: 71 | return False 72 | return True 73 | 74 | def recursive_get_father(self) -> List[GenericDispatcherBlockInfo]: 75 | if self.father is None: 76 | return [self] 77 | else: 78 | return self.father.recursive_get_father() + [self] 79 | 80 | def show_history(self): 81 | full_father_list = self.recursive_get_father() 82 | unflat_logger.info(" Show history of Block {0}".format(self.blk.serial)) 83 | for father in full_father_list[:-1]: 84 | for ins in father.ins: 85 | unflat_logger.info(" {0}.{1}".format(father.blk.serial, format_minsn_t(ins))) 86 | 87 | def print_info(self): 88 | unflat_logger.info("Block {0} information:".format(self.blk.serial)) 89 | unflat_logger.info(" USE list: {0}".format(format_mop_list(self.use_list))) 90 | unflat_logger.info(" DEF list: {0}".format(format_mop_list(self.def_list))) 91 | unflat_logger.info(" USE BEFORE DEF list: {0}".format(format_mop_list(self.use_before_def_list))) 92 | unflat_logger.info(" ASSUME DEF list: {0}".format(format_mop_list(self.assume_def_list))) 93 | 94 | -------------------------------------------------------------------------------- /D810_demo/d810/hexrays_formatters.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | from typing import List 4 | 5 | from d810.hexrays_helpers import OPCODES_INFO, MATURITY_TO_STRING_DICT, STRING_TO_MATURITY_DICT, MOP_TYPE_TO_STRING_DICT 6 | from ida_hexrays import minsn_t, mop_t, vd_printer_t, mbl_array_t 7 | 8 | 9 | logger = logging.getLogger('D810.helper') 10 | 11 | 12 | def format_minsn_t(ins: minsn_t) -> str: 13 | if ins is None: 14 | return "minsn_t is None" 15 | 16 | tmp = ins._print() 17 | pp_ins = "".join([c if 0x20 <= ord(c) <= 0x7e else "" for c in tmp]) 18 | return pp_ins 19 | 20 | 21 | def format_mop_t(mop_in: mop_t) -> str: 22 | if mop_in is None: 23 | return "mop_t is None" 24 | if mop_in.t > 15: 25 | # To avoid error 50581 26 | return "Unknown mop type {0}".format(mop_in.t) 27 | return mop_in.dstr() 28 | 29 | 30 | def format_mop_list(mop_list: List[mop_t]) -> str: 31 | return ", ".join([format_mop_t(x) for x in mop_list]) 32 | 33 | 34 | def maturity_to_string(maturity_level: int) -> str: 35 | return MATURITY_TO_STRING_DICT.get(maturity_level, "Unknown maturity: {0}".format(maturity_level)) 36 | 37 | 38 | def string_to_maturity(maturity_string: str) -> int: 39 | return STRING_TO_MATURITY_DICT.get(maturity_string) 40 | 41 | 42 | def mop_type_to_string(mop_type: int) -> str: 43 | return MOP_TYPE_TO_STRING_DICT.get(mop_type, "Unknown mop type: {0}".format(mop_type)) 44 | 45 | 46 | def opcode_to_string(opcode) -> str: 47 | try: 48 | return OPCODES_INFO[opcode]["name"] 49 | except KeyError: 50 | return "Unknown opcode: {0}".format(opcode) 51 | 52 | 53 | class mba_printer(vd_printer_t): 54 | def __init__(self): 55 | vd_printer_t.__init__(self) 56 | self.mc = [] 57 | 58 | def get_mc(self): 59 | return self.mc 60 | 61 | def _print(self, indent, line): 62 | self.mc.append("".join([c if 0x20 <= ord(c) <= 0x7e else "" for c in line])+"\n") 63 | return 1 64 | 65 | 66 | class block_printer(vd_printer_t): 67 | def __init__(self): 68 | vd_printer_t.__init__(self) 69 | self.block_ins = [] 70 | 71 | def get_block_mc(self): 72 | return "\n".join(self.block_ins) 73 | 74 | def _print(self, indent, line): 75 | self.block_ins.append("".join([c if 0x20 <= ord(c) <= 0x7e else "" for c in line])) 76 | return 1 77 | 78 | 79 | def write_mc_to_file(mba: mbl_array_t, filename: str, mba_flags: int = 0) -> bool: 80 | if not mba: 81 | return False 82 | 83 | vp = mba_printer() 84 | mba.set_mba_flags(mba_flags) 85 | mba._print(vp) 86 | 87 | with open(filename, "w") as f: 88 | f.writelines(vp.get_mc()) 89 | return True 90 | 91 | 92 | def dump_microcode_for_debug(mba: mbl_array_t, log_dir_path: str, name: str = ""): 93 | mc_filename = os.path.join(log_dir_path, "{0:x}_maturity_{1}_{2}.log".format(mba.entry_ea, mba.maturity, name)) 94 | logger.info("Dumping microcode in file {0}...".format(mc_filename)) 95 | write_mc_to_file(mba, mc_filename) 96 | -------------------------------------------------------------------------------- /D810_demo/d810/hexrays_hooks.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from ida_hexrays import * 4 | from d810.hexrays_helpers import check_ins_mop_size_are_ok, append_mop_if_not_in_list 5 | from d810.hexrays_formatters import format_minsn_t, format_mop_t, maturity_to_string, mop_type_to_string, \ 6 | dump_microcode_for_debug 7 | 8 | helper_logger = logging.getLogger('D810.helper') 9 | 10 | 11 | class InstructionDefUseCollector(mop_visitor_t): 12 | def __init__(self): 13 | super().__init__() 14 | self.unresolved_ins_mops = [] 15 | self.memory_unresolved_ins_mops = [] 16 | self.target_mops = [] 17 | 18 | def visit_mop(self, op: mop_t, op_type: int, is_target: bool): 19 | if is_target: 20 | append_mop_if_not_in_list(op, self.target_mops) 21 | else: 22 | # TODO whatever the case, in the end we will always return 0. May be this code can be better optimized. 23 | # TODO handle other special case (e.g. ldx ins, ...) 24 | if op.t == mop_S: 25 | append_mop_if_not_in_list(op, self.unresolved_ins_mops) 26 | elif op.t == mop_r: 27 | append_mop_if_not_in_list(op, self.unresolved_ins_mops) 28 | elif op.t == mop_v: 29 | append_mop_if_not_in_list(op, self.memory_unresolved_ins_mops) 30 | elif op.t == mop_a: 31 | if op.a.t == mop_v: 32 | return 0 33 | elif op.a.t == mop_S: 34 | return 0 35 | helper_logger.warning("Calling visit_mop with unsupported mop type {0} - {1}: '{2}'" 36 | .format(mop_type_to_string(op.t), mop_type_to_string(op.a.t), format_mop_t(op))) 37 | return 0 38 | elif op.t == mop_n: 39 | return 0 40 | elif op.t == mop_d: 41 | return 0 42 | elif op.t == mop_h: 43 | return 0 44 | elif op.t == mop_b: 45 | return 0 46 | else: 47 | helper_logger.warning("Calling visit_mop with unsupported mop type {0}: '{1}'" 48 | .format(mop_type_to_string(op.t), format_mop_t(op))) 49 | return 0 50 | 51 | -------------------------------------------------------------------------------- /D810_demo/d810/utils.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | 3 | from d810.hexrays_helpers import MSB_TABLE 4 | 5 | CTYPE_SIGNED_TABLE = {1: ctypes.c_int8, 2: ctypes.c_int16, 4: ctypes.c_int32, 8: ctypes.c_int64} 6 | CTYPE_UNSIGNED_TABLE = {1: ctypes.c_uint8, 2: ctypes.c_uint16, 4: ctypes.c_uint32, 8: ctypes.c_uint64} 7 | 8 | 9 | def get_all_subclasses(python_class): 10 | python_class.__subclasses__() 11 | 12 | subclasses = set() 13 | check_these = [python_class] 14 | 15 | while check_these: 16 | parent = check_these.pop() 17 | for child in parent.__subclasses__(): 18 | if child not in subclasses: 19 | subclasses.add(child) 20 | check_these.append(child) 21 | 22 | return sorted(subclasses, key=lambda x: x.__name__) 23 | 24 | 25 | def unsigned_to_signed(unsigned_value, nb_bytes): 26 | return CTYPE_SIGNED_TABLE[nb_bytes](unsigned_value).value 27 | 28 | 29 | def signed_to_unsigned(signed_value, nb_bytes): 30 | return CTYPE_UNSIGNED_TABLE[nb_bytes](signed_value).value 31 | 32 | 33 | def get_msb(value, nb_bytes): 34 | return (value & MSB_TABLE[nb_bytes]) >> (nb_bytes * 8 - 1) 35 | 36 | 37 | def get_add_cf(op1, op2, nb_bytes): 38 | res = op1 + op2 39 | return get_msb((((op1 ^ op2) ^ res) ^ ((op1 ^ res) & (~(op1 ^ op2)))), nb_bytes) 40 | 41 | 42 | def get_add_of(op1, op2, nb_bytes): 43 | res = op1 + op2 44 | return get_msb(((op1 ^ res) & (~(op1 ^ op2))), nb_bytes) 45 | 46 | 47 | def get_sub_cf(op1, op2, nb_bytes): 48 | res = op1 - op2 49 | return get_msb((((op1 ^ op2) ^ res) ^ ((op1 ^ res) & (op1 ^ op2))), nb_bytes) 50 | 51 | 52 | def get_sub_of(op1, op2, nb_bytes): 53 | res = op1 - op2 54 | return get_msb(((op1 ^ res) & (op1 ^ op2)), nb_bytes) 55 | 56 | 57 | def get_parity_flag(op1, op2, nb_bytes): 58 | tmp = CTYPE_UNSIGNED_TABLE[nb_bytes](op1 - op2).value 59 | return (bin(tmp).count("1") + 1) % 2 60 | 61 | 62 | def ror(x, n, nb_bits=32): 63 | mask = (2 ** n) - 1 64 | mask_bits = x & mask 65 | return (x >> n) | (mask_bits << (nb_bits - n)) 66 | 67 | 68 | def rol(x, n, nb_bits=32): 69 | return ror(x, nb_bits - n, nb_bits) 70 | -------------------------------------------------------------------------------- /D810_demo/ida_cfg_graph.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | import ida_kernwin as kw 4 | import ida_funcs 5 | import ida_ida 6 | import ida_bytes 7 | import ida_hexrays as hr 8 | import ida_range 9 | import idaapi 10 | 11 | # ---------------------------------------------------------------------------- 12 | 13 | class MyGraph(idaapi.GraphViewer): 14 | def __init__(self, title, mba): 15 | idaapi.GraphViewer.__init__(self, title) 16 | self._mba = mba 17 | 18 | def OnRefresh(self): 19 | self.Clear() 20 | nodes = {} 21 | for blk_idx in range(self._mba.qty): 22 | blk = self._mba.get_mblock(blk_idx) 23 | if blk.head == None: 24 | continue 25 | lines = [] 26 | 27 | lines.append("{0}:{1}".format(blk_idx, hex(blk.head.ea))) 28 | insn = blk.head 29 | while insn: 30 | lines.append(insn.dstr()) 31 | if insn == blk.tail: 32 | break 33 | insn = insn.next 34 | label = "\n".join(lines) 35 | node_id = self.AddNode(label) 36 | nodes[blk_idx] = node_id 37 | 38 | for blk_idx in range(self._mba.qty): 39 | blk = self._mba.get_mblock(blk_idx) 40 | succset_list = [x for x in blk.succset] 41 | for succ in succset_list: 42 | blk_succ = self._mba.get_mblock(succ) 43 | if blk_succ.head == None: 44 | continue 45 | if blk.head == None: 46 | continue 47 | self.AddEdge(nodes[blk_idx], nodes[blk_succ.serial]) 48 | return True 49 | 50 | def OnGetText(self, node_id): 51 | return self[node_id] 52 | 53 | 54 | def ask_desired_maturity(): 55 | """Displays a dialog which lets the user choose a maturity level 56 | of the microcode to generate.""" 57 | maturity_levels = [ 58 | ["MMAT_GENERATED", hr.MMAT_GENERATED], 59 | ["MMAT_PREOPTIMIZED", hr.MMAT_PREOPTIMIZED], 60 | ["MMAT_LOCOPT", hr.MMAT_LOCOPT], 61 | ["MMAT_CALLS", hr.MMAT_CALLS], 62 | ["MMAT_GLBOPT1", hr.MMAT_GLBOPT1], 63 | ["MMAT_GLBOPT2", hr.MMAT_GLBOPT2], 64 | ["MMAT_GLBOPT3", hr.MMAT_GLBOPT3], 65 | ["MMAT_LVARS", hr.MMAT_LVARS]] 66 | 67 | class MaturityForm(kw.Form): 68 | def __init__(self): 69 | self.title = "Display Microcode" 70 | form = ("STARTITEM {id:mat_lvl}\n" 71 | "%s\n" 72 | " \n" 73 | "\n\n" 74 | "<##Options##Output includes comments:{flags_short}>{chkgroup_flags}>\n\n" % 75 | self.title) 76 | 77 | dropdown_ctl = kw.Form.DropdownListControl( 78 | [text for text, _ in maturity_levels]) 79 | chk_ctl = kw.Form.ChkGroupControl(("flags_short",)) 80 | 81 | controls = {"mat_lvl": dropdown_ctl, 82 | "chkgroup_flags": chk_ctl} 83 | 84 | kw.Form.__init__(self, form, controls) 85 | 86 | form = MaturityForm() 87 | form, args = form.Compile() 88 | form.flags_short.checked = True 89 | ok = form.Execute() 90 | 91 | mmat = None 92 | text = None 93 | flags = 0 94 | if ok == 1: 95 | text, mmat = maturity_levels[form.mat_lvl.value] 96 | flags |= 0 if form.flags_short.checked else hr.MBA_SHORT 97 | form.Free() 98 | return (text, mmat, flags) 99 | 100 | 101 | 102 | 103 | def show_microcode(): 104 | """Generates and displays microcode for an address range. 105 | An address range can be a selection of code or that of 106 | the current function.""" 107 | sel, sea, eea = kw.read_range_selection(None) 108 | pfn = ida_funcs.get_func(kw.get_screen_ea()) 109 | if not sel and not pfn: 110 | return (False, "Position cursor within a function or select range") 111 | 112 | if not sel and pfn: 113 | sea = pfn.start_ea 114 | eea = pfn.end_ea 115 | 116 | addr_fmt = "%016x" if ida_ida.inf_is_64bit() else "%08x" 117 | fn_name = (ida_funcs.get_func_name(pfn.start_ea) 118 | if pfn else "0x%s-0x%s" % (addr_fmt % sea, addr_fmt % eea)) 119 | 120 | F = ida_bytes.get_flags(sea) 121 | if not ida_bytes.is_code(F): 122 | return (False, "The selected range must start with an instruction") 123 | 124 | text, mmat, mba_flags = ask_desired_maturity() 125 | if text is None and mmat is None: 126 | return (True, "Cancelled") 127 | 128 | if not sel and pfn: 129 | mbr = hr.mba_ranges_t(pfn) 130 | else: 131 | mbr = hr.mba_ranges_t() 132 | mbr.ranges.push_back(ida_range.range_t(sea, eea)) 133 | 134 | hf = hr.hexrays_failure_t() 135 | ml = hr.mlist_t() 136 | mba = hr.gen_microcode(mbr, hf, ml, hr.DECOMP_WARNINGS, mmat) 137 | if not mba: 138 | return (False, "0x%s: %s" % (addr_fmt % hf.errea, hf.desc())) 139 | 140 | mba.set_mba_flags(mba.get_mba_flags() | mba_flags) 141 | 142 | title = "Fun microcode FlowChart" 143 | graph = MyGraph(title, mba) 144 | if not graph.Show(): 145 | print("Failed to display the graph") 146 | 147 | return (True, 148 | "Successfully generated microcode for 0x%s..0x%s" % (addr_fmt % sea, addr_fmt % eea)) 149 | 150 | 151 | 152 | if __name__ == '__main__': 153 | try: 154 | sys.setrecursionlimit(2000) 155 | show_microcode() 156 | except Exception as e: 157 | print(f"error: {e}") -------------------------------------------------------------------------------- /D810_demo/ida_view.py: -------------------------------------------------------------------------------- 1 | import ida_hexrays 2 | import ida_idaapi 3 | import idaapi 4 | import ida_kernwin 5 | import ida_funcs 6 | import ida_ua 7 | import ida_lines 8 | 9 | ACTION_SHORTCUT = "Ctrl+Shift+G" 10 | ACTION_NAME = "my:clearview" 11 | 12 | 13 | class display_graph_ah_t(ida_kernwin.action_handler_t): 14 | def __init__(self): 15 | ida_kernwin.action_handler_t.__init__(self) 16 | 17 | def activate(self, ctx): 18 | print("activate") 19 | return 0 20 | 21 | def update(self, ctx): 22 | print("update") 23 | return ida_kernwin.AST_ENABLE_ALWAYS 24 | 25 | 26 | 27 | class AsmView(ida_kernwin.simplecustviewer_t): 28 | def Create(self, title): 29 | if not super(AsmView, self).Create(title): 30 | return False 31 | self.SetContent() 32 | 33 | return True 34 | 35 | def SetContent(self): 36 | self.ClearLines() 37 | func = ida_funcs.get_func(idaapi.get_screen_ea()) 38 | if func: 39 | ea = func.start_ea 40 | while ea < func.end_ea: 41 | insn = ida_ua.insn_t() 42 | if ida_ua.decode_insn(insn, ea): 43 | line = f"{hex(ea)}: {ida_lines.generate_disasm_line(ea, 0)}" 44 | self.AddLine(line) 45 | ea += insn.size 46 | else: 47 | self.AddLine("No function selected or function is invalid.") 48 | self.Refresh() 49 | 50 | 51 | def OnPopup(self, form, popup_handle): # dynamic add popup 52 | action_clear = ida_kernwin.action_desc_t(None, 'Clear View', display_graph_ah_t()) 53 | ida_kernwin.attach_dynamic_action_to_popup(form, popup_handle, action_clear, None) 54 | 55 | 56 | def show_asm_view(): 57 | 58 | view = AsmView() 59 | if view.Create("Assembly View"): 60 | view.Show() 61 | 62 | # class my_plugin_t(ida_idaapi.plugin_t): 63 | # flags = ida_idaapi.PLUGIN_UNL 64 | # wanted_name = "ryruytyutyut" 65 | # wanted_hotkey = "Ctrl+Shift+G" 66 | # comment = "Sample plugin5 for Hex-Rays decompiler" 67 | # help = "" 68 | # def init(self): 69 | # if ida_hexrays.init_hexrays_plugin(): 70 | # # show_asm_view() 71 | # return ida_idaapi.PLUGIN_KEEP # keep us in the memory 72 | # def term(self): 73 | # pass 74 | # def run(self, arg): 75 | # pass 76 | # 77 | # def PLUGIN_ENTRY(): 78 | # return my_plugin_t() 79 | 80 | 81 | 82 | if __name__ == '__main__': 83 | 84 | show_asm_view() -------------------------------------------------------------------------------- /D810_demo/main.py: -------------------------------------------------------------------------------- 1 | # This is a sample Python script. 2 | 3 | # Press Shift+F10 to execute it or replace it with your code. 4 | # Press Double Shift to search everywhere for classes, files, tool windows, actions, and settings. 5 | 6 | 7 | 8 | class GenericDispatcherBlockInfo(object): 9 | 10 | def __init__(self, str): 11 | self.string = str 12 | 13 | def register_father(self, father: 'GenericDispatcherBlockInfo'): 14 | print(father.string) 15 | 16 | 17 | 18 | def print_hi(name): 19 | # Use a breakpoint in the code line below to debug your script. 20 | print(f'Hi, {name}') # Press Ctrl+F8 to toggle the breakpoint. 21 | 22 | 23 | # Press the green button in the gutter to run the script. 24 | if __name__ == '__main__': 25 | gen = GenericDispatcherBlockInfo("321321321") 26 | gen2 = GenericDispatcherBlockInfo("11111") 27 | gen2.register_father(gen); 28 | print_hi('PyCharm') 29 | 30 | # See PyCharm help at https://www.jetbrains.com/help/pycharm/ 31 | -------------------------------------------------------------------------------- /D810_demo/plug_menu.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thehepta/D810Learn/52ad29245037f279411a2ddd6b8b6e3f15e9bc9a/D810_demo/plug_menu.py -------------------------------------------------------------------------------- /D810_demo/trave_microcode.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | import ida_kernwin as kw 4 | import ida_funcs 5 | import ida_ida 6 | import ida_bytes 7 | import ida_hexrays as hr 8 | import ida_range 9 | 10 | # ----------------------------------------------------------------------------- 11 | class printer_t(hr.vd_printer_t): 12 | """Converts microcode output to an array of strings.""" 13 | def __init__(self, *args): 14 | hr.vd_printer_t.__init__(self) 15 | self.mc = [] 16 | 17 | def get_mc(self): 18 | return self.mc 19 | 20 | def _print(self, indent, line): 21 | self.mc.append(line) 22 | return 1 23 | # 24 | def ask_desired_maturity(): 25 | """Displays a dialog which lets the user choose a maturity level 26 | of the microcode to generate.""" 27 | maturity_levels = [ 28 | ["MMAT_GENERATED", hr.MMAT_GENERATED], 29 | ["MMAT_PREOPTIMIZED", hr.MMAT_PREOPTIMIZED], 30 | ["MMAT_LOCOPT", hr.MMAT_LOCOPT], 31 | ["MMAT_CALLS", hr.MMAT_CALLS], 32 | ["MMAT_GLBOPT1", hr.MMAT_GLBOPT1], 33 | ["MMAT_GLBOPT2", hr.MMAT_GLBOPT2], 34 | ["MMAT_GLBOPT3", hr.MMAT_GLBOPT3], 35 | ["MMAT_LVARS", hr.MMAT_LVARS]] 36 | 37 | class MaturityForm(kw.Form): 38 | def __init__(self): 39 | self.title = "Display Microcode" 40 | form = ("STARTITEM {id:mat_lvl}\n" 41 | "%s\n" 42 | " \n" 43 | "\n\n" 44 | "<##Options##Output includes comments:{flags_short}>{chkgroup_flags}>\n\n" % 45 | self.title) 46 | 47 | dropdown_ctl = kw.Form.DropdownListControl( 48 | [text for text, _ in maturity_levels]) 49 | chk_ctl = kw.Form.ChkGroupControl(("flags_short",)) 50 | 51 | controls = {"mat_lvl": dropdown_ctl, 52 | "chkgroup_flags": chk_ctl} 53 | 54 | kw.Form.__init__(self, form, controls) 55 | 56 | form = MaturityForm() 57 | form, args = form.Compile() 58 | form.flags_short.checked = True 59 | ok = form.Execute() 60 | 61 | mmat = None 62 | mmat_name = None 63 | flags = 0 64 | if ok == 1: 65 | mmat_name, mmat = maturity_levels[form.mat_lvl.value] 66 | flags |= 0 if form.flags_short.checked else hr.MBA_SHORT 67 | form.Free() 68 | return (mmat_name, mmat, flags) 69 | 70 | 71 | # ----------------------------------------------------------------------------- 72 | class microcode_viewer_t(kw.simplecustviewer_t): 73 | """Creates a widget that displays Hex-Rays microcode.""" 74 | 75 | def __init__(self): 76 | super().__init__() 77 | self.insn_map = {} # 用于存储行号到 minsn_t 对象的映射 78 | 79 | def Create(self, mba, title, mmat_name, fn_name,lines = []): 80 | self.title = "Microcode: %s" % title 81 | self.mba = mba 82 | self.mmat_name = mmat_name 83 | self.fn_name = fn_name 84 | if not kw.simplecustviewer_t.Create(self, self.title): 85 | return False 86 | self.lines = lines 87 | for line in lines: 88 | self.AddLine(line) 89 | return True 90 | 91 | # title = "Fun microcode FlowChart" 92 | # graph = MyGraph(title, self.mba) 93 | # if not graph.Show(): 94 | # print("Failed to display the graph") 95 | 96 | 97 | 98 | def show_microcode(): 99 | """Generates and displays microcode for an address range. 100 | An address range can be a selection of code or that of 101 | the current function.""" 102 | sel, sea, eea = kw.read_range_selection(None) 103 | # get_screen_ea 获取光标的地址 104 | # get_func 通过这个地址,获取当前地址所在的函数 105 | pfn = ida_funcs.get_func(kw.get_screen_ea()) 106 | if not sel and not pfn: 107 | return (False, "Position cursor within a function or select range") 108 | 109 | if not sel and pfn: 110 | # sea 函数起始地址 eea 函数结束地址 111 | sea = pfn.start_ea 112 | eea = pfn.end_ea 113 | 114 | addr_fmt = "%016x" if ida_ida.inf_is_64bit() else "%08x" 115 | # 获取函数名字 116 | fn_name = (ida_funcs.get_func_name(pfn.start_ea) 117 | if pfn else "0x%s-0x%s" % (addr_fmt % sea, addr_fmt % eea)) 118 | 119 | F = ida_bytes.get_flags(sea) 120 | if not ida_bytes.is_code(F): 121 | return (False, "The selected range must start with an instruction") 122 | 123 | mmat_name, mmat, mba_flags = ask_desired_maturity() 124 | if mmat_name is None and mmat is None: 125 | return (True, "Cancelled") 126 | 127 | if not sel and pfn: 128 | mbr = hr.mba_ranges_t(pfn) 129 | else: 130 | mbr = hr.mba_ranges_t() 131 | mbr.ranges.push_back(ida_range.range_t(sea, eea)) 132 | 133 | hf = hr.hexrays_failure_t() 134 | ml = hr.mlist_t() 135 | # 生产microcode 136 | mba = hr.gen_microcode(mbr, hf, ml, hr.DECOMP_WARNINGS, mmat) 137 | if not mba: 138 | return (False, "0x%s: %s" % (addr_fmt % hf.errea, hf.desc())) 139 | # mba 指令遍历回调接口 140 | vp = printer_t() 141 | mba.set_mba_flags(mba.get_mba_flags() | mba_flags) 142 | # 通过mba _print 进行指令遍历 143 | mba._print(vp) 144 | 145 | # ui类 146 | mcv = microcode_viewer_t() 147 | # vp.get_mc() 存储的是所有microcode的指令列表 148 | if not mcv.Create(mba, "%s (%s)" % (fn_name, mmat_name), mmat_name, fn_name,vp.get_mc()): 149 | return (False, "Error creating viewer") 150 | 151 | # 显示窗口 152 | mcv.Show() 153 | 154 | return (True, 155 | "Successfully generated microcode for 0x%s..0x%s" % (addr_fmt % sea, addr_fmt % eea)) 156 | 157 | 158 | 159 | if __name__ == '__main__': 160 | try: 161 | sys.setrecursionlimit(2000) 162 | show_microcode() 163 | except Exception as e: 164 | print(f"error: {e}") -------------------------------------------------------------------------------- /D810_demo/trave_microcode2.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | import ida_kernwin as kw 4 | import ida_funcs 5 | import ida_ida 6 | import ida_bytes 7 | import ida_hexrays as hr 8 | import ida_range 9 | 10 | # ---------------------------------------------------------------------------- 11 | 12 | def ask_desired_maturity(): 13 | """Displays a dialog which lets the user choose a maturity level 14 | of the microcode to generate.""" 15 | maturity_levels = [ 16 | ["MMAT_GENERATED", hr.MMAT_GENERATED], 17 | ["MMAT_PREOPTIMIZED", hr.MMAT_PREOPTIMIZED], 18 | ["MMAT_LOCOPT", hr.MMAT_LOCOPT], 19 | ["MMAT_CALLS", hr.MMAT_CALLS], 20 | ["MMAT_GLBOPT1", hr.MMAT_GLBOPT1], 21 | ["MMAT_GLBOPT2", hr.MMAT_GLBOPT2], 22 | ["MMAT_GLBOPT3", hr.MMAT_GLBOPT3], 23 | ["MMAT_LVARS", hr.MMAT_LVARS]] 24 | 25 | class MaturityForm(kw.Form): 26 | def __init__(self): 27 | self.title = "Display Microcode" 28 | form = ("STARTITEM {id:mat_lvl}\n" 29 | "%s\n" 30 | " \n" 31 | "\n\n" 32 | "<##Options##Output includes comments:{flags_short}>{chkgroup_flags}>\n\n" % 33 | self.title) 34 | 35 | dropdown_ctl = kw.Form.DropdownListControl( 36 | [text for text, _ in maturity_levels]) 37 | chk_ctl = kw.Form.ChkGroupControl(("flags_short",)) 38 | 39 | controls = {"mat_lvl": dropdown_ctl, 40 | "chkgroup_flags": chk_ctl} 41 | 42 | kw.Form.__init__(self, form, controls) 43 | 44 | form = MaturityForm() 45 | form, args = form.Compile() 46 | form.flags_short.checked = True 47 | ok = form.Execute() 48 | 49 | mmat = None 50 | text = None 51 | flags = 0 52 | if ok == 1: 53 | text, mmat = maturity_levels[form.mat_lvl.value] 54 | flags |= 0 if form.flags_short.checked else hr.MBA_SHORT 55 | form.Free() 56 | return (text, mmat, flags) 57 | 58 | 59 | # ----------------------------------------------------------------------------- 60 | class microcode_viewer_t(kw.simplecustviewer_t): 61 | """Creates a widget that displays Hex-Rays microcode.""" 62 | 63 | def __init__(self): 64 | super().__init__() 65 | self.insn_map = {} # 用于存储行号到 minsn_t 对象的映射 66 | 67 | def Create(self, mba, title, mmat_name, fn_name): 68 | # import pydevd_pycharm 69 | # pydevd_pycharm.settrace('localhost', port=31235, stdoutToServer=True, stderrToServer=True) 70 | self.title = "Microcode: %s" % title 71 | self.mba = mba 72 | self.mmat_name = mmat_name 73 | self.fn_name = fn_name 74 | nodes = {} 75 | if not kw.simplecustviewer_t.Create(self, self.title): 76 | return False 77 | for blk_idx in range(self.mba.qty): 78 | blk = self.mba.get_mblock(blk_idx) 79 | if blk.head == None: 80 | continue 81 | insn = blk.head 82 | while insn: 83 | self.AddLine(insn.dstr()) 84 | if insn == blk.tail: 85 | break 86 | insn = insn.next 87 | 88 | return True 89 | 90 | # self.lines = lines 91 | # for line in lines: 92 | # self.AddLine(line) 93 | # return True 94 | 95 | # title = "Fun microcode FlowChart" 96 | # graph = MyGraph(title, self.mba) 97 | # if not graph.Show(): 98 | # print("Failed to display the graph") 99 | 100 | 101 | 102 | def show_microcode(): 103 | """Generates and displays microcode for an address range. 104 | An address range can be a selection of code or that of 105 | the current function.""" 106 | sel, sea, eea = kw.read_range_selection(None) 107 | pfn = ida_funcs.get_func(kw.get_screen_ea()) 108 | if not sel and not pfn: 109 | return (False, "Position cursor within a function or select range") 110 | 111 | if not sel and pfn: 112 | sea = pfn.start_ea 113 | eea = pfn.end_ea 114 | 115 | addr_fmt = "%016x" if ida_ida.inf_is_64bit() else "%08x" 116 | fn_name = (ida_funcs.get_func_name(pfn.start_ea) 117 | if pfn else "0x%s-0x%s" % (addr_fmt % sea, addr_fmt % eea)) 118 | 119 | F = ida_bytes.get_flags(sea) 120 | if not ida_bytes.is_code(F): 121 | return (False, "The selected range must start with an instruction") 122 | 123 | text, mmat, mba_flags = ask_desired_maturity() 124 | if text is None and mmat is None: 125 | return (True, "Cancelled") 126 | 127 | if not sel and pfn: 128 | mbr = hr.mba_ranges_t(pfn) 129 | else: 130 | mbr = hr.mba_ranges_t() 131 | mbr.ranges.push_back(ida_range.range_t(sea, eea)) 132 | 133 | hf = hr.hexrays_failure_t() 134 | ml = hr.mlist_t() 135 | mba = hr.gen_microcode(mbr, hf, ml, hr.DECOMP_WARNINGS, mmat) 136 | if not mba: 137 | return (False, "0x%s: %s" % (addr_fmt % hf.errea, hf.desc())) 138 | 139 | mba.set_mba_flags(mba.get_mba_flags() | mba_flags) 140 | 141 | mcv = microcode_viewer_t() 142 | if not mcv.Create(mba, "%s (%s)" % (fn_name, text), text, fn_name): 143 | return (False, "Error creating viewer") 144 | 145 | mcv.Show() 146 | 147 | return (True, 148 | "Successfully generated microcode for 0x%s..0x%s" % (addr_fmt % sea, addr_fmt % eea)) 149 | 150 | 151 | 152 | if __name__ == '__main__': 153 | try: 154 | sys.setrecursionlimit(2000) 155 | show_microcode() 156 | except Exception as e: 157 | print(f"error: {e}") -------------------------------------------------------------------------------- /D810_demo/utils.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | 4 | tracker_logger = logging.getLogger('D810.tracker') 5 | emulator_logger = logging.getLogger('D810.emulator') 6 | 7 | 8 | class UnflatteningException(Exception): 9 | pass 10 | 11 | 12 | class DispatcherUnflatteningException(UnflatteningException): 13 | pass 14 | 15 | 16 | class NotDuplicableFatherException(UnflatteningException): 17 | pass 18 | 19 | 20 | class NotResolvableFatherException(UnflatteningException): 21 | pass 22 | 23 | 24 | 25 | 26 | def configure_mop_tracker_log_verbosity(verbose=False): 27 | tracker_log_level = tracker_logger.getEffectiveLevel() 28 | emulator_log_level = emulator_logger.getEffectiveLevel() 29 | if not verbose: 30 | tracker_logger.setLevel(logging.ERROR) 31 | emulator_logger.setLevel(logging.ERROR) 32 | return [tracker_log_level, emulator_log_level] 33 | 34 | 35 | def restore_mop_tracker_log_verbosity(tracker_log_level, emulator_log_level): 36 | tracker_logger.setLevel(tracker_log_level) 37 | emulator_logger.setLevel(emulator_log_level) 38 | 39 | 40 | def get_all_possibles_values(mop_histories, searched_mop_list, verbose=False): 41 | log_levels = configure_mop_tracker_log_verbosity(verbose) 42 | mop_cst_values_list = [] 43 | for mop_history in mop_histories: 44 | mop_cst_values_list.append([mop_history.get_mop_constant_value(searched_mop) 45 | for searched_mop in searched_mop_list]) 46 | restore_mop_tracker_log_verbosity(*log_levels) 47 | return mop_cst_values_list 48 | 49 | 50 | def check_if_all_values_are_found(mop_cst_values_list): 51 | all_values_are_found = True 52 | for cst_list in mop_cst_values_list: 53 | if None in cst_list: 54 | all_values_are_found = False 55 | break 56 | return all_values_are_found 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thehepta/D810Learn/52ad29245037f279411a2ddd6b8b6e3f15e9bc9a/README.md -------------------------------------------------------------------------------- /demo/1.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thehepta/D810Learn/52ad29245037f279411a2ddd6b8b6e3f15e9bc9a/demo/1.so -------------------------------------------------------------------------------- /demo/2.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thehepta/D810Learn/52ad29245037f279411a2ddd6b8b6e3f15e9bc9a/demo/2.so -------------------------------------------------------------------------------- /demo/d810_samples/README.md: -------------------------------------------------------------------------------- 1 | # Samples Description 2 | 3 | Samples used to showcase D-810: 4 | * *anel.zip*: ANEL malware sample (password: infected) 5 | * Original sample can be found [here](https://github.com/malrev/ABD) 6 | * Detailled analysis can be found in [VMware Carbon Black blog post](https://www.carbonblack.com/blog/defeating-compiler-level-obfuscations-used-in-apt10-malware/) 7 | * In D-810, use the *example_anel.json* configuration 8 | * Obfuscation: O-LLVM 9 | * [Control Flow flattening](https://github.com/obfuscator-llvm/obfuscator/wiki/Control-Flow-Flattening) 10 | * [Bogus Control Flow](https://github.com/obfuscator-llvm/obfuscator/wiki/Bogus-Control-Flow) 11 | * [Instruction Substitutions](https://github.com/obfuscator-llvm/obfuscator/wiki/Instructions-Substitution) 12 | 13 | * *blog_instruction_obfuscation_sub1.bin*: Sample used in [eShard blog post](https://eshard.com/posts/d810_blog_post_1/) 14 | * In D-810, use the *default_instruction_only.json* configuration 15 | * Obfuscation: O-LLVM and manual 16 | * [Instruction Substitutions](https://github.com/obfuscator-llvm/obfuscator/wiki/Instructions-Substitution) with 1 pass 17 | * Constant obfuscation 18 | * Opaque predicate 19 | 20 | * *blog_instruction_obfuscation_sub2.bin*: Sample used in [eShard blog post](https://eshard.com/posts/d810_blog_post_1/) 21 | * Same as *blog_instruction_obfuscation_sub1.bin* but with more O-LLVM passes. 22 | * In D-810, use the *default_instruction_only.json* configuration 23 | * Obfuscation: O-LLVM and manual 24 | * [Instruction Substitutions](https://github.com/obfuscator-llvm/obfuscator/wiki/Instructions-Substitution) with 2 passes 25 | * Constant obfuscation 26 | * Opaque predicate 27 | 28 | * *qsynth_dataset_custom_ea.bin*: Sample created from [QSynth dataset](https://github.com/werew/qsynth-artifacts/) 29 | * Randomly generated function obfuscated with Tigress 30 | * In D-810, use the *default_instruction_only.json* configuration 31 | * Obfuscation: Tigress 32 | * [EncodeArithmetic](https://tigress.wtf/encodeArithmetic.html) 33 | 34 | * *libobfuscated.so*: Sample used to showcase control flow unflatening using multiple techniques 35 | * In D-810, use the *example_libobfuscated.json* configuration 36 | * Obfuscation: Tigress 37 | * [EncodeArithmetic](https://tigress.wtf/encodeArithmetic.html) 38 | * [TigressFlatten](https://tigress.wtf/flatten.html) with *switch* and *indirect* dispatcher 39 | * Obfuscation: O-LLVM 40 | * [Control Flow flattening](https://github.com/obfuscator-llvm/obfuscator/wiki/Control-Flow-Flattening) 41 | * [Bogus Control Flow](https://github.com/obfuscator-llvm/obfuscator/wiki/Bogus-Control-Flow) 42 | * [Instruction Substitutions](https://github.com/obfuscator-llvm/obfuscator/wiki/Instructions-Substitution) 43 | 44 | -------------------------------------------------------------------------------- /demo/d810_samples/binaries/anel.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thehepta/D810Learn/52ad29245037f279411a2ddd6b8b6e3f15e9bc9a/demo/d810_samples/binaries/anel.zip -------------------------------------------------------------------------------- /demo/d810_samples/binaries/blog_instruction_obfuscation_sub1.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thehepta/D810Learn/52ad29245037f279411a2ddd6b8b6e3f15e9bc9a/demo/d810_samples/binaries/blog_instruction_obfuscation_sub1.bin -------------------------------------------------------------------------------- /demo/d810_samples/binaries/blog_instruction_obfuscation_sub2.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thehepta/D810Learn/52ad29245037f279411a2ddd6b8b6e3f15e9bc9a/demo/d810_samples/binaries/blog_instruction_obfuscation_sub2.bin -------------------------------------------------------------------------------- /demo/d810_samples/binaries/libobfuscated.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thehepta/D810Learn/52ad29245037f279411a2ddd6b8b6e3f15e9bc9a/demo/d810_samples/binaries/libobfuscated.so -------------------------------------------------------------------------------- /demo/d810_samples/binaries/qsynth_dataset_custom_ea.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thehepta/D810Learn/52ad29245037f279411a2ddd6b8b6e3f15e9bc9a/demo/d810_samples/binaries/qsynth_dataset_custom_ea.bin -------------------------------------------------------------------------------- /demo/d810_samples/src/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc # C compiler 2 | CFLAGS = -fPIC -Wall -Wextra -Og -g # C flags 3 | LDFLAGS = -shared # linking flags 4 | RM = rm -f # rm command 5 | TARGET_LIB = libobfuscated.so # target lib 6 | TARGET_LIB_IDA = libobfuscated.so.i64 # target lib 7 | 8 | SRCS = blog_manual_obfuscated.c blog_tigress_obfuscated.c blog_manual_ollvm.c # source files 9 | OBJS = $(SRCS:.c=.o) 10 | 11 | .PHONY: all 12 | all: ${TARGET_LIB} 13 | 14 | $(TARGET_LIB): $(OBJS) 15 | $(CC) ${LDFLAGS} -s -o $@ $^ 16 | 17 | $(SRCS:.c=.d):%.d:%.c 18 | $(CC) $(CFLAGS) -MM $< >$@ 19 | 20 | include $(SRCS:.c=.d) 21 | 22 | .PHONY: clean 23 | clean: 24 | -${RM} ${TARGET_LIB} ${TARGET_LIB_IDA} ${OBJS} $(SRCS:.c=.d) 25 | -------------------------------------------------------------------------------- /demo/d810_samples/src/blog_manual_obfuscated.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | int test_chained_add(int *a){ 4 | return (((a[0] + 23) + a[2]) - (-a[1] + (-17 - (12 + ((a[1] - a[0]) + ~a[2]))))); 5 | } 6 | 7 | int test_cst_simplification(int *a){ 8 | int a1 = ((a[0] & 0x3) | 0x222E69C2) - ((a[0] & 0x3) | 0x2); 9 | a[1] = a1; 10 | int a2 = ((a[1] & 0x50211120) | 0x83020001) + ( (a[1] & 0x50211120) ^ 0x50295930); 11 | a[2] = a2; 12 | int a3 = (((~a[2] & 0x10500855) | 0x5204000) + ((a[2] & 0x10500855) | 0x2009500)) ^ 0x15482637; 13 | a[3] = a3; 14 | int a4 = ((((a[3] + 0x4) - (a3 | 0x4)) & 0x7FFFFC) >> 2) | 0xA29; 15 | a[4] = a4; 16 | return a1 + a2 + a3 + a4; 17 | } 18 | 19 | int test_opaque_predicate(volatile int *a) { 20 | if ((a[0] * (a[0] + 1 )) % 2 != 0){ 21 | return 91; 22 | } 23 | int a1 = (int) ((a[1] * (a[1] - 1)) % 2 == 0); 24 | int a2 = (int) (((a[1]& a[2]) | (~a[1] & ~a[2])) != ~(a[1] ^ a[2])); 25 | int a3 = (int) ( ((a[3] | a[4]) - (a[3] & a[4])) != (a[3] ^ a[4]) ); 26 | int a4 = (int) ((a[4] & 0x23) == 0x1); 27 | int a5 = (int) ((a[6] & 0x42) != 0x2); 28 | a[1] = a1; 29 | a[2] = a2; 30 | a[3] = a3; 31 | a[4] = a4; 32 | a[5] = a5; 33 | return 12 + 3 * a1 + 5 * a2 + 7 * a3 + 9 * a4 + 11 * a5; 34 | } 35 | 36 | long test_xor(long a, long b, long c, long *d) { 37 | d[0] = (a + b) - 2 * (a & b); 38 | d[1] = (a * c + (b - 3)) - 2 * ((a * c) & (b - 3)); 39 | return d[0] + d[1]; 40 | } 41 | 42 | long test_mba_guessing(long a, long b, long c, long d) 43 | { 44 | return (((((~ (((a ^ ~ d) + ((a | d) + (a | d))) + 1) | a) + (((a ^ ~ d) + ((a | d) + (a | d))) + 1)) + 1) - ((a ^ c) + ((a & c) + (a & c)))) - (((((~ (((a ^ ~ d) + ((a | d) + (a | d))) + 1) | a) + (((a ^ ~ d) + ((a | d) + (a | d))) + 1)) + 1) | ~ ((a ^ c) + ((a & c) + (a & c)))) + ((((~ (((a ^ ~ d) + ((a | d) + (a | d))) + 1) | a) + (((a ^ ~ d) + ((a | d) + (a | d))) + 1)) + 1) | ~ ((a ^ c) + ((a & c) + (a & c)))))) - 2; 45 | } -------------------------------------------------------------------------------- /demo/hello_ollvm_bcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thehepta/D810Learn/52ad29245037f279411a2ddd6b8b6e3f15e9bc9a/demo/hello_ollvm_bcf -------------------------------------------------------------------------------- /demo/hello_ollvm_bcf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thehepta/D810Learn/52ad29245037f279411a2ddd6b8b6e3f15e9bc9a/demo/hello_ollvm_bcf2 -------------------------------------------------------------------------------- /demo/hello_ollvm_fla: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thehepta/D810Learn/52ad29245037f279411a2ddd6b8b6e3f15e9bc9a/demo/hello_ollvm_fla -------------------------------------------------------------------------------- /demo/hello_ollvm_fla_split: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thehepta/D810Learn/52ad29245037f279411a2ddd6b8b6e3f15e9bc9a/demo/hello_ollvm_fla_split -------------------------------------------------------------------------------- /doc/1.md: -------------------------------------------------------------------------------- 1 | 如果使用d810 进行反编译优化,需要在d810中启用 unflattener 和 UnflattenerFakeJump 2 | un_1.py 直接脚本运行显示1.so的fla需要进行patch的信息,辅助进行反编译优化 3 | ``` 4 | 5 | ``` -------------------------------------------------------------------------------- /doc/D810.md: -------------------------------------------------------------------------------- 1 | 2 | ### d810组件 3 | 4 | + microcode 仿真执行 5 | 6 | + MopTracker 向后变量跟踪 7 | 8 | + Microcode control flow patching 微码控制流修补 9 | 首先获取所有的ollvm 分发器对应的分支,然后计算出对应分支的所有块,分支的计算从分发器修改的位置开始计算,不是从块起始位置计算。 10 | 在这些分支块中进行计算,找到块有两个以上前驱,并且这个两个前驱到这个块本身,会有两个分发值,这说明这个块需要复制,不是有两个以上前驱的一定需要复制,经常有的块 11 | 有好几个前驱,但是块本身会使用常量对分发器赋值,这种是不需要赋值的,因为分支的起始位置起始就是块本身。 12 | 13 | ### fla对抗工作流程 14 | 15 | + 识别分发器 16 | + 寻找的分发分支以及对应的 常量值 17 | + 分析常量值和对应的先后执行顺序 18 | + 修复代码块 19 | 20 | 21 | ### bcf 对抗 22 | 23 | 通过真实执行或者类似真实执行的分析可以识别出那些是虚假块,可能会遇到不透明谓词的分支跳转,或者花指令,混淆 24 | 25 | ### 混淆,指令替换 26 | 主要是不透明谓词,混淆,需要人工,或者半人工,配合一下自动化优化工具进行反向指令替换 -------------------------------------------------------------------------------- /doc/d810_bcf_microcode_2: -------------------------------------------------------------------------------- 1 | #d810 优化以后的微码内容 2 | 3 | ; 1WAY-BLOCK 0 FAKE [START=00401130 END=00401130] STK=48/ARG=250, MAXBSP: 0 4 | ; - OUTBOUND: [1] 5 | 6 | ; 1WAY-BLOCK 1 [START=00401130 END=00401174] STK=48/ARG=250, MAXBSP: 40 7 | ; - INBOUND: [0] OUTBOUND: [2] 8 | 1. 0 mov edi.4{1}, %var_C.4{1} ; 00401167 9 | 1. 1 mov rsi.8{2}, %var_18.8{2} ; 0040116A 10 | 11 | ; 1WAY-BLOCK 2 PROP [START=00401179 END=004011CA] STK=0/ARG=250, MAXBSP: 0 12 | ; - INBOUND: [1] OUTBOUND: [3] 13 | 2. 0 stx #0.4, ds.2{3}, (&(%0x0).8-#0x10.8){4} ; 004011A1 14 | 2. 1 stx %var_C.4, ds.2{3}, (&(%0x0).8-#0x10.8){4} ; 004011AA 15 | 2. 2 stx %var_18.8, ds.2{3}, (&(%0x0).8-#0x10.8){4} ; 004011B0 16 | 2. 3 ldx ds.2{3}, ([ds.2{3}:(&(%0x0{5}).8-#0x10.8){4}].8+#8.8), rdi.8{6} ; 004011BA 17 | 2. 4 sub &(%0x0{5}).8, #0x10.8, %var_20.8 ; 004011BD 18 | 2. 5 sub &(%0x0).8, #0x10.8, %var_28.8 ; 004011C1 19 | 2. 6 mov call $".atoi".4, eax.4{7} ; 004011C5 20 | 21 | ; 1WAY-BLOCK 3 [START=004011CA END=00401213] STK=0/ARG=250, MAXBSP: 0 22 | ; - INBOUND: [2] OUTBOUND: [4] 23 | 3. 0 mov %var_28.8{8}, rcx.8{8} ; 004011CA 24 | 3. 1 stx eax.4{7}, ds.2{9}, %var_28.8{8} ; 004011CE 25 | 3. 2 setz [ds.2{9}:rcx.8{8}].4, #0.4, %var_29.1 ; 00401209 26 | 27 | ; 2WAY-BLOCK 4 [START=00401218 END=00401223] STK=0/ARG=250, MAXBSP: 0 28 | ; - INBOUND: [3] OUTBOUND: [5, 6] 29 | 4. 0 jz (%var_29.1 & #1.1), #0.1, @6 ; 0040121D 30 | 31 | ; 1WAY-BLOCK 5 [START=00401261 END=0040129F] STK=0/ARG=250, MAXBSP: 0 32 | ; - INBOUND: [4] OUTBOUND: [7] 33 | 5. 0 stx #1.4, ds.2, %var_20.8 ; 00401265 34 | 5. 1 goto @7 ; 00401299 35 | 36 | ; 1WAY-BLOCK 6 [START=004012A9 END=004012B3] STK=0/ARG=250, MAXBSP: 0 37 | ; - INBOUND: [4] OUTBOUND: [7] 38 | 6. 0 stx #0xA.4, ds.2, %var_20.8 ; 004012AD 39 | 40 | ; 1WAY-BLOCK 7 [START=004012B3 END=004012C2] STK=0/ARG=250, MAXBSP: 48 41 | ; - INBOUND: [6, 5] OUTBOUND: [8] 42 | 7. 0 ldx ds.2, %var_20.8, eax.4 ; 004012B9 43 | 44 | ; STOP-BLOCK 8 FAKE [START=FFFFFFFFFFFFFFFF END=FFFFFFFFFFFFFFFF] STK=48/ARG=250, MAXBSP: 0 45 | ; - INBOUND: [7] -------------------------------------------------------------------------------- /doc/explanation of terms.md: -------------------------------------------------------------------------------- 1 | 2 | ### 分发器 3 | 4 | ### 预言 5 | 6 | ### 分发终止 7 | 8 | ### ret块 9 | 10 | ### 分发目的块(真是块) 11 | 分发器的前驱,有很多个前驱 12 | 13 | ### 分发分支 14 | 15 | ### 分发指令 16 | 17 | + microcode 仿真执行 18 | 19 | + MopTracker 向后变量跟踪 20 | 21 | + Microcode control flow patching 微码控制流修补 22 | 首先获取所有的ollvm 分发器对应的分支,然后计算出对应分支的所有块,从分发器修改的位置开始计算,不是从块起始位置计算。 23 | 在这些分支块中进行计算,找到块有两个以上前驱,并且这个两个前驱到这个块本身,会有两个分发值,这说明这个块需要复制,不是有两个以上前驱的一定需要复制,经常有的块 24 | 有好几个前驱,但是块本身会使用常量对分发器赋值,这种是不需要赋值的,因为分支的起始位置起始就是块本身。 25 | 26 | ### 支配算法和树的深度遍历 27 | 28 | ### ssa 和 dag 代码优化活性分析 29 | 30 | ### 代码流程图cfg建图逻辑和正确性说明,以及,cfg 代码流程修改 31 | 32 | -------------------------------------------------------------------------------- /doc/ida microcode Overview.md: -------------------------------------------------------------------------------- 1 | ### HexRays SDK 2 | HexRays SDK 在反汇编器里的二进制代码有2种表达形式: 3 | 4 | + microcode: 机器码先被转换成微码(类似LLVM IR等中间语言),反汇编器会对它进行反复优化和转换(译者注:这个过程和LLVM IR的opt类似,机器码一开始转成的微码是未被优化的,更接近于机器码的,然后通过多次的优化PASS过程,逐步转成优化过的微码,优化过后的微码则更接近于人写出来的源码) 5 | + ctree: ctree是建立在充分优化过的微码的基础上的,它将二进制代码用类似抽象语法树(AST)的形式来表达,其中是由一些类似C语言的语句(statements)和表达式(expressions)组成的。它可以直接用来打印并生成C代码的。 6 | ### Microcode 7 | microcode主要是通过下面的几个类来表示: 8 | 9 | + mba_t, 保存了关于反汇编代码的基础信息,以及所有基础块(Basic Block)的集合。 10 | + mblock_t, 一个基础块(Basic Block),包括了该基础块内所有指令(instructions)的列表。 11 | + minsn_t, 一个指令(instruction),包含了3个操作数(operands):left, right, destination。 12 | + mop_t, 一个操作数,根据它的类型可能包含不同的信息,例如可能是一个数字,寄存器,栈变量等。 13 | + mlist_t, 内存或寄存器地址的列表。它可以包含巨大的内存区间或很多个寄存器,这个类在反汇编器中被广泛的使用。它可以表示一个指令访问的一个地址的列表或者甚至表示一整个基础块。它也被很多函数作为其参数使用。例如,一个函数用来搜索一个指令引用到一个mlist_t的列表。 14 | 更多内容可参考 IDA插件开发2 - Microcode in pictures 。 15 | ### CTree 16 | CTree主要是通过下面的几个类来表示的: 17 | 18 | + cfunc_t 保存关于反汇编代码的基本信息,包含一个 mba_t 的指针,删除 cfunc_t 时也会删除 mba_t(尽管,反汇编器返回的是 cfuncptr_tr ,它是一个带引用计数的指针,会在没有外部引用时删除其下面所有包含的函数,cfunc_t 有一个函数体(body), 用 cinsn_t 对象来表示的反汇编的函数体。 19 | + cinsn_t 是一个C语句,它可以是一个复杂的语句,或其他任何合法的C语句(例如if,for,while,return,expression-statement等),根据它的不同类型它可以包含一些不同的指针指向额外的信息。例如if语句有一个指针指向 cif_t ,它内部保存着 if 条件,then 分支和一个可选的else分支。请注意尽管我们称 cinsn_t 为语句(statements),而不是指令(instructions),因为对于我们而言,指令是microcode的一部分,而不是ctree的概念。 20 | + cexpr_t 是一个C表达式,它通常是C语句的一部分,当需要的时候,cexpr_t 有一个type字段,它记录着表达式的类型。 21 | + citem_t 是 cinsn_t 和 cexpr_t 的父类,它包含一些通用的信息,例如地址,标签和操作符(opcode)。 22 | + cnumber_t 是一个常量64-bit数字。除了它的值之外,还包含了如何表达它的信息:十进制,十六进制,或一个常量符号(例如枚举符号),请注意在microcode中我们会使用另一个类( mnumber_t )来表达数字(m开头的都是microcode的,c开头的类都是ctree的)。 更多内容可参考 IDA插件开发3 - Hex-Rays Decompiler primer 23 | ### common 24 | 除此之外,还有一些类同时被microcode和ctree模块使用: 25 | 26 | + lvar_t ,是一个局部变量,可以表示一个栈或者寄存器变量。一个变量拥有一个名字,类型,位置等信息。在 mba->vars 中保存着一个变量的列表。 27 | + lvar_locator_t 保存着一个变量的位置 ( vdloc_t ) 和定义它的地址。 28 | + vdloc_t 描述一个变量的位置,例如一个寄存器的编号,一个栈的偏移,或在一个复杂的情形下可能是一个混合了寄存器和栈的位置。它和在IDA内使用的 argloc_t 很相似,argloc_t 和 vdloc_t 的不同在于: 29 | + vdloc_t 绝对不会使用 ARGLOG_REG2 30 | + vdloc_t 使用 micro 寄存器number,而不是 processor 寄存器number。 31 | + vdloc_t 中的栈偏移绝对不会是负数,而argloc_t则可能是负数。 32 | 上面这些就是在头文件中所有重要的类了,另外还有很多辅助的类,请查阅头文件来查看它们的定义。(译者注:请注意这里提到的这个hexrays.hpp头文件,它有一万多行代码!) 33 | 34 | 35 | ### ida py类信息 36 | https://hex-rays.com/products/decompiler/manual/sdk/annotated.shtml 37 | 38 | 39 | ### Hex-Rays Decompiler - User Manual 40 | https://hex-rays.com/products/decompiler/manual/ -------------------------------------------------------------------------------- /doc/ida microcode insn opcode list.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ``` 4 | m_nop: No operation. 5 | m_stx: Store value into memory. 6 | m_ldx: Load value from memory. 7 | m_ldc: Load constant value. 8 | m_mov: Move value from one location to another. 9 | m_neg: Negate (two's complement). 10 | m_lnot: Logical NOT. 11 | m_bnot: Bitwise NOT. 12 | m_xds: Sign-extend. 13 | m_xdu: Zero-extend. 14 | m_low: Extract low part of value. 15 | m_high: Extract high part of value. 16 | m_add: Add. 17 | m_sub: Subtract. 18 | m_mul: Multiply. 19 | m_udiv: Unsigned divide. 20 | m_sdiv: Signed divide. 21 | m_umod: Unsigned modulo. 22 | m_smod: Signed modulo. 23 | m_or: Bitwise OR. 24 | m_and: Bitwise AND. 25 | m_xor: Bitwise XOR. 26 | m_shl: Shift left. 27 | m_shr: Logical shift right. 28 | m_sar: Arithmetic shift right. 29 | m_cfadd: Add with carry flag. 30 | m_ofadd: Add with overflow flag. 31 | m_cfshl: Shift left with carry flag. 32 | m_cfshr: Shift right with carry flag. 33 | m_sets: Set on signed. 34 | m_seto: Set on overflow. 35 | m_setp: Set on parity. 36 | m_setnz: Set if not zero. 37 | m_setz: Set if zero. 38 | m_setae: Set if above or equal. 39 | m_setb: Set if below. 40 | m_seta: Set if above. 41 | m_setbe: Set if below or equal. 42 | m_setg: Set if greater. 43 | m_setge: Set if greater or equal. 44 | m_setl: Set if less. 45 | m_setle: Set if less or equal. 46 | m_jcnd: Conditional jump. 47 | m_jnz: Jump if not zero. 48 | m_jz: Jump if zero. 49 | m_jae: Jump if above or equal. 50 | m_jb: Jump if below. 51 | m_ja: Jump if above. 52 | m_jbe: Jump if below or equal. 53 | m_jg: Jump if greater. 54 | m_jge: Jump if greater or equal. 55 | m_jl: Jump if less. 56 | m_jle: Jump if less or equal. 57 | m_jtbl: Jump table. 58 | m_ijmp: Indirect jump. 59 | m_goto: Unconditional jump. 60 | m_call: Call subroutine. 61 | m_icall: Indirect call. 62 | m_ret: Return from subroutine. 63 | m_push: Push value onto stack. 64 | m_pop: Pop value from stack. 65 | m_und: Undefined instruction. 66 | m_ext: External function call. 67 | m_f2i: Convert float to integer. 68 | m_f2u: Convert float to unsigned integer. 69 | m_i2f: Convert integer to float. 70 | m_u2f: Convert unsigned integer to float. 71 | m_f2f: Convert float to float (different size). 72 | m_fneg: Negate float. 73 | m_fadd: Add floats. 74 | m_fsub: Subtract floats. 75 | m_fmul: Multiply floats. 76 | m_fdiv: Divide floats. 77 | ``` -------------------------------------------------------------------------------- /doc/ida microcode mop type.md: -------------------------------------------------------------------------------- 1 | ``` 2 | mop_z = cvar.mop_z 3 | r""" 4 | none 5 | """ 6 | mop_r = cvar.mop_r 7 | r""" 8 | register (they exist until MMAT_LVARS) 9 | """ 10 | mop_n = cvar.mop_n 11 | r""" 12 | immediate number constant 13 | """ 14 | mop_str = cvar.mop_str 15 | r""" 16 | immediate string constant (user representation) 17 | """ 18 | mop_d = cvar.mop_d 19 | r""" 20 | result of another instruction 21 | """ 22 | mop_S = cvar.mop_S 23 | r""" 24 | local stack variable (they exist until MMAT_LVARS) 25 | """ 26 | mop_v = cvar.mop_v 27 | r""" 28 | global variable 29 | """ 30 | mop_b = cvar.mop_b 31 | r""" 32 | micro basic block (mblock_t) 33 | """ 34 | mop_f = cvar.mop_f 35 | r""" 36 | list of arguments 37 | """ 38 | mop_l = cvar.mop_l 39 | r""" 40 | local variable 41 | """ 42 | mop_a = cvar.mop_a 43 | r""" 44 | mop_addr_t: address of operand (mop_l, mop_v, mop_S, mop_r) 45 | """ 46 | mop_h = cvar.mop_h 47 | r""" 48 | helper function 49 | """ 50 | mop_c = cvar.mop_c 51 | r""" 52 | mcases 53 | """ 54 | mop_fn = cvar.mop_fn 55 | r""" 56 | floating point constant 57 | """ 58 | mop_p = cvar.mop_p 59 | r""" 60 | operand pair 61 | """ 62 | mop_sc = cvar.mop_sc 63 | r""" 64 | scattered 65 | """ 66 | NOSIZE = cvar.NOSIZE 67 | r""" 68 | wrong or unexisting operand size 69 | """ 70 | ``` -------------------------------------------------------------------------------- /doc/libmsec: -------------------------------------------------------------------------------- 1 | father_blk:28 is not add dispatch 2 | blk.tail : jnz r1.4{19}, #0x15B28002.4, @17 mop_compared:r1.4{19} - father_mop_comparedr1.4{19} 3 | jnz r1.4{19}, #0x15B28002.4, @17 4 | 5 | 6 | father_blk:46 is not add dispatch 7 | blk.tail : jnz r1.4{19}, #0xCFEC6CDA.4, @17 mop_compared:r1.4{19} - father_mop_comparedr1.4{19} 8 | jnz r1.4{19}, #0xCFEC6CDA.4, @17 9 | 10 | 11 | father_blk:65 is not add dispatch 12 | blk.tail : jnz r1.4{19}, #0xDD392BCC.4, @17 mop_compared:r1.4{19} - father_mop_comparedr1.4{19} 13 | jnz r1.4{19}, #0xDD392BCC.4, @17 14 | 15 | 16 | father_blk:70 is not add dispatch 17 | blk.tail : jnz r1.4{19}, #0x9489B58.4, @17 mop_compared:r1.4{19} - father_mop_comparedr1.4{19} 18 | jnz r1.4{19}, #0x9489B58.4, @17 -------------------------------------------------------------------------------- /doc/ollvm.md: -------------------------------------------------------------------------------- 1 | ollvm概述 2 | 3 | + 指令替换 -mllvm -sub 4 | + 虚假控制流 -mllvm -bcf 5 | + 控制流程平坦化 -mllvm -fla 6 | + 字符串加密 7 | 8 | ### 指令替换 9 | 所谓指令替换仅仅是对标准二进制运算(比如加、减、位运算)使用更复杂的指令序列进行功能等价替换,当存在多种等价指令序列时,随机选择一种。 10 | 这种混淆并不直截了当,而且并没有增加更多的安全性,因为通过重新优化可以很容易地把替换的等价指令序列变回去。花指令指的就是她 11 | 目前,只有在整数上的操作可用,因为在浮点数上的运算替换会带来四舍五入的错误以及不必要的数值不准确。 12 | 13 | ### 虚假控制流 14 | 在原来的控制流程中添加一个虚假的,不会指令的代码快。用来增加分析难度,对于静态分析有效果,对于动态运行的分析没有对抗效果。 15 | 这种方式通过在当前基本块之前添加一个基本块,来修改函数调用流程图。新添加的基本块包含一个不透明的谓语,然后再跳转到原来的基本块。 16 | 17 | ### 流程平坦化 18 | 控制流打平的目的是将程序的控制流图完全地扁平化。 19 | 20 | ### 字符串加密 21 | 将字符串在编译的时候进行加密,在运行的时候解密,目前遇见过集体解密,和单个字符串运行时解密 22 | -------------------------------------------------------------------------------- /doc/un_patch.md: -------------------------------------------------------------------------------- 1 | 2 | ### d810组件 3 | 4 | + microcode 仿真执行 5 | 6 | + MopTracker 向后变量跟踪 7 | 8 | + Microcode control flow patching 微码控制流修补 9 | 首先获取所有的ollvm 分发器对应的分支,然后计算出对应分支的所有块,分支的计算从分发器修改的位置开始计算,不是从块起始位置计算。 10 | 在这些分支块中进行计算,找到块有两个以上前驱,并且这个两个前驱到这个块本身,会有两个分发值,这说明这个块需要复制,不是有两个以上前驱的一定需要复制,经常有的块 11 | 有好几个前驱,但是块本身会使用常量对分发器赋值,这种是不需要赋值的,因为分支的起始位置起始就是块本身。 12 | 13 | ### fla对抗工作流程 14 | 15 | + 识别分发器 16 | + 寻找的分发分支以及对应的 常量值 17 | + 分析常量值和对应的先后执行顺序 18 | + 修复代码块 19 | 20 | 21 | ### bcf 对抗 22 | 23 | 通过真实执行或者类似真实执行的分析可以识别出那些是虚假块,可能会遇到不透明谓词的分支跳转,或者花指令,混淆 24 | 25 | ### 混淆,指令替换 26 | 主要是不透明谓词,混淆,需要人工,或者半人工,配合一下自动化优化工具进行反向指令替换 -------------------------------------------------------------------------------- /example/addMicrocode.py: -------------------------------------------------------------------------------- 1 | """ 2 | summary: various decompiler hooks 3 | 4 | description: 5 | Shows how to hook to many notifications sent by the decompiler. 6 | 7 | This plugin doesn't really accomplish anything: it just prints 8 | the parameters. 9 | 10 | The list of notifications handled below should be exhaustive, 11 | and is there to hint at what is possible to accomplish by 12 | subclassing `ida_hexrays.Hexrays_Hooks` 13 | 14 | see_also: curpos_details 15 | """ 16 | 17 | from __future__ import print_function 18 | 19 | import inspect 20 | 21 | import ida_idaapi 22 | import ida_typeinf 23 | import ida_hexrays 24 | 25 | class vds_hooks_t(ida_hexrays.Hexrays_Hooks): 26 | def __init__(self): 27 | ida_hexrays.Hexrays_Hooks.__init__(self) 28 | self.display_shortened_cfuncs = False 29 | self.display_vdui_curpos = False 30 | self.inhibit_log = 0; 31 | 32 | def _format_lvar(self, v): 33 | parts = [] 34 | if v: 35 | if v.name: 36 | parts.append("name=%s" % v.name) 37 | if v.cmt: 38 | parts.append("cmt=%s" % v.cmt) 39 | parts.append("width=%s" % v.width) 40 | parts.append("defblk=%s" % v.defblk) 41 | parts.append("divisor=%s" % v.divisor) 42 | return "{%s}" % ", ".join(parts) 43 | 44 | def _format_vdui_curpos(self, v): 45 | return "cpos={lnnum=%d, x=%d, y=%d}" % (v.cpos.lnnum, v.cpos.x, v.cpos.y) 46 | 47 | def _format_value(self, v): 48 | if isinstance(v, ida_hexrays.lvar_t): 49 | v = self._format_lvar(v) 50 | elif isinstance(v, ida_hexrays.cfunc_t): 51 | if self.display_shortened_cfuncs: 52 | self.inhibit_log += 1 53 | v = str(v) 54 | if len(v) > 20: 55 | v = v[0:20] + "[...snipped...]" 56 | self.inhibit_log -= 1 57 | else: 58 | v = "" # cannot print contents: we'll end up being called recursively 59 | elif isinstance(v, ida_hexrays.vdui_t) and self.display_vdui_curpos: 60 | v = str(v) + " " + self._format_vdui_curpos(v) 61 | return str(v) 62 | 63 | def _log(self): 64 | if self.inhibit_log <= 0: 65 | stack = inspect.stack() 66 | frame, _, _, _, _, _ = stack[1] 67 | args, _, _, values = inspect.getargvalues(frame) 68 | method_name = inspect.getframeinfo(frame)[2] 69 | argstrs = [] 70 | for arg in args[1:]: 71 | argstrs.append("%s=%s" % (arg, self._format_value(values[arg]))) 72 | print("### %s: %s" % (method_name, ", ".join(argstrs))) 73 | return 0 74 | 75 | def flowchart(self, fc): 76 | return self._log() 77 | 78 | def stkpnts(self, mba, stkpnts): 79 | return self._log() 80 | 81 | def prolog(self, mba, fc, reachable_blocks, decomp_flags): 82 | return self._log() 83 | 84 | def microcode(self, mba): 85 | import pydevd_pycharm 86 | pydevd_pycharm.settrace('localhost', port=31235, stdoutToServer=True, stderrToServer=True) 87 | return self._log() 88 | 89 | def preoptimized(self, mba): 90 | return self._log() 91 | 92 | def locopt(self, mba): 93 | return self._log() 94 | 95 | def prealloc(self, mba): 96 | return self._log() 97 | 98 | def glbopt(self, mba): 99 | return self._log() 100 | 101 | def structural(self, ctrl_graph): 102 | return self._log() 103 | 104 | def maturity(self, cfunc, maturity): 105 | return self._log() 106 | 107 | def interr(self, code): 108 | return self._log() 109 | 110 | # def combine(self, blk, insn): 111 | # return self._log() 112 | 113 | def print_func(self, cfunc, printer): 114 | return self._log() 115 | 116 | def func_printed(self, cfunc): 117 | return self._log() 118 | 119 | def resolve_stkaddrs(self, mba): 120 | return self._log() 121 | 122 | def open_pseudocode(self, vu): 123 | return self._log() 124 | 125 | def switch_pseudocode(self, vu): 126 | return self._log() 127 | 128 | def refresh_pseudocode(self, vu): 129 | return self._log() 130 | 131 | def close_pseudocode(self, vu): 132 | return self._log() 133 | 134 | def keyboard(self, vu, key_code, shift_state): 135 | return self._log() 136 | 137 | def right_click(self, vu): 138 | return self._log() 139 | 140 | def double_click(self, vu, shift_state): 141 | return self._log() 142 | 143 | def curpos(self, vu): 144 | return self._log() 145 | 146 | def create_hint(self, vu): 147 | return self._log() 148 | 149 | def text_ready(self, vu): 150 | return self._log() 151 | 152 | def populating_popup(self, widget, popup, vu): 153 | return self._log() 154 | 155 | def lvar_name_changed(self, vu, v, name, is_user_name): 156 | return self._log() 157 | 158 | def lvar_type_changed(self, vu, v, tif): 159 | return self._log() 160 | 161 | def lvar_cmt_changed(self, vu, v, cmt): 162 | return self._log() 163 | 164 | def lvar_mapping_changed(self, vu, _from, to): 165 | return self._log() 166 | 167 | def cmt_changed(self, cfunc, loc, cmt): 168 | return self._log() 169 | 170 | def build_callinfo(self, *args): 171 | return self._log() 172 | 173 | vds_hooks = vds_hooks_t() 174 | vds_hooks.hook() 175 | -------------------------------------------------------------------------------- /example/bcf_optimizer_insn.py: -------------------------------------------------------------------------------- 1 | import ida_hexrays 2 | import ida_idaapi 3 | from d810.ast import minsn_to_ast, AstNode, AstLeaf, AstConstant 4 | 5 | 6 | # 这个指令优化是作用域microcode层面的,会根据不同的成熟度,各调用你一次,同时每个成熟度生成的每一条指令一会调用一次,每一条指令跟生成的miocrocode 是对应的 7 | 8 | 9 | class sample_optimizer_t(ida_hexrays.optinsn_t): 10 | 11 | def __init__(self): 12 | super().__init__() 13 | self.instruction_visitor = InstructionVisitorManager(self) 14 | self.PredOdd1 = AstNode(ida_hexrays.m_and, 15 | AstNode(ida_hexrays.m_mul, 16 | 17 | AstNode(ida_hexrays.m_sub, 18 | AstLeaf('x_0'), 19 | AstConstant('1', 1)), 20 | AstLeaf('x_0')), 21 | AstConstant('1', 1)) 22 | self.replace = AstNode(ida_hexrays.m_mov, AstConstant("val_0")) 23 | 24 | def func(self, blk, ins) -> bool: 25 | 26 | if (blk.head != None): 27 | if blk.mba.maturity != ida_hexrays.MMAT_LOCOPT: 28 | return False 29 | # print("mmat:",blk.mba.maturity) 30 | try: 31 | # print("start for_all_insns") 32 | optimization_performed = self.optimize(blk, ins) 33 | ins.for_all_insns(self.instruction_visitor) 34 | # if optimization_performed: 35 | # ins.optimize_solo() 36 | 37 | except RuntimeError as e: 38 | print("RuntimeError while optimizing ins") 39 | return False # report the number of changes 40 | 41 | def optimize(self, blk: ida_hexrays.mblock_t, ins: ida_hexrays.minsn_t) -> bool: 42 | 43 | try: 44 | if ins.opcode == ida_hexrays.m_and: 45 | # print("visit:{0}".format(ins.dstr())) 46 | tmp = minsn_to_ast(ins) 47 | new_ins = self.check_pattern_and_replace(self.PredOdd1, tmp) 48 | if new_ins is not None: 49 | ins.swap(new_ins) 50 | # print(new_ins.dstr()) 51 | except Exception as e: 52 | pass 53 | # print(e) 54 | 55 | return False 56 | 57 | def check_pattern_and_replace(self, candidate_pattern: AstNode, test_ast: AstNode): 58 | # import pydevd_pycharm 59 | # pydevd_pycharm.settrace('localhost', port=31235, stdoutToServer=True, stderrToServer=True) 60 | if not candidate_pattern.check_pattern_and_copy_mops(test_ast): 61 | return None 62 | new_instruction = ida_hexrays.minsn_t(test_ast.ea) 63 | mop_num = ida_hexrays.mop_t() 64 | mop_num.make_number(0, 8) 65 | new_instruction.opcode = ida_hexrays.m_mov 66 | new_instruction.l = mop_num 67 | 68 | new_instruction.d = test_ast.dst.mop 69 | 70 | return new_instruction 71 | 72 | 73 | class InstructionVisitorManager(ida_hexrays.minsn_visitor_t): 74 | 75 | def __init__(self, optimizer: sample_optimizer_t): 76 | super().__init__() 77 | self.instruction_optimizer = optimizer 78 | 79 | def visit_minsn(self) -> bool: 80 | return self.instruction_optimizer.optimize(self.blk, self.curins) 81 | # return False #这个函数需要返回false ,否则可能只执行一次,无法进行指令遍历 82 | 83 | 84 | if __name__ == '__main__': 85 | ida_hexrays.clear_cached_cfuncs() 86 | optimizer = sample_optimizer_t() 87 | optimizer.install() 88 | # optimizer.remove() 89 | -------------------------------------------------------------------------------- /example/blockoptimizerDemo.py: -------------------------------------------------------------------------------- 1 | import ida_hexrays 2 | import idaapi 3 | 4 | 5 | # 这个指令优化是作用域microcode层面的,也会多次调用,每个块都会调用 6 | # func 函数不会调用一次,会调用很多次,每一个块都会调用一次 7 | # ida 我们使用f5进行反编译的时候,成熟的一般在只能拦截到6左右 8 | First = False 9 | 10 | class UnflattenerOpt(ida_hexrays.optblock_t): 11 | 12 | def func(self, blk): 13 | global First 14 | 15 | if (blk.head != None): 16 | if blk.mba.maturity != ida_hexrays.MMAT_GLBOPT2: 17 | return 0 18 | if First: 19 | return 0 20 | if blk.serial == 2: 21 | print(blk.mba.maturity, hex(blk.head.ea)) 22 | First = True 23 | self.codeFlow(blk.mba) 24 | 25 | return 0 # report the number of changes 26 | 27 | 28 | def codeFlow(self,mba): 29 | class MyGraph(idaapi.GraphViewer): 30 | def __init__(self, title, mba): 31 | idaapi.GraphViewer.__init__(self, title) 32 | self._mba = mba 33 | 34 | def OnRefresh(self): 35 | self.Clear() 36 | nodes = {} 37 | for blk_idx in range(self._mba.qty): 38 | blk = self._mba.get_mblock(blk_idx) 39 | if blk.head == None: 40 | continue 41 | lines = [] 42 | 43 | lines.append("{0}:{1}".format(blk_idx,hex(blk.head.ea))) 44 | insn = blk.head 45 | while insn: 46 | lines.append(insn.dstr()) 47 | if insn == blk.tail: 48 | break 49 | insn = insn.next 50 | label = "\n".join(lines) 51 | node_id = self.AddNode(label) 52 | nodes[blk.head.ea] = node_id 53 | 54 | for blk_idx in range(self._mba.qty): 55 | blk = self._mba.get_mblock(blk_idx) 56 | succset_list = [x for x in blk.succset] 57 | for succ in succset_list: 58 | blk_succ = self._mba.get_mblock(succ) 59 | if blk_succ.head == None: 60 | continue 61 | if blk.head == None: 62 | continue 63 | if blk_succ.head.ea in nodes: 64 | self.AddEdge(nodes[blk.head.ea], nodes[blk_succ.head.ea]) 65 | return True 66 | 67 | def OnGetText(self, node_id): 68 | return self[node_id] 69 | print(" show flocchart") 70 | title = "Fun microcode FlowChart" 71 | graph = MyGraph(title, mba) 72 | if not graph.Show(): 73 | print("Failed to display the graph") 74 | 75 | 76 | if __name__ == '__main__': # 也可以直接在脚本里执行 77 | ida_hexrays.clear_cached_cfuncs() 78 | optimizer = UnflattenerOpt() 79 | optimizer.install() 80 | -------------------------------------------------------------------------------- /example/blockoptimizerReturnText.py: -------------------------------------------------------------------------------- 1 | import ida_hexrays 2 | import idaapi 3 | 4 | 5 | # 这个代码主要进行测试 func 函数返回的数据为为非零 6 | # 经过测试发现 如果func函数返回为非零会再次调用这个函数,直到这个函数为零,才会正常返回,如果一直不返回为0,会一直调用直到卡死 7 | # 再次调用,这个时候要分析的数据应该是已经处于我们上次修改过后的结果了 8 | # 返回 1 和 2,调用测试好像一样,不知道不同数值调用有什么区别 9 | 10 | # 经过测试 MMAT_GLBOPT2 原始调用次数为2,返回1后,发现调用次数为4次了 11 | First = True 12 | 13 | class UnflattenerOpt(ida_hexrays.optblock_t): 14 | 15 | def func(self, blk): 16 | global First 17 | print("maturity:",blk.mba.maturity,"serial:",blk.serial) 18 | if (blk.head != None): 19 | if blk.mba.maturity != ida_hexrays.MMAT_GLBOPT2: 20 | return 0 21 | 22 | if blk.serial == 2: 23 | print("entry blk.serial == 2") 24 | First = False 25 | # self.codeFlow(blk.mba) 26 | if First: 27 | print("retuen 1") 28 | return 1 29 | return 0 # report the number of changes 30 | 31 | 32 | 33 | 34 | 35 | if __name__ == '__main__': # 也可以直接在脚本里执行 36 | ida_hexrays.clear_cached_cfuncs() 37 | optimizer = UnflattenerOpt() 38 | optimizer.install() 39 | -------------------------------------------------------------------------------- /example/call_genmy.py: -------------------------------------------------------------------------------- 1 | 2 | #ida 中的python插件可以相互引用,这个是一个引用genmy插件的demo, 从下面导入genmy中的函数 3 | from datetime import datetime 4 | 5 | from genmy import * 6 | 7 | import os, shutil, errno, re 8 | 9 | import ida_idaapi 10 | import ida_bytes 11 | import ida_range 12 | import ida_kernwin as kw 13 | import ida_hexrays as hr 14 | import ida_funcs 15 | import ida_diskio 16 | import ida_ida 17 | import ida_graph 18 | import ida_lines 19 | import ida_moves 20 | 21 | 22 | def getMba(): 23 | """Generates and displays microcode for an address range. 24 | An address range can be a selection of code or that of 25 | the current function.""" 26 | sel, sea, eea = kw.read_range_selection(None) 27 | pfn = ida_funcs.get_func(kw.get_screen_ea()) 28 | if not sel and not pfn: 29 | return (False, "Position cursor within a function or select range") 30 | 31 | if not sel and pfn: 32 | sea = pfn.start_ea 33 | eea = pfn.end_ea 34 | 35 | addr_fmt = "%016x" if ida_ida.inf_is_64bit() else "%08x" 36 | 37 | F = ida_bytes.get_flags(sea) 38 | if not ida_bytes.is_code(F): 39 | return (False, "The selected range must start with an instruction") 40 | 41 | text, mmat, mba_flags = ask_desired_maturity() # dialog 42 | if text is None and mmat is None: 43 | return (True, "Cancelled") 44 | 45 | if not sel and pfn: 46 | mbr = hr.mba_ranges_t(pfn) 47 | else: 48 | mbr = hr.mba_ranges_t() 49 | mbr.ranges.push_back(ida_range.range_t(sea, eea)) 50 | 51 | hf = hr.hexrays_failure_t() 52 | ml = hr.mlist_t() 53 | mba = hr.gen_microcode(mbr, hf, ml, hr.DECOMP_WARNINGS, mmat) 54 | if not mba: 55 | return (False, "0x%s: %s" % (addr_fmt % hf.errea, hf.desc())) 56 | 57 | # show_microcode_graph(mba ,"text_graph") 58 | 59 | now = datetime.now() 60 | graphviz(mba, "/home/chic/unfla/" + now.strftime("%H-%M-%S-%f")) 61 | 62 | getMba() -------------------------------------------------------------------------------- /example/create_insn.py: -------------------------------------------------------------------------------- 1 | 2 | import ida_hexrays 3 | 4 | 5 | # ida mop 的寄存器,是通过mop传入数据来分辨是什么数据,但是并没有,并没有说明如何设置为某个具体寄存器 6 | # 经过测试发现 是通过传入的整数的bit来区分是什么寄存器,前4个bit 16中选择对应mop的类型,4各以后的bit开始对应寄存器,后面一个整数是长度,size 7 | def create_insn(): 8 | insn = ida_hexrays.minsn_t(0) 9 | print(insn.dstr()) 10 | insn.opcode = ida_hexrays.m_add # 加法操作 11 | insn.l = ida_hexrays.mop_t(ida_hexrays.mop_r, 8) # 左操作数 12 | insn.r = ida_hexrays.mop_t(64, 8) # 右操作数 13 | insn.d = ida_hexrays.mop_t(32, 8) # 右操作数 14 | print(insn.dstr()) 15 | 16 | 17 | def create_reg(): 18 | mop_reg = ida_hexrays.mop_t() 19 | mop_reg.make_reg(0) 20 | print("0:",mop_reg.dstr()) 21 | mop_reg.make_reg(8) 22 | print("8:",mop_reg.dstr()) 23 | mop_reg.make_reg(16) 24 | print("16:",mop_reg.dstr()) 25 | mop_reg.make_reg(32) 26 | print("32:",mop_reg.dstr()) 27 | mop_reg.make_reg(64) 28 | print("64:",mop_reg.dstr()) 29 | mop_reg.make_reg(128) 30 | print("128:",mop_reg.dstr()) 31 | mop_reg.make_reg(256) 32 | print("256:",mop_reg.dstr()) 33 | print("mop_reg is mop_r:", mop_reg.t) 34 | print("mop_reg is mop_r:", ida_hexrays.mop_r) 35 | 36 | # if mop_reg.oprops == ida_hexrays.mop_r: 37 | # print("mop_reg is mop_r:",mop_reg.oprops) 38 | 39 | 40 | def create_num(): 41 | mop_num = ida_hexrays.mop_t() 42 | mop_num.make_number(100, 8) 43 | print(mop_num.dstr()) 44 | print("mop_num is mop_r:", mop_num.t) 45 | print("mop_reg is mop_r:", ida_hexrays.mop_n) 46 | 47 | 48 | if __name__ == '__main__': 49 | try: 50 | # import pydevd_pycharm 51 | # pydevd_pycharm.settrace('localhost', port=31235, stdoutToServer=True, stderrToServer=True) 52 | create_insn() 53 | create_num() 54 | create_reg() 55 | except Exception as e: 56 | print(f"error: {e}") -------------------------------------------------------------------------------- /example/dfs.py: -------------------------------------------------------------------------------- 1 | #一个有循环的流程图,给定一个节点,深度遍历所有的分支,找到所有的可到达这个节点的路径,给定的节点和目标节点是一个节点,用于寻找循环 2 | 3 | from collections import defaultdict 4 | 5 | class Graph: 6 | def __init__(self): 7 | self.graph = defaultdict(list) 8 | 9 | def add_edge(self, u, v): 10 | self.graph[u].append(v) 11 | 12 | def find_cycles(self, start_node): 13 | paths = [] 14 | self.dfs(start_node, start_node, [], paths, set()) 15 | return paths 16 | 17 | def dfs(self, current_node, target_node, path, paths, visited): 18 | path.append(current_node) 19 | visited.add(current_node) 20 | 21 | for neighbor in self.graph[current_node]: 22 | if neighbor == target_node and len(path) > 1: 23 | paths.append(list(path)) 24 | elif neighbor not in visited: 25 | self.dfs(neighbor, target_node, path, paths, visited) 26 | 27 | path.pop() 28 | visited.remove(current_node) 29 | 30 | # 示例用法 31 | if __name__ == "__main__": 32 | g = Graph() 33 | g.add_edge('A', 'B') 34 | g.add_edge('B', 'C') 35 | g.add_edge('C', 'A') # 形成循环 36 | g.add_edge('B', 'D') 37 | g.add_edge('D', 'B') # 形成循环 38 | g.add_edge('A', 'G') # 形成循环 39 | g.add_edge('G', 'H') # 形成循环 40 | g.add_edge('H', 'A') # 形成循环 41 | 42 | start_node = 'A' 43 | cycles = g.find_cycles(start_node) 44 | print("所有包含目标节点的循环路径:") 45 | for path in cycles: 46 | print(" -> ".join(path)) -------------------------------------------------------------------------------- /example/getMultiple_predecessors.py: -------------------------------------------------------------------------------- 1 | from typing import List, Tuple, Union, Dict 2 | 3 | 4 | # 定义 mblock_t 类表示块 5 | class mblock_t: 6 | def __init__(self, serial: str): 7 | self.serial = serial 8 | 9 | def __repr__(self): 10 | return f"Block({self.serial})" 11 | 12 | 13 | # 定义 MopHistory 类表示变量的历史路径 14 | class MopHistory: 15 | def __init__(self, block_path: List[mblock_t]): 16 | self.block_path = block_path 17 | 18 | def __repr__(self): 19 | return f"MopHistory({[block.serial for block in self.block_path]})" 20 | 21 | 22 | # 假设的 get_blk_index 函数,找到块在路径中的位置 23 | def get_blk_index(block: mblock_t, block_path: List[mblock_t]) -> int: 24 | try: 25 | return block_path.index(block) 26 | except ValueError: 27 | return -1 28 | 29 | 30 | #d810通过某个终止块和分发器来寻找出他们之间的MopHistory,就是分支如果有多个分支,说明有多个值,这种时候需要进行优化,将终止块复制,将分支拆开。 31 | 32 | 33 | 34 | # 目标函数:单个块查找是否有多个前驱的块,由代表同时存在于两个分支路径中 35 | def get_block_with_multiple_predecessors(var_histories: List[MopHistory]) -> Tuple[Union[None, mblock_t],Union[None, Dict[int, List[MopHistory]]]]: 36 | # 第一个循环是进行MopHistory 循环,每个MopHistory 是一个分支 37 | for i, var_history in enumerate(var_histories): 38 | # 首先是循环所有分支,var_history.block_path[0] 是某个循环分支中的第一个块。从第一个块开始进入下层循环循环,下层循环会遍历当前分支的所有块 39 | pred_blk = var_history.block_path[0] 40 | # 从当前分支的第二个块开始循环,到最后,同时不断更新pred_blk,他永远是block的前一个块。也就是说这个横向双块循环 41 | for block in var_history.block_path[1:]: 42 | #前驱的serial作为下标后面进行存在判断 43 | tmp_dict = {pred_blk.serial: [var_history]} # 在这里加入遍历 44 | # 循环上面MopHistory 的后一个 45 | # 进入下一层循环,这个循环是分支循环,i+1 表明,从最上面开始循环分支的下一个分支开始进行这个分支循环,也就是说,这里是横向双分支循环 46 | for j in range(i + 1, len(var_histories)): 47 | #var_histories[j].block_path 就是当前分支所有的块的列表,找一下这个块是否在这个列表 48 | blk_index = get_blk_index(block, var_histories[j].block_path) #寻找 block 在 var_histories[j].block_path 中是否存在,如果存在返回下标 49 | if (blk_index - 1) >= 0: # 如果存在,也就是这个上层分支的块在当前分支中找到了 50 | other_pred = var_histories[j].block_path[blk_index - 1] # 找到他的这个块的前驱 51 | if other_pred.serial not in tmp_dict.keys(): # 判断是否已经加入到列表中 52 | tmp_dict[other_pred.serial] = [] # 没有就创建,不存在加入到列表中,这个tmp_dict 存在列表,在上面创建的时候就会加入上层分支,这个块以及前驱到map里 53 | tmp_dict[other_pred.serial].append(var_histories[j]) 54 | 55 | if len(tmp_dict) > 1: # 这个tmp_dict 对应具体分支,具体块的前驱和块,因为是具体分支,虽然单个块有前驱,但是在对应具体分支的时候,一个块只会有一个前驱 56 | # 所以如果出现两个,说明不同分支中同一个块有两个前驱,因为前驱是key,同一个前驱是不会有两个maps的 57 | return block, tmp_dict # 返回这个块 58 | pred_blk = block 59 | return None, None 60 | 61 | 62 | # -----------------测试代码------------------ 63 | 64 | # 定义一些测试块 65 | A = mblock_t("A") 66 | B = mblock_t("B") 67 | C = mblock_t("C") 68 | D = mblock_t("D") 69 | E = mblock_t("E") 70 | F = mblock_t("F") 71 | G = mblock_t("G") 72 | H = mblock_t("H") 73 | I = mblock_t("I") 74 | J = mblock_t("J") 75 | K = mblock_t("K") 76 | L = mblock_t("L") 77 | M = mblock_t("M") 78 | N = mblock_t("N") 79 | 80 | # 定义每个变量的历史路径 81 | var_histories = [ 82 | MopHistory([A, B, C, D, J, K, L]), # 第一个变量经过块 A -> B -> C -> D 83 | MopHistory([E, H, C, F, M, N,L]), # 第二个变量经过块 E -> H -> C -> F 84 | MopHistory([E, H, C, F, M, N,L]), # 第二个变量经过块 E -> H -> C -> F 85 | MopHistory([E, H, C, F, M, N,L]), # 第二个变量经过块 E -> H -> C -> F 86 | ] 87 | 88 | # 调用函数并打印结果 89 | block, predecessors = get_block_with_multiple_predecessors(var_histories) 90 | 91 | print(f"Found block: {block}") 92 | print(f"Predecessor mappings: {predecessors}") 93 | -------------------------------------------------------------------------------- /example/graphviz_demo.py: -------------------------------------------------------------------------------- 1 | from graphviz import Digraph 2 | 3 | dot = Digraph(comment='Flowchart with Curved Edges') 4 | 5 | # 设置图的属性 6 | dot.attr(rankdir='LR', splines='true') # 允许弯曲 7 | 8 | # 添加节点 9 | dot.node('A', 'Start', shape='ellipse', style='filled', fillcolor='lightblue') 10 | dot.node('B', 'Process 1', shape='box', style='filled', fillcolor='lightgreen') 11 | dot.node('C', 'Decision', shape='diamond', style='filled', fillcolor='lightcoral') 12 | dot.node('D', 'Process 2', shape='box', style='filled', fillcolor='lightgreen') 13 | dot.node('E', 'End', shape='ellipse', style='filled', fillcolor='lightblue') 14 | 15 | # 添加边 16 | dot.edge('A', 'B', label='Step 1') 17 | dot.edge('B', 'C', label='Step 2') 18 | dot.edge('C', 'D', label='Yes', color='green') 19 | dot.edge('C', 'E', label='No', color='red') 20 | dot.edge('D', 'E', label='Step 3') 21 | 22 | # 保存图像 23 | dot.render('/home/chic/curved_flowchart') 24 | 25 | print("曲线流程图已生成并保存") 26 | -------------------------------------------------------------------------------- /example/hook.py: -------------------------------------------------------------------------------- 1 | """ 2 | summary: various decompiler hooks 3 | 4 | description: 5 | Shows how to hook to many notifications sent by the decompiler. 6 | 7 | This plugin doesn't really accomplish anything: it just prints 8 | the parameters. 9 | 10 | The list of notifications handled below should be exhaustive, 11 | and is there to hint at what is possible to accomplish by 12 | subclassing `ida_hexrays.Hexrays_Hooks` 13 | 14 | see_also: curpos_details 15 | """ 16 | 17 | from __future__ import print_function 18 | 19 | import inspect 20 | 21 | import ida_idaapi 22 | import ida_typeinf 23 | import ida_hexrays 24 | 25 | class vds_hooks_t(ida_hexrays.Hexrays_Hooks): 26 | def __init__(self): 27 | ida_hexrays.Hexrays_Hooks.__init__(self) 28 | self.display_shortened_cfuncs = False 29 | self.display_vdui_curpos = False 30 | self.inhibit_log = 0; 31 | 32 | def _format_lvar(self, v): 33 | parts = [] 34 | if v: 35 | if v.name: 36 | parts.append("name=%s" % v.name) 37 | if v.cmt: 38 | parts.append("cmt=%s" % v.cmt) 39 | parts.append("width=%s" % v.width) 40 | parts.append("defblk=%s" % v.defblk) 41 | parts.append("divisor=%s" % v.divisor) 42 | return "{%s}" % ", ".join(parts) 43 | 44 | def _format_vdui_curpos(self, v): 45 | return "cpos={lnnum=%d, x=%d, y=%d}" % (v.cpos.lnnum, v.cpos.x, v.cpos.y) 46 | 47 | def _format_value(self, v): 48 | if isinstance(v, ida_hexrays.lvar_t): 49 | v = self._format_lvar(v) 50 | elif isinstance(v, ida_hexrays.cfunc_t): 51 | if self.display_shortened_cfuncs: 52 | self.inhibit_log += 1 53 | v = str(v) 54 | if len(v) > 20: 55 | v = v[0:20] + "[...snipped...]" 56 | self.inhibit_log -= 1 57 | else: 58 | v = "" # cannot print contents: we'll end up being called recursively 59 | elif isinstance(v, ida_hexrays.vdui_t) and self.display_vdui_curpos: 60 | v = str(v) + " " + self._format_vdui_curpos(v) 61 | return str(v) 62 | 63 | def _log(self): 64 | if self.inhibit_log <= 0: 65 | stack = inspect.stack() 66 | frame, _, _, _, _, _ = stack[1] 67 | args, _, _, values = inspect.getargvalues(frame) 68 | method_name = inspect.getframeinfo(frame)[2] 69 | argstrs = [] 70 | for arg in args[1:]: 71 | argstrs.append("%s=%s" % (arg, self._format_value(values[arg]))) 72 | print("### %s: %s" % (method_name, ", ".join(argstrs))) 73 | return 0 74 | 75 | def flowchart(self, fc): 76 | return self._log() 77 | 78 | def stkpnts(self, mba, stkpnts): 79 | return self._log() 80 | 81 | def prolog(self, mba, fc, reachable_blocks, decomp_flags): 82 | return self._log() 83 | 84 | def microcode(self, mba): 85 | return self._log() 86 | 87 | def preoptimized(self, mba): 88 | return self._log() 89 | 90 | def locopt(self, mba): 91 | return self._log() 92 | 93 | def prealloc(self, mba): 94 | return self._log() 95 | 96 | def glbopt(self, mba): 97 | return self._log() 98 | 99 | def structural(self, ctrl_graph): 100 | return self._log() 101 | 102 | def maturity(self, cfunc, maturity): 103 | return self._log() 104 | 105 | def interr(self, code): 106 | return self._log() 107 | 108 | def combine(self, blk, insn): 109 | return self._log() 110 | 111 | def print_func(self, cfunc, printer): 112 | return self._log() 113 | 114 | def func_printed(self, cfunc): 115 | return self._log() 116 | 117 | def resolve_stkaddrs(self, mba): 118 | return self._log() 119 | 120 | def open_pseudocode(self, vu): 121 | return self._log() 122 | 123 | def switch_pseudocode(self, vu): 124 | return self._log() 125 | 126 | def refresh_pseudocode(self, vu): 127 | return self._log() 128 | 129 | def close_pseudocode(self, vu): 130 | return self._log() 131 | 132 | def keyboard(self, vu, key_code, shift_state): 133 | return self._log() 134 | 135 | def right_click(self, vu): 136 | return self._log() 137 | 138 | def double_click(self, vu, shift_state): 139 | return self._log() 140 | 141 | def curpos(self, vu): 142 | return self._log() 143 | 144 | def create_hint(self, vu): 145 | return self._log() 146 | 147 | def text_ready(self, vu): 148 | return self._log() 149 | 150 | def populating_popup(self, widget, popup, vu): 151 | return self._log() 152 | 153 | def lvar_name_changed(self, vu, v, name, is_user_name): 154 | return self._log() 155 | 156 | def lvar_type_changed(self, vu, v, tif): 157 | return self._log() 158 | 159 | def lvar_cmt_changed(self, vu, v, cmt): 160 | return self._log() 161 | 162 | def lvar_mapping_changed(self, vu, _from, to): 163 | return self._log() 164 | 165 | def cmt_changed(self, cfunc, loc, cmt): 166 | return self._log() 167 | 168 | def build_callinfo(self, *args): 169 | return self._log() 170 | 171 | vds_hooks = vds_hooks_t() 172 | vds_hooks.hook() 173 | -------------------------------------------------------------------------------- /example/insnoptimizerDemo.py: -------------------------------------------------------------------------------- 1 | import ida_hexrays 2 | import ida_idaapi 3 | 4 | # 这个指令优化是作用域microcode层面的,会根据不同的成熟度,各调用你一次,同时每个成熟度生成的每一条指令一会调用一次,每一条指令跟生成的miocrocode 是对应的 5 | 6 | class sample_optimizer_t(ida_hexrays.optinsn_t): 7 | def func(self, blk, ins, optflags): 8 | print(blk.mba.maturity,hex(ins.ea)) 9 | return 0 # report the number of changes 10 | 11 | 12 | if __name__ == '__main__': 13 | ida_hexrays.clear_cached_cfuncs() 14 | optimizer = sample_optimizer_t() 15 | optimizer.install() 16 | # optimizer.remove() 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /example/miasm2.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | 3 | class Graph: 4 | def __init__(self): 5 | self.graph = defaultdict(list) 6 | 7 | def add_edge(self, u, v): 8 | self.graph[u].append(v) 9 | 10 | def find_cycles(self, start_node): 11 | paths = [] 12 | self.dfs(start_node, start_node, [], paths, set()) 13 | return paths 14 | 15 | def dfs(self, current_node, target_node, path, paths, visited): 16 | path.append(current_node) 17 | visited.add(current_node) 18 | 19 | for neighbor in self.graph[current_node]: 20 | if neighbor == target_node and len(path) > 1: 21 | paths.append(list(path)) 22 | elif neighbor not in visited: 23 | self.dfs(neighbor, target_node, path, paths, visited) 24 | 25 | path.pop() 26 | visited.remove(current_node) 27 | 28 | # 示例用法 29 | if __name__ == "__main__": 30 | g = Graph() 31 | g.add_edge('A', 'B') 32 | g.add_edge('B', 'C') 33 | g.add_edge('C', 'A') # 形成循环 34 | g.add_edge('B', 'D') 35 | g.add_edge('D', 'B') # 形成循环 36 | g.add_edge('A', 'G') # 形成循环 37 | g.add_edge('G', 'H') # 形成循环 38 | g.add_edge('H', 'A') # 形成循环 39 | 40 | start_node = 'A' 41 | cycles = g.find_cycles(start_node) 42 | print("所有包含目标节点的循环路径:") 43 | for path in cycles: 44 | print(" -> ".join(path)) -------------------------------------------------------------------------------- /example/mlist.py: -------------------------------------------------------------------------------- 1 | import ida_hexrays 2 | import ida_funcs 3 | import ida_kernwin 4 | 5 | 6 | def collect_block_xrefs( mlist, blk, ins,find_uses): 7 | p = ins 8 | while p and not mlist.empty(): 9 | use = blk.build_use_list(p, ida_hexrays.MUST_ACCESS); # things used by the insn 10 | _def = blk.build_def_list(p, ida_hexrays.MUST_ACCESS); # things defined by the insn 11 | plst = use if find_uses else _def 12 | if mlist.has_common(plst): 13 | print("collect_block_xrefs:",hex(p.ea)) 14 | p = p.next if find_uses else p.prev 15 | 16 | def collect_use_xrefs( ctx, mop, mlist, du): 17 | start = ctx.topins.next 18 | serial = ctx.blk.serial; # block number of the operand 19 | collect_block_xrefs(mlist, ctx.blk, start,False) 20 | 21 | bc = du[serial] 22 | voff = ida_hexrays.voff_t(mop) 23 | ch = bc.get_chain(voff) # chain of the operand 24 | if not ch: 25 | return # odd 26 | for bn in ch: 27 | b = ctx.mba.get_mblock(bn) 28 | def show_operand_info(): 29 | if ida_hexrays.init_hexrays_plugin(): 30 | ea = ida_kernwin.get_screen_ea() 31 | pfn = ida_funcs.get_func(ea) 32 | w = ida_kernwin.warning 33 | if pfn: 34 | gco = ida_hexrays.gco_info_t() 35 | if ida_hexrays.get_current_operand(gco): 36 | # generate microcode 37 | hf = ida_hexrays.hexrays_failure_t() 38 | mbr = ida_hexrays.mba_ranges_t(pfn) 39 | mba = ida_hexrays.gen_microcode( 40 | mbr, 41 | hf, 42 | None, 43 | ida_hexrays.DECOMP_WARNINGS | ida_hexrays.DECOMP_NO_CACHE, 44 | ida_hexrays.MMAT_PREOPTIMIZED) 45 | if mba: 46 | merr = mba.build_graph() 47 | if merr == ida_hexrays.MERR_OK: 48 | mlist = ida_hexrays.mlist_t() 49 | if gco.append_to_list(mlist, mba): 50 | ctx = ida_hexrays.op_parent_info_t() 51 | mop = mba.find_mop(ctx, ea, gco.is_def(), mlist) 52 | if mop: 53 | print("found mop:",mop.dstr()) 54 | graph = mba.get_graph() 55 | ud = graph.get_ud(ida_hexrays.GC_REGS_AND_STKVARS) 56 | du = graph.get_du(ida_hexrays.GC_REGS_AND_STKVARS) 57 | if gco.is_use(): 58 | collect_use_xrefs(ctx, mop, mlist, ud) 59 | # 获取当前指令的微代码 60 | # insn = None 61 | # for i in range(1,mba.qty): 62 | # block = mba.get_mblock(i) 63 | # ins = block.head 64 | # 65 | # while ins: 66 | # if ins.ea == ea: 67 | # insn = ins 68 | # break 69 | # ins = ins.next 70 | # if insn: 71 | # break 72 | # 73 | # if not insn: 74 | # print("Failed to get microcode for the instruction at 0x%x" % ea) 75 | # return 76 | # 77 | # # 获取操作数列表 78 | # mlist = ida_hexrays.mlist_t() 79 | # 80 | # if insn.t == ida_hexrays.mop_l: 81 | # mlist.add(insn.l) 82 | # elif insn.t == ida_hexrays.mop_d: 83 | # mlist.add(insn.d) 84 | # elif insn.t == ida_hexrays.mop_v: 85 | # mlist.add(insn.v) 86 | # 87 | # 88 | # # 遍历操作数列表并显示信息 89 | # print("Operand list for instruction at 0x%x:" % ea) 90 | # for i in range(len(mlist)): 91 | # operand = mlist[i] 92 | # print("Operand %d:" % i) 93 | # print(" Type: %d" % operand.opcode) 94 | # print(" Size: %d" % operand.size) 95 | # print(" Value: %s" % operand.print1(None)) 96 | 97 | 98 | # Example usage: 99 | import pydevd_pycharm 100 | pydevd_pycharm.settrace('localhost', port=31235, stdoutToServer=True, stderrToServer=True) 101 | try: 102 | show_operand_info() 103 | except Exception as e: 104 | print(e); 105 | -------------------------------------------------------------------------------- /example/networkx_demo.py: -------------------------------------------------------------------------------- 1 | 2 | import networkx as netx 3 | import matplotlib.pyplot as plt 4 | import urllib 5 | 6 | 7 | def py_graph(): 8 | g = netx.Graph() # 创建空图 9 | g.add_edge('a', 'b') # 插入一条连接a,b的边到图中,节点将自动插入 10 | g.add_edge('b', 'c') # 再插入一条连接b,c的边 11 | g.add_edge('c', 'a') # 再插入一条连接c,a的边 12 | netx.draw(g) # 输出一个三角形的图 13 | plt.show() # ui显示图形 14 | #plt.savefig('./generated_image.png') 如果你想保存图片,去除这句的注释 15 | print(g.nodes()) # 输出图g的节点值 16 | print(g.edges(data=True)) # 输出图g的边值 17 | g.remove_edge('c', 'a') #移除一条连接c,a的边 18 | print(g.edges()) # 输出图g的边值 19 | g.remove_node("a") 20 | print(g.nodes()) # 输出图g的节点值 21 | print(g.edges()) # 输出图g的边值 22 | 23 | 24 | 25 | def py_DiGraph(): 26 | G = netx.DiGraph() 27 | netx.add_path(G, [0, 1, 2, 3]) 28 | print(G.in_degree(0)) # node 0 with degree 0 29 | print(G.nodes()) # 输出图g的节点值 30 | print(G.edges()) # 输出图g的边值 31 | G.add_edge(1,3) 32 | print(G.edges()) # 输出图g的边值 33 | print(G.in_degree(0)) # node 0 with degree 0 34 | 35 | netx.draw(G) # 输出一个三角形的图 36 | plt.show() # ui显示图形 37 | 38 | g = netx.DiGraph() # 创建空图 39 | g.add_edge('a', 'b') # 插入一条连接a,b的边到图中,节点将自动插入 40 | g.add_edge('b', 'a') # 插入一条连接a,b的边到图中,节点将自动插入 41 | 42 | print(g.in_degree("b")) # node 0 with degree 0 43 | 44 | 45 | def demo2(): 46 | import networkx as nx 47 | import matplotlib.pyplot as plt 48 | 49 | # 创建图并添加多个节点和边 50 | G = nx.Graph() 51 | G.add_node(1, label="Node 1", description="First node", weight=5) 52 | G.add_node(2, label="Node 2", description="Second node", weight=3) 53 | G.add_node(3, label="Node 3", description="Third node", weight=7) 54 | G.add_node(4, label="Node 4", description="Fourth node", weight=2) 55 | G.add_node(5, label="Node 5", description="Fifth node", weight=9) 56 | 57 | # 添加一些边 58 | G.add_edges_from([(1, 2), (1, 3), (2, 4), (3, 5), (4, 5), (1, 4)]) 59 | 60 | # 提取标签信息,用于显示在图上 61 | labels = {} 62 | for node, data in G.nodes(data=True): 63 | labels[node] = f"{data['label']}\n{data['description']}\nWeight: {data['weight']}" 64 | 65 | # 绘制图形 66 | plt.figure(figsize=(10, 8)) 67 | pos = nx.spring_layout(G) # 使用 spring 布局生成节点位置 68 | 69 | # 绘制边(隐藏节点圆点) 70 | nx.draw_networkx_edges(G, pos) # 只绘制边,不绘制节点圆点 71 | 72 | # 绘制带边框的节点标签 73 | ax = plt.gca() 74 | for node, (x, y) in pos.items(): 75 | label_text = labels[node] 76 | # 创建文本框并设置位置 77 | bbox_props = dict(boxstyle="round,pad=0.4", edgecolor="black", facecolor="white", alpha=0.8) 78 | ax.text(x, y, label_text, ha="center", va="center", fontsize=10, bbox=bbox_props) 79 | 80 | # 保存图片 81 | plt.title("Network Graph with Only Bounded Content (No Node Circles)") 82 | plt.savefig("network_with_only_bounded_content.png", format="PNG") 83 | plt.show() 84 | if __name__ == "__main__": 85 | # py_graph() 86 | # py_DiGraph() 87 | demo2() 88 | -------------------------------------------------------------------------------- /example/optInstall.py: -------------------------------------------------------------------------------- 1 | """ 2 | summary: a custom microcode instruction optimization rule (`x | ~x => -1`) 3 | 4 | description: 5 | Installs a custom microcode instruction optimization rule, 6 | to transform: 7 | 8 | x | ~x 9 | 10 | into 11 | 12 | -1 13 | 14 | To see this plugin in action please use be_ornot_be.idb 15 | """ 16 | 17 | import ida_hexrays 18 | import ida_idaapi 19 | 20 | # recognize "x | ~x" and replace by -1 21 | class subinsn_optimizer_t(ida_hexrays.minsn_visitor_t): 22 | cnt = 0 23 | def visit_minsn(self): # for each instruction... 24 | ins = self.curins # take a reference to the current instruction 25 | if ins.opcode == ida_hexrays.m_sub: 26 | ins.opcode = ida_hexrays.m_add 27 | print(hex(ins.ea)) 28 | return 1 29 | # THE CORE OF THE PLUGIN IS HERE: 30 | # check the pattern "x | ~x" 31 | # if ins.opcode == ida_hexrays.m_or and ins.r.is_insn(ida_hexrays.m_bnot) and ins.l == ins.r.d.l: 32 | # if not ins.l.has_side_effects(): # avoid destroying side effects 33 | # # pattern matched, convert to "mov -1, ..." 34 | # ins.opcode = ida_hexrays.m_mov 35 | # ins.l.make_number(-1, ins.r.size) 36 | # ins.r = ida_hexrays.mop_t() 37 | # self.cnt = self.cnt + 1 # number of changes we made 38 | return 0 # continue traversal 39 | 40 | # a custom instruction optimizer, boilerplate code 41 | class sample_optimizer_t(ida_hexrays.optinsn_t): 42 | def func(self, blk, ins, optflags): 43 | opt = subinsn_optimizer_t() 44 | ins.for_all_insns(opt) 45 | if opt.cnt != 0: # if we modified microcode, 46 | blk.mba.verify(True) # run the verifier 47 | return opt.cnt # report the number of changes 48 | 49 | # a plugin interface, boilerplate code 50 | # class my_plugin_t(ida_idaapi.plugin_t): 51 | # flags = ida_idaapi.PLUGIN_HIDE 52 | # wanted_name = "optimize x|~x" 53 | # wanted_hotkey = "" 54 | # comment = "" 55 | # help = "" 56 | # def init(self): 57 | # if ida_hexrays.init_hexrays_plugin(): 58 | # self.optimizer = sample_optimizer_t() 59 | # self.optimizer.install() 60 | # print("Installed sample optimizer for 'x | ~x'") 61 | # return ida_idaapi.PLUGIN_KEEP # keep us in the memory 62 | # def term(self): 63 | # self.optimizer.remove() 64 | # def run(self, arg): 65 | # if arg == 1: 66 | # return self.optimizer.remove() 67 | # elif arg == 2: 68 | # return self.optimizer.install() 69 | 70 | if __name__ == '__main__': 71 | ida_hexrays.clear_cached_cfuncs() 72 | optimizer = sample_optimizer_t() 73 | optimizer.install() 74 | -------------------------------------------------------------------------------- /example/py_getitem.py: -------------------------------------------------------------------------------- 1 | class DataBase: 2 | '''Python 3 中的类''' 3 | 4 | def __init__(self, id, address): 5 | '''初始化方法''' 6 | self.id = id 7 | self.address = address 8 | self.d = {self.id: 1, 9 | self.address: "192.168.1.1", 10 | } 11 | 12 | def __getitem__(self, key): 13 | # return self.__dict__.get(key, "100") 14 | return self.d.get(key, "default") 15 | 16 | 17 | data = DataBase(1, "192.168.2.11") 18 | print(data["hi"]) 19 | print(data[data.id]) 20 | -------------------------------------------------------------------------------- /example/view_add_menu.py: -------------------------------------------------------------------------------- 1 | from ida_kernwin import simplecustviewer_t, attach_action_to_popup, register_action, unregister_action 2 | from ida_kernwin import action_handler_t, action_desc_t 3 | 4 | class MyCustomViewer(simplecustviewer_t): 5 | def __init__(self): 6 | super().__init__() 7 | 8 | def Create(self): 9 | # 创建自定义视图 10 | title = "My Custom Viewer" 11 | if not super().Create(title): 12 | return False 13 | 14 | # 填充示例内容 15 | self.AddLine("Line 1") 16 | self.AddLine("Line 2") 17 | self.AddLine("Line 3") 18 | return True 19 | 20 | def OnPopup(self, widget, popup_handle): 21 | """ 22 | 重写 OnPopup 方法,向右键菜单添加选项 23 | """ 24 | # 将动作附加到右键菜单 25 | attach_action_to_popup(widget, popup_handle, "my:action1", None) 26 | attach_action_to_popup(widget, popup_handle, "my:action2", None) 27 | return super().OnPopup(widget, popup_handle) 28 | 29 | # 定义自定义动作处理程序 30 | class MyActionHandler(action_handler_t): 31 | def __init__(self, callback): 32 | super().__init__() 33 | self.callback = callback 34 | 35 | def activate(self, ctx): 36 | self.callback() 37 | return 1 38 | 39 | def update(self, ctx): 40 | return 1 # Always enabled 41 | 42 | # 注册动作 43 | def register_actions(): 44 | action1 = action_desc_t( 45 | "my:action1", # 唯一标识符 46 | "Option 1", # 菜单显示名称 47 | MyActionHandler(lambda: print("Option 1 selected")), 48 | None, # 热键 49 | "Select Option 1", # 提示信息 50 | -1 # 图标 51 | ) 52 | action2 = action_desc_t( 53 | "my:action2", 54 | "Option 2", 55 | MyActionHandler(lambda: print("Option 2 selected")), 56 | None, 57 | "Select Option 2", 58 | -1 59 | ) 60 | register_action(action1) 61 | register_action(action2) 62 | 63 | def unregister_actions(): 64 | unregister_action("my:action1") 65 | unregister_action("my:action2") 66 | 67 | # 创建视图实例 68 | register_actions() 69 | viewer = MyCustomViewer() 70 | if viewer.Create(): 71 | viewer.Show() 72 | else: 73 | print("Failed to create viewer") 74 | -------------------------------------------------------------------------------- /plugs/z3Demo1.py: -------------------------------------------------------------------------------- 1 | # a1[2] + *a1 - 2005823921 + 2005823944 - (-(~a1[2] + a1[1] - *a1 + 12) - 17 - a1[1]); 2 | 3 | from z3 import * 4 | 5 | # 定义变量 6 | a1_2 = BitVec('a1_2', 32) 7 | a1_1 = BitVec('a1_1', 32) 8 | a1_0 = BitVec('a1_0', 32) # *a1 9 | 10 | # 定义复杂表达式 11 | part1 = a1_2 + a1_0 - 2005823921 + 2005823944 12 | part2 = -(~a1_2 + a1_1 - a1_0 + 12) - 17 - a1_1 13 | expr = part1 - part2 14 | 15 | # 创建求解器 16 | solver = Solver() 17 | 18 | # 简化表达式 19 | result = simplify(expr) 20 | 21 | print(result) -------------------------------------------------------------------------------- /plugs/z3_2.py: -------------------------------------------------------------------------------- 1 | from z3 import * 2 | # 创建一个整数变量x 3 | x = BitVec('x',32) 4 | # 创建一个Z3求解器实例 5 | solver = Solver() 6 | # 定义表达式 (((x-1)*x) & 1) != 0 7 | expr = ((((x - 1) * x) & 1) != 0) 8 | # 将表达式添加到求解器 9 | solver.add(expr) 10 | # 检查是否存在满足表达式的x值 11 | if solver.check() == sat: 12 | # 如果满足,打印满足条件的x值 13 | model = solver.model() 14 | print(f"满足条件的x值: {model[x]}") 15 | else: 16 | # 如果不满足,打印无解 17 | print("没有满足条件的x值") -------------------------------------------------------------------------------- /python3/ida_idc.py: -------------------------------------------------------------------------------- 1 | 2 | from sys import version_info as _swig_python_version_info 3 | if _swig_python_version_info < (2, 7, 0): 4 | raise RuntimeError("Python 2.7 or later required") 5 | 6 | # Import the low-level C/C++ module 7 | if __package__ or "." in __name__: 8 | from . import _ida_idc 9 | else: 10 | import _ida_idc 11 | 12 | try: 13 | import builtins as __builtin__ 14 | except ImportError: 15 | import __builtin__ 16 | 17 | def _swig_repr(self): 18 | try: 19 | strthis = "proxy of " + self.this.__repr__() 20 | except __builtin__.Exception: 21 | strthis = "" 22 | return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,) 23 | 24 | 25 | def _swig_setattr_nondynamic_instance_variable(set): 26 | def set_instance_attr(self, name, value): 27 | if name == "thisown": 28 | self.this.own(value) 29 | elif name == "this": 30 | set(self, name, value) 31 | elif hasattr(self, name) and isinstance(getattr(type(self), name), property): 32 | set(self, name, value) 33 | else: 34 | raise AttributeError("You cannot add instance attributes to %s" % self) 35 | return set_instance_attr 36 | 37 | 38 | def _swig_setattr_nondynamic_class_variable(set): 39 | def set_class_attr(cls, name, value): 40 | if hasattr(cls, name) and not isinstance(getattr(cls, name), property): 41 | set(cls, name, value) 42 | else: 43 | raise AttributeError("You cannot add class attributes to %s" % cls) 44 | return set_class_attr 45 | 46 | 47 | def _swig_add_metaclass(metaclass): 48 | """Class decorator for adding a metaclass to a SWIG wrapped class - a slimmed down version of six.add_metaclass""" 49 | def wrapper(cls): 50 | return metaclass(cls.__name__, cls.__bases__, cls.__dict__.copy()) 51 | return wrapper 52 | 53 | 54 | class _SwigNonDynamicMeta(type): 55 | """Meta class to enforce nondynamic attributes (no new attributes) for a class""" 56 | __setattr__ = _swig_setattr_nondynamic_class_variable(type.__setattr__) 57 | 58 | 59 | import weakref 60 | 61 | SWIG_PYTHON_LEGACY_BOOL = _ida_idc.SWIG_PYTHON_LEGACY_BOOL 62 | 63 | import ida_idaapi 64 | 65 | 66 | def mark_position(*args) -> "void": 67 | r""" 68 | mark_position(ea, lnnum, x, y, slot, comment) 69 | 70 | @param ea: ea_t 71 | @param lnnum: int 72 | @param x: short 73 | @param y: short 74 | @param slot: int32 75 | @param comment: char const * 76 | """ 77 | return _ida_idc.mark_position(*args) 78 | 79 | def get_marked_pos(*args) -> "ea_t": 80 | r""" 81 | get_marked_pos(slot) -> ea_t 82 | 83 | @param slot: int32 84 | """ 85 | return _ida_idc.get_marked_pos(*args) 86 | 87 | def get_mark_comment(*args) -> "PyObject *": 88 | r""" 89 | get_mark_comment(slot) -> PyObject * 90 | 91 | @param slot: int32 92 | """ 93 | return _ida_idc.get_mark_comment(*args) 94 | 95 | # 96 | # 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /python3/ida_srclang.py: -------------------------------------------------------------------------------- 1 | """ 2 | Third-party compiler support.""" 3 | 4 | from sys import version_info as _swig_python_version_info 5 | if _swig_python_version_info < (2, 7, 0): 6 | raise RuntimeError("Python 2.7 or later required") 7 | 8 | # Import the low-level C/C++ module 9 | if __package__ or "." in __name__: 10 | from . import _ida_srclang 11 | else: 12 | import _ida_srclang 13 | 14 | try: 15 | import builtins as __builtin__ 16 | except ImportError: 17 | import __builtin__ 18 | 19 | def _swig_repr(self): 20 | try: 21 | strthis = "proxy of " + self.this.__repr__() 22 | except __builtin__.Exception: 23 | strthis = "" 24 | return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,) 25 | 26 | 27 | def _swig_setattr_nondynamic_instance_variable(set): 28 | def set_instance_attr(self, name, value): 29 | if name == "thisown": 30 | self.this.own(value) 31 | elif name == "this": 32 | set(self, name, value) 33 | elif hasattr(self, name) and isinstance(getattr(type(self), name), property): 34 | set(self, name, value) 35 | else: 36 | raise AttributeError("You cannot add instance attributes to %s" % self) 37 | return set_instance_attr 38 | 39 | 40 | def _swig_setattr_nondynamic_class_variable(set): 41 | def set_class_attr(cls, name, value): 42 | if hasattr(cls, name) and not isinstance(getattr(cls, name), property): 43 | set(cls, name, value) 44 | else: 45 | raise AttributeError("You cannot add class attributes to %s" % cls) 46 | return set_class_attr 47 | 48 | 49 | def _swig_add_metaclass(metaclass): 50 | """Class decorator for adding a metaclass to a SWIG wrapped class - a slimmed down version of six.add_metaclass""" 51 | def wrapper(cls): 52 | return metaclass(cls.__name__, cls.__bases__, cls.__dict__.copy()) 53 | return wrapper 54 | 55 | 56 | class _SwigNonDynamicMeta(type): 57 | """Meta class to enforce nondynamic attributes (no new attributes) for a class""" 58 | __setattr__ = _swig_setattr_nondynamic_class_variable(type.__setattr__) 59 | 60 | 61 | import weakref 62 | 63 | SWIG_PYTHON_LEGACY_BOOL = _ida_srclang.SWIG_PYTHON_LEGACY_BOOL 64 | 65 | import ida_idaapi 66 | 67 | SRCLANG_C = _ida_srclang.SRCLANG_C 68 | r""" 69 | C. 70 | """ 71 | 72 | SRCLANG_CPP = _ida_srclang.SRCLANG_CPP 73 | r""" 74 | C++. 75 | """ 76 | 77 | SRCLANG_OBJC = _ida_srclang.SRCLANG_OBJC 78 | r""" 79 | Objective-C. 80 | """ 81 | 82 | SRCLANG_SWIFT = _ida_srclang.SRCLANG_SWIFT 83 | r""" 84 | Swift (not supported yet) 85 | """ 86 | 87 | SRCLANG_GO = _ida_srclang.SRCLANG_GO 88 | r""" 89 | Golang (not supported yet) 90 | """ 91 | 92 | 93 | def select_parser_by_name(*args) -> "bool": 94 | r""" 95 | select_parser_by_name(name) -> bool 96 | Set the parser with the given name as the current parser. Pass nullptr or an 97 | empty string to select the default parser. 98 | 99 | @param name: (C++: const char *) char const * 100 | @return: false if no parser was found with the given name 101 | """ 102 | return _ida_srclang.select_parser_by_name(*args) 103 | 104 | def select_parser_by_srclang(*args) -> "bool": 105 | r""" 106 | select_parser_by_srclang(lang) -> bool 107 | Set the parser that supports the given language(s) as the current parser. The 108 | selected parser must support all languages specified by the given srclang_t. 109 | 110 | @param lang: (C++: srclang_t) 111 | @return: false if no such parser was found 112 | """ 113 | return _ida_srclang.select_parser_by_srclang(*args) 114 | 115 | def set_parser_argv(*args) -> "int": 116 | r""" 117 | set_parser_argv(parser_name, argv) -> int 118 | Set the command-line args to use for invocations of the parser with the given 119 | name 120 | 121 | @param parser_name: (C++: const char *) name of the target parser 122 | @param argv: (C++: const char *) argument list 123 | @retval -1: no parser was found with the given name 124 | @retval -2: the operation is not supported by the given parser 125 | @retval 0: success 126 | """ 127 | return _ida_srclang.set_parser_argv(*args) 128 | 129 | def parse_decls_for_srclang(*args) -> "int": 130 | r""" 131 | parse_decls_for_srclang(lang, til, input, is_path) -> int 132 | Parse type declarations in the specified language 133 | 134 | @param lang: (C++: srclang_t) the source language(s) expected in the input 135 | @param til: (C++: til_t *) type library to store the types 136 | @param input: (C++: const char *) input source. can be a file path or decl string 137 | @param is_path: (C++: bool) true if input parameter is a path to a source file, false if the 138 | input is an in-memory source snippet 139 | @retval -1: no parser was found that supports the given source language(s) 140 | @retval else: the number of errors encountered in the input source 141 | """ 142 | return _ida_srclang.parse_decls_for_srclang(*args) 143 | 144 | def parse_decls_with_parser(*args) -> "int": 145 | r""" 146 | parse_decls_with_parser(parser_name, til, input, is_path) -> int 147 | Parse type declarations using the parser with the specified name 148 | 149 | @param parser_name: (C++: const char *) name of the target parser 150 | @param til: (C++: til_t *) type library to store the types 151 | @param input: (C++: const char *) input source. can be a file path or decl string 152 | @param is_path: (C++: bool) true if input parameter is a path to a source file, false if the 153 | input is an in-memory source snippet 154 | @retval -1: no parser was found with the given name 155 | @retval else: the number of errors encountered in the input source 156 | """ 157 | return _ida_srclang.parse_decls_with_parser(*args) 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /python3/idaapi.py: -------------------------------------------------------------------------------- 1 | 2 | import sys 3 | 4 | from ida_hexrays import * 5 | from ida_allins import * 6 | from ida_auto import * 7 | from ida_bitrange import * 8 | from ida_bytes import * 9 | from ida_dbg import * 10 | from ida_diskio import * 11 | from ida_dirtree import * 12 | from ida_entry import * 13 | from ida_enum import * 14 | from ida_expr import * 15 | from ida_fixup import * 16 | from ida_fpro import * 17 | from ida_frame import * 18 | from ida_funcs import * 19 | from ida_gdl import * 20 | from ida_graph import * 21 | from ida_ida import * 22 | from ida_idaapi import * 23 | from ida_idc import * 24 | from ida_idd import * 25 | from ida_idp import * 26 | from ida_ieee import * 27 | from ida_kernwin import * 28 | from ida_lines import * 29 | from ida_loader import * 30 | from ida_moves import * 31 | from ida_nalt import * 32 | from ida_name import * 33 | from ida_netnode import * 34 | from ida_offset import * 35 | from ida_pro import * 36 | from ida_problems import * 37 | from ida_range import * 38 | from ida_registry import * 39 | from ida_search import * 40 | from ida_segment import * 41 | from ida_segregs import * 42 | from ida_srclang import * 43 | from ida_strlist import * 44 | from ida_struct import * 45 | from ida_tryblks import * 46 | from ida_typeinf import * 47 | from ida_ua import * 48 | from ida_xref import * 49 | 50 | # guerilla-patch a few unfortunate overrides 51 | from ida_funcs import set_func_start 52 | from ida_funcs import set_func_end 53 | from ida_dbg import dbg_can_query 54 | 55 | class idaapi_Cvar(object): 56 | def __init__(self): 57 | # prevent endless recursion 58 | object.__setattr__(self, "modules", "hexrays,allins,auto,bitrange,bytes,dbg,diskio,dirtree,entry,enum,expr,fixup,fpro,frame,funcs,gdl,graph,ida,idaapi,idc,idd,idp,ieee,kernwin,lines,loader,moves,nalt,name,netnode,offset,pro,problems,range,registry,search,segment,segregs,srclang,strlist,struct,tryblks,typeinf,ua,xref".split(",")) 59 | object.__setattr__(self, "cvars_entries", dict()) 60 | 61 | def _get_module_cvar(self, modname): 62 | mod = sys.modules["ida_%s" % modname] 63 | cv, entries = None, None 64 | if hasattr(mod, "cvar"): 65 | cv = getattr(mod, "cvar") 66 | entries = [] 67 | if cv: 68 | if modname in self.cvars_entries.keys(): 69 | entries = self.cvars_entries[modname] 70 | else: 71 | # Parse 'str' version of cvar. Although this is braindeader than 72 | # braindead, I'm not sure there's another way to do it. 73 | entries_s = str(cv) 74 | entries = entries_s[1:len(entries_s)-1].split(", ") 75 | self.cvars_entries[modname] = entries 76 | return cv, entries 77 | 78 | def __getattr__(self, attr): 79 | for mod in self.modules: 80 | cv, entries = self._get_module_cvar(mod) 81 | if cv and attr in entries: 82 | return getattr(cv, attr) 83 | 84 | def __setattr__(self, attr, value): 85 | for mod in self.modules: 86 | cv, entries = self._get_module_cvar(mod) 87 | if cv and attr in entries: 88 | setattr(cv, attr, value) 89 | 90 | 91 | cvar = idaapi_Cvar() 92 | -------------------------------------------------------------------------------- /python3/init.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # ----------------------------------------------------------------------- 3 | # IDAPython - Python plugin for Interactive Disassembler 4 | # 5 | # Copyright (c) The IDAPython Team 6 | # 7 | # All rights reserved. 8 | # 9 | # For detailed copyright information see the file COPYING in 10 | # the root of the distribution archive. 11 | # ----------------------------------------------------------------------- 12 | # init.py - Essential init routines 13 | # ----------------------------------------------------------------------- 14 | from __future__ import print_function 15 | import os 16 | import sys 17 | import time 18 | import warnings 19 | 20 | # Prepare sys.path so loading of the shared objects works 21 | lib_dynload = os.path.join( 22 | sys.executable, 23 | IDAPYTHON_DYNLOAD_BASE, 24 | "python", 25 | str(sys.version_info.major)) 26 | 27 | is_x64 = sys.maxsize >= 0x100000000 28 | if is_x64: 29 | # x64 python requires our lib_dynload to be added; sys.path seems 30 | # to be composed differently than x86 builds. 31 | # In addition, we always want our own lib-dynload to come first: 32 | # the PyQt (& sip) modules that might have to be loaded, should 33 | # be the ones shipped with IDA and not those possibly available 34 | # on the system. 35 | sys.path.insert(0, os.path.join(lib_dynload, IDAPYTHON_DYNLOAD_RELPATH)) 36 | sys.path.insert(0, lib_dynload) 37 | else: 38 | # for non-x64 platforms, make sure everything works as it used to, 39 | # by appending our own lib-dynload to sys.argv.. 40 | sys.path.append(os.path.join(lib_dynload, IDAPYTHON_DYNLOAD_RELPATH)) 41 | 42 | try: 43 | import ida_idaapi 44 | import ida_kernwin 45 | import ida_diskio 46 | except ImportError as e: 47 | print("Import failed: %s. Current sys.path:" % str(e)) 48 | for p in sys.path: 49 | print("\t%s" % p) 50 | raise 51 | 52 | 53 | # ----------------------------------------------------------------------- 54 | # Take over the standard text outputs 55 | # ----------------------------------------------------------------------- 56 | class IDAPythonStdOut: 57 | """ 58 | Dummy file-like class that receives stout and stderr 59 | """ 60 | encoding = "UTF-8" 61 | 62 | def write(self, text): 63 | # NB: in case 'text' is Unicode, msg() will decode it 64 | # and call msg() to print it 65 | ida_kernwin.msg(text) 66 | 67 | def flush(self): 68 | pass 69 | 70 | def isatty(self): 71 | return False 72 | 73 | # ----------------------------------------------------------------------- 74 | def runscript(script): 75 | """ 76 | Executes a script. 77 | This function is present for backward compatiblity. Please use idaapi.IDAPython_ExecScript() instead 78 | 79 | @param script: script path 80 | 81 | @return: Error string or None on success 82 | """ 83 | 84 | import ida_idaapi 85 | return ida_idaapi.IDAPython_ExecScript(script, globals()) 86 | 87 | # ----------------------------------------------------------------------- 88 | def print_banner(): 89 | banner = [ 90 | "Python %s " % sys.version, 91 | "IDAPython" + (" 64-bit" if ida_idaapi.__EA64__ else "") + " v%d.%d.%d %s (serial %d) (c) The IDAPython Team " % IDAPYTHON_VERSION 92 | ] 93 | sepline = '-' * (max([len(s) for s in banner])+1) 94 | 95 | print(sepline) 96 | print("\n".join(banner)) 97 | print(sepline) 98 | 99 | # ----------------------------------------------------------------------- 100 | 101 | # Redirect stderr and stdout to the IDA message window 102 | _orig_stdout = sys.stdout; 103 | _orig_stderr = sys.stderr; 104 | sys.stdout = sys.stderr = IDAPythonStdOut() 105 | 106 | # ----------------------------------------------------------------------- 107 | # Initialize the help, with our own stdin wrapper, that'll query the user 108 | # ----------------------------------------------------------------------- 109 | import pydoc 110 | class IDAPythonHelpPrompter: 111 | def readline(self): 112 | return ida_kernwin.ask_str('', 0, 'Help topic?') 113 | help = pydoc.Helper(input = IDAPythonHelpPrompter(), output = sys.stdout) 114 | 115 | # Assign a default sys.argv 116 | sys.argv = [""] 117 | 118 | # Have to make sure Python finds our modules 119 | sys.path.append(ida_diskio.idadir("python")) 120 | 121 | # Remove current directory from the top of the patch search 122 | if '' in sys.path: # On non Windows, the empty path is added 123 | sys.path.remove('') 124 | 125 | if os.getcwd() in sys.path: 126 | sys.path.remove(os.getcwd()) 127 | 128 | # ...and add it to the end if needed 129 | if not IDAPYTHON_REMOVE_CWD_SYS_PATH: 130 | sys.path.append(os.getcwd()) 131 | 132 | # Additional $IDAUSR-derived paths 133 | if IDAPYTHON_IDAUSR_SYSPATH: 134 | idausr_python_list = ida_diskio.get_ida_subdirs("python") 135 | for idausr_python in idausr_python_list: 136 | one = os.path.join(idausr_python, str(sys.version_info.major)) 137 | if one not in sys.path: 138 | sys.path.append(one) 139 | 140 | if IDAPYTHON_COMPAT_AUTOIMPORT_MODULES: 141 | # Import all the required modules 142 | from idaapi import get_user_idadir, cvar, Appcall, Form 143 | from idc import * 144 | from idautils import * 145 | import idaapi 146 | 147 | # Load the users personal init file 148 | userrc = os.path.join(ida_diskio.get_user_idadir(), "idapythonrc.py") 149 | if os.path.exists(userrc): 150 | ida_idaapi.IDAPython_ExecScript(userrc, globals()) 151 | 152 | # In Python3, some modules (e.g., subprocess) will load the 'signal' 153 | # module which, upon loading, will registers default handlers for some 154 | # signals. In particular, for SIGINT, which we don't want to handle 155 | # since it'll prevent us from killing IDA with Ctrl+C on a TTY. 156 | if sys.version_info.major >= 3: 157 | import signal 158 | signal.signal(signal.SIGINT, signal.SIG_DFL) 159 | 160 | # Also, embedded Python3 will not include the 'site packages' by 161 | # default, which means many packages provided by the distribution 162 | # would not be reachable. Let's provide a way to load them. 163 | import site 164 | for sp in site.getsitepackages(): 165 | if sp not in sys.path: 166 | sys.path.append(sp) 167 | 168 | # All done, ready to rock. 169 | -------------------------------------------------------------------------------- /python3/setup.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thehepta/D810Learn/52ad29245037f279411a2ddd6b8b6e3f15e9bc9a/python3/setup.py --------------------------------------------------------------------------------