Краткий обзор ANGR
Какие только не встретишь определения, но своим языком я бы сказал, что ANGR это инструмент дающий возможность применить символическое выполнение к бинарным файлам. Сама его суть в этом: любой бинарный исполняемый файл можно рассмотреть как уравнение, части которого это не только какие-либо введенные пользователем значения, но и состояния регистров, значения памяти и т.д. Так вот Angr - подобный решатель таких уравнений)))
Работу фреймворка обеспечивают
Немного слов о загрузке
Формат загрузки angr.Project('name_of_project', load_options). Наиболее часто используется
поскольку ANGR по умолчанию включает библиотеки в проект и соответственно тратит время на их разбор. Кстати, в проект можно включить не только исполняемые файлы, но и дампы PE и ELF.
Загрузив проект, давайте взглянем на его архитектуру и порядок байтов с помощью BoyScout:
Out:
Можем также залезть под капот ANGR и взглянуть на то, как он выбирал нужный порядок байт и архитектуру
Out:
Таким образом получили некоторую информацию о поддерживаемых архитектурах.
Посмотрим на библиотеки, загружающиеся с нашим файлом
Собственно говоря здесь их не было. В противном случае их можно было бы отключить в опциях при перезагрузке проекта.
Вывод strings достаточно небольшой
Значит наш флаг, должен пройти 10 проверок, в результате мы получим Yes! Correct flag is... или соответствующее значение ошибки.
Снова вернемся к ANGR. Составим граф потока управления, сделать это можно с помощью CFGFast, CFGEmulated (в зависимости от использовании версии python может быть CFGAccurate). CFGFast дает преимущество по скорости (основан на анализе заголовка и границ функций), но если вы знаете, что ваш исследуемый образец поврежден, то лучше воспользоваться функционалом CFGEmulated.
Поскольку программа достаточно малых размеров (по этой причине позволяем себе использовать CFGEmulated), можем полностью распечатать ее CFG
Out:
Даже в нашем случае граф получился внушающий, поэтому здесь лишь его скромная часть, но тем не менее даже отсюда видны проверки флага. Куда эффективнее при анализе печатать лишь CFG отдельных интересующих функций. Получить их список можно таким образом
Out:
Немного отвлеклись от решения. Чтобы добраться до правильного вывода необходимо указать Angr к какому адресу ему надо дойти.
Получить адрес мы можем из распечатанного CFG
Возьмем адрес, который нам необходимо пройти для того чтобы мы получили
Теперь зададим первоначальное состояние:
Опять таки, в силу простоты решаемой задачи берем с самого начала, в особых случаях, можно переходить непосредственно к интересующим функциям.
Символическое выполнение позволяет выполнять SimulationManager. Задав изначальное состояние и указав конечное применим его
На этом этапе Angr начинает символическое исполнение. Формат достаточно простой
По истечении определенного срока, ANGR выдаст подобный результат
Out:
Где в результате мы имеем 1 решение для нашего “уравнения”
Out:
Поздравляю, мы решили таск. Причем замечу, мы не пользовались ни IDA, ни Ghidra…
Решение подобных заданий не требует участия реверс-инженера, если он имеет подобный скрипт за пазухой
ANGR - новое оружие в арсенале реверс-инженера, позволяющее автоматизировать большой пласт работы, ведь состояние нашей конечной системы ограничено лишь нашей фантазией и временем ее решения/достижения.
автор @Slekdzhov
взято с reverse4you.org
Какие только не встретишь определения, но своим языком я бы сказал, что ANGR это инструмент дающий возможность применить символическое выполнение к бинарным файлам. Сама его суть в этом: любой бинарный исполняемый файл можно рассмотреть как уравнение, части которого это не только какие-либо введенные пользователем значения, но и состояния регистров, значения памяти и т.д. Так вот Angr - подобный решатель таких уравнений)))
Работу фреймворка обеспечивают
- Начальное задаваемое состояние
- Модуль VEX, который эмулирует регистры, память и тд, учитывая особенности процессоров
- Модуль SimuVex, реализующий символические переходы между состояниями VEX
Покажу на примере возможности ANGR. Возьмем простой таск с forkbomb.ru 3 (CTF платформа).
Код:
import angr
p = angr.Project('name_of_project')
Формат загрузки angr.Project('name_of_project', load_options). Наиболее часто используется
Код:
load_options['auto_load_libs'] = False
Загрузив проект, давайте взглянем на его архитектуру и порядок байтов с помощью BoyScout:
Код:
boy = p.analyses.BoyScout()
print('arch ' + boy.arch)
print(boy.endianness)
Код:
arch X86
endianess Iend_LE
Код:
for arch, vote in boy.votes.items():
print(' '.join(arch) + ' = ' + str(vote))
Код:
AARCH64 Iend_LE = 0
ARMHF Iend_LE = 0
ARMEL Iend_BE = 0
ARMEL Iend_LE = 0
AARCH64 Iend_BE = 0
X86 Iend_LE = 22
S390X Iend_BE = 0
ARMHF Iend_BE = 0
PPC64 Iend_LE = 0
PPC32 Iend_LE = 0
Soot Iend_BE = 0
MIPS32 Iend_LE = 0
AMD64 Iend_LE = 6
MIPS64 Iend_LE = 0
MIPS32 Iend_BE = 0
MIPS64 Iend_BE = 0
PPC64 Iend_BE = 0
PPC32 Iend_BE = 0
Посмотрим на библиотеки, загружающиеся с нашим файлом
Код:
p.loader.shared_objects.items()
Вывод strings достаточно небольшой
Код:
!This program cannot be run in DOS mode.
.text
.data
Enter flag to check:
Wrong length!
Wrong check 1!
Wrong check 2!
Wrong check 3!
Wrong check 4!
Wrong check 5!
Wrong check 6!
Wrong check 7!
Wrong check 8!
Wrong check 9!
Wrong check 10!
No zero!
Yes! Correct flag is %s
msvcrt.dll
printf
scanf
strlen
atoi
_controlfp
__set_app_type__
getmainargs
exit
Снова вернемся к ANGR. Составим граф потока управления, сделать это можно с помощью CFGFast, CFGEmulated (в зависимости от использовании версии python может быть CFGAccurate). CFGFast дает преимущество по скорости (основан на анализе заголовка и границ функций), но если вы знаете, что ваш исследуемый образец поврежден, то лучше воспользоваться функционалом CFGEmulated.
Код:
cfg_emulated = p.analyses.CFGEmulated()
Код:
from angrutils import *
plot_cfg(cfg_emulated, "cfg_emulated", asminst=True, remove_imports=True, remove_path_terminator=True)
Даже в нашем случае граф получился внушающий, поэтому здесь лишь его скромная часть, но тем не менее даже отсюда видны проверки флага. Куда эффективнее при анализе печатать лишь CFG отдельных интересующих функций. Получить их список можно таким образом
Код:
functions = [function for function in cfg_emulated.kb.functions.values()]
Код:
[<Function sub_401000 (4198400)>,
<Function sub_40101d (4198429)>,
<Function _start (4199680)>,
<Function sub_401578 (4199800)>,
<Function sub_401580 (4199808)>,
<Function sub_401588 (4199816)>,
<Function sub_401590 (4199824)>,
<Function sub_401598 (4199832)>,
<Function sub_4015a0 (4199840)>,
<Function sub_4015a8 (4199848)>,
<Function sub_4015b0 (4199856)>,
<Function printf (16777216)>,
<Function scanf (16777220)>,
<Function strlen (16777224)>,
<Function atoi (16777228)>,
<Function _controlfp (16777232)>,
<Function _set_app_type (16777236)>,
<Function _getmainargs (16777240)>,
<Function exit (16777244)>]
Получить адрес мы можем из распечатанного CFG
Возьмем адрес, который нам необходимо пройти для того чтобы мы получили
"Yes! Correct flag is". Я взял 0x4014CE.Теперь зададим первоначальное состояние:
Код:
state = p.factory.entry_state()
Символическое выполнение позволяет выполнять SimulationManager. Задав изначальное состояние и указав конечное применим его
Код:
sm = p.factory.simgr(state)
sm.explore(find=0x4014ce)
Код:
sm.epxplore(find="Необходимые адреса", avoid="Ложные адреса")
Out:
Код:
<SimulationManager with 14 deadended, 1 found>
Код:
solution = sm.found[0]
solution.posix.dumps(0)
Код:
b'333gogog0gog0g0g0'
Решение подобных заданий не требует участия реверс-инженера, если он имеет подобный скрипт за пазухой
Код:
import angr
p = angr.Project("project_name")
def find_the_bytes(state):
return b"WELLDONE" in state.posix.dumps(1)
state = p.factory.entry_state()
sm = p.factory.simgr(state)
sm.explore(find=find_the_bytes)
solution = sm.found[0]
print(solution.posix.dumps(0))
автор @Slekdzhov
взято с reverse4you.org