Just as with the object file source code for Windows, as always we will begin by telling the assembler what kind of output we are expecting, which procedures are public, and which are external:
format ELF
; As we want GCC to take care of all the startup code
; we will call our procedure "main" instead of _start,
; otherwise we would be using LD instead of GCC and
; would have to specify all runtime libraries manually.
public main
; The following function is linked from libc
extrn printf
; And this one is from our crypto library
extrn GetPointers
Then we proceed with convenience macro definition, and the suggestion is to put convenience macros into a separate include file so that they may be painlessly used with different code without the need to rewrite them:
struc crypto_functions
{
.f_set_data_pointer dd ?
.f_set_data_length dd ?
.f_encrypt dd ?
.f_decrypt dd ?
}
struc string [s]
{
common
. db s
.length = $ - .
.terminator db 0
}
The data section is almost the same as in the case of the object file for Windows, except that we do not need a variable to hold the stdout handle:
section '.data' writeable
buffer string 'Hello from ELF linked from objects!', 0x0a
msg1 string 'Encrypted', 0x0a
msg2 string 'Decrypted', 0x0a
And, at last, the code section. It is logically the same code with the only difference being the use of printf() instead of WriteConsoleA(), in which case the printf() implementation in libc will make all the arrangements and invoke a SYS_write Linux system call for us. As we are, from GCC's point of view, only implementing the main() function, we do not have to terminate the process ourselves, hence there is no exit() procedure imported --the runtime code is automatically added and linked, and GCC will do all the rest, while we simply return from main():
section '.text' executable
; Remember that we are using GCC for linking, hence the name is
; main, rather than _start
main:
; Print the content of the buffer to stdout
; As all procedures (except crypto procedures) would be
; statically linked, we are using direct calls
push buffer
call printf
; Restore stack as printf() is a cdecl function
add esp, 4
; Get pointers to cryptographic procedures
call GetPointers
mov ebx, eax
; We will use the same trick to ease our access to cryptography
; procedures by defining a virtual structure
virtual at ebx
funds crypto_functions
end virtual
; Right now we will push parameters for all subsequent procedure
; calls onto the stack in reverse order (parameter for the last
; call is pushed first
push 0 buffer msg2 msg1 buffer.length buffer
; Set crypto library's data pointer
; Crypto procedures are not available at link time, hence not
; statically linked. Instead we obtain pointers thereof and this
; is the reason for indirect call
call [funcs.f_set_data_pointer]
; Restore stack
add esp, 4
; Set size of the data buffer
call [funcs.f_set_data_length]
add esp, 4
; Encrypt the buffer. As this procedure has no parameter, there
; is no reason to do anything to stack following this call
call [funcs.f_encrypt]
; Print msg1
call printf
add esp, 4
; Decrypt the buffer back
call [funcs.f_decrypt]
; Print msg2
call printf
add esp, 4
; Print the content of the buffer to ensure correct decryption
call printf
add esp, 4
; All is done, so we may safely exit
pop eax
ret