В данном посте хочу рассказать как можно запись исполняемый код в загрузочный сектор не задев при этом служебной информации о фат12(которая используется на дискете). Для большей ясности рекомендую прочесть статью на харбе , прочитать про FAT а также на нашем сайте уже писалось по поводу загрузочного сектора.
И так приступим. Наш загрузчик должен быть компактен, чтоб его размер не превышал 256 байт(хотя можно и чуть больше, я в данном примере копирую в сектор 432 (1b0h) байта то есть такой размер кода может быть в загрузочном секторе , но реально 256 байт хватает с головой чтоб загрузить с дискеты файл и запустить его ). Для начала просто напишем код код который выводит на экран «Hello world!» . Для уменьшения кода я написал простенький макрос который используется в 2х местах _rwsecto, этот макрос как видите принимает два параметра, первый отвечает за запись/чтение второй буфер куда или от куда считывать.
view source
01
macro_rwsector rw,buf{
02
;2 чтение, 3 запись
03
movah,rw ;//функция чтения/записи
04
moval,1 ;//кол секторов
05
movbx,buf ;//буфер куда читаем
06
movch,0 ;//номер дорожки
07
movcl,1 ;//номер сектора
08
movdh,0 ;//сторона головки
09
movdl,0 ;//сам дисковод А=0 B=1
10
int13h
11
}
12
13
org100h ;//генерируем СОМ файл, загружаемый с 0x100
14
use16
15
jmpstart
16
qwe db0ch dup(0)
17
fun:
18
jmppisem
19
;//-------------
20
pisem:
21
movax,07b3h
22
movds,ax
23
movsi,msg
24
cld;//направление для строковых команд
25
movah, 0x0E ;//номер функции BIOS
26
pnext:
27
lodsb;//загружаем очередной символ в al
28
cmpal,'$';// $ символ означает конец строки
29
jznext
30
int0x10 ;//вызываем функцию BIOS
31
jmppnext
32
next:
33
34
jmp$ ;// организовываем вечный цикл
35
;//==================
36
msg db"Hello World!", '$'; переменная которую выведем на экран
37
;//==================
38
start: ;// начало кода который записывает данные в загрузочный сектор
39
movax,cs;
40
moves,ax;//также определяем es так как чтение кластера дискеты будет проходить по адресу es:ds
41
_rwsector 2,buf ;//макрос который считывает в буфер загрузочный сектор дискеты
42
43
;//пишем сначала джамп а потом идем на +03аh и там наш код пишем
44
;//просто после первых 3 байт идет описание дискеты оно занимает 0а3h , дальше все наше))
45
;/если эту информацию повредить то windows или dos не смогут определить какая там файловая
46
;//система и предложат ее отформатировать
47
movsi,buf
48
movah,0ebh
49
mov[si],ah
50
movah,03ch
51
mov[si+1],ah
52
movah,90h
53
mov[si+2],ah
54
;//далее пишем сам код
55
movcx,1b0h ;
56
movdi,buf ;//куда писать будем
57
adddi,03eh ;//отступаем чтоб не повредить инфу о дискете
58
movsi,fun ;//указываем начало кода который нужно вписать
59
60
repmovsb;//и пишем
61
_rwsector 3,buf ;// затем преобразованный сектор пишем обратно на дискету
62
63
;================
64
65
movah,0x4C ;//эта функция завершает программу
66
moval,0 ;//код возврата 0
67
int0x21 ;//вызываем ядро операционной системы
68
buf db512 dup(0) ;// буфер для хранения информации считанной с сектора
Данный код должен быть выполнен на os Windows или в DOSe с тем учетом что в ПК иметься дискета (3,5″ и объемом 1,4 Мб) под буквой А. Можно (даже рекомендуется) все делать на виртуальной машине. Затем перезагрузить машину(ПК) и загрузиться с дискетки. Если все сделали верно то должно вывести «Hello world!!» как на рисунке ниже:
ОС на ассемблере. Загрузочный сектор
Теперь давайте разберемся как загрузиться с дискеты а затем загрузить нужный нам файл в память и передать управление этому участку памяти куда мы загрузили наш файл. Прежде всего давайте рассмотрим как вообще распределяется память в реальном режиме у ПК:
от 00000h до A0000h основная память 640Кбайта ( векторы прерываний, ядро ос,программы и прочее )
от A0000h до C0000h графика EGA
от C0000h до D0000h видео память.
от D0000h до F0000h системная область
от F0000h до FFFFFh системное ПЗУ
Исходя из того что в самом начале памяти размещены векторы прерываний то мы будим грузиться в память по адресу 0800h. Теперь рассмотрим пример программы которая будет грузить в память с дискеты файл io.bin а затем выполнит его. Давайте сначала разберем код файла io.bin :
view source
01
movax,cs
02
movds,ax
03
pisem:
04
movsi,msg
05
print:
06
moval,[si]
07
cmpal,0
08
jenext
09
10
movah,0x0e
11
int0x10
12
moval,[si]
13
14
jmppnext
15
next:
16
endfun:
17
jmpendfun
18
pnext:
19
incsi; i
20
jmpprint
21
msg db"files is load!", 0
Пример кода программы которая будет писать в загрузчик код, который ищет файл и загружает его в оперативную память:
view source
001
;описываем макросы
002
macro_rwsector rw,kols,buf,nomd,noms,nomg,disk{
003
;2 чтение, 3 запись
004
movah,rw ;функция чтения/записи
005
moval,kols ;кол секторов
006
movbx,buf ;буфер куда читаем ES:BX
007
movch,nomd ;номер дорожки
008
movcl,noms ;номер сектора
009
movdh,nomg ;сторона головки
010
movdl,disk ;сам дисковод А=0 B=1
011
int13h
012
}
013
014
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
015
macro_searchfile buf,nfile,nomer{
016
;макрос ищет файл если нашел то возвращает его относительный адрес
;секторов и возвращает значение по этому адресу(чтоб узнать,
059
;конец файла или нет)
060
;значение возвращаем в ах
061
;метод читает относительный адрес в карте
062
;ax- номер относительного сектора на карте
063
;будем учитывать что все находится в буфере buf(карта)
064
localnechet
065
localnext
066
pushsector
067
068
069
;загружаем карту сперва
070
_rwsector 2,9,buf,0,2,0,0 ;
071
072
movcx,2
073
xordx,dx
074
075
divcx
076
077
popbx
078
addax,bx
079
addax,dx
080
;ах = относительный сектор
081
;читаем карту диска и снова вычисляем относительный адрес
082
083
pushdx
084
pushax
085
086
087
popbx; номер кластера
088
popdx
089
090
091
movah,[buf+bx-1];идет следом
092
moval,[buf+bx-2] ;идет
093
094
cmpdx,1
095
jenechet
096
;если нет остачи то AB *C=*CAB если нет то A* BC= BCA*
097
098
shrax,4
099
100
jmpnext
101
;---------
102
nechet:
103
shlax,4
104
shrax,4
105
106
107
next:
108
;грузим в память сектор
109
110
111
112
113
}
114
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
115
;==================================
116
macro_loadsector buf,nomer,disk{
117
localost
118
localnext
119
pushbuf ;куда грузим
120
pushnomer
121
xordx,dx
122
123
popax
124
addax,14
125
movcx,18
126
divcx
127
;ah-остаток
128
129
movcl,dl
130
pushcx
131
movah,0
132
movcl,2
133
divcl
134
popcx
135
cmpah,1
136
jeost
137
movdh,1
138
jmpnext
139
ost:
140
movdh,0
141
next:
142
143
addal,ah
144
145
movch,al
146
147
148
popbuf
149
movah,2 ;функция чтения
150
moval,1 ;кол секторов
151
152
153
movdl,disk ;сам дисковод А=0 B=1
154
int13h
155
}
156
157
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
158
macro_loadfile2 fname,buf,fbuf{
159
;fname - имя файла, переменная 11 байт
160
;buf-буфер для чтения информационных секторов, желательно иметь
161
;размер 12*512byte=6144 byte
162
;fbuf - буфер для загрузки файла
163
;ах возвращает код операции fff все норм, 0-файл на найден
164
165
localexit
166
localgo
167
168
169
_searchfile buf,fname,0
170
171
cmpax,0
172
jeexit
173
174
movbx,fbuf
175
176
go:
177
178
pushbx
179
pushax
180
_loadsector bx,ax,0
181
popax
182
_nextsector ax,buf
183
popbx
184
185
cmpax,0fffh
186
jeexit
187
188
addbx,512
189
jmpgo
190
exit:
191
192
}
193
194
org100h ;генерируем СОМ файл, загружаемый с 0x10
195
use16
196
jmpstart
197
qwe db0bh dup(0)
198
fun:
199
jmppisem
200
;-------------
201
; код который выполниться при загрузке первого сектора
202
pisem:
203
;
204
movax,07b3h
205
movds,ax
206
movax,0800h ;сюда грузим имя файла
207
moves,ax; а затем от туда его вынимаем
208
cld
209
xordi,di
210
movsi,nfile
211
movcx,11
212
repmovsb
213
movds,ax
214
_loadfile2 0,buf,0
215
jmpdword 0800h:0000
216
exit:
217
jmp$
218
219
220
nfile db"IO BIN",'!$';имя нашего файла который грузим в ОП
221
;==================
222
start:
223
movax,cs
224
moves,ax
225
226
227
_rwsector 2,1,buf,0,1,0,0
228
229
230
;пишем сначала джамп а потом затем идем на +1а3 и там наш код пишем
231
232
movsi,buf
233
movah,0ebh
234
mov[si],ah
235
movah,03ch
236
mov[si+1],ah
237
movah,90h
238
mov[si+2],ah
239
240
movcx,1b0h ;
241
movdi,buf
242
adddi,03eh
243
movsi,fun
244
245
repmovsb
246
_rwsector 3,1,buf,0,1,0,0
247
;================
248
movah,0x4C ;эта функция завершает программу
249
moval,0 ;код возврата 0
250
int0x21 ;вызываем ядро операционной системы
251
buf db512 dup(0) ; буфер для хранения информации считанной с сектора
Теперь давайте разберемся с макросами. Слегка изменили макрос _rwsector , теперь он стал гибче и принимает больше параметров. _rwsector макрос который ищет файл, он принимает буфер для того чтоб грузить туда таблицу файлов, имя файла, и номер диска. Макрос читает таблицу файлов, и если файл найден то возвращает его относительный адрес, то есть относительный адрес первого сектора файла. Затем идет макрос _rwsector который принимает буфер и относительный сектор, он проверяет относительный сектор файла если его номер равен 0fffh то это последний сектор в файле если нет то смотрим второй и так пока не достигнем последний сектор. Макрос _rwsector загружает сектор по относительному адресу, принимает относительный адрес сектора а также буфер куда грузить и номер диска. И последний макрос _loadfile2 принимает имя файла, затем буфер служебный буфер куда будет грузиться сам файл, этот макрос содержит все выше перечисленные макросы и с помощью них загружает в память файл, также макрос в AX возвращает код операции если 0 то файл не найден.