Как починить CFG

Ещё в группе «Реверс»
- Почему IDA не сворачивает константы и как это исправить
Почему IDA не сворачивает константы и как это исправить 👨💻 В последнее время в ВПО все…
- Распознаем STL-код легко
Распознаем STL-код легко 😐 Нередко в процессе реверс-инжиниринга мы сталкиваемся с STL-кодом, анализ которого на первый…
- Опять CFG
Опять CFG 👋 Частой задачей при извлечении конфигураций ВПО на потоке является получение границ функций и…
- Деобфусцируем имена функций .NET вручную
Деобфусцируем имена функций .NET вручную 🙌 ВПО на .NET обожает пакеры, обфускацию (имен, CFG и прочего)…
- Идея для правила корреляции в SIEM
Идея для правила корреляции в SIEM 💡 Хотя отслеживание всей цепочки атаки, описанной в постах выше,…
В процессе реверса ВПО мы встречаемся со случаями, когда обфускация мешает понять общий алгоритм работы. Один из примеров — создание двух последовательных противоположных условных переходов в одну точку.
Схематично это выглядит так:
start:
jnX labelA
jX labelA
labelA:
<bytes>
Code language: YAML (yaml)То есть IDA при анализе кода идет сначала по ветке
False и создает код там, откладывая ветку True на потом. Встретив инструкцию jnX, IDA создает код сразу после текущей.Далее, встретив противоположный условный переход, она снова идет по ветке
False и создает код на следующем адресе, построив мусорную инструкцию, после которой дизассемблировать уже нельзя. Тогда IDA возвращается к отложенной на потом очереди и берет адрес оттуда, но беда в том, что там код уже создан, а значит анализ завершается (хорошо видно на скриншоте 1).При живом исполнении в независимости от состояния флагов будет выполнен переход на операнд, то есть выполнение будет как на скриншоте 2, если мы подправим control flow.
Трудность при исследовании таких шеллкодов в том, что переходов может быть много и каждый раз анализ будет о них спотыкаться. Можно попробовать починить вручную, но если таких блоков будут сотни?
✍️ Чтобы справиться с этим, напишем несложный скрипт на
IDAPython, который исправит проблему автоматически. Задача — найти эти блоки и пропатчить их.Сначала составим список противоположных условных переходов и положим его в функцию, которая будет сверять две последовательные инструкции со списком:
def c_jumps(addr, n_addr):
ops = [
("jz", "jnz"),
("jnz", "jz"),
("je", "jne"),
("jne", "je"),
...
]
if (ida_ua.print_insn_mnem(addr), ida_ua.print_insn_mnem(n_addr)) in ops:
return True
return False
Code language: Python (python)Будем последовательно проходить каждую инструкцию до тех пор, пока не встретим нужные либо не упремся в лимит.
def deobf(start, limit=BADADDR):
while addr != BADADDR:
n_addr = ida_search.find_code(addr, ida_search.SEARCH_DOWN)
if n_addr == BADADDR:
break
if not c_jumps(addr, n_addr):
addr = n_addr
continue
Code language: Python (python)🧐 С помощью метода
find_code из модуля ida_search находим следующий адрес, на котором есть код, а с помощью функции c_jumps проверяем, являются ли инструкции на этом и следующем адресе противоположными прыжками. Найдя их, мы должны проверить, указывают ли эти прыжки на одну точку (то есть равны ли их операнды):
o1 = get_operand_value(addr, 0)
o2 = get_operand_value(n_addr, 0)
if o1 != o2:
addr = n_addr
continue
insn = ida_ua.insn_t()
l1 = ida_ua.decode_insn(insn, addr)
l2 = ida_ua.decode_insn(insn, n_addr)
Code language: plaintext (plaintext)С помощью
get_operand_value получаем значение операндов (у jX и jXX он один) и проверяем их равенство. Чтобы определить длину отрезка, который нужно пропатчить, с помощью decode_insn из ida_ua находим длины инструкций.Затем исправляем условные переходы и просим IDA проанализировать новый код. Это необходимо, чтобы можно было найти дальнейшие блоки обфускации:
after_addr = n_addr + l2
ida_bytes.patch_bytes(addr, bytes([0x90] * (l1 + l2 + 1)))
ida_auto.auto_wait()
ida_bytes.del_items(after_addr, ida_bytes.DELIT_EXPAND)
ida_auto.auto_wait()
ida_ua.create_insn(o1)
addr = o1
ida_auto.auto_wait()
Code language: plaintext (plaintext)🗂 Методом
patch_bytes из ida_bytes мы патчим инструкции, с помощью auto_wait из ida_auto просим IDA проанализировать новый код, затем, используя del_items, удаляем мусорные инструкции, созданные при первичном анализе, и снова анализируем. С помощью create_insn создаем валидную инструкцию там, куда указывали условные переходы, и переанализируем в последний раз.Таким образом, этот скрипт позволяет из обфусцированного кода получить нормальный ASM-код, как на скриншоте 3.
Иногда после него остаются единичные нераспознанные байты, но их легко поправить. Из этого кода можно создать функцию, декомпилировать ее и проанализировать (как на скриншоте 4).
Изучайте IDAPython, пишите скрипты. Happy reversing!



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







