Inno-Setup-issrc/Projects/Src/Shared.ResUpdateFunc.pas

415 lines
14 KiB
ObjectPascal
Raw Permalink Normal View History

2024-08-04 19:59:25 +02:00
unit Shared.ResUpdateFunc;
2011-10-06 20:53:09 +02:00
{
Inno Setup
Copyright (C) 1997-2016 Jordan Russell
2011-10-06 20:53:09 +02:00
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;
2011-10-06 20:53:09 +02:00
function ReadSignatureAndChecksumFields(const F: TCustomFile;
var ASignatureAddress, ASignatureSize, AChecksum: DWORD): Boolean;
function ReadSignatureAndChecksumFields64(const F: TCustomFile;
var ASignatureAddress, ASignatureSize, AChecksum: DWORD): Boolean;
2011-10-06 20:53:09 +02:00
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;
2011-10-06 20:53:09 +02:00
const
IMAGE_NT_SIGNATURE = $00004550;
IMAGE_NT_OPTIONAL_HDR32_MAGIC = $10b;
IMAGE_NT_OPTIONAL_HDR64_MAGIC = $20b;
2011-10-06 20:53:09 +02:00
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;
2011-10-06 20:53:09 +02:00
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;
2011-10-06 20:53:09 +02:00
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 = '<requestedExecutionLevel level="';
Levels: array[Boolean] of AnsiString = (
'highestAvailable" ',
'requireAdministrator"');
var
S: AnsiString;
Offset: Integer64;
P: Integer;
begin
{ Read the manifest resource into a string }
SetString(S, nil, SeekToResourceData(F, 24, 1));
Offset := F.Position;
F.ReadBuffer(S[1], Length(S));
{ Locate and update the requestedExecutionLevel element }
P := Pos(ElementText, S);
if P = 0 then
Error('Element not found');
Inc(P, Length(ElementText));
if Copy(S, P+21, 10) <> ' 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. }
2011-10-06 20:53:09 +02:00
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;
2011-10-06 20:53:09 +02:00
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. }
2011-10-06 20:53:09 +02:00
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.