어셈블리 코드를 얻기 위해 Linux에서 바이너리 실행 파일을 분해하는 방법은 무엇입니까?


85

디스어셈블러를 사용하라고 들었습니다. 합니까는 gcc아무것도 내장 한? 이를 수행하는 가장 쉬운 방법은 무엇입니까?



관련 : GCC / clang 어셈블리 출력에서 ​​"노이즈"를 제거하는 방법? -컴파일러가 무엇을했는지 정말로보고 싶다면 항상 컴파일 + 링크 + 디스 어셈블 할 필요가 없습니다.
Peter Cordes

답변:


137

gcc주로 컴파일러이기 때문에 플래그가 있다고 생각하지 않지만 다른 GNU 개발 도구는 있습니다. objdump소요 -d/ --disassemble플래그 :

$ objdump -d /path/to/binary

분해는 다음과 같습니다.

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

9
인텔 구문 : objdump -Mintel -d. 또는 Agner Fog의 objconv 디스어셈블러는 내가 시도한 것 중 가장 좋은 것입니다 (내 대답 참조). 분기 대상에 번호가 매겨진 레이블을 추가하는 것은 정말 좋습니다.
Peter Cordes 2015

5
유용한 옵션 : objdump -drwC -Mintel. -r기호 테이블에서 재배치를 보여줍니다. -CC ++ 이름을 손상시킵니다. -W긴 지침에 대한 줄 바꿈을 피합니다. 자주 사용한다면 편리합니다 : alias disas='objdump -drwC -Mintel'.
Peter Cordes

2
-S디스 어셈블리와 혼합 된 소스 코드를 표시하려면 추가 합니다. (AS는에서 지적 다른 답변 .)
알렉산더 Pozdneev

47

objdump의 흥미로운 대안은 gdb입니다. 바이너리를 실행하거나 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)

전체 디버깅 정보를 사용하면 훨씬 좋습니다.

(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에는 유사한 옵션 (-S)이 있습니다.


12

이 답변은 x86에만 해당됩니다. AArch64, MIPS 또는 모든 기계어 코드에 objdumpllvm-objdump.


Agner Fog의 디스어셈블러 , objconv는 꽤 좋습니다. 성능 문제에 대한 디스 어셈블리 출력에 주석을 추가합니다 (예 : 16 비트 즉시 상수를 사용하는 명령에서 LCP 중단과 같은).

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

( -표준 출력의 약자로 인식되지 않으며 기본적으로 입력 파일과 유사한 이름의 파일로 출력하는 것으로 .asm고정되어 있습니다.)

또한 코드에 분기 대상을 추가합니다. 다른 디스어셈블러는 일반적으로 숫자 대상 만있는 점프 명령어를 디스 어셈블하고 루프의 상단을 찾는 데 도움이되는 분기 대상에 마커를 배치하지 않습니다.

또한 다른 디스어셈블러보다 NOP를 더 명확하게 나타냅니다 (다른 명령어로 분해하는 것이 아니라 패딩이있을 때 명확하게 함).

오픈 소스이며 Linux 용으로 쉽게 컴파일 할 수 있습니다. NASM, YASM, MASM 또는 GNU (AT & T) 구문으로 분해 할 수 있습니다.

샘플 출력 :

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

이 출력은 객체 파일로 다시 어셈블 할 준비가되었으므로 기계 코드의 16 진 편집기가 아닌 asm 소스 레벨에서 코드를 조정할 수 있습니다. (따라서 동일한 크기를 유지하는 데 국한되지 않습니다.) 변경 사항이 없으면 결과는 거의 동일해야합니다. 그러나 다음과 같은 물건을 분해하기 때문에 그렇지 않을 수도 있습니다.

  (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

소스에 더 긴 인코딩으로 어셈블되는지 확인하기 위해 재배치가 32 비트 오프셋으로 다시 쓸 수있는 공간을 남겨 두도록하는 것이 없습니다.


objconv를 설치하지 않으려면 GNU binutils objdump -Mintel -d는 매우 유용하며 일반 Linux gcc 설정이있는 경우 이미 설치됩니다.


6

몇 가지 단점이 있지만 nasm을 사용하면 더 유용 할 수있는 ndisasm도 있습니다. 나는 objdump가 아마도 최고라는 Michael Mrozek의 의견에 동의합니다.

[나중에] Albert van der Horst의 ciasdis : http://home.hccnet.nl/awmvan.der.horst/forthassembler.html 을 확인하고 싶을 수도 있습니다 . 이해하기 어려울 수 있지만 다른 곳에서는 찾을 수없는 흥미로운 기능이 있습니다.


2
특히 : home.hccnet.nl/awmvan.der.horst/ciasdis.html 에는 "최신 개발"아래에 쉽게 설치할 수있는 데비안 패키지가 포함되어 있습니다. 적절한 지침 (스크립팅 수행)을 사용하면 정확히 동일한 바이너리로 다시 어셈블 될 소스 파일이 생성됩니다. 나는 그렇게 할 수있는 어떤 패키지도 알지 못한다. 지침에 따라 사용하기 어려울 수 있으며 광범위한 예제와 함께 github에 게시하려고합니다.
Albert van der Horst

4

IDA ProDecompiler를 사용하십시오 .


IDA는 특히 오히려 비싼 고려하고, 이에 대한 조금 잔인한 것 같다
마이클 Mrozek

1
무료 버전은 Linux에서 사용할 수 없으며 제한된 데모 버전 만 사용할 수 있습니다. (윈도우에서는 내가 사용해 본 최고의 디스어셈블러이기 때문에 너무 나쁘다)
Adrien Plisson

IDA는 좋지만 IDA의 문제는 작은 작업에 사용하면 게으르다는 것입니다. gdb는 대부분의 모든 작업을 수행합니다. gdb가 더 쉽습니다. 아니요,하지만 가능합니다.
cfernandezlinux


3

재 조립할 어셈블리를 생성하는 데 아주 가까이 다가 갈 수 있습니다. bash.S 출력을 보낼 대상) :

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

그러나 이것이 얼마나 긴지 주목하십시오. 더 나은 방법 (또는 어셈블러가 인식 할 수있는 코드를 출력 할 수있는 디스어셈블러)이 있었으면 좋겠지 만 안타깝게도 그렇지 않습니다.


와! 이건 끝내줘. Btw, 문제와 관련하여이 거대한 명령 입력을 건너 뛰기 위해 별칭을 사용하는 것이 어떻습니까?
Bat

1

ht 편집기 는 다양한 형식의 바이너리를 분해 할 수 있습니다. Hiew와 비슷하지만 오픈 소스입니다.

분해하려면 바이너리를 연 다음 F6을 누른 다음 elf / image를 선택합니다.


0

다음이 있다고 가정 해 보겠습니다.

#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);
}

gcc를 사용하여 어셈블리 코드를 얻으려면 다음을 수행하십시오.

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

c++filt 디 멘글 기호

grep -vE '\s+\.' 쓸모없는 정보를 제거합니다.

이제 태그가 지정된 부분을 시각화하려면 다음을 사용하십시오.

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

내 컴퓨터로 다음을 얻습니다.

    # 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

보다 친숙한 접근 방식은 다음을 사용하는 것입니다. 컴파일러 탐색기


이는 최적화가 비활성화 된 경우에만 안정적입니다. 그렇지 않으면 지역 내부 작업의 일부가 외부 작업으로 최적화되거나 최적화 될 수 있습니다. 그래서 당신은 투박한 -O0asm 만 볼 수 있습니다 .
Peter Cordes 2019
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.