نوشتن یک بوت لودر ساده با اسمبلی

سلام ؛ امروز میخوایم با هم یه بوت لودر بنویسیم! بوت لودر اولین کدی هست که توی یه کامپیوتر بعد از روشن شدن اجرا میشه و واقعا نوشتن یک بوت لودر میتونه لذت بخش باشه. البته نیاز هست اشاره کنم که برای نوشتن یک بوت لودر نیاز دارید حتما حتما به اسمبلی یعنی یک زبان سطح پایین تسلط داشته باشید!چیا رو نصب کنم ؟به چیا نیاز دارم : اولین چیزی که قراره بهش بپردازیم اینه که باید چیا رو نصب داشته باشید و چیا رو بلد باشید. من این آموزش رو میخوام با اسمبلی Intel بنویسم اما بازنویسی اون رو با سینتکس At&T آخر این پست میزارم تا دوستان بتونن ازش استفاده کنن.و اینکه طی این آموزش من دارم از سیستم عامل "Ubuntu 180.4.1 LTS" استفاده میکنم که واقعا برای این کار بی نظیر هستش! برنامه هایی که بهش نیاز دارید GCC یا سری کامپایلر های گنو هستش ؛ به nasm یعنی اسمبلر نیاز دارید و همچنین به ld یعنی لینوکس لینکر نیاز دارید. اینها برای اسمبل کردن و کامپایل بوت لودر کافی هستن اما شما برای شبیه سازی محیط یک کامپیوتر به نرم افزار qemu نیاز دارید. برای نصب qemu مراحل زیر رو دنبال کنید :ابتدا کد زیر رو توی محیط ترمینال وارد کنید :sudo apt-get install qemuبعد از اون برای اطمینان از نصب qemu این کد رو اجرا کنید اگر پنجره ای باز شد یعنی مراحل نصب رو درست طی کردید :qemu-system-x86_64و برای نصب nasm هم از کد زیر استفاده کنید :sudo apt install nasmوقتی یه سیستم x86 رو روشن می کنیم چی میشه ؟حالا که تمام نیاز ها رو برای ساخت یه بوت لودر برطرف کردیم میریم ببینیم که یه بوت لودر چطوری کار میکنه. یه بوت لودر 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 &quotA&quot 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 &quotHello world!&quot, 0 ; we need to explicitely put the zero byte here times 510 - ($-$$) db 0 dw 0xaa55در واقع در کد بالا ،‌ اومدیم و تک تک کاراکتر ها رو توی al گذاشتیم و CPU رو با اینتراپت 0x10 صدا زدیم و گفتیم اگر که به صفر رسید این چرخه برنامه رو ببند. و اگر کمی اسمبلی بلد باشید حتما کد رو میفهمید.و در آخر ،‌ کد در سینتکس AT&T:.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 &quotdone&quot int $0x10 # prints the character in al to screen jmp print_char # repeat with next byte done: hlt # stop execution msg: .asciz &quotHello world!&quot .fill 510-(.-init), 1, 0 # add zeroes to make it 510 bytes long .word 0xaa55 # magic bytes that tell BIOS that this is bootableمنبع : https://50linesofco.de/post/2018-02-28-writing-an-x86-hello-world-bootloader-with-assembly