Реверс-инженеру на заметку: дизассемблирование указателей
В одной из предыдущих статей мы рассматривали, как применяются и выглядят в машинном коде массивы. Пришло время поговорить про указатели.
Итак, исходный код указателей:
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. Машинный код:
Вот и всё, что можно сказать про работу указателей и их отображение в машинном коде. Впрочем, вам также необходимо знать особенности дизассемблирования при реализации таких продвинутых концепций, как динамическое распределение памяти, многопоточность и программирование сокетов. Понимание этих аспектов поможет вам продвинуться в освоении реверс-инжиниринга. Но об этом — в следующий раз.