Rev 1942 | Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1065 | Lrz | 1 | ; Copyright (c) 2008-2009, diamond |
2 | ; All rights reserved. |
||
3 | ; |
||
4 | ; Redistribution and use in source and binary forms, with or without |
||
5 | ; modification, are permitted provided that the following conditions are met: |
||
6 | ; * Redistributions of source code must retain the above copyright |
||
7 | ; notice, this list of conditions and the following disclaimer. |
||
8 | ; * Redistributions in binary form must reproduce the above copyright |
||
9 | ; notice, this list of conditions and the following disclaimer in the |
||
10 | ; documentation and/or other materials provided with the distribution. |
||
11 | ; * Neither the name of the |
||
12 | ; names of its contributors may be used to endorse or promote products |
||
13 | ; derived from this software without specific prior written permission. |
||
14 | ; |
||
15 | ; THIS SOFTWARE IS PROVIDED BY Alexey Teplov aka |
||
16 | ; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||
17 | ; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||
18 | ; DISCLAIMED. IN NO EVENT SHALL |
||
19 | ; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||
20 | ; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||
21 | ; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||
22 | ; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||
23 | ; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||
24 | ; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||
25 | ;***************************************************************************** |
||
26 | |||
27 | Встречаются вирус и FAT. |
||
28 | - Привет, ты кто? |
||
29 | - Я? Вирус. |
||
30 | - A я AFT, то есть TAF, то есть FTA, черт, совсем запутался... |
||
31 | |||
32 | Бутсектор для FAT12/FAT16-тома на носителе с размером сектора 0x200 = 512 байт. |
||
33 | |||
34 | ===================================================================== |
||
35 | |||
36 | Есть две версии в зависимости от того, поддерживает ли носитель LBA, |
||
37 | выбор осуществляется установкой константы use_lba в первой строке исходника. |
||
38 | Требования для работы: |
||
39 | 1) Сам бутсектор, первая копия FAT и все используемые файлы |
||
40 | должны быть читабельны. |
||
41 | 2) Минимальный процессор - 80186. |
||
42 | 3) В системе должно быть как минимум 592K свободной базовой памяти. |
||
43 | |||
44 | ===================================================================== |
||
45 | |||
46 | Документация в тему (ссылки валидны на момент написания этого файла, 15.05.2008): |
||
47 | официальная спецификация FAT: http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx |
||
48 | в формате PDF: http://staff.washington.edu/dittrich/misc/fatgen103.pdf |
||
49 | русский перевод: http://wasm.ru/docs/11/fatgen103-rus.zip |
||
50 | официальная спецификация расширения EDD BIOS 3.0: http://www.phoenix.com/NR/rdonlyres/19FEBD17-DB40-413C-A0B1-1F3F560E222F/0/specsedd30.pdf |
||
51 | то же, версия 1.1: http://www.phoenix.com/NR/rdonlyres/9BEDED98-6B3F-4DAC-BBB7-FA89FA5C30F0/0/specsedd11.pdf |
||
52 | описание функций BIOS: Interrupt List by Ralf Brown: http://www.cs.cmu.edu/~ralf/files.html |
||
53 | официальная спецификация Boot BIOS: http://www.phoenix.com/NR/rdonlyres/56E38DE2-3E6F-4743-835F-B4A53726ABED/0/specsbbs101.pdf |
||
54 | |||
55 | ===================================================================== |
||
56 | |||
57 | Максимальное количество кластеров на FAT12-томе - 0xFF4 = 4084; каждый кластер |
||
58 | занимает 12 бит в таблице FAT, так что общий размер не превосходит |
||
59 | 0x17EE = 6126 байт. Вся таблица помещается в памяти. |
||
60 | Максимальное количество кластеров на FAT16-томе - 0xFFF4 = 65524; каждый |
||
61 | кластер занимает 16 бит в таблице FAT, так что общий размер не превосходит |
||
62 | 0x1FFE8 = 131048 байт. Вся таблица также помещается в памяти, однако в |
||
63 | этом случае несколько нецелесообразно считывать всю таблицу, поскольку |
||
64 | на практике нужна только небольшая её часть. Поэтому место в памяти |
||
65 | резервируется, но данные считываются только в момент, когда к ним |
||
66 | действительно идёт обращение. |
||
67 | |||
68 | Схема используемой памяти: |
||
69 | ...-7C00 стек |
||
70 | 7C00-7E00 код бутсектора |
||
71 | 7E00-8200 вспомогательный файл загрузчика (kordldr.f1x) |
||
72 | 8200-8300 список загруженных секторов таблицы FAT16 |
||
73 | (1 = соответствующий сектор загружен) |
||
74 | 60000-80000 загруженная таблица FAT12 / место для таблицы FAT16 |
||
75 | 80000-90000 текущий кластер текущей рассматриваемой папки |
||
76 | 90000-92000 кэш для корневой папки |
||
77 | 92000-... кэш для некорневых папок (каждой папке отводится |
||
78 | 2000h байт = 100h входов, одновременно в кэше |
||
79 | может находиться не более 7 папок; |
||
80 | точный размер определяется размером доступной |
||
81 | физической памяти - как правило, непосредственно |
||
82 | перед A0000 размещается EBDA, Extended BIOS Data Area) |
||
83 | |||
84 | ===================================================================== |
||
85 | |||
86 | Основной процесс загрузки. |
||
87 | Точка входа (start): получает управление от BIOS при загрузке, при этом |
||
88 | dl содержит идентификатор диска, с которого идёт загрузка |
||
89 | 1. Настраивает стек ss:sp = 0:7C00 (стек располагается непосредственно перед |
||
90 | кодом), сегмент данных ds = 0, и устанавливает ss:bp на начало |
||
91 | бутсектора (в дальнейшем данные будут адресоваться через [bp+N] - |
||
92 | это освобождает ds и экономит на размере кода). |
||
93 | 2. LBA-версия: проверяет, поддерживает ли носитель LBA, вызовом функции 41h |
||
94 | прерывания 13h. Если нет, переходит на код обработки ошибок с |
||
95 | сообщением об отсутствии LBA. |
||
96 | CHS-версия: определяет геометрию носителя вызовом функции 8 прерывания 13h и |
||
97 | записывает полученные данные поверх BPB. Если вызов завершился ошибкой, |
||
98 | предполагает уже существующие данные корректными. |
||
99 | 3. Вычисляет некоторые параметры FAT-тома: начальный сектор корневой папки |
||
100 | и начальный сектор данных. Кладёт их в стек; впоследствии они |
||
101 | всегда будут лежать в стеке и адресоваться через bp. |
||
102 | 4. Считывает начало корневой папки по адресу 9000:0000. Число считываемых |
||
103 | секторов - минимум из размера корневой папки, указанного в BPB, и 16 |
||
104 | (размер кэша для корневой папки - 2000h байт = 16 секторов). |
||
105 | 5. Ищет в корневой папке элемент kordldr.f1x. Если не находит, или если |
||
106 | он оказывается папкой, или если файл имеет нулевую длину - |
||
107 | переходит на код обработки ошибок с сообщением о |
||
108 | ненайденном загрузчике. |
||
109 | Замечание: на этом этапе загрузки искать можно только в корневой |
||
110 | папке и только имена, заданные в формате файловой системе FAT |
||
111 | (8+3 - 8 байт на имя, 3 байта на расширение, все буквы должны |
||
112 | быть заглавными, при необходимости имя и расширение дополняются |
||
113 | пробелами, разделяющей точки нет, завершающего нуля нет). |
||
114 | 6. Загружает первый кластер файла kordldr.f1x по адресу 0:7E00 и передаёт |
||
115 | ему управление. При этом в регистрах dx:ax оказывается абсолютный |
||
116 | номер первого сектора kordldr.f1x, а в cx - число считанных секторов |
||
117 | (равное размеру кластера). |
||
118 | |||
119 | Вспомогательные процедуры бутсектора. |
||
120 | Код обработки ошибок (err): |
||
121 | 1. Выводит строку с сообщением об ошибке. |
||
122 | 2. Выводит строку "Press any key...". |
||
123 | 3. Ждёт нажатия any key. |
||
124 | 4. Вызывает int 18h, давая шанс BIOSу попытаться загрузиться откуда-нибудь ещё. |
||
125 | 5. Для подстраховки зацикливается. |
||
126 | |||
127 | Процедура чтения секторов (read_sectors и read_sectors2): |
||
128 | на входе должно быть установлено: |
||
129 | ss:bp = 0:7C00 |
||
130 | es:bx = указатель на начало буфера, куда будут прочитаны данные |
||
131 | dx:ax = стартовый сектор (относительно начала логического диска |
||
132 | для read_sectors, относительно начала данных для read_sectors2) |
||
133 | cx = число секторов (должно быть больше нуля) |
||
134 | на выходе: es:bx указывает на конец буфера, в который были прочитаны данные |
||
135 | 0. Если вызывается read_sectors2, она переводит указанный ей номер сектора |
||
136 | в номер относительно начала логического диска, прибавляя номер сектора |
||
137 | начала данных, хранящийся в стеке как [bp-8]. |
||
138 | 1. Переводит стартовый сектор (отсчитываемый от начала тома) в сектор на |
||
139 | устройстве, прибавляя значение соответствующего поля из BPB. |
||
140 | 2. В цикле (шаги 3-6) читает секторы, следит за тем, чтобы на каждой итерации |
||
141 | CHS-версия: все читаемые секторы были на одной дорожке. |
||
142 | LBA-версия: число читаемых секторов не превосходило 7Fh (требование |
||
143 | спецификации EDD BIOS). |
||
144 | CHS-версия: |
||
145 | 3. Переводит абсолютный номер сектора в CHS-систему: сектор рассчитывается как |
||
146 | единица плюс остаток от деления абсолютного номера на число секторов |
||
147 | на дорожке; дорожка рассчитывается как остаток от деления частного, |
||
148 | полученного на предыдущем шаге, на число дорожек, а цилиндр - как |
||
149 | частное от этого же деления. Если число секторов для чтения больше, |
||
150 | чем число секторов до конца дорожки, уменьшает число секторов для |
||
151 | чтения. |
||
152 | 4. Формирует данные для вызова int 13h (ah=2 - чтение, al=число секторов, |
||
153 | dh=головка, (младшие 6 бит cl)=сектор, |
||
154 | (старшие 2 бита cl и весь ch)=дорожка, dl=диск, es:bx->буфер). |
||
155 | 5. Вызывает BIOS. Если BIOS рапортует об ошибке, выполняет сброс диска |
||
156 | и повторяет попытку чтения, всего делается не более трёх попыток |
||
157 | (несколько попыток нужно в случае дискеты для гарантии того, что |
||
158 | мотор раскрутился). Если все три раза происходит ошибка чтения, |
||
159 | переходит на код обработки ошибок с сообщением "Read error". |
||
160 | 6. В соответствии с числом прочитанных на текущей итерации секторов |
||
161 | корректирует текущий сектор, число оставшихся секторов и указатель на |
||
162 | буфер (в паре es:bx корректируется es). Если всё прочитано, заканчивает |
||
163 | работу, иначе возвращается на шаг 3. |
||
164 | LBA-версия: |
||
165 | 3. Если число секторов для чтения больше 7Fh, уменьшает его (для текущей |
||
166 | итерации) до 7Fh. |
||
167 | 4. Формирует в стеке пакет для int 13h (кладёт все нужные данные командами |
||
168 | push, причём в обратном порядке: стек - структура LIFO, и данные в |
||
169 | стеке хранятся в обратном порядке по отношению к тому, как их туда |
||
170 | клали). |
||
171 | 5. Вызывает BIOS. Если BIOS рапортует об ошибке, переходит на код обработки |
||
172 | ошибок с сообщением "Read error". Очищает стек от пакета, |
||
173 | сформированного на предыдущем шаге. |
||
174 | 6. В соответствии с числом прочитанных на текущей итерации секторов |
||
175 | корректирует текущий сектор, число оставшихся секторов и указатель на |
||
176 | буфер (в паре es:bx корректируется es). Если всё прочитано, заканчивает |
||
177 | работу, иначе возвращается на шаг 3. |
||
178 | |||
179 | Процедура поиска элемента по имени в уже прочитанных данных папки |
||
180 | (scan_for_filename): |
||
181 | на входе должно быть установлено: |
||
182 | ds:si = указатель на имя файла в формате FAT (11 байт, 8 на имя, |
||
183 | 3 на расширение, все буквы заглавные, если имя/расширение |
||
184 | короче, оно дополняется до максимума пробелами) |
||
185 | es = сегмент данных папки |
||
186 | cx = число элементов в прочитанных данных |
||
187 | на выходе: ZF определяет, нужно ли продолжать разбор данных папки |
||
188 | (ZF=1, если либо найден запрошенный элемент, либо достигнут |
||
189 | конец папки); CF определяет, удалось ли найти элемент с искомым именем |
||
190 | (CF=1, если не удалось); если удалось, то es:di указывает на него. |
||
191 | scan_for_filename считает, что данные папки размещаются начиная с es:0. |
||
192 | Первой командой процедура обнуляет di. Затем просто в цикле по элементам папки |
||
193 | проверяет имена. |
||
194 | |||
195 | Процедура поиска элемента в корневой папке (lookup_in_root_dir): |
||
196 | на входе должно быть установлено: |
||
197 | ss:bp = 0:7C00 |
||
198 | ds:si = указатель на имя файла в формате FAT (см. выше) |
||
199 | на выходе: флаг CF определяет, удалось ли найти файл; если удалось, то |
||
200 | CF сброшен и es:di указывает на элемент папки |
||
201 | Начинает с просмотра кэшированной (начальной) части корневой папки. В цикле |
||
202 | сканирует элементы; если по результатам сканирования обнаруживает, |
||
203 | что нужно читать папку дальше, то считывает не более 0x10000 = 64K |
||
204 | байт (ограничение введено по двум причинам: во-первых, чтобы заведомо |
||
205 | не вылезти за пределы используемой памяти, во-вторых, сканирование |
||
206 | предполагает, что все обрабатываемые элементы располагаются в одном |
||
207 | сегменте) и продолжает цикл. |
||
208 | Сканирование прекращается в трёх случаях: обнаружен искомый элемент; |
||
209 | кончились элементы в папке (судя по числу элементов, указанному в BPB); |
||
210 | очередной элемент папки сигнализирует о конце (первый байт нулевой). |
||
211 | |||
212 | Процедура вывода на экран ASCIIZ-строки (out_string): |
||
213 | на входе: ds:si -> строка |
||
214 | В цикле, пока не достигнут завершающий ноль, вызывает функцию int 10h/ah=0Eh. |
||
215 | |||
216 | ===================================================================== |
||
217 | |||
218 | Работа вспомогательного загрузчика kordldr.f1x: |
||
219 | 1. Определяет, был ли он загружен CHS- или LBA-версией бутсектора. |
||
220 | В зависимости от этого устанавливает смещения используемых процедур |
||
221 | бутсектора. Критерий проверки: scan_for_filename должна начинаться |
||
222 | с инструкции 'xor di,di' с кодом 31 FF (вообще-то эта инструкция может |
||
223 | с равным успехом ассемблироваться и как 33 FF, но fasm генерирует |
||
224 | именно такую форму). |
||
225 | 2. Узнаёт размер свободной базовой памяти (т.е. свободного непрерывного куска |
||
226 | адресов памяти, начинающегося с 0) вызовом int 12h. В соответствии с |
||
227 | ним вычисляет число элементов в кэше папок. Хотя бы для одного элемента |
||
228 | место должно быть, отсюда ограничение в 592 Kb (94000h байт). |
||
229 | Замечание: этот размер не может превосходить 0A0000h байт и |
||
230 | на практике оказывается немного (на 1-2 килобайта) меньшим из-за |
||
231 | наличия дополнительной области данных BIOS "вверху" базовой памяти. |
||
232 | 3. Определяет тип файловой системы: FAT12 или FAT16. Согласно официальной |
||
233 | спецификации от Microsoft (версия 1.03 спецификации датирована, |
||
234 | к слову, 06 декабря 2000 года), разрядность FAT определяется |
||
235 | исключительно числом кластеров: максимальное число кластеров на |
||
236 | FAT12-томе равно 4094 = 0xFF4. Согласно здравому смыслу, на FAT12 |
||
237 | может быть 0xFF5 кластеров, но не больше: кластеры нумеруются с 2, |
||
238 | а число 0xFF7 не может быть корректным номером класте |