Подтвердить что ты не робот

Как разобрать основную функцию разделенного приложения?

Скажем, я скомпилировал приложение ниже и разделил его на символы.

#include <stdio.h>

int main()
{
    printf("Hello\n");
}

Процедура сборки:

gcc -o hello hello.c
strip --strip-unneeded hello

Если приложение не было удалено, разбор основной функции был бы простым. Однако я не знаю, как разобрать основную функцию разделенного приложения.

(gdb) disas main
No symbol table is loaded.  Use the "file" command.

(gdb) info line main
Function "main" not defined.

Как я мог это сделать? Возможно ли это?

Примечания: это должно выполняться только с помощью GDB. Забудьте objdump. Предположим, что у меня нет доступа к коду.

Поэтапный пример будет очень полезен.

4b9b3361

Ответ 1

Хорошо, вот большой выпуск моего предыдущего ответа. Я думаю, что нашел путь сейчас.

У вас (все еще) есть эта специфическая проблема:

(gdb) disas main
No symbol table is loaded.  Use the "file" command.

Теперь, если вы скомпилируете код (я добавил a return 0 в конце), вы получите gcc -S:

    pushq   %rbp
    movq    %rsp, %rbp
    movl    $.LC0, %edi
    call    puts
    movl    $0, %eax
    leave
    ret

Теперь вы можете видеть, что ваш двоичный файл дает вам информацию:

Полосатый:

(gdb) info files
Symbols from "/home/beco/Documents/fontes/cpp/teste/stackoverflow/distrip".
Local exec file:
    `/home/beco/Documents/fontes/cpp/teste/stackoverflow/distrip', file type elf64-x86-64.
    Entry point: 0x400440
    0x0000000000400238 - 0x0000000000400254 is .interp
    ...
    0x00000000004003a8 - 0x00000000004003c0 is .rela.dyn
    0x00000000004003c0 - 0x00000000004003f0 is .rela.plt
    0x00000000004003f0 - 0x0000000000400408 is .init
    0x0000000000400408 - 0x0000000000400438 is .plt
    0x0000000000400440 - 0x0000000000400618 is .text
    ...
    0x0000000000601010 - 0x0000000000601020 is .data
    0x0000000000601020 - 0x0000000000601030 is .bss

Самая важная запись здесь - .text. Это общее название для начала сборки кода, и из нашего объяснения основного ниже, из его размера, вы можете видеть, что он включает в себя основной. Если вы разберете его, вы увидите вызов __libc_start_main. Самое главное, вы разбираете хорошую точку входа, которая является реальным кодом (вы не вводите в заблуждение, чтобы изменить DATA на CODE).

disas 0x0000000000400440,0x0000000000400618
Dump of assembler code from 0x400440 to 0x400618:
   0x0000000000400440:  xor    %ebp,%ebp
   0x0000000000400442:  mov    %rdx,%r9
   0x0000000000400445:  pop    %rsi
   0x0000000000400446:  mov    %rsp,%rdx
   0x0000000000400449:  and    $0xfffffffffffffff0,%rsp
   0x000000000040044d:  push   %rax
   0x000000000040044e:  push   %rsp
   0x000000000040044f:  mov    $0x400540,%r8
   0x0000000000400456:  mov    $0x400550,%rcx
   0x000000000040045d:  mov    $0x400524,%rdi
   0x0000000000400464:  callq  0x400428 <[email protected]>
   0x0000000000400469:  hlt
   ...

   0x000000000040046c:  sub    $0x8,%rsp
   ...
   0x0000000000400482:  retq   
   0x0000000000400483:  nop
   ...
   0x0000000000400490:  push   %rbp
   ..
   0x00000000004004f2:  leaveq 
   0x00000000004004f3:  retq   
   0x00000000004004f4:  data32 data32 nopw %cs:0x0(%rax,%rax,1)
   ...
   0x000000000040051d:  leaveq 
   0x000000000040051e:  jmpq   *%rax
   ...
   0x0000000000400520:  leaveq 
   0x0000000000400521:  retq   
   0x0000000000400522:  nop
   0x0000000000400523:  nop
   0x0000000000400524:  push   %rbp
   0x0000000000400525:  mov    %rsp,%rbp
   0x0000000000400528:  mov    $0x40062c,%edi
   0x000000000040052d:  callq  0x400418 <[email protected]>
   0x0000000000400532:  mov    $0x0,%eax
   0x0000000000400537:  leaveq 
   0x0000000000400538:  retq   

Призыв __ libc_start_main получает в качестве первого аргумента указатель на main(). Итак, последний аргумент в стеке непосредственно перед вызовом - ваш основной() адрес.

   0x000000000040045d:  mov    $0x400524,%rdi
   0x0000000000400464:  callq  0x400428 <[email protected]>

Вот он 0x400524 (как мы уже знаем). Теперь вы установите точку останова, попробуйте это:

(gdb) break *0x400524
Breakpoint 1 at 0x400524
(gdb) run
Starting program: /home/beco/Documents/fontes/cpp/teste/stackoverflow/disassembly/d2 

Breakpoint 1, 0x0000000000400524 in main ()
(gdb) n
Single stepping until exit from function main, 
which has no line number information.
hello 1
__libc_start_main (main=<value optimized out>, argc=<value optimized out>, ubp_av=<value optimized out>, 
    init=<value optimized out>, fini=<value optimized out>, rtld_fini=<value optimized out>, 
    stack_end=0x7fffffffdc38) at libc-start.c:258
258 libc-start.c: No such file or directory.
    in libc-start.c
(gdb) n

Program exited normally.
(gdb) 

Теперь вы можете разобрать его, используя:

(gdb) disas 0x0000000000400524,0x0000000000400600
Dump of assembler code from 0x400524 to 0x400600:
   0x0000000000400524:  push   %rbp
   0x0000000000400525:  mov    %rsp,%rbp
   0x0000000000400528:  sub    $0x10,%rsp
   0x000000000040052c:  movl   $0x1,-0x4(%rbp)
   0x0000000000400533:  mov    $0x40064c,%eax
   0x0000000000400538:  mov    -0x4(%rbp),%edx
   0x000000000040053b:  mov    %edx,%esi
   0x000000000040053d:  mov    %rax,%rdi
   0x0000000000400540:  mov    $0x0,%eax
   0x0000000000400545:  callq  0x400418 <[email protected]>
   0x000000000040054a:  mov    $0x0,%eax
   0x000000000040054f:  leaveq 
   0x0000000000400550:  retq   
   0x0000000000400551:  nop
   0x0000000000400552:  nop
   0x0000000000400553:  nop
   0x0000000000400554:  nop
   0x0000000000400555:  nop
   ...

Это прежде всего решение.

Кстати, это другой код, чтобы увидеть, работает ли он. Вот почему сборка выше немного отличается. Код из этого c файла:

#include <stdio.h>

int main(void)
{
    int i=1;
    printf("hello %d\n", i);
    return 0;
}

Но!


Если это не сработает, у вас все еще есть некоторые подсказки:

Теперь вы должны искать, чтобы установить точки останова в начале всех функций. Они находятся непосредственно перед символом ret или leave. Первая точка входа - .text. Это запуск сборки, но не основной.

Проблема заключается в том, что не всегда точка останова будет запускать вашу программу. Подобно этому в самом .text:

(gdb) break *0x0000000000400440
Breakpoint 2 at 0x400440
(gdb) run
Starting program: /home/beco/Documents/fontes/cpp/teste/stackoverflow/disassembly/d2 

Breakpoint 2, 0x0000000000400440 in _start ()
(gdb) n
Single stepping until exit from function _start, 
which has no line number information.
0x0000000000400428 in [email protected] ()
(gdb) n
Single stepping until exit from function [email protected], 
which has no line number information.
0x0000000000400408 in ?? ()
(gdb) n
Cannot find bounds of current function

Итак, вам нужно продолжать попытки, пока не найдете свой путь, установив точки останова:

0x400440
0x40046c
0x400490
0x4004f4
0x40051e
0x400524

Из другого ответа мы должны сохранить эту информацию:

В нестрочной версии файла мы видим:

(gdb) disas main
Dump of assembler code for function main:
   0x0000000000400524 <+0>: push   %rbp
   0x0000000000400525 <+1>: mov    %rsp,%rbp
   0x0000000000400528 <+4>: mov    $0x40062c,%edi
   0x000000000040052d <+9>: callq  0x400418 <[email protected]>
   0x0000000000400532 <+14>:    mov    $0x0,%eax
   0x0000000000400537 <+19>:    leaveq 
   0x0000000000400538 <+20>:    retq   
End of assembler dump.

Теперь мы знаем, что main находится в 0x0000000000400524,0x0000000000400539. Если мы используем одно и то же смещение, чтобы посмотреть на полосатый бинарный файл, мы получим те же результаты:

(gdb) disas 0x0000000000400524,0x0000000000400539
Dump of assembler code from 0x400524 to 0x400539:
   0x0000000000400524:  push   %rbp
   0x0000000000400525:  mov    %rsp,%rbp
   0x0000000000400528:  mov    $0x40062c,%edi
   0x000000000040052d:  callq  0x400418 <[email protected]>
   0x0000000000400532:  mov    $0x0,%eax
   0x0000000000400537:  leaveq 
   0x0000000000400538:  retq   
End of assembler dump.

Итак, если вы не можете получить подсказку, где начинается основной запуск (например, с использованием другого кода с символами), другой способ заключается в том, что вы можете получить некоторую информацию о первых инструкциях по сборке, чтобы вы могли разборки в определенных местах и ​​посмотреть, Матчи. Если у вас вообще нет доступа к коду, вы все равно можете прочитать определение ELF, чтобы понять, сколько секций должно появиться в коде и попробовать рассчитанный адрес. Тем не менее, вам нужна информация о разделах в коде!

Это тяжелая работа, мой друг! Удачи!

Беко

Ответ 2

Как сделать info files, чтобы получить список разделов (с адресами) и оттуда?

Пример:

gdb) info files

Symbols from "/home/bob/tmp/t".
Local exec file:
`/home/bob/tmp/t', file type elf64-x86-64.
Entry point: 0x400490
0x0000000000400270 - 0x000000000040028c is .interp
0x000000000040028c - 0x00000000004002ac is .note.ABI-tag
    ....

0x0000000000400448 - 0x0000000000400460 is .init
    ....

Разберите .init:

(gdb) disas 0x0000000000400448,0x0000000000400460
Dump of assembler code from 0x400448 to 0x400460:
   0x0000000000400448:  sub    $0x8,%rsp
   0x000000000040044c:  callq  0x4004bc
   0x0000000000400451:  callq  0x400550
   0x0000000000400456:  callq  0x400650
   0x000000000040045b:  add    $0x8,%rsp
   0x000000000040045f:  retq   

Затем продолжайте и разбирайте остальные.

Если бы я был вами, и у меня была такая же версия GCC, с которой был создан ваш исполняемый файл, я бы рассмотрел последовательность функций, называемую фиктивным незанятым исполняемым файлом. Последовательность вызовов, вероятно, схожа в большинстве обычных случаев, так что вы можете перемалывать через последовательность запуска до вашего main путем сравнения. Оптимизации, вероятно, придет, хотя.

Если ваш бинарный файл лишен и оптимизирован, main может не существовать как "сущность" в двоичном формате; скорее всего, вы не можете получить намного лучше, чем этот тип процедуры.

Ответ 3

Там появился новый бесплатный инструмент, называемый unstrip из проекта в парадине (полное раскрытие: я работаю над этим проектом), который перепишет ваш двоичный код программы, добавит к нему информацию о символах и восстановит все (или почти все) функции в с легкостью сняли двойные файлы Elf. Он не будет определять основную функцию как "основную", но она ее найдет, и вы можете применить эвристику, которую вы уже упоминали выше, чтобы выяснить, какая функция является основной.

http://www.paradyn.org/html/tools/unstrip.html

Мне жаль, что это не решение gdb.

Ответ 4

IIRC, x/i <location> - ваш друг. Конечно, вам нужно выяснить, в каком месте вы хотите разобраться.