Method Wrapping

As explained in the previous section, Rust doesn't support the Borland Register convention. This means we have to write a wrapper in x86 assembly to make our cdecl functions work in Exanima.

To do this, we push all our borland registers into the stack. This makes it usable as a cdecl function. Because cdecl arguments are pushed right-to-left, this is ideal for us, since eax, edx, and ecx are our first 3 arguments, and are also now at the top of the stack.

We then call our function. x86 has a limitation that it can't call directly to pointers, however it can do indirect calls. Essentially, we make a pointer to our function pointer (this is called an indirect pointer), and call the memory contained in that indirect pointer.

To visualise this indirect function pointer in Rust, it's something like this.

fn my_fn() {...}
fn main() {
	let fn_ptr = my_fn as *mut c_void; // cast the function as a function pointer
	let fn_ptr_indirect = &fn_ptr as *mut *mut c_void; // make a reference to our function pointer
	call(*fn_ptr_indirect); // dereference the indirect pointer, and call the value.
}

Indirect calls in x86 look like this

call DWORD [fn_ptr_indirect]

A full function hook as described above would look something like this:

; push the borland registers into the stack, so we can use it in cdecl.
push ecx
push edx
push eax

; x86 can only call absolute-addressed methods indirectly from memory.
; So we pass a pointer to our function pointer.
call DWORD [&our_function_pointer] ; run our function

; if the function returns true, don't run the original function
; instead, jump to the "end" label
test eax, eax
jne end
; if the function returns false, do the following...

; pop the registers to restore the original stack
pop eax
pop edx
pop ecx

; We jump to the original function instead of using call.
; This preserves the current memory layout.
jmp DWORD [&original_function_pointer] ; jump to the original function
ret ; The original function should clean up the stack for us.
    ; So we just return without cleaning the stack.

	end:
pop eax
pop edx
pop ecx
retn n ; n is the number of bytes remaining in the stack.
       ; this can vary depending on the function being called.