{ --------------------------------------------------------------------------- }
{ @file }
{ Example on assembling a basic function returning `0x1337` in `rax`. }
{$APPTYPE CONSOLE}
{$TYPEDADDRESS ON}
{$LONGSTRINGS OFF}
{$WRITEABLECONST OFF}
{ --------------------------------------------------------------------------- }
program EncodeFromScratchA;
uses
ZyDis,
winapi
;
{$include Z900_zydis_inlines.inc}
{ --------------------------------------------------------------------------- }
procedure ExpectSuccess(const InStatus : ZyanStatus); cdecl;
const
EXIT_FAILURE = 1; { defined in C's stdlib }
var
Buffer : packed array[0..511] of char = #0;
begin
sprintf(Buffer, 'Something failed: 0x08X', InStatus);
if ZYAN_FAILED(InStatus) then
begin
writeln(Buffer);
halt(EXIT_FAILURE);
end;
end;
{ --------------------------------------------------------------------------- }
type
PPZyanU8 = ^PZyanU8;
procedure AppendInstruction
(
const InReq : PZydisEncoderRequest;
InoutBuffer : PPZyanU8;
InoutBuffer_length : PZyanUSize
); cdecl;
var
Instr_length : ZyanUSize = 0;
begin
Assert(InReq <> nil, 'InReg parameter cannot be nil');
Assert(InoutBuffer <> nil, 'InoutBuffer cannot be nil');
Assert(InoutBuffer_length <> nil, 'InoutBufferLength cannot be nil');
Instr_length := InoutBuffer_length^;
ExpectSuccess
(
ZydisEncoderEncodeInstruction
(
InReq, InoutBuffer^, @Instr_length
)
);
inc(InoutBuffer^, Instr_length);
dec(InoutBuffer_length^, Instr_length);
end;
{ --------------------------------------------------------------------------- }
function AssembleCode
(
InoutBuffer : PZyanU8;
InBuffer_length : ZyanUSize
)
: ZyanUSize; cdecl;
var
write_ptr : PZyanU8 = nil;
remaining_length : ZyanUSize = 0;
req : ZydisEncoderRequest = ();
begin
Assert(InoutBuffer <> nil, 'InoutBuffer cannot be nil');
Assert(InBuffer_length > 0, 'InBuffer_length must be greater than zero');
write_ptr := InoutBuffer;
remaining_length := InBuffer_length;
// Assemble `mov rax, 0x1337`.
RtlZeroMemory(@req, sizeof(req));
with req do
begin
mnemonic := ZYDIS_MNEMONIC_MOV;
machine_mode := ZYDIS_MACHINE_MODE_LONG_64;
operand_count := 2;
operands[0].&type := ZYDIS_OPERAND_TYPE_REGISTER;
operands[0].reg.value := ZYDIS_REGISTER_RAX;
operands[1].&type := ZYDIS_OPERAND_TYPE_IMMEDIATE;
operands[1].imm.u := $1337;
end;
AppendInstruction(@req, @write_ptr, @remaining_length);
// Assemble `ret`.
RtlZeroMemory(@req, sizeof(req));
with req do
begin
mnemonic := ZYDIS_MNEMONIC_RET;
machine_mode := ZYDIS_MACHINE_MODE_LONG_64;
end;
AppendInstruction(@req, @write_ptr, @remaining_length);
result := InBuffer_length - remaining_length;
end;
{ --------------------------------------------------------------------------- }
type
pbyte = ^byte;
const
page_size = $1000;
alloc_size = page_size * 2;
var
buffer : pointer = nil;
length : ZyanUSize = 0;
sprintf_buf : packed array[0..511] of char = #0;
i : integer;
type
TFunction = function () : ZyanU64; { will use the correct 64bit ABI }
var
TheFunction : TFunction;
begin
// Allocate 2 pages of memory. We won't need nearly as much, but it simplifies
// re-protecting the memory to RWX later.
buffer := VirtualAlloc(nil, alloc_size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
// Assemble our function.
length := AssembleCode(buffer, alloc_size);
// Print a hex-dump of the assembled code.
for i := 0 to length - 1 do
begin
sprintf(sprintf_buf, '%02X ', pbyte(buffer)[i]);
write(sprintf_buf);
end;
writeln;
{$ifdef WIN64} // original C source uses an O/S independent test
// ------------------------------------------------------------------------
// Align pointer to typical page size.
// this step is unnecessary because we used VirtualAlloc which aligns
// the allocation
// ------------------------------------------------------------------------
// Re-protect the heap region as RWX. Don't do this at home, kids!
// this step is unnecessary because we used VirtualAlloc with protection
// settings that already include execute, read and write
// ------------------------------------------------------------------------
// Create a function pointer for our buffer.
TheFunction := TFunction(buffer);
// ------------------------------------------------------------------------
{ call the function }
writeln;
sprintf(sprintf_buf,
'Return value of JITed code: 0x%016llX',
TheFunction()); { the JITed function }
writeln(sprintf_buf);
{$endif}
writeln;
writeln;
writeln('press ENTER/RETURN to end this program');
readln;
{ there does not seem to be a way to cause this message to be the last one }
{ in the Lazarus message window. }
{$ifndef WIN64}
{$message warning this program has limited operation as a 32bit program }
{$endif}
end.
//
// end of file
// ----------------------------------------------------------------------------