• Курсы Академии Кодебай, стартующие в мае - июне, от команды The Codeby

    1. Цифровая криминалистика и реагирование на инциденты
    2. ОС Linux (DFIR) Старт: 16 мая
    3. Анализ фишинговых атак Старт: 16 мая Устройства для тестирования на проникновение Старт: 16 мая

    Скидки до 10%

    Полный список ближайших курсов ...

Статья Диски GPT и MBR – разбор полётов

Под капотом..

1. Разметка CHS и LBA;
2. Формат таблицы разделов MBR;
3. Формат таблицы разделов GPT;
4. Практика – сбор и вывод информации;
5. Заключение.
-----------------------------------------------------


1. CHS и LBA – общие сведения

Ещё в 90-х годах прошлого столетия стало очевидно, что исправно служившая столько лет трёхмерная геометрия жёстких дисков CHS (Cylinder, Head, Sector) не удовлетворяет уже современным требованиям и её срочно нужно менять. Так, следующим шагом к совершенству был ввод в прошивку HDD модуля под названием "транслятор адреса", который налету превращал физический номер сектора CHS, в логический LBA (Logical Block Address). Но и это лишь на время отложило неизбежную смерть механизма CHS-to-LBA, поскольку основная проблема была зарыта ещё глубже. Она заключалась в том, что в системе адресации логических блоков критически не хватало двоичных разрядов, и их нужно было увеличить как-минимум в 1,5 раза, с 32 до 48-бит. Это требовало-бы полной модернизации аппаратной части подсистемы хранения данных, на что инженеры шли без энтузиазма.

• Головки-чтения "Head"
В любом накопителе HDD изначально присутствуют от двух до шести магнитных дисков (пластины, блины). Информация наносится на них с обеих сторон, а считывают её головки чтения/записи. Соответственно получаем головок вдвое больше, чем кол-во двусторонних дисков. Все головки перемещаются вместе, хотя в любой момент активной может быть только одна. Изначально, для их адресации BIOS выделял 8-бит, что позволяло выбрать одну из 256 штук. Однако позже общее кол-во урезали до 16-ти (4-бит, макс.8 блинов), а оставшуюся тетраду отдали в распоряжение номера цилиндров.

• Цилиндры "Cylinder"
Поскольку все диски накопителя крепятся на общем (передающим крутящий момент) шпинделе, то дорожка(0) диска(0) оказывается прямо над дорожками(0) всех остальных дисков. Дорожки с одинаковыми номерами на всех дисках собираются в обобщённое понятие "цилиндр", а биос резервирует под общее их количество 10-бит, которыми можно адресовать максимум 1024 цилиндра. Если учесть отобранную у головок дополнительную тетраду, то получается уже 14-бит = 16.383 всего возможных цилиндров, на уровне IDE.

• Секторы "Sector"
На поверхности каждого из дисков имеются концентрические (не спиралевидные) дорожки, или как их ещё называют "треки". Эти треки поделены на сектора – основную единицу хранения данных. C диска нельзя считать 1-байт, а только минимум 512-байтный сектор целиком. Как-правило, в одном треке имеется фиксированное кол-во секторов = 63, под которые BIOS отводит всего 6-бит.

Таким образом, адрес конкретного сектора формируется из трёх составляющих – головка (выбирает диск), цилиндр на этом диске, и сектор на поверхности выбранного цилиндра. Рисунок ниже представляет трехмерный адрес CHS в визуальной форме:


HDD.png


Теперь проведём арифметические расчёты, чтобы определить макс.возможную ёмкоcть дисков с разметкой CHS. Для этого достаточно найти произведение всех значений BIOS и результат умножить на размер сектора. Для накопителей HDD, сектор в 512-байт является скорее правилом, чем исключением, хотя на твёрдотелых SSD он может достигать информационного веса в 4 Кб (его подогнали под размер виртуальной страницы ОЗУ). Но сейчас разговор об HDD, поэтому условимся считать сектор равным именно 512-байт. Тогда имеем..

цилиндров..(С): 14-бит = 16.383
головок....(H): 04-бит = 16
секторов...(S): 06-бит = 63
==========================================
16383*16*63 = 16.514.064 (всего секторов)
16514064*512 = 8.455.200.768 (всего байт)


Как видим, используя трёхмерную геометрию CHS, дисковый сервис биоса INT-13h способен оперировать накопителем с макс.размером ~8 Gb. Ещё в эпоху неолита таких объёмов не хватало даже для домашнего пользования. Поэтому инженеры сняли ограничения CHS=16383/16/63 и добавили в биос расширенный сервис INT-13h, более известный как "Enhanced Disk Drive", EDD. Если традиционный сервис адресовал секторы через регистры процессора CX и DX, то в расширенном ввели линейную адресацию LBA и т.н. "адресный пакет", который находится уже в памяти ОЗУ. Теперь регистры не ограничивают макс.номер сектора, и трюк позволил растянуть адресацию до LBA-64:

C-подобный:
Традиционный INT-13h, AH=02h (читать секторы CHS):
---------------------------------------------------
Вход:  DL = номер диска (0=A; 80h=C; 81h=D)
       DH = номер головки
       CH = номер цилиндра
       CL = номер сектора
       AL = число секторов (макс.63 = цилиндр)
       BX = адрес приёмного буфера
Выход: CF = 1 ошибка, иначе буфер содержит прочитанные данные.

;//***************************************************************

Расширенный INT-13h, AH=42h (читать секторы LBA):
---------------------------------------------------
Вход:  DL = номер диска
       SI = указатель на "пакет-адреса"
Выход: CF = 1 ошибка, иначе буфер содержит прочитанные данные.

;//------------------------------
;// Структура "пакета-адресации"
;//------------------------------
Offs  Size  Смысл
 0     1    Размер данного пакета (не меньше 16)
 1     1    Резерв = 0
 2     1    Число секторов для чтения/записи (1..127)
 3     1    Резерв = 0
 4     4    Сегментный адрес буфера (FFFF:FFFF означает, что используется поле 16)
 8     8    Номер сектора LBA-64
 16    8    Линейный адрес буфера (используется, если в поле(4) установлено FFFF:FFFF).

Хоть биос и поддерживал теперь 64-битный дисковый сервис, на практике использовался лишь LBA-32. В результате, ёмкость диска не превышала 4.294.967.295 всего секторов (32-бит) – при размере сектора 512-байт это получалось 2 тераБайт. На этот раз засаду устроила "таблица-разделов" накопителя, что в оригинале звучит как "MBR Partition Table". Эта таблица описывает разделы уже самого жёсткого диска, и в ней под номер сектора LBA зарезервировано именно 32-бита.

Таким образом, инженерам пришлось положить на операционный стол и MBR, чтобы перекроить его под таблицу "GUID Partition Table", в простонародье GPT. По сути требовал этого и сам прогресс, который решил "рубануть с плеча" и полностью искоренить устаревшую систему ввода-вывода BIOS, сделав бартер на более современный EFI. Рассмотрим бегло отличительные особенности этих таблиц, после чего завернём их в ассемблерный код.


2. Формат таблицы-разделов в секторе MBR

Термин MBR берёт своё начало от "Master Boot Record", что в дословно переводится как "Основная загрузочная запись". MBR занимает самый первый сектор с координатами: головка(0), цилиндр(0), сектор(1). Здесь нужно отметить, что в геометрии CHS отсчёт цилиндров и головок начинается с нуля, а секторов с единицы. Однако если мы имеем дело с выстроенными в ряд логическими блоками LBA, то нумеровать эти блоки (аля секторы) принято уже с нуля – такая вот муть..

Посмотрим на рисунок ниже, где представлена таблица-разделов отживающего свой век диска Seagate, объёмом 80 Gb. Здесь я открыл его в HEX-редакторе HxD как физический диск, и скопировал в новое окно только интересную на данный момент таблицу-разделов MBR. Как упоминалось выше, MBR – это равно один сектор, начиная с нуля и до адреса 0200h.

Первые 446-байт до смещения 01BEh отданы в распоряжение загрузчика ОС, а следующий за ним (выделенный) 64-байтный блок и есть таблица 4-х возможных разделов диска HDD. Правда я захватил в хвосте ещё и сигнатуру 55AAh по которой BIOS делает вывод, что сектор фактически является загрузочным. Каждый раздел Partition описывает своя 16-байтная запись (одна строка в нижнем окне), итого 16*4=64 байта. Ограниченный размер данной таблицы не позволяет создавать больше 4-х основных разделов, хотя раздел может быть и расширенным, тогда в нём присутствует своя таблица, ещё для 4-х его логических томов:


MBR-PT.png


В окне "Partition Table" выше я собрал в блоки одноимённые поля всех четырёх разделов. Одна строка – это запись об одном разделе. Судя по этим данным, мой диск имеет всего 2-раздела, поскольку две последние строки забиты нулями. Коричневый первый байт со-значением 80h характеризует флаг загрузочного раздела – обычно это тот, с которого загружается Win. Два серых блока хранят адрес начала и конца раздела в формате CHS. Если там лежит значение FEFFFFh, значит поле не действительно и нужно использовать геометрию LBA.

Байт с типом раздела по смещению(4) информирует о том, основной это раздел, или расширенный. Для файловой системы NTFS сейчас встречаются только три типа: 07h = основной, 0Fh = расширенный раздел, а так-же идентификатор EEh = таблица разделов GPT (о ней ниже). Со списком остальных типов можно ознакомиться
.

Наибольший интерес в таблицах MBR представляют последние два 32-битных значения – это номер сектора LBA с которого начинается раздел (зелёный блок), и всего секторов LBA в разделе (красный блок). Тома и разделы не могут начинаться с середины дорожки диска, а только с нового цилиндра. Поэтому в зелёном блоке первого раздела мы видим значение 0000003Fh=63. Если вспомнить, что в одной дорожке 63-сектора, то всё совпадает. Тогда выходит, что между MBR и началом первого раздела всегда имеются бесхозные 62-сектора (~32 Кб), которые мы можем подмять под себя.

Последний dword в красном блоке хранит общее число секторов в разделе: 02711637h = 40.965.687. Теперь можно вычислить и его размер: 40965687*512=20Gb. Аналогично и для второго партишена: 06DF8F8Ah = 115.314.570 всего секторов, а размер: 115314570*512=59Gb.

Чтобы на программном уровне было проще обращаться к MBR, имеет смысл оформить этот сектор в структуру соответствующего вида. Вот что у меня из этого вышло:


MBR.png


Рассмотрев анатомические особенности таблицы-разделов MBR можно сделать вывод, что она действительно отжила свой век и ей давно уже пора на покой. Во-первых, чтобы при малейшем чихе не потерять навсегда терабайты своих данных, обязательно нужно иметь резервную копию этой таблицы, с её контрольной суммой. Ведь превратить в труху диски MBR проще-простого – достаточно элементарно сбросить "Boot-Flag", сменить тип по-смещению(4) на какой-нибудь от фонаря, или поменять местами два последних поля LBA. Всё.. сушим вёсла.. После этих действий, система в лучшем случае откажется грузиться, а в худшем – вообще не распознает дисковые тома, что приведёт к полной потере информации. Все эти просчёты были учтены в более современной таблице-разделов GPT – разберём её на атомы..


3. Формат таблицы-разделов GPT

GPT это "GUID Partition Table". Права на неё принадлежат Intel, коллектив которой разработал её в 90-х прошлого столетия. Однако распространение таблица получила только с приходом интерфейса EFI "Extensible Firmware Interface" в конце 2000-х. По задумке инженеров, весь хлам из MBR планировалось выкинуть за борт, однако пережившие себя древние устройства не разделяли такую позицию, и грязно высказываясь разработчикам пришлось тащить за собой воз обратной совместимости.

В результате, в секторе(0) GPT по прежнему лежит MBR, только теперь он ущербный без загрузчика (первые 440-байт, забиты нулями), а осталась лишь описывающая всего один раздел запись. Причём последний дворд в ней под кличкой "всего LBA" выставлен на максимум FFFFFFFFh (нет места на диске), а флаг с типом раздела по смещению(4) имеет значение EEh. Это сделано для того, чтобы не знакомый с GPT софт времён динозавров, не повредил случайно таблицу GUID.

Если в MBR под описатель каждого из 4-х разделов выделялось по 16-байт, то в GPT раздел "Partition" описывает уже структура размером аж в 128-байт (четверть 512-байтного сектора). Здесь нет расширенных томов как в MBR – все разделы имеют одинаковые права и являются основными, а общее их кол-во увеличено с четырёх до 128-ми. Поскольку места на харде теперь предостаточно, под GPT выделяется целых 33-сектора =16 Кб, не в пример 64-байтам в MBR. При этом основная таблица GPT продублирована резервной копией, которую в зеркальном виде забросили в самый конец дискового пространства. Общая схема занимаемых секторов представлена рисунком ниже:


GPT-image.png


Здесь видно, что первые 33-сектора заняты служебкой, а непосредственно под данные выделяются секторы начиная с LBA(34). Точная ксерокопия основной таблицы притаилась в хвосте и ждёт своего часа. При каждом включении машины, код системного EFI пересчитывает контрольную сумму GPT, которая хранится по смещению(10h) в её заголовке. При несовпадении CRC, основной заголовок вместе со-всеми записями восстанавливается из резервной копии, на автомате корректируя таким образом служебную инфу.

Согласно документации, вне зависимости от реального числа разделов 128 или всего пару-штук, они начинаются исключительно с сектора LBA(34). Однако на моём буке, первый раздел сдвинут ещё дальше и занимает позицию LBA(2048), хотя в заголовке указано правильно 22h=34. Видимо в доках имелось в виду минимальный сектор(34), а дальше по настроению. Так-что их высказывания можно классифицировать по разному, а лучше не доверяя проверять всё на практике.

В общем случае, вся таблица GUID состоит из двух частей – заголовок "GPT Header" с определением глобальных характеристик накопителя, и 128 элементного массива записей "Partition Entry". Каждая такая запись имеет размер 128-байт и описывает свой раздел, которых так-же может быть 128 штук. Детальное рассмотрение всех составляющих
, а в данной статье мы рассмотрим их лишь поверхностно.


3.1. Заголовок таблицы "GPT-Header"

Программно распознать диск с разметкой GPT можно по сигнатуре "EFI PART" в начале сектора LBA(1), или-же по идентификатору типа-раздела EEh в секторе LBA(0) таблицы MBR. Ниже приводится описание полей данного заголовка, всего 1 сектор = 512-байт:


GPT.png


Обратите внимание, что в заголовке имеются две контрольные суммы CRC32.
Первая по смещению(10h) – сумма исключительно самого заголовка, а вторая с офсетом(58h) – всех имеющихся записей "Partition Entry". Так разрабы закрыли GPT аж на два амбарных замка, а специальная процедура дотошного EFI постоянно их проверяет. В случае малейшего несоответствия, на территории сразу включается ревун и данные тут-же восстанавливаются из резервной копии. Достопочтенный BIOS со-своим MBR о таких мелочах мог только мечтать. Остальные поля заголовка пояснений вроде не требуют.



3.2. Записи о разделах "Partition Entry"

В заключении рассмотрим формат паспорта каждого из разделов. Как упоминалось выше, таблица GPT поддерживает макс.128 разделов, и в 33-х секторах для каждого из них зарезервировано место под описатель. Лично мне не встречались диски с таким кол-вом томов, но как говорят: "лучше еврей без бороды, чем борода без еврея" – пусть лежат на чёрный день, авось когда-нибудь понадобятся.


GPT_entry.png


Обратите внимание на 64-битную маску атрибутов по смещению(30h). Большая часть битов в ней отправлена в резерв, а список активных представлен ниже. Если в двух словах, то у обычных разделов маска имеет значение нуль, т.е. все биты сброшены. А если какой-то из них взведён (как-правило нулевой или под номером 63), то он означает следующее:

Attrib.png


В записях разделов огромную роль играет GUID – "Globally Unique Identifier", или глобально-уникальный идентификатор. В каждой записи по два таких GUID'a (см.предыдущий скрин с форматом). Первый – системный и определяет тип данного раздела, по аналогии с байтом "типа" по смещению(4) в таблице MBR. Второй GUID – это просто рандомный номер раздела, по которому виндовый диспетчер-дисков монтирует том в систему. Кстати если в ком.строке запросить утилиту mountvol.exe без параметров, то она сбросит на консоль список именно этих идентификаторов, с назначенными им буквами разделов.

Ниже перечислены некоторые GUID, предопределяющие тип раздела в операционной системе Win. Например разделы с пользовательскими данными будут иметь тип "Basic Data Partition", а раздел типа "EFI System Partition" зарезервирован для кода загрузчика EFI. В таблице GPT любого накопителя их GUID'ы будут одинаковыми, поскольку они являются глобальными внутри системы:


GUID.png



4. Практика – сбор и вывод информации о разделах

В практической части напишем небольшую утилиту, которая позволит динамически определить способ разметки диска MBR или GPT. Дальше, код в цикле обойдёт все записи в "Partition Table" и отрапортует о собранной информации на консоль. Это будет просто демонстрацией того, как можно подобраться на программном уровне к данным таблицы-разделов. А что дальше делать с этими данными – это уже вопрос к нашей совести. Поскольку в атрибутах разделов GPT имеется бит(60), то можно взвести его в записи любого раздела, в результате чего раздел станет доступным только для чтения, без возможности записи на него. Или-же скрыть его к чертям, установив в единицу бит(62).

Небольшую проблему может создать вывод значений GUID на экран в приглядном виде типа: {EBD0A0A2-B9E5-4433-87C0-68B6B72699C7}. Для этого воспользуемся функцией из библиотеки ole32.dll StringFromGUID2(). Всё-что ей нужно, это указатель на GUID для преобразования, и указатель на приёмный буфер для результирующей строки. Если на выходе получим нуль, значит приёмный буф слишком мал и в аргумент "cchMax" вернётся требуемая длинна. Вот её прототип:


C-подобный:
StringFromGUID2
   rguid        dd  0   ;// указатель на GUID
   lpsz         dd  0   ;// указатель на приёмный буфер для строки
   cchMax       dd  0   ;// вернётся длинна строки

Эта функция сбрасывает в буфер GUID в виде Unicode-строки, значит для вывода на консоль её нужно будет преобразовать в ASCII, просто читая по 2-байта, и сохраняя в тот-же буфер по одному (т.е. отсекать парные нули). Весь алгоритм программы можно представить так:

1. Запросить номер диска на случай, если их несколько в системе.
2. Открыть указанный диск при помощи CreateFile() обязательно с шарой Write, чтобы диск был доступен остальным для записи.
3. Функцией VirtualAlloc() выделить память под 10-секторов диска (в идеале под 33-сектора, но хватит и 10-ти).
4. Через ReadFile() считать секторы из диска в память (чтение разрешено всем, а запись только админу).
5. Проверкой байта "тип-раздела" в MBR, распознать способ разметки GPT или MBR (байт должен иметь значение EEh).
6. В зависимости от результата, спроецировать соответствующую структуру на считанные сектора, и пропарсить их.

Мы не можем заранее знать, на машину какой разрядности попадёт наш код, 32 или 64-бит. Поэтому в таких случаях лучше писать 32-битное приложение. Если оно попадёт на х64, то отработает через её WOW64 (Windows-on-Windows). Зато 64-битное приложение вообще не запуститься на х32, и мы обломаемся по полной. Так-как большинство полей в GPT 64-битные, то придётся оперировать ими через сопр FPU. Исходник этой задумки на лексиконе ассемблера FASM представлен ниже. Инклуд с описанием структур MBR/GPT я спрятал в скрепку:


C-подобный:
format   pe console
include 'win32ax.inc'
include 'equates\mbr_gpt.inc'        ;// инклуд с описанием структур
entry    start
;//----------
.data
dName      db  '\\.\PhysicalDrive'   ;// драйв для открытия
drive      db  0,0,0,0,0,0,0,0,0,0   ;//  ..^^^под номер (с защитой от дурака)
fpuRes0    dq  0                     ;// под результаты,
fpuRes1    dq  0                     ;//   сопроцессора FPU,
fpuRes2    dq  0                     ;//      ^^^^
secSize    dd  512                   ;// размер лог.сектора LBA
kByte      dd  1024*1024             ;//
mByte      dd  1024*1024*1024        ;//
ccMax      dd  0                     ;// для StringFromGUID2()
dataOffs   dd  0                     ;// для VirtualAlloc()
tableOffs  dd  0                     ;// смещение к сл.таблице разделов MBR/GPT
hndl       dd  0                     ;//
buff       db  0
;//----------
.code
start:
;//----- Обзываем консоль и запрашиваем номер диска
         invoke  SetConsoleTitle,<'*** GPT/MBR info v0.1 ***',0>
        cinvoke  printf,<10,'Select Disk (0..7): ',0>
        cinvoke  gets,drive,<'%s',0>

;//----- Пытаемся открыть указанный диск
         invoke  CreateFile, dName,GENERIC_READ,FILE_SHARE_WRITE,0,\
                            OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0
         mov     [hndl],eax
         cmp     eax,-1
         jne     @f
        cinvoke  printf,<' ----> Disk opening error!!!',0>
         jmp     @exit

;//----- ОК! Выделить память под 10-секторов GPT.
;//----- в 10-ти секторах лежат записи о 32-х разделах диска - хватит с избытком.
@@:      invoke  VirtualAlloc,0,512*10,MEM_COMMIT,PAGE_READWRITE
         mov     [dataOffs],eax
         or      eax,eax
         jnz     @f
        cinvoke  printf,<' ----> VirtualAlloc() error!!!',0>
         jmp     @exit

;//----- ОК! Читаем секторы с диска в выделенную память,
;//----- после чего закрываем дескриптор харда.
@@:      invoke  ReadFile,[hndl],[dataOffs],512*10,buff,0
         invoke  CloseHandle,[hndl]

;//----- Проверим тип раздела на GPT (ESI = адрес буфера с данными)
         mov     esi,[dataOffs]
         cmp     byte[esi+MBR.Volume0.Type],0xEE
         jz      @GPT_Partition_Table
         jmp     @MBR_Partition_Table

;//************* ИНФОРМАЦИЯ О ЗАГОЛОВКЕ GPT **************//
@GPT_Partition_Table:
         mov     esi,[dataOffs]
         add     esi,[secSize]
         push    esi esi esi
        cinvoke  printf,<10,'GPT-Header info',\
                         10,'=====================',\
                         10,'  Header signature..........: %s',0>,esi
         pop     esi
         mov     eax,[esi+GPT_HEADER.BackupOffset+4]
         mov     ebx,[esi+GPT_HEADER.BackupOffset]
         push    eax ebx
         fild    qword[esp]
         fstp    [fpuRes0]
         add     esp,4*2
        cinvoke  printf,<10,'  Header backup offset LBA..: %.0f',\
                         10,'  GUID Partition Table LBA..: %04d',\
                         10,'  Disk first partition LBA..: %04d',0>,\
                         dword[fpuRes0],dword[fpuRes0+4],\
                         [esi+GPT_HEADER.PartTableOffset],\
                         [esi+GPT_HEADER.FirstPartOffset]
         pop     esi
         add     esi,GPT_HEADER.DiskGUID
         call    guid2str
        cinvoke  printf,<10,'  Physical disk GUID........: %s',0>,buff

         pop     esi
         mov     eax,[esi+GPT_HEADER.TotalDiskSector+4]
         mov     ebx,[esi+GPT_HEADER.TotalDiskSector]
         push    eax ebx
         fild    qword[esp]
         fimul   [secSize]
         fidiv   [mByte]
         fstp    [fpuRes0]
         add     esp,4*2
        cinvoke  printf,<10,'  Physical disk size........: %.1f Gb',10,0>,\
                         dword[fpuRes0],dword[fpuRes0+4]

        cinvoke  printf,<10,'GUID Partition Table',\
                         10,'=====================',0>

         mov     esi,[dataOffs]
         add     esi,[secSize]
         add     esi,GPT_HEADER.PART0
         mov     [tableOffs],esi

;//************* ЦИКЛ ОБХОДА ЗАПИСЕЙ "GPT PARTITION ENTRY" **************//
@start_GPT_scan:
         mov     esi,[tableOffs]
         cmp     dword[esi],0      ;// проверить на пустую запись
         je      @stop_GPT_scan    ;// выйти, если в записи болото нулей

         add     esi,GPT.PartitionTypeGUID
         call    guid2str
         mov     esi,[tableOffs]
         add     esi,GPT.PartitionUnicodeName
         push    esi
         call    Unicode2Asc
         pop     esi
        cinvoke  printf,<10,'  Type........: %s',\
                         10,'  GUID........: %s',0>,esi,buff

         mov     esi,[tableOffs]
         mov     eax,[esi+GPT.FirstLBA+4]
         mov     ebx,[esi+GPT.FirstLBA]
         push    eax ebx
         fild    qword[esp]
         fstp    [fpuRes0]      ;// запомнить LBA начала раздела

         mov     eax,[esi+GPT.LastLBA+4]
         mov     ebx,[esi+GPT.LastLBA]
         push    eax ebx
         fild    qword[esp]
         fstp    [fpuRes1]      ;// запомнить последний LBA раздела

         fild    qword[esp]
         fisub   dword[esp+8]
         fimul   [secSize]
         fidiv   [kByte]
         fstp    [fpuRes2]      ;// запомнить разницу = ёмкость раздела
         add     esp,4*4        ;// очистить стек от параметров..

         mov     eax,[esi+GPT.Attributes+4]
         mov     ebx,[esi+GPT.Attributes]
        cinvoke  printf,<10,'  Firts LBA...: %.0f',\
                         10,'  Last  LBA...: %.0f',\
                         10,'  Attributes..: 0x%08X%08X',\
                         10,'  Part size...: %.0f Mb',10,0>,\
                         dword[fpuRes0],dword[fpuRes0+4],\
                         dword[fpuRes1],dword[fpuRes1+4],\
                         eax,ebx,\
                         dword[fpuRes2],dword[fpuRes2+4]

         add     [tableOffs],128    ;// пряжок на сл.запись!
         jmp     @start_GPT_scan

@stop_GPT_scan:
         jmp     @exit

;//************* ЦИКЛ ОБХОДА "PARTITION TABLE" В MBR **************//
@MBR_Partition_Table:
         mov     esi,[dataOffs]
         mov     eax,[esi+MBR.DevID]
        cinvoke  printf,<10,'*** Drive-ID: %08X',10,\
                         10,'MBR Partition Table',\
                         10,'======================',0>,eax

         mov     esi,[dataOffs]
         mov     [tableOffs],esi
         add     [tableOffs],MBR.Volume0
@start_MBR_scan:
         mov     esi,[tableOffs]
         movzx   eax,byte[esi+VOLUME_ENTRY.BootFlag]
         movzx   ebx,byte[esi+VOLUME_ENTRY.Type]
         mov     ecx,[esi+VOLUME_ENTRY.FirstLBASector]
         mov     edx,[esi+VOLUME_ENTRY.LastLBASector]
         or      ecx,ecx
         jz      @exit
         call    PrintVolumeInfo

         add     [tableOffs],16
         jmp     @start_MBR_scan

@exit:   ;invoke  VirtualFree,[dataOffs],0,MEM_RELEASE
        cinvoke  gets,buff
        cinvoke  exit,0

;//************* ВСПОМОГАТЕЛЬНЫЕ ПРОЦЕДУРЫ **************//
proc     PrintVolumeInfo
         push    edx
        cinvoke  printf,<10,'  Boot-flag....:  %02X',\
                         10,'  Volume type..:  %02X',\
                         10,'  First LBA....:  %u',\
                         10,'  Total LBA....:  %u',0>,\
                         eax,ebx,ecx,edx
         fild    dword[esp]
         fimul   [secSize]
         fidiv   [mByte]
         fstp    [fpuRes0]
         add     esp,4*1
        cinvoke  printf,<10,'  Volume size..:  %.1f Gb',10,0>,\
                         dword[fpuRes0],dword[fpuRes0+4]
         ret
endp
;//---------------
proc     guid2str
         invoke  StringFromGUID,esi,buff,ccMax
         mov     esi,buff
         mov     edi,esi
         mov     ecx,38
@@:      lodsw
         stosb
         loop    @b
         xor     eax,eax
         stosd
         ret
endp
;//---------------
proc     Unicode2Asc
         mov     edi,esi
@@:      lodsw
         or      ax,ax
         je      @f
         stosb
         jmp     @b
@@:      stosw
         ret
endp
;//---------------
section '.idata' import data readable
library  msvcrt,'msvcrt.dll',kernel32,'kernel32.dll',ole32,'ole32.dll'
import   msvcrt, printf,'printf',gets,'gets',exit,'exit'
import   ole32,  StringFromGUID,'StringFromGUID2'
include 'api\kernel32.inc'


GPT-info.png


Посмотрим, что в итоге получили..
Значит перед нами таблица GPT, в которой сначала идёт заголовок с информацией о самом диске, а дальше логи из записей "Partition Entry". В заголовке видно, что резервная копия заголовка лежит в конце пространства, в секторе(976773167). Сама таблица лежит в секторе(2) – это текущий сектор, ..а разделы с данными (как и утверждал производитель) начинаются с сектора(34). Чтобы вычислить ёмкость раздела в секторах, нужно от Last отнять First-LBA.

Однако в моём случае, внутри записи первого раздела видим начальный сектор(2048), а не 34. Раздел размером 500 Мб и в его атрибутах взведены биты 0 и 63, что означает "Защищённый ОЕМ-раздел, без буквы", т.е. не отображается в проводнике Win. Это бокс для различных драйверов и прочей системной утвари, которую любезно сбросил туда производитель моего бука. Нужно сказать, что и последующие два раздела тоже из этой-же кухни с установленным битом(63), только они не принадлежат вендору ОЕМ (разработчику). В разделе EFI лежит загрузчик ОС размером 100 Мб (старушка MBR его таскала с собой), а в третьем разделе – барахло мелкомягких.


5. Заключение


Доступ к стартовым секторам диска с правами на запись открывает большие возможности. В своё время это было излюбленное место червей и всякой нечисти, поэтому начиная с Висты, MS отобрала у нас эти права (привет Жанне Рутковской, с её "голубой пилюлей"). Читать – пожалуйста, а вот записывать в начало диска (до файловой системы), юзеру нельзя. Чтобы сидя на нарах не ждать с воли сухарей, мы всегда должны помнить об уголовной ответственности за порчу чужой информации. Поэтому всё сказанное здесь носит чисто оборонительный характер, чтобы мы были осведомлены, как при программных сбоях можно восстановить работоспособность своего накопителя. Особенно актуально это для размеченных в формате MBR дисков, где самостоятельная правка пару байт может сэкономить Вам честно заработанные шекели.

В скрепке лежит готовый исполняемый файл для тестов, исходник загрузчика из MBR, а так-же инклуд с описанием структур "Partition Table". Всем удачи, пока!
 

Вложения

  • MBR_GPT.ZIP
    3,5 КБ · Просмотры: 534

qt00

One Level
13.01.2018
5
2
BIT
1
Было время. В bios (а был конкретный зоопарк) выставляли целиндры, сектора, головки.. Интересно, как в MIPS-ах базовая подсистема реализована? Bios Это же удел x086.
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!