// ----------------------------------------------------------
// file: x86code.pas
// autor: Jens Kallup - paule32
// policy: (c) 2021 by kallup.net - non-profit
//
// desc: if scan/lexem, and parse ok, try to create x86
// 32-bit binary code for using under Windows 10 Pro.
// ----------------------------------------------------------
unit x86code;
interface
uses
Windows, Classes, SysUtils, Dialogs;
type TSize = type Cardinal;
procedure WritePEHeader;
procedure EmitCode(X: array of Byte; const Bytes: TSize);
implementation
uses
scanner;
type
TX86Register = (
regEAX, regECX, regEDX, regEBX, regESP, regEBP, regESI, regEDI,
regAX , regCX , regDX , regBX , regSP , regBP , regSI , regDI ,
regAL , regCL , regDL , regBL , regAH , regCH , regDH , regBH);
const
// MZ header (start of the file)
DOSStubHeader: Array[1..64] of Byte = (
$0e, $1f, $ba, $0e, $00, $b4, $09, $cd, $21, $b8, $01, $4c, $cd, $21, $54, $68,
$69, $73, $20, $70, $72, $6f, $67, $72, $61, $6d, $20, $63, $61, $6e, $6e, $6f,
$74, $20, $62, $65, $20, $72, $75, $6e, $20, $69, $6e, $20, $44, $4f, $53, $20,
$6d, $6f, $64, $65, $2e, $0d, $0a, $24, $00, $00, $00, $00, $00, $00, $00, $00
);
// PE header:
PEHeader: Array[0..3] of Byte = ($50, $45, $00, $00);
type
TImageDosHeader = record
e_magic : WORD; // $0000
e_cblp : WORD; // $0002
e_cp : WORD; // $0004
e_creloc : WORD; // $0006
e_cparhdr : WORD; // $0008
e_minalloc: WORD; // $000a
e_maxalloc: WORD; // $000c
e_ss : WORD; // $000e
e_sp : WORD; // $0010
e_csum : WORD; // $0012
e_ip : DWORD; // $0014
e_lfarlc : WORD; // $0018
e_ovno : WORD; // $001a
// new executable
e_res : Array[1..4] of Byte; // $001c
e_oemid : WORD; // $0020
e_oeminfo : WORD; // $0022
e_res2 : Array[1..22] of Char; //
e_lfanew : DWORD; //
end;
var
ImageDosHeader: TImageDosHeader;
type
TImageNtHeader = record
signature: Array [1..4] of Byte;
end;
var
ImageNTHeader: TImageNTHeader;
type
// file image header:
TImageFileHeader = record
machine : WORD ;
numbersOfSections : WORD ;
timeDateStamp : DWORD;
pointerToSymbolTable: DWORD;
numberOfSymbols : DWORD;
sizeOfOptionalHdr : WORD ;
characteristics : WORD ;
end;
var
ImageFileHeader: TImageFileHeader;
const
NUMBEROFSECTIONS = 3; // 1x .text
// 1x .rdata
// 1x .data
BIN_IMAGEBASE = $400000;
BIN_SECTIONALIGN = $1000;
BIN_FILEALIGN = $200;
type
TImageSectionHeader = record
name : Array[1..8] of Char; // .text .data .rdata
vsize : DWORD;
voffset : DWORD;
rsize : DWORD;
roffset : DWORD;
ptrReloc : DWORD;
ptrLines : DWORD;
numReloc : WORD ;
numLines : WORD ;
flags : DWORD;
end;
var
ImageSectionHeaderTEXT: TImageSectionHeader =
( name : '.text' + #0#0#0;
vsize : $00001000 ;
voffset: $00001000 * 1;
rsize : $00000200 ;
roffset: $00000200 * 1;
flags : $60000020);
ImageSectionHeaderRDATA: TImageSectionHeader =
( name : '.rdata' + #0#0;
vsize : $00001000 ;
voffset: $00001000 * 2;
rsize : $00000200 ;
roffset: $00000200 * 2;
flags : $40000040);
ImageSectionHeaderDATA: TImageSectionHeader =
( name : '.data' + #0#0#0;
vsize : $00001000 ;
voffset: $00001000 * 3;
rsize : $00000200 ;
roffset: $00000200 * 3;
flags : $C0000040);
type
TImageOptionalHeader32 = record
magic : WORD ;
majorLinkerVersion : BYTE ;
minorLinkerVersion : BYTE ;
sizeOfCode : DWORD;
sizeOfInitializedData : DWORD;
sizeOfUninitializedData : DWORD;
addressOfEntryPoint : DWORD;
baseOfCode : DWORD;
baseOfData : DWORD;
imageBase : DWORD;
sectionAlignment : DWORD;
fileAlignment : DWORD;
majorOperatingSystemVersion: WORD ;
minorOperatongSystemVersion: WORD ;
majorImageVersion : WORD ;
minorImageVersion : WORD ;
majorSubsystemVersion : WORD ;
minorSubsystemVersion : WORD ;
win32VersionValue : DWORD;
sizeOfImage : DWORD;
sizeOfHeaders : DWORD;
checkSum : DWORD;
subsystem : WORD ;
dllCharacteristics : WORD ;
sizeOfStackReserve : DWORD;
sizeOfStackCommit : DWORD;
sizeOfHeapReserve : DWORD;
sizeOfHeapCommit : DWORD;
loaderFlags : DWORD;
numberOfRVAandSizes : DWORD;
end;
var
ImageOptionalHeader32: TImageOptionalHeader32;
type
TImageDataDirectory16 = record
exportRVA : DWORD; // export table
exportSize : DWORD; // size
importsRVA : DWORD; // import table
importsSize : DWORD;
resourceVA : DWORD;
resourceSize : DWORD;
exception : DWORD;
exceptionSize : DWORD;
security : DWORD;
securitySize : DWORD;
relocation : DWORD;
relocationSize : DWORD;
debug : DWORD;
debugSize : DWORD;
Copyright : DWORD;
CopyrightSize : DWORD;
globalPtr : DWORD;
globalPtrSize : DWORD;
TLSTable : DWORD;
TLSTableSize : DWORD;
LoadConfig : DWORD;
LoadConfigSize : DWORD;
boundImport : DWORD;
boundImportSize : DWORD;
IAT : DWORD;
IATSize : DWORD;
delayImportsVA : DWORD;
delayImportsSize: DWORD;
COM : DWORD;
COMSize : DWORD;
reserved : DWORD;
reservedSize : DWORD;
end;
var
ImageDataDirectory16: TImageDataDirectory16;
const
IMAGE_FILE_MACHINE_I386 = $014c;
IMAGE_FILE_MACHINE_AMD64 = $8664;
IMAGE_FILE_EXECUTABLE_IMAGE = $2;
IMAGE_FILE_32BIT_MACHINE = $100;
IMAGE_NT_OPTIONAL_HDR32_MAGIC = $10b;
IMAGE_NT_OPTIONAL_HDR64_MAGIC = $20b;
IMAGE_SUBSYSTEM_WINDOWS_GUI = 3;
IMAGE_SUBSYSTEM_WINDOWS_CUI = 2;
type
TImageImportDescriptor = record
originalFirstThunk: DWORD;
timeStamp : DWORD;
forwarderChain : DWORD;
name1 : DWORD;
firstThunk : DWORD;
end;
// --------------------------------------------------------
// transpile byte code to string "streamCode" ...
// --------------------------------------------------------
procedure EmitCode(X: array of byte; const Bytes: TSize);
var
s: string;
begin
SetString(s, PChar(@X), Bytes);
streamSrcCode := streamSrcCode + s;
end;
// ----------------------------------------------------------
// write Windows 10 32-Bit PE file image ...
// ----------------------------------------------------------
procedure WritePEHeader;
var
stream: TStream;
p, len: Integer;
zero : Array [1..BIN_FILEALIGN] of Byte;
c : Char;
title : string;
caption: string;
begin
FillChar(ImageDosHeader , sizeof(TImageDosHeader ), 0);
FillChar(ImageNtHeader , sizeof(TImageNtHeader ), 0);
FillChar(ImageFileHeader , sizeof(TImageFileHeader ), 0);
FillChar(ImageOptionalHeader32, sizeof(TImageOptionalHeader32), 0);
FillChar(ImageDataDirectory16 , sizeof(TImageDataDirectory16 ), 0);
FillChar(zero,BIN_FILEALIGN,0);
// --------------------------------------------------------
// MZ DOS-stub header ...
// --------------------------------------------------------
with ImageDosHeader do begin
e_magic := $5a4d; { MZ }
e_cblp := $0090;
e_cp := $0003;
e_cparhdr := $0004;
e_minalloc := $0000;
e_maxalloc := $ffff;
e_ss := $0000;
e_sp := $0000;
e_lfanew := $0080; // PE (after MZ header)
end;
// --------------------------------------------------------
// PE Windows 32-Bit header:
// --------------------------------------------------------
with ImageNtHeader do begin
signature[1] := Byte('P');
signature[2] := Byte('E');
signature[3] := $00;
signature[4] := $00;
end;
with ImageFileHeader do begin
machine := IMAGE_FILE_MACHINE_I386;
numbersOfSections := NUMBEROFSECTIONS;
sizeOfOptionalHdr := $e0; //SIZEOFOPTIONALHEADER;
characteristics := IMAGE_FILE_EXECUTABLE_IMAGE or IMAGE_FILE_32BIT_MACHINE;
end;
with ImageOptionalHeader32 do begin
magic := IMAGE_NT_OPTIONAL_HDR32_MAGIC;
addressOfEntryPoint := $1000; // fixme
imageBase := BIN_IMAGEBASE;
sectionAlignment := BIN_SECTIONALIGN;
fileAlignment := BIN_FILEALIGN;
majorSubsystemVersion := 4 ;
sizeOfImage := 4 * BIN_SECTIONALIGN; // 3 * sections + header
sizeOfHeaders := $0200; //SIZEOFHEADERS;
Subsystem := IMAGE_SUBSYSTEM_WINDOWS_CUI;
numberOfRvaAndSizes := $10;
end;
// ImageDataDirectory16.importSize := BIN_SECTIONALIGN * 2; // fixme import table size
// --------------------------------------------------------
// application 32-Bit x86 binary code:
// --------------------------------------------------------
if Length(streamSrcCode) > 0 then begin
try
stream := TFileStream.Create('a.out', fmCreate);
except
on e: Exception do begin
ShowMessage('can not open output file.');
exit;
end;
end;
stream.WriteBuffer(ImageDosHeader , sizeof(TImageDosHeader));
stream.WriteBuffer(DosStubHeader , 64);
stream.WriteBuffer(ImageNtHeader , 4);
stream.WriteBuffer(ImageFileHeader , sizeof(TImageFileHeader ));
stream.WriteBuffer(ImageOptionalHeader32, sizeof(TImageOptionalHeader32));
stream.WriteBuffer(ImageDataDirectory16 , sizeof(TImageDataDirectory16 ));
stream.WriteBuffer(ImageSectionHeaderTEXT,sizeof(ImageSectionHeaderTEXT ));
stream.WriteBuffer(ImageSectionHeaderRDATA,sizeof(ImageSectionHeaderRDATA));
stream.WriteBuffer(ImageSectionHeaderDATA,sizeof(ImageSectionHeaderDATA ));
for p := 1 to 80 do
stream.WriteBuffer(zero[p],1);
for p := 1 to 4 do
stream.WriteBuffer(streamSrcCode[p],1);
// data section:
title := 'simple PE executable.' + #$0;
caption := 'Hello World !' + #$0;
len := 22; for p := 1 to len do stream.WriteBuffer(title [p], 1);
len := 14; for p := 1 to len do stream.WriteBuffer(caption[p], 1);
len := stream.Position;
if len < 2048 then begin
len := (2048 - len);
for p := 1 to len do
stream.WriteBuffer(zero,1);
end;
// stream.WriteBuffer(zero,BIN_FILEALIGN);
stream.Free;
end else begin
ShowMessage('warning: no application code.');
end;
ShowMessage('file a.out written.');
end;
end.