Память косвенного вызова - это вызов, который берет адрес вызываемого абонента из памяти, а регистр косвенного вызова соответственно получает адрес из регистра.
Реализация вызовов очень зависит от архитектуры и используемого компилятора.На x86 возможны как вызовы памяти, так и регистров, поэтому компилятор может выбрать оптимальный вариант.
Взгляните на простой тест:
#include "stdlib.h"
#include "stdio.h"
#include "time.h"
void example_fun(int param)
{
printf("%d\n", param);
}
void fn1(void)
{
printf("fn1\n");
}
void fn2(void)
{
printf("fn2\n");
}
typedef void (*fn)(void);
int main(void)
{
void (*fp) (int) = &example_fun;
int c;
fn fn_arr[] = {&fn1, &fn2};
(*fp)(2);
srand(time(NULL));
c = rand() / (RAND_MAX / 2);
switch (c)
{
case 0: (*(fn_arr[0]))(); break;
case 1: (*(fn_arr[1]))(); break;
default: (*fp)(1);
}
}
Сгенерированная сборка (gcc4.6.1 без каких-либо оптимизаций):
.file "indirect.c"
.section .rodata
.LC0:
.string "%d\n"
.text
.globl example_fun
.type example_fun, @function
example_fun:
.LFB0:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
subl $24, %esp
movl $.LC0, %eax
movl 8(%ebp), %edx
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE0:
.size example_fun, .-example_fun
.section .rodata
.LC1:
.string "fn1"
.text
.globl fn1
.type fn1, @function
fn1:
.LFB1:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
subl $24, %esp
movl $.LC1, (%esp)
call puts
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE1:
.size fn1, .-fn1
.section .rodata
.LC2:
.string "fn2"
.text
.globl fn2
.type fn2, @function
fn2:
.LFB2:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
subl $24, %esp
movl $.LC2, (%esp)
call puts
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE2:
.size fn2, .-fn2
.globl main
.type main, @function
main:
.LFB3:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
andl $-16, %esp
subl $32, %esp
movl $example_fun, 24(%esp)
movl $fn1, 16(%esp)
movl $fn2, 20(%esp)
movl $2, (%esp)
movl 24(%esp), %eax
call *%eax
movl $0, (%esp)
call time
movl %eax, (%esp)
call srand
call rand
movl %eax, %ecx
movl $-2147483645, %edx
movl %ecx, %eax
imull %edx
leal (%edx,%ecx), %eax
movl %eax, %edx
sarl $29, %edx
movl %ecx, %eax
sarl $31, %eax
movl %edx, %ecx
subl %eax, %ecx
movl %ecx, %eax
movl %eax, 28(%esp)
movl 28(%esp), %eax
testl %eax, %eax
je .L6
cmpl $1, %eax
je .L7
jmp .L9
.L6:
movl 16(%esp), %eax
call *%eax
jmp .L10
.L7:
movl 20(%esp), %eax
call *%eax
jmp .L10
.L9:
movl $1, (%esp)
movl 24(%esp), %eax
call *%eax
.L10:
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE3:
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1"
.section .note.GNU-stack,"",@progbits
Во всех случаях наши вызовы переводились в call *%eax
, который является косвенным вызовом памяти, выполняющим функцию, расположенную по адресу, указанному %eax
.Добавление оптимизации портит структуру, но механика остается вызовом регистра.
Вызов общей динамической библиотеки (см. Пример здесь ) привел к просто call <function_name>
.Обратитесь к x86 Руководству разработчика , call
принимает регистр, память или операнд указателя.Такой вызов обрабатывается компоновщиком при запуске, а имя функции заменяется фактическим адресом памяти.Таким образом, вызов общей библиотеки приводит (в конце концов) к косвенному вызову памяти.
Помимо таких легко воспроизводимых случаев, вы можете столкнуться с операциями с непосредственным адресом, такими как call dword ptr ds:[00923030h]
(здесь описано ).) или аналог jmp
.Я не могу дать разумного объяснения, откуда они берутся.
Различить вызовы регистра и памяти довольно просто: просто посмотрите на операнд.
Если вы заинтересованы в руткитеобнаружение, было бы неплохо просто взять какой-нибудь существующий руткит и посмотреть его источники и сгенерированную сборку рядом.
Я не очень разбираюсь в компиляторах и архитектуре, поэтому могу ошибитьсянемного.
Надеюсь, это поможет.