unicorn/bindings/pascal/examples/x86.lpr
Coldzer0 c1267811e2
Pascal/Delphi binding
* Pascal/Delphi binding

Pascal/Delphi language binding

* update credits

Backports commit 84220d83601ba929c56b9e1fadd1686e02cbb93c from unicorn.
2019-02-28 16:36:56 -05:00

1001 lines
32 KiB
ObjectPascal

{
FreePascal/Delphi bindings for the UnicornEngine Emulator Engine .
Copyright(c) 2018 Coldzer0 .
License : GPLv2 .
}
program x86;
{$IFDEF FPC}
{$MODE Delphi}
{$ENDIF}
{$ifdef MSWINDOWS}
{$apptype CONSOLE}
{$endif}
uses
SysUtils,
Unicorn_dyn,
UnicornConst,
X86Const;
const
// code to be emulated .
X86_CODE32: array[0..6] of Byte = ($41, $4a,$66,$0f,$ef,$c1, $00); // INC ecx; DEC edx ; PXOR xmm0, xmm1 ;
X86_CODE32_JUMP: array[0..8] of Byte = ($eb, $02, $90, $90, $90, $90, $90, $90, $00); // jmp 4; nop; nop; nop; nop; nop; nop ;
X86_CODE32_LOOP: array[0..4] of Byte = ($41, $4a, $eb, $fe, $00); // INC ecx; DEC edx; JMP self-loop
X86_CODE32_MEM_WRITE: array[0..8] of Byte = ($89, $0d, $aa, $aa, $aa, $aa, $41, $4a, $00); // mov [0xaaaaaaaa], ecx; INC ecx; DEC edx ;
X86_CODE32_MEM_READ: array[0..8] of Byte = ($8b, $0d, $aa, $aa, $aa, $aa, $41, $4a, $00); // mov ecx,[0xaaaaaaaa]; INC ecx; DEC edx ;
X86_CODE32_JMP_INVALID: array[0..6] of Byte = ($e9, $e9, $ee, $ee, $41, $4a, $00); // JMP outside; INC ecx; DEC edx ;
X86_CODE32_INOUT: array[0..7] of Byte = ($41, $E4, $3F, $4a, $E6, $46, $43, $00); // INC ecx; IN AL, 0x3f; DEC edx; OUT 0x46, AL; INC ebx ;
X86_CODE32_INC : array[0..1] of byte = ($40,$00); // INC eax .
X86_CODE64: array[0..75] of Byte = (
$41, $BC, $3B, $B0, $28, $2A, $49, $0F, $C9, $90, $4D, $0F, $AD, $CF, $49, $87, $FD, $90, $48, $81,
$D2, $8A, $CE, $77, $35, $48, $F7, $D9, $4D, $29, $F4, $49, $81, $C9, $F6, $8A, $C6, $53, $4D, $87,
$ED, $48, $0F, $AD, $D2, $49, $F7, $D4, $48, $F7, $E1, $4D, $19, $C5, $4D, $89, $C5, $48, $F7, $D6,
$41, $B8, $4F, $8D, $6B, $59, $4D, $87, $D0, $68, $6A, $1E, $09, $3C, $59, $00);
X86_CODE16: array[0..2] of Byte = ($00, $00, $00); // add byte ptr [bx + si], al
X86_CODE64_SYSCALL: array[0..2] of Byte = ($0f, $05, $00); // SYSCALL
// memory address where emulation starts
ADDRESS = $1000000;
// callback for tracing basic blocks
procedure HookBlock(uc: uc_engine; address: UInt64; size: Cardinal; user_data: Pointer); cdecl;
begin
WriteLn(Format('>>> Tracing basic block at 0x%x, block size = 0x%x', [address, size]));
end;
// callback for tracing instruction
procedure HookCode(uc: uc_engine; address: UInt64; size: Cardinal; user_data: Pointer); cdecl;
var
eflags: integer;
begin
WriteLn(Format('>>> Tracing instruction at 0x%x, instruction size = 0x%x', [address, size]));
uc_reg_read(uc, UC_X86_REG_EFLAGS, @eflags);
WriteLn(Format('>>> --- EFLAGS is 0x%x', [eflags]));
end;
// callback for tracing instruction
procedure HookCode64(uc: uc_engine; address: UInt64; size: Cardinal; user_data: Pointer); cdecl;
var
rip: UInt64;
begin
WriteLn(Format('>>> Tracing instruction at 0x%x, instruction size = 0x%x', [address, size]));
uc_reg_read(uc, UC_X86_REG_RIP, @rip);
WriteLn(Format('>>> --- RIP is 0x%x', [rip]));
end;
function HookMemInvalid(uc: uc_engine; _type: uc_mem_type; address: UInt64; size: Cardinal; value: Int64; user_data: Pointer): LongBool; cdecl;
begin
case _type of
UC_MEM_WRITE_UNMAPPED:
begin
WriteLn(Format('>>> Missing memory is being WRITE at 0x%x, data size = %u, data value = 0x%x', [address, size, value]));
// map this memory in with 2MB in size
uc_mem_map(uc, $aaaa0000, 2 * 1024*1024, UC_PROT_ALL);
// return true to indicate we want to continue
Result := true;
end
else
begin
// return false to indicate we want to stop emulation
Result := false;
end;
end;
end;
procedure HookMem64(uc: uc_engine; _type: uc_mem_type; address: UInt64; size: Cardinal; value: Int64; user_data: Pointer); cdecl;
begin
case _type of
UC_MEM_READ:
begin
WriteLn(Format('>>> Memory is being READ at 0x%x, data size = %u', [address, size]));
end;
UC_MEM_WRITE:
begin
WriteLn(Format('>>> Memory is being WRITE at 0x%x, data size = %u, data value = 0x%x', [address, size, value]));
end;
end;
end;
// callback for IN instruction (X86).
// this returns the data read from the port
function HookIn(uc: uc_engine; port: UInt32; size: integer; user_data: Pointer): Uint32; cdecl;
var
eip: UInt32;
begin
uc_reg_read(uc, UC_X86_REG_EIP, @eip);
WriteLn(Format('--- reading from port 0x%x, size: %u, address: 0x%x', [port, size, eip]));
case size of
1:
begin
// read 1 byte to AL
Result := $f1;
end;
2:
begin
// read 2 byte to AX
Result := $f2;
end;
4:
begin
// read 4 byte to EAX
Result := $f4;
end;
else
begin
// should never reach this
Result := 0;
end;
end;
end;
// callback for OUT instruction (X86).
procedure HookOut(uc: uc_engine; port: UInt32; size: integer; value: UInt32; user_data: Pointer); cdecl;
var
tmp, eip: UInt32;
begin
uc_reg_read(uc, UC_X86_REG_EIP, @eip);
WriteLn(Format('--- writing to port 0x%x, size: %u, value: 0x%x, address: 0x%x', [port, size, value, eip]));
// confirm that value is indeed the value of AL/AX/EAX
case size of
1:
begin
uc_reg_read(uc, UC_X86_REG_AL, @tmp);
end;
2:
begin
uc_reg_read(uc, UC_X86_REG_AX, @tmp);
end;
4:
begin
uc_reg_read(uc, UC_X86_REG_EAX, @tmp);
end;
else
begin
// should never reach this
Exit;
end;
end;
WriteLn(Format('--- register value = 0x%x', [tmp]));
end;
// callback for SYSCALL instruction (X86).
procedure HookSyscall(uc: uc_engine; user_data: Pointer); cdecl;
var
rax: UInt64;
begin
uc_reg_read(uc, UC_X86_REG_RAX, @rax);
if (rax = $100) then begin
rax := $200;
uc_reg_write(uc, UC_X86_REG_RAX, @rax);
end else
WriteLn(Format('ERROR: was not expecting rax=0x%x in syscall', [rax]));
end;
procedure TestI386;
var
uc: uc_engine;
err: uc_err;
tmp: UInt32;
trace1, trace2: uc_hook;
r_ecx, r_edx: integer;
r_xmm0,r_xmm1 : array [0..1] of UInt64;
begin
r_ecx := $1234; // ECX register
r_edx := $7890; // EDX register
r_xmm0[0] := $08090a0b0c0d0e0f; r_xmm0[1] := $0001020304050607;
r_xmm1[0] := {%H-}$8090a0b0c0d0e0f0; r_xmm1[1] := $0010203040506070;
WriteLn('Emulate i386 code');
// Initialize emulator in X86-32bit mode
err := uc_open(UC_ARCH_X86, UC_MODE_32, uc);
if (err <> UC_ERR_OK) then begin
WriteLn(Format('Failed on uc_open() with error returned: %u', [err]));
Exit;
end;
// map 2MB memory for this emulation
uc_mem_map(uc, ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL);
// write machine code to be emulated to memory
if (uc_mem_write_(uc, ADDRESS, @X86_CODE32, SizeOf(X86_CODE32) - 1) <> UC_ERR_OK) then begin
WriteLn('Failed to write emulation code to memory, quit!');
Exit;
end;
// initialize machine registers
uc_reg_write(uc, UC_X86_REG_ECX, @r_ecx);
uc_reg_write(uc, UC_X86_REG_EDX, @r_edx);
uc_reg_write(uc, UC_X86_REG_XMM0, @r_xmm0);
uc_reg_write(uc, UC_X86_REG_XMM1, @r_xmm1);
// tracing all basic blocks with customized callback
uc_hook_add(uc, trace1, UC_HOOK_BLOCK, @HookBlock, nil, 1, 0,[]);
// tracing all instruction by having @begin > @end
uc_hook_add(uc, trace2, UC_HOOK_CODE, @HookCode, nil, 1, 0,[]);
// emulate machine code in infinite time
err := uc_emu_start(uc, ADDRESS, ADDRESS + SizeOf(X86_CODE32) - 1, 0, 0);
if (err <> UC_ERR_OK) then begin
WriteLn(Format('Failed on uc_emu_start() with error returned %u: %s', [err, uc_strerror(err)]));
end;
// now print out some registers
WriteLn('>>> Emulation done. Below is the CPU context');
uc_reg_read(uc, UC_X86_REG_ECX, @r_ecx);
uc_reg_read(uc, UC_X86_REG_EDX, @r_edx);
uc_reg_read(uc, UC_X86_REG_XMM0, @r_xmm0);
WriteLn(Format('>>> ECX = 0x%x', [r_ecx]));
WriteLn(Format('>>> EDX = 0x%x', [r_edx]));
WriteLn(Format('>>> XMM0 = 0x%s%s', [IntToHex(r_xmm0[1],16),IntToHex(r_xmm0[0],16)]));
// read from memory
err := uc_mem_read_(uc, ADDRESS, @tmp, SizeOf(tmp));
if (err = UC_ERR_OK) then begin
WriteLn(Format('>>> Read 4 bytes from [0x%x] = 0x%x', [ADDRESS, tmp]));
end else begin
WriteLn(Format('>>> Failed to read 4 bytes from [0x%x], err = %u: %s', [ADDRESS, err, uc_strerror(err)]));
end;
uc_close(uc);
end;
procedure test_i386_map_ptr();
var
uc: uc_engine;
err: uc_err;
tmp: UInt32;
trace1, trace2: uc_hook;
mem : Pointer;
r_ecx, r_edx: integer;
r_xmm0,r_xmm1 : array [0..1] of UInt64;
begin
r_ecx := $1234; // ECX register
r_edx := $7890; // EDX register
r_xmm0[0] := $08090a0b0c0d0e0f; r_xmm0[1] := $0001020304050607;
r_xmm1[0] := {%H-}$8090a0b0c0d0e0f0; r_xmm1[1] := $0010203040506070;
WriteLn('===================================');
WriteLn('Emulate i386 code - use uc_mem_map_ptr()');
// Initialize emulator in X86-32bit mode
err := uc_open(UC_ARCH_X86, UC_MODE_32, uc);
if (err <> UC_ERR_OK) then begin
WriteLn(Format('Failed on uc_open() with error returned: %u', [err]));
Exit;
end;
mem := AllocMem(2 * 1024 * 1024);
if mem = nil then
begin
Writeln('Failed to Allocmem');
uc_close(uc);
exit;
end;
err := uc_mem_map_ptr(uc,ADDRESS,2 * 1024 * 1024,UC_PROT_ALL,mem);
if err <> UC_ERR_OK then
begin
WriteLn(Format('Failed on uc_mem_map_ptr() with error returned: %u - %s', [err,uc_strerror(err)]));
FreeMem(mem,2 * 1024 * 1024);
uc_close(uc);
Exit;
end;
Move(X86_CODE32,mem^,SizeOf(X86_CODE32)-1);
if CompareMem(mem,@X86_CODE32,SizeOf(X86_CODE32)-1) <> true then
begin
Writeln('Failed to write emulation code to memory, quit!');
Freemem(mem,2 * 1024 * 1024);
uc_close(uc);
exit;
end;
uc_reg_write(uc, UC_X86_REG_ECX, @r_ecx);
uc_reg_write(uc, UC_X86_REG_EDX, @r_edx);
uc_reg_write(uc, UC_X86_REG_XMM0, @r_xmm0);
uc_reg_write(uc, UC_X86_REG_XMM1, @r_xmm1);
// tracing all basic blocks with customized callback
uc_hook_add(uc, trace1, UC_HOOK_BLOCK, @HookBlock, nil, 1, 0,[]);
// tracing all instruction by having @begin > @end .
uc_hook_add(uc, trace2, UC_HOOK_CODE, @HookCode, nil, 1, 0,[]);
// emulate machine code in infinite time
err := uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32) - 1, 0, 0);
if err <> UC_ERR_OK then
WriteLn(Format('Failed on uc_emu_start() with error returned %u: %s', [err, uc_strerror(err)]));
Writeln('>>> Emulation done. Below is the CPU context');
uc_reg_read(uc, UC_X86_REG_ECX, @r_ecx);
uc_reg_read(uc, UC_X86_REG_EDX, @r_edx);
uc_reg_read(uc, UC_X86_REG_XMM0, @r_xmm0);
WriteLn(Format('>>> ECX = 0x%x', [r_ecx]));
WriteLn(Format('>>> EDX = 0x%x', [r_edx]));
WriteLn(Format('>>> XMM0 = 0x%s%s', [IntToHex(r_xmm0[1],16),IntToHex(r_xmm0[0],16)]));
// read from memory
err := uc_mem_read_(uc, ADDRESS, @tmp, SizeOf(tmp));
if (err = UC_ERR_OK) then begin
WriteLn(Format('>>> Read 4 bytes from [0x%x] = 0x%x', [ADDRESS, tmp]));
end else begin
WriteLn(Format('>>> Failed to read 4 bytes from [0x%x], err = %u: %s', [ADDRESS, err, uc_strerror(err)]));
end;
Freemem(mem,2 * 1024 * 1024);
uc_close(uc);
end;
procedure TestI386Jump;
var
uc: uc_engine;
err: uc_err;
trace1, trace2: uc_hook;
begin
WriteLn('===================================');
WriteLn('Emulate i386 code with jump');
// Initialize emulator in X86-32bit mode
err := uc_open(UC_ARCH_X86, UC_MODE_32, uc);
if (err <> UC_ERR_OK) then begin
WriteLn(Format('Failed on uc_open() with error returned: %u', [err]));
Exit;
end;
// map 2MB memory for this emulation
uc_mem_map(uc, ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL);
// write machine code to be emulated to memory
if (uc_mem_write_(uc, ADDRESS, @X86_CODE32_JUMP, SizeOf(X86_CODE32_JUMP) - 1) <> UC_ERR_OK) then begin
WriteLn('Failed to write emulation code to memory, quit!');
Exit;
end;
// tracing 1 basic block with customized callback
uc_hook_add(uc, trace1, UC_HOOK_BLOCK, @HookBlock, nil, ADDRESS, ADDRESS,[]);
// tracing 1 instruction at ADDRESS
uc_hook_add(uc, trace2, UC_HOOK_CODE, @HookCode, nil, ADDRESS, ADDRESS,[]);
// emulate machine code in infinite time
err := uc_emu_start(uc, ADDRESS, ADDRESS + SizeOf(X86_CODE32_JUMP) - 1, 0, 0);
if (err <> UC_ERR_OK) then begin
WriteLn(Format('Failed on uc_emu_start() with error returned %u: %s', [err, uc_strerror(err)]));
end;
WriteLn('>>> Emulation done.');
uc_close(uc);
end;
procedure TestI386Loop;
var
uc: uc_engine;
err: uc_err;
r_ecx, r_edx: integer;
begin
r_ecx := $1234; // ECX register
r_edx := $7890; // EDX register
WriteLn('===================================');
WriteLn('Emulate i386 code that loop forever');
// Initialize emulator in X86-32bit mode
err := uc_open(UC_ARCH_X86, UC_MODE_32, uc);
if (err <> UC_ERR_OK) then begin
WriteLn(Format('Failed on uc_open() with error returned: %u', [err]));
Exit;
end;
// map 2MB memory for this emulation
uc_mem_map(uc, ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL);
// write machine code to be emulated to memory
if (uc_mem_write_(uc, ADDRESS, @X86_CODE32_LOOP, SizeOf(X86_CODE32_LOOP) - 1) <> UC_ERR_OK) then begin
WriteLn('Failed to write emulation code to memory, quit!');
Exit;
end;
// initialize machine registers
uc_reg_write(uc, UC_X86_REG_ECX, @r_ecx);
uc_reg_write(uc, UC_X86_REG_EDX, @r_edx);
// emulate machine code in 2 seconds, so we can quit even
// if the code loops
err := uc_emu_start(uc, ADDRESS, ADDRESS + SizeOf(X86_CODE32_LOOP) - 1, 2 * UC_SECOND_SCALE, 0);
if (err <> UC_ERR_OK) then begin
WriteLn(Format('Failed on uc_emu_start() with error returned %u: %s', [err, uc_strerror(err)]));
end;
// now print out some registers
WriteLn('>>> Emulation done. Below is the CPU context');
uc_reg_read(uc, UC_X86_REG_ECX, @r_ecx);
uc_reg_read(uc, UC_X86_REG_EDX, @r_edx);
WriteLn(Format('>>> ECX = 0x%x', [r_ecx]));
WriteLn(Format('>>> EDX = 0x%x', [r_edx]));
uc_close(uc);
end;
procedure TestI386InvalidMemRead;
var
uc: uc_engine;
err: uc_err;
trace1, trace2: uc_hook;
r_ecx, r_edx: integer;
begin
r_ecx := $1234; // ECX register
r_edx := $7890; // EDX register
WriteLn('===================================');
WriteLn('Emulate i386 code that read from invalid memory');
// Initialize emulator in X86-32bit mode
err := uc_open(UC_ARCH_X86, UC_MODE_32, uc);
if (err <> UC_ERR_OK) then begin
WriteLn(Format('Failed on uc_open() with error returned: %u', [err]));
Exit;
end;
// map 2MB memory for this emulation
uc_mem_map(uc, ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL);
// write machine code to be emulated to memory
if (uc_mem_write_(uc, ADDRESS, @X86_CODE32_MEM_READ, SizeOf(X86_CODE32_MEM_READ) - 1) <> UC_ERR_OK) then begin
WriteLn('Failed to write emulation code to memory, quit!');
uc_close(uc);
Exit;
end;
// initialize machine registers
uc_reg_write(uc, UC_X86_REG_ECX, @r_ecx);
uc_reg_write(uc, UC_X86_REG_EDX, @r_edx);
// tracing all basic blocks with customized callback
uc_hook_add(uc, trace1, UC_HOOK_BLOCK, @HookBlock, nil, 1, 0,[]);
// tracing all instruction by having @begin > @end
uc_hook_add(uc, trace2, UC_HOOK_CODE, @HookCode, nil, 1, 0,[]);
err := uc_emu_start(uc, ADDRESS, ADDRESS + SizeOf(X86_CODE32_MEM_READ) - 1, 0, 0);
if (err <> UC_ERR_OK) then begin
WriteLn(Format('Failed on uc_emu_start() with error returned %u: %s', [err, uc_strerror(err)]));
end;
// now print out some registers
WriteLn('>>> Emulation done. Below is the CPU context');
uc_reg_read(uc, UC_X86_REG_ECX, @r_ecx);
uc_reg_read(uc, UC_X86_REG_EDX, @r_edx);
WriteLn(Format('>>> ECX = 0x%x', [r_ecx]));
WriteLn(Format('>>> EDX = 0x%x', [r_edx]));
uc_close(uc);
end;
procedure TestI386InvalidMemWrite;
var
uc: uc_engine;
err: uc_err;
trace1, trace2, trace3: uc_hook;
r_ecx, r_edx: integer;
tmp: UInt32;
begin
r_ecx := $1234; // ECX register
r_edx := $7890; // EDX register
WriteLn('===================================');
WriteLn('Emulate i386 code that write to invalid memory');
// Initialize emulator in X86-32bit mode
err := uc_open(UC_ARCH_X86, UC_MODE_32, uc);
if (err <> UC_ERR_OK) then begin
WriteLn(Format('Failed on uc_open() with error returned: %u', [err]));
Exit;
end;
// map 2MB memory for this emulation
uc_mem_map(uc, ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL);
// write machine code to be emulated to memory
if (uc_mem_write_(uc, ADDRESS, @X86_CODE32_MEM_WRITE, SizeOf(X86_CODE32_MEM_WRITE) - 1) <> UC_ERR_OK) then begin
WriteLn('Failed to write emulation code to memory, quit!');
Exit;
end;
// initialize machine registers
uc_reg_write(uc, UC_X86_REG_ECX, @r_ecx);
uc_reg_write(uc, UC_X86_REG_EDX, @r_edx);
// tracing all basic blocks with customized callback
uc_hook_add(uc, trace1, UC_HOOK_BLOCK, @HookBlock, nil, 1, 0,[]);
// tracing all instruction by having @begin > @end
uc_hook_add(uc, trace2, UC_HOOK_CODE, @HookCode, nil, 1, 0,[]);
// intercept invalid memory events
uc_hook_add(uc, trace3, UC_HOOK_MEM_READ_UNMAPPED or UC_HOOK_MEM_WRITE_UNMAPPED, @HookMemInvalid, nil,1,0,[]);
err := uc_emu_start(uc, ADDRESS, ADDRESS + SizeOf(X86_CODE32_MEM_WRITE) - 1, 0, 0);
if (err <> UC_ERR_OK) then begin
WriteLn(Format('Failed on uc_emu_start() with error returned %u: %s', [err, uc_strerror(err)]));
end;
// now print out some registers
WriteLn('>>> Emulation done. Below is the CPU context');
uc_reg_read(uc, UC_X86_REG_ECX, @r_ecx);
uc_reg_read(uc, UC_X86_REG_EDX, @r_edx);
WriteLn(Format('>>> ECX = 0x%x', [r_ecx]));
WriteLn(Format('>>> EDX = 0x%x', [r_edx]));
// read from memory
err := uc_mem_read_(uc, $aaaaaaaa, @tmp, SizeOf(tmp));
if (err = UC_ERR_OK) then
WriteLn(Format('>>> Read 4 bytes from [0x%x] = 0x%x', [$aaaaaaaa, tmp]))
else
WriteLn(Format('>>> Failed to read 4 bytes from [0x%x]', [$aaaaaaaa]));
err := uc_mem_read_(uc, $ffffffaa, @tmp, SizeOf(tmp));
if (err = UC_ERR_OK) then
WriteLn(Format('>>> Read 4 bytes from [0x%x] = 0x%x', [$ffffffaa, tmp]))
else
WriteLn(Format('>>> Failed to read 4 bytes from [0x%x]', [$ffffffaa]));
uc_close(uc);
end;
procedure TestI386JumpInvalid;
var
uc: uc_engine;
err: uc_err;
trace1, trace2: uc_hook;
r_ecx, r_edx: integer;
begin
r_ecx := $1234; // ECX register
r_edx := $7890; // EDX register
WriteLn('===================================');
WriteLn('Emulate i386 code that jumps to invalid memory');
// Initialize emulator in X86-32bit mode
err := uc_open(UC_ARCH_X86, UC_MODE_32, uc);
if (err <> UC_ERR_OK) then begin
WriteLn(Format('Failed on uc_open() with error returned: %u', [err]));
Exit;
end;
// map 2MB memory for this emulation
uc_mem_map(uc, ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL);
// write machine code to be emulated to memory
if (uc_mem_write_(uc, ADDRESS, @X86_CODE32_JMP_INVALID, SizeOf(X86_CODE32_JMP_INVALID) - 1) <> UC_ERR_OK) then begin
WriteLn('Failed to write emulation code to memory, quit!');
uc_close(uc);
Exit;
end;
// initialize machine registers
uc_reg_write(uc, UC_X86_REG_ECX, @r_ecx);
uc_reg_write(uc, UC_X86_REG_EDX, @r_edx);
// tracing all basic blocks with customized callback
uc_hook_add(uc, trace1, UC_HOOK_BLOCK, @HookBlock, nil, 1, 0,[]);
// tracing all instruction by having @begin > @end
uc_hook_add(uc, trace2, UC_HOOK_CODE, @HookCode, nil, 1, 0,[]);
err := uc_emu_start(uc, ADDRESS, ADDRESS + SizeOf(X86_CODE32_JMP_INVALID) - 1, 0, 0);
if (err <> UC_ERR_OK) then begin
WriteLn(Format('Failed on uc_emu_start() with error returned %u: %s', [err, uc_strerror(err)]));
end;
// now print out some registers
WriteLn('>>> Emulation done. Below is the CPU context');
uc_reg_read(uc, UC_X86_REG_ECX, @r_ecx);
uc_reg_read(uc, UC_X86_REG_EDX, @r_edx);
WriteLn(Format('>>> ECX = 0x%x', [r_ecx]));
WriteLn(Format('>>> EDX = 0x%x', [r_edx]));
uc_close(uc);
end;
procedure TestI386Inout;
var
uc: uc_engine;
err: uc_err;
trace1, trace2, trace3, trace4: uc_hook;
r_ecx, r_edx: integer;
begin
r_ecx := $1234; // ECX register
r_edx := $7890; // EDX register
WriteLn('===================================');
WriteLn('Emulate i386 code with IN/OUT instructions');
// Initialize emulator in X86-32bit mode
err := uc_open(UC_ARCH_X86, UC_MODE_32, uc);
if (err <> UC_ERR_OK) then begin
WriteLn(Format('Failed on uc_open() with error returned: %u', [err]));
Exit;
end;
// map 2MB memory for this emulation
uc_mem_map(uc, ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL);
// write machine code to be emulated to memory
if (uc_mem_write_(uc, ADDRESS, @X86_CODE32_INOUT, SizeOf(X86_CODE32_INOUT) - 1) <> UC_ERR_OK) then begin
WriteLn('Failed to write emulation code to memory, quit!');
Exit;
end;
// initialize machine registers
uc_reg_write(uc, UC_X86_REG_ECX, @r_ecx);
uc_reg_write(uc, UC_X86_REG_EDX, @r_edx);
// tracing all basic blocks with customized callback
uc_hook_add(uc, trace1, UC_HOOK_BLOCK, @HookBlock, nil, 1, 0,[]);
// tracing all instruction by having @begin > @end
uc_hook_add(uc, trace2, UC_HOOK_CODE, @HookCode, nil, 1, 0,[]);
// uc IN instruction
uc_hook_add(uc, trace3, UC_HOOK_INSN, @HookIn, nil, 1,0,[UC_X86_INS_IN]);
// uc OUT instruction
uc_hook_add(uc, trace4, UC_HOOK_INSN, @HookOut, nil, 1,0,[UC_X86_INS_OUT]);
err := uc_emu_start(uc, ADDRESS, ADDRESS + SizeOf(X86_CODE32_INOUT) - 1, 0, 0);
if (err <> UC_ERR_OK) then begin
WriteLn(Format('Failed on uc_emu_start() with error returned %u: %s', [err, uc_strerror(err)]));
end;
// now print out some registers
WriteLn('>>> Emulation done. Below is the CPU context');
uc_reg_read(uc, UC_X86_REG_ECX, @r_ecx);
uc_reg_read(uc, UC_X86_REG_EDX, @r_edx);
WriteLn(Format('>>> ECX = 0x%x', [r_ecx]));
WriteLn(Format('>>> EDX = 0x%x', [r_edx]));
uc_close(uc);
end;
procedure test_i386_context_save();
var
uc: uc_engine;
context : uc_context;
err: uc_err;
r_eax : integer;
begin
r_eax := 1; // EAX register
WriteLn('===================================');
WriteLn('Emulate i386 code - Save/restore CPU context in opaque blob');
// Initialize emulator in X86-32bit mode
err := uc_open(UC_ARCH_X86, UC_MODE_32, uc);
if (err <> UC_ERR_OK) then begin
WriteLn(Format('Failed on uc_open() with error returned: %u', [err]));
Exit;
end;
uc_mem_map(uc,ADDRESS,8 * 1024 , UC_PROT_ALL);
// write machine code to be emulated to memory
if (uc_mem_write_(uc, ADDRESS, @X86_CODE32_INC, SizeOf(X86_CODE32_INC) - 1) <> UC_ERR_OK) then begin
WriteLn('Failed to write emulation code to memory, quit!');
uc_close(uc);
Exit;
end;
// initialize machine registers
uc_reg_write(uc, UC_X86_REG_EAX, @r_eax);
// emulate machine code in infinite time
writeln('>>> Running emulation for the first time');
err := uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32_INC) - 1, 0, 0);
if (err <> UC_ERR_OK) then begin
WriteLn(Format('Failed on uc_emu_start() with error returned %u: %s', [err, uc_strerror(err)]));
end;
Writeln('>>> Emulation done. Below is the CPU context');
uc_reg_read(uc, UC_X86_REG_EAX, @r_eax);
WriteLn(Format('>>> EAX = 0x%x', [r_eax]));
Writeln('>>> Saving CPU context');
err := uc_context_alloc(uc,context);
if (err <> UC_ERR_OK) then begin
WriteLn(Format('Failed on uc_context_alloc() with error returned %u : %s', [err, uc_strerror(err)]));
exit;
end;
err := uc_context_save(uc, context);
if (err <> UC_ERR_OK) then begin
WriteLn(Format('Failed on uc_context_save() with error returned %u : %s', [err, uc_strerror(err)]));
exit;
end;
Writeln('>>> Running emulation for the second time');
err := uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32_INC) - 1, 0, 0);
if (err <> UC_ERR_OK) then begin
WriteLn(Format('Failed on uc_emu_start() with error returned %u: %s', [err, uc_strerror(err)]));
end;
Writeln('>>> Emulation done. Below is the CPU context');
uc_reg_read(uc, UC_X86_REG_EAX, @r_eax);
WriteLn(Format('>>> EAX = 0x%x', [r_eax]));
err := uc_context_restore(uc, context);
if (err <> UC_ERR_OK) then begin
WriteLn(Format('Failed on uc_context_restore() with error returned %u: %s', [err, uc_strerror(err)]));
exit;
end;
Writeln('>>> CPU context restored. Below is the CPU context');
uc_reg_read(uc, UC_X86_REG_EAX, @r_eax);
WriteLn(Format('>>> EAX = 0x%x', [r_eax]));
err := uc_free(context);
if (err <> UC_ERR_OK) then begin
WriteLn(Format('Failed on uc_free() with error returned %u: %s', [err, uc_strerror(err)]));
exit;
end;
uc_close(uc);
end;
procedure TestX86_64;
var
uc: uc_engine;
err: uc_err;
trace1, trace2, trace3, trace4: uc_hook;
rax, rbx, rcx, rdx, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15, rsp: UInt64;
begin
rax := $71f3029efd49d41d;
rbx := $d87b45277f133ddb;
rcx := $ab40d1ffd8afc461;
rdx := $919317b4a733f01;
rsi := $4c24e753a17ea358;
rdi := $e509a57d2571ce96;
r8 := $ea5b108cc2b9ab1f;
r9 := $19ec097c8eb618c1;
r10 := $ec45774f00c5f682;
r11 := $e17e9dbec8c074aa;
r12 := $80f86a8dc0f6d457;
r13 := $48288ca5671c5492;
r14 := $595f72f6e4017f6e;
r15 := $1efd97aea331cccc;
rsp := ADDRESS + $200000;
WriteLn('Emulate x86_64 code');
// Initialize emulator in X86-64bit mode
err := uc_open(UC_ARCH_X86, UC_MODE_64, uc);
if (err <> UC_ERR_OK) then begin
WriteLn(Format('Failed on uc_open() with error returned: %u', [err]));
Exit;
end;
// map 2MB memory for this emulation
uc_mem_map(uc, ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL);
// write machine code to be emulated to memory
if (uc_mem_write_(uc, ADDRESS, @X86_CODE64, SizeOf(X86_CODE64) - 1) <> UC_ERR_OK) then begin
WriteLn('Failed to write emulation code to memory, quit!');
Exit;
end;
// initialize machine registers
uc_reg_write(uc, UC_X86_REG_RSP, @rsp);
uc_reg_write(uc, UC_X86_REG_RAX, @rax);
uc_reg_write(uc, UC_X86_REG_RBX, @rbx);
uc_reg_write(uc, UC_X86_REG_RCX, @rcx);
uc_reg_write(uc, UC_X86_REG_RDX, @rdx);
uc_reg_write(uc, UC_X86_REG_RSI, @rsi);
uc_reg_write(uc, UC_X86_REG_RDI, @rdi);
uc_reg_write(uc, UC_X86_REG_R8, @r8);
uc_reg_write(uc, UC_X86_REG_R9, @r9);
uc_reg_write(uc, UC_X86_REG_R10, @r10);
uc_reg_write(uc, UC_X86_REG_R11, @r11);
uc_reg_write(uc, UC_X86_REG_R12, @r12);
uc_reg_write(uc, UC_X86_REG_R13, @r13);
uc_reg_write(uc, UC_X86_REG_R14, @r14);
uc_reg_write(uc, UC_X86_REG_R15, @r15);
// tracing all basic blocks with customized callback
uc_hook_add(uc, trace1, UC_HOOK_BLOCK, @HookBlock, nil, 1, 0,[]);
// tracing all instruction by having @begin > @end
uc_hook_add(uc, trace2, UC_HOOK_CODE, @HookCode64, nil, ADDRESS, ADDRESS + 20,[]);
// tracing all memory WRITE access (with @begin > @end)
uc_hook_add(uc, trace3, UC_HOOK_MEM_WRITE, @HookMem64, nil, 1, 0,[]);
// tracing all memory READ access (with @begin > @end)
uc_hook_add(uc, trace4, UC_HOOK_MEM_READ, @HookMem64, nil, 1, 0,[]);
err := uc_emu_start(uc, ADDRESS, ADDRESS + SizeOf(X86_CODE64) - 1, 0, 0);
if (err <> UC_ERR_OK) then begin
WriteLn(Format('Failed on uc_emu_start() with error returned %u: %s', [err, uc_strerror(err)]));
end;
// now print out some registers
WriteLn('>>> Emulation done. Below is the CPU context');
uc_reg_read(uc, UC_X86_REG_RAX, @rax);
uc_reg_read(uc, UC_X86_REG_RBX, @rbx);
uc_reg_read(uc, UC_X86_REG_RCX, @rcx);
uc_reg_read(uc, UC_X86_REG_RDX, @rdx);
uc_reg_read(uc, UC_X86_REG_RSI, @rsi);
uc_reg_read(uc, UC_X86_REG_RDI, @rdi);
uc_reg_read(uc, UC_X86_REG_R8, @r8);
uc_reg_read(uc, UC_X86_REG_R9, @r9);
uc_reg_read(uc, UC_X86_REG_R10, @r10);
uc_reg_read(uc, UC_X86_REG_R11, @r11);
uc_reg_read(uc, UC_X86_REG_R12, @r12);
uc_reg_read(uc, UC_X86_REG_R13, @r13);
uc_reg_read(uc, UC_X86_REG_R14, @r14);
uc_reg_read(uc, UC_X86_REG_R15, @r15);
WriteLn(Format('>>> RAX = 0x%.16x', [rax]));
WriteLn(Format('>>> RBX = 0x%.16x', [rbx]));
WriteLn(Format('>>> RCX = 0x%.16x', [rcx]));
WriteLn(Format('>>> RDX = 0x%.16x', [rdx]));
WriteLn(Format('>>> RSI = 0x%.16x', [rsi]));
WriteLn(Format('>>> RDI = 0x%.16x', [rdi]));
WriteLn(Format('>>> R8 = 0x%.16x', [r8]));
WriteLn(Format('>>> R9 = 0x%.16x', [r9]));
WriteLn(Format('>>> R10 = 0x%.16x', [r10]));
WriteLn(Format('>>> R11 = 0x%.16x', [r11]));
WriteLn(Format('>>> R12 = 0x%.16x', [r12]));
WriteLn(Format('>>> R13 = 0x%.16x', [r13]));
WriteLn(Format('>>> R14 = 0x%.16x', [r14]));
WriteLn(Format('>>> R15 = 0x%.16x', [r15]));
uc_close(uc);
end;
procedure TestX86_64Syscall;
var
uc: uc_engine;
err: uc_err;
trace1: uc_hook;
rax: UInt64;
begin
rax := $100;
WriteLn('===================================');
WriteLn('Emulate x86_64 code with "syscall" instruction');
// Initialize emulator in X86-64bit mode
err := uc_open(UC_ARCH_X86, UC_MODE_64, uc);
if (err <> UC_ERR_OK) then begin
WriteLn(Format('Failed on uc_open() with error returned: %u', [err]));
Exit;
end;
// map 2MB memory for this emulation
uc_mem_map(uc, ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL);
// write machine code to be emulated to memory
if (uc_mem_write_(uc, ADDRESS, @X86_CODE64_SYSCALL, SizeOf(X86_CODE64_SYSCALL) - 1) <> UC_ERR_OK) then begin
WriteLn('Failed to write emulation code to memory, quit!');
Exit;
end;
// hook interrupts for syscall
uc_hook_add(uc, trace1, UC_HOOK_INSN, @HookSyscall, nil, 1 , 0 , [UC_X86_INS_SYSCALL]);
// initialize machine registers
uc_reg_write(uc, UC_X86_REG_RAX, @rax);
// emulate machine code in infinite time (last param = 0), or when
// finishing all the code.
err := uc_emu_start(uc, ADDRESS, ADDRESS + SizeOf(X86_CODE64_SYSCALL) - 1, 0, 0);
if (err <> UC_ERR_OK) then begin
WriteLn(Format('Failed on uc_emu_start() with error returned %u: %s', [err, uc_strerror(err)]));
end;
// now print out some registers
WriteLn('>>> Emulation done. Below is the CPU context');
uc_reg_read(uc, UC_X86_REG_RAX, @rax);
WriteLn(Format('>>> RAX = 0x%x', [rax]));
uc_close(uc);
end;
procedure TestX86_16;
var
uc: uc_engine;
err: uc_err;
tmp: Word;
eax, ebx, esi: UInt32;
begin
eax := 7;
ebx := 5;
esi := 6;
WriteLn('Emulate x86 16-bit code');
// Initialize emulator in X86-16bit mode
err := uc_open(UC_ARCH_X86, UC_MODE_16, uc);
if (err <> UC_ERR_OK) then begin
WriteLn(Format('Failed on uc_open() with error returned: %u', [err]));
Exit;
end;
// map 8KB memory for this emulation
uc_mem_map(uc, 0, 8 * 1024, UC_PROT_ALL);
// write machine code to be emulated to memory
if (uc_mem_write_(uc, 0, @X86_CODE16, SizeOf(X86_CODE16) - 1) <> UC_ERR_OK) then begin
WriteLn('Failed to write emulation code to memory, quit!');
Exit;
end;
// initialize machine registers
uc_reg_write(uc, UC_X86_REG_EAX, @eax);
uc_reg_write(uc, UC_X86_REG_EBX, @ebx);
uc_reg_write(uc, UC_X86_REG_ESI, @esi);
// emulate machine code in infinite time (last param = 0), or when
// finishing all the code.
err := uc_emu_start(uc, 0, SizeOf(X86_CODE16) - 1, 0, 0);
if (err <> UC_ERR_OK) then begin
WriteLn(Format('Failed on uc_emu_start() with error returned %u: %s', [err, uc_strerror(err)]));
end;
// now print out some registers
WriteLn('>>> Emulation done. Below is the CPU context');
err := uc_mem_read_(uc, 11, @tmp, 1);
if (err = UC_ERR_OK) then
WriteLn(Format('>>> Read 1 bytes from [0x%x] = 0x%x', [11, tmp]))
else
WriteLn(Format('>>> Failed to read 1 bytes from [0x%x]', [11]));
uc_close(uc);
end;
begin
if ParamCount > 0 then begin
if (ParamStr(1) = '-32') then begin
TestI386;
test_i386_map_ptr;
test_i386_context_save;
TestI386Inout;
TestI386Jump;
TestI386Loop;
TestI386InvalidMemRead;
TestI386InvalidMemWrite;
TestI386JumpInvalid;
end;
if (ParamStr(1) = '-64') then begin
TestX86_64;
TestX86_64Syscall;
end;
if (ParamStr(1) = '-16') then begin
TestX86_16;
end;
end else
WriteLn(#10'Syntax: SampleX86 <-16|-32|-64>'#10);
end.