디스어셈블러를 사용하라고 들었습니다. 합니까는 gcc
아무것도 내장 한? 이를 수행하는 가장 쉬운 방법은 무엇입니까?
디스어셈블러를 사용하라고 들었습니다. 합니까는 gcc
아무것도 내장 한? 이를 수행하는 가장 쉬운 방법은 무엇입니까?
답변:
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
objdump -Mintel -d
. 또는 Agner Fog의 objconv 디스어셈블러는 내가 시도한 것 중 가장 좋은 것입니다 (내 대답 참조). 분기 대상에 번호가 매겨진 레이블을 추가하는 것은 정말 좋습니다.
objdump -drwC -Mintel
. -r
기호 테이블에서 재배치를 보여줍니다. -C
C ++ 이름을 손상시킵니다. -W
긴 지침에 대한 줄 바꿈을 피합니다. 자주 사용한다면 편리합니다 : alias disas='objdump -drwC -Mintel'
.
-S
디스 어셈블리와 혼합 된 소스 코드를 표시하려면 추가 합니다. (AS는에서 지적 다른 답변 .)
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)이 있습니다.
이 답변은 x86에만 해당됩니다. AArch64, MIPS 또는 모든 기계어 코드에 objdump
및 llvm-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 설정이있는 경우 이미 설치됩니다.
몇 가지 단점이 있지만 nasm을 사용하면 더 유용 할 수있는 ndisasm도 있습니다. 나는 objdump가 아마도 최고라는 Michael Mrozek의 의견에 동의합니다.
[나중에] Albert van der Horst의 ciasdis : http://home.hccnet.nl/awmvan.der.horst/forthassembler.html 을 확인하고 싶을 수도 있습니다 . 이해하기 어려울 수 있지만 다른 곳에서는 찾을 수없는 흥미로운 기능이 있습니다.
IDA Pro 와 Decompiler를 사용하십시오 .
ODA가 유용 할 수 있습니다. 수많은 아키텍처를 지원하는 웹 기반 디스어셈블러입니다.
재 조립할 어셈블리를 생성하는 데 아주 가까이 다가 갈 수 있습니다. 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
그러나 이것이 얼마나 긴지 주목하십시오. 더 나은 방법 (또는 어셈블러가 인식 할 수있는 코드를 출력 할 수있는 디스어셈블러)이 있었으면 좋겠지 만 안타깝게도 그렇지 않습니다.
다음이 있다고 가정 해 보겠습니다.
#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
보다 친숙한 접근 방식은 다음을 사용하는 것입니다. 컴파일러 탐색기
-O0
asm 만 볼 수 있습니다 .