Patching a running process code

The first feature we have to do is to allow on-the-fly change of the assembler code of a process.

When an executable is mapped in RAM, the memory page corresponding to the process code is marked as Read Only, in order to avoid any security attack from the outside. Only the current process can patch its own code.

We'll need to override a pointer value in the code memory. The following function, defined in SynCommons.pas will handle it:

procedure PatchCodePtrUInt(Code: PPtrUInt; Value: PtrUInt);
var RestoreProtection, Ignore: DWORD;
  if VirtualProtect(Code, SizeOf(Code^), PAGE_EXECUTE_READWRITE, RestoreProtection) then
    Code^ := Value;
    VirtualProtect(Code, SizeOf(Code^), RestoreProtection, Ignore);
    FlushInstructionCache(GetCurrentProcess, Code, SizeOf(Code^));

The VirtualProtect low-level Windows API is called to force the corresponding memory to be written (via the PAGE_EXECUTE_READWRITE flag), then modify the corresponding pointer value, then the original memory page protection setting (should be PAGE_EXECUTE_READ) is restored.

According to the MSDN documentation, we'd need to flush the CPU operation cache in order to force the modified code to be read on next access.

Per-class variable in the VMT

The VMT is the Virtual-Method Table, i.e. a Table which defines every Delphi class. In fact, every Delphi class is defined internally by its VMT, contains a list of pointers to the class’s virtual methods. This VMT also contains non-method values, which are class-specific information at negative offsets:

Name Offset Description
vmtSelfPtr –76 points back to the beginning of the table
vmtIntfTable –72 TObject.GetInterfaceTable method value
vmtAutoTable –68 class’s automation table (deprecated)
vmtInitTable –64 reference-counted fields type information
vmtTypeInfo –60 the associated RTTI type information
vmtFieldTable –56 field addresses
vmtMethodTable –52 method names
vmtDynamicTable –48 dynamic methods table
vmtClassName –44 PShortString of the class name
vmtInstanceSize –40 bytes needed by one class Instance
vmtParent –36 parent VMT

We'll use the trick as detailed in in order to use the vmtAutoTable deprecated entry in the VMT.
This slot entry was used in Delphi 2 only for implementing Automation. Later version of Delphi (our goal) won't use it any more.
But the slot is still here, ready for being used by the framework.

We'll therefore be able to store a pointer to the TSQLRecordProperties instance corresponding to a TSQLRecord class, which will be retrieved as such:

class function TSQLRecord.RecordProps: TSQLRecordProperties;
  if Self<>nil then begin
    result := PPointer(PtrInt(Self)+vmtAutoTable)^;
    if result=nil then
      result := PropsCreate(self);
  end else
    result := nil;

Since this method is called a lot of time by our ORM, there is an asm-optimized version of the pascal code above:

class function TSQLRecord.RecordProps: TSQLRecordProperties;
  or eax,eax
  jz @null
  mov edx,[eax+vmtAutoTable]
  or edx,edx
  jz PropsCreate
  mov eax,edx

Most of the time, this method will be executed very quickly. In fact, the PropsCreate global function is called only once, i.e. the first time this RecordProps method is called.

The TSQLRecordProperties instance is therefore created within this function:

function PropsCreate(aTable: TSQLRecordClass): TSQLRecordProperties;
begin // private sub function makes the code faster in most case
  if not aTable.InheritsFrom(TSQLRecord) then
    // invalid call
    result := nil else begin
    // create the properties information from RTTI
    result := TSQLRecordProperties.Create(aTable);
    // store the TSQLRecordProperties instance into AutoTable unused VMT entry
    // register to the internal garbage collection (avoid memory leak)

The GarbageCollector is a global TObjectList, which is used to store some global instances, living the whole process time, just like our TSQLRecordProperties values.

A per-class TSQLRecordProperties was made therefore available for each kind of TSQLRecord class.

Feedback and comments are welcome on our forum.