miércoles, 14 de marzo de 2012

Hola mundo a pelo

Hace poco fue la RootedCon de este año, conferencia que me encanta y a la que voy desde la primera edición. A nivel personal me aporta muchísimo ya que en Gran Canaria (mi islita) no hay nada de seguridad informática y esta conferencia me permite darme un viajecito y juntarme con gente a la que le gusta lo mismo que a mi :).

Vamos al ajo, el título de esta entrada se parece mucho al anterior, WTF!... tranquilos, no tiene nada que ver, me explico. Hace poco me puse a juguetear un poco con el arranque de los sistemas (concretamente el de los IBM PC y compatibles), con sus BIOS, el POST (Power-On-Self-Test) y todas esas movidas. Como sabemos el sistema operativo no es "lo primero" y más bajo nivel del ordenador, antes del sistema la BIOS hace unas pequeñas comprobaciones sobre el hardware que hay y luego carga en memoria un cacho de código. Esto es lo primero sobre lo que tenemos control, esa primera instrucción. ¿Pero dónde se encuentra y qué podemos hacer con eso?. Bueno, aquí es donde empieza lo interesante. Este código lo carga la BIOS desde el primer sector del primer dispositivo con un MBR (Master Boot Record) válido. El código que se suele encontrar aquí es el de los gestores de arranque (GRUB, Lilo, etc...), aunque el gestor de arranque no es sólo ese código, suele ser una pequeña porción que se encarga de ir a otra zona del disco y coger el resto del código... 440 bytes es bastante poco.

Sabiendo esto ya podemos empezar a jugar para comprobar que realmente ésto es así. Con unos compañeros estuve hablando de escribir cada uno un pequeño "hola mundo" en alguna arquitectura y así ir prácticando. Yo elegí x86 que es la que conozco (a ver si un día empiezo a mirar ARM). A continuación veremos un ejemplo de un pequeño programa muy quick and dirty para hacer que el ordenador al arrancar diga "hola mundo" y luego haya que pegarle botonazo xD.

; This is a quick and dirty program that shows how to handle with BIOS
; interrupts (specifically video interrupts) at real-mode on x86 processors
; family.
;
; Be careful, i have hardcoded the values directly in the instructions, there
; is a reason for every value used here, you should read the Ralph Brown's
; Interrupt List to understand them... or ask me :D.
;
; Author: Ole (olelen <at> gmail <dot> com)
; Architecture: i386

; How to compile this?
; $ nasm -o <output_file> <this_file>
;
; How to run this?
; $ qemu <output_file>
;
; What is this shit supposed to do?
; Write "Hola mundo" in the screen with no other resources than BIOS (this is
; how bootloaders works).

cpu 386
use16

; BIOS is supposed to load this code in memory 0000:7c00
org    0x7c00
jmp 0x0000:main

main:   
        ; Set video memory.
        mov ax, 0xb800
        mov es, ax

        ; Clear the screen.
        mov ax, 0x06
        mov al, 0
        mov bx, 7
        mov cx, 0
        mov dh, 24
        mov dl, 79
        int 0x10

        ; Set the cursor.
        mov    ah, 0x02
        mov bh, 0
        mov    dx, 0
        int    0x10

        ; Write the message. DI will point to each character.
        mov    di, msg

write_msg:
        mov    ah, 0x0a
        mov    al, [di]
        mov bh, 0
        mov bl, 0x80
        mov cx, 1
        int    0x10
        cmp al, 0 ; BEWARE: Stop condition is finding character '\0' (the last one of `Hola mundo\0`).
        je exit
        inc di
       
        ; Get the cursor.
        mov ah, 0x03
        mov bh, 0
        int 0x10

        ; Increase cursor position and set it again.
        mov ah, 0x02
        mov bh, 0
        inc dl
        int 0x10

        jmp write_msg

; Finish the program by freezing the computer. BOTONAZO!!!
exit:
        hlt

; The message we will write.
msg        db    `Hola mundo\0`

; A bootable sector MUST has 0x55aa at its 511 and 512 bytes. We create an
; empty space up to 509 bytes and then initialize 0x55aa
resb    424
db        0x55, 0xaa

De las cosas más destacables es la directiva "org 0x7c00". La BIOS carga el código en esa posición de memoria, así que cualquier referencia a memoria que haga el programa (saltos por ejemplo) debe tener eso en cuenta. Con la directiva org le especificamos a nasm en qué posición de memoria empieza el programa y así genera todas las referencias en base a esa dirección. De resto pues no tiene mucha ciencia salvo los int 0x10. Esa es la forma de pedirle funcionalidad básica a la BIOS, para aprender de esto lo mejor es mirar la Ralph Brown's Interrupt List, donde se nos especifíca todas las cosas que puede hacer la BIOS (aunque es bastante duro de leer :s). Aquí básicamente le pedimos un scrolling de la pantalla, posicionar el cursor y escribir un caracter.

Se podría haber hecho algo más legible usando macros para clarificar pero se perdía la esencia del "quick and dirty" y era un requisito indispensable xDD.

Saludos.