Реверс-инженеру на заметку: дизассемблирование указателей

В одной из предыдущих статей мы рассматривали, как применяются и выглядят в машинном коде массивы. Пришло время поговорить про указатели. Как и прежде, будем использовать для дизассемблирования 64-битную версию IDA Pro и язык программирования «Си».

Итак, исходный код указателей:

int Pointers() {
    int num = 10;

    // указатель на целочисленную переменную
    // включает адрес этой переменной
    int *pointer;

    // &<переменная> вернёт адрес указанной переменной
    pointer = #

    printf("num: %d\n", num);
    printf("*pointer: %d\n", *pointer);
    printf("Address of num: %p\n", &num);
    printf("Address of num using pointer: %p\n", pointer);
    printf("Address of pointer: %p\n", &pointer);

    return 0;
}

Что же, на первый взгляд ничего сложно нет. Но давайте разбираться в машинном коде. Сначала присваиваем переменной num значение 10 (int num = 10).

Идём дальше. Следующий этап — присваивание указателю pointer адреса переменной num (pointer = &num).

Продолжаем. Судя по машинному коду, мы выводим переменную num на экран.

Также нам потребуется вывести на экран и переменную pointer. Вот, как это выглядит в машинном коде:

Теперь выводим адрес переменной num. Это осуществляется посредством инструкции lea (речь идёт о загрузке результирующего адреса) вместо mov:

Теперь выводим адрес num через указатель pointer. Машинный код:

Останется вывести адрес pointer, что происходит посредством инструкции lea вместо mov. Машинный код:

Вот и всё, что можно сказать про работу указателей и их отображение в машинном коде. Впрочем, вам также необходимо знать особенности дизассемблирования при реализации таких продвинутых концепций, как динамическое распределение памяти, многопоточность и программирование сокетов. Понимание этих аспектов поможет вам продвинуться в освоении реверс-инжиниринга. Но об этом — в следующий раз.

Источник