В алгоритме загрузки IBM PC-совместимых персональных компьютеров, использующих классический принцип загрузки (MBR/legacy), непосредственно после включения питания начинает исполняться код Базовой системы ввода-вывода (БИОС, BIOS). Почему я акцентировал внимание на термине "классический", дело в том, что в последнее время интенсивно разрабатывается спецификация Extensible Firmware Interface (EFI), которая призвана прийти на смену базовой системе ввода-вывода (BIOS). Однако в данной статье мы поведем рассказ именно о классическом методе загрузки (иначе называемом legacy boot), исторически сложившемся сценарии запуска посредством BIOS с использованием кода MBR-сектора. На финальной стадии работы BIOS, после инициализации и тестирования оборудования (процедура POST), происходит выбор устройства, с которого предполагается дальнейшая загрузка "пользовательского" кода (операционной системы). Загрузочным устройством может служить любой тип накопителя, доступный для загрузки: гибкий диск, жесткий диск, флеш-диск, компакт-диск, карта памяти и некоторые другие. После того, как загрузочное устройство инициализировано (выбрано), код BIOS считывает с него первый физический сектор (цилиндр:головка:сектор (CHS) которого = 0:0:1 или адрес логического блока (LBA) = 0), в память по адресу 0000:7C00
, затем проверяет сигнатуру 55AAh в последних двух байтах (смещение 1FEh), и если сигнатура присутствует, то управление передается по адресу 0000:7C00
. Если же сигнатура не найдена, то управление получает собственный специализированный код BIOS, сигнализирующий об отсутствии операционной системы на носителе.
Благодаря принятому в 80х годах отраслевому стандарту на размер физического сектора в 512 байт, размер кода и данных MBR традиционно ограничивается теми же 512 байтами. Тем не менее, теоретически ничто не мешает разработчику изменить структуру MBR таким образом, что он будет либо занимать более одного сектора на пространстве диска, что, в некоторых случаях, упрощает код да и алгоритм самой загрузки. В подобных ситуациях под MBR понимают весь загрузочный код, а первые 512 байт (то есть первый сектор) называют MBS (Master Boot Sector). Поскольку в загрузчиках Windows подобный подход не используется, термины MBS и MBR для нас абсолютно идентичны. Существует большое множество вариантов исполнения MBR-сектора, написанных для разнообразных операционных систем или устройств, но наиболее распространённый формат MBR — это формат Windows.
Теперь давайте перейдём к изучению функциональных особенностей сектора. В данной статье мы с вами будем исследовать загрузочный сектор одной из операционных систем линейки Windows - Microsoft Windows 7. С самого начала, немного забегая вперед, посмотрим на общую структуру размещения функциональных блоков кода в секторе MBR Windows 7:
Смещение | Длина, байт | Описание |
---|---|---|
0000h | 355 | Код загрузчика |
0163h | 80 | Сообщения об ошибках |
01B3h | 2 | Заполнители (00) |
01B5h | 3 | Каждый байт из этих трех является указателем на сообщение об ошибке. Значение 0700h + значение байта определяет смещение сообщения в текущем сегменте данных: 0763h, 077Bh, 079Ah. |
01B8h | 4 | Сигнатура диска (NT Disk Signature) или Идентификатор тома (Volume ID) или Серийный номер диска (Drive Serial Number). Используется самой системой Windows для однозначной идентификации носителя информации, а так же для сопоставления литеры (буквы) устройству. Так же может использоваться различного рода защитами с целью привязки устанавливаемого программного обеспечения к компьютеру. |
01BCh | 2 | Заполнитель (обычно нули). Возможно зарезервировано для будущего использования. |
01BEh | 16 | Таблица разделов (Partition Table, PT). Описывает каждый из четырех возможных для MBR-разметки разделов накопителя. Раздел 1 |
01CEh | 16 | Раздел 2 |
01DEh | 16 | Раздел 3 |
01EEh | 16 | Раздел 4 |
01FEh | 2 | Сигнатура (55AAh). Последние 2 байта сектора. |
Как раз по этой структуре можно проследить, что помимо загрузочного кода в MBR содержатся еще и данные о разметке диска, так называемая таблица разделов, каждая запись в которой описывает параметры одного раздела.
Возможно, небольшие вопросы по структуре оставляют попытки использовать замысловатый триггер на сообщения об ошибках и размещение таблицы разделов непосредственно в MBR-секторе (устоявшаяся традиция), однако в остальном можно отдать должное лаконичности и логичности размещения кода в миниатюрном 512-байтовом пространстве, в таком пространстве особо не разбежишься, как говорится.
Не мешало бы тут же привести и общий алгоритм работы сектора:
- Код BIOS загружает 512 байт сектора MBR в память по адресу
0000:7C00
. - Код MBR копирует себя (собственный код) по адресу
0000:0600
и передает управление на этот адрес. Стек инициализируется в значение0000:7C00
. - Парсим (разбираем) таблицу разделов. В таблице разделов ищется запись активного раздела. Если раздел найден, то запоминаем его. Если найдено несколько активных разделов, или активного раздела вовсе нет, то выводим ошибку.
- Проверяем наличие расширений прерывания int 13h.
- Грузим загрузочный сектор активного раздела (PBR) по адресу
0000:7C00
. - Включаем адресную линию
A20
. - Проверяем наличие
TPM
версии 1.02. - Уходим на дальнейшую загрузку ОС: передаем управление на уже загруженный к этому моменту код PBR по адресу
0000:7C00
.
Теперь давайте взглянем на код внимательнее и попробуем углубиться в логику работы MBR Windows 7. В целях изучения с загрузочного диска нам необходимо этот самый MBR сектор каким-либо образом извлечь и записать в файл, а затем выполнить дизассемблирование кода MBR, то есть перевод его из шестнадцатеричного представления в понятный человеку язык Ассемблера. Для сохранения загрузочной записи MBR с диска я подумал использовать что-нибудь легковесное и, по возможности, не требующее инсталляции.. и инструмент был довольно быстро найден - им оказалась утилита DM Disk Editor and Data Recovery. Спасибо огромное автору за хороший и простой в освоении инструментарий.
У меня получилось выгрузить сектор MBR Windows 7 в виде .bin-файла, в итоге, после данной процедуры я получил 512 байт кода сектора. Теперь можно взглянуть на получившийся шестнадцатеричный дамп (блок кода/данных сектора, представленный в шестнадцатеричной системе счисления):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
0000000000: 33 C0 8E D0 BC 00 7C 8E C0 8E D8 BE 00 7C BF 00 3А.Рј.|.А.Шѕ.|.. 0000000010: 06 B9 00 02 FC F3 A4 50 68 1C 06 CB FB B9 04 00 .№..ьу.Ph..Лы№.. 0000000020: BD BE 07 80 7E 00 00 7C 0B 0F 85 0E 01 83 C5 10 Ѕѕ..~..|.....ѓЕ. 0000000030: E2 F1 CD 18 88 56 00 55 C6 46 11 05 C6 46 10 00 всН..V.UЖF..ЖF.. 0000000040: B4 41 BB AA 55 CD 13 5D 72 0F 81 FB 55 AA 75 09 .A..UН.]r..ыUЄu. 0000000050: F7 C1 01 00 74 03 FE 46 10 66 60 80 7E 10 00 74 чБ..t.юF.f`.~..t 0000000060: 26 66 68 00 00 00 00 66 FF 76 08 68 00 00 68 00 &fh....fяv.h..h. 0000000070: 7C 68 01 00 68 10 00 B4 42 8A 56 00 8B F4 CD 13 |h..h..ґB.V.‹фН. 0000000080: 9F 83 C4 10 9E EB 14 B8 01 02 BB 00 7C 8A 56 00 џ.Д..л.ё..».|.V. 0000000090: 8A 76 01 8A 4E 02 8A 6E 03 CD 13 66 61 73 1C FE Љv.ЉN.Љn.Н.fas.ю 00000000a0: 4E 11 75 0C 80 7E 00 80 0F 84 8A 00 B2 80 EB 84 N.u..~...„..І.л„ 00000000b0: 55 32 E4 8A 56 00 CD 13 5D EB 9E 81 3E FE 7D 55 U2д.V.Н.]л..>ю}U 00000000c0: AA 75 6E FF 76 00 E8 8D 00 75 17 FA B0 D1 E6 64 .unяv.и..u.ъ.Сжd 00000000d0: E8 83 00 B0 DF E6 60 E8 7C 00 B0 FF E6 64 E8 75 и...Яж`и|..яжdиu 00000000e0: 00 FB B8 00 BB CD 1A 66 23 C0 75 3B 66 81 FB 54 .ыё..Н.f.Аu;f.ыT 00000000f0: 43 50 41 75 32 81 F9 02 01 72 2C 66 68 07 BB 00 CPAu2.щ..r,fh.». 0000000100: 00 66 68 00 02 00 00 66 68 08 00 00 00 66 53 66 .fh....fh....fSf 0000000110: 53 66 55 66 68 00 00 00 00 66 68 00 7C 00 00 66 SfUfh....fh.|..f 0000000120: 61 68 00 00 07 CD 1A 5A 32 F6 EA 00 7C 00 00 CD ah...Н.Z2цк.|..Н 0000000130: 18 A0 B7 07 EB 08 A0 B6 07 EB 03 A0 B5 07 32 E4 . ·.л. ¶.л. µ.2д 0000000140: 05 00 07 8B F0 AC 3C 00 74 09 BB 07 00 B4 0E CD ...‹р.<.t »..ґ.Н 0000000150: 10 EB F2 F4 EB FD 2B C9 E4 64 EB 00 24 02 E0 F8 .лтфлэ+Йдdл.$.аш 0000000160: 24 02 C3 49 6E 76 61 6C 69 64 20 70 61 72 74 69 $.ГInvalid parti 0000000170: 74 69 6F 6E 20 74 61 62 6C 65 00 45 72 72 6F 72 tion table.Error 0000000180: 20 6C 6F 61 64 69 6E 67 20 6F 70 65 72 61 74 69 loading operati 0000000190: 6E 67 20 73 79 73 74 65 6D 00 4D 69 73 73 69 6E ng system.Missin 00000001a0: 67 20 6F 70 65 72 61 74 69 6E 67 20 73 79 73 74 g operating syst 00000001b0: 65 6D 00 00 00 63 7B 9A DC 09 A9 7E 00 00 80 20 em...c{.Ь .~..Ђ 00000001c0: 21 00 07 DF 13 0C 00 08 00 00 00 20 03 00 00 DF !..Я....... ...Я 00000001d0: 14 0C 07 FE FF FF 00 28 03 00 00 30 35 3A 00 00 ...юяя.(...05:.. 00000001e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000001f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA ..............UЄ |
Собственно, как вы поняли, этот блок данных есть ни что иное, как шестнадцатеричное представление 512 байт сектора. Что же, дамп сектора у нас имеется, теперь самое время воспользоваться дизассемблером с целью трансляции кода сектора. На этом этапе мной был использован достаточно популярный и проверенный дизассемблер IDA, один из наиболее мощных в своем классе. С другой стороны, в коде MBR Windows 7 ничего сверхсложного нет, поэтому можно было бы использовать абсолютно любые доступные в сети дизассемблеры (в том числе онлайновые). После того, как исходный код MBR получен, мы попытаемся понять логику его работы.
Итак, BIOS, посредством прерывания 19h, производит загрузку первого сектора устройства в память, затем передает на только что загруженный код управление. Управление получено, и в этой точке мы имеем следующее состояние:
- реальный режим (real mode) работы процессора;
- значение регистра CS =
0
; - значение регистра IP =
7C00
; - значение регистра DL = номер "текущего" диска (с которого была выполнена загрузка сектора);
- значение регистра DH = бит5 = 0: загрузочное устройство поддерживается INT 13h; иначе: (все 0) - не важно;
- (для PnP BIOS): пара ES:DI = указатель на структуру PnP Installation check;
- Прерывания запрещены.
Так уж сложилось, что любой код (функционирующий в реальном режиме) на старте производит подготовку собственного рабочего окружения: инициализацию сегментных регистров. Не далеко ушел от этого постулата и код сектора MBR Windows 7, тут мы видим инициализацию сегмента стека, данных, дополнительного сегмента. Затем производится копирование блока размером в 512 байт (самого себя) с адреса 0000:7C00
по адресу 0000:061C
. Делается это по той причине, что впоследствии, на очередном этапе загрузки, код MBR по адресу 0000:7C00
будет перезаписан (затерт) кодом из PBR (загрузочного сектора раздела). После копирования кода управление передается по адресу 0000:061C
.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
0000:7C00 xor ax, ax 0000:7C02 mov ss, ax 0000:7C04 mov sp, 7C00h 0000:7C07 mov es, ax 0000:7C09 mov ds, ax 0000:7C0B mov si, 7C00h 0000:7C0E mov di, 600h 0000:7C11 mov cx, 200h 0000:7C14 cld 0000:7C15 rep movsb 0000:7C17 push ax 0000:7C18 push 61Ch 0000:7C1B retf |
- Обнуляем регистр AX. Этот регистр использоваться для дальнейшей инициализации сегментных регистров;
- Обнуляем сегмент стека SS;
- Устанавливаем указать стека SP в значение
7C00h
. Не смотря на то, что по этому адресу у нас располагается первая инструкция, стек у нас не будет пересекаться с кодом, поскольку перед выполнением команда помещения в стек и SP уменьшается; - Обнуляем сегментный регистр ES;
- Обнуляем сегментный регистр DS;
- Устанавливаем SI в значение
7C00h
; - Устанавливаем DI в значение
0600h
. Значение0600h
будет использоваться как смещение адреса назначения для пересылки 512 байт далее по коду; - Устанавливаем CX в значение
0200h
. Это количество копируемых байт (512 в десятичной системе); - Устанавливаем флаг направления cld. Направление "вперед" для инструкции movsb;
- Переносим 512 байт;
- Записываем в стек содержимое AX, который равен 0 (сегмент для перехода);
- Записываем в стек значение
061Ch
(смещение для перехода); - Командой retf делаем "дальний возврат" из процедуры. На самом деле, наш код никакой процедуры не вызывал, чтобы потом из нее "вернуться". Это просто распространенный прием, используемый для перехода по адресу, записанному в стеке (в нашем случае -
0000:061Ch
), то есть ровно в то местоположение, в которое мы только что скопировали код.
0000:061C
, с этого момента будет логичнее продолжить адресацию уже относительно нового адреса, куда мы, собственно и переместились после копирования участка кода.Следующий блок кода MBR Windows 7 предназначен для организации поиска активного раздела на текущем (физическом) диске. Любой физический диск может быть разделен на блоки, называемые разделами (partitions). Разделы могут быть двух типов:
- Основной (Primary) - (в первую очередь) используется для размещения файлов/каталогов ОС (тем не менее может размещать все что угодно) и может быть "активным", соответственно и "загрузочным";
- Расширенный (Extended) - используется для размещения любой информации (в том числе и файлов/каталогов ОС), однако не может маркироваться в качестве "активного", соответственно загрузочным быть не может. Может разбиваться на логические (Logical) подразделы;
Для организации разбиения диска на разделы используется структура под названием таблица разделов, располагающаяся внутри MBR по смещению 01BEh
от начала сектора. Активный (загрузочный) раздел требуется найти для того, чтобы считать (и в дальнейшем запустить) с него код уже [другого] загрузочного сектора раздела, носящего несколько имен: VBR (Volume Boot Record), PBR (Partition Boot Record) и даже PBS (Partition Boot Sector). Поскольку максимальное количество основных разделов равно 4, то таблица разделов у нас имеет 4 записи (по 16 байт каждая), описывающих каждый раздел на диске (итого размер таблицы - 64 байта). Признак загрузочного раздела - это наличие значения 80h
в первом байте записи. Значение 00h
указывает на незагрузочный раздел. Любое другое значение кроме 00h
и 80h
считается некорректным и игнорируется. Давайте рассмотрим структуру 16-байтовой записи таблицы разделов:
Смещение | Длина | Описание |
---|---|---|
00h | 1 | Признак активности раздела. 80h - активный (загрузочный) раздел. 00h - неактивный (незагрузочный) раздел. |
01h | 1 | Начало раздела — головка. Для совместимости со старыми функциями int 13h, работающими в формате CHS. |
02h | 1 | Начало раздела — сектор (биты 0—5), цилиндр (биты 6, 7). Для совместимости со старыми функциями int 13h, работающими в формате CHS. |
03h | 1 | Начало раздела — цилиндр (старшие биты 8, 9 хранятся в байте номера сектора). Для совместимости со старыми функциями int 13h, работающими в формате CHS. |
04h | 1 | [идентификатор] типа раздела. Фактически описывает используемую [на разделе] файловую систему. |
05h | 1 | Конец раздела — головка. Для совместимости со старыми функциями int 13h, работающими в формате CHS. |
06h | 1 | Конец раздела — сектор (биты 0—5), цилиндр (биты 6, 7). Для совместимости со старыми функциями int 13h, работающими в формате CHS. |
07h | 1 | Конец раздела — цилиндр (старшие биты 8, 9 хранятся в байте номера сектора). Для совместимости со старыми функциями int 13h, работающими в формате CHS. |
08h | 4 | Номер первого блока (читай: сектора) в формате LBA. С какого блока раздел начинается. |
0Ch | 4 | Количество секторов раздела (длина раздела). для 4 байт имеется ограничение 232 секторов * 512 = 2199023255552 байт = 2 терабайта. |
Как раз описанные ограничения на номер первого блока раздела (смещение 08h) и размер раздела (0Ch) и явились основной причиной перехода к GPT-разметке. Ну а теперь, собственно, сам код поиска активного раздела:
1 2 3 4 5 6 7 8 9 10 |
0000:061C sti 0000:061D mov cx, 4 0000:0620 mov bp, 7BEh 0000:0623 loc_623: 0000:0623 cmp byte ptr [bp+0], 0 0000:0627 jl short loc_634 0000:0629 jnz loc_73B 0000:062D add bp, 10h 0000:0630 loop loc_623 0000:0632 int 18h |
- Разрешить асинхронные прерывания (аппаратные, от "внешних" устройств).
- Задаем счетчик записей (4) в регистре CX.
- задаем в BP смещение таблицы разделов (
07BEh
= 0600h + 01BEh). помните, мы переместили код по адресу0000:0600h
? значит теперь любые смещения у нас скорректированы относительно адреса0600h
. - [метка]
- сравниваем первый байт первого раздела со значением 0.
- (знаковое) если меньше (то есть значение попадает в диапазон 80h-0FFh), то мы, предположительно, нашли загрузочную запись, и переходим на
0000:0634
для дальнейшей проверки. - если не равно (с учетом первого сравнения jl - или больше, то есть значение попало в диапазон 01h-79h), то переходим на процедуру выдачи ошибки Invalid partition table.
- добавляем к BP значение 10h (16) для того, чтобы сместить указатель на следующую запись в таблице разделов;
- цикл на
0623h
. то есть проверяем все четыре раздела, пока cx не будет равен 0; - если мы проверили все 4 записи и не нашли среди них ни одной активной - то вызываем прерывание 18h. На многих современных BIOS просто выдается текст PRESS ANY KEY TO REBOOT, на очень старых моделях IBM PC вызывался встроенный интерпретатор языка BASIC.
Активный раздел найден. Но перед тем как перейти непосредственно к чтению с найденного раздела загрузочного сектора раздела (PBR), обеспечивающего дальнейшее выполнение загрузки, необходимо выбрать метод чтения. Часть кода MBR Windows 7, описанная ниже, предназначена для проверки факта поддержки BIOS так называемых "расширений прерывания 13h". Почему именно "расширения" и зачем они потребовались разработчикам из Microsoft? От чего же, к примеру, не использовать старые-добрые стандартные функции чтения секторов? Я постараюсь, в меру собственного понимания вопроса дать исчерпывающее пояснение. История этого события, как я полагаю, уходит корнями в то время, когда для дисков в BIOS существовало ограничение на количество адресуемых секторов - 16515072, поскольку в интерфейсе int 13h для номера цилиндра отводилось 10 бит (1024), для номера головки — 8 бит (256), для номера сектора — 6 бит (63), то есть всего 24 бита, что позволяло адресовать не более 8455716864 байт (8064 Мб или 7,875 Гб). Но диски неуклонно росли в "объемах", очевидно что требовалось что-то в данной ситуации менять. Вот именно тогда и были добавлены дополнительные (расширенные) функции, которые предусматривали использование так называемого режима LBA (Logical Block Addressing), который обеспечивал автоматическое изменение геометрии жестких дисков и позволял адресовать сектор уже как 64-битное целое число. LBA адресовал любой сектор на носителе линейным адресом, в отличии от старого неудобного стандарта CHS, в котором сектор адресовался посредством трех параметров - цилиндра, головки и номера сектора как такового. Этот режим LBA, судя по всему, не поддерживался стандартными (устаревшими) функциями прерывания int 13h, для его то поддержки и требуются "расширения".
1 2 3 4 5 6 7 |
0000:0634 mov [bp+0], dl 0000:0637 push bp 0000:0638 mov byte ptr [bp+11h], 5 0000:063C mov byte ptr [bp+10h], 0 0000:0640 mov ah, 41h 0000:0642 mov bx, 55AAh 0000:0645 int 13h |
- Регистр DL содержит номер текущего диска (мы не "портили" регистр DX в коде выше), который он получает от кода BIOS. Записываем его значение в память по адресу ds:[bp+0]. Соответственно, локальная переменная, адресуемая как [bp+0] - номер текущего диска. Если в системе один диск и загрузка происходит с него, то значение равно
80h
, если же дисков много и загрузка происходит не с первого диска, то, вероятно, значение может быть иным. - Сохраняем значение регистра BP в стек.
- [bp+11h] - локальная переменная, содержащая счетчик попыток чтения диска. Задаем 5 попыток.
- [bp+10h] - локальная переменная, указывающая на наличие/отсутствие расширений прерывания 13h.
- Записываем в аккумулятор AX значение
41h
. Эта функция проверяет наличие расширений прерывания int 13h в BIOS. - В регистре BX должно быть значение
55AAh
. - Вызываем прерывание.
Далее в MBR Windows 7 следует блок кода, который предназначается для обработки возвращенных функцией 41h прерывания 13h значений. На этом этапе мы и понимаем, если ли в BIOS расширения, или их нет. Не все BIOS (особенно старые реализации) поддерживают расширения, поэтому нам надо подобный вариант предусмотреть. Если возвращается сброшенный carry flag (CF=0) и значение регистра BX
меняется на AA55h, то это означает наличие расширений. Версия возвращается в регистре AH
. В регистре CX
возвращается битовая карта поддерживаемого функционала.
1 2 3 4 5 6 7 8 9 10 11 |
0000:0647 pop bp 0000:0648 jb short loc_659 0000:064A cmp bx, 0AA55h 0000:064E jnz short loc_659 0000:0650 test cx, 1 0000:0654 jz short loc_659 0000:0656 inc byte ptr [bp+10h] 0000:0659 loc_659: 0000:0659 pushad 0000:065B cmp byte ptr [bp+10h], 0 0000:065F jz short loc_687 |
- Восстанавливаем из стека регистр BP.
- Условие (беззнаковое) "если меньше", то есть если флаг CF=1 (установлен), то расширений нет и мы передаем управление по адресу
0000:0659
. - Еще одна проверка на отсутствие ошибки выполнения функции: проверяем
BX
на значение0AA55h
; - Если нет - то передаем управление на
0000:0659
. - Заключительная проверка - тест значения в регистре CX на установленный нулевой бит;
- Если бит не взведен (0), то расширения не обнаружены и код прыгает на метку
0000:0659
; - Иначе, проверка прошла успешно, расширения есть. Увеличим на единицу содержимое ячейки [bp+10]. Вспомним, что она используется как внутренняя переменная, описывающая наличие или отсутствие расширений int 13h.
- [метка]
- Сохраняем все 32-битные регистры общего назначения в стек.
- Сравниваем локальную переменную [bp+10h] (которая определяет наличие расширений), с нулем;
- если 0 - то мы не можем использовать расширения и переходим под адресу
0000:0687
. Иначе идем далее по коду.
Описанный далее код выполняет в случае присутствия расширений и использует функцию 42h
прерывания int 13h под названием “Extended Read” (расширенное чтение), которая предназначается для считывания секторов с диска в режиме LBA. А считываем мы первый сектор активного раздела (PBR). Тут используется один не совсем стандартный прием: весь пакет DAP записывается в стек (то есть для задания параметров функции используется стек) и функции перед вызовом дается на этот блок данных указатель. Стоит помнить, что информация в стеке формируется в обратном порядке.
1 2 3 4 5 6 7 8 9 10 |
0000:0661 push 00000000 0000:0667 push dword ptr [bp+8] 0000:066B push 0 0000:066E push 7C00h 0000:0671 push 1 0000:0674 push 10h 0000:0677 mov ah, 42h 0000:0679 mov dl, [bp+0] 0000:067C mov si, sp 0000:067E int 13h |
данные, формирующие блок DAP (Disk Address Packet, Адресный пакет диска) следующие:
смещение | размер | описание |
---|---|---|
00h | 1 байт | длина DAP. 10h или 18h (16 или 24 байта). |
01h | 1 байт | зарезервировано, не используется. должно быть 0. |
02h..03h | 2 байта | количество секторов для чтения (некоторые BIOS имеют ограничение). |
04h..07h | 4 байта | сегмент:смещение буфера памяти для чтения секторов (в x86 сначала задается смещение, потом сегмент) |
08h..0Fh | 8 байт | абсолютный номер стартового сектора для чтения (1 сектор диска имеет номер 0) |
10h | 8 байт | Опционально. 64-битный адрес буфера трансфера. используется только тогда, когда двойное слово по смещению 04h DAP имеет значение FFFF:FFFF |
* Поскольку у нас используется стандартный, 16-байтный пакет DAP, то последнее поле расширенного пакета DAP в нашем случае не задействовано.
- Поместим в стек старшие 4 байта номера стартового сектора для чтения (00000000);
- Поместим в стек младшие 4 байта номера стартового сектора для чтения сразу из переменной [bp+8]. Это у нас смещение в записи таблицы разделов?
- Поместим в стек сегмент буфера для чтения;
- Поместим в стек смещение буфера для чтения;
- Поместим в стек количество секторов для чтения (в нашем случае 1);
- Поместим в стек длину блока DAP (в нашем случае 10h = 16 байт);
- Инициализируем AH = 42h. (номер функции расширенного чтения);
- В DL поместим индекс (номер) диска. он у нас лежим в [bp+0];
- А теперь DS:SI приравняем к SS:SP, чтобы они указывали на блок DAP;
- Вызываем функцию прерывания;
Блок кода, описанный ниже предназначен для корректировки указателя стека, иными словами для "освобождения" стека от занесенных туда 20 байт параметров при вызове функции, но только так, чтобы не затронуть флаги. Если carry flag (CF) сброшен и AH
=0, то функция завершилась удачно. В случае возникновения ошибки CF=1, а AH
содержит код ошибки. В обеих случаях, поле счетчика блоков DAP содержит количество фактически считанных блоков.
1 2 3 4 |
0000:0680 lahf 0000:0681 add sp, 10h 0000:0684 sahf 0000:0685 jmp short loc_69B |
- Сохраняем флаги в регистр AH.
- Оригинальным образом исключаем все данные блока DAP из стека просто "сдвигом" указателя стека на 10h (десятичное 20).
- Восстановим флаги из регистра AH. Таким образом, операция сложения не тронула флаги.
- Переход на метку по адресу
0000:069B
. Там у нас в коде сектора MBR Windows 7 располагает код проверки успешности чтения.
Что мы видим далее? Приведенный ниже блок кода выполняется в том случае, если расширения int 13h не были найдены. В современных машинах трудно представить подобную ситуацию, однако разработчики, видимо, решили обеспечить совместимость со старым оборудованием, либо просто передрали код из старых версий :) Код считывает загрузочный сектор активного раздела (PBR) при помощи стандартной функции 02h
прерывания int 13h. В регистре AL указывается количество секторов для чтения. DL = диск, DH = головка, CL = сектор, CH = цилиндр.
1 2 3 4 5 6 7 |
0000:0687 mov ax, 0201h 0000:068A mov bx, 7C00h 0000:068D mov dl, [bp+0] 0000:0690 mov dh, [bp+1] 0000:0693 mov cl, [bp+2] 0000:0696 mov ch, [bp+3] 0000:0699 int 13h |
- Инициализируем AX. AL=1, значит читаем одни сектор;
- В BX = смещение буфера для чтения
7C00h
; - Инициализируем DL = [bp+0] - номер (индекс) диска;
- Инициализируем DH = [bp+1] - номер головки;
- Инициализируем CL = [bp+2] - номер сектора;
- Инициализируем CH = [bp+3] - номер цилиндра;
- Вызываем функцию прерывания.
Ну а далее, судя по всему, в секторе MBR у нас присутствует блок проверки на успешность считывания стандартной функцией чтения. Это часть большого цикла.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
0000:069B popad 0000:069D jnb short loc_6BB 0000:069F dec byte ptr [bp+11h] 0000:06A2 jnz short loc_6B0 0000:06A4 cmp byte ptr [bp+0], 80h 0000:06A8 jz loc_736 0000:06AC mov dl, 80h 0000:06AE jmp short loc_634 0000:06B0 loc_6B0: 0000:06B0 push bp 0000:06B1 xor ah, ah 0000:06B3 mov dl, [bp+0] 0000:06B6 int 13h 0000:06B8 pop bp 0000:06B9 jmp short loc_659 0000:06BB loc_6BB: 0000:06BB cmp word ptr ds:7DFEh, 0AA55h 0000:06C1 jnz short loc_731 0000:06C3 push word ptr [bp+0] |
- Восстанавливаем все 32-битные регистры общего назначения из стека. Напомню, что незадолго до этого мы их сохраняли.
- Проверяем успешность выполнения функции
02h
прерывания 13h. (беззнаковое) "если не меньше", то уходим на проверку считывания именно загрузочного сектора (0000:06BB
). - Уменьшаем счетчик попыток чтения сектора (байт по адресу [bp+11]).
- Если счетчик попыток не исчерпан еще (не 0), то передаем управление по адресу
0000:06B0
(сброс диска); - Иначе сравниваем индекс текущего диска ([bp+0]) с 80h. первый диск?
- Если диск первый (+ с учетом всех вышеописанных условий) - то уходим на ошибку Error loading operating system;
- Загрузим в DL значение
80h
(первый жесткий диск в системе); - Возвращаемся на проверку расширений int 13h (
0000:0634
); - [метка]
- Сохраняем BP. он у нас используется как база адресации внутренних переменных;
- Функция AH=0 - инициализация (сброс) диска;
- В DL поместим значение байта по адресу [bp+0], это у нас индекс (номер) диска;
- Вызываем функцию 0 прерывания 13h;
- Восстанавливаем значение регистра BP.
- Передаем управление код по адресу
0000:0659
(считывание сектора); - [метка]
- Сюда мы перешли после считывания сектора стандартной функцией. Проверим на наличие в последних двух байтах считанного сектора (
ds:7DFEh
) значенияAA55h
(сигнатура MBR). А действительно ли мы считали PBR (VBR)? - Если нет сигнатуры MBR, то вероятно, что по какой-то причине сектор не был считан, либо считан не тот сектор - уходим на ошибку Missing operating system;
- Сохраним индекс (номер) диска в стеке;
Следующий блок кода сектора Windows 7 MBR является подготовительным перед проверкой наличия TPM (подробнее о TPM ниже). Для проверки наличия TPM, сперва требуется включить адресную линию A20 (которая обеспечивает, помимо прочего, возможность передачи 21-го бита на адресной шине, соответственно открывая доступ к расширенной памяти). Управлением этой линией в IBM-PC-совместимых системах занимается контроллер клавиатуры (исторически?), поэтому с ним мы и будем работать напрямую через порты.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
0000:06C6 call sub_756 0000:06C9 jnz short loc_6E2 0000:06CB cli 0000:06CC mov al, 0D1h 0000:06CE out 64h, al 0000:06D0 call sub_756 0000:06D3 mov al, 0DFh 0000:06D5 out 60h, al 0000:06D7 call sub_756 0000:06DA mov al, 0FFh 0000:06DC out 64h, al 0000:06DE call sub_756 0000:06E1 sti |
- Вызываем процедуру по адресу
0000:0756
. Она является частью кода по включению линии A20 и ожидает готовности контроллера клавиатуры к приему очередного байта; - Если функция вернула не 0 (то есть линия уже включена), то сразу уходим по адресу
0000:06E2
; - Маскируем прерывания;
- Инициализируем AL значением
0D1h
. Это команда управления линией A20. выдачи данных в порт 2 контроллера, то есть мы говорим, что хотим записать байт статуса; - Запишем в порт
64h
это значение; - Вызовем подпрограмму по адресу
0000:0756
. Она предназначена для ожидания готовности; - Инициализируем AL значением
0DFh
. это данные для порта 2. мы хотим открыть линию A20; - Запишем в порт
60h
это значение; - Вызовем подпрограмму по адресу
0000:0756
. Ожидание готовности; - Инициализируем AL значение
0FFh
: сброс клавиатуры и запуск диагностики; - Запишем в порт
64h
это значение; - Вызовем подпрограмму (процедуру) по адресу
0000:0756
. Ожидание готовности; - Разрешим прерывания.
Затем у нас следует блок определения наличия интерфейса TPM (Trusted Platform Module) версии 1.2. Что это за интерфейс и почему разработчики вынуждены были включить его поддержку в и без того переполненные 512 байт сектора MBR Windows 7?
TPM (Доверяемый Платформенный Модуль, Trusted Platdorm Module) - криптографический процессор, предоставляющий инструментарий: генератор случайных чисел, генератор ключей RSA, генератор хешей SHA-1, подсистема шифрования, цифровой подписи. Физически это чип на материнской плате, в энергонезависимой/оперативной памяти которого хранятся корневые/производные ключи. Используется для организации "корня доверия" для технологии шифрования разделов Microsoft BitLocker Drive Encryption.
Именно по этой причине TPM нам требуется на столь раннем этапе загрузки, иначе мы не сможем в нужный момент расшифровать раздел, а соответственно и продолжить с него загрузку операционной системы. Функция BB00h прерывания int 1Ah (TCG_StatusCheck) возвращает в EAX=0, если TPM поддерживается, при этом регистр EBX должен содержать строку ‘TCPA’ (шестнадцатеричное: 54 43 50 41). Если значение не совпадает, то выходим из TCG-кода. Делаем проверку версии в регистре CX, если версия не 1.02, то так же выходим из TCG-кода. Если нужная версия найдена, то вызываем функцию TCG_CompactHashLogExtendEvent. При возврате из функции мы получаем имеем выходные данные в регистрах EAX и EDX.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
0000:06E2 loc_6E2: 0000:06E2 mov ax, 0BB00h 0000:06E5 int 1Ah 0000:06E7 and eax, eax 0000:06EA jnz short loc_727 0000:06EC cmp ebx, 41504354h 0000:06F3 jnz short loc_727 0000:06F5 cmp cx, 102h 0000:06F9 jb short loc_727 0000:06FB push 0000BB07h 0000:0701 push 00000200h 0000:0707 push 00000008 0000:070D push ebx 0000:070F push ebx 0000:0711 push ebp 0000:0713 push 00000000 0000:0719 push 00007C00h 0000:071F popad 0000:0721 push 0000 0000:0724 pop es 0000:0725 int 1Ah |
- [метка]
- Теперь начинаем проверку. AX=BB00h. Функция TCG_StatusCheck;
- Вызываем функцию прерывания;
- Логическое "И" над EAX. Для проверки, вернула ли функция 0 в регистре EAX. Поддерживается ли TPM?
- Если не поддерживается, то уходим на
0000:0727
; - Продолжаем проверку. Сравниваем EBX со значением
41504354h
(TCPA); - Если не равно, то уходим на код по адресу
0000:0727
; - Продолжаем проверку. сравниваем CX со значением
102h
. Версия 1.02 TPM? - (беззнаковое) "если меньше", то уходим на код по адресу
0000:0727
; - TPM найдена нужной версии. готовим вызов функции TCG_CompactHashLogExtendEvent. Заносим параметры через стек. AH=0bbh, AL=07h.
- Занесем в стек значение
00000200h
. 0200h - 512 в десятичном. - Занесем в стек значение
00000008
. старшее слово 0000, младшее слово 0008; - Занесем в стек содержимое регистра EBX.
- Занесем в стек содержимое регистра EBX.
- Занесем в стек содержимое регистра EBP.
- Занесем в стек значение
00000000
; - Занесем в стек значение
00007C00
; - Восстановим из стека основные 32-разрядные регистры. Вот для чего мы заносили все эти значения в стек, чтобы потом одной командой popad инициализировать сразу все регистры, необходимые для вызова функции BB07h прерывания int 1Ah. получается AX = BB07h, DI = смещение буфера для хеширования (7C00h), ECX = длина буфера (0200h=512), EDX = номер PCR (Platform Configuration Registry) = 8, SI = значение для лога событий = 0, EBX = 41504354h;
- Занесем в стек значение
0000
; - Восстановим значение из стека в регистр ES, тем самым инициализировав его в значение
0000
(es=0000); - Вызовем функцию BB07h прерывания 1Ah.
Нижеприведенная часть кода сектора является подготовительной. Она подготавливает регистр DL для последующего применения его уже кодом PBR (VBR) и выполняет переход на код PBR, который был загружен в коде MBR Windows 7 ранее. Помните?
1 2 3 4 5 |
0000:0727 loc_727: 0000:0727 pop dx 0000:0728 xor dh, dh 0000:072A jmp far ptr 0:7C00h 0000:072F int 18h |
- [метка]
- Восстанавливаем из стека слово в регистр DX. там у нас слово [bp+0], а это текущий диск. Обычно значение
80h
; - Обнуляем DH, ведь для нас важен только DL;
- Выполняем "длинный прыжок" по адресу
0000:7C00
, куда мы считали загрузочный сектор активной партиции (Partition Boot Record); - Вызов прерывания 18h. Похоже, что сюда никогда не передается управление и эта часть кода написана "на всякий случай"?
Теперь в секторе следует блок кода, который отвечает у нас за вывод сообщений об ошибках. Ошибки критические, то есть после их вывода возвращаться в основную программу уже нет никакого смысла. Поэтому стоит отметить, что инструкции под адресам 0748
, 0753
, 0754
вводят машину в бесконечный цикл. Соответственно, потребуется ручная перезагрузка. Для вывода символов на экран используется функция 0Eh (телетайп) прерывания int 10h.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
0000:0731 loc_731: 0000:0731 mov al, ds:byte_7B7 0000:0734 jmp short loc_73E 0000:0736 loc_736: 0000:0736 mov al, ds:byte_7B6 0000:0739 jmp short loc_73E 0000:073B loc_73B: 0000:073B mov al, ds:byte_7B5 0000:073E loc_73E: 0000:073E xor ah, ah 0000:0740 add ax, 700h 0000:0743 mov si, ax 0000:0745 loc_745: 0000:0745 lodsb 0000:0746 cmp al, 0 0000:0748 jz short loc_753 0000:074A mov bx, 7 0000:074D mov ah, 0Eh 0000:074F int 10h 0000:0751 jmp short loc_745 0000:0753 loc_753: 0000:0753 hlt 0000:0754 jmp short loc_753 |
- [метка]
- Занесем в регистр AL смещение первого сообщения, которое размещается по адресу
ds:07B7
. Сообщение: Invalid partition table; - Перейдем на процедуру подготовки вывода по адресу
0000:073E
; - [метка]
- Занесем в AL смещение второго сообщения, которое размещается по адресу
ds:07B6
. Сообщение: Error loading operating system; - Перейдем на процедуру подготовки вывода по адресу
0000:073E
; - [метка]
- Занесем в AL смещение третьего сообщения, которое размещается по адресу
ds:07B5
. Сообщение: Missing operating system; - [метка]
- Обнулим AH.
- Прибавим к ax значение
0700h
, чтобы скорректировать смещение сообщений; - SI = смещение сообщения;
- [метка]
- Загрузим в AL байт из ячейки по адресу DS:SI;
- AL=0? это конец строки?
- Если да, то переходим на метку
0000:0753
; - BX=7 - BH-страница, BL-цвет символа;
- AH=0Eh - функция
0Eh
прерывания 10h. Вывод на экран в режиме телетайпа; - Вызываем функцию прерывания;
- Тут организуется цикл с адресом
0000:0745
, выход из цикла по условию достижения конца строки, чтобы допечатать строку по одному символу; - [метка]
- Команда hlt - приостановка процессора;
- Короткий переход на
0753
. Зацикливаем hlt. Требуется аппаратная перезагрузка.
Оставшаяся часть кода MBR Windows 7 - это дополнение для включения линии A20: процедура, ожидающая готовность контроллера клавиатуры к приему очередного байта. Выше, в коде открытия линии A20 мы постоянно вызывали её для ожидания готовности.
1 2 3 4 5 6 7 8 9 |
0000:0756 sub_756 proc near 0000:0756 sub cx, cx 0000:0758 loc_758: 0000:0758 in al, 64h 0000:075A jmp short $+2 0000:075C and al, 2 0000:075E loopne loc_758 0000:0760 and al, 2 0000:0762 retn |
- [подпрограмма/процедура]
- Обнулим регистр CX;
- [метка]
- Считываем байт из порта
64h
в регистр AL; - Делаем небольшую паузу;
- Логическое И: проверка флага готовности;
- Если он не активен (бит выставлен) и счетчик в CX не равен 0, то выполняем очередную итерацию цикла ожидания;
- Иначе опять логическое И;
- Возврат из процедуры.
На этом код MBR заканчивается и далее в секторе располагаются разнообразные данные, как я уже и говорил ранее. Их можно увидеть на общем дампе MBR Windows 7, приведенном в начале статьи. Эти данные представляют из себя сообщения об ошибках, различные внутренние переменные и таблица партиций, а так же - завершающий сектор байт со значением AA55h - сигнатура MBR, записанная в обратном порядке (в силу особенностей архитектуры процессоров Intel x86).
Для Window7 x64 дамп MBR совершенно другой.
Странно, я дамп брал со своей Windows 7 x64 версии. Windows 7 Professional SP1 русская. 6.1.7601.
UPD: Сейчас специально проверил еще раз сектор LBA0. Дамп соответствует. Какая у Вас версия?
Дамп может и другой.Дизассемблируйте код как описано в статье.И посмотрите.Существенных различий нет.
Код поиска активного раздела:
>sti
Комментарии:
"1: Запрещаем немаскируемые прерывания."
-------------------------------------------------------------------
Тут какая-то ошибка. Sti их наоборот, разрешает. Раньше в секции инициализации в начале было cli, а сразу перед "самокопированием" - sti. Здесь, может, cli из дампа выпало?
А в целом - огромное спасибо за проделанную работу. Чёткий, грамотный, пошаговый разбор кусочков кода (это тоже хороший приём, т.к. не приходится глазами по общему листингу шарить). Всё супер!
P.S. Ещё бы ссылочку на скачивание чистого дампа в bin-формате и файлы lst и asm. То же и к PBR относится.
точно :) вот ведь ошибка то :) сейчас проверю. cli в коде не было, кстати.
поправил. спасибо. над файлами подумаю.
Будьте добры подскажите, а какой код запускает драйвер ntfs.sys? Наверно загрузочная запись, которая в PBR?
Честно говоря пока не разбирался с этим вопросом. Но, судя по всему, код поддержки NTFS (в виде серии функций) присутствует уже на стадиях PBR и Bootmgr, но полноценным драйвером не является. Полноценный драйвер \Windows\System32\Drivers\Ntfs.sys загружается на стадии функционирования Winload, перед передачей управления ntoskrnl.exe.
Такой вопрос, подскажите начинающему, а что за сигнатура AA55h и адрес 01FE? Это я про структуру сектора mbr, я вот открыл hex-редактор. Сектор 512 байт . Кончается он слева строчкой 0000001F0 , по центру. Да , в самом конце этой строчки есть 55 AA, AA идет под буквой F, если смотреть сверху. А откуда берется h? В книге тоже одной прочитал , сигнатура AA 55 по адресу 01FE-01FF. Откуда эти адреса берутся, в hex-редакторе такого нет. Их надо как-то самостоятельно высчитывать?
01FE - адрес последних двух байт 512 байтового сектора, обычно не показывается в hex-редакторе, поскольку он отображает только адреса начала каждой строки, а конец надо высчитывать самостоятельно. 55AA - два байта 55 и AA, расположенные по этому смещению (01FE) относительно начала сектора. в типовом hex-редакторе каждая строка 16 байт, поэтому высчитать смещение конкретного байта от начала строки не сложно. и 01FE и 55AA - это шестнадцатеричные числа. просто первое - это смещение от начала сектора, а второе - непосредственно значение (2 байта), хранящееся по данному смещению. h - обозначение шестнадцатеричного числа, может быть опущено.
Ага, спасибо, понял, что h - это просто обозначение системы счисления. А адрес видимо получается так - последняя строчка 0000001F0 , мы ноль в конце заменяем на E(55) получается как раз 01FE, ну а если AA, то заменяем на F(АА), получается 01FF. Т.е этот ноль в конце строчки для того, что потом вместо него поставить одно из 16-ти верхних значений.
ну да, если в вашем hex-редакторе есть вертикальные обозначения смещения в строке, то актуальное смещение вычисляет так, как вы и описали. 0 в конце шестнадцатеричного числа означает, что число кратно 16, собственно строка у нас и есть размером 16 байт.
очень хорошо, серьёзная работа, а чему могут помочь такие знания?
я думаю многому.. например, написанию mbr-загрузчика для собственной ОС или пониманию внутренней организации загрузки Windows, пониманию, на каком этапе с каким железом взаимодействует ОС, с какой целью и какие именно функции задействованы. С точки зрения обучения ассемблеру :) пониманию принципов разбиения на разделы... ну и со всеми вытекающими смежными областями - огромное количество механизмов и алгоритмов :) для общего развития, одним словом :))
задаюсь вопросом - для кого эта статья?
- те, кто как рыба в воде в этих знаниях, им это не нужно.
Кто разбирается и занимается восстановлением дисков - это давно съели и ... Программистам на это на чихать. Остаются юзеры вроде меня :(
Чтобы понять то, что здесь написал автор, как он выматерился - нужно выучить Ассемблер. Вы в своём уме? предлагать такое самоубийство юзеру.
В таком случае, нужно было тогда посоветовать узерам изучить мат. часть, железо, Булеву алгебру со всеми системами исчисления ну про BIOS не забыть вдогонку.
Статья мне понравилась еще бы картинок цветных типа конек-горбунок, Иван царевич и лягушка болотная и т.п.
Но начать сначала не мешало бы с того: Что, при включении ПК после процедуры тестирования которая прошита в BIOS (да, ешо как сам BIOS сапускается ) и удачном отклике всех прицепленных железок к MB (motherboard) - BIOS начинает рыскать MBR на диске Мастер первого слота который задается в параметрах самого окна BIOS, а потом уже MBR выбор диска, системы ОС и т.д.
Иль я чёт не так понимаю.
Не мешало бы вкратце объяснить, что за абракадабра в окне утилита DM Disk Editor and Data Recovery - а то, сразу дизассемблировать код MBR.
Да я может и дизассемблировать не буду, а попросту гыляну что там матерное написано про разделы внесу поправки и забуду это может на всю жизнь.
Нет, так дело не попрет - статью нужно полностью переписать. Не понимаю, за что платят деньги блин, ученным :(
:) отличный комментарий!! единственно я вот подумал, что вы не совсем правы относительно целевой аудитории: как минимум он нужен тем, кто помнил, но забыл. а так многое по делу. ок, на досуге приведу статью к более упрощенному варианту.
Судя по тому что статья 3-я в гугле по запросу "mbr pbr" статья много кому нужна :)))
Отличная статья, спасибо.
В таблице "общая структура сектора MBR Windows 7" после "01B8h - 4 - Сигнатура диска (NT Disk Signature)" по моему должно идти "01BCh - 2 - заполнители (00)". Иначе 2 байта выпадают
да, выпадают, спасибо :) но по сути там все верно, посмотрите еще раз таблицу, подправил!!
И оффтоп конечно. Но подскажите как сделать в IDA, чтобы с начало кода сегмент начинался с 0000:7C00, а потом оно менялось на 0000:0600. А то у меня получается только целиком сегмент смещать (или 7С00 или 0600)?
А почему используется ещё и PBR? Почему MBR не может сразу загрузить загрузчик Windows?
Хороший вопрос!! Казалось бы, зачем усложнять, вводя еще один (промежуточный) этап? Грузи сразу загрузчик NTLDR/Bootmgr и упрощай структуру.. На устройствах, имеющих разделы, первым сектором диска является MBR (как независимый от операционной системы этап загрузки), а на устройствах, не имеющих разделы в первом секторе размещается PBR (начинает уже загрузку непосредственно установленной на разделе операционной системы). Так вот, дабы дать возможность загружаться разнообразным операционным системам после MBR, и ввели еще один промежуточный этап в виде PBR. Подробнее читайте в статье про PBR на этом ресурсе.
Ребят, привет.Мож кто подскажет, где что поменять в загрузчике установочной флэшки Win7, сделаной руфусом 2.0, чтоб загрузка пошла. Дело в том что я решил ушустрить установку, провёл несколько тестов с форматированием с разным размером кластера, определился на одном значении, но загрузка с флэшки идёт только со стандартным размером кластера. Пока выдаёт ошибку " disk read error". с ассемблером имел дело уже 20 лет назад и то поверхностно, но оч. надо. MBR PBR могу скинуть.
???