¿Cómo desmontar un ejecutable binario en Linux para obtener el código ensamblador?

Respuestas:

130

No creo que gcctenga una bandera, ya que es principalmente un compilador, pero otra de las herramientas de desarrollo GNU sí. objdumptoma una bandera -d/ --disassemble:

$ objdump -d /path/to/binary

El desmontaje se ve así:

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
Michael Mrozek
fuente
9
Para Intel-sintaxis: objdump -Mintel -d. O el desensamblador objconv de Agner Fog es el mejor que he probado hasta ahora (vea mi respuesta). Agregar etiquetas numeradas a los objetivos de las ramas es realmente agradable.
Peter Cordes
5
Opciones útiles: objdump -drwC -Mintel. -rmuestra las reubicaciones de la tabla de símbolos. -Cexige nombres C ++. -Wevita enrollar la línea para instrucciones largas. Si lo usa a menudo, esto es muy práctico: alias disas='objdump -drwC -Mintel'.
Peter Cordes
2
Agregar -Spara mostrar el código fuente mezclado con el desmontaje. (Como se señaló en otra respuesta .)
Alexander Pozdneev
45

Una alternativa interesante a objdump es gdb. No es necesario ejecutar el binario o tener 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  _init
0x00000000004003e0  __libc_start_main@plt
0x00000000004003f0  __gmon_start__@plt
0x0000000000400400  _start
0x0000000000400430  deregister_tm_clones
0x0000000000400460  register_tm_clones
0x00000000004004a0  __do_global_dtors_aux
0x00000000004004c0  frame_dummy
0x00000000004004f0  fce
0x00000000004004fb  main
0x0000000000400510  __libc_csu_init
0x0000000000400580  __libc_csu_fini
0x0000000000400584  _fini
(gdb) disassemble main
Dump 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 fce
Dump 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)

Con información de depuración completa, es aún mejor.

(gdb) disassemble /m main
Dump of assembler code for function main:
9       {
   0x00000000004004fb <+0>:     push   %rbp
   0x00000000004004fc <+1>:     mov    %rsp,%rbp
   0x00000000004004ff <+4>:     sub    $0x10,%rsp

10        int x = fce ();
   0x0000000000400503 <+8>:     callq  0x4004f0 <fce>
   0x0000000000400508 <+13>:    mov    %eax,-0x4(%rbp)

11        return x;
   0x000000000040050b <+16>:    mov    -0x4(%rbp),%eax

12      }
   0x000000000040050e <+19>:    leaveq 
   0x000000000040050f <+20>:    retq   

End of assembler dump.
(gdb)

objdump tiene una opción similar (-S)

Franco Miroslav
fuente
12

Esta respuesta es específica para x86. Herramientas portátiles que pueden desmontar AArch64, MIPS o cualquier código de máquina que incluya objdumpy llvm-objdump.


Desensamblador de Agner Fog , objconves bastante agradable. Agregará comentarios a la salida de desmontaje por problemas de rendimiento (como el temido bloqueo de LCP de instrucciones con constantes inmediatas de 16 bits, por ejemplo).

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

(No reconoce -como abreviatura de stdout y, por defecto, la salida a un archivo de nombre similar al archivo de entrada, con .asmtachuelas).

También agrega destinos de rama al código. Otros desensambladores generalmente desensamblan las instrucciones de salto con solo un destino numérico, y no colocan ningún marcador en un objetivo de rama para ayudarlo a encontrar la parte superior de los bucles, etc.

También indica los NOP con más claridad que otros desensambladores (dejando claro cuándo hay relleno, en lugar de desensamblarlo como una instrucción más).

Es de código abierto y fácil de compilar para Linux. Puede desmontarse en sintaxis NASM, YASM, MASM o GNU (AT&T).

Salida de muestra:

; Filling space: 0FH
; Filler type: Multi-byte NOP
;       db 0FH, 1FH, 44H, 00H, 00H, 66H, 2EH, 0FH
;       db 1FH, 84H, 00H, 00H, 00H, 00H, 00H

ALIGN   16

foo:    ; 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, 00H

ALIGN   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
...

Tenga en cuenta que esta salida está lista para volver a ensamblarse en un archivo de objeto, por lo que puede modificar el código en el nivel de fuente de asm, en lugar de con un editor hexadecimal en el código de máquina. (Por lo tanto, no está limitado a mantener las cosas del mismo tamaño). Sin cambios, el resultado debería ser casi idéntico. Sin embargo, puede que no lo sea, ya que el desmontaje de cosas como

  (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

no tiene nada en la fuente para asegurarse de que se ensambla con la codificación más larga que deja espacio para reubicaciones para reescribirlo con un desplazamiento de 32 bits.


Si no desea instalarlo objconv, GNU binutils objdump -Mintel -des muy útil y ya estará instalado si tiene una configuración gcc normal de Linux.

Peter Cordes
fuente
6

también está ndisasm, que tiene algunas peculiaridades, pero puede ser más útil si usa nasm. Estoy de acuerdo con Michael Mrozek en que objdump es probablemente lo mejor.

[más adelante] es posible que también desee ver la ciasdis de Albert van der Horst: http://home.hccnet.nl/awmvan.der.horst/forthassembler.html . Puede ser difícil de entender, pero tiene algunas características interesantes que probablemente no encontrará en ningún otro lugar.

jcomeau_ictx
fuente
2
En particular: home.hccnet.nl/awmvan.der.horst/ciasdis.html contiene en "últimas novedades" un paquete Debian que puede instalar fácilmente. Con las instrucciones adecuadas (realiza secuencias de comandos), generará un archivo fuente que se volverá a ensamblar exactamente en el mismo binario. No conozco ningún paquete que pueda hacer eso. Puede ser difícil de usar a partir de las instrucciones, tengo la intención de publicarlo en github con ejemplos extensos.
Albert van der Horst
3

Utilice IDA Pro y el Decompiler .

ta.speot.is
fuente
IDA parece un poco excesivo para esto, especialmente considerando que es bastante caro
Michael Mrozek
1
la versión gratuita no está disponible para Linux, solo la versión demo limitada. (Lástima porque, en Windows, es el mejor desensamblador que he usado)
Adrien Plisson
IDA es bueno, pero el problema de IDA es que te vuelves perezoso si lo usas para pequeñas tareas ... gdb hace el trabajo para la mayor parte de todo, gdb más fácil? no, pero posible.
cfernandezlinux
1

ht editor puede desensamblar binarios en muchos formatos. Es similar a Hiew, pero de código abierto.

Para desmontar, abra un binario, luego presione F6 y luego seleccione elf / image.

arbóreo84
fuente
1

Puede acercarse bastante (pero no puro) a generar un ensamblaje que se volverá a ensamblar, si eso es lo que pretende hacer, utilizando este truco de tubería bastante tosco y tediosamente largo (reemplace / bin / bash con el archivo que desea desensamblar y bash.S con lo que pretendes enviar la salida):

objdump --no-show-raw-insn -Matt,att-mnemonic -Dz /bin/bash | grep -v "file format" | grep -v "(bad)" | sed '1,4d' | cut -d' ' -f2- | cut -d '<' -f2 | tr -d '>' | cut -f2- | sed -e "s/of\ section/#Disassembly\ of\ section/" | grep -v "\.\.\." > bash.S

Sin embargo, tenga en cuenta cuánto tiempo es esto. Realmente desearía que hubiera una forma mejor (o, para el caso, un desensamblador capaz de generar código que un ensamblador reconocerá), pero desafortunadamente no la hay.

realkstrawn93
fuente
¡Guauu! Esto es fantástico. Por cierto, con respecto a su problema, ¿por qué no usa un alias para omitir este comando enorme?
Bat
0

Digamos que tienes:

#include <iostream>

double foo(double x)
{
  asm("# MyTag BEGIN"); // <- asm comment,
                        //    used later to locate piece of code
  double y = 2 * x + 1;

  asm("# MyTag END");

  return y;
}

int main()
{
  std::cout << foo(2);
}

Para obtener el código ensamblador usando gcc, puede hacer lo siguiente:

 g++ prog.cpp -c -S -o - -masm=intel | c++filt | grep -vE '\s+\.'

c++filt símbolos de demanda

grep -vE '\s+\.' elimina alguna información inútil

Ahora, si desea visualizar la parte etiquetada, simplemente use:

g++ prog.cpp -c -S -o - -masm=intel | c++filt | grep -vE '\s+\.' | grep "MyTag BEGIN" -A 20

Con mi computadora obtengo:

    # MyTag BEGIN
# 0 "" 2
#NO_APP
    movsd   xmm0, QWORD PTR -24[rbp]
    movapd  xmm1, xmm0
    addsd   xmm1, xmm0
    addsd   xmm0, xmm1
    movsd   QWORD PTR -8[rbp], xmm0
#APP
# 9 "poub.cpp" 1
    # MyTag END
# 0 "" 2
#NO_APP
    movsd   xmm0, QWORD PTR -8[rbp]
    pop rbp
    ret
.LFE1814:
main:
.LFB1815:
    push    rbp
    mov rbp, rsp

Un enfoque más amigable es usar: Compiler Explorer

Picaud Vincent
fuente
Esto solo es confiable con la optimización deshabilitada; de lo contrario, partes de las operaciones dentro de la región podrían optimizarse en cosas externas o optimizarse. Entonces solo puedes ver el tosco -O0asm.
Peter Cordes