unit stub_manager;

{$mode objfpc}{$H+}

interface

uses
 Windows;

type
 TStubMemory=object
  protected
   type
    PNode=^TNode;
    TNode=record
     pNext:PNode;
     Stub_va:Pointer;
    end;
   Const
    VA_SIZE=16*1024;
   var
    pHead:PNode;
    Stub_va:Pointer;
    Stub_pos:Word;
   procedure Push(P:Pointer);
   function  Pop:Pointer;
  public
   Procedure Clear;
   procedure FinStub;
   function  NewStub(data:Pointer;size:Word):Pointer;
 end;

 TStubMemoryProc=object(TStubMemory)
  function NewNopStub(nid:QWORD;lib,proc:Pointer):Pointer;
 end;

implementation

procedure TStubMemory.Push(P:Pointer);
var
 Node:PNode;
begin
 Node:=AllocMem(SizeOf(TNode));
 Node^.Stub_va:=P;
 if (pHead=nil) then
 begin
  node^.pNext:=nil;
 end else
 begin
  node^.pNext:=pHead;
 end;
 pHead:=node;
end;

function TStubMemory.Pop:Pointer;
var
 Node:PNode;
begin
 Result:=nil;
 Node:=pHead;
 if (pHead<>nil) then
 begin
  pHead:=pHead^.pNext;
 end;
 if (Node<>nil) then
 begin
  Node^.pNext:=nil;
 end;
 if (Node<>nil) then
 begin
  Result:=Node^.Stub_va;
  FreeMem(Node);
 end;
end;

Procedure TStubMemory.Clear;
var
 P:Pointer;
begin
 P:=Pop;
 While (P<>nil) do
 begin
  VirtualFree(P,0,MEM_RELEASE);
  P:=Pop;
 end;
 if (Stub_va<>nil) then
 begin
  VirtualFree(Stub_va,0,MEM_RELEASE);
 end;
 Stub_va:=nil;
 Stub_pos:=0;
end;

procedure TStubMemory.FinStub;
var
 dummy:DWORD;
begin
 if (Stub_va<>nil) then
 begin
  Push(Stub_va);
  //VirtualProtect(Stub_va,VA_SIZE,PAGE_EXECUTE_READ,@dummy);
  FlushInstructionCache(GetCurrentProcess,Stub_va,VA_SIZE);
  Stub_va:=nil;
  Stub_pos:=0;
 end;
end;

function TStubMemory.NewStub(data:Pointer;size:Word):Pointer;
begin
 if ((Stub_pos+size)>VA_SIZE) then
   FinStub;

 if (Stub_va=nil) then
 begin
  Stub_va:=VirtualAlloc(nil,VA_SIZE,MEM_COMMIT or MEM_RESERVE,{PAGE_READWRITE}PAGE_EXECUTE_READWRITE);
 end;

 Result:=Stub_va+Stub_pos;
 Move(data^,Result^,size);
 Stub_pos:=Stub_pos+size;
end;

//


//52                       push   %rdx
//51                       push   %rcx
//48ba0100000000000000     movabs $0x1,%rdx
//48b90200000000000000     movabs $0x2,%rcx
//48b80300000000000000     movabs $0x3,%rax
//ffd0                     callq  *%rax
//4831c0                   xor    %rax,%rax
//59                       pop    %rcx
//5a                       pop    %rdx
//c3                       retq


Type
 Pnopstub=^Tnopstub;
 Tnopstub=packed record
  _push_rdx:Byte; // $52
  _push_rcx:Byte; // $51
  _movabs_rdx:array[0..1] of Byte; // $48 $ba
  _lib:Pointer;
  _movabs_rcx:array[0..1] of Byte; // $48 $B9
  _nid:QWord;
  _movabs_rax:array[0..1] of Byte; // $48 $B8
  _addr:Pointer;
  _call_rax:array[0..1] of Byte;   // $FF $D0
  _pop_rcx:Byte; // $59
  _pop_rdx:Byte; // $5A
  _ret:Byte;     // $C3
 end;

const
 _nopstub:Tnopstub=(
  _push_rdx:$52;
  _push_rcx:$51;
  _movabs_rdx:($48,$BA);
  _lib:nil;
  _movabs_rcx:($48,$B9);
  _nid:0;
  _movabs_rax:($48,$B8);
  _addr:nil;
  _call_rax:($FF,$D0);
  _pop_rcx:$59;
  _pop_rdx:$5A;
  _ret:$C3
 );

function TStubMemoryProc.NewNopStub(nid:QWORD;lib,proc:Pointer):Pointer;
var
 nopstub:Tnopstub;
begin
 nopstub:=_nopstub;
 nopstub._lib:=lib;
 nopstub._nid:=nid;
 nopstub._addr:=proc;
 Result:=NewStub(@nopstub,SizeOf(Tnopstub));
end;

end.

