Разница между памятью косвенного вызова и регистром косвенного вызова - PullRequest
4 голосов
/ 21 января 2012

В чем разница между памятью косвенного вызова и регистром косвенного вызова?

Я пытаюсь узнать кое-что об обнаружении руткитов в Linux, как я могу распознать такие вызовы в разобранной памяти? Как они выглядят на языке C перед компиляцией?

Ответы [ 2 ]

5 голосов
/ 22 января 2012

Косвенная ветвь - это ветвь, в которой ветвь создается по адресу, который хранится в регистре или в ячейке памяти.Операндом инструкции перехода является регистр или ячейка памяти, в которой хранится адрес ветви.

См. Страницу википедии для получения дополнительной информации: http://en.wikipedia.org/wiki/Indirect_branch

В C в зависимости от реализации (иЦП), косвенная ветвь часто создается, когда функция вызывается через указатель на функцию.И поскольку некоторые эвристики для операторов switch используют указатели на функции (через таблицы переходов), косвенные ветви также можно найти в операторах switch.

3 голосов
/ 29 января 2012

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

Реализация вызовов очень зависит от архитектуры и используемого компилятора.На 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.Я не могу дать разумного объяснения, откуда они берутся.

Различить вызовы регистра и памяти довольно просто: просто посмотрите на операнд.

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

Я не очень разбираюсь в компиляторах и архитектуре, поэтому могу ошибитьсянемного.

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...