4

sorry about the messy title! Basically here's what I want to know.

This might sound a bit silly but, if you have a disassembled instruction say:

jz 0x8048e1a

and you also have the source code, how do you go about figuring out what this instruction relates to in the source code?

I should probably mention that I am using Intel's PIN as the application that spits out these disassembled instructions, so I know which function/routine these instructions belong to, I just want to be a bit more precise.

Thanks for any help/hints.

8
  • Do you want to know where jz or some other specific assembly instruction comes from (a specific question), or do you want to know general methodologies on how to go about reverse-engineering assembly code to match against source code (a general question, probably too broad for SO/SE)?
    – ajp15243
    Jul 2 '14 at 15:02
  • Well, basically I have an application that spits out a dissembled instruction and now I have to go find what it corresponds to in the source code. The disassembled instruction that is spat out, is usually a jump instruction.
    – Faheem
    Jul 2 '14 at 15:04
  • 2
    @Achilles: impossible. there is no 1 on 1 mapping. multiple source code lines can end up in one asm insruction, as well as the other way round or mixed.
    – PlasmaHH
    Jul 2 '14 at 15:05
  • @PlasmaHH : Is there any possibility to at least figure out the Basic block that the instruction comes from?
    – Faheem
    Jul 2 '14 at 15:07
  • 8
    You cannot reasonable go from machine code back to source code. Particularly the optimizer makes a big olde mess of it. The other way around is feasible, ask your compiler to generate an assembly listing.
    – Hans Passant
    Jul 2 '14 at 15:15
3

If you have the source code (and the means to compile it), you shouldn't be using a decompiler. The compiler is already producing a mapping between the original source code and the assembly instructions; that's what debugging information is.

The easiest way to see it in action is to start the program in the debugger, set a breakpoint, and when that breakpoint hits, switch to the disassembly view. That'll show you the instructions, as well as the source lines which led to them. (If the compiler had optimizations enabled there won't always be a good correspondence between the two, but you can at least see the rudiments.)

You can also tell your compiler to stop after the code generation phase, and output an assembly file instead of object code. Depending on the compiler, you may also be able to get it to annotate the assembly with the source lines; this'll produce similar information to what you can see in the debugger's disassembly view.

The details of how you do all that depend entirely on your IDE. Read its documentation.

3

If you're using the GCC toolchain, you can use objdump to disassemble and mix with source code:

objdump -S executable > disasm.txt

See the objdump man pages for more options and information.

If you're using a different toolchain, see the docs for that toolchain.

3

For that specific instruction:

jz 0x8048e1a
  • it's a conditional jump, so search your function for an if, for, do/while or ternary expression (?:)
  • it jumps if the zero flag is set, but you'll need to check the previous instruction(s) to see what sets that flag. You may be able to line that logic up with one of the branch conditions you identified above
  • it jumps to an address - where is it? What does the code at that address do? You may be able to match that up with the contents of a conditional branch.
  • what happens if it doesn't branch? You may be able to line that up with a conditional branch (or the implicit jump of a branch not taken)
  • note that the compiler/optimizer are allowed to reverse the sense of comparisons and branches, for example to jump to a nested block, and then back

In general:

  • if you have the source, compile to assembler and include all the source and line number annotations your compiler supports
    • it may help to do this once with optimization disabled, so you get a fairly clean translation, and then compare this to the optimized version. The intermediate step can help clarify what your optimizer changed.
  • if you have the source but can't compile (or don't know the compile flags used), include all the source etc. annotations your disassembler supports instead
  • look for distinctive points in the code:
    • function entry and exit are easy to spot
    • calls to out-of-line (especially libc or other library) functions should show up cleanly, and limit the optimizer's ability to reorder at least the function's arguments, and logic dependent on its result
  • instruction-step through the code in a debugger, watching both the source line number and the instruction. The line number may jump around crazily if the optimizer has been busy, but it'll give you an idea how the two relate
  • just read the source and understand its logic and control flow. Then just read the assembly and understand it's logic and control flow. Then think about how they match up.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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