The code for dynamically linked ELF is almost the same as for an ELF object file, with a few tiny differences. First of all, the formatter directive must tell the assembler to produce an executable, rather than an object file:
format ELF executable 3 ; The 3 may be omitted if on Linux
; Include this in order to be able to create import section
include 'linux_include/import32.inc'
; We have to specify the entry point for the executable
entry _start
The convenience structures we used in this chapter (crypto_functions and string) are still intact and should be placed in the file too. There is no strict definition as to where they should be placed exactly, but they should appear before they are used:
; The content of the data section is the same as in object file
; source. The section itself is declared in a different way (in
; fact, although, an ELF file is divided into sections, it is
; treated a bit differently when in memory - it is divided into
; segments)
segment readable writeable
buffe string 'Hello from dynamically linked ELF!', 0x0a
msg1 string 'Encrypted', 0x0a
msg2 string 'Decrypted', 0x0a
A new segment is introduced in order to improve the Flat Assembler's ELF support; one is the interpreter that contains the name of the loader to be used with the executable:
segment interpreter writeable
db '/lib/ld-linux.so.2',0
Another one is dynamic and serves as an import index. However, we are not going to declare this segment ourselves; instead, we will use two macros --one of them will create a list of the needed libraries and the other specifies procedures to be imported. In our case, it will look like this:
; In our example we only need to libraries - libc for
; printf() and exit() (and we will use exit() this time)
; and crypto_32.so for our cryptographic core.
needed\
'libc-2.19.so',\
'crypto_32.so'
; Then we specify requested procedures
import\
printf,\
exit,\
GetPointers
The rest of the code has only a few changes. First of all, the code section is declared as follows:
segment executable readable
_start:
This time all procedures are called indirectly:
push buffer
call [printf]
add esp, 4
call [GetPointers]
mov ebx, eax
virtual at ebx
funcs crypto_functions
end virtual
push 0 buffer msg2 msg1 buffer.length buffer
; All procedures are cdecl, so we have to adjust
; the stack pointer upon return from procedures
; with parameters
call [funcs.f_set_data_pointer]
add esp, 4
call [funcs.f_set_data_length]
add esp, 4
call [funcs.f_encrypt]
call [printf]
add esp, 4
call [funcs.f_decrypt]
call [printf]
add esp, 4
call [printf]
add esp, 4
call [exit]
The last two instructions we replace the two lines:
pop eax
ret
with:
cal [exit]
Save the file as so_lin.asm.
Now, you may build and run the newly created executable:
fasm so_lin.asm
./so_lin
If everything is done right, then you should see this:
