Откройте для себя образ программы в памяти - PullRequest
3 голосов
/ 24 июня 2011

Вот кое-что менее важное, о чем я недавно размышлял:

Я знаю, что виртуальное адресное пространство моей программы содержит стек (каждого потока) и кучу, а также некоторую статически распределенную память ивсе это.Но содержит ли он изображение программы со всеми инструкциями?И возможно ли каким-то образом (независимо от того, как платформа зависит от хитрости) узнать диапазон адресов моего собственного изображения?Доступна ли память только для чтения?

Короче говоря: могу ли я создать программу, которая печатает сама себя?

Если это невозможно, возникает меньший вопрос, могу ли я напечатать свою собственную?стек?Я думал что-то вроде этого:

const char * BASE;

void print_stack();

int main(int argc, char * argv[]) {
  BASE = &argc;
  /* do stuff */
  print_stack();
  return 0;
}

void print_stack() {
  int sentinel;
  const char * bottom = &sentinel;
  while (bottom < BASE)
    printf("%02X ", *bottom++);
}

Ответы [ 2 ]

1 голос
/ 24 июня 2011

Да, байты кода, обычно называемые в этом контексте программой «текст», являются частью вашего виртуального адресного пространства.

Вы можете определить адрес функции, например, main(), и используйте его для определения одного действительного адреса в диапазоне текстовых страниц. Затем вам придется вызывать API, специфичные для виртуальной памяти, чтобы определить степень отображения по этому адресу.

Совместно используемым библиотекам (.so файлам) будут сопоставлены тексты в смежные области виртуальных машин.

1 голос
/ 24 июня 2011

Чтобы ответить на ваш первый вопрос, конечно, он содержит инструкции вашей программы: вы можете выполнить только то, к чему у вас есть доступ. Чтобы попасть по адресу ваших инструкций, вы можете взять адрес функции и начать печать оттуда. Затем вы можете использовать библиотеку типа udis86 , чтобы разобрать их. Однако обратите внимание, что ваш компилятор не обязан упорядочивать функции каким-либо особым образом, поэтому начиная с main и считывание оттуда не гарантирует получение всего, может растоптать нераспределенную память.

Чтобы получить доступ ко всему диапазону памяти команд (вы ищете сегмент .text), вы можете найти адрес + размер в вашей операционной системе (в Linux эта информация будет в /proc/[pid]/maps, в OS X вы можете использовать vmmap или запросить ядро ​​через ловушку ядра mach_vm_region(), а затем просто прочитать память напрямую. Вы также можете использовать nm, чтобы вывести символы вашей программы, изолировать все, что указывает на сегмент .text (они должны быть помечены T в выводе nm), и вывести их. Это не очень хороший метод, так как вам придется разбирать все, чтобы определить, где они заканчиваются, в случае, если между ними есть отступы.

Доступна вся отображенная память, но не вся она будет доступна для записи (сегмент .text не будет). Следует иметь в виду, что адреса, вероятно, не будут стабильными при вызове, если ваша операционная система реализует ASLR.

Чтобы ответить на второй вопрос, да, вы можете напечатать свой собственный стек и обозначить его с помощью сторонних библиотек, но не так, как вы пытаетесь это сделать. Стек обычно увеличивается на вниз (т. Е. Начинается с высокого адреса и перемещается к младшим адресам. В качестве упражнения для читателя разберите одну из своих функций с помощью gdb или другого дизассемблера и посмотрите, как память в стеке становится выделяется во время пролога функции), поэтому цикл for никогда не будет работать, так как BASE, вероятно, всегда будет больше, чем адрес sentinel.

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