The program is a Unicorn Engine x86 64-bit VM that can runs default program or a program that the user uploads. A program is a sequence of x86 64-bit machine language instructions.
Call win
which will print out the flag
In main
, depending on user input, run_vm
gets called with the default program or the user specified program:
run_vm
then sets up a uc_engine
that executes the program with this hook:
which calls hook_syscall
whenever UC_X86_INS_SYSCALL
is called.
More details on uc_add_hook arguments
From https://github.com/unicorn-engine/unicorn/blob/master/include/unicorn/unicorn.h/*
Register callback for a hook event.
The callback will be run when the hook event is hit.
@uc: handle returned by uc_open()
@hh: hook handle returned from this registration. To be used in uc_hook_del()
API
@type: hook type, refer to uc_hook_type enum
@callback: callback to be run when instruction is hit
@user_data: user-defined data. This will be passed to callback function in its
last argument @user_data
@begin: start address of the area where the callback is in effect (inclusive)
@end: end address of the area where the callback is in effect (inclusive)
NOTE 1: the callback is called only if related address is in range [@begin,
@end] NOTE 2: if @begin > @end, callback is called whenever this hook type is
triggered
@...: variable arguments (depending on @type)
NOTE: if @type = UC_HOOK_INSN, this is the instruction ID.
currently, only x86 in, out, syscall, sysenter, cpuid are supported.
NOTE: if @type = UC_HOOK_TCG_OPCODE, arguments are @opcode and @flags. See
@uc_tcg_op_code and @uc_tcg_op_flag for details.
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error).
*/
UNICORN_EXPORT
uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback,
void *user_data, uint64_t begin, uint64_t end, ...);
Hook type 2 is UC_HOOK_INSN
and instruction ID 399 is UC_X86_INS_SYSCALL
The callback function hook_syscall
checks the value of rax
and only allows read, write and open syscalls through sys_write
, sys_read
and sys_open
functions.
The write flow is hook_syscall
->sys_write
->do_write
->file_write
The read flow is hook_syscall
->sys_read
->do_read
->file_read
The open flow is hook_syscall
->sys_open
->do_open
Files are stored in a struct File
with length 0x240
Buffer Overflow
In file_write
, there is an extra byte copied from the source 'buffer' to the file content. The extra byte will overwrite the least significant byte of file size.
By changing the size of the file such that the file size >= size
, we can overwrite file addresses of file_read
and file_write
.
Information Leak
By having control of the file size, we can also treat the addresses of file_read
and file_write
as the content of the file. So we can leak the address of file_read
or file_write
and calculate the address of win
.