UNIT BeRoPE;

INTERFACE

USES Windows,BeRoStream;

CONST IMPORTED_NAME_OFFSET=$00000002;
      IMAGE_ORDINAL_FLAG32=$80000000;
      IMAGE_ORDINAL_MASK32=$0000FFFF;

      RTL_CRITSECT_TYPE=0;
      RTL_RESOURCE_TYPE=1;

      DLL_PROCESS_ATTACH=1;
      DLL_THREAD_ATTACH=2;
      DLL_THREAD_DETACH=3;
      DLL_PROCESS_DETACH=0;

      IMAGE_SizeHeader=20;

      IMAGE_FILE_RELOCS_STRIPPED=$0001;
      IMAGE_FILE_EXECUTABLE_IMAGE=$0002;
      IMAGE_FILE_LINE_NUMS_STRIPPED=$0004;
      IMAGE_FILE_LOCAL_SYMS_STRIPPED=$0008;
      IMAGE_FILE_AGGRESIVE_WS_TRIM=$0010;
      IMAGE_FILE_BYTES_REVERSED_LO=$0080;
      IMAGE_FILE_32BIT_MACHINE=$0100;
      IMAGE_FILE_DEBUG_STRIPPED=$0200;
      IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP=$0400;
      IMAGE_FILE_NET_RUN_FROM_SWAP=$0800;
      IMAGE_FILE_SYSTEM=$1000;
      IMAGE_FILE_DLL=$2000;
      IMAGE_FILE_UP_SYSTEM_ONLY=$4000;
      IMAGE_FILE_BYTES_REVERSED_HI=$8000;

      IMAGE_FILE_MACHINE_UNKNOWN=0;
      IMAGE_FILE_MACHINE_I386=$14C;
      IMAGE_FILE_MACHINE_R3000=$162;
      IMAGE_FILE_MACHINE_R4000=$166;
      IMAGE_FILE_MACHINE_R10000=$168;
      IMAGE_FILE_MACHINE_ALPHA=$184;
      IMAGE_FILE_MACHINE_POWERPC=$1F0;

      IMAGE_NUMBEROF_DIRECTORY_ENTRIES=16;

      IMAGE_SUBSYSTEM_UNKNOWN=0;
      IMAGE_SUBSYSTEM_NATIVE=1;
      IMAGE_SUBSYSTEM_WINDOWS_GUI=2;
      IMAGE_SUBSYSTEM_WINDOWS_CUI=3;
      IMAGE_SUBSYSTEM_OS2_CUI=5;
      IMAGE_SUBSYSTEM_POSIX_CUI=7;
      IMAGE_SUBSYSTEM_RESERVED=8;

      IMAGE_DIRECTORY_ENTRY_EXPORT=0;
      IMAGE_DIRECTORY_ENTRY_IMPORT=1;
      IMAGE_DIRECTORY_ENTRY_RESOURCE=2;
      IMAGE_DIRECTORY_ENTRY_EXCEPTION=3;
      IMAGE_DIRECTORY_ENTRY_SECURITY=4;
      IMAGE_DIRECTORY_ENTRY_BASERELOC=5;
      IMAGE_DIRECTORY_ENTRY_DEBUG=6;
      IMAGE_DIRECTORY_ENTRY_COPYRIGHT=7;
      IMAGE_DIRECTORY_ENTRY_GLOBALPTR=8;
      IMAGE_DIRECTORY_ENTRY_TLS=9;
      IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG=10;
      IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT=11;
      IMAGE_DIRECTORY_ENTRY_IAT=12;

      IMAGE_SIZEOF_SHORT_NAME=8;

      IMAGE_SCN_TYIMAGE_REG=$00000000;
      IMAGE_SCN_TYIMAGE_DSECT=$00000001;
      IMAGE_SCN_TYIMAGE_NOLOAD=$00000002;
      IMAGE_SCN_TYIMAGE_GROUP=$00000004;
      IMAGE_SCN_TYIMAGE_NO_PAD=$00000008;
      IMAGE_SCN_TYIMAGE_COPY=$00000010;
      IMAGE_SCN_CNT_CODE=$00000020;
      IMAGE_SCN_CNT_INITIALIZED_DATA=$00000040;
      IMAGE_SCN_CNT_UNINITIALIZED_DATA=$00000080;
      IMAGE_SCN_LNK_OTHER=$00000100;
      IMAGE_SCN_LNK_INFO=$00000200;
      IMAGE_SCN_TYIMAGE_OVER=$0000400;
      IMAGE_SCN_LNK_REMOVE=$00000800;
      IMAGE_SCN_LNK_COMDAT=$00001000;
      IMAGE_SCN_MEM_PROTECTED=$00004000;
      IMAGE_SCN_MEM_FARDATA=$00008000;
      IMAGE_SCN_MEM_SYSHEAP=$00010000;
      IMAGE_SCN_MEM_PURGEABLE=$00020000;
      IMAGE_SCN_MEM_16BIT=$00020000;
      IMAGE_SCN_MEM_LOCKED=$00040000;
      IMAGE_SCN_MEM_PRELOAD=$00080000;
      IMAGE_SCN_ALIGN_1BYTES=$00100000;
      IMAGE_SCN_ALIGN_2BYTES=$00200000;
      IMAGE_SCN_ALIGN_4BYTES=$00300000;
      IMAGE_SCN_ALIGN_8BYTES=$00400000;
      IMAGE_SCN_ALIGN_16BYTES=$00500000;
      IMAGE_SCN_ALIGN_32BYTES=$00600000;
      IMAGE_SCN_ALIGN_64BYTES=$00700000;
      IMAGE_SCN_LNK_NRELOC_OVFL=$01000000;
      IMAGE_SCN_MEM_DISCARDABLE=$02000000;
      IMAGE_SCN_MEM_NOT_CACHED=$04000000;
      IMAGE_SCN_MEM_NOT_PAGED=$08000000;
      IMAGE_SCN_MEM_SHARED=$10000000;
      IMAGE_SCN_MEM_EXECUTE=$20000000;
      IMAGE_SCN_MEM_READ=$40000000;
      IMAGE_SCN_MEM_WRITE=LONGWORD($80000000);

      IMAGE_REL_BASED_ABSOLUTE=0;
      IMAGE_REL_BASED_HIGH=1;
      IMAGE_REL_BASED_LOW=2;
      IMAGE_REL_BASED_HIGHLOW=3;
      IMAGE_REL_BASED_HIGHADJ=4;
      IMAGE_REL_BASED_MIPS_JMPADDR=5;
      IMAGE_REL_BASED_SECTION=6;
      IMAGE_REL_BASED_REL32=7;

      IMAGE_REL_BASED_MIPS_JMPADDR16=9;
      IMAGE_REL_BASED_IA64_IMM64=9;
      IMAGE_REL_BASED_DIR64=10;
      IMAGE_REL_BASED_HIGH3ADJ=11;

      PAGE_NOACCESS=1;
      PAGE_READONLY=2;
      PAGE_READWRITE=4;
      PAGE_WRITECOPY=8;
      PAGE_EXECUTE=$10;
      PAGE_EXECUTE_READ=$20;
      PAGE_EXECUTE_READWRITE=$40;
      PAGE_EXECUTE_WRITECOPY=$80;
      PAGE_GUARD=$100;
      PAGE_NOCACHE=$200;
      MEM_COMMIT=$1000;
      MEM_RESERVE=$2000;
      MEM_DECOMMIT=$4000;
      MEM_RELEASE=$8000;
      MEM_FREE=$10000;
      MEM_PRIVATE=$20000;
      MEM_MAPPED=$40000;
      MEM_RESET=$80000;
      MEM_TOP_DOWN=$100000;
      SEC_FILE=$800000;
      SEC_IMAGE=$1000000;
      SEC_RESERVE=$4000000;
      SEC_COMMIT=$8000000;
      SEC_NOCACHE=$10000000;
      MEM_IMAGE=SEC_IMAGE;

      PE_SCN_TYPE_REG=$00000000;
      PE_SCN_TYPE_DSECT=$00000001;
      PE_SCN_TYPE_NOLOAD=$00000002;
      PE_SCN_TYPE_GROUP=$00000004;
      PE_SCN_TYPE_NO_PAD=$00000008;
      PE_SCN_TYPE_COPY=$00000010;
      PE_SCN_CNT_CODE=$00000020;
      PE_SCN_CNT_INITIALIZED_DATA=$00000040;
      PE_SCN_CNT_UNINITIALIZED_DATA=$00000080;
      PE_SCN_LNK_OTHER=$00000100;
      PE_SCN_LNK_INFO=$00000200;
      PE_SCN_TYPE_OVER=$0000400;
      PE_SCN_LNK_REMOVE=$00000800;
      PE_SCN_LNK_COMDAT=$00001000;
      PE_SCN_MEM_PROTECTED=$00004000;
      PE_SCN_MEM_FARDATA=$00008000;
      PE_SCN_MEM_SYSHEAP=$00010000;
      PE_SCN_MEM_PURGEABLE=$00020000;
      PE_SCN_MEM_16BIT=$00020000;
      PE_SCN_MEM_LOCKED=$00040000;
      PE_SCN_MEM_PRELOAD=$00080000;
      PE_SCN_ALIGN_1BYTES=$00100000;
      PE_SCN_ALIGN_2BYTES=$00200000;
      PE_SCN_ALIGN_4BYTES=$00300000;
      PE_SCN_ALIGN_8BYTES=$00400000;
      PE_SCN_ALIGN_16BYTES=$00500000;
      PE_SCN_ALIGN_32BYTES=$00600000;
      PE_SCN_ALIGN_64BYTES=$00700000;
      PE_SCN_LNK_NRELOC_OVFL=$01000000;
      PE_SCN_MEM_DISCARDABLE=$02000000;
      PE_SCN_MEM_NOT_CACHED=$04000000;
      PE_SCN_MEM_NOT_PAGED=$08000000;
      PE_SCN_MEM_SHARED=$10000000;
      PE_SCN_MEM_EXECUTE=$20000000;
      PE_SCN_MEM_READ=$40000000;
      PE_SCN_MEM_WRITE=LONGWORD($80000000);

      SignMZEXE=$5A4D;
      SignNEEXE=$454E;
      SignLEEXE=$454C;
      SignPE=$00004550;

TYPE PWORD=^WORD;
     PPWORD=^PPWORD;

     PLONGWORD=^LONGWORD;
     PPLONGWORD=^PLONGWORD;

     PImageDOSStandardHeader=^TImageDOSStandardHeader;
     TImageDOSStandardHeader=PACKED RECORD
      Signature:WORD;
      PartPag:WORD;
      PageCnt:WORD;
      ReloCnt:WORD;
      HdrSize:WORD;
      MinMem:WORD;
      MaxMem:WORD;
      ReloSS:WORD;
      ExeSP:WORD;
      ChkSum:WORD;
      ExeIP:WORD;
      ReloCS:WORD;
      TablOff:WORD;
      Overlay:WORD;
     END;

     PImageDOSExtendedHeader=^TImageDOSExtendedHeader;
     TImageDOSExtendedHeader=PACKED RECORD
      Reserved:PACKED ARRAY[0..3] OF WORD;
      OEMID:WORD;
      OEMInfo:WORD;
      Reserved2:PACKED ARRAY[0..9] OF WORD;
      LFAOffset:LONGWORD;
     END;

     PImageDOSHeader=^TImageDOSHeader;
     TImageDOSHeader=PACKED RECORD
      StandardHeader:TImageDOSStandardHeader;
      ExtendedHeader:TImageDOSExtendedHeader;
     END;

     PImageSignature=^TImageSignature;
     TImageSignature=LONGWORD;

     TISHMisc=PACKED RECORD
      CASE INTEGER OF
       0:(PhysicalAddress:LONGWORD);
       1:(VirtualSize:LONGWORD);
     END;

     PImageExportDirectory=^TImageExportDirectory;
     TImageExportDirectory=PACKED RECORD
      Characteristics:LONGWORD;
      TimeDateStamp:LONGWORD;
      MajorVersion:WORD;
      MinorVersion:WORD;
      Name:LONGWORD;
      Base:LONGWORD;
      NumberOfFunctions:LONGWORD;
      NumberOfNames:LONGWORD;
      AddressOfFunctions:PPLONGWORD;
      AddressOfNames:PPLONGWORD;
      AddressOfNameOrdinals:PPWORD;
     END;

     PImageSectionHeader=^TImageSectionHeader;
     TImageSectionHeader=PACKED RECORD
      Name:PACKED ARRAY[0..IMAGE_SIZEOF_SHORT_NAME-1] OF CHAR;
      Misc:TISHMisc;
      VirtualAddress:LONGWORD;
      SizeOfRawData:LONGWORD;
      PointerToRawData:LONGWORD;
      PointerToRelocations:LONGWORD;
      PointerToLineNumbers:LONGWORD;
      NumberOfRelocations:WORD;
      NumberOfLineNumbers:WORD;
      Characteristics:LONGWORD;
     END;

     PImageSectionHeaders=^TImageSectionHeaders;
     TImageSectionHeaders=ARRAY[0..(2147483647 DIV SIZEOF(TImageSectionHeader))-1] OF TImageSectionHeader;

     PImageDataDirectory=^TImageDataDirectory;
     TImageDataDirectory=PACKED RECORD
      VirtualAddress:LONGWORD;
      Size:LONGWORD;
     END;

     PImageFileHeader=^TImageFileHeader;
     TImageFileHeader=PACKED RECORD
      Machine:WORD;
      NumberOfSections:WORD;
      TimeDateStamp:LONGWORD;
      PointerToSymbolTable:LONGWORD;
      NumberOfSymbols:LONGWORD;
      SizeOfOptionalHeader:WORD;
      Characteristics:WORD;
     END;

     PImageOptionalHeader=^TImageOptionalHeader;
     TImageOptionalHeader=PACKED RECORD
      Magic:WORD;
      MajorLinkerVersion:BYTE;
      MinorLinkerVersion:BYTE;
      SizeOfCode:LONGWORD;
      SizeOfInitializedData:LONGWORD;
      SizeOfUninitializedData:LONGWORD;
      AddressOfEntryPoint:LONGWORD;
      BaseOfCode:LONGWORD;
      BaseOfData:LONGWORD;
      ImageBase:LONGWORD;
      SectionAlignment:LONGWORD;
      FileAlignment:LONGWORD;
      MajorOperatingSystemVersion:WORD;
      MinorOperatingSystemVersion:WORD;
      MajorImageVersion:WORD;
      MinorImageVersion:WORD;
      MajorSubsystemVersion:WORD;
      MinorSubsystemVersion:WORD;
      Win32VersionValue:LONGWORD;
      SizeOfImage:LONGWORD;
      SizeOfHeaders:LONGWORD;
      CheckSum:LONGWORD;
      Subsystem:WORD;
      DllCharacteristics:WORD;
      SizeOfStackReserve:LONGWORD;
      SizeOfStackCommit:LONGWORD;
      SizeOfHeapReserve:LONGWORD;
      SizeOfHeapCommit:LONGWORD;
      LoaderFlags:LONGWORD;
      NumberOfRvaAndSizes:LONGWORD;
      DataDirectory:PACKED ARRAY[0..IMAGE_NUMBEROF_DIRECTORY_ENTRIES-1] OF TImageDataDirectory;
     END;

     PImageNTHeaders=^TImageNTHeaders;
     TImageNTHeaders=PACKED RECORD
      Signature:TImageSignature;
      FileHeader:TImageFileHeader;
      OptionalHeader:TImageOptionalHeader;
     END;

     PImageImportDescriptor=^TImageImportDescriptor;
     TImageImportDescriptor=PACKED RECORD
      OriginalFirstThunk:LONGWORD;
      TimeDateStamp:LONGWORD;
      ForwarderChain:LONGWORD;
      Name:LONGWORD;
      FirstThunk:LONGWORD;
     END;

     PImageBaseRelocation=^TImageBaseRelocation;
     TImageBaseRelocation=PACKED RECORD
      VirtualAddress:LONGWORD;
      SizeOfBlock:LONGWORD;
     END;

     PImageThunkData=^TImageThunkData;
     TImageThunkData=PACKED RECORD
      ForwarderString:LONGWORD;
      Funktion:LONGWORD;
      Ordinal:LONGWORD;
      AddressOfData:LONGWORD;
     END;

     TProcInfo=PACKED RECORD
      BaseAddr:LONGWORD;
      ImageSize:LONGWORD;
     END;

     TFixUpBlock=PACKED RECORD
      PageRVA:LONGINT;
      BlockSize:LONGINT;
     END;

     PImageSection=^TImageSection;
     TImageSection=PACKED RECORD
      SectionHeader:TImageSectionHeader;
      SectionData:TBeRoMemoryStream;
     END;

     TImageSections=ARRAY OF TImageSection;

     TBeRoPE=CLASS
      PRIVATE
       FUNCTION DoAlign(Value,Alignment:LONGWORD):LONGWORD;
       FUNCTION RunEx(Image:POINTER;CommandLineParameters:STRING):BOOLEAN;
      PUBLIC
       StubHeader:TBeRoMemoryStream;
       Overlay:TBeRoMemoryStream;
       OverlayOffset:INTEGER;
       DOSHeader:TImageDOSHeader;
       NTHeaders:TImageNTHeaders;
       Sections:TImageSections;
       CONSTRUCTOR Create;
       DESTRUCTOR Destroy; OVERRIDE;
       PROCEDURE Clear;
       FUNCTION IsMZEXE(Stream:TBeRoStream):BOOLEAN;
       FUNCTION IsPEEXE(Stream:TBeRoStream):BOOLEAN;
       FUNCTION ReadImage(Stream:TBeRoStream):BOOLEAN;
       FUNCTION WriteImage(Stream:TBeRoStream):BOOLEAN;
       FUNCTION ReadMemoryImage(Stream:TBeRoStream):BOOLEAN;
       FUNCTION WriteMemoryImage(Stream:TBeRoStream):BOOLEAN;
       FUNCTION ReadImageBuffered(Stream:TBeRoStream):BOOLEAN;
       FUNCTION WriteImageBuffered(Stream:TBeRoStream):BOOLEAN;
       FUNCTION ReadMemoryImageBuffered(Stream:TBeRoStream):BOOLEAN;
       FUNCTION WriteMemoryImageBuffered(Stream:TBeRoStream):BOOLEAN;
       FUNCTION CalculateStreamCheckSum(Stream:TBeRoStream):LONGWORD;
       FUNCTION CalculateImageCheckSum:LONGWORD;
       FUNCTION CorrectImageCheckSum(Stream:TBeRoStream):BOOLEAN;
       PROCEDURE InitializeImage;
       FUNCTION CorrectSectionAlign(Offset:LONGWORD):LONGWORD;
       FUNCTION CorrectFileAlign(Offset:LONGWORD):LONGWORD;
       PROCEDURE CorrectImageData;
       FUNCTION GetNewAlignedVirtualAddress:LONGWORD;
       FUNCTION CreateNewSection(VirtualSize:LONGWORD):INTEGER;
       FUNCTION DeleteSection(Index:INTEGER):BOOLEAN;
       FUNCTION ClearSection(Index:INTEGER):BOOLEAN;
       FUNCTION FindDataDirectoryEntrySection(Index:INTEGER):INTEGER;
       FUNCTION DeleteDataDirectoryEntry(Index:INTEGER;WithRemoveSectionHeader:BOOLEAN=FALSE;DoRemoveSectionName:BOOLEAN=FALSE):BOOLEAN;
       FUNCTION Run(CommandLineParameters:STRING):BOOLEAN;
     END;

IMPLEMENTATION

FUNCTION ZwUnmapViewOfSection(ProcessHandle:THandle;BaseAddress:Pointer):LONGWORD; STDCALL; EXTERNAL 'ntdll.dll'

FUNCTION IsWinNT:BOOLEAN;
VAR VersionInfo:TOSVersionInfo;
BEGIN
 VersionInfo.dwOSVersionInfoSize:=SIZEOF(TOSVersionInfo);
 GetVersionEx(VersionInfo);
 RESULT:=VersionInfo.dwPlatformId=VER_PLATFORM_WIN32_NT;
END;

CONSTRUCTOR TBeRoPE.Create;
BEGIN
 INHERITED Create;
 StubHeader:=TBeRoMemoryStream.Create;
 Overlay:=TBeRoMemoryStream.Create;
 Sections:=NIL;
 Clear;
END;

DESTRUCTOR TBeRoPE.Destroy;
BEGIN
 Clear;
 StubHeader.Destroy;
 Overlay.Destroy;
 INHERITED Destroy;
END;

PROCEDURE TBeRoPE.Clear;
VAR Counter:INTEGER;
BEGIN
 StubHeader.Clear;
 Overlay.Clear;
 OverlayOffset:=0;
 FILLCHAR(DOSHeader,SIZEOF(TImageDOSHeader),#0);
 FILLCHAR(NTHeaders,SIZEOF(TImageNTHeaders),#0);
 FOR Counter:=0 TO LENGTH(Sections)-1 DO Sections[Counter].SectionData.Destroy;
 SETLENGTH(Sections,0);
END;

FUNCTION TBeRoPE.IsMZEXE(Stream:TBeRoStream):BOOLEAN;
VAR ImageDOSStandardHeader:TImageDOSStandardHeader;
BEGIN
 RESULT:=FALSE;
 IF ASSIGNED(Stream) THEN BEGIN
  Stream.Seek(0);
  IF Stream.Read(ImageDOSStandardHeader,SIZEOF(TImageDOSStandardHeader))=SIZEOF(TImageDOSStandardHeader) THEN BEGIN
   RESULT:=ImageDOSStandardHeader.Signature=SignMZEXE;
  END;
 END;
END;

FUNCTION TBeRoPE.IsPEEXE(Stream:TBeRoStream):BOOLEAN;
VAR ImageDOSHeader:TImageDOSHeader;
    ImageSignature:TImageSignature;
BEGIN
 RESULT:=FALSE;
 IF ASSIGNED(Stream) THEN BEGIN
  Stream.Seek(0);
  IF Stream.Read(ImageDOSHeader,SIZEOF(TImageDOSHeader))<>SIZEOF(TImageDOSHeader) THEN EXIT;
  IF ImageDOSHeader.StandardHeader.Signature<>SignMZEXE THEN EXIT;
  IF Stream.Seek(ImageDOSHeader.ExtendedHeader.LFAOffset)<>LONGINT(ImageDOSHeader.ExtendedHeader.LFAOffset) THEN EXIT;
  IF Stream.Read(ImageSignature,SIZEOF(TImageSignature))<>SIZEOF(TImageSignature) THEN EXIT;
  RESULT:=ImageSignature=SignPE;
 END;
END;

FUNCTION TBeRoPE.ReadImage(Stream:TBeRoStream):BOOLEAN;
VAR Counter,CurrentOffset,MaxOffset,RemainSize:INTEGER;
BEGIN
 RESULT:=FALSE;
 IF ASSIGNED(Stream) THEN BEGIN
  Clear;
  FILLCHAR(DOSHeader,SIZEOF(TImageDOSHeader),#0);
  FILLCHAR(NTHeaders,SIZEOF(TImageNTHeaders),#0);
  Stream.Seek(0);
  IF Stream.Read(DOSHeader,SIZEOF(TImageDOSHeader))<>SIZEOF(TImageDOSHeader) THEN EXIT;
  IF DOSHeader.StandardHeader.Signature<>SignMZEXE THEN EXIT;
  IF Stream.Seek(DOSHeader.ExtendedHeader.LFAOffset)<>LONGINT(DOSHeader.ExtendedHeader.LFAOffset) THEN EXIT;
  IF Stream.Read(NTHeaders.Signature,SIZEOF(TImageSignature))<>SIZEOF(TImageSignature) THEN EXIT;
  IF NTHeaders.Signature<>SignPE THEN EXIT;
  IF Stream.Read(NTHeaders.FileHeader,SIZEOF(TImageFileHeader))<>SIZEOF(TImageFileHeader) THEN EXIT;
  IF Stream.Read(NTHeaders.OptionalHeader,NTHeaders.FileHeader.SizeOfOptionalHeader)<>NTHeaders.FileHeader.SizeOfOptionalHeader THEN EXIT;
  SETLENGTH(Sections,NTHeaders.FileHeader.NumberOfSections);
  FOR Counter:=0 TO NTHeaders.FileHeader.NumberOfSections-1 DO BEGIN
   IF Stream.Read(Sections[Counter].SectionHeader,SIZEOF(TImageSectionHeader))<>SIZEOF(TImageSectionHeader) THEN BEGIN
    NTHeaders.FileHeader.NumberOfSections:=0;
    SETLENGTH(Sections,0);
    EXIT;
   END;
  END;
  FOR Counter:=0 TO NTHeaders.FileHeader.NumberOfSections-1 DO BEGIN
   Sections[Counter].SectionData:=TBeRoMemoryStream.Create;
  END;
  FOR Counter:=0 TO NTHeaders.FileHeader.NumberOfSections-1 DO BEGIN
   IF Stream.Seek(Sections[Counter].SectionHeader.PointerToRawData)<>LONGINT(Sections[Counter].SectionHeader.PointerToRawData) THEN BEGIN
    Clear;
    EXIT;
   END;
   IF Sections[Counter].SectionData.AppendFrom(Stream,Sections[Counter].SectionHeader.SizeOfRawData)<>LONGINT(Sections[Counter].SectionHeader.SizeOfRawData) THEN BEGIN
    Clear;
    EXIT;
   END;
  END;
  MaxOffset:=0;
  FOR Counter:=0 TO NTHeaders.FileHeader.NumberOfSections-1 DO BEGIN
   CurrentOffset:=Sections[Counter].SectionHeader.PointerToRawData+Sections[Counter].SectionHeader.SizeOfRawData;
   IF CurrentOffset>MaxOffset THEN MaxOffset:=CurrentOffset;
  END;
  RemainSize:=Stream.Size-MaxOffset;
  Overlay.Clear;
  IF RemainSize>0 THEN BEGIN
   OverlayOffset:=MaxOffset;
   Stream.Seek(MaxOffset);
   IF Overlay.AppendFrom(Stream,RemainSize)<>RemainSize THEN BEGIN
    Clear;
    EXIT;
   END;
  END;
  Stream.Seek(0);
  StubHeader.Clear;
  IF StubHeader.AppendFrom(Stream,NTHeaders.OptionalHeader.SizeOfHeaders)<>LONGINT(NTHeaders.OptionalHeader.SizeOfHeaders) THEN BEGIN
   Clear;
   EXIT;
  END;
  RESULT:=TRUE;
 END;
END;

FUNCTION TBeRoPE.WriteImage(Stream:TBeRoStream):BOOLEAN;
VAR Counter,CurrentOffset,MaxOffset:INTEGER;
BEGIN
 RESULT:=FALSE;
 IF ASSIGNED(Stream) THEN BEGIN
  CorrectImageData;
  Stream.Clear;
  Stream.Seek(0);
  StubHeader.Seek(0);
  Stream.Append(StubHeader);
  IF Stream.Seek(0)<>0 THEN EXIT;
  IF Stream.Write(DOSHeader,SIZEOF(TImageDOSHeader))<>SIZEOF(TImageDOSHeader) THEN EXIT;
  IF Stream.Seek(DOSHeader.ExtendedHeader.LFAOffset)<>LONGINT(DOSHeader.ExtendedHeader.LFAOffset) THEN EXIT;
  Stream.WriteByteCount(0,NTHeaders.OptionalHeader.SizeOfHeaders-DOSHeader.ExtendedHeader.LFAOffset);
  IF Stream.Seek(DOSHeader.ExtendedHeader.LFAOffset)<>LONGINT(DOSHeader.ExtendedHeader.LFAOffset) THEN EXIT;
  IF Stream.Write(NTHeaders.Signature,SIZEOF(TImageSignature))<>SIZEOF(TImageSignature) THEN EXIT;
  IF Stream.Write(NTHeaders.FileHeader,SIZEOF(TImageFileHeader))<>SIZEOF(TImageFileHeader) THEN EXIT;
  IF Stream.Write(NTHeaders.OptionalHeader,NTHeaders.FileHeader.SizeOfOptionalHeader)<>NTHeaders.FileHeader.SizeOfOptionalHeader THEN EXIT;
  FOR Counter:=0 TO NTHeaders.FileHeader.NumberOfSections-1 DO BEGIN
   IF Stream.Write(Sections[Counter].SectionHeader,SIZEOF(TImageSectionHeader))<>SIZEOF(TImageSectionHeader) THEN EXIT;
  END;
  FOR Counter:=0 TO NTHeaders.FileHeader.NumberOfSections-1 DO BEGIN
   IF Stream.Seek(Sections[Counter].SectionHeader.PointerToRawData)<>LONGINT(Sections[Counter].SectionHeader.PointerToRawData) THEN EXIT;
   IF Sections[Counter].SectionData.Seek(0)<>0 THEN EXIT;
   IF Stream.AppendFrom(Sections[Counter].SectionData,Sections[Counter].SectionHeader.SizeOfRawData)<>LONGINT(Sections[Counter].SectionHeader.SizeOfRawData) THEN EXIT;
  END;
  IF Overlay.Size>0 THEN BEGIN
   MaxOffset:=0;
   FOR Counter:=0 TO NTHeaders.FileHeader.NumberOfSections-1 DO BEGIN
    CurrentOffset:=Sections[Counter].SectionHeader.PointerToRawData+Sections[Counter].SectionHeader.SizeOfRawData;
    IF CurrentOffset>MaxOffset THEN MaxOffset:=CurrentOffset;
   END;
   IF MaxOffset<OverlayOffset THEN MaxOffset:=OverlayOffset;
   Stream.Seek(MaxOffset);
   Overlay.Seek(0);
   IF Stream.Append(Overlay)<>Overlay.Size THEN BEGIN
    Clear;
    EXIT;
   END;
  END;
  Stream.Seek(Stream.Size);
  RESULT:=CorrectImageCheckSum(Stream);
 END;
END;

FUNCTION TBeRoPE.ReadMemoryImage(Stream:TBeRoStream):BOOLEAN;
VAR Counter:INTEGER;
BEGIN
 RESULT:=FALSE;
 IF ASSIGNED(Stream) THEN BEGIN
  Clear;
  FILLCHAR(DOSHeader,SIZEOF(TImageDOSHeader),#0);
  FILLCHAR(NTHeaders,SIZEOF(TImageNTHeaders),#0);
  Stream.Seek(0);
  IF Stream.Read(DOSHeader,SIZEOF(TImageDOSHeader))<>SIZEOF(TImageDOSHeader) THEN EXIT;
  IF DOSHeader.StandardHeader.Signature<>SignMZEXE THEN EXIT;
  IF Stream.Seek(DOSHeader.ExtendedHeader.LFAOffset)<>LONGINT(DOSHeader.ExtendedHeader.LFAOffset) THEN EXIT;
  IF Stream.Read(NTHeaders.Signature,SIZEOF(TImageSignature))<>SIZEOF(TImageSignature) THEN EXIT;
  IF NTHeaders.Signature<>SignPE THEN EXIT;
  IF Stream.Read(NTHeaders.FileHeader,SIZEOF(TImageFileHeader))<>SIZEOF(TImageFileHeader) THEN EXIT;
  IF Stream.Read(NTHeaders.OptionalHeader,NTHeaders.FileHeader.SizeOfOptionalHeader)<>NTHeaders.FileHeader.SizeOfOptionalHeader THEN EXIT;
  SETLENGTH(Sections,NTHeaders.FileHeader.NumberOfSections);
  FOR Counter:=0 TO NTHeaders.FileHeader.NumberOfSections-1 DO BEGIN
   IF Stream.Read(Sections[Counter].SectionHeader,SIZEOF(TImageSectionHeader))<>SIZEOF(TImageSectionHeader) THEN BEGIN
    NTHeaders.FileHeader.NumberOfSections:=0;
    SETLENGTH(Sections,0);
    EXIT;
   END;
  END;
  FOR Counter:=0 TO NTHeaders.FileHeader.NumberOfSections-1 DO BEGIN
   Sections[Counter].SectionData:=TBeRoMemoryStream.Create;
  END;
  FOR Counter:=0 TO NTHeaders.FileHeader.NumberOfSections-1 DO BEGIN
   IF Stream.Seek(Sections[Counter].SectionHeader.VirtualAddress)<>LONGINT(Sections[Counter].SectionHeader.VirtualAddress) THEN BEGIN
    Clear;
    EXIT;
   END;
   IF Sections[Counter].SectionData.AppendFrom(Stream,Sections[Counter].SectionHeader.SizeOfRawData)<>LONGINT(Sections[Counter].SectionHeader.SizeOfRawData) THEN BEGIN
    Clear;
    EXIT;
   END;
  END;
  Overlay.Clear;
  Stream.Seek(0);
  StubHeader.Clear;
  IF StubHeader.AppendFrom(Stream,NTHeaders.OptionalHeader.SizeOfHeaders)<>LONGINT(NTHeaders.OptionalHeader.SizeOfHeaders) THEN BEGIN
   Clear;
   EXIT;
  END;
  RESULT:=TRUE;
 END;
END;

FUNCTION TBeRoPE.WriteMemoryImage(Stream:TBeRoStream):BOOLEAN;
VAR Counter:INTEGER;
BEGIN
 RESULT:=FALSE;
 IF ASSIGNED(Stream) THEN BEGIN
  CorrectImageData;
  Stream.Clear;
  IF WriteImage(Stream) THEN BEGIN
   Stream.Seek(NTHeaders.OptionalHeader.SizeOfHeaders);
   Stream.WriteByteCount(0,NTHeaders.OptionalHeader.SizeOfImage-NTHeaders.OptionalHeader.SizeOfHeaders);
   Stream.Seek(0);
   FOR Counter:=0 TO NTHeaders.FileHeader.NumberOfSections-1 DO BEGIN
    IF Stream.Seek(Sections[Counter].SectionHeader.VirtualAddress)<>LONGINT(Sections[Counter].SectionHeader.VirtualAddress) THEN EXIT;
    IF Sections[Counter].SectionData.Seek(0)<>0 THEN EXIT;
    IF Stream.AppendFrom(Sections[Counter].SectionData,Sections[Counter].SectionHeader.SizeOfRawData)<>LONGINT(Sections[Counter].SectionHeader.SizeOfRawData) THEN EXIT;
   END;
   Stream.Seek(Stream.Size);
   RESULT:=TRUE;
  END;
 END;
END;

FUNCTION TBeRoPE.ReadImageBuffered(Stream:TBeRoStream):BOOLEAN;
VAR BufferedStream:TBeRoMemoryStream;
BEGIN
 IF ASSIGNED(Stream) THEN BEGIN
  BufferedStream:=TBeRoMemoryStream.Create;
  BufferedStream.Assign(Stream);
  BufferedStream.Seek(0);
  RESULT:=ReadImage(BufferedStream);
  BufferedStream.Destroy;
 END ELSE BEGIN
  RESULT:=FALSE;
 END;
END;

FUNCTION TBeRoPE.WriteImageBuffered(Stream:TBeRoStream):BOOLEAN;
VAR BufferedStream:TBeRoMemoryStream;
BEGIN
 IF ASSIGNED(Stream) THEN BEGIN
  BufferedStream:=TBeRoMemoryStream.Create;
  RESULT:=WriteImage(BufferedStream);
  Stream.Clear;
  Stream.Seek(0);
  Stream.Assign(BufferedStream);
  Stream.Seek(Stream.Size);
  BufferedStream.Destroy;
 END ELSE BEGIN
  RESULT:=FALSE;
 END;
END;

FUNCTION TBeRoPE.ReadMemoryImageBuffered(Stream:TBeRoStream):BOOLEAN;
VAR BufferedStream:TBeRoMemoryStream;
BEGIN
 IF ASSIGNED(Stream) THEN BEGIN
  BufferedStream:=TBeRoMemoryStream.Create;
  BufferedStream.Assign(Stream);
  BufferedStream.Seek(0);
  RESULT:=ReadMemoryImage(BufferedStream);
  BufferedStream.Destroy;
 END ELSE BEGIN
  RESULT:=FALSE;
 END;
END;

FUNCTION TBeRoPE.WriteMemoryImageBuffered(Stream:TBeRoStream):BOOLEAN;
VAR BufferedStream:TBeRoMemoryStream;
BEGIN
 IF ASSIGNED(Stream) THEN BEGIN
  BufferedStream:=TBeRoMemoryStream.Create;
  RESULT:=WriteMemoryImage(BufferedStream);
  Stream.Clear;
  Stream.Seek(0);
  Stream.Assign(BufferedStream);
  Stream.Seek(Stream.Size);
  BufferedStream.Destroy;
 END ELSE BEGIN
  RESULT:=FALSE;
 END;
END;

FUNCTION TBeRoPE.CalculateStreamCheckSum(Stream:TBeRoStream):LONGWORD;
VAR StreamCopy:TBeRoMemoryStream;
    ImageDOSHeader:TImageDOSHeader;
    ImageNTHeaders:TImageNTHeaders;
    OldOffset:INTEGER;
    WordValue:WORD;
    CheckSum:LONGWORD;
BEGIN
 RESULT:=0;
 IF ASSIGNED(Stream) THEN BEGIN
  IF IsPEEXE(Stream) THEN BEGIN
   FILLCHAR(ImageDOSHeader,SIZEOF(TImageDOSHeader),#0);
   FILLCHAR(ImageNTHeaders,SIZEOF(TImageNTHeaders),#0);
   StreamCopy:=TBeRoMemoryStream.Create;
   StreamCopy.Assign(Stream);
   IF StreamCopy.Seek(0)<>0 THEN EXIT;
   IF StreamCopy.Read(ImageDOSHeader,SIZEOF(TImageDOSHeader))<>SIZEOF(TImageDOSHeader) THEN EXIT;
   IF ImageDOSHeader.StandardHeader.Signature<>SignMZEXE THEN EXIT;
   IF StreamCopy.Seek(ImageDOSHeader.ExtendedHeader.LFAOffset)<>LONGINT(ImageDOSHeader.ExtendedHeader.LFAOffset) THEN EXIT;
   IF StreamCopy.Read(ImageNTHeaders.Signature,SIZEOF(TImageSignature))<>SIZEOF(TImageSignature) THEN EXIT;
   IF ImageNTHeaders.Signature<>SignPE THEN EXIT;
   IF StreamCopy.Read(ImageNTHeaders.FileHeader,SIZEOF(TImageFileHeader))<>SIZEOF(TImageFileHeader) THEN EXIT;
   OldOffset:=StreamCopy.Position;
   IF StreamCopy.Read(ImageNTHeaders.OptionalHeader,ImageNTHeaders.FileHeader.SizeOfOptionalHeader)<>ImageNTHeaders.FileHeader.SizeOfOptionalHeader THEN EXIT;
   ImageNTHeaders.OptionalHeader.CheckSum:=0;
   StreamCopy.Seek(OldOffset);
   IF StreamCopy.Write(ImageNTHeaders.OptionalHeader,ImageNTHeaders.FileHeader.SizeOfOptionalHeader)<>ImageNTHeaders.FileHeader.SizeOfOptionalHeader THEN EXIT;
   StreamCopy.Seek(0);
   CheckSum:=0;
   WHILE StreamCopy.Position<StreamCopy.Size DO BEGIN
    IF StreamCopy.Read(WordValue,SIZEOF(WORD))<>SIZEOF(WORD) THEN BEGIN
     Clear;
     EXIT;
    END;
    INC(CheckSum,WordValue);
    CheckSum:=((CheckSum AND $FFFF)+(CheckSum SHR 16)) AND $FFFF;
   END;
   INC(CheckSum,StreamCopy.Size);
   RESULT:=CheckSum;
   StreamCopy.Destroy;
  END;
 END;
END;

FUNCTION TBeRoPE.CalculateImageCheckSum:LONGWORD;
VAR Stream:TBeRoMemoryStream;
BEGIN
 Stream:=TBeRoMemoryStream.Create;
 IF WriteImage(Stream) THEN BEGIN
  RESULT:=CalculateStreamCheckSum(Stream);
 END ELSE BEGIN
  RESULT:=0;
 END;
 Stream.Destroy;
END;

FUNCTION TBeRoPE.CorrectImageCheckSum(Stream:TBeRoStream):BOOLEAN;
VAR ImageDOSHeader:TImageDOSHeader;
    ImageNTHeaders:TImageNTHeaders;
    OldOffset:INTEGER;
    WordValue:WORD;
    CheckSum:LONGWORD;
BEGIN
 RESULT:=FALSE;
 IF ASSIGNED(Stream) THEN BEGIN
  FILLCHAR(ImageDOSHeader,SIZEOF(TImageDOSHeader),#0);
  FILLCHAR(ImageNTHeaders,SIZEOF(TImageNTHeaders),#0);
  IF Stream.Seek(0)<>0 THEN EXIT;
  IF Stream.Read(ImageDOSHeader,SIZEOF(TImageDOSHeader))<>SIZEOF(TImageDOSHeader) THEN EXIT;
  IF ImageDOSHeader.StandardHeader.Signature<>SignMZEXE THEN EXIT;
  IF Stream.Seek(ImageDOSHeader.ExtendedHeader.LFAOffset)<>LONGINT(ImageDOSHeader.ExtendedHeader.LFAOffset) THEN EXIT;
  IF Stream.Read(ImageNTHeaders.Signature,SIZEOF(TImageSignature))<>SIZEOF(TImageSignature) THEN EXIT;
  IF ImageNTHeaders.Signature<>SignPE THEN EXIT;
  IF Stream.Read(ImageNTHeaders.FileHeader,SIZEOF(TImageFileHeader))<>SIZEOF(TImageFileHeader) THEN EXIT;
  OldOffset:=Stream.Position;
  IF Stream.Read(ImageNTHeaders.OptionalHeader,ImageNTHeaders.FileHeader.SizeOfOptionalHeader)<>ImageNTHeaders.FileHeader.SizeOfOptionalHeader THEN EXIT;
  ImageNTHeaders.OptionalHeader.CheckSum:=0;
  Stream.Seek(OldOffset);
  IF Stream.Write(ImageNTHeaders.OptionalHeader,ImageNTHeaders.FileHeader.SizeOfOptionalHeader)<>ImageNTHeaders.FileHeader.SizeOfOptionalHeader THEN EXIT;
  Stream.Seek(0);
  CheckSum:=0;
  WHILE Stream.Position<Stream.Size DO BEGIN
   IF Stream.Read(WordValue,SIZEOF(WORD))<=0 THEN BEGIN
    EXIT;
   END;
   INC(CheckSum,WordValue);
   CheckSum:=((CheckSum AND $FFFF)+(CheckSum SHR 16)) AND $FFFF;
  END;
  INC(CheckSum,Stream.Size);
  ImageNTHeaders.OptionalHeader.CheckSum:=CheckSum;
  Stream.Seek(OldOffset);
  IF Stream.Write(ImageNTHeaders.OptionalHeader,ImageNTHeaders.FileHeader.SizeOfOptionalHeader)<>ImageNTHeaders.FileHeader.SizeOfOptionalHeader THEN EXIT;
  Stream.Seek(Stream.Size);
  RESULT:=TRUE;
 END;
END;

PROCEDURE TBeRoPE.InitializeImage;
BEGIN
 Clear;
 StubHeader.Clear;
 StubHeader.WriteWord(SignMZEXE);
 StubHeader.WriteWord($3C);
 StubHeader.WriteWord(1);
 StubHeader.WriteWord(0);
 StubHeader.WriteWord(2);
 StubHeader.WriteWord(0);
 StubHeader.WriteWord($FFFF);
 StubHeader.WriteWord(0);
 StubHeader.WriteWord($98);
 StubHeader.WriteWord(0);
 StubHeader.WriteWord(0);
 StubHeader.WriteWord(0);
 StubHeader.WriteWord($20);
 StubHeader.WriteWord(0);
 StubHeader.WriteWord(0);
 StubHeader.WriteWord(0);
 StubHeader.WriteByte($0E); // PUSH CS      DS=CS
 StubHeader.WriteByte($1F); // POP DS       (Data segment = code segment)
  StubHeader.WriteByte($BA); // MOV DX,text\-----------+
 StubHeader.WriteWord($0D); // MOV DX,text/           I
 StubHeader.WriteWord($09B4); // MOV AH,09h print str I
 StubHeader.WriteWord($21CD); // INT 21h              I
 StubHeader.WriteWord($4CB4); // MOV AH,4Ch exit      I
 StubHeader.WriteWord($21CD); // INT 21h              I
 StubHeader.WriteString('W32NotFound!'#13#10+'$');//<-+
 StubHeader.WriteDWord($40); // LFA-Offset
 StubHeader.WriteByteCount(0,$200-StubHeader.Size);
 FILLCHAR(DOSHeader,SIZEOF(TImageDOSHeader),#0);
 FILLCHAR(NTHeaders,SIZEOF(TImageNTHeaders),#0);
 StubHeader.Seek(0);
 StubHeader.Read(DOSHeader,SIZEOF(TImageDOSHeader));
 NTHeaders.Signature:=SignPE;
 NTHeaders.FileHeader.Machine:=IMAGE_FILE_MACHINE_I386;
 NTHeaders.FileHeader.NumberOfSections:=0;
 NTHeaders.FileHeader.TimeDateStamp:=0;
 NTHeaders.FileHeader.PointerToSymbolTable:=0;
 NTHeaders.FileHeader.NumberOfSymbols:=0;
 NTHeaders.FileHeader.SizeOfOptionalHeader:=SIZEOF(TImageOptionalHeader);
 NTHeaders.FileHeader.Characteristics:=IMAGE_FILE_EXECUTABLE_IMAGE OR IMAGE_FILE_32BIT_MACHINE;
 NTHeaders.OptionalHeader.Magic:=$10B;
 NTHeaders.OptionalHeader.MajorLinkerVersion:=0;
 NTHeaders.OptionalHeader.MinorLinkerVersion:=0;
 NTHeaders.OptionalHeader.SizeOfCode:=0;
 NTHeaders.OptionalHeader.SizeOfInitializedData:=0;
 NTHeaders.OptionalHeader.SizeOfUninitializedData:=0;
 NTHeaders.OptionalHeader.AddressOfEntryPoint:=0;
 NTHeaders.OptionalHeader.BaseOfCode:=0;
 NTHeaders.OptionalHeader.BaseOfData:=0;
 NTHeaders.OptionalHeader.ImageBase:=0;
 NTHeaders.OptionalHeader.SectionAlignment:=$1000;
 NTHeaders.OptionalHeader.FileAlignment:=$200;
 NTHeaders.OptionalHeader.MajorOperatingSystemVersion:=1;
 NTHeaders.OptionalHeader.MinorOperatingSystemVersion:=0;
 NTHeaders.OptionalHeader.MajorImageVersion:=0;
 NTHeaders.OptionalHeader.MinorImageVersion:=0;
 NTHeaders.OptionalHeader.MajorSubsystemVersion:=4;
 NTHeaders.OptionalHeader.MinorSubsystemVersion:=0;
 NTHeaders.OptionalHeader.Win32VersionValue:=0;
 NTHeaders.OptionalHeader.SizeOfImage:=0;
 NTHeaders.OptionalHeader.SizeOfHeaders:=$200;
 NTHeaders.OptionalHeader.CheckSum:=0;
 NTHeaders.OptionalHeader.Subsystem:=0;
 NTHeaders.OptionalHeader.DllCharacteristics:=0;
 NTHeaders.OptionalHeader.SizeOfStackReserve:=0;
 NTHeaders.OptionalHeader.SizeOfStackCommit:=0;
 NTHeaders.OptionalHeader.SizeOfHeapReserve:=0;
 NTHeaders.OptionalHeader.SizeOfHeapCommit:=0;
 NTHeaders.OptionalHeader.LoaderFlags:=0;
 NTHeaders.OptionalHeader.NumberOfRvaAndSizes:=IMAGE_NUMBEROF_DIRECTORY_ENTRIES;
END;

FUNCTION TBeRoPE.CorrectSectionAlign(Offset:LONGWORD):LONGWORD;
BEGIN
 RESULT:=Offset;
 IF NTHeaders.OptionalHeader.SectionAlignment<>0 THEN BEGIN
  IF (RESULT MOD NTHeaders.OptionalHeader.SectionAlignment)<>0 THEN BEGIN
   INC(RESULT,NTHeaders.OptionalHeader.SectionAlignment-(RESULT MOD NTHeaders.OptionalHeader.SectionAlignment));
  END;
 END;
END;

FUNCTION TBeRoPE.CorrectFileAlign(Offset:LONGWORD):LONGWORD;
BEGIN
 RESULT:=Offset;
 IF NTHeaders.OptionalHeader.FileAlignment<>0 THEN BEGIN
  IF (RESULT MOD NTHeaders.OptionalHeader.FileAlignment)<>0 THEN BEGIN
   INC(RESULT,NTHeaders.OptionalHeader.FileAlignment-(RESULT MOD NTHeaders.OptionalHeader.FileAlignment));
  END;
 END;
END;

PROCEDURE TBeRoPE.CorrectImageData;
VAR Counter,CurrentOffset:INTEGER;
    MaxOffset:LONGWORD;
BEGIN
 NTHeaders.FileHeader.NumberOfSections:=LENGTH(Sections);
 NTHeaders.OptionalHeader.SizeOfHeaders:=CorrectFileAlign(DOSHeader.ExtendedHeader.LFAOffset+SIZEOF(TImageFileHeader)+NTHeaders.FileHeader.SizeOfOptionalHeader+(NTHeaders.FileHeader.NumberOfSections*SIZEOF(TImageSectionHeader)));
 CurrentOffset:=NTHeaders.OptionalHeader.SizeOfHeaders;
 FOR Counter:=0 TO NTHeaders.FileHeader.NumberOfSections-1 DO BEGIN
  IF Sections[Counter].SectionHeader.SizeOfRawData<>0 THEN BEGIN
   CurrentOffset:=CorrectFileAlign(CurrentOffset);
   Sections[Counter].SectionHeader.PointerToRawData:=CurrentOffset;
   Sections[Counter].SectionHeader.SizeOfRawData:=Sections[Counter].SectionData.Size;
   INC(CurrentOffset,Sections[Counter].SectionHeader.SizeOfRawData);
  END;
 END;
 OverlayOffset:=CorrectFileAlign(CurrentOffset);
 MaxOffset:=0;
 FOR Counter:=0 TO NTHeaders.FileHeader.NumberOfSections-1 DO BEGIN
  IF MaxOffset<(Sections[Counter].SectionHeader.VirtualAddress+Sections[Counter].SectionHeader.Misc.VirtualSize) THEN BEGIN
   MaxOffset:=Sections[Counter].SectionHeader.VirtualAddress+Sections[Counter].SectionHeader.Misc.VirtualSize;
  END;
 END;
 NTHeaders.OptionalHeader.SizeOfImage:=CorrectSectionAlign(MaxOffset);
END;

FUNCTION TBeRoPE.GetNewAlignedVirtualAddress:LONGWORD;
VAR Counter:INTEGER;
    NewVirtualAddress:LONGWORD;
BEGIN
 NewVirtualAddress:=0;
 FOR Counter:=0 TO NTHeaders.FileHeader.NumberOfSections-1 DO BEGIN
  IF Sections[Counter].SectionHeader.Misc.VirtualSize<>0 THEN BEGIN
   IF NewVirtualAddress<Sections[Counter].SectionHeader.VirtualAddress THEN BEGIN
    NewVirtualAddress:=Sections[Counter].SectionHeader.VirtualAddress;
   END;
   INC(NewVirtualAddress,Sections[Counter].SectionHeader.Misc.VirtualSize);
  END;
  NewVirtualAddress:=CorrectSectionAlign(NewVirtualAddress);
 END;
 RESULT:=NewVirtualAddress;
END;

FUNCTION TBeRoPE.CreateNewSection(VirtualSize:LONGWORD):INTEGER;
VAR Index:INTEGER;
    NewVirtualAddress:LONGWORD;
BEGIN
 NewVirtualAddress:=GetNewAlignedVirtualAddress;
 Index:=LENGTH(Sections);
 SETLENGTH(Sections,Index+1);
 FILLCHAR(Sections[Index].SectionHeader,SIZEOF(TImageSectionHeader),#0);
 Sections[Index].SectionHeader.Misc.VirtualSize:=VirtualSize;
 Sections[Index].SectionHeader.VirtualAddress:=NewVirtualAddress;
 Sections[Index].SectionData:=TBeRoMemoryStream.Create;
 NTHeaders.FileHeader.NumberOfSections:=LENGTH(Sections);
 CorrectImageData;
 RESULT:=Index;
END;

FUNCTION TBeRoPE.DeleteSection(Index:INTEGER):BOOLEAN;
VAR Counter:INTEGER;
BEGIN
 RESULT:=(Index>=0) AND (Index<LENGTH(Sections));
 IF RESULT THEN BEGIN
  Sections[Index].SectionData.Destroy;
  FOR Counter:=Index TO LENGTH(Sections)-2 DO BEGIN
   Sections[Index]:=Sections[Index+1];
  END;
  SETLENGTH(Sections,LENGTH(Sections)-1);
  NTHeaders.FileHeader.NumberOfSections:=LENGTH(Sections);
  CorrectImageData;
 END;
END;

FUNCTION TBeRoPE.ClearSection(Index:INTEGER):BOOLEAN;
BEGIN
 RESULT:=(Index>=0) AND (Index<LENGTH(Sections));
 IF RESULT THEN BEGIN
  Sections[Index].SectionData.Clear;
  Sections[Index].SectionHeader.SizeOfRawData:=0;
  CorrectImageData;
 END;
END;

FUNCTION TBeRoPE.FindDataDirectoryEntrySection(Index:INTEGER):INTEGER;
VAR Counter:INTEGER;
BEGIN
 RESULT:=-1;
 IF (Index>=0) AND (Index<IMAGE_NUMBEROF_DIRECTORY_ENTRIES) THEN BEGIN
  IF NTHeaders.OptionalHeader.DataDirectory[Index].Size<>0 THEN BEGIN
   FOR Counter:=0 TO NTHeaders.FileHeader.NumberOfSections-1 DO BEGIN
    IF (Sections[Counter].SectionHeader.VirtualAddress=NTHeaders.OptionalHeader.DataDirectory[Index].VirtualAddress) AND
       (Sections[Counter].SectionHeader.Misc.VirtualSize=NTHeaders.OptionalHeader.DataDirectory[Index].Size) THEN BEGIN
     RESULT:=Counter;
     EXIT;
    END;
   END;
  END;
 END;
END;

FUNCTION TBeRoPE.DeleteDataDirectoryEntry(Index:INTEGER;WithRemoveSectionHeader:BOOLEAN=FALSE;DoRemoveSectionName:BOOLEAN=FALSE):BOOLEAN;
VAR SectionIndex:INTEGER;
BEGIN
 RESULT:=(Index>=0) AND (Index<IMAGE_NUMBEROF_DIRECTORY_ENTRIES);
 IF RESULT THEN BEGIN
  RESULT:=NTHeaders.OptionalHeader.DataDirectory[Index].Size<>0;
  IF RESULT THEN BEGIN
   SectionIndex:=FindDataDirectoryEntrySection(Index);
   IF SectionIndex>=0 THEN BEGIN
    IF WithRemoveSectionHeader THEN BEGIN
     RESULT:=RESULT AND DeleteSection(SectionIndex);
    END ELSE BEGIN
     RESULT:=RESULT AND ClearSection(SectionIndex);
     IF RESULT AND DoRemoveSectionName THEN BEGIN
      Sections[SectionIndex].SectionHeader.Name:=#0#0#0#0#0#0#0#0;
     END;
    END;
   END;
   IF RESULT THEN BEGIN
    NTHeaders.OptionalHeader.DataDirectory[Index].VirtualAddress:=0;
    NTHeaders.OptionalHeader.DataDirectory[Index].Size:=0;
   END;
  END;
 END;
END;

FUNCTION TBeRoPE.DoAlign(Value,Alignment:LONGWORD):LONGWORD;
BEGIN
 RESULT:=Value;
 IF (RESULT MOD Alignment)<>0 THEN INC(RESULT,Alignment-(RESULT MOD Alignment));
END;

FUNCTION TBeRoPE.RunEx(Image:POINTER;CommandLineParameters:STRING):BOOLEAN;
VAR Data,HeaderSize,ImageDataSize,SectionSize,CurrentAddress,OldProtect:LONGWORD;
    Src,Dst,ImagePointer,ImageDataMemory:POINTER;
    Counter:INTEGER;
    Context:TContext;
    NewProcInfo:TProcInfo;
    ImageSections:PImageSectionHeaders;
    StartupInfo:TStartupInfo;
    ImageNTHeaders:PImageNTHeaders;
    ProcInfo:TProcessInformation;
    MemInfo:MEMORY_BASIC_INFORMATION;
BEGIN
 RESULT:=FALSE;
 TRY
  // Skip DOS MZ EXE header
  ImageNTHeaders:=POINTER(LONGWORD(Image)+LONGWORD(PImageDOSHeader(Image).ExtendedHeader.LFAOffset));

  // Calculate the image size
  ImageDataSize:=DoAlign(ImageNTHeaders.OptionalHeader.SizeOfHeaders,ImageNTHeaders.OptionalHeader.SectionAlignment);
  ImageSections:=POINTER(LONGWORD(@ImageNTHeaders.OptionalHeader)+ImageNTHeaders.FileHeader.SizeOfOptionalHeader);
  FOR Counter:=0 TO ImageNTHeaders.FileHeader.NumberOfSections-1 DO BEGIN
   IF ImageSections^[Counter].Misc.VirtualSize<>0 THEN BEGIN
    IF ImageDataSize<ImageSections^[Counter].VirtualAddress THEN BEGIN
     ImageDataSize:=ImageSections^[Counter].VirtualAddress;
    END;
    INC(ImageDataSize,DoAlign(ImageSections^[Counter].Misc.VirtualSize,ImageNTHeaders.OptionalHeader.SectionAlignment));
   END;
  END;

  // Get memory for the memory image
  ImageDataMemory:=VirtualAlloc(NIL,ImageDataSize,MEM_COMMIT,PAGE_EXECUTE_READWRITE);

  // Check and correct header size
  HeaderSize:=ImageNTHeaders.OptionalHeader.SizeOfHeaders;
  FOR Counter:=0 TO ImageNTHeaders.FileHeader.NumberOfSections-1 DO BEGIN
   IF ImageSections^[Counter].PointerToRawData<HeaderSize THEN BEGIN
    HeaderSize:=ImageSections^[Counter].PointerToRawData;
   END;
  END;

  // Copy file header to the begin of the memory image
  MOVE(Image^,ImageDataMemory^,HeaderSize);

  // Copy sections into the memory image
  FOR Counter:=0 TO ImageNTHeaders.FileHeader.NumberOfSections-1 DO BEGIN
   IF ImageSections^[Counter].SizeOfRawData>0 THEN BEGIN
    SectionSize:=ImageSections^[Counter].SizeOfRawData;
    IF SectionSize>ImageSections^[Counter].Misc.VirtualSize THEN BEGIN
     SectionSize:=ImageSections^[Counter].Misc.VirtualSize;
    END;
    Src:=POINTER(LONGWORD(Image)+ImageSections^[Counter].PointerToRawData);
    Dst:=POINTER(LONGWORD(ImageDataMemory)+ImageSections^[Counter].VirtualAddress);
    MOVE(Src^,Dst^,SectionSize);
   END;
  END;

  // Clear structures
  FILLCHAR(StartupInfo,SIZEOF(TStartupInfo),#0);
  FILLCHAR(Context,SIZEOF(TContext),#0);

  // Create a new process
  IF CreateProcess(NIL,PCHAR(CommandLineParameters),NIL,NIL,FALSE,CREATE_SUSPENDED,NIL,NIL,StartupInfo,ProcInfo) THEN BEGIN
   Context.ContextFlags:=CONTEXT_FULL;
   GetThreadContext(ProcInfo.hThread,Context);

   // Read process memory
   ReadProcessMemory(ProcInfo.hProcess,POINTER(Context.EBX+8),@NewProcInfo.BaseAddr,SIZEOF(LONGWORD),Data);

   // Get base address and image size
   CurrentAddress:=NewProcInfo.BaseAddr;
   WHILE VirtualQueryEx(ProcInfo.hProcess,POINTER(CurrentAddress),MemInfo,SIZEOF(MemInfo))<>0 DO BEGIN
    IF MemInfo.State=MEM_FREE THEN BREAK;
    INC(CurrentAddress,MemInfo.RegionSize);
   END;
   NewProcInfo.ImageSize:=CurrentAddress-LONGWORD(NewProcInfo.BaseAddr);

   // Overwrite the exist image memory code
   ImagePointer:=NIL;
   IF (ImageDataSize<=NewProcInfo.ImageSize) AND (ImageNTHeaders.OptionalHeader.ImageBase=NewProcInfo.BaseAddr) THEN BEGIN
    ImagePointer:=POINTER(NewProcInfo.BaseAddr);
    VirtualProtectEx(ProcInfo.hProcess,ImagePointer,NewProcInfo.ImageSize,PAGE_EXECUTE_READWRITE,@OldProtect);
   END ELSE BEGIN
    // Unmap old image
    IF (ZwUnmapViewOfSection(ProcInfo.hProcess,POINTER(NewProcInfo.BaseAddr)))=0 THEN BEGIN
     // Get new memory at the base address
     ImagePointer:=VirtualAllocEx(ProcInfo.hProcess,Pointer(ImageNtHeaders.OptionalHeader.ImageBase),ImageDataSize,MEM_RESERVE OR MEM_COMMIT,PAGE_EXECUTE_READWRITE);
    END;
   END;
   IF ASSIGNED(ImagePointer) THEN BEGIN
    // Patch image
    WriteProcessMemory(ProcInfo.hProcess,Pointer(Context.EBX+8),@ImagePointer,SIZEOF(LONGWORD),Data);
    IF WriteProcessMemory(ProcInfo.hProcess,ImagePointer,ImageDataMemory,ImageDataSize,Data) THEN BEGIN
     Context.ContextFlags:=CONTEXT_FULL;
     IF LONGWORD(ImagePointer)=NewProcInfo.BaseAddr THEN BEGIN
      Context.EAX:=LONGWORD(ImageNTHeaders.OptionalHeader.ImageBase)+ImageNtHeaders.OptionalHeader.AddressOfEntryPoint;
     END ELSE BEGIN
      Context.EAX:=LONGWORD(ImagePointer)+ImageNtHeaders.OptionalHeader.AddressOfEntryPoint;
     END;
     SetThreadContext(ProcInfo.hThread,Context);
     RESULT:=ResumeThread(ProcInfo.hThread)<>$FFFFFFFF;
     IF NOT RESULT THEN TerminateProcess(ProcInfo.hProcess,0);
    END;
   END;
  END;
 FINALLY
 END;
END;

FUNCTION TBeRoPE.Run(CommandLineParameters:STRING):BOOLEAN;
VAR ImageStream:TBeRoMemoryStream;
    Image:POINTER;
BEGIN
 IF IsWinNT THEN BEGIN
  ImageStream:=TBeRoMemoryStream.Create;
  WriteImage(ImageStream);
  ImageStream.Seek(0);
  GETMEM(Image,ImageStream.Size);
  ImageStream.Read(Image^,ImageStream.Size);
  ImageStream.Destroy;
  RESULT:=RunEx(Image,CommandLineParameters);
  FREEMEM(Image);
 END ELSE BEGIN
  RESULT:=FALSE;
 END;
END;

END.
