Бинарные файлы различны что это значит

Обновлено: 06.07.2024

Т екстовые файлы хранят данные в виде текста (sic!). Это значит, что если, например, мы записываем целое число 12345678 в файл, то записывается 8 символов, а это 8 байт данных, несмотря на то, что число помещается в целый тип. Кроме того, вывод и ввод данных является форматированным, то есть каждый раз, когда мы считываем число из файла или записываем в файл происходит трансформация числа в строку или обратно. Это затратные операции, которых можно избежать.

Текстовые файлы позволяют хранить информацию в виде, понятном для человека. Можно, однако, хранить данные непосредственно в бинарном виде. Для этих целей используются бинарные файлы.

Выполните программу и посмотрите содержимое файла output.bin. Число, которое ввёл пользователь записывается в файл непосредственно в бинарном виде. Можете открыть файл в любом редакторе, поддерживающем представление в шестнадцатеричном виде (Total Commander, Far) и убедиться в этом.

Запись в файл осуществляется с помощью функции

Функция возвращает число удачно записанных элементов. В качестве аргументов принимает указатель на массив, размер одного элемента, число элементов и указатель на файловый поток. Вместо массив, конечно, может быть передан любой объект.

Запись в бинарный файл объекта похожа на его отображение: берутся данные из оперативной памяти и пишутся как есть. Для считывания используется функция fread

Функция возвращает число удачно прочитанных элементов, которые помещаются по адресу ptr. Всего считывается count элементов по size байт. Давайте теперь считаем наше число обратно в переменную.

fseek

Одной из важных функций для работы с бинарными файлами является функция fseek

Эта функция устанавливает указатель позиции, ассоциированный с потоком, на новое положение. Индикатор позиции указывает, на каком месте в файле мы остановились. Когда мы открываем файл, позиция равна 0. Каждый раз, записывая байт данных, указатель позиции сдвигается на единицу вперёд.
fseek принимает в качестве аргументов указатель на поток и сдвиг в offset байт относительно origin. origin может принимать три значения

  • SEEK_SET - начало файла
  • SEEK_CUR - текущее положение файла
  • SEEK_END - конец файла. К сожалению, стандартом не определено, что такое конец файла, поэтому полагаться на эту функцию нельзя.

В случае удачной работы функция возвращает 0.

Дополним наш старый пример: запишем число, затем сдвинемся указатель на начало файла и прочитаем его.

Вместо этого можно также использовать функцию rewind, которая перемещает индикатор позиции в начало.

В си определён специальный тип fpos_t, который используется для хранения позиции индикатора позиции в файле.
Функция

используется для того, чтобы назначить переменной pos текущее положение. Функция

используется для перевода указателя в позицию, которая хранится в переменной pos. Обе функции в случае удачного завершения возвращают ноль.

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

Рассмотрим пример: пользователь вводит числа. Первые 4 байта файла: целое, которое обозначает, сколько чисел было введено. После того, как пользователь прекращает вводить числа, мы перемещаемся в начало файла и записываем туда число введённых элементов.

Вторая программа сначала считывает количество записанных чисел, а потом считывает и выводит числа по порядку.

Примеры

1. Имеется бинарный файл размером 10*sizeof(int) байт. Пользователь вводит номер ячейки, после чего в неё записывает число. После каждой операции выводятся все числа. Сначала пытаемся открыть файл в режиме чтения и записи. Если это не удаётся, то пробуем создать файл, если удаётся создать файл, то повторяем попытку открыть файл для чтения и записи.

2. Пишем слова в бинарный файл. Формат такой - сначало число букв, потом само слово без нулевого символа. Ели длина слова равна нулю, то больше слов нет. Сначала запрашиваем слова у пользователя, потом считываем обратно.

3. Задача - считать данные из текстового файла и записать их в бинарный. Для решения зачи создадим функцию обёртку. Она будет принимать имя файла, режим доступа, функцию, которую необходимо выполнить, если файл был удачно открыт и аргументы этой функции. Так как аргументов может быть много и они могут быть разного типа, то их можно передавать в качестве указателя на структуру. После выполнения функции файл закрывается. Таким образом, нет необходимости думать об освобождении ресурсов.

4. Функция saveInt32Array позволяет сохранить массив типа int32_t в файл. Обратная ей loadInt32Array считывает массив обратно. Функция loadInt32Array сначала инициализирует переданный ей массив, поэтому мы должны передавать указатель на указатель; кроме того, она записывает считанный размер массива в переданный параметр size, из-за чего он передаётся как указатель.

5. Создание таблицы поиска. Для ускорения работы программы вместо вычисления функции можно произвести сначала вычисление значений функции на интервале с определённой точностью, после чего брать значения уже из таблицы. Программа сначала производит табулирование функции с заданными параметрами и сохраняет его в файл, затем подгружает предвычисленный массив, который уже используется для определения значений. В этой программе все функции возвращают переменную типа Result, которая хранит номер ошибки. Если функция отработала без проблем, то она возвращает Ok (0).

6. У нас имеются две структуры. Первая PersonKey хранит логин, пароль, id пользователя и поле offset. Вторая структура PersonInfo хранит имя и фамилию пользователя и его возраст. Первые структуры записываются в бинарный файл keys.bin, вторые структуры в бинарный файл values.bin. Поле offset определяет положение соответствующей информации о пользователе во втором файле. Таким образом, получив PersonKey из первого файла, по полю offset можно извлечь из второго файла связанную с данным ключом информацию.

Зачем так делать? Это выгодно в том случае, если структура PersonInfo имеет большой размер. Извлекать массив маленьких структур из файла не накладно, а когда нам понадобится большая структура, её можно извлечь по уже известному адресу в файле.

email

Всё ещё не понятно? – пиши вопросы на ящик


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

Некоторые из них компилятор включает или отключает по умолчанию. Так в бинарных файлах могут возникать уязвимости, о которых мы не знаем.

Checksec — это простая утилита, позволяющая определить, какие свойства были включены при компиляции. В этой статье я расскажу:

  • как использовать утилиту checksec для поиска уязвимостей;
  • как использовать компилятор gcc для устранения найденных уязвимостей.

Установка checksec

Для Fedora OS и других систем на базе RPM:


Для систем на базе Debian используйте apt.

Быстрый старт с checksec

Утилита сhecksec состоит из единственного скриптового файла, который, впрочем, довольно большой. Благодаря такой прозрачности вы можете узнать, какие системные команды для поиска уязвимостей в бинарных файлах выполняются под капотом:

Давайте запустим checksec для утилиты просмотра содержимого каталогов (ls):


Выполнив команду в терминале, вы получите отчёт о том, какими полезными свойствами обладает этот бинарник, и какими не обладает.

Первая строка — это шапка таблицы, в которой перечислены различные свойства безопасности — RELRO, STACK CANARY, NX и так далее. Вторая строка показывает значения этих свойств для бинарного файла утилиты ls.

Hello, бинарник!

Я скомпилирую бинарный файл из простейшего кода на языке С:


Обратите внимание, что пока я не передал компилятору ни одного флага, за исключением -o (он не относится к делу, а просто говорит, куда выводить результат компиляции):


Теперь запущу утилиту checksec для моего бинарника. Некоторые свойства отличаются от свойств


Checksec позволяет использовать различные форматы вывода, которые вы можете указать с помощью опции --output. Я выберу формат JSON и сделаю вывод более наглядным с помощью утилиты jq:

Анализ (checksec) и устранение (gcc) уязвимостей

Созданный выше бинарный файл имеет несколько свойств, определяющих, скажем так, степень его уязвимости. Я сравню свойства этого файла со свойствами бинарника ls (тоже указаны выше) и объясню, как это сделать с помощью утилиты checksec.

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

1. Отладочные символы

Начну с простого. Во время компиляции в бинарный файл включаются определённые символы. Эти символы используются при разработке программного обеспечения: они нужны для отладки и исправления ошибок.

Отладочные символы обычно удаляются из той версии бинарника, которую разработчики выпускают для широкого использования. Это никак не влияет на работу программы. Такая очистка (она обозначена словом strip) часто выполняется для экономии места, так как файл становится легче после удаления символов. А в проприетарном программном обеспечении эти символы часто удаляют ещё и потому, что у злоумышленников есть возможность прочитать их в двоичном формате и использовать в своих целях.

Сhecksec показывает, что отладочные символы присутствуют в моём бинарнике, но их нет в файле ls.

То же самое может показать запуск команды file. Символы не удалены (not stripped).

Как работает checksec

Запустим эту команду с опцией --debug:


Так как утилита checksec — это один длинный скрипт, то для его изучения можно использовать функции Bash. Выведем команды, которые запускает скрипт для моего файла hello:


Утилита checksec использует для чтения двоичного файла команду readelf со специальным флагом --symbols. Она выводит все отладочные символы, которые содержатся в бинарнике.


Из содержимого раздела .symtab можно узнать количество найденных символов:

Как удалить отладочные символы после компиляции

В этом нам поможет утилита strip.

Как удалить отладочные символы во время компиляции

При компиляции используйте флаг -s:


Убедиться, что символы удалены, можно и с помощью утилиты checksec:

2. Canary

Canary (осведомители) — это «секретные» значения, которые хранятся в стеке между буфером и управляющими данными. Они используются для защиты от атаки переполнения буфера: если эти значения оказываются изменены, то стоит бить тревогу. Когда приложение запускается, для него создаётся свой стек. В данном случае это просто структура данных с операциями push и pop. Злоумышленник может подготовить вредоносные данные и записать их в стек. В этом случае буфер может быть переполнен, а стек повреждён. В дальнейшем это приведёт к сбою работы программы. Анализ значений canary позволяет быстро понять, что произошёл взлом и принять меры.

Включаем canary

Для этого при компиляции используем флаг -stack-protector-all:


Вот теперь сhecksec может с чистой совестью сообщить нам, что механизм canary включён:

3. PIE

Включённое свойство PIE позволяет произвольно размещать в памяти исполняемый код независимо от его абсолютного адреса:

PIE (Position Independent Executable) — исполняемый позиционно-независимый код. Возможность предсказать, где и какие области памяти находятся в адресном пространстве процесса играет на руку взломщикам. Пользовательские программы загружаются и выполняются с предопределённого адреса виртуальной памяти процесса, если они не скомпилированы с опцией PIE. Использование PIE позволяет операционной системе загружать секции исполняемого кода в произвольные участки памяти, что существенно усложняет взлом.


Часто свойство PIE включают только при компиляции библиотек. В выводе ниже hello помечен как LSB executable, а файл стандартной библиотеки libc (.so) — как LSB shared object:


Checksec получает эту информацию следующим образом:


Если вы запустите эту же команду для библиотеки, то вместо EXEC увидите DYN:

Включаем PIE

При компиляции программы нужно указать следующие флаги:


Чтобы убедиться, что свойство PIE включено, выполним такую команду:


Теперь у нашего бинарного файла (hello) тип сменится с EXEC на DYN:

Средства операционной системы и процессора позволяют гибко настраивать права доступа к страницам виртуальной памяти. Включив свойство NX (No Execute), мы можем запретить воспринимать данные в качестве инструкций процессора. Часто при атаках переполнения буфера злоумышленники помещают код в стек, а затем пытаются его выполнить. Однако, если запретить выполнение кода в этих сегментах памяти, можно предотвратить такие атаки. При обычной компиляции с использованием gcc это свойство включено по умолчанию:


Чтобы получить информацию о свойстве NX, checksec вновь использует команду readelf. В данном случае RW означает, что стек доступен для чтения и записи. Но так как в этой комбинации отсутствует символ E, на выполнение кода из этого стека стоит запрет:

Отключение NX

Отключать свойство NX не рекомендуется, но сделать это можно так:


После компиляции мы увидим, что права доступа к стеку изменились на RWE:

5. RELRO

В динамически слинкованных бинарниках для вызова функций из библиотек используется специальная таблица GOT (Global Offset Table). К этой таблице обращаются бинарные файлы формата ELF (Executable Linkable Format). Когда защита RELRO (Relocation Read-Only) включена, таблица GOT становится доступной только для чтения. Это позволяет защититься от некоторых видов атак, изменяющих записи таблицы:


В данном случае включено только одно из свойств RELRO, поэтому checksec выводит значение «partial». Для отображения настроек сhecksec использует команду readelf.

Включаем полную защиту (FULL RELRO)

Для этого при компиляции нужно использовать соответствующие флаги:


Всё, теперь наш бинарник получил почётное звание FULL RELRO:

Другие возможности checksec

Тему безопасности можно изучать бесконечно. Даже, рассказывая про простую утилиту checksec в этой статье, я не смогу охватить всё. Тем не менее, упомяну ещё несколько интересных возможностей.

Проверка нескольких файлов

Нет необходимости для каждого файла запускать отдельную команду. Можно запустить одну команду сразу для нескольких бинарных файлов:

Проверка процессов

Утилита checksec также позволяет анализировать безопасность процессов. Следующая команда отображает свойства всех запущенных программ в вашей системе (для этого нужно использовать опцию --proc-all):


Вы также можете выбрать для проверки один процесс, указав его имя:

Проверка ядра

Аналогично вы можете анализировать уязвимости в ядре вашей системы.

Предупреждён — значит вооружён

Подробно изучите свойства безопасности и попытайтесь понять, на что именно влияет каждое из них и какие виды атак это позволяет предотвратить. Checksec вам в помощь!


Облачные серверы от Маклауд быстрые и безопасные.

Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Т екстовые файлы хранят данные в виде текста (sic!). Это значит, что если, например, мы записываем целое число 12345678 в файл, то записывается 8 символов, а это 8 байт данных, несмотря на то, что число помещается в целый тип. Кроме того, вывод и ввод данных является форматированным, то есть каждый раз, когда мы считываем число из файла или записываем в файл происходит трансформация числа в строку или обратно. Это затратные операции, которых можно избежать.

Текстовые файлы позволяют хранить информацию в виде, понятном для человека. Можно, однако, хранить данные непосредственно в бинарном виде. Для этих целей используются бинарные файлы.

Выполните программу и посмотрите содержимое файла output.bin. Число, которое ввёл пользователь записывается в файл непосредственно в бинарном виде. Можете открыть файл в любом редакторе, поддерживающем представление в шестнадцатеричном виде (Total Commander, Far) и убедиться в этом.

Запись в файл осуществляется с помощью функции

Функция возвращает число удачно записанных элементов. В качестве аргументов принимает указатель на массив, размер одного элемента, число элементов и указатель на файловый поток. Вместо массив, конечно, может быть передан любой объект.

Запись в бинарный файл объекта похожа на его отображение: берутся данные из оперативной памяти и пишутся как есть. Для считывания используется функция fread

Функция возвращает число удачно прочитанных элементов, которые помещаются по адресу ptr. Всего считывается count элементов по size байт. Давайте теперь считаем наше число обратно в переменную.

fseek

Одной из важных функций для работы с бинарными файлами является функция fseek

Эта функция устанавливает указатель позиции, ассоциированный с потоком, на новое положение. Индикатор позиции указывает, на каком месте в файле мы остановились. Когда мы открываем файл, позиция равна 0. Каждый раз, записывая байт данных, указатель позиции сдвигается на единицу вперёд.
fseek принимает в качестве аргументов указатель на поток и сдвиг в offset байт относительно origin. origin может принимать три значения

  • SEEK_SET - начало файла
  • SEEK_CUR - текущее положение файла
  • SEEK_END - конец файла. К сожалению, стандартом не определено, что такое конец файла, поэтому полагаться на эту функцию нельзя.

В случае удачной работы функция возвращает 0.

Дополним наш старый пример: запишем число, затем сдвинемся указатель на начало файла и прочитаем его.

Вместо этого можно также использовать функцию rewind, которая перемещает индикатор позиции в начало.

В си определён специальный тип fpos_t, который используется для хранения позиции индикатора позиции в файле.
Функция

используется для того, чтобы назначить переменной pos текущее положение. Функция

используется для перевода указателя в позицию, которая хранится в переменной pos. Обе функции в случае удачного завершения возвращают ноль.

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

Рассмотрим пример: пользователь вводит числа. Первые 4 байта файла: целое, которое обозначает, сколько чисел было введено. После того, как пользователь прекращает вводить числа, мы перемещаемся в начало файла и записываем туда число введённых элементов.

Вторая программа сначала считывает количество записанных чисел, а потом считывает и выводит числа по порядку.

Примеры

1. Имеется бинарный файл размером 10*sizeof(int) байт. Пользователь вводит номер ячейки, после чего в неё записывает число. После каждой операции выводятся все числа. Сначала пытаемся открыть файл в режиме чтения и записи. Если это не удаётся, то пробуем создать файл, если удаётся создать файл, то повторяем попытку открыть файл для чтения и записи.

2. Пишем слова в бинарный файл. Формат такой - сначало число букв, потом само слово без нулевого символа. Ели длина слова равна нулю, то больше слов нет. Сначала запрашиваем слова у пользователя, потом считываем обратно.

3. Задача - считать данные из текстового файла и записать их в бинарный. Для решения зачи создадим функцию обёртку. Она будет принимать имя файла, режим доступа, функцию, которую необходимо выполнить, если файл был удачно открыт и аргументы этой функции. Так как аргументов может быть много и они могут быть разного типа, то их можно передавать в качестве указателя на структуру. После выполнения функции файл закрывается. Таким образом, нет необходимости думать об освобождении ресурсов.

4. Функция saveInt32Array позволяет сохранить массив типа int32_t в файл. Обратная ей loadInt32Array считывает массив обратно. Функция loadInt32Array сначала инициализирует переданный ей массив, поэтому мы должны передавать указатель на указатель; кроме того, она записывает считанный размер массива в переданный параметр size, из-за чего он передаётся как указатель.

5. Создание таблицы поиска. Для ускорения работы программы вместо вычисления функции можно произвести сначала вычисление значений функции на интервале с определённой точностью, после чего брать значения уже из таблицы. Программа сначала производит табулирование функции с заданными параметрами и сохраняет его в файл, затем подгружает предвычисленный массив, который уже используется для определения значений. В этой программе все функции возвращают переменную типа Result, которая хранит номер ошибки. Если функция отработала без проблем, то она возвращает Ok (0).

6. У нас имеются две структуры. Первая PersonKey хранит логин, пароль, id пользователя и поле offset. Вторая структура PersonInfo хранит имя и фамилию пользователя и его возраст. Первые структуры записываются в бинарный файл keys.bin, вторые структуры в бинарный файл values.bin. Поле offset определяет положение соответствующей информации о пользователе во втором файле. Таким образом, получив PersonKey из первого файла, по полю offset можно извлечь из второго файла связанную с данным ключом информацию.

Зачем так делать? Это выгодно в том случае, если структура PersonInfo имеет большой размер. Извлекать массив маленьких структур из файла не накладно, а когда нам понадобится большая структура, её можно извлечь по уже известному адресу в файле.

email

Всё ещё не понятно? – пиши вопросы на ящик

Во время работы с Linux у вас есть возможность на выбор использовать два совсем разных способа установки программ, а именно:

Выбрать нужный необходимо отталкиваясь от ваших потребностей и требований к системе, ну и конечно от наличия навыков и опыта в развертывании ПО. Рассмотрим отдельно каждый из методов, их плюсы, а также минусы и трудности, которые могут встретиться при установке.

Бинарный файл

Бинарный файл - это фактическая программа, которая уже полностью готовая к использованию. Это исполняемый файл, который создается при компиляции из исходного кода. Как правило, они имеют все необходимые библиотеки, встроенные в них, или устанавливают / разворачивают их по мере необходимости (в зависимости от того, как было написано ПО). В большинстве случаев предоставляются в архивном формате.

Для установки требуется специальная программа для распаковки этих файлов и помещения их на компьютер. То есть менеджер пакетов вашего дистрибутива Linux (например, apt, yum и т. д.). Менеджер пакетов также выполняет и другие полезные функции кроме распаковки, такие как отслеживание установленных файлов и управление обновлениями программного обеспечения.

Преимущества и плюсы использования бинарных файлов

  • Файл сразу готов к запуску. Если у вас есть бинарный файл, разработанный для вашего процессора и операционной системы, скорее всего, вы сможете запустить программу, и все будет работать как надо уже с первого раза.
  • Выполнение меньшего количества конфигураций. Вам не нужно настраивать целую кучу параметров конфигурации, чтобы использовать программу, файл просто будет использовать общую конфигурацию по умолчанию.
  • Если что-то пойдет не так, и случится ошибка, будет проще найти помощь в Интернете, поскольку бинарный файл предварительно скомпилирован, и логично, что другие люди могут его уже использовали, а это значит, что вы используете аналогичную программу, как и у других пользователей, а не уникальную, оптимизированную для вашей системы, поэтому можно будет найти советы о том как решить полученные ошибки или получить информацию, что следует делать дальше.

Недостатки и минусы использования

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

Исходные файлы

Исходные файлы - файлы для “сборки” утилиты/ПО в бинарный файл. Исходный код программного обеспечения для Linux поставляется в виде сжатых tar-файлов, которые обычно имеют расширения .tar.gz или .tar.bz2. Инструменты используются для упаковки исходного кода в tarballs, где «tar» (используется для объединения нескольких файлов в один), «gzip» или bzip2 (используется для сжатия).

Чтобы получить tar-архив с исходным кодом для определенного программного обеспечения, вам нужно знать URL-адрес к tar-архиву. После чего нужно распаковать скачанный tar-архив специальной командой tar для определенного типа расширения архива, чтобы получить доступ к файлам и возможность работать с исходником. Следующим шагом выполняются нужные настройки среды для компиляции и установки программного обеспечения из исходного кода.

Исходные файлы, написанные на разных языках, и нуждаются в специальных компиляторах и командах для преобразования его в исполняемый бинарный файл, который будет читаемым для системы и затем сможет запустить ваш компьютер.

Специальный набор инструментов помогает автоматизировать этот процесс. На десктопах Linux это обычно происходит в форме программы командной строки под названием make. Выше перечислены стандартные этапы, при выполнении каких возможно могут появляться ошибки, и будет необходимо выполнять дополнительные манипуляции, в этом и есть сложность внедрения проектов через исходные файлы.

Касательно вопроса, где можно найти исходный код к продукту, вариантов много, в большинстве случаев Вы можете загрузить исходный код проекта с таких сервисов, как GitHub или BitBucket. Некоторые владельцы ПО могут даже разместить его на личном веб-сайте.

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

Преимущества и плюсы использования исходных файлов

  • Дает гибкость в конфигурации программного обеспечения под себя, нужды и требования конкретной системы.
  • Хороший вариант для приобретения практических навыков и получения информации о работе и понимания приложения в системе в целом.

Недостатки и минусы использования

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

К минусам этот пункт можно и не относить, но для установки ПО с исходника потребуется уже наличие теоретических знаний и необходимых навыков в понимании документации к продукту, работы с терминалом и т.д., тут обычному пользователю может быть сложно.

Оба метода хороши и несут в себе разные цели использования. В большинстве случаев достаточно выбрать стандартный метод с помощью бинарных файлов.

Читайте также: