Я хочу эффективно находить определенные функции в 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;
}
🤔 А знаете ли вы, что...
C является компилируемым языком, что означает, что программа на C должна быть скомпилирована перед выполнением.
Значение в столбце «Аргументы», похоже, представляет собой пространство, зарезервированное для аргументов функции перед кадром стека функции (более высокие адреса стека). Следовательно, он не представляет общий размер всех аргументов функции, а только тех, которые прошли через стек. Спасибо Андрею Туркину за то, что указал на это в комментариях ниже.
Например, дизассемблируя /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). Сбивает с толку.