Видео смотреть бесплатно

Смотреть 3d видео

Официальный сайт медиатэк 24/7/365

Смотреть видео бесплатно

15.09.10 18:54 dementiy

CodingПривет из свободного от libc мира. Часть 2

Буквально вчера наткнулся на статью под названием "Привет из свободного от libc мира. Часть 1". Автором этой статьи была девушка и, наверное, именно этот факт заставил меня посетить ее блог, где я нашел продолжение первой части (и другие интересные посты, о которых скажу в конце), которое и хотел бы представить здесь. Перед тем, как прочитать вторую часть, прочитайте первую, ее перевод есть на хабре. Итак...

В предыдущем посте мы справились с компиляцией, написав маленькую программу, которая может быть собрана без использования libc. Понимание объектного кода и деталей структуры ELF исполняемых файлов — следующий шаг в нашем приключении.
Мы остановились на следующем коде:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
jesstess@kid-charlemagne:~$ cat stubstart.S
.globl _start
_start:
        call main
        movl $1, %eax
        xorl %ebx, %ebx
        int $0x80
jesstess@kid-charlemagne:~$ cat hello.c
int main()
{
  char *str = "Hello World";
  return 0;
}
jesstess@kid-charlemagne:~/c$ gcc -nostdlib stubstart.S hello.c -o hello

И что мы получили, проделав всю эту работу?

1
2
3
4
jesstess@kid-charlemagne:~/c$ wc -c hello
1373 hello
jesstess@kid-charlemagne:~$ objdump -D hello | wc -l
93

Теперь наш исполняемый файл занимает немногим более 1300 байт и менее 100 линий, это довольно разумное количество для анализа. Маленькая часть ассемблерного кода не может испугать нас к этому моменту, давайте теперь взглянем на него с использованием objdump -D, который покажет содержимое всех секций (вывод здесь). Если вывод кажется пугающим, то быстро пробегите по нему, я обещаю вам, что к концу этого поста от страха ничего не останется.
Итак, мы имеем 5 секций: .text, которая содержит знакомые символы _start и main, .rodata, .eh_frame_hdr, .eh_frame и .comment.

Шаг 1: Собрались – что же такое «секция»?

Если мы стряхнем пыль с нашей любимой копии документа Tool Interface Standard ELF Specification и заглянем в нее, то она скажет нам:
У исполняемого файла, как результата нашей компиляции, есть два представления: он содержит program header, описывающий сегменты, которые содержат информацию, используемую во время выполнения, и section header, описывающий секции, которые содержат информацию для линковки и перемещения. Мы можем получить информацию о сегментах или секциях с помощью readelf -l или readelf -S соответственно (вывод здесь). Результат работы этих двух команд для нашей программы сведен в рисунок 1.

Рис.1. Сегменты и секции нашего ELF

Шаг 2: Что происходит в нашей секции?

Спецификация также сообщает нам, что и в каком порядке располагается в нашем исполняемом файле:
  • .text — содержит исполняемые инструкции программы (попросту код программы);

  • .rodata — содержит константные значения, это сегмент «только для чтения»;

  • .eh_frame — информация, необходимая для frame-unwinding во время обработки исключений;

  • .eh_frame_hdr — цитата из Linux Standard Base Specification: "Эта секция содержит указатель на секцию .eh_frame, которая доступна для «runtime support code» С++ приложения. Эта секция также может содержать двоичную таблицу поиска, которая может быть использована «runtime support code» для более эффективного доступа к записям секции .eh_frame".
    Мы не должны волноваться об исключениях в нашем примере, поэтому .eh_frame и .eh_frame_hdr не должны нас беспокоить и компилирование с флагом -fno-asynchronous-unwind-tables подавит создание этих двух секций;

  • .comment — информация о версии компилятора.

Говоря о избавлении от секций: для нас, приверженцев минимализма, strip(1) хороший друг. Мы можем воспользоваться --remove-section по несущественным секциям, таким как .comment, чтобы полностью от них избавиться; file(1) сообщит нам, если исполняемая программа была укорочена (удалены некоторые из секций).

Мы не видим другие секции в нашем примере (они не были включены в исполняемый файл), потому что они были пустыми:
  • .data — в этой секции инициализируются глобальные и локальные переменные;

  • .bss — секция неинициализированных глобальных и локальных переменных, то есть заполненных нулями.

На этом все о секциях. Теперь мы знаем, что символы, такие как _start и main, находятся в этих секциях, но есть ли в программе еще символы?

Шаг 3: Понимание символов и почему они расположены там, где расположены.

Мы можем получить информацию о символах нашего исполняемого файла с помощью objdump -t:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
jesstess@kid-charlemagne:~/c$ objdump -t hello

hello:     file format elf64-x86-64

SYMBOL TABLE:
00000000004000e8 l    d  .text                   0000000000000000 .text
0000000000400107 l    d  .rodata                 0000000000000000 .rodata
0000000000400114 l    d  .eh_frame_hdr           0000000000000000 .eh_frame_hdr
0000000000400128 l    d  .eh_frame               0000000000000000 .eh_frame
0000000000000000 l    d  .comment                0000000000000000 .comment
0000000000000000 l    df *ABS*                   0000000000000000 hello.c
00000000004000e8 g       .text                   0000000000000000 _start
0000000000600fe8 g       *ABS*                   0000000000000000 __bss_start
00000000004000f4 g     F .text                   0000000000000013 main
0000000000600fe8 g       *ABS*                   0000000000000000 _edata
0000000000600fe8 g       *ABS*                   0000000000000000 _end


Таблица символов для нашего исполняемого файла имеет 11 записей. Странным образом только редкие версии документации (man pages) по objdump, как эта, объясняет таблицу символов столбец за столбцом. Таблица поделена следующим образом:

Столбец 1: значение/адрес символа.
Столбец 2: набор символов и пробелов представляющих флаговые биты, установленные для символа. Есть 7 групп, три из которых представлены в этой таблице символов. Значение из первой группы может быть — l, g, <пробел> или !, если символ локальный, глобальный, ни то ни другое или оба сразу, соответственно. Значение из шестой группы может быть — d, D или <пробел> для debugging, dynamic или normal соответственно. Значение из седьмой может быть — F, f, O или <пробел> для функции, файла, объекта или нормального символа, соответственно. Описание оставшихся 4 групп может быть найдено в unusally comprehensive objdump manpage.
Столбец 3: в какой секции расположен символ. *ABS* (абсолютный) означает символ, не связанный с определенной секцией.
Столбец 4: размер/выравнивание символа.
Столбец 5: имя символа.

Все наши 5 секций имеют связи с local (l), debugging (d) и symbols (s). main действительно функция (F), hello.c действительно файл (f), и он не связан с какой-либо секцией (*ABS*). _start и main — часть исполняемых инструкций для нашей программы и, таким образом, расположены в секции .text, как мы и предполагали. Единственной причудой здесь является __bss_start, _edata и _end, все *ABS*, глобальные символы, которые мы не писали в нашей программе. Откуда они взялись?
Виновником на этот раз является скрипт компоновщика. gcc неявно вызывает ld, как часть процесса компиляции. ld --verbose предоставит сценарий компоновщика, который был использован и, глядя на него (вывод здесь), мы видим, что _edata определен как конец секции .data, __bss_start и _end отмечают начало и конец секции .bss. Эти символы могли быть использованы механизмом управления памятью (например, если sbrk хочет знать, где начинается «куча») и сборщиком «мусора»».
Следует отметить, что str, наша инициализированная локальная переменная, не представлена в таблице символов. Почему? Потому что она размещается в стеке (возможно в регистре) во время выполнения. Однако, что-то связанное с str находиться в секции .rodata, несмотря на то, что мы не видим это в таблице символов...

Теперь о char *str = «Hello World»; на самом деле мы создаем два различных объекта. Первый это строковый литерал «Hello World», который представляет собой просто массив символов и имеет некоторый адрес, но явного имени не имеет. Это массив «только для чтения» и расположен в секции .rodata. Второй — локальная переменная str, которая имеет тип «pointer to char». Она и располагается в стеке, а ее начальное значение — адрес строкового литерала, который был создан.
Мы можем доказать это и получить некоторую другую полезную информацию, смотря на содержимое наших секций, используя декодирование строк:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
jesstess@kid-charlemagne:~$ objdump -s hello

hello:     file format elf64-x86-64

Contents of section .text:
 4000e8 e80b0000 00b80100 000031db cd809090  ..........1.....
 4000f8 554889e5 48c745f8 0b014000 b8000000  UH..H.E...@.....
 400108 00c9c3                               ...
Contents of section .rodata:
 40010b 48656c6c 6f20576f 726c6400           Hello World.
Contents of section .eh_frame_hdr:
 400118 011b033b 14000000 01000000 e0ffffff  ...;............
 400128 30000000                             0...
Contents of section .eh_frame:
 400130 14000000 00000000 017a5200 01781001  .........zR..x..
 400140 030c0708 90010000 1c000000 1c000000  ................
 400150 f8004000 13000000 00410e10 8602430d  ..@......A....C.
 400160 06000000 00000000                    ........
Contents of section .comment:
 0000 00474343 3a202855 62756e74 7520342e  .GCC: (Ubuntu 4.
 0010 332e332d 35756275 6e747534 2920342e  3.3-5ubuntu4) 4.
 0020 332e3300                             3.3.


Вуаля! Наша строка «Hello World» расположена в .rodata и наша секция .comment объяснена: она просто содержит строку с версией gcc, используемой для компиляции программы.

Шаг 4: Исключим лишнее и соединим все вместе

Исполняемый файл содержит 5 секций: .text, .rodata, .eh_frame_hdr, .eh_frame и .comment. В действительности только одна из них (.text) содержит код, определяющий, что делает эта маленькая программа. В этом можно убедиться, используя objdump -d (дизассемблирует только те секции, которые должны содержать инструкции) вместо objdump -D (дизассемблирует содержимое всех секций, не только содержащих инструкции), использованного в начале поста, отмечая, что выведено только содержимое .text (используя objudump -d).
.rodata в действительности содержит только строку «Hello World», а .comment содержит только версию gcc. «Инструкции» для этих секций, показанные в выводе objdump -D получаются в следствии ошибочного представления objdump ASCII символов как инструкций и попытке дизассемблировать их. Мы можем конвертировать первую пару чисел из секции .comment в ASCII символы, чтобы доказать это. На Python:

1
2
>>> "".join(chr(int(x, 16)) for x in "47 43 43 3a 20 28 55 62 75 6e 74 75".split())
'GCC: (Ubuntu'


В секции .text, _start вызывает main, и в main в стек вталкивается указатель на адрес в памяти, где хранится «Hello World», 0x40010b (где начинается секция .rodata, как видно из вывода objdump -D). Затем мы возвращаемся из main в _start, который заботится о выходе из программы, как описано в Части 1.

И это все! Все секции и символы учтены. Никакого волшебства (я имею ввиду волшебство в хорошем смысле Я-бы-прошел-это-испытание, а не в смысле прости-Jimmy-Santa-не-настоящий). Вот так. Whew.

Ссылки:
  • Первая часть;

  • Перевод первой части;

  • Оригинал второй части;

  • pdf этого перевода.

P.S. В блоге у этой милой девушки есть еще две статьи, которые меня заинтересовали, эта и эта. Возможно кто-то захочит оформить перевод (я могу помочь), если кто-то согласиться напишите в комментарии.
Если заметите ошибки по переводу, то пишите в ЛС.


Теги:

settler 15.09.10 19:25 # +0
уже есть специальная тема, где можно оставлять статьи для перевода - http://welinux.ru/post/4105/
mealsforall 15.09.10 20:10 # +0
Блин, это же товарищи из ksplice. Они там гениальные.
dementiy 16.09.10 00:27 # +0
Гениальные или нет, но вот резюме автора статьи впечатляет, один только MIT (Massachusetts Institute of Technology) уже внушает уважение.
hate 17.09.10 11:56 # +0
фейсбук и фоточка: http://www.facebook.com/jesstess
Minoru 16.09.10 00:02 # +0
Спасибо!
dementiy 16.09.10 00:24 # +0
Рад стараться =)
hate 16.09.10 12:28 # +1
file(1) сообщит нам, если исполняемая программа была разделена.


лолшто? "очищена", "укорочена", "оголена", сленговое "пострипанна", но ни как не "разделена"
dementiy 16.09.10 12:56 # +-1
Мне кажется, что тут одним словом не сказать. Ведь имеется ввиду, что из программы были удалены некоторы секции, поэтму сказать "очищена" или "оголена" нельзя, а вот со словом "уорочена" соглашусь. Спасибо.
На всякий случай для остальных приведу пример удаления секции .comment из программы hello:
$ file hello
hello: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped
$ strip -R=comment
$ file hello
hello: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, stripped

Посты Комментарии
Последние посты
    Посты Комментарии
    Последние комментарии
      Посты Комментарии
      Изменения
        Посты Комментарии Изменения Черновики Избранное
        Черновики (все)
          Посты Комментарии Изменения Черновики Избранное
          Избранное (всё)
            Посты Комментарии Изменения Черновики Избранное
            Лучшие блоги (все 140)
            Топ пользователей Топ блогов
            Топ пользователей Топ блогов
            Элита (все 2734 из 212 городов)
            Топ пользователей Топ блогов
            welinux.ru

            Смотреть онлайн бесплатно

            Онлайн видео бесплатно


            Смотреть русское с разговорами видео

            Online video HD

            Видео скачать на телефон

            Русские фильмы бесплатно

            Full HD video online

            Смотреть видео онлайн

            Смотреть HD видео бесплатно

            School смотреть онлайн