Как посмотреть внутри двоичных файлов из командной строки Linux

Есть тайный файл? Linux file
Команда быстро скажет вам, какой это тип файла. Если это бинарный файл, вы можете узнать о нем еще больше. file
имеет целый ряд стабильных товарищей, которые помогут вам проанализировать его. Мы покажем вам, как использовать некоторые из этих инструментов.
Определение типов файлов
Файлы обычно имеют характеристики, которые позволяют программным пакетам идентифицировать, какой это тип файла, а также какие данные в нем представлены. Не имеет смысла пытаться открыть файл PNG в музыкальном проигрывателе MP3, поэтому полезно и прагматично, если файл содержит некоторую форму идентификатора.
Это может быть несколько байтов подписи в самом начале файла. Это позволяет файлу явно указывать его формат и содержание. Иногда тип файла выводится из отличительного аспекта внутренней организации самих данных, известного как файловая архитектура.
Некоторые операционные системы, такие как Windows, полностью руководствуются расширением файла. Вы можете назвать это доверчивым или доверчивым, но Windows предполагает, что любой файл с расширением DOCX действительно является файлом обработки текста DOCX. Linux не такой, как вы скоро увидите. Он хочет доказательства и заглядывает в файл, чтобы найти его.
Программы для Windows, мобильные приложения, игры - ВСЁ БЕСПЛАТНО, в нашем закрытом телеграмм канале - Подписывайтесь:)
Описанные здесь инструменты уже были установлены в дистрибутивах Manjaro 20, Fedora 21 и Ubuntu 20.04, которые мы использовали для исследования этой статьи. Давайте начнем наше расследование, используя file
команда,
Использование файла Command
У нас есть коллекция файлов разных типов в нашем текущем каталоге. Это смесь документа, исходного кода, исполняемых и текстовых файлов.
ls
Команда покажет нам, что находится в каталоге, и -hl
Опция (удобочитаемые размеры, длинный список) покажет нам размер каждого файла:
ls -hl
Давай попробуем file
на несколько из них и посмотрим, что мы получим:
file build_instructions.odt
file build_instructions.pdf
file COBOL_Report_Apr60.djvu
Три формата файла определены правильно. Где возможно, file
дает нам немного больше информации. Сообщается, что файл PDF находится в формат версии 1.5,
Даже если мы переименуем файл ODT, чтобы иметь расширение с произвольным значением XYZ, файл все равно будет правильно идентифицирован, как в пределах Files
файловый браузер и в командной строке с помощью file
,
В пределах Files
в браузере файлов указан правильный значок. В командной строке file
игнорирует расширение и просматривает файл, чтобы определить его тип:
file build_instructions.xyz
С помощью file
на носителях, таких как графические и музыкальные файлы, обычно выдает информацию относительно их формата, кодировки, разрешения и т. д.
file screenshot.png
file screenshot.jpg
file Pachelbel_Canon_In_D.mp3
Интересно, что даже с простыми текстовыми файлами, file
не судит файл по его расширению. Например, если у вас есть файл с расширением «.c», содержащий стандартный текст, но не исходный код, file
не принимайте это за настоящий C файл исходного кода:
file function+headers.h
file makefile
file hello.c
file
правильно идентифицирует заголовочный файл («.h») как часть коллекции файлов исходного кода на C и знает, что make-файл является скриптом.
Использование файла с двоичными файлами
Двоичные файлы — это больше «черный ящик», чем другие. Можно просматривать файлы изображений, воспроизводить звуковые файлы и открывать файлы документов с помощью соответствующего программного пакета. Двоичные файлы, тем не менее, являются более сложной задачей.
Например, файлы «hello» и «wd» являются двоичными исполняемыми файлами. Это программы. Файл с именем «wd.o» является объектным файлом. Когда исходный код компилируется компилятором, создается один или несколько объектных файлов. Они содержат машинный код, который компьютер в конечном итоге выполнит при запуске готовой программы, а также информацию для компоновщика. Компоновщик проверяет каждый объектный файл для вызовов функций в библиотеках. Он связывает их с любыми библиотеками, которые использует программа. Результатом этого процесса является исполняемый файл.
Файл «watch.exe» представляет собой двоичный исполняемый файл, который был скомпилирован для работы в Windows:
file wd
file wd.o
file hello
file watch.exe
Взять последний первым, file
говорит нам, что файл «watch.exe» представляет собой исполняемую консольную программу PE32 + для семейства процессоров x86 в Microsoft Windows. PE обозначает переносимый исполняемый формат, который имеет 32- и 64-битные версии, PE32 — это 32-разрядная версия, а PE32 + — это 64-разрядная версия.
Все остальные три файла обозначены как Исполняемый и связываемый формат (ELF) файлы. Это стандарт для исполняемых файлов и общих объектных файлов, таких как библиотеки. Мы вскоре рассмотрим формат заголовка ELF.
Что может броситься в глаза, так это то, что два исполняемых файла («wd» и «hello») определены как Linux Standard Base (LSB) совместно используемые объекты, и объектный файл «wd.o» идентифицируется как перемещаемый LSB. Исполняемое слово очевидно при его отсутствии.
Объектные файлы можно перемещать, что означает, что код внутри них может быть загружен в память в любом месте. Исполняемые файлы перечислены в качестве общих объектов, поскольку они были созданы компоновщиком из объектных файлов таким образом, что они наследуют эту возможность.
Это позволяет Рандомизация размещения адресного пространства (ASMR) система для загрузки исполняемых файлов в память по адресам по своему выбору. Стандартные исполняемые файлы имеют адрес загрузки, закодированный в их заголовках, которые определяют, где они загружены в память.
ASMR — это техника безопасности. Загрузка исполняемых файлов в память по предсказуемым адресам делает их уязвимыми для атак. Это связано с тем, что их точки входа и расположение их функций всегда будут известны злоумышленникам. Положение Независимых Исполняемых файлов (PIE), расположенный по случайному адресу, преодолевает эту восприимчивость.
Если мы составить нашу программу с gcc
компилятор и предоставить -no-pie
вариант, мы сгенерируем обычный исполняемый файл.
-o
(выходной файл) опция позволяет нам указать имя для нашего исполняемого файла:
gcc -o hello -no-pie hello.c
Мы будем использовать file
на новый исполняемый файл и посмотреть, что изменилось:
file hello
Размер исполняемого файла такой же, как и раньше (17 КБ):
ls -hl hello
Двоичный файл теперь определяется как стандартный исполняемый файл. Мы делаем это только в демонстрационных целях. Если вы компилируете приложения таким способом, вы потеряете все преимущества ASMR.
Почему исполняемый файл такой большой?
Наш пример hello
Программа занимает 17 КБ, поэтому ее вряд ли можно назвать большой, но при этом все относительно. Исходный код составляет 120 байт:
cat hello.c
Что мешает двоичному файлу, если все, что он делает, это печатает одну строку в окне терминала? Мы знаем, что есть заголовок ELF, но для 64-битного двоичного файла это всего 64 байта. Проще говоря, это должно быть что-то еще:
ls -hl hello
Давайте сканировать двоичный файл с помощью strings
Команда как простой первый шаг, чтобы узнать, что внутри него. Мы доставим это в less
:
strings hello | less
Есть много строк внутри двоичного файла, кроме «Привет, Geek мир!» из нашего исходного кода. Большинство из них являются метками для областей в двоичном файле, а также именами и связующей информацией общих объектов. К ним относятся библиотеки и функции внутри этих библиотек, от которых зависит двоичный файл.
ldd
команда показывает нам зависимости общего объекта двоичного файла:
ldd hello
В выводе есть три записи, и две из них содержат путь к каталогу (первая — нет):
- linux-vdso.so: Виртуальный динамический общий объект (VDSO) это механизм ядра, который позволяет получить доступ к набору подпрограмм пространства ядра через двоичный файл пространства пользователя. Эта избегает накладных расходов на переключение контекста из режима ядра пользователя. Общие объекты VDSO придерживаются формата исполняемого и связанного формата (ELF), что позволяет динамически связывать их с двоичным файлом во время выполнения. VDSO выделяется динамически и использует преимущества ASMR. Возможность VDSO обеспечивается стандартом Библиотека GNU C если ядро поддерживает схему ASMR.
- libc.so.6: Библиотека GNU C общий объект.
- /lib64/ld-linux-x86-64.so.2: Это динамический компоновщик, который двоичный файл хочет использовать. Динамический компоновщик опрашивает двоичный файл, чтобы выяснить, какие у него зависимости, Он запускает эти общие объекты в память. Он готовит двоичный файл к запуску и сможет найти и получить доступ к зависимостям в памяти. Затем он запускает программу.
Заголовок ELF
Мы можем изучить и расшифровать заголовок ELF с использованием readelf
утилита и -h
(заголовок файла) опция:
readelf -h hello
Заголовок интерпретируется для нас.
Первый байт всех двоичных файлов ELF установлен в шестнадцатеричное значение 0x7F. Следующие три байта установлены в 0x45, 0x4C и 0x46. Первый байт — это флаг, который идентифицирует файл как двоичный файл ELF. Чтобы сделать этот кристалл прозрачным, следующие три байта обозначают «ELF» в ASCII:
- Учебный класс: Указывает, является ли двоичный файл 32- или 64-разрядным исполняемым файлом (1 = 32, 2 = 64).
- Данные: Указывает на порядок байт в использовании. Кодировка Endian определяет способ хранения многобайтовых чисел. В кодировании с прямым порядком байтов число сохраняется первым с его старшими значащими битами. При кодировании с прямым порядком байтов число сохраняется первым с его младшими значащими битами.
- Версия: Версия ELF (в настоящее время это 1).
- OS / ABI: Представляет тип двоичный интерфейс приложения в использовании. Это определяет интерфейс между двумя двоичными модулями, такими как программа и разделяемая библиотека.
- Версия ABI: Версия ABI.
- Тип: Тип двоичного файла ELF. Общие значения
ET_REL
для перемещаемого ресурса (такого как объектный файл),ET_EXEC
для исполняемого файла, скомпилированного с-no-pie
флаг иET_DYN
для исполняемого файла с поддержкой ASMR. - Машина: архитектура набора команд, Это указывает целевую платформу, для которой был создан двоичный файл.
- Версия: Всегда установлен на 1, для этой версии ELF.
- Адрес пункта въезда: Адрес памяти в двоичном файле, с которого начинается выполнение.
Другие записи — это размеры и количество областей и разделов в двоичном файле, чтобы можно было рассчитать их местоположение.
Быстрый взгляд на первые восемь байтов двоичного файла с hexdump
покажет сигнатурный байт и строку «ELF» в первых четырех байтах файла. -C
(каноническая) опция дает нам представление ASCII байтов вместе с их шестнадцатеричными значениями, и -n
Параметр (число) позволяет нам указать, сколько байтов мы хотим видеть:
hexdump -C -n 8 hello
objdump и гранулярный вид
Если вы хотите увидеть мельчайшие детали, вы можете использовать objdump
команда с -d
(разбирать) вариант:
objdump -d hello | less
Это разбирает исполняемый машинный код и отображает его в шестнадцатеричных байтах вместе с эквивалентом языка ассемблера. Расположение адреса первого пока в каждой строке показано слева.
Это полезно, только если вы можете читать на ассемблере или вам интересно, что происходит за кулисами. Выводов много, поэтому мы передали less
,
Компиляция и связывание
Есть много способов собрать двоичный файл. Например, разработчик выбирает, включать ли отладочную информацию. Способ, которым двоичный файл связан, также играет роль в его содержании и размере. Если двоичные ссылки совместно используют объекты как внешние зависимости, он будет меньше, чем тот, на который статически связаны зависимости.
Большинство разработчиков уже знают команды, которые мы рассмотрели здесь. Для других, тем не менее, они предлагают несколько простых способов порыться и посмотреть, что находится внутри двоичного черного ящика.
Программы для Windows, мобильные приложения, игры - ВСЁ БЕСПЛАТНО, в нашем закрытом телеграмм канале - Подписывайтесь:)