Naloga:

Napiši najmanjši možen program, ki izpiše vsaj 1 zaslon pesmice "Živel je mož, imel je psa, lepo ga je učil. Nekoč mu ukrade kos mesa, zato ga je ubil. Na grob napiše mu:"Živel je mož, imel je psa .... pod OS Linux ali BSD.

Ideja:

Če napišemo preprost program v C-ju, s print funkcijo je program, vsaj na mojem sistemu velik 8520 bajtov. Če se pri prevajanju znebimo executabla smo program oklestili za dobri 2000 bajtov oz. na natan;no 6248 bajtov. Ker je to skoraj največ kar lahko dosežemo v C-ju se presilimo na asembler.

Prvi poskus v asemblerju nam vrne kaj klavrne rezultate, saj je program velik 6376 bajtov. Vendar če program napišemo malo drugače, tako da ne uporabljamo knjižnic, ampak samo sistemske klice takoj pridemo na 800 bajtov, kar je pa že, kar precejšen napredek. Vendar ali gre še bolje?

Elf header

ELF (Executable and Linking Format) definira binarni format izvršilne datoteke datotek v Linuxu. Ko pokličeš izvršilno datoteko, mora OS vedeti, kako program pravilno naložiti v spomin, znati mora povezati dinamične knjižnice in vedeti, kje aj začne izvajati program. Vsaka ELF datoteka se začne s strukturo, ki ji rečemo ELF header. ELF header je dolg 52 bajtov in vsebuje informacije, ki opisujejo datoteko. Torej tu se skriva odgovor na naše vprašanje ali gre še bolje. Če ELF header prilagodimo svojim potrebam in odstranimo, kar je za nas nepomembno in nepotrebno lahko naredimo program še manjši.


0x7F ELF je "magic number", na podlagi katere lahko OS identificira da gre za ELF datoteko. 2 - 64 bitov, 1 - little endian, 1 - original ELF, 0 - ni važno na kateri platformi poganjamo.

                        ehdr:                                         
                                    db      0x7F, "ELF", 2, 1, 1, 0         ;   e_ident  
                    

e_type 2 nam predstavlja d agre za executable datoteko.
e_machine 0x3e nam pove da poganjamo na x86_64 platformi
e_version 1 nam pove, da gre za original elf

  
                            times 8 db      0                                       
                                    dw      2                               ;   e_type
                                    dw      0x3e                            ;   e_machine   
                                    dd      1                               ;   e_version
                    

Sledijo podatki o začetku programa njegovi velikosti in zastavicah:
e_entry _start - naslov zacetka programa
e_phoff in e_shoff predstavljata začetek programskega in section headerja
e_phentsize - velikost program headerja

                                    dd      _start                          ;   e_entry     
                                    dd      phdr - $$                       ;   e_phoff     
                                    dd      0                               ;   e_shoff     
                                    dd      0                               ;   e_flags     
                                    dw      ehdrsize                        ;   e_phentsize 
                                    dw      phdrsize                        ;   e_phentsize 
                          
                        ehdrsize    equ     $ - ehdr
                    

Program header

V program headerju so definrani segmenti, ki jih program uporablja v run-timu in nekatere druge informacije.

p_type - pove nam kakšne vrste segmentov opisuje proram header oz. kako moramo te podatke interpretirati.
p_offset - offset od začetka datoteke v katerem se nahaja prvi segment.
p_vaddr - viratualni naslov prvega bajta segmenta.
p_paddr - fizični naslov segmenta.
p_filesz - velikost datoteke v bajtih.
p_memsz - velikost v spominu v bajtih.
p_flags - zastavice. 5 = PT_SHLIB - rezervirana zastavica, brez definirane semantike.
p_align - kako je program poravnan v spominu.

  
                         phdr:                                              ;   Elf32_Phdr
                                    dd      1                               ;   p_type
                                    dd      0                               ;   p_offset
                                    dd      $$                              ;   p_vaddr
                                    dd      $$                              ;   p_paddr
                                    dd      filesize                        ;   p_filesz
                                    dd      filesize                        ;   p_memsz
                                    dd      5                               ;   p_flags
                                    dd      0x1000                          ;   p_align
                          
                        phdrsize      equ     $ - phdr   
                    

Program

Na naslov z labelo text si shranimo besedilo pesmi, ki jo moramo izpisovati, v length pa si shranimo dolzino pesmi.

  
    text   db "Zivel je moz, imel je psa lepo ga je redil.",10,"Nekoc mu ukrade kos mesa, zato ga je ubil."
    ,10,"Na vrt ga pokopal je, na grob mu napisal je:", 10

    length equ $-text
                    

V edx nalozimo dolžino programa, nato si v ecx shranimo pointer na besedilo, ki ga želimo izpisati. V ebx si shranimo konstanto s katero povemo kam želimo besedilo izpisovati, v našem primeru je to 1, ki nam pove da želimo izpisovati na zaslon. V eax naložimo write system komand in nato kličemo prekinite 0x80, ki pokliče kernel.

  
                        _start:
                            loop:
                                mov edx, length     ; arg3, length of string to print
                                mov ecx, text       ; arg2, pointer to string
                                mov ebx,1           ; arg1, where to write, screen
                                mov eax,4           ; write sysout command to int 80 hex
                                int 0x80            ; interrupt 80 hex, call kernel
                       
                                jmp loop

                    filesize      equ     $ - $$
                    

Prevajanje in poganjanje

-f bin - zastavica ki pove nasm, kaksnega tipa je izhod, v nasem primeru binarni.
-o - zastavica, ki ti omogoci, da das izhodni datoteki svoje ime.

  
                        nasm -f bin pesmica.asm -o pesmica
                    

Z chmod +x vsem uporabnikom dodaš še pravice za poganjanje tega programa
in ga z ukazom ./pesmica peženeš.
Če je vse v redu bi moral program nenehno izpisovati pesmico Živel je mož...

  
                        chmod +x pesmica
                        ./pesmica
                    

Zaključek

S tem, da sem naredil svoj ELF header in sem ohranil samo tisto minimalno, kar program potrebuje za delovanje sem uspel iz začetnih 8520 bajtov prišel na 232 bajtov, kar pa niti ni slabo :)
Velikost programa lahko tudi sami preverite z ukazom:

  
                        wc -c pesmica
                    

Viri