Загрузка...

Поиск скрытых возможностей посредством перехода между случайными функциями в памяти

Тема в разделе Уроки реверсинга создана пользователем SilverDreams 22 мар 2019. 635 просмотров

Загрузка...
  1. SilverDreams
    SilverDreams Автор темы 22 мар 2019 Мамонт 1 18 июн 2017
    Иногда реверс-инжиниринг бывает элегантен и целенаправлен, когда, например, вы решаете сложную задачу и пытаетесь выяснить предназначение непонятной незадокументированной функции, и как можно использовать эту функцию наилучшим образом. Однако эта статья про другое.

    В этой заметке мы рассмотрим поиск скрытых возможностей посредством перехода между случайными функциями в памяти! Подобным образом обычно легко вызвать крах приложения, но может произойти и так, что вы найдете нечто полезное.


    [IMG]


    Данная техника полезна для поиска скрытой функциональности, но есть некоторые ограничения. Метод работает только для приложений, которые вы можете отлаживать. За некоторыми исключениями (например, я использовал подобную технику для выхода из криво реализованной песочницы), приведенный способ в основном пригоден для анализа, а не для эксплуатации уязвимостей или расширения привилегий.

    Создание тестового бинарного файла

    Начнем с создания простейшей подопытной программы. Вы можете воспользоваться как 32 / 64-битным бинарником для Linux, так и исходным кодом и Make-файлом.

    Код

    #include <stdio.h>

    void random_function() {
    printf("You called me!\n");
    }

    int main(int argc, char *argv[]) {
    printf("Can you call random_function()?\n");
    printf("Press <enter> to continue\n");
    getchar();

    printf("Good bye!\n");
    }

    Сохраните этот код в файле jumpdemo.c и скомпилируйте при помощи следующей команды:

    Код
    gcc -g -O0 -o jumpdemo jumpdemo.c

    Опция -O0 добавляется для того, чтобы компилятор не выполнял оптимизации. Например, не удалял неиспользованные функции под видом «помощи». Если вы скачали вышеуказанный файл, то после распаковки архива можете просто запустить командуmake.

    В учебном контексте предположим, что все бинарные файлы компилируются с символами. То есть вы можете видеть имена функций! IDA– мое любимое приложение для анализа бинарных файлов, но для нашей цели утилиты nm более, чем достаточно:

    Код

    $ nm ./jumpdemo
    0000000000601040 B __bss_start
    0000000000601030 D __data_start
    0000000000601030 W data_start
    0000000000601038 D __dso_handle
    0000000000601040 D _edata
    0000000000601048 B _end
    0000000000400624 T _fini
    U getchar@@GLIBC_2.2.5
    w __gmon_start__
    0000000000400400 T _init
    0000000000400630 R _IO_stdin_used
    w _ITM_deregisterTMCloneTable
    w _ITM_registerTMCloneTable
    w _Jv_RegisterClasses
    0000000000400620 T __libc_csu_fini
    00000000004005b0 T __libc_csu_init
    U __libc_start_main@@GLIBC_2.2.5
    0000000000400577 T main
    U puts@@GLIBC_2.2.5
    0000000000400566 T random_function
    0000000000400470 T _start
    0000000000601040 D __TMC_END__

    Все, что вы видите выше, является символами. Функции с префиксом T можно вызывать. Однако функции, имена которых начинаются с символа нижнего подчеркивания, являются встроенными, и мы можем проигнорировать эти методы (но в реальной жизни, конечно, не стоит игнорировать все то, что начинается с нижнего подчеркивания).

    Для нас представляют интерес две функции «main» и «random_function».

    Загрузка файла в gdb

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

    Код

    $ gdb -q ./jumpdemo
    Reading symbols from ./jumpdemo...(no debugging symbols found)...done.
    (gdb)

    Флаг –q отключает вывод необязательных результатов. После того как вы окажетесь в командной строке (gdb),бинарный файл будет загружен и готов к запуску, но еще не запущен. Можно проверить, если, например, ввести команду continue:

    Код

    (gdb) continue
    The program is not being run.

    Отладчик gdb – очень мощная утилита со множеством различных команд. Общую справочную информацию можно получить, если ввести команду help.Также можно использовать команду help<command>для получения более детальной справки по конкретной команде (например, helpbreak). Попробуйте!

    Простой вызов
    Теперь, когда файл загружен в gdb, можно выполнить запуск при помощи команды run (перед этим не лишним будет ознакомиться со справкой help run!). Вы получите те же самые результаты, как если бы выполнение происходило обычным образом. В конце происходит возврат в начало. При желании можете продолжать запуск снова и снова, однако никаких полезных результатов вы не получите.

    Чтобы изменить логику работы во время выполнения приложения, нужно запустить программу и остановиться, пока не произошло завершение. Наиболее распространенный способ решить эту задачу – поставить точку останова (help break) на функции main:

    Код

    $ gdb -q ./jumpdemo
    Reading symbols from ./jumpdemo...(no debugging symbols found)...done.
    (gdb) break main
    Breakpoint 1 at 0x40057b
    (gdb)

    Затем запускаем программу и смотрим, что происходит:
    Код

    (gdb) run
    Starting program: /home/ron/blogs/jumpdemo

    Breakpoint 1, 0x000000000040057b in main ()
    (gdb)

    Теперь приложение оказалось приостановлено во время выполнения, и мы можем управлять дальнейшим процессом. Далее можно просматривать/редактировать участки памяти, изменять регистры, возобновлять выполнение, переходить к другим участкам кода и много всего другого!

    В нашем случае, как вы уже могли догадаться, мы будем перемещается выполнение в другую часть программы. Если говорить конкретно, то при помощи команды jump (helpjump!) будем возобновлять выполнение, начиная с функции random_function():

    Код

    $ gdb -q ./jumpdemo
    Reading symbols from ./jumpdemo...(no debugging symbols found)...done.
    (gdb) break main
    Breakpoint 1 at 0x40057b
    (gdb) run
    Starting program: /home/ron/blogs/jumpdemo

    Breakpoint 1, 0x000000000040057b in main ()
    (gdb) help jump
    Continue program being debugged at specified line or address.
    Usage: jump <location>
    Give as argument either LINENUM or *ADDR, where ADDR is an expression
    for an address to start at.
    (gdb) jump random_function
    Continuing at 0x40056a.
    You called me!
    [Inferior 1 (process 11391) exited with code 017]

    Задуманное реализовано! Программа вывела фразу «You called me!», а, значит, функция random_function() отработала успешно. Код 017 означает, что выход произошел не совсем корректно, однако мы сознательно пошли на этот шаг, когда запустили случайную функцию вне всякого контекста.

    Если по каким-то причинам вы не можете поставить точку останова (возможно, программа была скомпилирована без символов, или вы не знаете, где находится функция main), то можно проделать тот же самый трюк без точек останова, если нажать ctrl-c во время выполнения бинарного файла:

    Код

    (gdb) run
    Starting program: /home/ron/blogs/jumpdemo
    Can you call random_function()?
    Press <enter> to continue
    ^C
    Program received signal SIGINT, Interrupt.
    0x00007ffff7b04260 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:84
    84 ../sysdeps/unix/syscall-template.S: No such file or directory.
    (gdb) jump random_function
    Continuing at 0x40056a.
    You called me!
    Program received signal SIGSEGV, Segmentation fault.
    0x0000000000000001 in ?? ()


    Насчет segmentation fault не переживайте. Как и в случае с кодом 017, подобное сообщение выводится, если программа не понимает, что делать после выполнения функции random_function.

    Еще один трюк, который вы можете проделать, перейти обратно к main(), чтобы программа «думала», что начинается выполнение заново:

    Код

    (gdb) run
    Starting program: /home/ron/blogs/jumpdemo
    Can you call random_function()?
    Press <enter> to continue
    ^C
    Program received signal SIGINT, Interrupt.
    0x00007ffff7b04260 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:84
    84 ../sysdeps/unix/syscall-template.S: No such file or directory.
    (gdb) jump main
    Continuing at 0x40057b.
    Can you call random_function()?
    Press <enter> to continue

    Я часто использую переход к main во время разработки эксплоита, чтобы проверить, получилось ли выполнить код без создания рабочего шелл-кода. Если программа начинает выполняться с начала, значит, у нас получилось поменять текущую инструкцию!
     
Top