سلام ؛ امروز میخوایم با هم یه بوت لودر بنویسیم! بوت لودر اولین کدی هست که توی یه کامپیوتر بعد از روشن شدن اجرا میشه و واقعا نوشتن یک بوت لودر میتونه لذت بخش باشه. البته نیاز هست اشاره کنم که برای نوشتن یک بوت لودر نیاز دارید حتما حتما به اسمبلی یعنی یک زبان سطح پایین تسلط داشته باشید!
ابتدا کد زیر رو توی محیط ترمینال وارد کنید :
sudo apt-get install qemu
بعد از اون برای اطمینان از نصب qemu این کد رو اجرا کنید اگر پنجره ای باز شد یعنی مراحل نصب رو درست طی کردید :
qemu-system-x86_64
و برای نصب nasm هم از کد زیر استفاده کنید :
sudo apt install nasm
حالا که تمام نیاز ها رو برای ساخت یه بوت لودر برطرف کردیم میریم ببینیم که یه بوت لودر چطوری کار میکنه. یه بوت لودر 512 بیت از حافظه ی bootable میخونه ، اگر که ۲ باید آخر از اون برابر عدد جادویی بود ، اون رو به عنوان یک بوت لودر شناسایی میکنه. برنامه ی ما هم باید در نقطه ی 0x7c00 از حافظه باشه.
عدد جادویی برابر 0x55aa هست تو مبنای hex. اما همیشه ۲ بیت آخر با هم جا به جا میشن ، پس ما باید ۲ بیت آخر رو برابر 0xaa55 قرار بدیم تا به عنوان عدد جادویی شناخته بشه.
خیلی خب ، میریم سراغ کد نویسی با اسمبلی . از اونجایی که مهم نیست اصلا اون ۵۱۰ بیت دیگه چی باشن ، ما هم با صفر پرشون میکنیم. اما اول از همه اجازه بدید تا یه کد اولیه رو بنویسیم:
[BITS 16] [ORG 0x7c00] global _strat _start: jmp _start
خط اول داره میگه که دارم توی مدل ۱۶ بیت کد میزنم.
خط دوم میگه که برنامه ی ما در نقطه ی 0x7c00 قرار داره.
خط بعد داره به لینکر یا هر چیز دیگه ای میگه که نقطه ی شروع برنامه ی من _start هست.
و _start یه حلقه ی بی پایان هست.
خب ،حالا بریم سراغ پر کردن اون ۵۱۲ بیت.
[BITS 16] global _strat _start: jmp _start times 510-($-$$) db 0 ; fill the output file with zeroes until 510 bytes are full dw 0xaa55 ; magic number that tells the BIOS this is bootable
این هم از این ، به همین سادگی ۵۱۲ بیت رو هم پر کردیم. بزارید حالا با nasm کامپایلش کنیم :
nasm -o boot.bin boot.asm
و از دستور زیر برای دیدن فایلتون استفاده کنید :
$ ls -l
میبینید که یه فایل 512 بیتی به نام boot.bin داریم.
بزارید تا کدمون رو امتحان کنیم با دستور :
qemu-system-x86_64 boot.bin
خب ، حالا باید دستوارتمون رو توی بوت لودر بنویسیم. میخوایم یه hello world بنویسیم. برای این کار ، باید پیام رو در ثبات ax بریزیم و سیگنال 0x10 رو به CPU بفرستیم. در واقع ، هر بار باید کاراکتر مورد نظر خودمونو در al بزاریم و کد رنگ اون رو در ah و بعدش CPU رو با اینتراپت 0x10 صدا کنیم. پس شروع میکنیم :
[BITS 16] [ORG 0x7c00] global _start _start: mov ax, 0x0e41 ; 0x0e = White color, and 0x41 is "A" in ASCII ... Similar to mov ah, 0x0e and mov al, 0x41 int 0x10 ; Call CPU with 0x10 interrupt hlt ; Stop Execution times 510 - ($-$$) db 0 dw 0xaa55
خیلی خب ،برنامه رو دوباره کامپایل کنید و میبینید که نتیجه مثل زیره :
و حالا میریم که کل کد رو بنویسیم :
[BITS 16] [ORG 0x7c00] global _start _start: mov si, msg mov ah, 0x0e ; sets AH to 0xe (function teletype) print_char: lodsb ; loads the current byte from SI into AL and increments the address in SI cmp al, 0 je done int 0x10 done: hlt msg: db "Hello world!", 0 ; we need to explicitely put the zero byte here times 510 - ($-$$) db 0 dw 0xaa55
در واقع در کد بالا ، اومدیم و تک تک کاراکتر ها رو توی al گذاشتیم و CPU رو با اینتراپت 0x10 صدا زدیم و گفتیم اگر که به صفر رسید این چرخه برنامه رو ببند. و اگر کمی اسمبلی بلد باشید حتما کد رو میفهمید.
.code16 # use 16 bits .global init init: mov $msg, %si # loads the address of msg into si mov $0xe, %ah # loads 0xe (function number for int 0x10) into ah print_char: lodsb # loads the byte from the address in si into al and increments si cmp $0, %al # compares content in AL with zero je done # if al == 0, go to "done" int $0x10 # prints the character in al to screen jmp print_char # repeat with next byte done: hlt # stop execution msg: .asciz "Hello world!" .fill 510-(.-init), 1, 0 # add zeroes to make it 510 bytes long .word 0xaa55 # magic bytes that tell BIOS that this is bootable
منبع :