Como utilizar o ObjDump como ferramenta de análise

Ter acesso a uma ferramenta de análise enquanto está trabalhando com arquivos executáveis é sempre muito útil. Em sistemas POSIX (Linux, WSL, Cygwin, etc.) ObjDump é uma destas ferramentas. A mesma pode ser utilizada para extrair informação de arquivos objetos.

Aqui veremos como utilizar tal ferramenta e ter uma visão geral de como a mesma se comporta juntamente com a linguagem assembly.

O que é o ObjDump?

Conforme mencionado no início do artigo, ObjDump é uma ferramenta bastante útil para extrair informações de arquivos objetos. Esta ferramenta vem pré-instalada com a maioria das distribuições Linux. A seguir estão as opções de ajuda disponíveis ao executar (em inglês):

$ objdump
Usage: objdump <option(s)> <file(s)>
 Display information from object <file(s)>.
 At least one of the following switches must be given:
  -a, --archive-headers    Display archive header information
  -f, --file-headers       Display the contents of the overall file header
  -p, --private-headers    Display object format specific file header contents
  -P, --private=OPT,OPT... Display object format specific contents
  -h, --[section-]headers  Display the contents of the section headers
  -x, --all-headers        Display the contents of all headers
  -d, --disassemble        Display assembler contents of executable sections
  -D, --disassemble-all    Display assembler contents of all sections
      --disassemble=<sym>  Display assembler contents from <sym>
  -S, --source             Intermix source code with disassembly
      --source-comment[=<txt>] Prefix lines of source code with <txt>
  -s, --full-contents      Display the full contents of all sections requested
  -g, --debugging          Display debug information in object file
  -e, --debugging-tags     Display debug information using ctags style
  -G, --stabs              Display (in raw form) any STABS info in the file
  -W, --dwarf[a/=abbrev, A/=addr, r/=aranges, c/=cu_index, L/=decodedline,
              f/=frames, F/=frames-interp, g/=gdb_index, i/=info, o/=loc,
              m/=macro, p/=pubnames, t/=pubtypes, R/=Ranges, l/=rawline,
              s/=str, O/=str-offsets, u/=trace_abbrev, T/=trace_aranges,
              U/=trace_info]
                           Display the contents of DWARF debug sections
  -Wk,--dwarf=links        Display the contents of sections that link to
                            separate debuginfo files
  -WK,--dwarf=follow-links
                           Follow links to separate debug info files (default)
  -WN,--dwarf=no-follow-links
                           Do not follow links to separate debug info files
  -L, --process-links      Display the contents of non-debug sections in
                            separate debuginfo files.  (Implies -WK)
      --ctf[=SECTION]      Display CTF info from SECTION, (default '.ctf')
  -t, --syms               Display the contents of the symbol table(s)
  -T, --dynamic-syms       Display the contents of the dynamic symbol table
  -r, --reloc              Display the relocation entries in the file
  -R, --dynamic-reloc      Display the dynamic relocation entries in the file
  @<file>                  Read options from <file>
  -v, --version            Display this program's version number
  -i, --info               List object formats and architectures supported
  -H, --help               Display this information

Como extrair o código assembly

A ferramenta ObjDump pode ser usada para extrair código assembly de um binário já montado. Vamos começar analisando o seguinte programa em assembly para entender melhor a abordagem que pode ser usada.

No exemplo vamos utilizar um programa que demonstra a concorrência utilizando threads (concorrencia.c):

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

volatile int contador = 0;
int loops;

void *worker(void *arg) {
    int i;
    for (i = 0; i < loops; i++) {
        contador++;
    }
    return NULL;
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "uso: concorrencia <loops>\n");
        exit(1);
    }
    loops = atoi(argv[1]);
    pthread_t p1, p2;
    printf("Valor inicial : %d\n", contador);
    pthread_create(&p1, NULL, worker, NULL);
    pthread_create(&p2, NULL, worker, NULL);
    pthread_join(p1, NULL);
    pthread_join(p2, NULL);
    printf("Valor final   : %d\n", contador);
    return 0;
}

Para compilar digite em um terminal:

gcc -o concorrencia.exe main.c -O2 -Wall -pthread -pg

Para visualizar o conteúdo de um binário, podemos utilizar a opção -x como mostrado a seguir:

$ objdump.exe -x concorrencia.exe

concorrencia.exe:     file format pei-x86-64
concorrencia.exe
architecture: i386:x86-64, flags 0x0000013b:
HAS_RELOC, EXEC_P, HAS_DEBUG, HAS_SYMS, HAS_LOCALS, D_PAGED
start address 0x0000000100401000

Characteristics 0x26
        executable
        line numbers stripped
        large address aware

Time/Date               Tue Sep 20 08:11:12 2022
Magic                   020b    (PE32+)
MajorLinkerVersion      2
MinorLinkerVersion      39
SizeOfCode              0000000000001800
SizeOfInitializedData   0000000000003600
SizeOfUninitializedData 0000000000000400
AddressOfEntryPoint     0000000000001000
BaseOfCode              0000000000001000
ImageBase               0000000100400000
SectionAlignment        00001000
FileAlignment           00000200
MajorOSystemVersion     4
MinorOSystemVersion     0
MajorImageVersion       0
MinorImageVersion       0
MajorSubsystemVersion   5
MinorSubsystemVersion   2
Win32Version            00000000
SizeOfImage             00023000
SizeOfHeaders           00000600
CheckSum                00021ce5
Subsystem               00000003        (Windows CUI)
DllCharacteristics      00008000
                                        TERMINAL_SERVICE_AWARE
SizeOfStackReserve      0000000000200000
SizeOfStackCommit       0000000000001000
SizeOfHeapReserve       0000000000100000
SizeOfHeapCommit        0000000000001000
LoaderFlags             00000000
NumberOfRvaAndSizes     00000010

The Data Directory
Entry 0 0000000000000000 00000000 Export Directory [.edata (or where ever we found it)]
Entry 1 0000000000009000 000006c0 Import Directory [parts of .idata]
Entry 2 000000000000a000 000004e8 Resource Directory [.rsrc]
Entry 3 0000000000006000 0000018c Exception Directory [.pdata]
Entry 4 0000000000000000 00000000 Security Directory
Entry 5 000000000000b000 0000000c Base Relocation Directory [.reloc]
Entry 6 0000000000005000 0000001c Debug Directory
Entry 7 0000000000000000 00000000 Description Directory
Entry 8 0000000000000000 00000000 Special Directory
Entry 9 0000000000000000 00000000 Thread Storage Directory [.tls]
Entry a 0000000000000000 00000000 Load Configuration Directory
Entry b 0000000000000000 00000000 Bound Import Directory
Entry c 00000000000091bc 00000180 Import Address Table Directory
Entry d 0000000000000000 00000000 Delay Import Directory
Entry e 0000000000000000 00000000 CLR Runtime Header
Entry f 0000000000000000 00000000 Reserved

There is an import table in .idata at 0x100409000

The Import Tables (interpreted .idata section contents)
 vma:            Hint    Time      Forward  DLL       First
                 Table   Stamp     Chain    Name      Thunk
 00009000       0000903c 00000000 00000000 0000966c 000091bc

        DLL Name: cygwin1.dll
        vma:  Hint/Ord Member-Name Bound-To
        933c        7  __assert_func
        934c       15  __cxa_atexit
        935c       22  __errno
        9368       35  __getreent
        9378       46  __main
        9384      108  _dll_crt0
        9390      115  _impure_ptr
        93a0      236  atoi
        93a8      258  calloc
        93b4      323  close
        93bc      380  cygwin_detach_dll
        93d0      382  cygwin_internal
        93e4      410  dll_dllcrt0
        93f4      468  exit
        93fc      586  free
        9404      627  fwrite
        9410      654  getenv
        941c      688  getpid
        9428      712  gettimeofday
        9438      916  malloc
        9444      933  memcpy
        9450      938  memset
        945c     1005  open
        9464     1020  perror
        9470     1028  posix_memalign
        9484     1059  printf
        9490     1065  pthread_atfork
        94a4     1108  pthread_create
        94b8     1120  pthread_join
        94c8     1213  realloc
        94d4     1444  strlen
        94e0     1693  write

 00009014       00009144 00000000 00000000 000096b0 000092c4

        DLL Name: KERNEL32.dll
        vma:  Hint/Ord Member-Name Bound-To
        94e8      141  CloseHandle
        94f6      197  CreateEventA

Como mencionado anteriormente, usamos o programa “concorrencia.exe” como nosso alvo. A saída anterior mostra as informações extraídas do cabeçalho. Isso inclui os metadados do binário do tipo elf (com os detalhes como formato de arquivo, arquitetura, etc.), cabeçalho do programa, seções disponíveis no binário (.text, .rodata) e a tabela de símbolos do executável.

Exibindo o conteúdo de uma seção executável de um código assembler

A seção .text é utilizada em um código Assembly na pré construção do binário. Isso pode ser feito utilizando a opção -d como mostrado a seguir:

$ objdump -d concorrencia.exe

concorrencia:     file format elf32-i386

Disassembly of section .text:

08049000 <_start>:
8049000: ba 0e 00 00 00       mov    $0xe,%edx
8049005: b9 00 a0 04 08       mov    $0x804a000,%ecx
804900a: bb 01 00 00 00       mov    $0x1,%ebx
804900f: b8 04 00 00 00       mov    $0x4,%eax
8049014: cd 80                int    $0x80
8049016: b8 01 00 00 00       mov    $0x1,%eax
804901b: bb 00 00 00 00       mov    $0x0,%ebx
8049020: cd 80                int    $0x80

Como pode ser visto no exemplo acima o código assembly está no formato de sintax da AT&T, porém o mesmo pode ser modificado e mostrar seu conteúdo no formato x86 da Intel com a opção -M.

$ objdump -d concorrencia.exe -M intel

concorrencia:     file format elf32-i386

Disassembly of section .text:

08049000 <_start>:
8049000: ba 0e 00 00 00       mov    edx,0xe
8049005: b9 00 a0 04 08       mov    ecx,0x804a000
804900a: bb 01 00 00 00       mov    ebx,0x1
804900f: b8 04 00 00 00       mov    eax,0x4
8049014: cd 80                int    0x80
8049016: b8 01 00 00 00       mov    eax,0x1
804901b: bb 00 00 00 00       mov    ebx,0x0
8049020: cd 80                int    0x80

Como podemos observar a saída no formato Intel é similar a AT&T. No caso descrito o formato Intel não é o padrão de saída. Porém dependendo do ambiente em que você esteja utilizando o mesmo poderá ser. Neste caso é bom conhecer quais os registradores que compõe a saída ou utilizar a opção -M explicitamente.

Se precisar mostrar todas as seções do código assembly no padrão Intel digite a opção -D conforme a seguir.

$ objdump -D concorrencia.exe -M intel

concorrencia:     file format elf32-i386

Disassembly of section .text:

08049000 <_start>:
8049000: ba 0e 00 00 00       mov    edx,0xe
8049005: b9 00 a0 04 08       mov    ecx,0x804a000
804900a: bb 01 00 00 00       mov    ebx,0x1
804900f: b8 04 00 00 00       mov    eax,0x4
8049014: cd 80                int    0x80
8049016: b8 01 00 00 00       mov    eax,0x1
804901b: bb 00 00 00 00       mov    ebx,0x0
8049020: cd 80                int    0x80
Disassembly of section .rodata:

0804a000 <msg>:

804a000: 48                   dec    eax

804a001: 65 6c                gs ins BYTE PTR es:[edi],dx

804a003: 6c                   ins    BYTE PTR es:[edi],dx

804a004: 6f                   outs   dx,DWORD PTR ds:[esi]

804a005: 2c 20                sub    al,0x20

804a007: 77 6f                ja     804a078 <msg+0x78>

804a009: 72 6c                jb     804a077 <msg+0x77>

804a00b: 64 21 0a             and    DWORD PTR fs:[edx],ecx

Exibindo informações de depuração

Podemos também utilzar a opção -g para exibir informações a respeito de depuração de um binário. O resultado a seguir mostra estas informações de um arquivo compilado de um código C.

$ objdump -g mem

mem:     file format elf64-x86-64

Contents of the .eh_frame section (loaded from mem):

00000000 0000000000000014 00000000 CIE
Version:               1

Augmentation:          “zR”
Code alignment factor: 1
Data alignment factor: -8
Return address column: 16
Augmentation data:     1b
DW_CFA_def_cfa: r7 (rsp) ofs 8
DW_CFA_offset: r16 (rip) at cfa-8
DW_CFA_nop
DW_CFA_nop

Leave a Reply

Your email address will not be published. Required fields are marked *