Let's get to business and write the code for our object file, obj_win.asm:
; First we need to tell the assembler that
; we expect an object file compatible with MS linker
format MS COFF
; Then specify the external API we need
; extrn means that the procedure is not in this file
; the 4 after the '@' specifies size of procedure parameters
; in bytes
; ExitProcess is a label and dword is its size
extrn '__imp__ExitProcess@4' as ExitProcess:dword
extrn '__imp__GetStdHandle@4' as GetStdHandle:dword
extrn '__imp__WriteConsoleA@20' as WriteConsole:dword
; And, of course, our "crypto API"
extrn '_GetPointers' as GetPointers:dword
; Define a constant for GetStdHandle()
STD_OUTPUT_HANDLE equal -11
; and a structure to ease the access to "crypto functions"
struc crypto_functions
{
.f_set_data_pointer dd ?
.f_set_data_length dd ?
.f_encrypt dd ?
.f_decrypt dd ?
}
; The following structure makes it a bit easier
; to manipulate strings and sizes thereof
struc string [s]
{
common
. db s
.length = $ - .
}
Before we implement our code, let's create the data section so that the code is easier to understand:
section '.data' data readable writeable
; We will store the STDOUT handle here
stdout dd ?
; This buffer contains the message we will operate on
buffer string 'Hello from object file!', 0x0a, 0x0d
; Progress messages
msg1 string 'Encrypted', 0x0a, 0x0d
msg2 string 'Decrypted', 0x0a, 0x0d
; This one is required by the WriteConsole procedure
bytesWritten dd ?
The data section is quite self-explanatory and we are now ready to write the code at last:
section '.text' code readable executable
; We need the entry point to be accessible to the linker,
; therefore we make it "public"
public _start
_start:
; The first step would be obtaining the STDOUT handle
push STD_OUTPUT_HANDLE
; Since we are linking against a DLL, the GetStdHandle
; label would refer to a location in the import section
; which the linker will create for us. Hence we make an
; indirect call
call [GetStdHandle]
; Store the handle
mov [stdout], eax
; Print the message
push 0 bytesWritten buffer.length buffer eax
call [WriteConsole]
; Let's play with encryption a bit
; First get the procedure pointers. Since the GetPointers()
; is in another object file, it would be statically linked,
; therefore we make a direct call
call GetPointers
; Store the pointer to the crypto_functions structure in EBX
mov ebx, eax
Remember the virtual directive?
We, programmers, are sometimes lazy people and like things to be convenient, especially when it comes to reading our own code a week after it was written, therefore, we would prefer to address our cryptographic procedures by name, rather than by an offset from the address of the crypto_functions structure, and this is when the virtual directive comes in handy allowing us to label the location pointed by the EBX register as shown in the following code snippet:
virtual at ebx
funcs crypto_functions
end virtual
The funcs is a virtual label that refers to the location pointed to by the ebx register, and it will be replaced with ebx in compile time. Any member of the crypto_functions structure referred by funcs will be replaced by its offset within the structure. Let's now set up the crypto engine and encrypt and then decrypt the message stored at buffer:
; Set the pointer to data and its length
push buffer
call [funcs.f_set_data_pointer] ; Equivalent to 'call [ebx]'
push buffer.length
call [funcs.f_set_data_length]
; We have to restore the stack pointer due to the
; fact that the above two procedures are in accordance
; with the cdecl calling convention
add esp, 8
; Encrypt the content of the buffer
call [funcs.f_encrypt]
; Print progress message
push 0 bytesWritten msg1.length msg1 [stdout]
call [WriteConsole]
; Decrypt the content of the buffer
call [funcs.f_decrypt]
; Print another progress message
push 0 bytesWritten msg2.length msg2 [stdout]
call [WriteConsole]
; Print the content of the buffer in order to verify
; decryption
push 0 bytesWritten buffer.length buffer [stdout]
call [WriteConsole]
; All is fine and we are free to exit
push 0
call [ExitProcess]