Assembler чтение разделов жесткого диска
Поскольку зарегистрировался на данном форуме, внесу и я свои 5-копеек в общую копилку статей. Занимаюсь по-большей частью ассемблером, то и статья на эту тему. Гуру в этой области прошу строго не судить, т.к. с изложением своих мыслей всегда имел проблемы. Поскольку пишу по-ходу дела и в свободное время, то между частями могут быть перерывы. Рассматривается работа процессоров x86 в двух режимах – реальном RM, и зашищённом РМ. Как и принято - начну издалека..
Часть 1. Общие сведения
1.1. Процессор и его регистры.
В компьютерной системе всё вращается вокруг центрального процессора CPU. Он работает под управлением какой-либо системы. Программист пишет инструкций, которые система помещает в память ОЗУ. В свою очередь процессор берёт инструкции из памяти в свои регистры, выполняет их, и помещает опять в память. Цикл повторяется до тех пор, пока пользователь не остановит программу.
CPU имеет огромное кол-во регистров, и только малая их часть предназначена непосредственно для выполнения кода – эти регистры назвали РОН, или регистры общего назначения: EAX, EBX, ECX, EDX и т.д. Гугл не напрягаясь выдаст кучу ссылок на их описание.
Но такая картина только снаружи процессора. Попав в контейнер процессорного ядра, все регистры переименовываются по типу R0, R1..RN, где N – нагрузка на процессор. Блок переименования ‘Rename’ ввели для параллельного исполнения инструкций на одном ядре процессора, и теперь не возникает путаницы, какому именно потоку принадлежит та или иная инструкция. На выходе из ядра, регистры опять приобретают свои имена EAX и прочие.
Кроме РОН, процессор имеет и следующие регистры:
- Пять регистров-управления самим процессором CR0..CR4 (Control Registers);
- Четыре регистра управления памятью GDTR, LDTR, IDTR, TR (Descriptor Table Registers);
- Восемь регистров-отладки DR0..DR7 (Debug Registers);
- Более 1000 специфических регистров MSR (Model-Specific Registers).
Для начала, рассмотрим такой ассемблерный код в виндовом отладчике ‘DEBUG’..
Здесь командой а я ассемблировал некоторый код, который потом командой u дизассемблировал:
- Всё-что выделено голубым – это команды процессора. Общее их кол-во давно перевалило за 200, и в каждом процессоре появляются всё новые и новые. Забегая вперёд скажу, что львиная их доля никому не нужна, кроме самих AMD и INTEL - мы будем использовать из них всего штук 20-30.
- Выделенное красным – это операнды команд. Слева от запятой находится приёмник, а справа – источник, который может быть регистром, константой, или значением в памяти. Если операнд является значением в памяти, то он берётся в квадратные скобки (по крайней мере в fasm’e, самом ‘правильном’ ассемблере из всех). Здесь в ход идут регистры процессора.
- Выделенное зелёным – это инструкция ассемблера, т.е. команда с операндами. Именно её исполняет CPU.
- Ну и жёлтое это то, как видит инструкции процессор. Этот поток байт (каждая пара чисел) принято называть Operation-Code, или по нашему ‘Опкод’ инструкции.
1.3. Оперативная память ОЗУ.
Другими словами, при любых обстоятельствах (хоть запись, хоть чтение) процессор обменивается с памятью только 64-байтными кэш строками ‘Cache-Line’ в независимости от того, нужен ему байт или двойное слово. Он никогда не станет запрашивать шину ради одного байта, ведь шина – это общий ресурс, который может быть занят, например если кто-то в данный момент использует прямой обмен с памятью посредством DMA. Информацию о размере своей кэш-линейки можете почерпнуть из программы CPU-Z.
Основной проблемой DDR-SDRAM является время доступа к ней. Связано это с тем, что запоминающая матрица её ядра собрана на крошечных конденсаторах, время разряда которых физически не способно превысить частоту в 200 МГц. На более высоких частотах, кондёры просто не успевают полностью разряжаться, что приводит к искажению данных.
Но DRAM дешёвая и отказываться от неё пока рановато. Поэтому инженеры пошли другим путём, и ввели понятие 2n-prefetch (предвыборка). Суть фишки - в увеличении числа линий-чтения с матрицы. N считается тут степенью двойки. Например, для DDR2 n=2, или 2^2=4 параллельные линии, для DDR3 получаем уже 2^3=8 линий, для DDR4 соотвественно 16. Предвыбранные (prefetch) данные накапливаются в промежуточном буфере, который через мультиплексор MUX может сливать информацию с ядра, на любой частоте. Таким образом удалось решить проблему века, придерживаясь опорной частоты запоминающей матрицы в 200 МГц:
Вообще идея использовать конденсаторы для хранения битов не совсем удачна, т.к. они имеют свойства со-временем разряжаться. Значит нужно периодически считывать с них заряд, и записывать его обратно. Этот процесс известен как ‘регенерация ячеек памяти’. До модулей DDR2 регенерацией строк занимался контроллёр-памяти, а начиная с DDR3 - функцию перенесли внутрь DRAM-чипов. Теперь проблема ушла на второй план и выполняется в фоне. Конроллёр просто выделяет чипам определённое время на регенерацию, которое (среди таймингов) числится в розысках под кличкой ‘tRP’:
1.4. Кэш-память процессора.
Другое дело кэш процессора, где в роли запоминающих элементов выступают не конденсаторы, а пара транзисторов, включённых по схеме триггера. Такой тип памяти получил название ‘статическая’ или SRAM. Тут не нужно регенерировать ячейки, на то она и статическая. Процессор получает доступ к ней мгновенно, т.к. его регистры собраны на таких-же триггерах. Ему без разницы от куда брать данные, с другого регистра или со-своего кэш – скорость практически одинакова, если не учитывать небольшие издержки на внутреннюю шину.
Процессор имеет трёхуровневую организацию кеш: L1, L2 и L3. Первые два уровня физически разделены пополам – половина для кода, половина - для данных. Кэш L3 является общим и имеет наибольшую ёмкость памяти. Внутри своего кэш, процессор хранит только действительно нужную информацию и ту, что может потребоваться в будущем (предварительная загрузка данных). Именно поэтому он читает из памяти не единичными байтами, а прихватывает с собой сразу и последующие 63. Практика доказала, что программисты пишут код последовательно и лишь иногда появляются циклы. Значит кэш-контроллёр действительно знает своё дело, раз читает инфу пачками.
Структура и работа кэш памяти - довольная обширная тема, которая потянет на отдельную статью. Только кэш поддерживается полностью на аппаратном уровне, и асм-программисту доступны лишь некоторые средства работы с ним - например полная его очистка, когда хотим получить время выполнения (профайл) определённого участка кода. Поэтому мы не будем копать в эту тему глубоко, а позже - двинемся дальше..
Вы используете устаревший браузер. Этот и другие сайты могут отображаться в нём некорректно.
Необходимо обновить браузер или попробовать использовать другой.
ATA для дZенствующих. Часть 1
- Дождаться готовности устройства (BSY=0)
- Записать в DEV номер устройства на канале.
- Дождаться BSY=0, DRDY=1 считывая 1F7h или 3F6h (для первого канала).
- Записать в регистры остальные параметры.
- Записать в регистр команды код операции.
- Читать регистр статуса пока устройство не установит BSY=0.
- Дождаться готовности обмена данными (DRQ=1)
- Принять данные (или передать).
- Запретить прерывания записью в 3F6h
- Дождаться готовности канала читая бит BSY в порту 1F7h
- Выбрать устройство на канале записью в 1F6h
- Дождаться DRDY=1 и BSY=0
- Загрузить LBA адрес.
- Послать команду чтения (20h)
- Дождаться BSY=0
- Дождаться готовности обмена данными (DRQ=1)
- Принять данные от устройства через 1F0h строковой операцией ввода из порта.
- Разрешить прерывания от устройства.
Исходник конечно из серии "за 5 минут", так, если девайса нет, мы будем ждать до бесконечности пока BSY будет 0. Это во-первых. Во-вторых - если какие-то проблемы с чтением (мы используем команду "чтение с повторами", есть еще чтение без повторов ее код 21h, в этом случае, если сектор не прочитался с 1 раза, значит не судьба), таймаутов у нас нет, опять есть перспектива бесконечно ждать. В-третьих, нет проверки на ошибки чтения и т.д. Имея определенные навыки, исходник можно легко модифицировать с учетом этих требований, оставим это в качестве упражнения.
В случае записи на диск, все происходит аналогично, только данные естественно передаются а не принимаются. И еще, чуть не забыл, прежде чем экспериментировать с записью на диск, сначала потренируйся на каком-нибудь девайсе старом, даже если вы уверены, что все правильно. Все, предупредил.
Отдельно рассмотрим команды идентификации и остановки.
Команда IDENTIFY DEVICE возвращает целый сектор различной инфы о накопителе, ее код - EСh, параметров нет. Мы сейчас ограничимся тем, что определим объем винчестера, формат всего сектора можно найти на любом сайте, посвященном системному программированию.
(сегментная часть адреса буфера в ES)
Здравствуйте,пытаюсь понять логику и написать программу для работы с жестким диском на ассемблере под DOS. Вопрос такой:
1. Как перейти в расширенный раздел на диске? ( Сначала проверяется в MBR на то, что является ли расширенным 05,0f это я знаю, вроде)
2. Как удалить второй логический диск из него? ( По идее могу затереть ссылку с первого на него)
Перехват прерывания жесткого диска
здр. вот, например, существует int 13h - сервис BIOS для работы с диском на низком уровне (чтение.
DOS виснет на int 13h при чтении MBR жесткого диска
читаю MBR жесткого диска, досю гружу с флешки, как доходит до инт 13 - встает. masm .model small.
Показать координаты 1-го основного раздела жесткого диска в CHS виде
Мне нужно показать координаты 1-го основного раздела жесткого диска в CHS виде. Код чтения сектора.
Программа для измерения скорости ввода данных с жесткого диска на Assembler
Нужно написать программу для измерения скорости ввода данных с жесткого диска на Assembler.Опрос.
Во-первых не в MBR, а в таблице разделов внутри MBR.
Во-вторых, расширенный раздел имеет свою таблицу разделов, в которой хранятся записи о его разделах. Соответственно удалять его лог/диски нужно от туда. Для удаления можно забить весь партишен (16-байт) нулями, а можно просто обнулить BootID-байт (4-ый от начала), и раздел будет считаться свободным.
Именно так. По крайней мере точечым воздействием и корректно именно так :
Элемент первичного раздела указывает сразу на загрузочный сектор логического диска
(в первичном разделе всегда имеется только один логический диск), а элемент
расширенного раздела - на список логических дисков, составленный из структур,
которые именуются вторичными MBR (Secondary MBR, SMBR).
Свой блок SMBR имеется у каждого диска расширенного раздела. SMBR имеет структуру,
аналогичную MBR, но загрузочная запись у него отсутствует (заполнена нулями),
а из четырех полей описателей разделов используются только два. Первый элемент
раздела при этом указывает на логический диск, второй элемент указывает на
следующую структуру SMBR в списке. Последний SMBR списка содержит во втором
элементе нулевой код раздела.
Здравствуйте!
Хочу сделать хакерскую программу для посекторного копирования одного жесткого диска на другой.
Сначала я хотел делать на Си, под ДОС, но там очень ограниченное число функций.
Потом, я передумал делать даже под ДОС, потому что в ДОС как в малоразрядной системе нельзя адресовать (наверное) диски размером 3 терабайта.
И поэтому я решил делать не на Си, а на ассемблере. Без ОС на самодельном загрузчике. 64 бита.
Ассемблер я не знаю.
Я нашел немного материала:
http://www.firststeps.ru/asm/r.php?20
"AH=02H
Al - количество секторов для чтения.
CH - цилиндр
CL - сектор
DH - головка
DL - диск
ES:BX - сегмент смещение для буфера."
На этом сайте написано, что цилиндр - это физический диск. Но в диска есть 2 стороны. Мне не понятно, головки используются для обозначения сторон? Или обе стороны физического диска представляют собой цилиндр, а головки используются для деления этого цилиндра на два?
Еще мне не понятно, как прочитать и куда информацию? И какими цифрами, в каком виде идет нумерация? Как представить число например 5657?
А дальше что? Количество считанных секторов, такое как 5657, не уместится ни в один регистр. Где находится тот буфер?
Как посчитать количество обращений к жесткому диску?
Знаю, что необходимо перехватывать прерывания, но не знаю как это сделать. Помогите пожалуйста((
Доступ к жёсткому диску
Всем привет! Есть внешний жёсткий диск. На нём хранятся и программы, нужные файлы и ненужные. В.
Доступ к жесткому диску
Здравствуйте. Установил вин7. Подключил дополнительный жесткий диск со своими данными.
Доступ к жесткому диску
Здравствуйте, имеется 2 системы (windows 7 + windows 7). Обе установлены на разных физических ЖД.
Эти данные из древнего мира древнего оборудования.
Конечно, BIOS для материнки обновляется и улучшает программное обеспечение прерывания int 13h, но что-то остаётся неучтённым.
Решение не в мире DOS и не в мире ассемблера.
Спасибо за ответ! А Вы случайно не знаете, как выполнить операцию в новых биосах?
У меня есть диск 1 терабайт размером. Я с ним замучился. Надо восстановить с него данные. У него первых 200 миллионов LBA (я не знаю, что это) MHDD отображает битыми. Вернее, он на них намертво виснет до выключения питания. Подбирал методов тыка и где-то с 200 миллионов прекращаются зависания.
Когда Windows или Linux загружаются (конечно не с этого, а с другого диска), то этот диск трещит как трактор и зависает. Даже после перезагрузки он остается зависшим и трещит. Помогает только выключение компьютера и снятие питания с диска.
Если же не обращаться к этим первым 200 миллионов LBA, то диск работает нормально (в MHDD).
Я взял программу для DOS "CopyR DMA HDD Cloner" и удачно скопировал при ее помощи с 200 до 256 миллионов LBA. Это около 25 Гб. Но больше я не смог. Эта программа не видит пространство более 128 Гб (256 миллионов LBA). Первых 100 Гб (200 миллионов) испорчены, поэтому можно копировать только остальных 25 Гб (до 256 миллионов).
Я искал другие программы посекторного копирования, но я ничего не нашел. Есть только для Windows, Linux. А они обязательно лезут в эти сектора (первые на диске) и диск виснет. Поэтому я не могу использовать эти системы.
Я так понял, что я могу использовать только ДОС. Но я так понял, ДОС не адресует диски более 128 Гб. И поэтому я никогда не найду нужной программы. Только изготовить самостоятельно. Либо переделать как-то ДОС и CopyR под 64-бит. Или может я неправильно понимаю. Расскажите, пожалуйста, все что Вам известно по этой теме.
Добавлено через 7 минут
Хотя вообще MHDD увидел и просканировал этот терабайтный диск.
А вот второй, 3-терабайтный, он просканировать не смог. Он отобразил максимальный LBA - 5 с чем-то миллиардов, но просканировать он смог только до 1.9 миллиарда.
Если данные имеют высокую ценность, то лучше совсем - в специализированную фирму. Это, конечно, дорого, но вероятность восстановления выше.
Таким образом, адрес конкретного сектора формируется из трёх составляющих – головка (выбирает диск), цилиндр на этом диске, и сектор на поверхности выбранного цилиндра. Рисунок ниже представляет трехмерный адрес CHS в визуальной форме:
Теперь проведём арифметические расчёты, чтобы определить макс.возможную ёмко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:
Хоть биос и поддерживал теперь 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-х его логических томов:
В окне "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 можно сделать вывод, что она действительно отжила свой век и ей давно уже пора на покой. Во-первых, чтобы при малейшем чихе не потерять навсегда терабайты своих данных, обязательно нужно иметь резервную копию этой таблицы, с её контрольной суммой. Ведь превратить в труху диски 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.
Здесь видно, что первые 33-сектора заняты служебкой, а непосредственно под данные выделяются секторы начиная с LBA(34). Точная ксерокопия основной таблицы притаилась в хвосте и ждёт своего часа. При каждом включении машины, код системного EFI пересчитывает контрольную сумму GPT, которая хранится по смещению(10h) в её заголовке. При несовпадении CRC, основной заголовок вместе со-всеми записями восстанавливается из резервной копии, на автомате корректируя таким образом служебную инфу.
Согласно документации, вне зависимости от реального числа разделов 128 или всего пару-штук, они начинаются исключительно с сектора LBA(34). Однако на моём буке, первый раздел сдвинут ещё дальше и занимает позицию LBA(2048), хотя в заголовке указано правильно 22h=34 . Видимо в доках имелось в виду минимальный сектор(34), а дальше по настроению. Так-что их высказывания можно классифицировать по разному, а лучше не доверяя проверять всё на практике.
, а в данной статье мы рассмотрим их лишь поверхностно.
3.1. Заголовок таблицы "GPT -Header "
Программно распознать диск с разметкой GPT можно по сигнатуре "EFI PART" в начале сектора LBA(1), или-же по идентификатору типа-раздела EEh в секторе LBA(0) таблицы MBR. Ниже приводится описание полей данного заголовка, всего 1 сектор = 512-байт:
Обратите внимание, что в заголовке имеются две контрольные суммы CRC32.
Первая по смещению(10h) – сумма исключительно самого заголовка, а вторая с офсетом(58h) – всех имеющихся записей "Partition Entry". Так разрабы закрыли GPT аж на два амбарных замка, а специальная процедура дотошного EFI постоянно их проверяет. В случае малейшего несоответствия, на территории сразу включается ревун и данные тут-же восстанавливаются из резервной копии. Достопочтенный BIOS со-своим MBR о таких мелочах мог только мечтать. Остальные поля заголовка пояснений вроде не требуют.
3.2. Записи о разделах "Partition Entry "
В заключении рассмотрим формат паспорта каждого из разделов. Как упоминалось выше, таблица GPT поддерживает макс.128 разделов, и в 33-х секторах для каждого из них зарезервировано место под описатель. Лично мне не встречались диски с таким кол-вом томов, но как говорят: "лучше еврей без бороды, чем борода без еврея" – пусть лежат на чёрный день, авось когда-нибудь понадобятся.
Обратите внимание на 64-битную маску атрибутов по смещению(30h). Большая часть битов в ней отправлена в резерв, а список активных представлен ниже. Если в двух словах, то у обычных разделов маска имеет значение нуль, т.е. все биты сброшены. А если какой-то из них взведён (как-правило нулевой или под номером 63), то он означает следующее:
В записях разделов огромную роль играет GUID – "Globally Unique Identifier" , или глобально-уникальный идентификатор. В каждой записи по два таких GUID'a (см.предыдущий скрин с форматом). Первый – системный и определяет тип данного раздела, по аналогии с байтом "типа" по смещению(4) в таблице MBR. Второй GUID – это просто рандомный номер раздела, по которому виндовый диспетчер-дисков монтирует том в систему. Кстати если в ком.строке запросить утилиту mountvol.exe без параметров, то она сбросит на консоль список именно этих идентификаторов, с назначенными им буквами разделов.
Ниже перечислены некоторые GUID, предопределяющие тип раздела в операционной системе Win. Например разделы с пользовательскими данными будут иметь тип "Basic Data Partition" , а раздел типа "EFI System Partition" зарезервирован для кода загрузчика EFI. В таблице GPT любого накопителя их GUID'ы будут одинаковыми, поскольку они являются глобальными внутри системы:
4. Практика – сбор и вывод информации о разделах
В практической части напишем небольшую утилиту, которая позволит динамически определить способ разметки диска MBR или GPT. Дальше, код в цикле обойдёт все записи в "Partition Table" и отрапортует о собранной информации на консоль. Это будет просто демонстрацией того, как можно подобраться на программном уровне к данным таблицы-разделов. А что дальше делать с этими данными – это уже вопрос к нашей совести. Поскольку в атрибутах разделов GPT имеется бит(60), то можно взвести его в записи любого раздела, в результате чего раздел станет доступным только для чтения, без возможности записи на него. Или-же скрыть его к чертям, установив в единицу бит(62).
Небольшую проблему может создать вывод значений GUID на экран в приглядном виде типа: . Для этого воспользуемся функцией из библиотеки ole32.dll StringFromGUID2() . Всё-что ей нужно, это указатель на GUID для преобразования, и указатель на приёмный буфер для результирующей строки. Если на выходе получим нуль, значит приёмный буф слишком мал и в аргумент "cchMax" вернётся требуемая длинна. Вот её прототип:
Эта функция сбрасывает в буфер 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 я спрятал в скрепку:
Посмотрим, что в итоге получили..
Значит перед нами таблица GPT, в которой сначала идёт заголовок с информацией о самом диске, а дальше логи из записей "Partition Entry". В заголовке видно, что резервная копия заголовка лежит в конце пространства, в секторе(976773167). Сама таблица лежит в секторе(2) – это текущий сектор, ..а разделы с данными (как и утверждал производитель) начинаются с сектора(34). Чтобы вычислить ёмкость раздела в секторах, нужно от Last отнять First-LBA .
Однако в моём случае, внутри записи первого раздела видим начальный сектор(2048), а не 34. Раздел размером 500 Мб и в его атрибутах взведены биты 0 и 63, что означает "Защищённый ОЕМ-раздел, без буквы" , т.е. не отображается в проводнике Win. Это бокс для различных драйверов и прочей системной утвари, которую любезно сбросил туда производитель моего бука. Нужно сказать, что и последующие два раздела тоже из этой-же кухни с установленным битом(63), только они не принадлежат вендору ОЕМ (разработчику). В разделе EFI лежит загрузчик ОС размером 100 Мб (старушка MBR его таскала с собой), а в третьем разделе – барахло мелкомягких.
Доступ к стартовым секторам диска с правами на запись открывает большие возможности. В своё время это было излюбленное место червей и всякой нечисти, поэтому начиная с Висты, MS отобрала у нас эти права (привет Жанне Рутковской, с её "голубой пилюлей"). Читать – пожалуйста, а вот записывать в начало диска (до файловой системы), юзеру нельзя. Чтобы сидя на нарах не ждать с воли сухарей, мы всегда должны помнить об уголовной ответственности за порчу чужой информации. Поэтому всё сказанное здесь носит чисто оборонительный характер, чтобы мы были осведомлены, как при программных сбоях можно восстановить работоспособность своего накопителя. Особенно актуально это для размеченных в формате MBR дисков, где самостоятельная правка пару байт может сэкономить Вам честно заработанные шекели.
В скрепке лежит готовый исполняемый файл для тестов, исходник загрузчика из MBR, а так-же инклуд с описанием структур "Partition Table". Всем удачи, пока!
Читайте также: