Tag: fasm

  • Chastity’s Source for ELF 32-bit executable creation

    I wrote an example program using a custom made ELF-32 header using data declaration statements. I wrote many comments which reference the official specification. This was an exercise both in programming and also Technical Reading and Writing. I had to read enough of the specification PDF file to understand what I was doing. I then tried to write descriptive comments that at the very least I would understand when I need to remind myself how this format is created.

    FASM Assembler 32-bit ELF Hello World

    ;Chastity's Source for ELF 32-bit executable creation
    ;
    ;All data as defined in this file is based off of the specification of the ELF file format.
    ;I first looked at the type of file created by FASM's "format ELF executable" directive.
    ;It is great that FASM can create an executable file automatically. (Thanks Tomasz Grysztar, you are a true warrior!)
    
    ;However, I wanted to understand the format for theoretical use in other assemblers like NASM.
    ;However, the syntax of FASM and NASM is still different enough that this will work only in FASM.
    ;I do have a NASM version in a separate file in my repository though.
    
    ;The Github repository with the spec I used is here.
    ;<https://github.com/xinuos/gabi>
    ;And this is the wikipedia article which linked me to the specification document
    ;<https://en.wikipedia.org/wiki/Executable_and_Linkable_Format>
    
    ;This file contains a raw binary ELF32 header created using db,dw,dd commands.
    ;After that, it proceeds to assemble a real "Hello World!" program
    
    ;Header for 32 bit ELF executable (with comments based on specification)
    
    db 0x7F,"ELF" ;ELFMAGIC: 4 bytes that identify this as an ELF file. The magic numbers you could say.
    db 1          ;EI_CLASS: 1=32-bit 2=64-bit
    db 1          ;EI_DATA: The endianness of the data. 1=ELFDATA2LSB 2=ELFDATA2MSB For Intel x86 this is always 1 as far as I know.
    db 1          ;EI_VERSION: 1=EV_CURRENT (ELF identity version 1) (which is current at time of specification Version 4.2 I was using)
    db 9 dup 0    ;padding zeros to bring us to address 0x10
    dw 2          ;e_type: 2=ET_EXEC (executable instead of object file)
    dw 3          ;e_machine : 3=EM_386 (Intel 80386) 0x3E (AMD x86-64 architecture)
    dd 1          ;e_version: 1=EV_CURRENT (ELF object file version.)
    
    p_vaddr  =  0x8048000 ;the absolute base address where the file is loaded into memory
    e_entry  =  0x8048054 ;program starts running at this address (right after header)
    
    dd e_entry    ;e_entry: the address at which the program starts running
    dd 0x34       ;e_phoff: where in the file the program header offset is
    db 8 dup 0    ;e_shoff and e_flags are unused in this example,therefore all zeros
    dw 0x34       ;e_ehsize: size of the ELF header
    dw 0x20       ;e_phentsize: size of program header which happens after ELF header
    dw 1          ;e_phnum: How many program headers. Only 1 in this case
    dw 0x28       ;e_shentsize: Size of a section header
    dw 0          ;e_shnum number of section headers
    dw 0          ;e_shstrndx: section header string index (not used here)
    
    ;That is the end of the 0x34 byte (52 bytes decimal) ELF header. Sadly, this is not the end and a program header is also required (what drunk person made this format?)
    
    dd 1          ;p_type: 1=PT_LOAD
    dd 0          ;p_offset: Base address from file (zero)
    dd p_vaddr    ;p_vaddr: Virtual address in memory where the file will be.
    dd p_vaddr    ;p_paddr: Physical address. Same as previous
    
    ;The file_size variable I have defined uses some trickery to get the size of the file.
    ;An EOF constant (End Of File) is defined at the end of the program code
    ;By subtracting the program virtual address from that address,
    ;I get the actual number of bytes of this entire program
    
    file_size equ EOF-p_vaddr ;Place the actual size of the file using NASM address constants
    
    dd file_size  ;p_filesz: Size of file image of the segment. Must be equal to the file size or greater
    dd file_size  ;p_memsz: Size of memory image of the segment, which may be equal to or greater than file image.
    
    dd 7           ;p_flags: permission flags: 7=4(Read)+2(Write)+1(Execute)
    dd 0x1000      ;p_align; Alignment (same page alignment that FASM uses of 4096 bytes)
    
    ;important FASM directives
    
    use32          ;tell assembler that 32 bit code is being used
    org e_entry    ;origin of new code begins here
    
    ;Now, the actual hello world program
    
    mov eax,4   ;invoke SYS_WRITE (kernel opcode 4 on 32 bit systems)
    mov ebx,1   ;write to the STDOUT file
    mov ecx,msg ;pointer/address of string to write
    mov edx,13  ;number of bytes to write
    int 80h
    
    mov eax,1   ;function SYS_EXIT (kernel opcode 1 on 32 bit systems)
    mov ebx,0   ;return 0 status on exit - 'No Errors'
    int 80h
    
    msg db 'Hello World!',0Ah,0
    
    EOF  =  $ ; define a label for the end of file. This is used in the ELF header
    
    ;To Assemble and run this program on Linux, you can use the following makefile.
    ;You can also disassemble it with ndisasm because the exact address of code is known
    
    ;main-fasm:
    ;	fasm ELF-32-hello.asm
    ;	chmod +x ELF-32-hello.bin
    ;	./ELF-32-hello.bin
    ;ndisasm:
    ;	ndisasm -b 32 -o 0x8048054 -e 0x54 ELF-32-hello.bin
    

    NASM Assembler 32-bit ELF Hello World

    ;Chastity's Source for ELF 32-bit executable creation
    ;
    ;All data as defined in this file is based off of the specification of the ELF file format.
    ;I first looked at the type of file created by FASM's "format ELF executable" directive.
    ;It is great that FASM can create an executable file automatically. (Thanks Tomasz Grysztar, you are a true warrior!)
    
    ;However, I wanted to understand the format for theoretical use in other assemblers like NASM.
    ;Therefore, what you see here is a complete Hello World program that should work within NASM
    ;to create an executable file without using a linker. It worked perfectly on my machine running Debian Linux and NASM version 2.16.01.
    
    ;The Github repository with the spec I used is here.
    ;<https://github.com/xinuos/gabi>
    ;And this is the wikipedia article which linked me to the specification document
    ;<https://en.wikipedia.org/wiki/Executable_and_Linkable_Format>
    
    ;This file contains a raw binary ELF32 header created using db,dw,dd commands.
    ;After that, it proceeds to assemble a real "Hello World!" program
    
    ;Header for 32 bit ELF executable (with comments based on specification)
    
    db 0x7F,"ELF" ;ELFMAGIC: 4 bytes that identify this as an ELF file. The magic numbers you could say.
    db 1          ;EI_CLASS: 1=32-bit 2=64-bit
    db 1          ;EI_DATA: The endianness of the data. 1=ELFDATA2LSB 2=ELFDATA2MSB For Intel x86 this is always 1 as far as I know.
    db 1          ;EI_VERSION: 1=EV_CURRENT (ELF identity version 1) (which is current at time of specification Version 4.2 I was using)
    db 9 dup 0    ;padding zeros to bring us to address 0x10
    dw 2          ;e_type: 2=ET_EXEC (executable instead of object file)
    dw 3          ;e_machine : 3=EM_386 (Intel 80386) 0x3E (AMD x86-64 architecture)
    dd 1          ;e_version: 1=EV_CURRENT (ELF object file version.)
    
    p_vaddr equ 0x8048000 ;the absolute base address where the file is loaded into memory
    e_entry equ 0x8048054 ;program starts running at this address (right after header)
    
    dd e_entry    ;e_entry: the address at which the program starts running
    dd 0x34       ;e_phoff: where in the file the program header offset is
    db 8 dup 0    ;e_shoff and e_flags are unused in this example,therefore all zeros
    dw 0x34       ;e_ehsize: size of the ELF header
    dw 0x20       ;e_phentsize: size of program header which happens after ELF header
    dw 1          ;e_phnum: How many program headers. Only 1 in this case
    dw 0x28       ;e_shentsize: Size of a section header
    dw 0          ;e_shnum number of section headers
    dw 0          ;e_shstrndx: section header string index (not used here)
    
    ;That is the end of the 0x34 byte (52 bytes decimal) ELF header. Sadly, this is not the end and a program header is also required (what drunk person made this format?)
    
    dd 1          ;p_type: 1=PT_LOAD
    dd 0          ;p_offset: Base address from file (zero)
    dd p_vaddr    ;p_vaddr: Virtual address in memory where the file will be.
    dd p_vaddr    ;p_paddr: Physical address. Same as previous
    
    ;The file_size variable I have defined uses some trickery to get the size of the file.
    ;An EOF constant (End Of File) is defined at the end of the program code
    ;By subtracting the program virtual address from that address,
    ;I get the actual number of bytes of this entire program
    
    file_size equ EOF-p_vaddr ;Place the actual size of the file using NASM address constants
    
    dd file_size  ;p_filesz: Size of file image of the segment. Must be equal to the file size or greater
    dd file_size  ;p_memsz: Size of memory image of the segment, which may be equal to or greater than file image.
    
    dd 7           ;p_flags: permission flags: 7=4(Read)+2(Write)+1(Execute)
    dd 0x1000      ;p_align; Alignment (same page alignment that FASM uses of 4096 bytes)
    
    ;important NASM directives
    
    use32          ;tell assembler that 32 bit code is being used
    org p_vaddr    ;origin of new code begins here
    
    ;Now, the actual hello world program
    
    mov eax,4   ;invoke SYS_WRITE (kernel opcode 4 on 32 bit systems)
    mov ebx,1   ;write to the STDOUT file
    mov ecx,msg ;pointer/address of string to write
    mov edx,13  ;number of bytes to write
    int 80h
    
    mov eax,1   ;function SYS_EXIT (kernel opcode 1 on 32 bit systems)
    mov ebx,0   ;return 0 status on exit - 'No Errors'
    int 80h
    
    msg db 'Hello World!',0Ah,0
    
    EOF equ $ ; define a label for the end of file. This is used in the ELF header
    
    ;To Assemble and run this program on Linux, you can use the following makefile.
    ;You can also disassemble it with ndisasm because the exact address of code is known
    
    ;main-nasm:
    ;	nasm ELF-32-hello.asm
    ;	chmod +x ELF-32-hello
    ;	./ELF-32-hello
    ;ndisasm:
    ;	ndisasm -b 32 -o 0x8048054 -e 0x54 ELF-32-hello
    

    I made a repository for examples like this. Others may want to understand the header used on Linux systems.

    https://github.com/chastitywhiterose/ELF