Я хочу эффективно находить определенные функции в IDA Pro, используя значение «Аргументы» на вкладке «Функции». Однако для следующих примеров кода, полученных в результате декомпиляции IDA:
__int64 __fastcall sub_49FB60(__int64 a1)
{
sub_484690(a1 + 208, 0LL);
sub_484690(a1 + 112, 0LL);
return sub_484690(a1 + 16, 0LL);
}
_QWORD *__fastcall sub_4A0050(_QWORD *a1, __int64 a2)
{
sub_4A3CB0(a1);
*a1 = off_979AD8;
a1[13] = a2;
return a1;
}
__int64 __fastcall sub_4D3D20(__int64 a1, __int64 a2, unsigned __int8 a3)
{
__int64 v5; // rcx
*(_QWORD *)a1 = a2;
v5 = *(_QWORD *)(*(int *)(*(_QWORD *)a2 + 4LL) + a2 + 72);
if ( v5 )
(*(void (__fastcall **)(__int64))(*(_QWORD *)v5 + 8LL))(v5);
*(_BYTE *)(a1 + 8) = sub_4D3BC0(*(_QWORD *)a1, a3);
return a1;
}
Все эти функции имеют значение «Аргументы» 00000010. Первоначально я думал, что оно может представлять собой сумму размеров аргументов, но вычисления не совпадают. Что на самом деле представляет собой поле «Аргументы»?
【Функции коснитесь изображения】
Кроме того, что означает значение «Аргументы» для предоставленных примеров кода?
__int64 __fastcall sub_52AFD0(unsigned int **a1, unsinged int a2, __int64 a3)
{
unsinged int v3;
unsinged int v4;
unsinged int v6;
unsinged __int i;
v6 =-1;
for ( i = a2 / 8ui64; i; --i)
{
v3 = **a1 ^ v6;
++*a1;
v4 = **a1;
v6 = *(_DWORD *)(a3 + 4i64 * (v4 >> 24)));
}
return v6;
}
Пожалуйста, покажите сборку для некоторых из этих функций, а также макет стека той же функции (Правка->Функции->Переменные стека).
Поскольку я впервые использую Stack Overflow, я не знал, что здесь можно публиковать ответы. Извините за поздний ответ. Я предоставлю запрошенный вами ассемблерный код. !код_сборки ! код_декомплие ! функции_tap ! стек_переменные
Было бы полезно добавить к вопросу, что это за архитектура. x86_64 (он же amd64)?
Вы вручную вводили декомпилированные фрагменты кода в вопросе? Я скептически отношусь к тому, что IDA напишет unsigned
с ошибкой как unsinged
.
Значение в столбце «Аргументы», похоже, представляет собой пространство, зарезервированное для аргументов функции перед кадром стека функции (более высокие адреса стека). Следовательно, он не представляет общий размер всех аргументов функции, а только тех, которые прошли через стек. Спасибо Андрею Туркину за то, что указал на это в комментариях ниже.
Например, дизассемблируя /bin/awk
в моей системе Linux x86-64 с IDA Free 8.4, я вижу следующую функцию:
__int64 __fastcall sub_47EC80(
__int64 a1,
unsigned int *a2,
int a3,
unsigned int a4,
unsigned int a5,
int a6,
unsigned int a7)
{
// ...
}
В столбце «Аргументы» отображается 00000004
. Если посмотреть на разборку, то можно увидеть следующее:
.text:000000000047EC80 ; =============== S U B R O U T I N E =======================================
.text:000000000047EC80
.text:000000000047EC80
.text:000000000047EC80 ; __int64 __fastcall sub_47EC80(__int64, unsigned int *, int, unsigned int, unsigned int, int, unsigned int)
.text:000000000047EC80 sub_47EC80 proc near ; CODE XREF: sub_47F350+2F↓p
.text:000000000047EC80 ; sub_47F500+6C5↓p
.text:000000000047EC80
.text:000000000047EC80 var_D0 = dword ptr -0D0h
... a bunch more var_XX here ...
.text:000000000047EC80 var_54 = dword ptr -54h
.text:000000000047EC80 ptr = qword ptr -50h
.text:000000000047EC80 var_40 = qword ptr -40h
.text:000000000047EC80 arg_0 = dword ptr 8
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ arg passed through stack
.text:000000000047EC80
.text:000000000047EC80 ; __unwind {
.text:000000000047EC80 push r15
.text:000000000047EC82 mov r15, rdi
.text:000000000047EC85 push r14
.text:000000000047EC87 push r13
.text:000000000047EC89 push r12
...
Обратите внимание, что единственная переменная, выделенная после указателя стека (с положительным смещением 8
), — это arg_0
. Это соответствует аргументу a7
в сигнатуре функции. В этом случае первые 6 аргументов передаются через регистры RDI, RSI, RDX, RCX, R8 и R9. Седьмой номер передается в стек.
Фактически, если я взгляну на внешние ссылки (нажмите X на имени функции), я увижу этот вызов:
.text:000000000047FBAD mov [rsp+0E8h+var_E0], r10
.text:000000000047FBB2 sub rsp, 8
.text:000000000047FBB6 mov edx, [r15+4]
.text:000000000047FBBA mov r9d, r13d
.text:000000000047FBBD push 9
.text:000000000047FBBF mov r8d, ebp
.text:000000000047FBC2 mov rdi, r14
.text:000000000047FBC5 call sub_47EC80
Как видите, седьмой аргумент передается с помощью push 9
. Несмотря на то, что инструкция push 9
помещает 8-байтовое значение в стек, функция, похоже, получает доступ к значению только с помощью 4-байтовых (dword
) операций разыменования (или в любом случае обрабатывает его как 4-байтовое значение). Видимо, поэтому в столбце «Аргументы» отображается 00000004
.
Аналогично, для этой другой функции:
__int64 __fastcall sub_44DC90(
__int64 a1,
const char *a2,
int a3,
__int64 a4,
__int64 a5,
void (__fastcall *a6)(_QWORD, _QWORD, _QWORD, _QWORD),
__int64 a7,
__int64 a8,
char a9)
{
// ...
}
Я вижу, что в столбце «Аргументы» отображается 00000011
(17), потому что __int64 a7
, __int64 a8
и char a9
передаются через стек:
...
mov rdi, 7FFFFFFFFFFFFFFFh
mov [rsp+60h+var_48], rax
push 0
push r12
push rbp
call r15
...
IDA имеет довольно хорошую встроенную документацию. Если вы поместите курсор в окно функции, а затем нажмете клавишу F1, документация откроется в новом окне. Вот как это выглядит на моей IDA Free 8.4.
Глядя на страницу «Окно функций» в документации, можно увидеть следующие поля:
Эта же страница документации также доступна онлайн здесь.
Судя по документу, столбец «Аргументы» должен представлять «размер аргументов, передаваемых функции». На YouTube-канале Hex-Rays также есть видео об окне «Функции» с пояснениями, а также говорится, что столбец «Аргументы» должен представлять «размер аргументов, передаваемых функции».
Эти описания кажутся слишком общими и не совсем точно отражают смысл столбца. Похоже, страницу документации следует обновить более четкими описаниями.
Те же рассуждения справедливы для 2-го и 3-го столбцов (Сегмент и Начало): столбец «Начало» определенно не представляет смещение внутри сегмента ELF, показанного в столбце «Сегмент» (хотя он показывает .text
, который является разделом), а скорее абсолютный виртуальный адрес. Это может быть потому, что, как предлагает Бен Фойгт ниже, слово «сегмент» в документации для столбца «Сегмент» относится к двоичным (например, ELF) сегментам/разделам, но для столбца «Начало» оно может относиться к определенной архитектуре. сегменты, когда ЦП использует сегментированную модель памяти (например, сегментированный 16-битный реальный режим x86). Сбивает с толку.
P.S. для тех, кто голосует против: было бы неплохо оставить комментарий, объясняющий причину отрицательного голоса, если вы считаете, что этот ответ бесполезен, чтобы я мог его исправить/улучшить!
Большое спасибо за ваш искренний ответ.
Вы правы в том, что это поле представляет собой размер стека области аргументов. IDA (x64) 7.7 показывает именно это. Например. поле вообще не заполнено для функции с 1 аргументом в двоичном файле Linux, но оно равно 8 для функции с 1 аргументом в двоичном файле Windows (где аргумент все еще передается в качестве аргумента, но затем сохраняется в теневое пространство). Я думаю, что более конкретно это поле представляет размер области стека над адресом возврата, как показано в окне стека функций.
@AndreyTurkin, теперь это имеет гораздо больше смысла. Хотя бывают странные случаи, это кажется правильным определением. Я обновлю свой ответ, спасибо!
"определенно не является "смещением функции внутри сегмента", а абсолютным виртуальным адресом функции... это одно и то же, если только не используется сегментированная архитектура памяти (например, 16-битная x86). "сегмент" != " раздел"
Может ли значение «аргументы» иметь какое-либо отношение к соглашению о вызовах в регистрах сохранения вызывающего абонента и вызываемого абонента? Если определенное количество регистров доступно вызываемому объекту для использования в качестве нуля, даже если не передается никаких значимых данных, они все равно могут рассматриваться как хранилище аргументов для целей этого расчета.
@BenVoigt Кажется, вы правы в том, что это смещение внутри сегмента (как в сегменте, определенном архитектурой в сегментированной модели памяти). Это имело бы больше смысла. Что сбивает с толку, так это то, что если вы посмотрите на то, что IDA называет «сегментом», вы увидите, что в столбце «Сегмент» указано .text
... что является сегментом ELF. Итак, читая документ, мы видим «сегмент, содержащий функцию», за которым сразу следует «смещение функции внутри сегмента», что мне кажется совершенно неправильным/вводящим в заблуждение.
@BenVoigt, похоже, это не имеет особого отношения к регистрам, сохраненным вызывающим/вызывающим объектом, поскольку я вижу различные функции с нулевым столбцом «Аргументы», использующие сохраненные регистры вызывающего абонента для раздачи аргументов. Кажется, это связано только с пространством стека, непосредственно используемым вызывающей стороной для аргументов, как предположил выше Андрей.
Большое спасибо всем
@MarcoBonelli: .text
— это раздел, а не сегмент (в значении указателя/адреса). Даже в контексте ELF Похоже, что IDA неправильно это называет.
У меня есть вопрос. Согласно MSDN, на x64 для аргументов используются 4 регистра и начиная с 5-го аргумента используется стек. Однако в вашем объяснении упоминается использование 6 регистров. Какой из них правильный?
@jeong: MSDN документирует соглашение о вызовах Windows x86_64, которое не совсем совпадает с соглашением SYS V, на котором основан ответ (упоминание «SYS V» было удалено в одном из изменений).
О, я был в замешательстве, потому что я привык к Windows. Спасибо.
@BenVoigt Если да, то сложно ли оценить значение аргумента, только взглянув на декомпилированный код? Я использую 64-битную систему Windows. <br/> Пример декомпилированного исходного кода
Я хотел бы получить значение locals
на вкладке «Функция» в IDA.
@jeong: Сигнатуры функций, сгенерированные IDA, очень полезны в этом отношении (гораздо более полезны, чем когда-либо был бы размер «аргументов», даже если бы он был правильным). Однако IDA не вывела указателей на структуры, и декомпиляция вашего примера включает в себя то, что для меня явно является доступом к членам структуры. *a1
и a3
оба являются указателями на неизвестные типы структур.
Я думаю, что некоторые из показанных вами функций представляют собой вычисления CRC.
@BenVoigt «.text — это раздел, а не сегмент», вы правы, я виноват, хотя обычно он соответствует 1:1 разделу RX, поэтому нередко можно услышать «текстовый раздел». Путаница с именами и описанием вещей IDA довольно дикая :')
@BenVoigt Спасибо, благодаря тебе я многому научился.
Как выглядит ассемблерный код? Я предполагаю, что значение аргументов — это то, что добавляется в RSP при выходе (не включая обратный адрес).