FPPS4/sys/md/md_systm_reserve.pas

797 lines
21 KiB
Plaintext

unit md_systm_reserve;
{$mode ObjFPC}{$H+}
{$CALLING SysV_ABI_CDecl}
interface
uses
sysutils,
windows,
ntapi;
type
t_md_map_reserve_result=record
error:DWORD;
id :DWORD;
base :Pointer;
size :QWORD;
end;
function md_map_reserve(hProcess:THandle=NtCurrentProcess;rip:QWORD=0):t_md_map_reserve_result;
implementation
uses
vmparam,
md_systm,
md_map,
md_context,
md_hook;
var
md_map_init_flags:Byte=0;
md_hook_page:Pointer;
///////////////////////////////////////////////////////
type
t_NtCreateThreadEx=function(
hThread :PHandle;
DesiredAccess :ACCESS_MASK;
ObjectAttributes:POBJECT_ATTRIBUTES;
ProcessHandle :THandle;
StartRoutine :Pointer;
Argument :Pointer;
CreateFlags :ULONG; // THREAD_CREATE_FLAGS_*
ZeroBits :ULONG_PTR;
StackSize :ULONG_PTR;
MaximumStackSize:ULONG_PTR;
AttributeList :PPS_ATTRIBUTE_LIST
):DWORD; stdcall;
var
real_NtCreateThreadEx:t_NtCreateThreadEx=@NtCreateThreadEx;
function Inj_NtCreateThreadEx(
hThread :PHandle;
DesiredAccess :ACCESS_MASK;
ObjectAttributes:POBJECT_ATTRIBUTES;
ProcessHandle :THandle;
StartRoutine :Pointer;
Argument :Pointer;
CreateFlags :ULONG; // THREAD_CREATE_FLAGS_*
ZeroBits :ULONG_PTR;
StackSize :ULONG_PTR;
MaximumStackSize:ULONG_PTR;
AttributeList :PPS_ATTRIBUTE_LIST
):DWORD; stdcall; forward;
///////////////////////////////////////////////////////
type
t_NtMapViewOfSection=function(
SectionHandle :THandle;
ProcessHandle :THandle;
BaseAddress :PPointer;
ZeroBits :ULONG_PTR;
CommitSize :ULONG_PTR;
SectionOffset :PLARGE_INTEGER;
ViewSize :PULONG_PTR;
InheritDisposition:DWORD;
AllocationType :ULONG;
Protect :ULONG
):DWORD; stdcall;
var
real_NtMapViewOfSection:t_NtMapViewOfSection=@NtMapViewOfSection;
function Inj_NtMapViewOfSection(
SectionHandle :THandle;
ProcessHandle :THandle;
BaseAddress :PPointer;
ZeroBits :ULONG_PTR;
CommitSize :ULONG_PTR;
SectionOffset :PLARGE_INTEGER;
ViewSize :PULONG_PTR;
InheritDisposition:DWORD;
AllocationType :ULONG;
Protect :ULONG
):DWORD; stdcall; forward;
///////////////////////////////////////////////////////
type
t_NtMapViewOfSectionEx=function(
SectionHandle :THandle;
ProcessHandle :THandle;
BaseAddress :PPointer;
SectionOffset :PLARGE_INTEGER;
ViewSize :PULONG_PTR;
AllocationType :ULONG;
Protect :ULONG;
ExtendedParameters :Pointer; //MEM_EXTENDED_PARAMETER
ExtendedParameterCount:ULONG
):DWORD; stdcall;
var
real_NtMapViewOfSectionEx:t_NtMapViewOfSectionEx=@NtMapViewOfSectionEx;
function Inj_NtMapViewOfSectionEx(
SectionHandle :THandle;
ProcessHandle :THandle;
BaseAddress :PPointer;
SectionOffset :PLARGE_INTEGER;
ViewSize :PULONG_PTR;
AllocationType :ULONG;
Protect :ULONG;
ExtendedParameters :Pointer; //MEM_EXTENDED_PARAMETER
ExtendedParameterCount:ULONG
):DWORD; stdcall; forward;
///////////////////////////////////////////////////////
type
t_NtAllocateVirtualMemory=function(
ProcessHandle :THandle;
BaseAddress :PPointer;
ZeroBits :ULONG_PTR;
RegionSize :PULONG_PTR;
AllocationType:ULONG;
Protect :ULONG
):DWORD; stdcall;
var
real_NtAllocateVirtualMemory:t_NtAllocateVirtualMemory=@NtAllocateVirtualMemory;
function Inj_NtAllocateVirtualMemory(
ProcessHandle :THandle;
BaseAddress :PPointer;
ZeroBits :ULONG_PTR;
RegionSize :PULONG_PTR;
AllocationType:ULONG;
Protect :ULONG
):DWORD; stdcall; forward;
///////////////////////////////////////////////////////
type
t_NtAllocateVirtualMemoryEx=function(
ProcessHandle :THandle;
BaseAddress :PPointer;
RegionSize :PULONG_PTR;
AllocationType :ULONG;
Protect :ULONG;
ExtendedParameters :Pointer; //MEM_EXTENDED_PARAMETER
ExtendedParameterCount:ULONG
):DWORD; stdcall;
var
real_NtAllocateVirtualMemoryEx:t_NtAllocateVirtualMemoryEx=@NtAllocateVirtualMemoryEx;
function Inj_NtAllocateVirtualMemoryEx(
ProcessHandle :THandle;
BaseAddress :PPointer;
RegionSize :PULONG_PTR;
AllocationType :ULONG;
Protect :ULONG;
ExtendedParameters :Pointer; //MEM_EXTENDED_PARAMETER
ExtendedParameterCount:ULONG
):DWORD; stdcall; forward;
///////////////////////////////////////////////////////
function md_map_reserve(hProcess:THandle=NtCurrentProcess;rip:QWORD=0):t_md_map_reserve_result;
var
Module:t_dll_base_pair;
page :t_hook_page;
temp_pmap_mem:t_addr_range_array;
base:Pointer;
size:QWORD;
i:Integer;
orig_flags:Byte;
init_flags:Byte;
begin
Result:=Default(t_md_map_reserve_result);
//load flags
init_flags:=0;
md_copyin(@md_map_init_flags,@init_flags,sizeof(init_flags),nil,hProcess);
orig_flags:=init_flags;
if ((init_flags and 1)=0) then
begin
///
{
if you reserve up to 43 bits of memory, then when searching for free memory from address 0,
the Windows virtual memory manager starts to lag terribly, in particular,
NtCreateThreadEx is visible here, which automatically allocates memory for the stack,
it takes about 5 seconds to execute.
Need to hook NtCreateThreadEx and emulate it through NtCreateThread and
a custom allocator with setting the lower search limit to LowestStartingAddress
and this generally worked and everything became faster.
}
if (hProcess<>NtCurrentProcess) and (rip<>0) then
begin
//RIP -> RtlUserThreadStart
Module:=NtQueryNtdllByRTUTS(hProcess,rip);
end else
begin
Module:=NtQueryModuleByName(hProcess,'ntdll.dll');
end;
page :=NewHookPage(hProcess,Module.DllBaseProc,4*1024,VM_RW);
WriteHook(page,NewHookPlan(Module,'NtCreateThreadEx' ,@Inj_NtCreateThreadEx ,@real_NtCreateThreadEx ));
WriteHook(page,NewHookPlan(Module,'NtMapViewOfSection' ,@Inj_NtMapViewOfSection ,@real_NtMapViewOfSection));
WriteHook(page,NewHookPlan(Module,'NtMapViewOfSectionEx' ,@Inj_NtMapViewOfSectionEx ,@real_NtMapViewOfSectionEx));
WriteHook(page,NewHookPlan(Module,'NtAllocateVirtualMemory' ,@Inj_NtAllocateVirtualMemory ,@real_NtAllocateVirtualMemory));
WriteHook(page,NewHookPlan(Module,'NtAllocateVirtualMemoryEx',@Inj_NtAllocateVirtualMemoryEx,@real_NtAllocateVirtualMemoryEx));
//allow execute
i:=md_protect(page.base,4*1024,VM_RX,hProcess);
//save change
i:=md_copyout(@page.base,@md_hook_page,sizeof(Pointer),nil,hProcess);
///
init_flags:=init_flags or 1;
end;
if ((init_flags and 2)=0) and (Length(temp_pmap_mem)<>0) then
begin
//copy original
temp_pmap_mem:=initial_pmap_mem;
i:=0;
while (i<=High(temp_pmap_mem)) do
begin
base:=Pointer(temp_pmap_mem[i].start);
//try union range
if (base=Pointer(DL_AREA_START)) then
begin
size:=VM_MAXUSER_ADDRESS-DL_AREA_START;
Result.error:=md_placeholder_mmap(base,size,MD_MAP_FIXED,hProcess);
if (Result.error=0) then
begin
//union range
temp_pmap_mem[i+0].__end:=VM_MAXUSER_ADDRESS;
temp_pmap_mem[i+1].start:=VM_MAXUSER_ADDRESS;
//
i:=i+2;
Continue;
end;
end;
base:=Pointer(temp_pmap_mem[i].start);
size:=temp_pmap_mem[i].__end-temp_pmap_mem[i].start;
Result.error:=md_placeholder_mmap(base,size,MD_MAP_FIXED,hProcess);
if (Result.error<>0) then
begin
Result.id :=i;
Result.base:=base;
Result.size:=size;
//STATUS_COMMITMENT_LIMIT = $C000012D
Exit;
end;
//update start region
temp_pmap_mem[i].start:=QWORD(base);
i:=i+1;
end;
//save change
md_copyout(@temp_pmap_mem,@pmap_mem,sizeof(pmap_mem),nil,hProcess);
init_flags:=init_flags or 2;
end;
//save change
if (orig_flags<>init_flags) then
begin
md_copyout(@init_flags,@md_map_init_flags,sizeof(init_flags),nil,hProcess);
end;
end;
const
C_REQ:TMEM_ADDRESS_REQUIREMENTS=(
LowestStartingAddress:Pointer(KERNEL_LOWER);
HighestEndingAddress :nil;
Alignment :0
);
C_EXT:TMEM_EXTENDED_PARAMETER=(
pType :MemExtendedParameterAddressRequirements;
Pointer:@C_REQ
);
type
TMEM_EXTENDED_PARAMETER_4=array[0..3] of TMEM_EXTENDED_PARAMETER;
TEXT_PATCHER=object
data:TMEM_EXTENDED_PARAMETER_4;
REQ :TMEM_ADDRESS_REQUIREMENTS;
Procedure Patch(var EParams:Pointer;var ECounts:ULONG);
end;
Procedure TEXT_PATCHER.Patch(var EParams:Pointer;var ECounts:ULONG); inline;
var
P:TMEM_EXTENDED_PARAMETER;
R:TMEM_ADDRESS_REQUIREMENTS;
i:Byte;
used:Boolean;
begin
if (EParams=nil) or (ECounts=0) then
begin
EParams:=@C_EXT;
ECounts:=1;
end else
if (ECounts<=Length(data)) then
begin
REQ:=C_REQ;
used:=False;
For i:=0 to ECounts-1 do
begin
P:=PMEM_EXTENDED_PARAMETER(EParams)[i]; //if use md_copyin then the initial load will crash 0xC0000005
case P.pType of
MemExtendedParameterAddressRequirements:
begin
R:=PMEM_ADDRESS_REQUIREMENTS(P.Pointer)^;
if (R.LowestStartingAddress>REQ.LowestStartingAddress) then
begin
REQ.LowestStartingAddress:=R.LowestStartingAddress;
end;
if (R.Alignment>REQ.Alignment) then
begin
REQ.Alignment:=R.Alignment;
end;
if (R.HighestEndingAddress<>nil) then
if (REQ.LowestStartingAddress>=R.HighestEndingAddress) then
begin
//oof
Exit;
end;
P.Pointer:=@REQ;
data[i]:=P;
used:=True;
end;
else
begin
data[i]:=P;
end;
end; //case
end; //for
if (not used) then
begin
if (ECounts=Length(data)) then
begin
//oof
Exit;
end;
i:=ECounts;
ECounts:=ECounts+1;
data[i]:=C_EXT;
end;
EParams:=@data;
end;
end;
///////////////////////////////////////////////////////
procedure BaseInitializeStack(InitialTeb :PINITIAL_TEB;
StackAddress:Pointer;
StackSize :Ptruint); inline;
begin
InitialTeb^.PreviousStackBase :=nil;
InitialTeb^.PreviousStackLimit:=nil;
InitialTeb^.StackBase :=StackAddress+StackSize; //start addr
InitialTeb^.StackLimit :=StackAddress; //lo addr
InitialTeb^.AllocatedStackBase:=StackAddress; //DeallocationStack
end;
var
BaseThreadInitThunk:Pointer=nil; //ECX->0 RDX->StartRoutine R8->Argument
procedure BaseInitializeContext(Context :PCONTEXT;
Parameter :Pointer;
StartAddress:Pointer;
StackAddress:Pointer); inline;
begin
Context^:=Default(TCONTEXT);
if (BaseThreadInitThunk=nil) then
begin
BaseThreadInitThunk:=GetProcAddress(GetModuleHandle('kernel32.dll'),'BaseThreadInitThunk');
end;
// Setup the Initial Win64 Thread Context
Context^.Rsp:=ptruint(StackAddress - $A8);
Context^.Rdx:=ptruint(StartAddress);
Context^.R8 :=ptruint(Parameter);
Context^.Rip:=ptruint(BaseThreadInitThunk);
Context^.SegGs:=KGDT64_R3_DATA or RPL_MASK;
Context^.SegEs:=KGDT64_R3_DATA or RPL_MASK;
Context^.SegDs:=KGDT64_R3_DATA or RPL_MASK;
Context^.SegCs:=KGDT64_R3_CODE or RPL_MASK;
Context^.SegSs:=KGDT64_R3_DATA or RPL_MASK;
Context^.SegFs:=KGDT64_R3_CMTEB or RPL_MASK;
Context^.EFlags:=$3000 or EFLAGS_INTERRUPT_MASK;
Context^.MxCsr:=__INITIAL_MXCSR__;
Context^.FltSave.ControlWord:=__INITIAL_FPUCW__;
//Context^.FltSave.StatusWord: WORD;
Context^.FltSave.MxCsr :=__INITIAL_MXCSR__;
Context^.FltSave.MxCsr_Mask :=__INITIAL_MXCSR_MASK__;
Context^.ContextFlags:=CONTEXT_THREAD;
//Context^.ContextFlags:=CONTEXT_CONTROL or CONTEXT_INTEGER or CONTEXT_SEGMENTS;
end;
function NtQueryTeb(td_handle:THandle;var teb:Pointer):Integer;
var
data:array[0..SizeOf(THREAD_BASIC_INFORMATION)-1+7] of Byte;
P_TBI:PTHREAD_BASIC_INFORMATION;
begin
Result:=0;
teb:=nil;
P_TBI:=Align(@data,8);
P_TBI^:=Default(THREAD_BASIC_INFORMATION);
Result:=NtQueryInformationThread(
td_handle,
ThreadBasicInformation,
P_TBI,
SizeOf(THREAD_BASIC_INFORMATION),
nil);
if (Result<>0) then Exit;
teb:=P_TBI^.TebBaseAddress;
end;
const
MinStackSize=$1000000;
function Inj_NtCreateThreadEx(
hThread :PHandle;
DesiredAccess :ACCESS_MASK;
ObjectAttributes:POBJECT_ATTRIBUTES;
ProcessHandle :THandle;
StartRoutine :Pointer;
Argument :Pointer;
CreateFlags :ULONG; // THREAD_CREATE_FLAGS_*
ZeroBits :ULONG_PTR;
StackSize :ULONG_PTR;
MaximumStackSize:ULONG_PTR;
AttributeList :PPS_ATTRIBUTE_LIST
):DWORD; stdcall;
var
_ClientId :array[0..SizeOf(TCLIENT_ID )+14] of Byte;
_InitialTeb:array[0..SizeOf(TINITIAL_TEB)+14] of Byte;
_Context :array[0..SizeOf(TCONTEXT )+14] of Byte;
ClientId :PCLIENT_ID;
InitialTeb:PINITIAL_TEB;
Context :PCONTEXT;
stack_base:Pointer;
Stack :Pointer;
teb :Pointer;
Attribute :PS_ATTRIBUTE;
a,count:Integer;
begin
if (ZeroBits<>0) then
begin
Result:=real_NtCreateThreadEx(
hThread ,
DesiredAccess ,
ObjectAttributes,
ProcessHandle ,
StartRoutine ,
Argument ,
CreateFlags ,
ZeroBits ,
StackSize ,
MaximumStackSize,
AttributeList
);
Exit;
end;
ClientId :=Align(@_ClientId ,16);
InitialTeb:=Align(@_InitialTeb,16);
Context :=Align(@_Context ,16);
ClientId^.UniqueProcess:=hThread^;
ClientId^.UniqueThread :=ProcessHandle;
if (MaximumStackSize<MinStackSize) then MaximumStackSize:=MinStackSize;
if (StackSize <MinStackSize) then StackSize :=MinStackSize;
if (StackSize <MaximumStackSize) then StackSize :=MaximumStackSize;
StackSize:=MinStackSize;
stack_base:=nil;
Result:=real_NtAllocateVirtualMemoryEx(
ProcessHandle ,
@stack_base ,
@StackSize ,
MEM_COMMIT or MEM_RESERVE,
PAGE_READWRITE ,
@C_EXT ,
1
);
if (Result<>0) then Exit;
BaseInitializeStack(InitialTeb,stack_base,StackSize);
Stack:=stack_base+StackSize;
Stack:=Pointer((ptruint(Stack) and (not $F)));
BaseInitializeContext(Context,
Argument,
StartRoutine,
Stack);
Result:=NtCreateThread(
hThread,
DesiredAccess,
ObjectAttributes,
ProcessHandle,
ClientId,
Context,
InitialTeb,
(CreateFlags and THREAD_CREATE_FLAGS_CREATE_SUSPENDED)<>0);
if (Result=0) and (AttributeList<>nil) then
begin
count:=AttributeList^.TotalLength div sizeof(PS_ATTRIBUTE);
if (count<>0) then
for a:=0 to count-1 do
begin
Attribute:=AttributeList^.Attributes[a];
case (Attribute.Attribute and PS_ATTRIBUTE_NUMBER_MASK) of
PsAttributeClientId:
if (Attribute.size<>0) and (Attribute.Value<>0) then
begin
if (Attribute.size>sizeof(TCLIENT_ID)) then Attribute.size:=sizeof(TCLIENT_ID);
Move(ClientId^,Pointer(Attribute.Value)^,Attribute.size);
if (Attribute.ReturnLength<>nil) then
begin
Attribute.ReturnLength^:=Attribute.size;
end;
end;
PsAttributeTebAddress:
if (Attribute.size<>0) and (Attribute.Value<>0) then
begin
if (Attribute.size>sizeof(Pointer)) then Attribute.size:=sizeof(Pointer);
teb:=nil;
if (NtQueryTeb(hThread^,teb)=0) then
begin
Move(teb,Pointer(Attribute.Value)^,Attribute.size);
if (Attribute.ReturnLength<>nil) then
begin
Attribute.ReturnLength^:=Attribute.size;
end;
end;
end;
else;
end;
end;
end;
end;
///////////////////////////////////////////////////////
function Inj_NtMapViewOfSection(
SectionHandle :THandle;
ProcessHandle :THandle;
BaseAddress :PPointer;
ZeroBits :ULONG_PTR;
CommitSize :ULONG_PTR;
SectionOffset :PLARGE_INTEGER;
ViewSize :PULONG_PTR;
InheritDisposition:DWORD;
AllocationType :ULONG;
Protect :ULONG
):DWORD; stdcall;
begin
AllocationType:=AllocationType and (not MEM_TOP_DOWN);
if (ZeroBits=0) and
(ViewSize<>nil) and
(BaseAddress<>nil) then
begin
if (CommitSize=ViewSize^) and
(BaseAddress^=nil) then
begin
Result:=real_NtMapViewOfSectionEx(
SectionHandle ,
ProcessHandle ,
BaseAddress ,
SectionOffset ,
ViewSize ,
AllocationType,
Protect ,
@C_EXT ,
1
);
Exit;
end;
end;
Result:=real_NtMapViewOfSection(
SectionHandle ,
ProcessHandle ,
BaseAddress ,
ZeroBits ,
CommitSize ,
SectionOffset ,
ViewSize ,
InheritDisposition,
AllocationType ,
Protect
);
end;
///////////////////////////////////////////////////////
function Inj_NtMapViewOfSectionEx(
SectionHandle :THandle;
ProcessHandle :THandle;
BaseAddress :PPointer;
SectionOffset :PLARGE_INTEGER;
ViewSize :PULONG_PTR;
AllocationType :ULONG;
Protect :ULONG;
ExtendedParameters :Pointer; //MEM_EXTENDED_PARAMETER
ExtendedParameterCount:ULONG
):DWORD; stdcall;
var
EXT:TEXT_PATCHER;
begin
AllocationType:=AllocationType and (not MEM_TOP_DOWN);
if (BaseAddress<>nil) then
if (BaseAddress^=nil) then
begin
EXT.Patch(ExtendedParameters,ExtendedParameterCount);
end;
Result:=real_NtMapViewOfSectionEx(
SectionHandle ,
ProcessHandle ,
BaseAddress ,
SectionOffset ,
ViewSize ,
AllocationType ,
Protect ,
ExtendedParameters ,
ExtendedParameterCount
);
end;
///////////////////////////////////////////////////////
function Inj_NtAllocateVirtualMemory(
ProcessHandle :THandle;
BaseAddress :PPointer;
ZeroBits :ULONG_PTR;
RegionSize :PULONG_PTR;
AllocationType:ULONG;
Protect :ULONG
):DWORD; stdcall;
begin
AllocationType:=AllocationType and (not MEM_TOP_DOWN);
if (ZeroBits=0) and
(BaseAddress<>nil) then
begin
if (BaseAddress^=nil) then
begin
Result:=real_NtAllocateVirtualMemoryEx(
ProcessHandle ,
BaseAddress ,
RegionSize ,
AllocationType,
Protect ,
@C_EXT ,
1
);
Exit;
end;
end;
Result:=real_NtAllocateVirtualMemory(
ProcessHandle ,
BaseAddress ,
ZeroBits ,
RegionSize ,
AllocationType,
Protect
);
end;
///////////////////////////////////////////////////////
function Inj_NtAllocateVirtualMemoryEx(
ProcessHandle :THandle;
BaseAddress :PPointer;
RegionSize :PULONG_PTR;
AllocationType :ULONG;
Protect :ULONG;
ExtendedParameters :Pointer; //MEM_EXTENDED_PARAMETER
ExtendedParameterCount:ULONG
):DWORD; stdcall;
var
EXT:TEXT_PATCHER;
begin
AllocationType:=AllocationType and (not MEM_TOP_DOWN);
if (BaseAddress<>nil) then
if (BaseAddress^=nil) then
begin
EXT.Patch(ExtendedParameters,ExtendedParameterCount);
end;
Result:=real_NtAllocateVirtualMemoryEx(
ProcessHandle ,
BaseAddress ,
RegionSize ,
AllocationType ,
Protect ,
ExtendedParameters ,
ExtendedParameterCount
);
end;
///////////////////////////////////////////////////////
end.