Горячее
Лучшее
Свежее
Подписки
Сообщества
Блоги
Эксперты
Войти
Забыли пароль?
или продолжите с
Создать аккаунт
Я хочу получать рассылки с лучшими постами за неделю
или
Восстановление пароля
Восстановление пароля
Получить код в Telegram
Войти с Яндекс ID Войти через VK ID
Создавая аккаунт, я соглашаюсь с правилами Пикабу и даю согласие на обработку персональных данных.
ПромокодыРаботаКурсыРекламаИгрыПополнение Steam

Топ прошлой недели

  • Oskanov Oskanov 8 постов
  • alekseyJHL alekseyJHL 6 постов
  • XpyMy XpyMy 1 пост
Посмотреть весь топ

Лучшие посты недели

Рассылка Пикабу: отправляем самые рейтинговые материалы за 7 дней 🔥

Нажимая кнопку «Подписаться на рассылку», я соглашаюсь с Правилами Пикабу и даю согласие на обработку персональных данных.

Спасибо, что подписались!
Пожалуйста, проверьте почту 😊

Новости Пикабу Помощь Кодекс Пикабу Реклама О компании
Команда Пикабу Награды Контакты О проекте Зал славы
Промокоды Скидки Работа Курсы Блоги
Купоны Biggeek Купоны AliExpress Купоны М.Видео Купоны YandexTravel Купоны Lamoda
Мобильное приложение

Реверс

С этим тегом используют

Вертикальное видео Кот Юмор Без звука YouTube Собака Повтор Все
621 пост сначала свежее
75
Catslikeme
Catslikeme
3 месяца назад
Кусь
Серия Котики 2

Просто очень щедрые и заботливые коты⁠⁠

Вертикальное видео Кот Толстые котики Кошатники Пушистые Юмор Видео Короткие видео Реверс Повтор
4
2
0sadchi
0sadchi
3 месяца назад
Лига Разработчиков Видеоигр

Как создать скрипт-beautifier в Ghidra на Python?⁠⁠

Как создать скрипт-beautifier в Ghidra на Python? Программирование, Python, Гайд, Windows, Реверс, Длиннопост

Приветствую, дорогой читатель. Хочу представить вашему вниманию пример, как можно упростить себе жизнь при исследовании кода программ, используя скриптинг в Ghidra.

Если вы уже имели опыт работы с дизассемблером, то заметили, что читать его вывод не так легко, если целью является понять более высокие абстракции, заложенные в нём. Возможно, вы даже пытались декомпилировать его в псевдокод, но работать с переменными типа local_1-999 – то ещё удовольствие. Да, можно щёлкнуть на каждую из них и присвоить имя на основе логики. А что, если у вас 2000 строк и более?

Чтобы не натереть мозоль, давайте разберёмся, как написать скрипт, который сделает большую часть работы за нас.

Все манипуляции были проделаны на версии 11.1.2. Чтобы попасть в список доступных скриптов, откройте меню Window → Script Manager и там же создайте новый скрипт, нажав в правом верхнем углу кнопку Create New Script и выбрав язык Python.

Важное ограничение: Ghidra использует внутреннюю реализацию языка Python версии 2.7.

Пишем код.

Первым делом, нужно объявить кодировку, чтобы не получить кучу ошибок о наличии не-ASCI символов

# -*- coding: utf-8 -*-

Указываем, что исходный файл сохранён в кодировке UTF-8 (поддержка Unicode символов).

from ghidra.util.task import Task

Импортируем класс Task из модуля Ghidra, который позволяет создавать задачи для выполнения операций в фоне.

from ghidra.app.decompiler import DecompInterface

Импортируем интерфейс декомпилятора, позволяющий получать C-подобный код из бинарных данных.

from ghidra.util.task import ConsoleTaskMonitor

Импортируем класс ConsoleTaskMonitor для отслеживания прогресса выполнения задач с выводом в консоль.

from ghidra.program.model.symbol import SourceType, SymbolType

Импортируем типы источников и типов символов, используемые для обозначения происхождения имен (например, USER_DEFINED) и вида символа (функция, переменная, метка).

from ghidra.program.model.pcode import HighFunctionDBUtil

Импортируем утилиты для работы с высокоуровневым представлением функции (HighFunction) в базе данных Ghidra.

from ghidra.program.model.pcode.HighFunctionDBUtil import ReturnCommitOption

Импортируем опцию фиксации (commit) изменений в базе данных при обновлении параметров функции.

from java.awt import BorderLayout

Импортируем менеджер компоновки BorderLayout для организации компонентов в окне Java.

from javax.swing import JButton, JFrame, JTextArea, JScrollPane, JPanel

Импортируем стандартные Swing-компоненты: кнопку, окно, текстовую область, панель прокрутки и панель для построения GUI.

import re

Импортируем модуль регулярных выражений для поиска и обработки строк.

class RenameDialog(JFrame):

Объявляем класс RenameDialog, наследующийся от JFrame. Он представляет окно диалога для ввода новых имён.

def __init__(self, suggestions):

Конструктор класса, принимающий список предложенных имен (suggestions) для переименования.

JFrame.__init__(self, "Advanced Renamer")

Инициализируем базовый класс JFrame, задавая заголовок окна “Advanced Renamer”.

self.setSize(800, 600)

Устанавливаем размер окна – 800 пикселей по ширине и 600 по высоте.

self.setLayout(BorderLayout())

Задаём менеджер компоновки BorderLayout для организации компонентов внутри окна.

self.text_area = JTextArea()

Создаём текстовую область, в которой пользователь сможет редактировать имена.

self.text_area.setText("# Format: old=new\n" + "\n".join(suggestions))

Заполняем текстовую область начальным текстом с примером формата и списком предложенных имён, разделённых переносами строк.

scroll_pane = JScrollPane(self.text_area)

Создаём панель прокрутки, содержащую нашу текстовую область, чтобы можно было просматривать длинный текст.

self.add(scroll_pane, BorderLayout.CENTER)

Добавляем панель прокрутки в центр окна (согласно BorderLayout).

button_panel = JPanel()

Создаём панель для размещения кнопок в окне.

self.apply_btn = JButton("Apply", actionPerformed=lambda _: self.setVisible(False))

Создаём кнопку “Apply” с обработчиком события: при нажатии окно будет скрыто (setVisible(False)).

button_panel.add(self.apply_btn)

Добавляем кнопку “Apply” на панель кнопок.

self.add(button_panel, BorderLayout.SOUTH)

Размещаем панель кнопок в нижней части окна.

class AdvancedRenamer(Task):

Объявляем класс AdvancedRenamer, наследующийся от Task. Он отвечает за логику переименования символов в Ghidra.

def __init__(self, program, function):

Конструктор класса принимает объект программы (program) и функцию (function), над которой будет производиться переименование.

super(AdvancedRenamer, self).__init__("Advanced Renamer", True, False, True)

Вызываем конструктор базового класса Task, задавая имя задачи и некоторые флаги (например, показывать прогресс, отменяемость и т.д.).

self.program = program

Сохраняем ссылку на текущую программу Ghidra.

self.function = function

Сохраняем ссылку на функцию, которую собираемся анализировать и переименовывать.

self.monitor = ConsoleTaskMonitor()

Создаем экземпляр монитора задач, который выводит статус выполнения в консоль.

self.skipped = {'int', 'char', 'void', 'return', 'break', 'float'}

Определяем множество ключевых слов, которые не будут изменяться при переименовании (например, базовые типы и управляющие конструкции).

def find_and_rename(self, old_name, new_name):

Определяем метод для поиска символа с именем old_name и его переименования в new_name.

decompiler = DecompInterface()

Создаём экземпляр интерфейса декомпилятора.

decompiler.openProgram(self.program)

Открываем текущую программу в декомпиляторе для дальнейшей работы.

results = decompiler.decompileFunction(self.function, 60, self.monitor)

Декомпилируем функцию с таймаутом 60 секунд, используя монитор для отслеживания прогресса.

if results.decompileCompleted():

Проверяем, успешно ли завершилась декомпиляция.

hfunction = results.getHighFunction()

Получаем высокоуровневое представление функции (HighFunction) из результатов декомпиляции.

signatureSrcType = self.function.getSignatureSource()

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

HighFunctionDBUtil.commitParamsToDatabase(hfunction, True, ReturnCommitOption.COMMIT, signatureSrcType)

Фиксируем изменения параметров функции в базе данных, используя указанные опции. Это необходимо для того, чтобы локальные имена, такие как lVar1, uVar2, pVar3, были согласованы с базой данных, потому, что они генерируются самим декомпилятором и просто выводятся на экран, без коммита в базу.

if old_name.startswith("FUN_"):

Если имя символа начинается с “FUN_”, считаем его именем функции.

return self.rename_function(old_name, new_name)

Вызываем метод для переименования функции.

elif old_name.startswith(("DAT_", "PTR_", "UNK_", "LAB_")):

Если имя начинается с “DAT_”, “PTR_”, “UNK_” или “LAB_”, обрабатываем его как метку или данные. Покаяние: У меня так и не получилось дать ума PTR_ и UNK_, хоть это и глобальные имена, как DAT_ и LAB_, они не переименовываются данной функцией, но я оставил их как есть :з

return self.rename_label(old_name, new_name)

Вызываем метод для переименования метки.

elif old_name.startswith(("local_", "param_", "uVar", "lVar")):

Если имя соответствует шаблону локальной переменной или параметра, то…

return self.rename_local_variable(old_name, new_name)

… вызываем метод для переименования локальной переменной.

else:

Если ни одно из условий не выполнено, по умолчанию обрабатываем как локальную переменную.

return self.rename_local_variable(old_name, new_name)

Вызываем метод для переименования локальной переменной.

def rename_function(self, old_name, new_name):

Определяем метод, который переименовывает функцию с именем old_name в new_name.

try:

Начинаем блок обработки исключений, чтобы избежать сбоев при ошибках.

addr_str = old_name[4:] if old_name.startswith("FUN_") else old_name

Если имя начинается с “FUN_”, удаляем этот префикс, чтобы получить строку адреса функции.

addr = toAddr("0x{}".format(addr_str))

Преобразуем строку с адресом в объект адреса Ghidra, добавляя префикс “0x”.

func = getFunctionAt(addr)

Получаем объект функции, расположенной по данному адресу.

if func and func.getName() == old_name:

Если функция найдена и её имя соответствует old_name, то…

func.setName(new_name, SourceType.USER_DEFINED)

… устанавливаем новое имя для функции, указывая, что оно задано пользователем (USER_DEFINED).

except: pass

Если возникает ошибка (например, неверный адрес), просто игнорируем её.

def rename_label(self, old_name, new_name):

Определяем метод для переименования метки или символа, представляющего данные.

try:

Начинаем блок обработки исключений.

addr_str = old_name[4:] if old_name.startswith(("LAB_", "DAT_", "PTR_", "UNK_")) else old_name

Извлекаем адрес метки, убирая префикс (например, “LAB_”) если он присутствует.

addr = toAddr(addr_str)

Преобразуем строку адреса в объект адреса Ghidra.

for sym in self.program.getSymbolTable().getSymbols(addr):

Перебираем все символы, зарегистрированные по этому адресу, из таблицы символов программы.

if sym.getName() == old_name:

Если имя символа совпадает с old_name, то…

sym.setName(new_name, SourceType.USER_DEFINED)

… устанавливаем новое имя для символа с типом источника USER_DEFINED.

return True

Возвращаем True, указывая, что переименование прошло успешно.

except: pass

При возникновении исключения игнорируем его.

def rename_local_variable(self, old_name, new_name):

Определяем метод для переименования локальной переменной или параметра.

try:

Начинаем блок обработки исключений.

if new_name.lower() in self.skipped:

Если новое имя (в нижнем регистре) содержится в списке ключевых слов для пропуска, то…

return False

… прекращаем переименование, возвращая False.

# Далее идет блок, необходимый для обработки параметров

for param in self.function.getParameters():

Перебираем все параметры текущей функции.

if param.getName() == old_name:

Если имя параметра совпадает с old_name, то…

param.setName(new_name, SourceType.USER_DEFINED)

… устанавливаем новое имя для параметра.

# Local variables (даже если наш пациент был найдет среди параметров

# необходимо пройтись и по локальным переменным,

# иначе они не переименовываются, возможно надо тут просто

# сделать коммит в базу)

decompiler = DecompInterface()

Создаём новый экземпляр декомпилятора для обработки локальных переменных.

decompiler.openProgram(self.program)

Открываем программу в декомпиляторе.

results = decompiler.decompileFunction(self.function, 60, self.monitor)

Декомпилируем функцию с таймаутом 60 секунд.

if results.decompileCompleted():

Если декомпиляция прошла успешно, то…

hfunction = results.getHighFunction()

… получаем высокоуровневое представление функции.

syms = hfunction.getLocalSymbolMap().getSymbols()

Получаем список локальных символов (переменных) из высокоуровневой функции.

for sym in syms :

Перебираем каждый локальный символ.

if sym.getName() == old_name and sym.getName() != new_name:

Если имя символа совпадает с old_name и ещё не равно new_name, то…

HighFunctionDBUtil.updateDBVariable(sym, new_name, None, SourceType.USER_DEFINED)

… обновляем имя переменной в базе данных с новым именем и источником USER_DEFINED.

return True

Возвращаем True после успешного переименования локальной переменной.

return False

Если ни один из блоков не сработал, возвращаем False – переименование не выполнено.

except Exception as e:

Ловим исключения и сохраняем их в переменной e для отладки.

print("Error: {} -> {} ({})".format(old_name, new_name, str(e)))

Выводим сообщение об ошибке с указанием старого и нового имени, а также текста ошибки. Обратите внимание на форматирование строки, т.к. это Python 2.7.

return False

Возвращаем False, сигнализируя, что произошла ошибка при переименовании.

def run(self):

Определяем метод run, который является точкой входа при выполнении задачи AdvancedRenamer.

decompiler = DecompInterface()

Создаем экземпляр декомпилятора для работы с функцией.

decompiler.openProgram(self.program)

Открываем программу в декомпиляторе.

results = decompiler.decompileFunction(self.function, 60, self.monitor)

Декомпилируем функцию с таймаутом 60 секунд.

if not results.decompileCompleted():

Если декомпиляция не завершилась успешно, то…

print("Decompilation failed!")

… выводим сообщение об ошибке декомпиляции.

return

Прерываем выполнение метода run (задача не может продолжаться без декомпиляции).

code = results.getDecompiledFunction().getC()

Получаем декомпилированный C-подобный код функции в виде строки.

entities = re.findall(r'\b([A-Za-z_][A-Za-z0-9_]*)\b', code)

С помощью регулярного выражения находим все идентификаторы (слова, начинающиеся с буквы или подчёркивания) в коде.

filtered_names = [n for n in set(entities) if n not in self.skipped]

Создаём множество уникальных идентификаторов и исключаем те, что присутствуют в self.skipped (ключевые слова).

dialog = RenameDialog(filtered_names)

Создаем диалоговое окно RenameDialog, передавая список найденных имён для потенциального переименования.

dialog.setLocationRelativeTo(None)

Устанавливаем расположение диалога по центру экрана (None означает центр относительно родительского окна).

dialog.setVisible(True)

Делаем диалог видимым – ожидаем, пока пользователь внесёт изменения и нажмёт “Apply”.

while dialog.isVisible(): pass

Активно ждём, пока окно не будет закрыто (пользователь не закончит ввод).

success = 0

Инициализируем счётчик успешно переименованных символов.

for line in dialog.text_area.getText().split('\n'):

Перебираем каждую строку из текста, введённого пользователем в текстовой области диалога.

line = line.strip()

Удаляем пробелы в начале и конце строки.

if line and '=' in line and not line.startswith('#'):

Если строка не пуста, содержит символ “=” и не является комментарием (не начинается с “#”), то…

old, new = line.split('=', 1)

Разбиваем строку на две части по первому символу “=”, где левая часть – старое имя, правая – новое.

if self.find_and_rename(old.strip(), new.strip()):

Вызываем метод find_and_rename с очищенными от пробелов старыми и новыми именами; если переименование прошло успешно, то…

success += 1

… увеличиваем счётчик успешных переименований.

print("Successfully renamed: {}/{}".format(success, len(filtered_names)))

Выводим итоговое сообщение о том, сколько из найденных идентификаторов было успешно переименовано.

if __name__ == "__main__":

Стандартная проверка: выполняется ли скрипт как главный модуль.

func = getFunctionContaining(currentAddress)

Получаем функцию, в которой находится текущий адрес курсора в Ghidra.

if func:

Если функция найдена, то…

task = AdvancedRenamer(currentProgram, func)

Создаем экземпляр задачи AdvancedRenamer, передавая текущую программу и найденную функцию.

task.run()

Запускаем выполнение задачи переименования.

else:

Если функция не найдена (курсор не находится внутри функции), то…

print("Position cursor inside function!")

… выводим сообщение с просьбой установить курсор внутри функции для корректной работы скрипта.

Подготовка.

Когда все готово, можем пойти в любую нейросеть. По-моему мнению самая лучшая на сегодняшний день для данной задачи будет DeepSeek-r1. Выверенный опытным путем промпт.

Прокомментируй каждую строку и переименнуй переменные и функции нормально. Саму структуру кода оставь, как есть, не сокращая и не меняя, то есть никаких "и так далее" - пиши код полностью! Все имена должны быть подробными, то есть не tmp и прочее, никаких сокращенных бысмысленных имен. Опиши к чему пренадлежат переменные, не просто windowStruct, а UIState как напимер. Так же переименую DAT_ как g_..новоеИмя, и LABEL_куда прыгаем {НАШ_ПСЕВДО_КОД} после кода выведи все, что переименовал в формате списка старое_имя=новое_имя\n в список включай все переменные, функции, DAT_, LAB_, param_ и прочее без исключений прям все пиши

Выбираем цель, для эксперимента

Как создать скрипт-beautifier в Ghidra на Python? Программирование, Python, Гайд, Windows, Реверс, Длиннопост

В итоге, у меня получился вот такой вот список для моей функции

param_1=hWndPointer

bVar1=isCursorConfinedDueToCapture

bVar2=isMarginApplied

cVar3=isCursorCapturedResult

local_res8=clientTopLeftPoint

local_res10=clientBottomRightPoint

local_38=confinedCursorRect

local_28=windowRect

Запускаем!!

После запуска скрипта (при условии, что курсор установлен внутри функции) откроется диалоговое окно, в котором будут выведены найденные идентификаторы. Введите список переименований в формате:

ВАЖНО!!! Переменные по типу xVarN нужно сортировать по убыванию, так как они не внесены в базу. Переименование меньшей переменной приведёт к тому, что большая займёт её место, и список станет невалидным.

После ввода нажмите кнопку Apply и дождитесь окончания выполнения.

Как создать скрипт-beautifier в Ghidra на Python? Программирование, Python, Гайд, Windows, Реверс, Длиннопост

(метод подсчета требует доработки)

Как создать скрипт-beautifier в Ghidra на Python? Программирование, Python, Гайд, Windows, Реверс, Длиннопост

В итоге должно получиться нечто подобное:

Как создать скрипт-beautifier в Ghidra на Python? Программирование, Python, Гайд, Windows, Реверс, Длиннопост

Спасибо за внимание! Скачать PDF и сам скрипт можно по ссылке – https://t.me/osiechan/62 А начать путь в реверс-инжиниринге можно на увлекательном бесплатном курсе по ботостроению для ММОРПГ – https://t.me/osiechan/41

Показать полностью 4
[моё] Программирование Python Гайд Windows Реверс Длиннопост
1
307
Pnevmopix
Pnevmopix
3 месяца назад
Дикие животные

Ответ на пост «Дом для осьминога»⁠⁠2

Вы думали я не реверсну? Я реверсну)

Осьминог Головоногие Моллюск Морские обитатели Дикие животные Дикая природа Пляж Бутылка Пластиковые бутылки Вертикальное видео Короткие видео Реверс Видео Ответ на пост
25
3
CTEPEOCOH
CTEPEOCOH
4 месяца назад
Пакистанские технологии

Ответ на пост «Двигатель превращается в...»⁠⁠

В комментарии видео не добавляется. @tlsandos, @DartViktor

Пакистан Пакистанские технологии Техника безопасности Изготовление Реверс Видео Ответ на пост
3
55
EgoRProG
EgoRProG
4 месяца назад
Видеохостинг на Пикабу

Хорошему танцору ничего не мешает⁠⁠

Черный юмор Танцы Видео Вертикальное видео Короткие видео Неожиданно Реверс
20
2723
SourLemon1
SourLemon1
4 месяца назад

Маленькие динозавры⁠⁠

Коати Носухи Гватемала Видео Вертикальное видео Короткие видео Реверс Динозавры
134
1431
Bahdavar
Bahdavar
4 месяца назад

Помощь пострадавшим от пожаров⁠⁠

Шок контент! Сердобольные волонтеры доставляют погорельцам в Лос-Анджелесе новую технику. Добро победит!

Странный юмор Юмор Видео Вертикальное видео Негры Реверс
77
10
KAPTA.KOTA
KAPTA.KOTA
5 месяцев назад

Помолился боженьке что бы тот помог быстро собрать всё⁠⁠

Неудача Юмор Fail Молитва Видео Вертикальное видео Реверс
0
Посты не найдены
О Нас
О Пикабу
Контакты
Реклама
Сообщить об ошибке
Сообщить о нарушении законодательства
Отзывы и предложения
Новости Пикабу
RSS
Информация
Помощь
Кодекс Пикабу
Награды
Команда Пикабу
Бан-лист
Конфиденциальность
Правила соцсети
О рекомендациях
Наши проекты
Блоги
Работа
Промокоды
Игры
Скидки
Курсы
Зал славы
Mobile
Мобильное приложение
Партнёры
Промокоды Biggeek
Промокоды Маркет Деливери
Промокоды Яндекс Путешествия
Промокоды М.Видео
Промокоды в Ленте Онлайн
Промокоды Тефаль
Промокоды Сбермаркет
Промокоды Спортмастер
Постила
Футбол сегодня
На информационном ресурсе Pikabu.ru применяются рекомендательные технологии