Created
December 19, 2024 10:19
-
-
Save erdesigns-eu/dba4e9a6839e56c80ba6db4484b5e1df to your computer and use it in GitHub Desktop.
Cross-Platform System Call Executor (x86, x64, ARM) in Delphi
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
unit SysCallExecutor; | |
interface | |
uses | |
Windows, SysUtils; | |
type | |
TSysCallExecutor = class | |
private | |
function GetSysCallNumber(const FunctionName: string): Cardinal; | |
function GetProcAddressEx(const ModuleName, FunctionName: string): Pointer; | |
public | |
function ExecuteSysCall(SysCallNumber: Cardinal; Parameters: array of Pointer): Integer; | |
function ExecuteFunctionByName(const FunctionName: string; Parameters: array of Pointer): Integer; | |
end; | |
implementation | |
function TSysCallExecutor.GetProcAddressEx(const ModuleName, FunctionName: string): Pointer; | |
var | |
ModuleHandle: HMODULE; | |
begin | |
ModuleHandle := GetModuleHandle(PChar(ModuleName)); | |
if ModuleHandle = 0 then | |
raise Exception.CreateFmt('Module not found: %s', [ModuleName]); | |
Result := GetProcAddress(ModuleHandle, PChar(FunctionName)); | |
if not Assigned(Result) then | |
raise Exception.CreateFmt('Function not found: %s in module %s', [FunctionName, ModuleName]); | |
end; | |
function TSysCallExecutor.GetSysCallNumber(const FunctionName: string): Cardinal; | |
var | |
NtFunction: Pointer; | |
begin | |
NtFunction := GetProcAddressEx('ntdll.dll', FunctionName); | |
if not Assigned(NtFunction) then | |
raise Exception.CreateFmt('Failed to locate function: %s', [FunctionName]); | |
Result := PCardinal(Cardinal(NtFunction) + 1)^; // Syscall number is located at +1 offset | |
end; | |
function TSysCallExecutor.ExecuteSysCall(SysCallNumber: Cardinal; Parameters: array of Pointer): Integer; | |
{$IFDEF WIN32} | |
asm | |
// Save state | |
pushad | |
pushfd | |
// Load syscall number | |
mov eax, SysCallNumber | |
// Load parameters into registers | |
mov ecx, Parameters | |
// Execute syscall | |
int $2E // System call interrupt for 32-bit systems | |
// Store result in EAX | |
mov @Result, eax | |
// Restore state | |
popfd | |
popad | |
end; | |
{$ELSEIF Defined(WIN64)} | |
asm | |
// x64 uses the syscall instruction | |
mov r10, rcx // First parameter in rcx | |
mov eax, SysCallNumber | |
syscall // Invoke syscall | |
mov @Result, eax // Return value in eax | |
end; | |
{$ELSEIF Defined(ARM)} | |
asm | |
// ARM architecture uses SVC (Supervisor Call) for syscalls | |
mov w8, SysCallNumber // Load syscall number into w8 (ARM convention) | |
svc #0 // Supervisor call | |
mov @Result, w0 // Store the result from x0 (ARM convention) | |
end; | |
{$ELSE} | |
{$ERROR Unsupported architecture} | |
{$ENDIF} | |
function TSysCallExecutor.ExecuteFunctionByName(const FunctionName: string; Parameters: array of Pointer): Integer; | |
var | |
SysCallNumber: Cardinal; | |
begin | |
SysCallNumber := GetSysCallNumber(FunctionName); | |
Result := ExecuteSysCall(SysCallNumber, Parameters); | |
end; | |
end. |
Author
erdesigns-eu
commented
Dec 19, 2024
// Example: Allocate memory, write shellcode, and execute it
uses
SysCallExecutor, Windows, SysUtils;
procedure RunShellcode;
var
Executor: TSysCallExecutor;
BaseAddress: Pointer := nil;
RegionSize: NativeUInt := $1000; // 4 KB
Shellcode: array[0..4] of Byte = ($48, $31, $C0, $C3); // x64 NOP + RET
begin
Executor := TSysCallExecutor.Create;
try
// Allocate executable memory
if Executor.ExecuteFunctionByName(
'NtAllocateVirtualMemory',
[GetCurrentProcess(),
@BaseAddress,
Pointer(0),
@RegionSize,
Pointer($3000), // MEM_COMMIT | MEM_RESERVE
Pointer($40) // PAGE_EXECUTE_READWRITE
]) = 0 then
begin
Writeln('Memory allocated at: ', IntToHex(NativeUInt(BaseAddress), 8));
// Copy shellcode to allocated memory
Move(Shellcode, BaseAddress^, Length(Shellcode));
// Execute the shellcode
TProcedure(BaseAddress)();
Writeln('Shellcode executed.');
end
else
Writeln('Failed to allocate memory.');
finally
Executor.Free;
end;
end;
begin
RunShellcode;
end.
program LowLevelHttp;
{$APPTYPE CONSOLE}
uses
SysCallExecutor, Windows, SysUtils;
procedure PerformHttpPostAndGet;
var
Executor: TSysCallExecutor;
SocketHandle: THandle;
ServerAddr: TSockAddrIn;
IoStatusBlock: IO_STATUS_BLOCK;
Request: string;
Response: array[0..4095] of AnsiChar;
Result: Integer;
BytesReceived: Cardinal;
begin
Executor := TSysCallExecutor.Create;
try
// Step 1: Create a socket using NtCreateFile
Result := Executor.ExecuteFunctionByName(
'NtCreateFile',
[@SocketHandle,
Pointer($80100000), // FILE_GENERIC_WRITE | FILE_GENERIC_READ
nil, // ObjectAttributes
@IoStatusBlock,
nil, // Allocation size
Pointer(0), // File attributes
Pointer(3), // Share access: FILE_SHARE_READ
Pointer(1), // Create disposition: FILE_OPEN
Pointer(0), // Create options
nil, // EaBuffer
Pointer(0) // EaLength
]);
if Result <> 0 then
raise Exception.CreateFmt('Failed to create socket: %d', [Result]);
Writeln('Socket created.');
// Step 2: Connect to the server
FillChar(ServerAddr, SizeOf(ServerAddr), 0);
ServerAddr.sin_family := AF_INET;
ServerAddr.sin_port := htons(80); // HTTP port
ServerAddr.sin_addr.S_addr := inet_addr('93.184.216.34'); // example.com
Result := Executor.ExecuteFunctionByName(
'NtDeviceIoControlFile',
[SocketHandle,
nil, // Event
nil, // ApcRoutine
nil, // ApcContext
@IoStatusBlock,
Pointer(IOCTL_CONNECT), // IOCTL_CONNECT placeholder
@ServerAddr,
Pointer(SizeOf(ServerAddr)),
nil,
Pointer(0)
]);
if Result <> 0 then
raise Exception.CreateFmt('Failed to connect: %d', [Result]);
Writeln('Connected to server.');
// Step 3: Perform HTTP POST to upload data
Request :=
'POST /upload HTTP/1.1' + #13#10 +
'Host: example.com' + #13#10 +
'Content-Type: text/plain' + #13#10 +
'Content-Length: 11' + #13#10 +
#13#10 +
'Hello World';
Result := Executor.ExecuteFunctionByName(
'NtDeviceIoControlFile',
[SocketHandle,
nil, // Event
nil, // ApcRoutine
nil, // ApcContext
@IoStatusBlock,
Pointer(IOCTL_SEND), // IOCTL_SEND placeholder
@Request[1],
Pointer(Length(Request)),
nil,
Pointer(0)
]);
if Result <> 0 then
raise Exception.CreateFmt('Failed to send POST request: %d', [Result]);
Writeln('POST request sent.');
// Step 4: Perform HTTP GET to retrieve uploaded data
Request :=
'GET /upload HTTP/1.1' + #13#10 +
'Host: example.com' + #13#10 +
#13#10;
Result := Executor.ExecuteFunctionByName(
'NtDeviceIoControlFile',
[SocketHandle,
nil, // Event
nil, // ApcRoutine
nil, // ApcContext
@IoStatusBlock,
Pointer(IOCTL_SEND), // IOCTL_SEND placeholder
@Request[1],
Pointer(Length(Request)),
nil,
Pointer(0)
]);
if Result <> 0 then
raise Exception.CreateFmt('Failed to send GET request: %d', [Result]);
Writeln('GET request sent.');
// Step 5: Receive the response
FillChar(Response, SizeOf(Response), 0);
Result := Executor.ExecuteFunctionByName(
'NtDeviceIoControlFile',
[SocketHandle,
nil, // Event
nil, // ApcRoutine
nil, // ApcContext
@IoStatusBlock,
Pointer(IOCTL_RECEIVE), // IOCTL_RECEIVE placeholder
@Response,
Pointer(SizeOf(Response)),
nil,
Pointer(0)
]);
if Result = 0 then
begin
BytesReceived := IoStatusBlock.Information;
Writeln('Response received:');
Writeln(Copy(Response, 1, BytesReceived));
end
else
Writeln('Failed to receive response: ', Result);
finally
Executor.Free;
end;
end;
begin
PerformHttpPostAndGet;
end.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment