How to disassemble a binary executable in Linux to get the assembly code? How to disassemble a binary executable in Linux to get the assembly code? linux linux

How to disassemble a binary executable in Linux to get the assembly code?


I don't think gcc has a flag for it, since it's primarily a compiler, but another of the GNU development tools does. objdump takes a -d/--disassemble flag:

$ objdump -d /path/to/binary

The disassembly looks like this:

080483b4 <main>: 80483b4:   8d 4c 24 04             lea    0x4(%esp),%ecx 80483b8:   83 e4 f0                and    $0xfffffff0,%esp 80483bb:   ff 71 fc                pushl  -0x4(%ecx) 80483be:   55                      push   %ebp 80483bf:   89 e5                   mov    %esp,%ebp 80483c1:   51                      push   %ecx 80483c2:   b8 00 00 00 00          mov    $0x0,%eax 80483c7:   59                      pop    %ecx 80483c8:   5d                      pop    %ebp 80483c9:   8d 61 fc                lea    -0x4(%ecx),%esp 80483cc:   c3                      ret     80483cd:   90                      nop 80483ce:   90                      nop 80483cf:   90                      nop


An interesting alternative to objdump is gdb. You don't have to run the binary or have debuginfo.

$ gdb -q ./a.out Reading symbols from ./a.out...(no debugging symbols found)...done.(gdb) info functions All defined functions:Non-debugging symbols:0x00000000004003a8  _init0x00000000004003e0  __libc_start_main@plt0x00000000004003f0  __gmon_start__@plt0x0000000000400400  _start0x0000000000400430  deregister_tm_clones0x0000000000400460  register_tm_clones0x00000000004004a0  __do_global_dtors_aux0x00000000004004c0  frame_dummy0x00000000004004f0  fce0x00000000004004fb  main0x0000000000400510  __libc_csu_init0x0000000000400580  __libc_csu_fini0x0000000000400584  _fini(gdb) disassemble mainDump of assembler code for function main:   0x00000000004004fb <+0>:     push   %rbp   0x00000000004004fc <+1>:     mov    %rsp,%rbp   0x00000000004004ff <+4>:     sub    $0x10,%rsp   0x0000000000400503 <+8>:     callq  0x4004f0 <fce>   0x0000000000400508 <+13>:    mov    %eax,-0x4(%rbp)   0x000000000040050b <+16>:    mov    -0x4(%rbp),%eax   0x000000000040050e <+19>:    leaveq    0x000000000040050f <+20>:    retq   End of assembler dump.(gdb) disassemble fceDump of assembler code for function fce:   0x00000000004004f0 <+0>:     push   %rbp   0x00000000004004f1 <+1>:     mov    %rsp,%rbp   0x00000000004004f4 <+4>:     mov    $0x2a,%eax   0x00000000004004f9 <+9>:     pop    %rbp   0x00000000004004fa <+10>:    retq   End of assembler dump.(gdb)

With full debugging info it's even better.

(gdb) disassemble /m mainDump of assembler code for function main:9       {   0x00000000004004fb <+0>:     push   %rbp   0x00000000004004fc <+1>:     mov    %rsp,%rbp   0x00000000004004ff <+4>:     sub    $0x10,%rsp10        int x = fce ();   0x0000000000400503 <+8>:     callq  0x4004f0 <fce>   0x0000000000400508 <+13>:    mov    %eax,-0x4(%rbp)11        return x;   0x000000000040050b <+16>:    mov    -0x4(%rbp),%eax12      }   0x000000000040050e <+19>:    leaveq    0x000000000040050f <+20>:    retq   End of assembler dump.(gdb)

objdump has a similar option (-S)


This answer is specific to x86. Portable tools that can disassemble AArch64, MIPS, or whatever machine code include objdump and llvm-objdump.


Agner Fog's disassembler, objconv, is quite nice. It will add comments to the disassembly output for performance problems (like the dreaded LCP stall from instructions with 16bit immediate constants, for example).

objconv  -fyasm a.out /dev/stdout | less

(It doesn't recognize - as shorthand for stdout, and defaults to outputting to a file of similar name to the input file, with .asm tacked on.)

It also adds branch targets to the code. Other disassemblers usually disassemble jump instructions with just a numeric destination, and don't put any marker at a branch target to help you find the top of loops and so on.

It also indicates NOPs more clearly than other disassemblers (making it clear when there's padding, rather than disassembling it as just another instruction.)

It's open source, and easy to compile for Linux. It can disassemble into NASM, YASM, MASM, or GNU (AT&T) syntax.

Sample output:

; Filling space: 0FH; Filler type: Multi-byte NOP;       db 0FH, 1FH, 44H, 00H, 00H, 66H, 2EH, 0FH;       db 1FH, 84H, 00H, 00H, 00H, 00H, 00HALIGN   16foo:    ; Function begin        cmp     rdi, 1                                  ; 00400620 _ 48: 83. FF, 01        jbe     ?_026                                   ; 00400624 _ 0F 86, 00000084        mov     r11d, 1                                 ; 0040062A _ 41: BB, 00000001?_020:  mov     r8, r11                                 ; 00400630 _ 4D: 89. D8        imul    r8, r11                                 ; 00400633 _ 4D: 0F AF. C3        add     r8, rdi                                 ; 00400637 _ 49: 01. F8        cmp     r8, 3                                   ; 0040063A _ 49: 83. F8, 03        jbe     ?_029                                   ; 0040063E _ 0F 86, 00000097        mov     esi, 1                                  ; 00400644 _ BE, 00000001; Filling space: 7H; Filler type: Multi-byte NOP;       db 0FH, 1FH, 80H, 00H, 00H, 00H, 00HALIGN   8?_021:  add     rsi, rsi                                ; 00400650 _ 48: 01. F6        mov     rax, rsi                                ; 00400653 _ 48: 89. F0        imul    rax, rsi                                ; 00400656 _ 48: 0F AF. C6        shl     rax, 2                                  ; 0040065A _ 48: C1. E0, 02        cmp     r8, rax                                 ; 0040065E _ 49: 39. C0        jnc     ?_021                                   ; 00400661 _ 73, ED        lea     rcx, [rsi+rsi]                          ; 00400663 _ 48: 8D. 0C 36...

Note that this output is ready to be assembled back into an object file, so you can tweak the code at the asm source level, rather than with a hex-editor on the machine code. (So you aren't limited to keeping things the same size.) With no changes, the result should be near-identical. It might not be, though, since disassembly of stuff like

  (from /lib/x86_64-linux-gnu/libc.so.6)SECTION .plt    align=16 execute                        ; section number 11, code?_00001:; Local function        push    qword [rel ?_37996]                     ; 0001F420 _ FF. 35, 003A4BE2(rel)        jmp     near [rel ?_37997]                      ; 0001F426 _ FF. 25, 003A4BE4(rel)...    ALIGN   8?_00002:jmp     near [rel ?_37998]                      ; 0001F430 _ FF. 25, 003A4BE2(rel); Note: Immediate operand could be made smaller by sign extension        push    11                                      ; 0001F436 _ 68, 0000000B; Note: Immediate operand could be made smaller by sign extension        jmp     ?_00001                                 ; 0001F43B _ E9, FFFFFFE0

doesn't have anything in the source to make sure it assembles to the longer encoding that leaves room for relocations to rewrite it with a 32bit offset.


If you don't want to install it objconv, GNU binutils objdump -Mintel -d is very usable, and will already be installed if you have a normal Linux gcc setup.