stihl не предоставил(а) никакой дополнительной информации.
Языковые модели способны взять на себя рутинную часть работы реверс‑инженера. Они объяснят, как работает блок кода, и подскажут удачные имена функций и переменных. Рассмотрим широкий спектр облегчающих анализ инструментов — от локальных языковых моделей до агентов, способных к рассуждению и запуску пользовательского кода.
Декомпилируй ассемблерный код из IDA Pro в псевдо-C, как Hex-Rays.
Правильный ответ от Hex-Rays:
Ответ GPT-4o:
IDA предполагает, что функция возвращает MMRESULT, хотя смысла получать ответ от waveOutClose нет и void — правильный тип. Имя WAVEHDR понятней, чем wavehdr_tag, хотя это одна и та же структура.
Пока ответ GPT лучше, попробуем с участком кода побольше.
Декомпилируй ассемблерный код из IDA Pro в псевдо-C, как Hex-Rays.
Ответ Hex-Rays:
Ответ GPT-4o:
И вот у модели начинаются галлюцинации. Зачем‑то поставила два вызова GetVersion. Откуда‑то взялся возвращаемый ноль, хотя из кода очевидно, что EAX берется из WinHelpA, следовательно, тип возврата должен быть BOOL.
Тем не менее модель удачно распознала имена переменных, преобразовала константы к нужным типам и даже увидела макрос isdigit. Если бы не галлюцинации, инструменту бы цены не было. Круто, но Hex-Rays пока еще рано списывать со счетов.
Here is a C-like decompiled function from a binary:
Please suggest a concise and descriptive name for this function (using snake_case), and provide a brief explanation of what it does.
Код получает адреса распознанных функций и, если функция еще не была переименована, отправляет декомпилированный Hex-Rays код запросом к GPT. В ответе мы ожидаем увидеть новое имя функции и краткое описание, что она делает. Далее скрипт переименовывает функцию и добавляет комментарий с описанием ее работы.
Для маленьких функций без контекста придуманные GPT названия практически бесполезны. Если бы мы предварительно указали имена аргументов и правильные типы, анализ был бы точнее. Еще одна проблема скрипта — порядок функций. Сначала надо передавать на анализ вызываемые функции и только затем вызывающие, чтобы расширить контекст последних. Для этого надо создать граф вызовов и постепенно идти от самых дальних узлов наверх.
Попробуем отдать GPT большую изолированную функцию, понятную без внешнего контекста.
Работа над байтами без вызова внешнего кода декодируется достаточно бодро. GPT понял, что алгоритм работает с двумя областями изображения. Действительно, по коду v12 = a1 - a3 можно догадаться, что это координаты x1 и x2.
Разбирать подобную математику вручную было бы слишком утомительно. Обычно я просто смотрю в отладчике, что было с данными до и что стало после. А из этого уже делаю выводы о назначении функции.
Наивность текущего подхода — в зашкаливающем числе запросов. Модель GPT-4 за анализ двухсот небольших функций сожрала аж три бакса! Вероятно, модели попроще стоят дешевле. В любом случае подобный анализ применим только к конкретной функции. То есть загрузить exe-файл целиком и получить его исходный код пока что не удастся.
У каждого инструмента есть имя и описание, а также функция _run, принимающая строку с аргументом от модели.
При запуске выясняется еще одна проблема: плагин запрашивает модель gpt-3.5-turbo-0613, которая давно не существует. Заменяю в коде актуальной.
Устанавливаю курсор на тестовую функцию и запускаю плагин. Запрос уходит на сервер, функция меняет имя. Но добавленный комментарий на китайском. Меняем промпт на английский, так как оригинал тоже был на китайском.
1. Function Purpose Analysis: Describe in detail what this function does and what its purpose is. Add comments in English directly to the function, each prefixed with
2. Function Signature Correction: Based on your understanding of the code logic, infer and correct any function signatures that may have been incorrectly or ambiguously decompiled by IDA Pro. Explain in detail why you made this decision.
3. Function Naming Analysis: Thoroughly analyze the current function and all related functions it calls. Rename any functions starting with
Description:
Поначалу все работает нормально, но затем я натыкаюсь на ограничения по времени:
Теперь с плагином можно работать.
Возьмем небольшую функцию и проследим цепочку рассуждений Copilot.
- Для начала анализа необходимо получить псевдокод
- Для этого вызывается действие
- Псевдокод отображает справку через
- Следуя гайдлайну:
- Добавим комментарий к псевдокоду
- Исправим прототип функции при необходимости
- Проанализируем и поменяем имена функций
- Я собираюсь добавить комментарий:
- Вызываю действие
- Получаю текст комментария и ставлю его на функцию
- Комментарий успешно добавлен, теперь анализ прототипов:
- Текущий прототип
- Код возвращает
- Теперь проведем анализ имени:
- Имя сообщает об отображении справки
- Переименование не требуется
-
- Переименование не требуется
- Убедимся, что все задачи исполнены:
- Вызываем
- Получаем ошибку
- Вызываем
- Получаем ту же самую ошибку
- Похоже, ошибка имеет постоянный характер
- Это мешает проверке завершения всех задач
- Однако, исходя из текста гайдлайна, все задачи исполнены
- Анализ завершен
Это краткий пересказ, полный лог занимает шесть килобайт. Идея плагина крутая, хотя реализация сыровата. Запросов к GPT довольно много, но результат работы того стоит. Инструмент недоделан, однако на его основе можно написать агент под свои задачи.
Натравив плагин на реальную функцию, через раз получаю ошибку ERROR - aiDAPal: error unpacking response. Значит, модель не всегда отдает правильные ответы. Попробую перехватить ее вход и выход, чтобы оценить масштаб проблемы. Добавляю свои print в оригинальный метод.
Запускаю анализ функции.
Ответ практически идеален, разве что имя функции не поменяла. Попробуем с другим кодом.
Теперь вместо переименования переменных модель добавляет к ним комментарий. Спасибо, но не от всей души. Код в этом месте ломается, так как не может найти в ответе обязательное поле new_name.
Перезапустив анализ пару раз на новом коде, видим, что модель отдает неполные данные и откровенно галлюцинирует, добавляя __fastcall.
Задумка была крутой, но скромный размер модели не позволяет использовать ее в работе, разве что в цикле запрашивать ответы, пока не выдаст верный. Но для этого потребуется приличный GPU, чтобы не ждать годами.
Плагин работает на IDA Pro 7.4 или старше. Первоначальная разработка финансировалась «Лабораторией Касперского», сейчас проект нашел другого спонсора. Поддерживает множество моделей, в том числе локальных. Настройка через config.ini. Хотя для работы с GPT достаточно добавить свой ключ в переменную окружения OPENAI_API_KEY.
После установки в меню Edit появился новый пункт Gepetto. Он предоставляет две команды: Rename variables и Explain function.
Для теста я выбрал самую жирную функцию и попросил объяснить, как она работает. На выходе меня ждал объемный комментарий на три килобайта. Язык настраивается в конфиге, но я оставил английский по умолчанию.
GPT выжал максимум из доступного, но жалуется на нехватку контекста — информации о вызываемых функциях, что логично. Действительно, анализ лучше начинать «снизу» и постепенно идти «наверх». Тогда новые имена вызываемых функций будут подспорьем для анализа вызывающего их кода.
Теперь проверим, как работает автоматическое именование переменных. GPT назвал возвращаемое значение returnCode, распознал работу с таблицей и правильно именовал индексы. Весьма недурно.
Комментарий детально описал, что делает функция, и даже предложил ей удачное имя.
Подводя итоги, можно сказать, что плагин оказался весьма полезным. Он помогает автоматизировать скучную часть анализа, при этом эффективно расходуя бюджет — анализ одной функции стоит меньше цента.
Декомпиляция ассемблерного кода
Начнем с простого теста. Возьмем ассемблерный код и попросим GPT декомпилировать в псевдо-C.Декомпилируй ассемблерный код из IDA Pro в псевдо-C, как Hex-Rays.
Код:
waveOut_work proc near
push ebx
mov ebx, offset pwh
loc_409F0E:
push 20h ; ' ' ; cbwh
push ebx ; pwh
push ds:hwo ; hwo
call __imp_waveOutUnprepareHeader
add ebx, 20h ; ' '
cmp ebx, offset byte_412DE4
jb short loc_409F0E
push ds:hwo ; hwo
call __imp_waveOutClose
pop ebx
retn
waveOut_work endp
Правильный ответ от Hex-Rays:
Код:
MMRESULT waveOut_work()
{
struct wavehdr_tag *v0; // ebx
v0 = &pwh;
do
waveOutUnprepareHeader(hwo, v0++, 0x20u);
while ( v0 < (struct wavehdr_tag *)&byte_412DE4 );
return waveOutClose(hwo);
}
Ответ GPT-4o:
Код:
void waveOut_work()
{
WAVEHDR *pwh = (WAVEHDR *) &pwh; // Начало массива структур WAVEHDR
while ((BYTE *)pwh < (BYTE *)0x412DE4)
{
waveOutUnprepareHeader(hwo, pwh, sizeof(WAVEHDR));
pwh++;
}
waveOutClose(hwo);
}
IDA предполагает, что функция возвращает MMRESULT, хотя смысла получать ответ от waveOutClose нет и void — правильный тип. Имя WAVEHDR понятней, чем wavehdr_tag, хотя это одна и та же структура.
Пока ответ GPT лучше, попробуем с участком кода побольше.
Декомпилируй ассемблерный код из IDA Pro в псевдо-C, как Hex-Rays.
Код:
sub_40C5A4 proc near
String = dword ptr 4
push ebx
push esi
mov ebx, [esp+8+String]
mov esi, 105h
cmp word ptr [ebx], 21h ; '!'
jnz short loc_40C5D0
xor ebx, ebx
call __imp_GetVersion
test eax, 80000000h
mov esi, 3
jnz short loc_40C5FB
mov esi, 0Bh
jmp short loc_40C5FB
loc_40C5D0:
cmp word ptr [ebx], 3Fh ; '?'
jnz short loc_40C5DF
xor ebx, ebx
mov esi, 3
jmp short loc_40C5FB
loc_40C5DF:
movsx ecx, byte ptr [ebx]
test ds__ctype+1)[ecx], 4
jz short loc_40C5FB
push ebx ; String
call j__atoi
mov ebx, eax
mov esi, 1
add esp, 4
loc_40C5FB:
push ebx ; dwData
push esi ; uCommand
push offset FullPath_HLP ; lpszHelp
push ds:g_hWnd_0 ; hWndMain
call __imp_WinHelpA
pop esi
pop ebx
retn 4
sub_40C5A4 endp
Ответ Hex-Rays:
Код:
BOOL __stdcall sub_40C5A4(char *String)
{
char *v1; // ebx
UINT v2; // esi
v1 = String;
v2 = 261;
if ( *(_WORD *)String == 33 )
{
v1 = 0;
v2 = 3;
if ( (GetVersion() & 0x80000000) == 0 )
v2 = 11;
}
else if ( *(_WORD *)String == 63 )
{
v1 = 0;
v2 = 3;
}
else if ( (*((_BYTE *)_ctype + *String + 1) & 4) != 0 )
{
v1 = (char *)j__atoi(String);
v2 = 1;
}
return WinHelpA(g_hWnd_0, FullPath_HLP, v2, (ULONG_PTR)v1);
}
Ответ GPT-4o:
Код:
int __stdcall sub_40C5A4(LPCSTR String)
{
int dwData = 0;
UINT uCommand = 0x105; // Значение по умолчанию
if (*(WORD *)String == '!')
{
// Если строка начинается с '!'
GetVersion(); // результат используется только для проверки платформы
if (GetVersion() & 0x80000000)
uCommand = 3;
else
uCommand = 0x0B;
}
else if (*(WORD *)String == '?')
{
// Если строка начинается с '?'
uCommand = 3;
}
else if (isxdigit((unsigned char)*String)) // или isdigit, если __ctype+1[ecx] == 4 означает цифру
{
dwData = atoi(String);
uCommand = 1;
}
WinHelpA(g_hWnd_0, FullPath_HLP, uCommand, dwData);
return 0;
}
И вот у модели начинаются галлюцинации. Зачем‑то поставила два вызова GetVersion. Откуда‑то взялся возвращаемый ноль, хотя из кода очевидно, что EAX берется из WinHelpA, следовательно, тип возврата должен быть BOOL.
Тем не менее модель удачно распознала имена переменных, преобразовала константы к нужным типам и даже увидела макрос isdigit. Если бы не галлюцинации, инструменту бы цены не было. Круто, но Hex-Rays пока еще рано списывать со счетов.
Наивная декомпиляция си-кода
Считается, что с анализом кода на си GPT справляется лучше. Проверим это простым скриптом на IDAPython, работающим через API от OpenAI.
Код:
import idc
import idaapi
import idautils
import openai
import re
client = openai.OpenAI(
api_key="sk-proj-*"
)
def ask_gpt_about_function(c_code):
prompt_text = f"""
Here is a C-like decompiled function from a binary:
{c_code}
Please suggest a concise and descriptive name for this function (using snake_case), and provide a brief explanation of what it does.
Код:
Respond strictly in the following format:
Function name: <name>
Description: <short explanation>
"""
try:
response = client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "user", "content": prompt_text}
],
temperature=0.5
)
return response.choices[0].message.content
except Exception as e:
print(f"[!] GPT API error: {e}")
return None
def apply_result_to_function(ea, gpt_response):
match = re.search(r"Function name:\s*(\w+).?Description:\s(.*)", gpt_response, re.DOTALL)
if match:
name = "gpt_" + match.group(1)
desc = match.group(2).strip()
if ida_name.force_name(ea, name, idc.SN_AUTO):
print(f"[+] Renamed function at {hex(ea)} to: {name}")
else:
print(f"[!] Could not rename function to: {name}")
idc.set_func_cmt(ea, desc, 0)
else:
print(f"[!] Could not parse GPT response:\n{gpt_response}")
def is_user_defined_name(ea):
return not idc.get_name(ea).startswith("sub_")
def get_decompiled_code(ea):
try:
cfunc = idaapi.decompile(ea)
return str(cfunc)
except Exception as e:
print(f"[!] Failed to decompile function at {hex(ea)}: {e}")
return None
def main():
for func_ea in idautils.Functions():
print(f"[*] Processing function at {hex(func_ea)} {is_user_defined_name(func_ea)}")
if is_user_defined_name(func_ea):
continue
code = get_decompiled_code(func_ea)
if not code:
continue
gpt_result = ask_gpt_about_function(code)
if gpt_result:
apply_result_to_function(func_ea, gpt_result)
main()
Код получает адреса распознанных функций и, если функция еще не была переименована, отправляет декомпилированный Hex-Rays код запросом к GPT. В ответе мы ожидаем увидеть новое имя функции и краткое описание, что она делает. Далее скрипт переименовывает функцию и добавляет комментарий с описанием ее работы.
Код:
// This function swaps the second and third bytes of the input array and then masks the second byte with a value from a global byte array and a bitwise AND operation. The result is stored back into the second byte of the input array.
int __fastcall gpt_swap_and_mask_bytes_0(int a1, int a2, _BYTE *a3)
{
int result; // eax
LOBYTE(a2) = a3[2];
a3[2] = a3[3];
a3[3] = a2;
LOBYTE(a2) = a3[1];
result = a2;
LOBYTE(result) = byte_4110E4[(int)(unsigned __int8)a2 >> 4] | a2 & 0xF;
a3[1] = result;
return result;
}
Для маленьких функций без контекста придуманные GPT названия практически бесполезны. Если бы мы предварительно указали имена аргументов и правильные типы, анализ был бы точнее. Еще одна проблема скрипта — порядок функций. Сначала надо передавать на анализ вызываемые функции и только затем вызывающие, чтобы расширить контекст последних. Для этого надо создать граф вызовов и постепенно идти от самых дальних узлов наверх.
Попробуем отдать GPT большую изолированную функцию, понятную без внешнего контекста.
Код:
// This function copies and inverts the pixels from one area of the image to another. The source and destination areas, as well as the dimensions, are determined by the input parameters. The pixels are processed in reverse order, and zero pixels are skipped.
char __stdcall gpt_copy_inverted_pixels(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9)
{
char *v9; // edi
char *v10; // esi
int v11; // edx
int v12; // ebp
int v13; // ecx
char result; // al
v9 = (char *)lpBits + a4 + dword_412CD0 * (dword_412CD4 - 1 - a3);
v10 = (char *)(a9 + a8 + a6 + a6 * -a7 - 1);
v11 = a2 - a4 + dword_412CD0;
v12 = a1 - a3;
dword_412CD8 = a2 - a4;
do
{
v13 = dword_412CD8;
while ( 1 )
{
result = *v10--;
if ( !result )
break;
LABEL_4:
*v9++ = result;
if ( !--v13 )
goto LABEL_8;
}
while ( 1 )
{
++v9;
if ( !--v13 )
break;
result = *v10--;
if ( result )
goto LABEL_4;
}
LABEL_8:
v9 -= v11;
v10 += a2 - a4 + a6;
}
while ( v12-- > 1 );
return result;
}
Работа над байтами без вызова внешнего кода декодируется достаточно бодро. GPT понял, что алгоритм работает с двумя областями изображения. Действительно, по коду v12 = a1 - a3 можно догадаться, что это координаты x1 и x2.
Разбирать подобную математику вручную было бы слишком утомительно. Обычно я просто смотрю в отладчике, что было с данными до и что стало после. А из этого уже делаю выводы о назначении функции.
Наивность текущего подхода — в зашкаливающем числе запросов. Модель GPT-4 за анализ двухсот небольших функций сожрала аж три бакса! Вероятно, модели попроще стоят дешевле. В любом случае подобный анализ применим только к конкретной функции. То есть загрузить exe-файл целиком и получить его исходный код пока что не удастся.
IDA Copilot
- Для просмотра ссылки Войди
или Зарегистрируйся
Код:
tools = [
self.__GetAddressInfoTool(),
self.__GetDefinitionTool(),
self.__GetPseudocodeTool(),
self.__SetFunctionCommentTool(),
self.__SetFunctionDefinitionTool(),
self.__SetFunctionNameTool(),
self.__GetIsMyWorkDoneTool(ea)
]
class __GetAddressInfoTool(BaseTool):
name = 'get_address_info'
description = ('Given a hex address or function name, show its information. '
'Input Format: <hex_address_or_function_name>. '
'Input Example1: sub_140007080. '
'Input Example2: 0x140007080.')
У каждого инструмента есть имя и описание, а также функция _run, принимающая строку с аргументом от модели.
Установка
В requirements.txt указаны старые версии библиотек, их установка ломает другие плагины. Так что советую переставить их после работы с плагином вручную.При запуске выясняется еще одна проблема: плагин запрашивает модель gpt-3.5-turbo-0613, которая давно не существует. Заменяю в коде актуальной.
Код:
class Copilot:
def run(self, temperature=0.5, model='gpt-4o'):
Устанавливаю курсор на тестовую функцию и запускаю плагин. Запрос уходит на сервер, функция меняет имя. Но добавленный комментарий на китайском. Меняем промпт на английский, так как оригинал тоже был на китайском.
Код:
default_prompt_en = PromptTemplate(
input_variables=['binary_description'],
template="""
You are Copilot, a professional reverse engineer currently conducting an in-depth analysis of a binary file. You are using IDA Pro and have observed a specific function's decompiled pseudocode.
Your task is to perform a comprehensive analysis of this pseudocode to better understand its purpose and logic. Please follow the guidelines below:
1. Function Purpose Analysis: Describe in detail what this function does and what its purpose is. Add comments in English directly to the function, each prefixed with
Copilot Comment:
so they are clearly distinguishable.2. Function Signature Correction: Based on your understanding of the code logic, infer and correct any function signatures that may have been incorrectly or ambiguously decompiled by IDA Pro. Explain in detail why you made this decision.
3. Function Naming Analysis: Thoroughly analyze the current function and all related functions it calls. Rename any functions starting with
sub_
to more meaningful names, and provide clear explanations for each renaming.Description:
Код:
{binary_description}
Please continue your analysis until get_is_my_work_done tells you the work is complete.
""")
Поначалу все работает нормально, но затем я натыкаюсь на ограничения по времени:
Добавляем задержку для каждой функции:Rate limit reached for gpt-4o on tokens per min (TPM): Limit 30000, Used 26575, Requested 3712.
Код:
import time
class __GetIsMyWorkDoneTool(BaseTool):
def _run(self, query: str, run_manager: Optional[CallbackManagerForToolRun] = None) -> Any:
time.sleep(1)
class __SetFunctionNameTool(BaseTool):
def _run(self, query: str, run_manager: Optional[CallbackManagerForToolRun] = None) -> Any:
time.sleep(1)
Теперь с плагином можно работать.
Тестирование
Код:
BOOL show_help()
{
BOOL result; // eax
if ( g_hWnd_0 )
{
WinHelpA(g_hWnd_0, FullPath_HLP, 2u, 0);
return DestroyWindow(g_hWnd_0);
}
return result;
}
Возьмем небольшую функцию и проследим цепочку рассуждений Copilot.
- Для начала анализа необходимо получить псевдокод
show_help
- Для этого вызывается действие
get_pseudocode
- Псевдокод отображает справку через
WinHelpA
- Следуя гайдлайну:
- Добавим комментарий к псевдокоду
- Исправим прототип функции при необходимости
- Проанализируем и поменяем имена функций
- Я собираюсь добавить комментарий:
- Вызываю действие
set_function_comment
- Получаю текст комментария и ставлю его на функцию
- Комментарий успешно добавлен, теперь анализ прототипов:
- Текущий прототип
BOOL show_help()
- Код возвращает
BOOL
от DestroyWindow
, все верно- Теперь проведем анализ имени:
- Имя сообщает об отображении справки
- Переименование не требуется
-
WinHelpA
и DestroyWindow
— Windows API- Переименование не требуется
- Убедимся, что все задачи исполнены:
- Вызываем
get_is_my_work_done
- Получаем ошибку
'NoneType' object has no attribute 'startswith'
- Вызываем
get_is_my_work_done
еще раз- Получаем ту же самую ошибку
- Похоже, ошибка имеет постоянный характер
- Это мешает проверке завершения всех задач
- Однако, исходя из текста гайдлайна, все задачи исполнены
- Анализ завершен
Это краткий пересказ, полный лог занимает шесть килобайт. Идея плагина крутая, хотя реализация сыровата. Запросов к GPT довольно много, но результат работы того стоит. Инструмент недоделан, однако на его основе можно написать агент под свои задачи.
aiDAPal
- Для просмотра ссылки Войди
или Зарегистрируйся
Натравив плагин на реальную функцию, через раз получаю ошибку ERROR - aiDAPal: error unpacking response. Значит, модель не всегда отдает правильные ответы. Попробую перехватить ее вход и выход, чтобы оценить масштаб проблемы. Добавляю свои print в оригинальный метод.
Код:
def do_analysis(code,model_name):
url = ollama_url
headers = {"Content-Type": "application/json"}
payload = {"model": model_name, "prompt": code, "stream": False,"format":"json"}
print('Prompt:', code)
res = requests.post(url, headers=headers, json=payload)
try:
t = res.json()['response']
t = json.loads(t)
print('Response:', t)
return t
except:
# rarely this occurs, leftover from early on
logging.error(f'aiDAPal: error unpacking response\n{res.json()["response"]}')
return None
Запускаю анализ функции.
Код:
Prompt:
/*
0x401a74 is __imp_GetVersion //
0x417404 is g_hWnd_0 //
0x401b08 is __imp_WinHelpA //
*/
BOOL __stdcall sub_40C5A4(char *String)
{
char *v1; // ebx
UINT v2; // esi
v1 = String;
v2 = 261;
if ( *(_WORD *)String == 33 )
{
v1 = 0;
v2 = 3;
if ( (GetVersion() & 0x80000000) == 0 )
v2 = 11;
}
else if ( *(_WORD *)String == 63 )
{
v1 = 0;
v2 = 3;
}
else if ( (*((_BYTE *)_ctype + *String + 1) & 4) != 0 )
{
v1 = (char *)j__atoi(String);
v2 = 1;
}
return WinHelpA(g_hWnd_0, FullPath_HLP, v2, (ULONG_PTR)v1);
}
Response:
{
"function":"sub_40C5A4",
"comment":"This function interprets a string input and performs specific actions based on the first character of the string. It primarily calls WinHelpA with different parameters depending on the initial character of the input string.",
"variables":[
{
"original_name":"String",
"new_name":"input_string"
},
{
"original_name":"v1",
"new_name":"temp_variable"
},
{
"original_name":"v2",
"new_name":"command_flag"
}
],
}
Ответ практически идеален, разве что имя функции не поменяла. Попробуем с другим кодом.
Код:
Prompt: int __stdcall sub_409590(int a1)
{
unsigned int v1; // esi
int result; // eax
unsigned int *v3; // edx
v1 = *(_DWORD *)(a1 + 12);
result = dword_417300 + 8 * dword_417204 - 8;
if ( v1 < *(_DWORD *)(result + 4) )
{
v3 = (unsigned int *)(result + 4);
do
{
v3 -= 2;
result -= 8;
}
while ( *v3 > v1 );
}
return result;
}
Response:
{
"function":"sub_409590",
"comment":"This function seems to be calculating an index based on a given input value and a set of predefined values. It uses several pointer arithmetic operations and conditional logic to find the index, possibly for use in accessing or manipulating data in memory.",
"variables":[
{
"original_name":"a1",
"comment":"Input parameter"
},
{
"original_name":"v1",
"comment":"Value calculated from input parameter"
},
{
"original_name":"result",
"comment":"Calculated index or address"
},
{
"original_name":"v3",
"comment":"Pointer used in loop to compare and update index"
}
]
}
Теперь вместо переименования переменных модель добавляет к ним комментарий. Спасибо, но не от всей души. Код в этом месте ломается, так как не может найти в ответе обязательное поле new_name.
Код:
Prompt: int __stdcall sub_408F20(char *String)
{
return sub_40C5A4(String);
}
Response:
{
"function":"int sub_408F20(char *String)",
"comment":"This function is a simple wrapper for another function, 'sub_40C5A4'. It takes a character string as an argument and returns the result of calling 'sub_40C5A4' with that string.",
"variables":[
{
"originalName":"String",
"newName":"inputString"
}
]
}
Response:
{
"function":"int __fastcall sub_408F20(_DWORD *String)",
"comment":"This function is a simple wrapper for another function named 'sub_40C5A4'. It takes a pointer to a character string as its argument, calls 'sub_40C5A4' with that argument, and returns the result of that call."
}
Перезапустив анализ пару раз на новом коде, видим, что модель отдает неполные данные и откровенно галлюцинирует, добавляя __fastcall.
Задумка была крутой, но скромный размер модели не позволяет использовать ее в работе, разве что в цикле запрашивать ответы, пока не выдаст верный. Но для этого потребуется приличный GPU, чтобы не ждать годами.
Gepetto
Для просмотра ссылки Войдиили Зарегистрируйся
Плагин работает на IDA Pro 7.4 или старше. Первоначальная разработка финансировалась «Лабораторией Касперского», сейчас проект нашел другого спонсора. Поддерживает множество моделей, в том числе локальных. Настройка через config.ini. Хотя для работы с GPT достаточно добавить свой ключ в переменную окружения OPENAI_API_KEY.
После установки в меню Edit появился новый пункт Gepetto. Он предоставляет две команды: Rename variables и Explain function.
Для теста я выбрал самую жирную функцию и попросил объяснить, как она работает. На выходе меня ждал объемный комментарий на три килобайта. Язык настраивается в конфиге, но я оставил английский по умолчанию.
// Note that without more context on
// what this function is part of or specific documentation on the external
// functions it calls, creating an accurate name involves some degree of
// speculation.
GPT выжал максимум из доступного, но жалуется на нехватку контекста — информации о вызываемых функциях, что логично. Действительно, анализ лучше начинать «снизу» и постепенно идти «наверх». Тогда новые имена вызываемых функций будут подспорьем для анализа вызывающего их кода.
Код:
char sub_4081B8()
{
int rowIndex; // ecx
int columnIndex; // ebx
_BYTE *currentCell; // edx
char returnCode; // [esp+4h] [ebp-4h]
rowIndex = 0;
columnIndex = 0;
returnCode = 0;
for ( currentCell = (_BYTE *)dword_4173D4; !*currentCell; ++currentCell )
{
if ( rowIndex && *(currentCell - 1) == 6
|| rowIndex < dword_4173D8 && currentCell[1] == 6
|| columnIndex && currentCell[dword_4173DC] == 6
|| columnIndex < dword_4173E0 && currentCell[dword_4171E8] == 6 )
{
returnCode = 4;
*currentCell = 4;
}
LABEL_12:
if ( ++rowIndex >= dword_4171E8 )
{
if ( ++columnIndex >= dword_4171EC )
return returnCode;
rowIndex = 0;
}
}
if ( *currentCell != 2
|| (!rowIndex || *(currentCell - 1) != 6)
&& (rowIndex >= dword_4173D8 || currentCell[1] != 6)
&& (!columnIndex || currentCell[dword_4173DC] != 6)
&& (columnIndex >= dword_4173E0 || currentCell[dword_4171E8] != 6) )
{
goto LABEL_12;
}
dword_412C94 = rowIndex;
dword_412C98 = columnIndex;
return 1;
}
Теперь проверим, как работает автоматическое именование переменных. GPT назвал возвращаемое значение returnCode, распознал работу с таблицей и правильно именовал индексы. Весьма недурно.
// ----- Comment generated by Gepetto -----
//
// The given C function appears to process a two-dimensional array (or grid) based
// on conditions involving the number6
, and updates its values. Let's break down
// its operations to understand what's happening:
(...)
// ### Suggested Name
// A more descriptive name for the
// function could beprocessGridForValue6AndSetFlag
. This name indicates that the
// function processes a grid-like structure to inspect values and sets a flag based
// on conditions involving the number6
.
Комментарий детально описал, что делает функция, и даже предложил ей удачное имя.
Подводя итоги, можно сказать, что плагин оказался весьма полезным. Он помогает автоматизировать скучную часть анализа, при этом эффективно расходуя бюджет — анализ одной функции стоит меньше цента.