Автор оригинала: PACKT.
Узнайте, как обратным инженером исполняемого исполняемого в Linux — Hello World в этой статье Reginald Wong, исследователь ведущих болеводомохозяйств в Vipre Security, Global Company J2, охватывающую различные технологии безопасности, ориентированные на атаки и вредоносные программы.
Многие наши инструменты отлично работают в Linux. Эта статья обсудит, как изменить файл ELF, исследуя инструменты реверсива.
Для начала, давайте создадим Hello World Program. До того, как все остальное нам нужно убедиться, что инструменты, необходимые для его создания, установлены. Откройте терминал и введите следующую команду. Это может потребовать, чтобы вы ввести свой пароль Super User:
sudo apt install gcc
Compiler Common Compiler CREAD, GCC, обычно предустановлена в Linux. Откройте любой текстовый редактор и введите строки следующего кода, сохраняя его как Hello.c:
#includevoid main(void) { printf ("hello world!\n"); }
Вы можете использовать VIM в качестве текстового редактора, запустив VI из терминала. Чтобы компилировать и запустить программу, используйте следующие команды:
Файл Hello — это наш исполняемый файл Linux, который отображает сообщение в консоли. Теперь, на обращении этой программы.
Dlrow olleh
В качестве примера хорошей практики процесс обратного изменения программы сначала должен начать с правильной идентификации. Давайте начнем с файла:
Это 32-разрядный тип файла ELF. Файлы ELF — это нативные исполнители на платформах Linux. Следующая остановка, давайте возьмем быстрый взгляд на текстовые строки с командой строк:
Эта команда будет производить что-то вроде следующего выхода:
/lib/ld-linux.so.2 libc.so.6 _ IO_stdin_used puts __ libc_start_main __ gmon_start__ GLIBC_2.0 PTRh UWVS t$,U [^_ ] hello world! ;* 2$"( GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609 crtstuff.c __ JCR_LIST__ deregister_tm_clones __ do_global_dtors_aux completed.7209 __ do_global_dtors_aux_fini_array_entry frame_dummy __ frame_dummy_init_array_entry hello.c __ FRAME_END__ __ JCR_END__ __ init_array_end _ DYNAMIC __ init_array_start __ GNU_EH_FRAME_HDR _GLOBAL_OFFSET_TABLE_ __ libc_csu_fini _ ITM_deregisterTMCloneTable __ x86.get_pc_thunk.bx _ edata __ data_start puts@@GLIBC_2.0 __ gmon_start__ __ dso_handle _ IO_stdin_used __ libc_start_main@@GLIBC_2.0 __ libc_csu_init _ fp_hw __ bss_start main _ Jv_RegisterClasses __ TMC_END__ _ ITM_registerTMCloneTable .symtab .strtab .shstrtab .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame .init_array .fini_array .jcr .dynamic .got.plt .data .bss .comment
Строки перечислены в порядке с начала файла. Первая часть списка содержит наше сообщение и информацию компилятора. Первые две строки также показывают, какие библиотеки используются программой:
/lib/ld-linux.so.2 libc.so.6
Последняя часть списка содержит имена разделов файла. Мы только знаем о нескольких битах текста, которые мы поместили в наш C-код. Остальные помещаются туда на сам компилятор, как часть его кода, который готовится и заканчивает изящное исполнение нашего кода.
Разборка в Linux — это просто командная линия. Используя параметр -D-параметр команды objdump, мы должны иметь возможность показать разборку исполняемого кода. Возможно, вам придется трусить вывод в файл, используя эту командную строку:
objdump -d hello > disassembly.asm
Выходной файл, разборка .asm, должен содержать следующий код:
Если вы заметите, что синтаксис разборки отличается от формата языка Intel Assembly язык, который мы узнали. Вот что мы видим, — это синтаксис разборки AT & T. Чтобы получить синтаксис Intel, нам нужно использовать параметр Intel -m, следующим образом:
objdump -M intel -d hello > disassembly.asm
Вывод должен дать нам этот результат разборки:
Результат показывает код разборки каждой функции. Таким образом, было в общей сложности 15 функций из исполняемых разделов:
Disassembly of section .init: 080482a8 <_ init>: Disassembly of section .plt: 080482d0: 080482e0 : 080482f0 <__libc_start_main@plt>: Disassembly of section .plt.got: 08048300 <.plt.got>: Disassembly of section .text: 08048310 <_ start>: 08048340 <__ x86.get_pc_thunk.bx>: 08048350 : 08048380 : 080483c0 <__ do_global_dtors_aux>: 080483e0 : 0804840b : 08048440 <__libc_csu_init>: 080484a0 <__libc_csu_fini>: Disassembly of section .fini: 080484a4 <_ fini>:
Разборка нашего кода обычно находится в разделе .Text. И, поскольку это компиляция GCC-скомпилированная программа, мы можем пропустить весь код инициализации и отправиться прямо к основной функции, в которой находится наш код:
Я подчеркнул вызов API на поставленных классах. PUTS API также является версией Printf. GCC был достаточно умным, чтобы выбрать, набрал Printf по той причине, что строка не была интерпретирована как строка форматирования стилей C-стилей. Строка форматирования или форматировала, содержит управляющие символы, которые обозначены с помощью% знака, такими как% DVer Integer и% s для строки. По сути, MEDS используется для неформатных строк, в то время как printf используется для форматированных строк.
Что мы собрались до сих пор?
Предполагая, что у нас нет идеи исходного кода, это информация, которую мы набрали до сих пор: • Файл является 32-разрядным исполняемым ELF. • Он был составлен с использованием GCC. • Он имеет 15 исполняемых функций, включая функцию Main (). • Код использует общие библиотеки Linux: libc.co и ld-inux.so. • На основании кода разборки программа, как ожидается, просто покажет сообщение. • Ожидается, что программа отобразит сообщение, используя PUT.
Динамический анализ
Теперь давайте сделаем какой-то динамический анализ. Помните, что динамический анализ должен быть сделан в среде песочницы. Существует несколько инструментов, которые обычно предустановлены в Linux, которые можно использовать для отображения более подробной информации. Мы представляем LTRACE, STRACE и GDB для этой реверсирующей активности.
Вот как используется ltrace:
Выход Ltrace показывает читаемый код того, что сделала программа. Ltrace зарегистрированные библиотеки функционируют, что программа вызывается и получена. Это называется, чтобы отобразить сообщение. Он также получил выходной статус 13, когда программа прекращена.
Адрес 0x804840B также является адресом основной функции, перечисленной в результатах разборки.
stroce — это другой инструмент, который мы можем использовать, но это системные вызовы журналов. Вот результат беговой стрелки в нашей главной программе Hello:
Race зарегистрирована каждая система, которая произошла, начиная с того, когда он выполняется системой. Execve — это первый системный вызов, который был зарегистрирован. Вызова Execve запускает программу, указанную в файле имени файла в его функциональном аргументе. Открыть и читайте системные вызовы, которые используются здесь для чтения файлов. MMAP2, MPROTECT и BRK несут ответственность за действия памяти, такие как распределение, разрешения и граничные настройки сегмента.
Глубоко внутри кода ставят, в конечном итоге он выполняет вызов системы записи. Пишите, в общем, пишет данные на объект, на который он был указан. Обычно он используется для записи в файл. В этом случае первый параметр записи имеет значение 1. Значение 1 обозначает STDOUT, которая является ручкой для вывода консоли. Второй параметр — это сообщение, таким образом, он записывает сообщение на STDOUT.
Идти дальше с отладкой
Во-первых, нам нужно установить GDB, запустив следующую команду:
sudo apt install gdb
Установка должна выглядеть что-то подобное:
Затем используйте GDB для отладки программы Hello, следующим образом:
gdb ./hello
GDB можно управлять с помощью команд. Команды полностью перечислены в онлайн-документации, но просто поступление помощи может помочь нам с основы.
Вы также можете использовать GDB, чтобы показать разборку указанных функций, используя команду dissass. Например, давайте посмотрим, что произойдет, если мы используем основную команду разряда:
Затем, опять же нам дали разборку в синтаксисе AT & T. Чтобы установить GDB для использования синтаксиса Intel, используйте следующую команду:
set disassembly-flavor intel
Это должно дать нам синтаксис языка ассамблеи Intel следующим образом:
Чтобы разместить точку останова в главной функции, команда будет B * Main.
После размещения точки останова мы можем запустить программу, используя команду запуска. Мы должны в конечном итоге по адресу основной функции:
Чтобы получить текущие значения регистров, введите информационные реестры. Поскольку используются в 32-битной среде, используются расширенные регистры (то есть EAX, ECX, EDX, EBX и EIP). 64-битная среда будет отображать регистры с префиксом R-префикса (то есть Rax, RCX, RDX, RBX и RIP).
Теперь, когда мы находимся в главной функции, мы можем запустить каждую инструкцию с помощью STEPT в (команду STEPI) и шага (команду nexti). Обычно мы следуем за этим с помощью команды info регистров, чтобы увидеть, какие значения изменены.
Следите за входом в SI и раздирание, пока вы не достигнете линии, содержащей звонок 0x80482e0, ставит @ PLT. Вы должны в конечном итоге с этими раздиральными и информационными регистрами Регистрация:
The => найдено на левой стороне, указывает, где находится указатель инструкции. Регистры должны выглядеть аналогично этому:
До появления функции MEDS мы можем проверить, какие значения были нажаты в стек. Мы можем просмотреть это с x/8x $ ESP:
Далее нам нужно сделать шаг через (в) линии инструкции вызовов. Это должно отобразить следующее сообщение:
Но если вы использовали Si, указатель инструкции будет в The Trust Code Code. Мы все еще можем вернуться к тому, где мы остановились, используя до команды, сокращенные как U. Просто используя до нескольких шагов команд в одной инструкции. Вам придется указать место адреса, где он остановится. Это как временная точка останова. Не забудьте разместить звездочку перед адресом:
Остальные 6 строк кода восстанавливают значения EBP и ESP сразу после ввода основной функции, затем возвращаясь с RET. Помните, что инструкция звонков будет хранить обратный адрес в верхней части стека, прежде чем фактически переходить на адрес функции. Инструкция RET будет прочитать значение возврата, указанного на Espregister.
Значения ESP и EBP, сразу после ввода основной функции должны быть восстановлены до ретинструкции. Как правило, функция начинается с настройки собственной кадры стека для использования с локальными переменными функции.
Вот таблица, показывающая изменения в значении регистров ESP, EBP и ECX после инструкции по данному адресу.
Вы можете продолжать изучать код очистки после RET, либо просто заставить программу в конечном итоге завершаться с помощью продолжения или его сокращения, C, следующим образом:
Если вы нашли эту статью интересную, вы можете исследовать Оснащение обратной машины Чтобы реализовать методы обратного инженерных технологий для анализа программного обеспечения, используйте цели программного обеспечения и защищать от угроз безопасности, таких как вредоносные программы и вирусы. Если вы хотите проанализировать программное обеспечение, чтобы использовать свои слабости и укрепить свою защиту, то вам следует исследовать Оснащение обратной машины Отказ
Оригинал: «https://www.codementor.io/@packt/reverse-engineering-a-linux-executable-hello-world-rjceryk5d»