Статический резолв импортов

Ещё в группе «Реверс»
- Почему IDA не сворачивает константы и как это исправить
Почему IDA не сворачивает константы и как это исправить 👨💻 В последнее время в ВПО все…
- Распознаем STL-код легко
Распознаем STL-код легко 😐 Нередко в процессе реверс-инжиниринга мы сталкиваемся с STL-кодом, анализ которого на первый…
- Опять CFG
Опять CFG 👋 Частой задачей при извлечении конфигураций ВПО на потоке является получение границ функций и…
- Деобфусцируем имена функций .NET вручную
Деобфусцируем имена функций .NET вручную 🙌 ВПО на .NET обожает пакеры, обфускацию (имен, CFG и прочего)…
- Идея для правила корреляции в SIEM
Идея для правила корреляции в SIEM 💡 Хотя отслеживание всей цепочки атаки, описанной в постах выше,…
Динамический резолв импортов по хеш-суммам в ВПО — тема заезженная, но для проведения статического анализа необходимо разметить имена и прототипы API. Чтобы из того, что представлено на скрине 1, получить то, что на скрине 2, и не мучаться с ручной разметкой, можно написать скрипт IDAPython.
😠 На примере DodgeBox рассмотрим резолв, который заключается в вычислении адреса API-функции и помещении его в глобальную структуру. Реализация — на скрине 3.
Алгоритм хеширования опустим, поскольку здесь он не столь важен. Перед началом необходимо подготовить словарь с именами функций WinAPI и их хеш-суммами. Для этого выберем те библиотеки, что используются в бинаре. Здесь есть имена DLL в открытом виде, но иногда — только хеш-суммы, в этом случае можно составить словарь из всех системных DLL. Наш словарь — на скрине 4.
Далее убеждаемся, что члены в структуре заполняются последовательно, чтобы автоматически извлечь из кода хеш-суммы, имена модулей. Внимание — первый член структуры пропускается. Создаем структуру нужного размера (скрин 5), чтобы она вместила все функции.
Чтобы извлечь хеш-суммы и имена библиотек, пишем функцию, которая пройдет по всем вызовам
get_proc_by_hash и извлечет ее аргументы.def get_hashes(resolve_API_addr, get_proc_by_hash_addr):
result = []
func: ida_funcs.func_t
func = ida_funcs.get_func(resolve_API_addr)
cur = func.start_ea
while cur < func.end_ea:
if get_operand_value(cur, 0) == get_proc_by_hash_addr:
result.append(get_args(cur))
cur = ida_search.find_code(cur, SEARCH_DOWN)
return resultCode language: Python (python)Функция
get_args поднимается на несколько шагов вверх от операции call и извлекает аргументы.def get_args(call_addr):
func_name_hash = None
lib_name = None
cur = call_addr
True:
if print_insn_mnem(cur) == "mov" and print_operand(cur, 0) == "r8d":
func_name_hash = get_operand_value(cur, 1) & 0xFFFFFFFF
elif print_insn_mnem(cur) == "lea" and print_operand(cur, 0) == "rcx":
lib_name = ida_bytes.get_strlit_contents(get_operand_value(cur, 1), -1, STRTYPE_C_16).decode()
if func_name_hash and lib_name:
return lib_name, func_name_hash
cur = ida_search.find_code(cur, SEARCH_UP)Code language: Intel x86 Assembly (x86asm)На выходе
get_hashes получим список, который используем для заполнения структуры API. Основная функция будет выглядеть так:struc: ida_struct.struc_t = ida_struct.get_struc(ida_struct.get_struc_id("API"))
funcs = get_hashes(0x180007A90, 0x1800078E0)
for i in range(1, len(funcs) + 1):
lib_name, func_name_hash = funcs[i - 1]
member: ida_struct.member_t = struc.members[i]
func_name = get_func_name(lib_name, func_name_hash, winapi_hashes_dict)
if func_name:
ida_struct.set_member_name(struc, member.soff, func_name)
func_tinfo = get_func_tinfo(func_name)
if func_tinfo:
ida_struct.set_member_tinfo(struc, member, 0, func_tinfo, 0)Code language: YAML (yaml)Функция
get_func_name проста в реализации, она находит в словаре имя API по хеш-сумме. А вот get_func_tinfo более интересна: она создает объект, содержащий прототип функции, который мы также применим к члену структуры.def get_func_tinfo(func_name):
tinfo = ida_typeinf.get_named_type(None, func_name, 0)
if tinfo:
type_s = tinfo[1]
field_s = tinfo[2]
t = ida_typeinf.tinfo_t()
t.deserialize(None, type_s, field_s)
t.create_ptr(t)
return t
else:
return NoneCode language: Python (python)Функция
ida_typeinf.get_named_type получает информацию о типе, который содержится в Type Library (*.til). Вызов выглядит так:Python>get_func_tinfo("GetWindowsDirectoryW")
UINT (__stdcall *)(LPWSTR lpBuffer, UINT uSize)Code language: plaintext (plaintext)Однако на самом деле функция возвращает объект типа
ida_typeinf.tinfo_t.Структура API после вызова скрипта представлена на скрине 6. Если применить ее к глобальной переменной, резолв превратится в то, что видно на скрине 7, и можно будет удобно анализировать бинарь статически, не запуская отладчик.
Есть, конечно, соблазн написать функцию
make_beautifully, которая сама вычитает офсеты, создаст структуру и члены внутри нее, но об этом в другой раз.






#tip #reverse #idapython
@ptescalator
Ещё в группе «Реверс»
- Почему IDA не сворачивает константы и как это исправить
Почему IDA не сворачивает константы и как это исправить 👨💻 В последнее время в ВПО все…
- Распознаем STL-код легко
Распознаем STL-код легко 😐 Нередко в процессе реверс-инжиниринга мы сталкиваемся с STL-кодом, анализ которого на первый…
- Опять CFG
Опять CFG 👋 Частой задачей при извлечении конфигураций ВПО на потоке является получение границ функций и…
- Деобфусцируем имена функций .NET вручную
Деобфусцируем имена функций .NET вручную 🙌 ВПО на .NET обожает пакеры, обфускацию (имен, CFG и прочего)…
- Идея для правила корреляции в SIEM
Идея для правила корреляции в SIEM 💡 Хотя отслеживание всей цепочки атаки, описанной в постах выше,…







