A workflow is a way for you to define a new analysis phase that can be inserted into all of BinaryNinja's analysis framework. Workflows can be written in python or c++ however the python support is relatively new and my not be feature complete # Topology ![[topogrpahy.png]] The workflow that we implement for `CoolVM` is to clean up the singlular 1 char prints into a print that prints the entire string at once. Before: ![[before_workflow.png]] After: ![[after_workflow.png]] # Creating a workflow To create a workflow, we will - clone the exitsting workflow and all the analysis phases - register a new phase which we will implement a function that gets run for each function in the binary - insert this analysis phase, the order is important because we will build off analysis that was run earlier. - register this workflow - It will be imported and registered in the [[__init__]] ```python CoolVMPrintStrWf = Workflow().clone("CoolVMprintStrWorkflow") CoolVMPrintStrWf.register_activity(Activity("CoolVMprintStr",action=outline_prints)) CoolVMPrintStrWf.insert('core.function.analyzeTailCalls',['CoolVMprintStr']) ``` It is inserted BEFORE `core.function.analyzeTailCalls` because this is right after `MLIL` is generated and the workflow will operate and alter the `MLIL`. # outline_prints The outline function will perform a few steps: - find a call to the `print` instruction - iterate to the next instruction - for each instruction, if its a `print` then append the char being printed to a total string - if the instruction is not a `print` thenreplace all the prints before with `nop` and a call to `printStr` - append the string to the raw binaryview so that this complete string can be mapped into virutal address space and then referenced by the IL - if a string hasnt been appended yet, add a delimiter string before so the [[BinaryView]] loader knows where code ends and the complete strings begin - create segment/section for the newly created string - generate ssa form so the MLIL can progopgate up to the HLIL The action of the activity is a function that takes one argument `analysis_context`. Since the python API is not complete, you have to call a `core` function on analysis_context to grab the current function that is being analyzed. ```python def outline_prints(analysis_context): function = Function(handle=core.BNAnalysisContextGetFunction(analysis_context)) bv = function.view ``` One weird thing with creating MLIL expressions was that I had to subtract `1` from the expression ids of MLIL expressions just created. ```python mlil_const_ptr = function.mlil.expr(MediumLevelILOperation.MLIL_CONST_PTR, ExpressionIndex(STRING_BASE),size=4) call_param = function.mlil.expr(MediumLevelILOperation.MLIL_CALL_PARAM,2,function.mlil.add_operand_list([mlil_const_ptr])) mlil_intrinsic = function.mlil.expr(MediumLevelILOperation.MLIL_INTRINSIC,0,0,3,1,call_param-1) function.mlil.replace_expr(function.mlil[index-1],mlil_intrinsic) ``` # Running the workflow You will need to enable the workflow orchestration plugin from the settings as well as select the workflow to use. To enable it only for the current binary you are looking at, open the file with options so you can modify these settings only when loading a `CoolVM` binary. ![[workflow_settings.png]] # Code Can be found at [coolvm_binja/workflow.py at master · thisusernameistaken/coolvm_binja (github.com)](https://github.com/thisusernameistaken/coolvm_binja/blob/master/workflow.py) # Results The String table: ![[string_table.png]] The `printStr` instruction: ![[printStr.png]] //TODO: figure out how to make nops actually disappear //TODO: Replace exprs with correct ILSource address