5

I need to get an ELF binary's total count of

  1. Function call instruction
  2. conditional jump (branch) instruction

The binary could be any CPU architecture, like x64, ARM, MIPS, Motorola 68K, etc.

It would be best if the disassembly tool can provide an intermediate representation/language.

I found there are several candidate options for that, like IDA Pro, Binary Ninja, Radare2, Capstone, Angr, Bap.

Finally feel Radare2 is a good tool to implement that. But I searched on the Internet, most tutorials show how to investigate specific code pieces for a specific function (e.g., main). How to directly get the whole IR (called ESIL) after disassembly?

Or any suggestions on accomplishing this task?

3 Answers 3

0

You can also do this with a Ghidra script:

  • Iterate through the instructions with an InstructionIterator

  • Get the instruction's FlowType with getFlowType()

  • Use Ghidra's isCall() and isConditional() methods

Ghidra is free, open-source and this approach works on various architectures.

0

You can do it using angr and their IL - vex.

The general logic will be:

  • Create angr project from the binary.
  • Create angr cfg.
  • Run on all cfg nodes.
  • Get the vex representation of each instruction from the cfg node.
  • On each instruction representation, check its jumpkind. For example 'Ijk_Call' is a call, regardless of the CPU architecture.
0

This is a really interesting question actually, there's several options and all have issues they need to overcome. I'm not very familiar with ELF (primarily Windows here) but the file format shouldn't make any difference regardless. Static analysis crawling the code section(s), which can be retrieved from the file format, and using a disassembler library such as DiStorm, is enough in many cases. For this you need something smart enough to skip past data in code (i.e embedded jump tables). Symbolic execution is another alternative, but what happens if you have obfuscated control flow branches relying on some dynamic value? Then there's further obfuscation in terms of complete junk being thrown into code which all needs to be skipped. You can also use runtime debugging, which of course has its own flaws, namely limitations on only following branches that have been executed, speed, and evading detection. I would say that in most cases the simplest solution is to write your own static analysis tool or taking advantage of something like IDA scripting. Assuming the majority of your targets are largely unobfuscated, this should be an effective and nearly universal tool (per CPU implementation). If your targets are protected then something like angr is probably more suited to your needs. If you're dealing with virtualization then that's an entirely different question that deserves an answer of its own.

2
  • What do you mean by targets are protected? Also, is there any way to know the dynamic values if obfuscated?
    – I Wonder
    Commented Oct 12, 2019 at 4:02
  • So what you essentially want is to build a Control Flow Graph (CFG) of the code in a binary. There are really only two ways to do this -- statically or dynamically. Using a static approach (specifically processing all code sections with a disassembler library, skipping data in code, etc.) is both the fastest and simplest to implement. However, if the target is protected (i.e obfuscated, virtualized, etc.) then static analysis will usually completely break because there are so many cases that will break the disassembler. Commented Oct 13, 2019 at 0:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.