unit Shared.ResUpdateFunc; { Inno Setup Copyright (C) 1997-2016 Jordan Russell Portions by Martijn Laan For conditions of distribution and use, see LICENSE.TXT. Resource update functions used by both Setup and the compiler } interface uses Windows, SysUtils, Shared.FileClass; function ReadSignatureAndChecksumFields(const F: TCustomFile; var ASignatureAddress, ASignatureSize, AChecksum: DWORD): Boolean; function ReadSignatureAndChecksumFields64(const F: TCustomFile; var ASignatureAddress, ASignatureSize, AChecksum: DWORD): Boolean; function SeekToResourceData(const F: TCustomFile; const ResType, ResId: Cardinal): Cardinal; function UpdateSignatureAndChecksumFields(const F: TCustomFile; const ASignatureAddress, ASignatureSize, AChecksum: DWORD): Boolean; procedure UpdateManifestRequestedExecutionLevel(const F: TCustomFile; const RequireAdministrator: Boolean); implementation uses Shared.Int64Em; const IMAGE_NT_SIGNATURE = $00004550; IMAGE_NT_OPTIONAL_HDR32_MAGIC = $10b; IMAGE_NT_OPTIONAL_HDR64_MAGIC = $20b; IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16; IMAGE_SIZEOF_SHORT_NAME = 8; IMAGE_DIRECTORY_ENTRY_RESOURCE = 2; IMAGE_DIRECTORY_ENTRY_SECURITY = 4; type PImageFileHeader = ^TImageFileHeader; TImageFileHeader = packed record Machine: Word; NumberOfSections: Word; TimeDateStamp: DWORD; PointerToSymbolTable: DWORD; NumberOfSymbols: DWORD; SizeOfOptionalHeader: Word; Characteristics: Word; end; PImageDataDirectory = ^TImageDataDirectory; TImageDataDirectory = record VirtualAddress: DWORD; Size: DWORD; end; PImageOptionalHeader = ^TImageOptionalHeader; TImageOptionalHeader = packed record { Standard fields. } Magic: Word; MajorLinkerVersion: Byte; MinorLinkerVersion: Byte; SizeOfCode: DWORD; SizeOfInitializedData: DWORD; SizeOfUninitializedData: DWORD; AddressOfEntryPoint: DWORD; BaseOfCode: DWORD; BaseOfData: DWORD; { NT additional fields. } ImageBase: DWORD; SectionAlignment: DWORD; FileAlignment: DWORD; MajorOperatingSystemVersion: Word; MinorOperatingSystemVersion: Word; MajorImageVersion: Word; MinorImageVersion: Word; MajorSubsystemVersion: Word; MinorSubsystemVersion: Word; Win32VersionValue: DWORD; SizeOfImage: DWORD; SizeOfHeaders: DWORD; CheckSum: DWORD; Subsystem: Word; DllCharacteristics: Word; SizeOfStackReserve: DWORD; SizeOfStackCommit: DWORD; SizeOfHeapReserve: DWORD; SizeOfHeapCommit: DWORD; LoaderFlags: DWORD; NumberOfRvaAndSizes: DWORD; DataDirectory: packed array[0..IMAGE_NUMBEROF_DIRECTORY_ENTRIES-1] of TImageDataDirectory; end; PImageOptionalHeader64 = ^TImageOptionalHeader64; TImageOptionalHeader64 = packed record { Standard fields. } Magic: Word; MajorLinkerVersion: Byte; MinorLinkerVersion: Byte; SizeOfCode: DWORD; SizeOfInitializedData: DWORD; SizeOfUninitializedData: DWORD; AddressOfEntryPoint: DWORD; BaseOfCode: DWORD; { NT additional fields. } ImageBase: Integer64; SectionAlignment: DWORD; FileAlignment: DWORD; MajorOperatingSystemVersion: Word; MinorOperatingSystemVersion: Word; MajorImageVersion: Word; MinorImageVersion: Word; MajorSubsystemVersion: Word; MinorSubsystemVersion: Word; Win32VersionValue: DWORD; SizeOfImage: DWORD; SizeOfHeaders: DWORD; CheckSum: DWORD; Subsystem: Word; DllCharacteristics: Word; SizeOfStackReserve: Integer64; SizeOfStackCommit: Integer64; SizeOfHeapReserve: Integer64; SizeOfHeapCommit: Integer64; LoaderFlags: DWORD; NumberOfRvaAndSizes: DWORD; DataDirectory: packed array[0..IMAGE_NUMBEROF_DIRECTORY_ENTRIES-1] of TImageDataDirectory; end; TISHMisc = packed record case Integer of 0: (PhysicalAddress: DWORD); 1: (VirtualSize: DWORD); end; PImageSectionHeader = ^TImageSectionHeader; TImageSectionHeader = packed record Name: packed array[0..IMAGE_SIZEOF_SHORT_NAME-1] of Byte; Misc: TISHMisc; VirtualAddress: DWORD; SizeOfRawData: DWORD; PointerToRawData: DWORD; PointerToRelocations: DWORD; PointerToLinenumbers: DWORD; NumberOfRelocations: Word; NumberOfLinenumbers: Word; Characteristics: DWORD; end; TImageResourceDirectory = packed record Characteristics: DWORD; TimeDateStamp: DWORD; MajorVersion: Word; MinorVersion: Word; NumberOfNamedEntries: Word; NumberOfIdEntries: Word; end; TImageResourceDirectoryEntry = packed record Id: DWORD; Offset: DWORD; end; TImageResourceDataEntry = packed record OffsetToData: DWORD; Size: DWORD; CodePage: DWORD; Reserved: DWORD; end; procedure Error(const Msg: String); begin raise Exception.Create('Resource update error: ' + Msg); end; function SeekToPEHeader(const F: TCustomFile): Boolean; var DosHeader: packed record Sig: array[0..1] of AnsiChar; Other: array[0..57] of Byte; PEHeaderOffset: LongWord; end; Sig: DWORD; begin Result := False; F.Seek(0); if F.Read(DosHeader, SizeOf(DosHeader)) = SizeOf(DosHeader) then begin if (DosHeader.Sig[0] = 'M') and (DosHeader.Sig[1] = 'Z') and (DosHeader.PEHeaderOffset <> 0) then begin F.Seek(DosHeader.PEHeaderOffset); if F.Read(Sig, SizeOf(Sig)) = SizeOf(Sig) then if Sig = IMAGE_NT_SIGNATURE then Result := True; end; end; end; function SeekToAndReadPEOptionalHeader(const F: TCustomFile; var OptHeader: TImageOptionalHeader; var OptHeaderOffset: Integer64): Boolean; var Header: TImageFileHeader; begin Result := False; if SeekToPEHeader(F) then begin if (F.Read(Header, SizeOf(Header)) = SizeOf(Header)) and (Header.SizeOfOptionalHeader = SizeOf(OptHeader)) then begin OptHeaderOffset := F.Position; if F.Read(OptHeader, SizeOf(OptHeader)) = SizeOf(OptHeader) then if OptHeader.Magic = IMAGE_NT_OPTIONAL_HDR32_MAGIC then Result := True; end; end; end; function SeekToAndReadPEOptionalHeader64(const F: TCustomFile; var OptHeader: TImageOptionalHeader64; var OptHeaderOffset: Integer64): Boolean; var Header: TImageFileHeader; begin Result := False; if SeekToPEHeader(F) then begin if (F.Read(Header, SizeOf(Header)) = SizeOf(Header)) and (Header.SizeOfOptionalHeader = SizeOf(OptHeader)) then begin OptHeaderOffset := F.Position; if F.Read(OptHeader, SizeOf(OptHeader)) = SizeOf(OptHeader) then if OptHeader.Magic = IMAGE_NT_OPTIONAL_HDR64_MAGIC then Result := True; end; end; end; procedure FindResourceSection(const F: TCustomFile; var SectionVirtualAddr, SectionPhysOffset, SectionPhysSize: Cardinal); var EXESig: Word; PEHeaderOffset, PESig: Cardinal; PEHeader: TImageFileHeader; PEOptHeader: TImageOptionalHeader; PESectionHeader: TImageSectionHeader; I: Integer; begin { Read DOS header } F.Seek(0); F.ReadBuffer(EXESig, SizeOf(EXESig)); if EXESig <> $5A4D {'MZ'} then Error('File isn''t an EXE file (1)'); F.Seek($3C); F.ReadBuffer(PEHeaderOffset, SizeOf(PEHeaderOffset)); if PEHeaderOffset = 0 then Error('File isn''t a PE file (1)'); { Read PE header & optional header } F.Seek(PEHeaderOffset); F.ReadBuffer(PESig, SizeOf(PESig)); if PESig <> $00004550 {'PE'#0#0} then Error('File isn''t a PE file (2)'); F.ReadBuffer(PEHeader, SizeOf(PEHeader)); if PEHeader.SizeOfOptionalHeader <> SizeOf(PEOptHeader) then Error('File isn''t a PE file (3)'); F.ReadBuffer(PEOptHeader, SizeOf(PEOptHeader)); if PEOptHeader.Magic <> IMAGE_NT_OPTIONAL_HDR32_MAGIC then Error('File isn''t a PE file (4)'); { Scan section headers for resource section } if (PEOptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = 0) or (PEOptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = 0) then Error('No resources (1)'); SectionVirtualAddr := PEOptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress; SectionPhysOffset := 0; for I := 0 to PEHeader.NumberOfSections-1 do begin F.ReadBuffer(PESectionHeader, SizeOf(PESectionHeader)); if (PESectionHeader.VirtualAddress = SectionVirtualAddr) and (PESectionHeader.SizeOfRawData <> 0) then begin SectionPhysOffset := PESectionHeader.PointerToRawData; SectionPhysSize := PESectionHeader.SizeOfRawData; Break; end; end; if SectionPhysOffset = 0 then Error('No resources (2)'); end; function FindResOffset(const F: TCustomFile; const AnyId: Boolean; const Id: Cardinal; const FindSubdir: Boolean; var Offset: Cardinal): Boolean; var Dir: TImageResourceDirectory; Entry: TImageResourceDirectoryEntry; I: Integer; begin F.ReadBuffer(Dir, SizeOf(Dir)); { Skip over named entries } for I := 0 to Dir.NumberOfNamedEntries-1 do F.ReadBuffer(Entry, SizeOf(Entry)); { Now process ID entries } Result := False; for I := 0 to Dir.NumberOfIdEntries-1 do begin F.ReadBuffer(Entry, SizeOf(Entry)); if (AnyId or (Entry.Id = Id)) and ((Entry.Offset and $80000000 <> 0) = FindSubdir) then begin Offset := Entry.Offset and $7FFFFFFF; Result := True; Break; end; end; end; function SeekToResourceData(const F: TCustomFile; const ResType, ResId: Cardinal): Cardinal; { Seeks to the specified resource's data, and returns its size. Raises an exception if the resource cannot be found. } var SectionVirtualAddr, SectionPhysOffset, SectionPhysSize, Ofs: Cardinal; DataEntry: TImageResourceDataEntry; begin FindResourceSection(F, SectionVirtualAddr, SectionPhysOffset, SectionPhysSize); { Scan the resource directory } F.Seek(SectionPhysOffset); if not FindResOffset(F, False, ResType, True, Ofs) then Error('Can''t find resource (1)'); F.Seek(SectionPhysOffset + Ofs); if not FindResOffset(F, False, ResId, True, Ofs) then Error('Can''t find resource (2)'); F.Seek(SectionPhysOffset + Ofs); if not FindResOffset(F, True, 0, False, Ofs) then Error('Can''t find resource (3).'); F.Seek(SectionPhysOffset + Ofs); F.ReadBuffer(DataEntry, SizeOf(DataEntry)); { Sanity check: DataEntry.OffsetToData is an RVA. It's technically possible for the RVA to point to a different section, but we don't support that. } if Cardinal(DataEntry.OffsetToData) < SectionVirtualAddr then Error('Invalid resource (1)'); if Cardinal(DataEntry.OffsetToData - SectionVirtualAddr + DataEntry.Size) > SectionPhysSize then Error('Invalid resource (2)'); { Seek to the resource } F.Seek(SectionPhysOffset + (DataEntry.OffsetToData - SectionVirtualAddr)); Result := DataEntry.Size; end; procedure UpdateManifestRequestedExecutionLevel(const F: TCustomFile; const RequireAdministrator: Boolean); const ElementText: AnsiString = ' ' uiAccess=' then Error('Level too short'); Inc64(Offset, P-1); F.Seek64(Offset); F.WriteAnsiString(Levels[RequireAdministrator]); end; function ReadSignatureAndChecksumFields(const F: TCustomFile; var ASignatureAddress, ASignatureSize, AChecksum: DWORD): Boolean; { Reads the signature and checksum fields in the specified file's header. If the file is not a valid PE32 executable, False is returned. } var OptHeader: TImageOptionalHeader; OptHeaderOffset: Integer64; begin Result := SeekToAndReadPEOptionalHeader(F, OptHeader, OptHeaderOffset); if Result then begin ASignatureAddress := OptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress; ASignatureSize := OptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].Size; AChecksum := OptHeader.CheckSum; end; end; function ReadSignatureAndChecksumFields64(const F: TCustomFile; var ASignatureAddress, ASignatureSize, AChecksum: DWORD): Boolean; { Reads the signature and checksum fields in the specified file's header. If the file is not a valid PE32+ executable, False is returned. } var OptHeader: TImageOptionalHeader64; OptHeaderOffset: Integer64; begin Result := SeekToAndReadPEOptionalHeader64(F, OptHeader, OptHeaderOffset); if Result then begin ASignatureAddress := OptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress; ASignatureSize := OptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].Size; AChecksum := OptHeader.CheckSum; end; end; function UpdateSignatureAndChecksumFields(const F: TCustomFile; const ASignatureAddress, ASignatureSize, AChecksum: DWORD): Boolean; { Sets the signature and checksum fields in the specified file's header. If the file is not a valid PE32 executable, False is returned. } var OptHeader: TImageOptionalHeader; OptHeaderOffset: Integer64; begin Result := SeekToAndReadPEOptionalHeader(F, OptHeader, OptHeaderOffset); if Result then begin OptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress := ASignatureAddress; OptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].Size := ASignatureSize; OptHeader.CheckSum := AChecksum; F.Seek64(OptHeaderOffset); F.WriteBuffer(OptHeader, SizeOf(OptHeader)); end; end; end.