[ << ALL_FEED ]

Почему IDA не сворачивает константы и как это исправить

Ещё в группе «Реверс»

Почему IDA не сворачивает константы и как это исправить 👨‍💻

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

😑 В чем тут проблема: часть констант, из которых собирается итоговое выражение, хранится в памяти. Из-за этого IDA по умолчанию не может нормально применить constant propagation и автоматически «свернуть» такие конструкции в исходные значения (скриншот 1).

Особенно неприятно, когда таким способом маскируют еще и адреса вызываемых функций. На практике это выливается в конструкции вида:

mov   <reg>, <const>
add   <reg>, cs:<offset_*>
call  <reg>Code language: Intel x86 Assembly (x86asm)

Для статического анализа это, конечно, лишняя головная боль 🧱

Но есть нюанс: если явно указать, что данные по адресу cs:<offset_*> — это константные qword/dword, то во время декомпиляции IDA сама подтянет значение из памяти, пересчитает выражение и свернет его обратно в исходную константу.

Перед тем как явно проставлять таким данным const, стоит убедиться, что значение действительно readonly. Один из простых способов — посмотреть на перекрестные ссылки: если все обращения к адресу идут только на чтение, то это хороший кандидат на const (скриншот 2).

Заодно полезно отфильтровать значения, которые на самом деле являются указателями, чтобы не испортить типизацию (скриншот 3).

😎 Все это можно автоматизировать небольшим IDAPython-скриптом (IDA 9.0+), который проходит по сегменту .data (или любому другому нужному сегменту), находит значения, используемые только на чтение, исключает те, что похожи на адреса, и явно проставляет им const byte/word/dword/qword в зависимости от размера.

В результате псевдокод становится заметно чище и понятнее (скриншот 4).

А вот и этот IDAPython-скрипт:

import ida_bytes
import ida_segment
import ida_typeinf
import idautils
import ida_xref
import idaapi
from collections import namedtuple

PTR_SIZE = 8 if idaapi.inf_is_64bit() else 4


TypeInfo = namedtuple('SizeInfo', ['get_data', 'typename', 'badaddr'])

SIZE_TO_TYPE_INFO_MAP = {
    1: (TypeInfo(ida_bytes.get_byte,  "byte",  0xFF)),
    2: (TypeInfo(ida_bytes.get_word,  "word",  0xFFFF)),
    4: (TypeInfo(ida_bytes.get_dword, "dword", 0xFFFFFFFF)),
    8: (TypeInfo(ida_bytes.get_qword, "qword", 0xFFFFFFFFFFFFFFFF))
}


def apply_const_type(ea: int) -> bool:
    flags = ida_bytes.get_flags(ea)
    if not ida_bytes.is_data(flags):
        return False

    item_size = ida_bytes.get_item_size(ea)
    type_info = SIZE_TO_TYPE_INFO_MAP.get(item_size)
    if type_info is None:
        return False

    if type_info.get_data(ea) == type_info.badaddr:
        return False

    typeinfo_str = "const " + type_info.typename
    tif = ida_typeinf.tinfo_t(typeinfo_str)

    ida_bytes.clr_op_type(ea, 0)
    return ida_typeinf.apply_tinfo(ea, tif, 1)


def is_addr_readonly(target_addr: int) -> bool:
    flags = ida_bytes.get_full_flags(target_addr)
    return ida_bytes.has_xref(flags) and \
           all(ref.type == ida_xref.dr_R for ref in idautils.XrefsTo(target_addr))


def apply_const_type_if_readonly(target_addr: int) -> bool:
    return apply_const_type(target_addr) if is_addr_readonly(target_addr) else False


def data_is_offset(ea:int) -> bool: 
    item_size = ida_bytes.get_item_size(ea)
    if item_size != PTR_SIZE:
        return False
    
    type_info = SIZE_TO_TYPE_INFO_MAP.get(item_size)
    if type_info is None:
        return False
    
    value = type_info.get_data(ea)
    return  (idaapi.getseg(value)) is not None 


def main():
    print("Casting readonly memory values to consts ")
    print("---------------------------------")
    
    segment_name = ".data"
    
    seg = ida_segment.get_segm_by_name(segment_name)
    if not seg:
        print("No {segment_name} segment")
        return

    ea = seg.start_ea
    fixed_consts_num = 0

    while ea < seg.end_ea:
        ea_size = ida_bytes.get_item_size(ea)
        if ea_size <= 0:
            ea += 1
            continue

        if not data_is_offset(ea) and apply_const_type_if_readonly(ea):
            fixed_consts_num += 1

        ea += ea_size

    print("---------------------------------")
    print(f"Done. fixed = {fixed_consts_num} consts")
    print("Decompile again to view results: F5")


if __name__ == "__main__":
    main()

#tips #reverse
@ptescalator (X, Max)

Ещё от global_author

  • Этот ваш Siemens…

    Недавно наши коллеги из Центра промышленной экспертизы Позитива обнаружили на MalwareBazaar один любопытный Windows-образец. Вот его…

  • Анти-антивирус

    Недавно нам на глаза попалась APK-шка с интригующим и внушающим доверие названием «Антивирус ФСБ.apk». Установив его,…

  • .exe .docm .xlsm

    .exe .docm .xlsm Вредоносные файлы с этими расширениями чаще всего встречаются в сетевом трафике компаний. Давайте…

  • Хаос, шок, приватные ключи от не менее приватного GitLab 💻

    Последние пару месяцев злоумышленник распространял трояны с нескольких npm-учеток: alex05255, mdrafiqulislamrabby, b.w1001, abdev8773 и mollspotwood54400. Список…

  • ::%16777216 — так что же ты такое?

    ::%16777216 — так что же ты такое? Известно, что в ходе атак злоумышленники могут использовать туннелирование.…

Ещё от global_author

  • Этот ваш Siemens…

    Недавно наши коллеги из Центра промышленной экспертизы Позитива обнаружили на MalwareBazaar один любопытный Windows-образец. Вот его…

  • Анти-антивирус

    Недавно нам на глаза попалась APK-шка с интригующим и внушающим доверие названием «Антивирус ФСБ.apk». Установив его,…

  • .exe .docm .xlsm

    .exe .docm .xlsm Вредоносные файлы с этими расширениями чаще всего встречаются в сетевом трафике компаний. Давайте…

  • Хаос, шок, приватные ключи от не менее приватного GitLab 💻

    Последние пару месяцев злоумышленник распространял трояны с нескольких npm-учеток: alex05255, mdrafiqulislamrabby, b.w1001, abdev8773 и mollspotwood54400. Список…

  • ::%16777216 — так что же ты такое?

    ::%16777216 — так что же ты такое? Известно, что в ходе атак злоумышленники могут использовать туннелирование.…

Ещё в группе «Реверс»