Compiler.ExeUpdateFunc.pas 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857
  1. unit Compiler.ExeUpdateFunc;
  2. {
  3. Inno Setup
  4. Copyright (C) 1997-2025 Jordan Russell
  5. Portions by Martijn Laan
  6. For conditions of distribution and use, see LICENSE.TXT.
  7. PE header and resource update functions used by the compiler only
  8. }
  9. interface
  10. uses
  11. Windows, SysUtils, Shared.FileClass, Shared.VerInfoFunc;
  12. function ReadSignatureAndChecksumFields(const F: TCustomFile;
  13. var ASignatureAddress, ASignatureSize, AChecksum: DWORD): Boolean;
  14. function ReadSignatureAndChecksumFields64(const F: TCustomFile;
  15. var ASignatureAddress, ASignatureSize, AChecksum: DWORD): Boolean;
  16. function SeekToResourceData(const F: TCustomFile; const ResType, ResId: Cardinal): Cardinal;
  17. function UpdateSignatureAndChecksumFields(const F: TCustomFile;
  18. const ASignatureAddress, ASignatureSize, AChecksum: DWORD): Boolean;
  19. procedure UpdateSetupPEHeaderFields(const F: TCustomFile;
  20. const IsTSAware, IsDEPCompatible, IsASLRCompatible: Boolean);
  21. procedure UpdateIcons(const FileName, IcoFileName: String; const DeleteUninstallIcon: Boolean);
  22. procedure UpdateVersionInfo(const F: TCustomFile;
  23. const NewBinaryFileVersion, NewBinaryProductVersion: TFileVersionNumbers;
  24. const NewCompanyName, NewFileDescription, NewTextFileVersion, NewLegalCopyright,
  25. NewProductName, NewTextProductVersion, NewOriginalFileName: String;
  26. const SetFileVersionAndDescription: Boolean);
  27. procedure PreventCOMCTL32Sideloading(const F: TCustomFile);
  28. implementation
  29. uses
  30. Math;
  31. const
  32. IMAGE_NT_SIGNATURE = $00004550;
  33. IMAGE_NT_OPTIONAL_HDR32_MAGIC = $10b;
  34. IMAGE_NT_OPTIONAL_HDR64_MAGIC = $20b;
  35. IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;
  36. IMAGE_SIZEOF_SHORT_NAME = 8;
  37. IMAGE_DIRECTORY_ENTRY_RESOURCE = 2;
  38. IMAGE_DIRECTORY_ENTRY_SECURITY = 4;
  39. type
  40. PImageFileHeader = ^TImageFileHeader;
  41. TImageFileHeader = packed record
  42. Machine: Word;
  43. NumberOfSections: Word;
  44. TimeDateStamp: DWORD;
  45. PointerToSymbolTable: DWORD;
  46. NumberOfSymbols: DWORD;
  47. SizeOfOptionalHeader: Word;
  48. Characteristics: Word;
  49. end;
  50. PImageDataDirectory = ^TImageDataDirectory;
  51. TImageDataDirectory = record
  52. VirtualAddress: DWORD;
  53. Size: DWORD;
  54. end;
  55. PImageOptionalHeader = ^TImageOptionalHeader;
  56. TImageOptionalHeader = packed record
  57. { Standard fields. }
  58. Magic: Word;
  59. MajorLinkerVersion: Byte;
  60. MinorLinkerVersion: Byte;
  61. SizeOfCode: DWORD;
  62. SizeOfInitializedData: DWORD;
  63. SizeOfUninitializedData: DWORD;
  64. AddressOfEntryPoint: DWORD;
  65. BaseOfCode: DWORD;
  66. BaseOfData: DWORD;
  67. { NT additional fields. }
  68. ImageBase: DWORD;
  69. SectionAlignment: DWORD;
  70. FileAlignment: DWORD;
  71. MajorOperatingSystemVersion: Word;
  72. MinorOperatingSystemVersion: Word;
  73. MajorImageVersion: Word;
  74. MinorImageVersion: Word;
  75. MajorSubsystemVersion: Word;
  76. MinorSubsystemVersion: Word;
  77. Win32VersionValue: DWORD;
  78. SizeOfImage: DWORD;
  79. SizeOfHeaders: DWORD;
  80. CheckSum: DWORD;
  81. Subsystem: Word;
  82. DllCharacteristics: Word;
  83. SizeOfStackReserve: DWORD;
  84. SizeOfStackCommit: DWORD;
  85. SizeOfHeapReserve: DWORD;
  86. SizeOfHeapCommit: DWORD;
  87. LoaderFlags: DWORD;
  88. NumberOfRvaAndSizes: DWORD;
  89. DataDirectory: packed array[0..IMAGE_NUMBEROF_DIRECTORY_ENTRIES-1] of TImageDataDirectory;
  90. end;
  91. PImageOptionalHeader64 = ^TImageOptionalHeader64;
  92. TImageOptionalHeader64 = packed record
  93. { Standard fields. }
  94. Magic: Word;
  95. MajorLinkerVersion: Byte;
  96. MinorLinkerVersion: Byte;
  97. SizeOfCode: DWORD;
  98. SizeOfInitializedData: DWORD;
  99. SizeOfUninitializedData: DWORD;
  100. AddressOfEntryPoint: DWORD;
  101. BaseOfCode: DWORD;
  102. { NT additional fields. }
  103. ImageBase: Int64;
  104. SectionAlignment: DWORD;
  105. FileAlignment: DWORD;
  106. MajorOperatingSystemVersion: Word;
  107. MinorOperatingSystemVersion: Word;
  108. MajorImageVersion: Word;
  109. MinorImageVersion: Word;
  110. MajorSubsystemVersion: Word;
  111. MinorSubsystemVersion: Word;
  112. Win32VersionValue: DWORD;
  113. SizeOfImage: DWORD;
  114. SizeOfHeaders: DWORD;
  115. CheckSum: DWORD;
  116. Subsystem: Word;
  117. DllCharacteristics: Word;
  118. SizeOfStackReserve: Int64;
  119. SizeOfStackCommit: Int64;
  120. SizeOfHeapReserve: Int64;
  121. SizeOfHeapCommit: Int64;
  122. LoaderFlags: DWORD;
  123. NumberOfRvaAndSizes: DWORD;
  124. DataDirectory: packed array[0..IMAGE_NUMBEROF_DIRECTORY_ENTRIES-1] of TImageDataDirectory;
  125. end;
  126. TISHMisc = packed record
  127. case Integer of
  128. 0: (PhysicalAddress: DWORD);
  129. 1: (VirtualSize: DWORD);
  130. end;
  131. PImageSectionHeader = ^TImageSectionHeader;
  132. TImageSectionHeader = packed record
  133. Name: packed array[0..IMAGE_SIZEOF_SHORT_NAME-1] of Byte;
  134. Misc: TISHMisc;
  135. VirtualAddress: DWORD;
  136. SizeOfRawData: DWORD;
  137. PointerToRawData: DWORD;
  138. PointerToRelocations: DWORD;
  139. PointerToLinenumbers: DWORD;
  140. NumberOfRelocations: Word;
  141. NumberOfLinenumbers: Word;
  142. Characteristics: DWORD;
  143. end;
  144. TImageResourceDirectory = packed record
  145. Characteristics: DWORD;
  146. TimeDateStamp: DWORD;
  147. MajorVersion: Word;
  148. MinorVersion: Word;
  149. NumberOfNamedEntries: Word;
  150. NumberOfIdEntries: Word;
  151. end;
  152. TImageResourceDirectoryEntry = packed record
  153. Id: DWORD;
  154. Offset: DWORD;
  155. end;
  156. TImageResourceDataEntry = packed record
  157. OffsetToData: DWORD;
  158. Size: DWORD;
  159. CodePage: DWORD;
  160. Reserved: DWORD;
  161. end;
  162. procedure Error(const Msg: String);
  163. begin
  164. raise Exception.Create('Resource update error: ' + Msg);
  165. end;
  166. function SeekToPEHeader(const F: TCustomFile): Boolean;
  167. var
  168. DosHeader: packed record
  169. Sig: array[0..1] of AnsiChar;
  170. Other: array[0..57] of Byte;
  171. PEHeaderOffset: LongWord;
  172. end;
  173. Sig: DWORD;
  174. begin
  175. Result := False;
  176. F.Seek(0);
  177. if F.Read(DosHeader, SizeOf(DosHeader)) = SizeOf(DosHeader) then begin
  178. if (DosHeader.Sig[0] = 'M') and (DosHeader.Sig[1] = 'Z') and
  179. (DosHeader.PEHeaderOffset <> 0) then begin
  180. F.Seek(DosHeader.PEHeaderOffset);
  181. if F.Read(Sig, SizeOf(Sig)) = SizeOf(Sig) then
  182. if Sig = IMAGE_NT_SIGNATURE then
  183. Result := True;
  184. end;
  185. end;
  186. end;
  187. function SeekToAndReadPEOptionalHeader(const F: TCustomFile;
  188. var OptHeader: TImageOptionalHeader; var OptHeaderOffset: Int64): Boolean;
  189. var
  190. Header: TImageFileHeader;
  191. begin
  192. Result := False;
  193. if SeekToPEHeader(F) then begin
  194. if (F.Read(Header, SizeOf(Header)) = SizeOf(Header)) and
  195. (Header.SizeOfOptionalHeader = SizeOf(OptHeader)) then begin
  196. OptHeaderOffset := F.Position;
  197. if F.Read(OptHeader, SizeOf(OptHeader)) = SizeOf(OptHeader) then
  198. if OptHeader.Magic = IMAGE_NT_OPTIONAL_HDR32_MAGIC then
  199. Result := True;
  200. end;
  201. end;
  202. end;
  203. function SeekToAndReadPEOptionalHeader64(const F: TCustomFile;
  204. var OptHeader: TImageOptionalHeader64; var OptHeaderOffset: Int64): Boolean;
  205. var
  206. Header: TImageFileHeader;
  207. begin
  208. Result := False;
  209. if SeekToPEHeader(F) then begin
  210. if (F.Read(Header, SizeOf(Header)) = SizeOf(Header)) and
  211. (Header.SizeOfOptionalHeader = SizeOf(OptHeader)) then begin
  212. OptHeaderOffset := F.Position;
  213. if F.Read(OptHeader, SizeOf(OptHeader)) = SizeOf(OptHeader) then
  214. if OptHeader.Magic = IMAGE_NT_OPTIONAL_HDR64_MAGIC then
  215. Result := True;
  216. end;
  217. end;
  218. end;
  219. procedure FindResourceSection(const F: TCustomFile;
  220. var SectionVirtualAddr, SectionPhysOffset, SectionPhysSize: Cardinal);
  221. var
  222. EXESig: Word;
  223. PEHeaderOffset, PESig: Cardinal;
  224. PEHeader: TImageFileHeader;
  225. PEOptHeader: TImageOptionalHeader;
  226. PESectionHeader: TImageSectionHeader;
  227. I: Integer;
  228. begin
  229. { Read DOS header }
  230. F.Seek(0);
  231. F.ReadBuffer(EXESig, SizeOf(EXESig));
  232. if EXESig <> $5A4D {'MZ'} then
  233. Error('File isn''t an EXE file (1)');
  234. F.Seek($3C);
  235. F.ReadBuffer(PEHeaderOffset, SizeOf(PEHeaderOffset));
  236. if PEHeaderOffset = 0 then
  237. Error('File isn''t a PE file (1)');
  238. { Read PE header & optional header }
  239. F.Seek(PEHeaderOffset);
  240. F.ReadBuffer(PESig, SizeOf(PESig));
  241. if PESig <> $00004550 {'PE'#0#0} then
  242. Error('File isn''t a PE file (2)');
  243. F.ReadBuffer(PEHeader, SizeOf(PEHeader));
  244. if PEHeader.SizeOfOptionalHeader <> SizeOf(PEOptHeader) then
  245. Error('File isn''t a PE file (3)');
  246. F.ReadBuffer(PEOptHeader, SizeOf(PEOptHeader));
  247. if PEOptHeader.Magic <> IMAGE_NT_OPTIONAL_HDR32_MAGIC then
  248. Error('File isn''t a PE file (4)');
  249. { Scan section headers for resource section }
  250. if (PEOptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = 0) or
  251. (PEOptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = 0) then
  252. Error('No resources (1)');
  253. SectionVirtualAddr := PEOptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
  254. SectionPhysOffset := 0;
  255. for I := 0 to PEHeader.NumberOfSections-1 do begin
  256. F.ReadBuffer(PESectionHeader, SizeOf(PESectionHeader));
  257. if (PESectionHeader.VirtualAddress = SectionVirtualAddr) and
  258. (PESectionHeader.SizeOfRawData <> 0) then begin
  259. SectionPhysOffset := PESectionHeader.PointerToRawData;
  260. SectionPhysSize := PESectionHeader.SizeOfRawData;
  261. Break;
  262. end;
  263. end;
  264. if SectionPhysOffset = 0 then
  265. Error('No resources (2)');
  266. end;
  267. function FindResOffset(const F: TCustomFile; const AnyId: Boolean;
  268. const Id: Cardinal; const FindSubdir: Boolean; var Offset: Cardinal): Boolean;
  269. var
  270. Dir: TImageResourceDirectory;
  271. Entry: TImageResourceDirectoryEntry;
  272. I: Integer;
  273. begin
  274. F.ReadBuffer(Dir, SizeOf(Dir));
  275. { Skip over named entries }
  276. for I := 0 to Dir.NumberOfNamedEntries-1 do
  277. F.ReadBuffer(Entry, SizeOf(Entry));
  278. { Now process ID entries }
  279. Result := False;
  280. for I := 0 to Dir.NumberOfIdEntries-1 do begin
  281. F.ReadBuffer(Entry, SizeOf(Entry));
  282. if (AnyId or (Entry.Id = Id)) and
  283. ((Entry.Offset and $80000000 <> 0) = FindSubdir) then begin
  284. Offset := Entry.Offset and $7FFFFFFF;
  285. Result := True;
  286. Break;
  287. end;
  288. end;
  289. end;
  290. function SeekToResourceData(const F: TCustomFile; const ResType, ResId: Cardinal): Cardinal;
  291. { Seeks to the specified resource's data, and returns its size. Raises an
  292. exception if the resource cannot be found. }
  293. var
  294. SectionVirtualAddr, SectionPhysOffset, SectionPhysSize, Ofs: Cardinal;
  295. DataEntry: TImageResourceDataEntry;
  296. begin
  297. FindResourceSection(F, SectionVirtualAddr, SectionPhysOffset, SectionPhysSize);
  298. { Scan the resource directory }
  299. F.Seek(SectionPhysOffset);
  300. if not FindResOffset(F, False, ResType, True, Ofs) then
  301. Error('Can''t find resource (1)');
  302. F.Seek(SectionPhysOffset + Ofs);
  303. if not FindResOffset(F, False, ResId, True, Ofs) then
  304. Error('Can''t find resource (2)');
  305. F.Seek(SectionPhysOffset + Ofs);
  306. if not FindResOffset(F, True, 0, False, Ofs) then
  307. Error('Can''t find resource (3).');
  308. F.Seek(SectionPhysOffset + Ofs);
  309. F.ReadBuffer(DataEntry, SizeOf(DataEntry));
  310. { Sanity check: DataEntry.OffsetToData is an RVA. It's technically possible
  311. for the RVA to point to a different section, but we don't support that. }
  312. if Cardinal(DataEntry.OffsetToData) < SectionVirtualAddr then
  313. Error('Invalid resource (1)');
  314. if Cardinal(DataEntry.OffsetToData - SectionVirtualAddr + DataEntry.Size) > SectionPhysSize then
  315. Error('Invalid resource (2)');
  316. { Seek to the resource }
  317. F.Seek(SectionPhysOffset + (DataEntry.OffsetToData - SectionVirtualAddr));
  318. Result := DataEntry.Size;
  319. end;
  320. function ReadSignatureAndChecksumFields(const F: TCustomFile;
  321. var ASignatureAddress, ASignatureSize, AChecksum: DWORD): Boolean;
  322. { Reads the signature and checksum fields in the specified file's header.
  323. If the file is not a valid PE32 executable, False is returned. }
  324. var
  325. OptHeader: TImageOptionalHeader;
  326. OptHeaderOffset: Int64;
  327. begin
  328. Result := SeekToAndReadPEOptionalHeader(F, OptHeader, OptHeaderOffset);
  329. if Result then begin
  330. ASignatureAddress := OptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress;
  331. ASignatureSize := OptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].Size;
  332. AChecksum := OptHeader.CheckSum;
  333. end;
  334. end;
  335. function ReadSignatureAndChecksumFields64(const F: TCustomFile;
  336. var ASignatureAddress, ASignatureSize, AChecksum: DWORD): Boolean;
  337. { Reads the signature and checksum fields in the specified file's header.
  338. If the file is not a valid PE32+ executable, False is returned. }
  339. var
  340. OptHeader: TImageOptionalHeader64;
  341. OptHeaderOffset: Int64;
  342. begin
  343. Result := SeekToAndReadPEOptionalHeader64(F, OptHeader, OptHeaderOffset);
  344. if Result then begin
  345. ASignatureAddress := OptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress;
  346. ASignatureSize := OptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].Size;
  347. AChecksum := OptHeader.CheckSum;
  348. end;
  349. end;
  350. function UpdateSignatureAndChecksumFields(const F: TCustomFile;
  351. const ASignatureAddress, ASignatureSize, AChecksum: DWORD): Boolean;
  352. { Sets the signature and checksum fields in the specified file's header.
  353. If the file is not a valid PE32 executable, False is returned. }
  354. var
  355. OptHeader: TImageOptionalHeader;
  356. OptHeaderOffset: Int64;
  357. begin
  358. Result := SeekToAndReadPEOptionalHeader(F, OptHeader, OptHeaderOffset);
  359. if Result then begin
  360. OptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress := ASignatureAddress;
  361. OptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].Size := ASignatureSize;
  362. OptHeader.CheckSum := AChecksum;
  363. F.Seek(OptHeaderOffset);
  364. F.WriteBuffer(OptHeader, SizeOf(OptHeader));
  365. end;
  366. end;
  367. procedure UpdateSetupPEHeaderFields(const F: TCustomFile;
  368. const IsTSAware, IsDEPCompatible, IsASLRCompatible: Boolean);
  369. function SeekToPEHeader(const F: TCustomFile): Boolean;
  370. var
  371. DosHeader: packed record
  372. Sig: array[0..1] of AnsiChar;
  373. Other: array[0..57] of Byte;
  374. PEHeaderOffset: LongWord;
  375. end;
  376. Sig: DWORD;
  377. begin
  378. Result := False;
  379. F.Seek(0);
  380. if F.Read(DosHeader, SizeOf(DosHeader)) = SizeOf(DosHeader) then begin
  381. if (DosHeader.Sig[0] = 'M') and (DosHeader.Sig[1] = 'Z') and
  382. (DosHeader.PEHeaderOffset <> 0) then begin
  383. F.Seek(DosHeader.PEHeaderOffset);
  384. if F.Read(Sig, SizeOf(Sig)) = SizeOf(Sig) then
  385. if Sig = IMAGE_NT_SIGNATURE then
  386. Result := True;
  387. end;
  388. end;
  389. end;
  390. const
  391. IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = $0040;
  392. IMAGE_DLLCHARACTERISTICS_NX_COMPAT = $0100;
  393. IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE = $8000;
  394. OffsetOfOperatingSystemVersion = $28;
  395. OffsetOfImageVersion = $2C;
  396. OffsetOfSubsystemVersion = $30;
  397. OffsetOfDllCharacteristics = $46;
  398. var
  399. Header: TImageFileHeader;
  400. OptMagic, DllChars, OrigDllChars: Word;
  401. begin
  402. if SeekToPEHeader(F) then begin
  403. if (F.Read(Header, SizeOf(Header)) = SizeOf(Header)) and
  404. (Header.SizeOfOptionalHeader = 224) then begin
  405. const Ofs = F.Position;
  406. if (F.Read(OptMagic, SizeOf(OptMagic)) = SizeOf(OptMagic)) and
  407. (OptMagic = IMAGE_NT_OPTIONAL_HDR32_MAGIC) then begin
  408. { Update DllCharacteristics }
  409. F.Seek(Ofs + OffsetOfDllCharacteristics);
  410. if F.Read(DllChars, SizeOf(DllChars)) = SizeOf(DllChars) then begin
  411. OrigDllChars := DllChars;
  412. if IsTSAware then
  413. DllChars := DllChars or IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
  414. else
  415. DllChars := DllChars and not IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE;
  416. if IsDEPCompatible then
  417. DllChars := DllChars or IMAGE_DLLCHARACTERISTICS_NX_COMPAT
  418. else
  419. DllChars := DllChars and not IMAGE_DLLCHARACTERISTICS_NX_COMPAT;
  420. if IsASLRCompatible then
  421. DllChars := DllChars or IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
  422. else
  423. DllChars := DllChars and not IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE;
  424. if DllChars <> OrigDllChars then begin
  425. F.Seek(Ofs + OffsetOfDllCharacteristics);
  426. F.WriteBuffer(DllChars, SizeOf(DllChars));
  427. end;
  428. Exit;
  429. end;
  430. end;
  431. end;
  432. end;
  433. raise Exception.Create('UpdateSetupPEHeaderFields failed');
  434. end;
  435. procedure ResUpdateError(const Msg: String);
  436. begin
  437. raise Exception.Create('Resource update error: ' + Msg);
  438. end;
  439. procedure ResUpdateErrorWithLastError(const Msg: String);
  440. begin
  441. ResUpdateError(Msg + ' (' + IntToStr(GetLastError) + ')');
  442. end;
  443. procedure UpdateVersionInfo(const F: TCustomFile;
  444. const NewBinaryFileVersion, NewBinaryProductVersion: TFileVersionNumbers;
  445. const NewCompanyName, NewFileDescription, NewTextFileVersion, NewLegalCopyright,
  446. NewProductName, NewTextProductVersion, NewOriginalFileName: String;
  447. const SetFileVersionAndDescription: Boolean);
  448. function WideStrsEqual(P1, P2: PWideChar): Boolean;
  449. function WideUpCase(C: WideChar): WideChar;
  450. begin
  451. Result := C;
  452. if (Result >= 'a') and (Result <= 'z') then
  453. Dec(Result, Ord('a') - Ord('A'));
  454. end;
  455. begin
  456. while True do begin
  457. if WideUpCase(P1^) <> WideUpCase(P2^) then begin
  458. Result := False;
  459. Exit;
  460. end;
  461. if P1^ = #0 then
  462. Break;
  463. Inc(P1);
  464. Inc(P2);
  465. end;
  466. Result := True;
  467. end;
  468. procedure BumpToDWordBoundary(var P: Pointer);
  469. begin
  470. if Cardinal(P) and 3 <> 0 then
  471. Cardinal(P) := (Cardinal(P) or 3) + 1;
  472. end;
  473. function QueryValue(P: Pointer; Path: PWideChar; var Buf: Pointer;
  474. var BufLen: Cardinal): Boolean;
  475. var
  476. EndP: Pointer;
  477. ValueLength: Cardinal;
  478. begin
  479. Result := False;
  480. Cardinal(EndP) := Cardinal(P) + PWord(P)^;
  481. Inc(PWord(P));
  482. ValueLength := PWord(P)^;
  483. Inc(PWord(P));
  484. Inc(PWord(P));
  485. if WideStrsEqual(PWideChar(P), Path) then begin
  486. Inc(PWideChar(P), lstrlenW(P) + 1);
  487. BumpToDWordBoundary(P);
  488. Inc(Path, lstrlenW(Path) + 1);
  489. if Path^ = #0 then begin
  490. { Found the requested value }
  491. Buf := P;
  492. BufLen := ValueLength;
  493. Result := True;
  494. end
  495. else begin
  496. { Handle children.
  497. Note: Like VerQueryValue, we always treat ValueLength as a byte count
  498. when looking for child nodes. Many resource compilers, including
  499. Borland's, wrongly set ValueLength to a *character* count on string
  500. nodes. But since we never try to query for a child of a string node,
  501. that doesn't matter here. }
  502. Inc(Cardinal(P), ValueLength);
  503. BumpToDWordBoundary(P);
  504. while Cardinal(P) < Cardinal(EndP) do begin
  505. Result := QueryValue(P, Path, Buf, BufLen);
  506. if Result then
  507. Exit;
  508. Inc(Cardinal(P), PWord(P)^);
  509. BumpToDWordBoundary(P);
  510. end;
  511. end;
  512. end;
  513. end;
  514. procedure ReplaceWithRealCopyrightSymbols(const Value: PWideChar);
  515. var
  516. Len, I, J: Integer;
  517. begin
  518. Len := lstrlenW(Value);
  519. for I := 0 to Len-3 do begin
  520. if (Value[I] = '(') and (Value[I+1] = 'C') and (Value[I+2] = ')') then begin
  521. Value[I] := WideChar($00A9);
  522. { Shift back two characters }
  523. for J := I+1 to Len-3 do
  524. Value[J] := Value[J+2];
  525. Value[Len-2] := ' ';
  526. Value[Len-1] := ' ';
  527. end;
  528. end;
  529. end;
  530. procedure UpdateStringValue(P: Pointer; const Path: PWideChar; NewValue: String);
  531. var
  532. Value: PWideChar;
  533. ValueLen: Cardinal;
  534. begin
  535. if not QueryValue(P, Path, Pointer(Value), ValueLen) then
  536. ResUpdateError('Unexpected version resource format (1)');
  537. Move(Pointer(NewValue)^, Value^, (Min(Length(NewValue), lstrlenW(Value)))*SizeOf(Char));
  538. ReplaceWithRealCopyrightSymbols(Value);
  539. end;
  540. procedure UpdateFixedFileInfo(P: Pointer; const Path: PWideChar;
  541. const NewFileVersion, NewProductVersion: TFileVersionNumbers;
  542. const SetFileVersion: Boolean);
  543. var
  544. FixedFileInfo: PVSFixedFileInfo;
  545. ValueLen: Cardinal;
  546. begin
  547. if not QueryValue(P, Path, Pointer(FixedFileInfo), ValueLen) then
  548. ResUpdateError('Unexpected version resource format (2)');
  549. if FixedFileInfo.dwSignature <> $FEEF04BD then
  550. ResUpdateError('Unexpected version resource format (3)');
  551. if SetFileVersion then begin
  552. FixedFileInfo.dwFileVersionLS := NewFileVersion.LS;
  553. FixedFileInfo.dwFileVersionMS := NewFileVersion.MS;
  554. end;
  555. FixedFileInfo.dwProductVersionLS := NewProductVersion.LS;
  556. FixedFileInfo.dwProductVersionMS := NewProductVersion.MS;
  557. end;
  558. var
  559. ResSize: Cardinal;
  560. VersRes: Pointer;
  561. begin
  562. { Locate the resource }
  563. ResSize := SeekToResourceData(F, Cardinal(RT_VERSION), 1);
  564. const ResOffset = F.Position;
  565. GetMem(VersRes, ResSize);
  566. try
  567. { Read the resource }
  568. F.ReadBuffer(VersRes^, ResSize);
  569. { Update the resource }
  570. UpdateStringValue(VersRes, 'VS_VERSION_INFO'#0'StringFileInfo'#0'000004b0'#0'CompanyName'#0, NewCompanyName);
  571. if SetFileVersionAndDescription then begin
  572. UpdateStringValue(VersRes, 'VS_VERSION_INFO'#0'StringFileInfo'#0'000004b0'#0'FileDescription'#0, NewFileDescription);
  573. UpdateStringValue(VersRes, 'VS_VERSION_INFO'#0'StringFileInfo'#0'000004b0'#0'FileVersion'#0, NewTextFileVersion);
  574. end;
  575. UpdateStringValue(VersRes, 'VS_VERSION_INFO'#0'StringFileInfo'#0'000004b0'#0'LegalCopyright'#0, NewLegalCopyright);
  576. UpdateStringValue(VersRes, 'VS_VERSION_INFO'#0'StringFileInfo'#0'000004b0'#0'ProductName'#0, NewProductName);
  577. UpdateStringValue(VersRes, 'VS_VERSION_INFO'#0'StringFileInfo'#0'000004b0'#0'OriginalFileName'#0, NewOriginalFileName);
  578. UpdateStringValue(VersRes, 'VS_VERSION_INFO'#0'StringFileInfo'#0'000004b0'#0'ProductVersion'#0, NewTextProductVersion);
  579. UpdateFixedFileInfo(VersRes, 'VS_VERSION_INFO'#0, NewBinaryFileVersion, NewBinaryProductVersion, SetFileVersionAndDescription);
  580. { Write the updated resource }
  581. F.Seek(ResOffset);
  582. F.WriteBuffer(VersRes^, ResSize);
  583. finally
  584. FreeMem(VersRes);
  585. end;
  586. end;
  587. function EnumLangsFunc(hModule: Cardinal; lpType, lpName: PAnsiChar; wLanguage: Word; lParam: Integer): BOOL; stdcall;
  588. begin
  589. PWord(lParam)^ := wLanguage;
  590. Result := False;
  591. end;
  592. function GetResourceLanguage(hModule: Cardinal; lpType, lpName: PChar; var wLanguage: Word): Boolean;
  593. begin
  594. wLanguage := 0;
  595. EnumResourceLanguages(hModule, lpType, lpName, @EnumLangsFunc, Integer(@wLanguage));
  596. Result := True;
  597. end;
  598. procedure UpdateIcons(const FileName, IcoFileName: String; const DeleteUninstallIcon: Boolean);
  599. type
  600. PIcoItemHeader = ^TIcoItemHeader;
  601. TIcoItemHeader = packed record
  602. Width: Byte;
  603. Height: Byte;
  604. Colors: Byte;
  605. Reserved: Byte;
  606. Planes: Word;
  607. BitCount: Word;
  608. ImageSize: DWORD;
  609. end;
  610. PIcoItem = ^TIcoItem;
  611. TIcoItem = packed record
  612. Header: TIcoItemHeader;
  613. Offset: DWORD;
  614. end;
  615. PIcoHeader = ^TIcoHeader;
  616. TIcoHeader = packed record
  617. Reserved: Word;
  618. Typ: Word;
  619. ItemCount: Word;
  620. Items: array [0..MaxInt shr 4 - 1] of TIcoItem;
  621. end;
  622. PGroupIconDirItem = ^TGroupIconDirItem;
  623. TGroupIconDirItem = packed record
  624. Header: TIcoItemHeader;
  625. Id: Word;
  626. end;
  627. PGroupIconDir = ^TGroupIconDir;
  628. TGroupIconDir = packed record
  629. Reserved: Word;
  630. Typ: Word;
  631. ItemCount: Word;
  632. Items: array [0..MaxInt shr 4 - 1] of TGroupIconDirItem;
  633. end;
  634. function IsValidIcon(P: Pointer; Size: Cardinal): Boolean;
  635. var
  636. ItemCount: Cardinal;
  637. begin
  638. Result := False;
  639. if Size < Cardinal(SizeOf(Word) * 3) then
  640. Exit;
  641. if (PChar(P)[0] = 'M') and (PChar(P)[1] = 'Z') then
  642. Exit;
  643. ItemCount := PIcoHeader(P).ItemCount;
  644. if Size < Cardinal((SizeOf(Word) * 3) + (ItemCount * SizeOf(TIcoItem))) then
  645. Exit;
  646. P := @PIcoHeader(P).Items;
  647. while ItemCount > Cardinal(0) do begin
  648. if (Cardinal(PIcoItem(P).Offset + PIcoItem(P).Header.ImageSize) < Cardinal(PIcoItem(P).Offset)) or
  649. (Cardinal(PIcoItem(P).Offset + PIcoItem(P).Header.ImageSize) > Cardinal(Size)) then
  650. Exit;
  651. Inc(PIcoItem(P));
  652. Dec(ItemCount);
  653. end;
  654. Result := True;
  655. end;
  656. function DeleteIcon(const H: THandle; const M: HMODULE; const ResourceName: PChar): PGroupIconDir;
  657. var
  658. R: HRSRC;
  659. Res: HGLOBAL;
  660. GroupIconDir: PGroupIconDir;
  661. I: Integer;
  662. wLanguage: Word;
  663. begin
  664. { Load the group icon resource }
  665. R := FindResource(M, ResourceName, RT_GROUP_ICON);
  666. if R = 0 then
  667. ResUpdateErrorWithLastError('FindResource failed (1)');
  668. Res := LoadResource(M, R);
  669. if Res = 0 then
  670. ResUpdateErrorWithLastError('LoadResource failed (1)');
  671. GroupIconDir := LockResource(Res);
  672. if GroupIconDir = nil then
  673. ResUpdateErrorWithLastError('LockResource failed (1)');
  674. { Delete the group icon resource }
  675. if not GetResourceLanguage(M, RT_GROUP_ICON, ResourceName, wLanguage) then
  676. ResUpdateError('GetResourceLanguage failed (1)');
  677. if not UpdateResource(H, RT_GROUP_ICON, ResourceName, wLanguage, nil, 0) then
  678. ResUpdateErrorWithLastError('UpdateResource failed (1)');
  679. { Delete the icon resources that belonged to the group }
  680. for I := 0 to GroupIconDir.ItemCount-1 do begin
  681. if not GetResourceLanguage(M, RT_ICON, MakeIntResource(GroupIconDir.Items[I].Id), wLanguage) then
  682. ResUpdateError('GetResourceLanguage failed (2)');
  683. if not UpdateResource(H, RT_ICON, MakeIntResource(GroupIconDir.Items[I].Id), wLanguage, nil, 0) then
  684. ResUpdateErrorWithLastError('UpdateResource failed (2)');
  685. end;
  686. Result := GroupIconDir;
  687. end;
  688. var
  689. H: THandle;
  690. M: HMODULE;
  691. OldGroupIconDir, NewGroupIconDir: PGroupIconDir;
  692. I: Integer;
  693. F: TFile;
  694. Ico: PIcoHeader;
  695. N: Cardinal;
  696. NewGroupIconDirSize: LongInt;
  697. begin
  698. Ico := nil;
  699. try
  700. { Load the icons }
  701. F := TFile.Create(IcoFileName, fdOpenExisting, faRead, fsRead);
  702. try
  703. N := F.CappedSize;
  704. if Cardinal(N) > Cardinal($100000) then { sanity check }
  705. ResUpdateError('Icon file is too large');
  706. GetMem(Ico, N);
  707. F.ReadBuffer(Ico^, N);
  708. finally
  709. F.Free;
  710. end;
  711. { Ensure the icon is valid }
  712. if not IsValidIcon(Ico, N) then
  713. ResUpdateError('Icon file is invalid');
  714. { Update the resources }
  715. H := BeginUpdateResource(PChar(FileName), False);
  716. if H = 0 then
  717. ResUpdateErrorWithLastError('BeginUpdateResource failed (1)');
  718. try
  719. M := LoadLibraryEx(PChar(FileName), 0, LOAD_LIBRARY_AS_DATAFILE);
  720. if M = 0 then
  721. ResUpdateErrorWithLastError('LoadLibraryEx failed (1)');
  722. try
  723. { Delete default icons }
  724. OldGroupIconDir := DeleteIcon(H, M, 'MAINICON');
  725. if DeleteUninstallIcon then
  726. DeleteIcon(H, M, 'Z_UNINSTALLICON');
  727. { Build the new group icon resource }
  728. NewGroupIconDirSize := 3*SizeOf(Word)+Ico.ItemCount*SizeOf(TGroupIconDirItem);
  729. GetMem(NewGroupIconDir, NewGroupIconDirSize);
  730. try
  731. { Build the new group icon resource }
  732. NewGroupIconDir.Reserved := OldGroupIconDir.Reserved;
  733. NewGroupIconDir.Typ := OldGroupIconDir.Typ;
  734. NewGroupIconDir.ItemCount := Ico.ItemCount;
  735. for I := 0 to NewGroupIconDir.ItemCount-1 do begin
  736. NewGroupIconDir.Items[I].Header := Ico.Items[I].Header;
  737. NewGroupIconDir.Items[I].Id := I+100; //start at 100 to avoid overwriting other icons that may exist
  738. end;
  739. { Update 'MAINICON' }
  740. for I := 0 to NewGroupIconDir.ItemCount-1 do
  741. if not UpdateResource(H, RT_ICON, MakeIntResource(NewGroupIconDir.Items[I].Id), 1033, Pointer(DWORD(Ico) + Ico.Items[I].Offset), Ico.Items[I].Header.ImageSize) then
  742. ResUpdateErrorWithLastError('UpdateResource failed (3)');
  743. { Update the icons }
  744. if not UpdateResource(H, RT_GROUP_ICON, 'MAINICON', 1033, NewGroupIconDir, NewGroupIconDirSize) then
  745. ResUpdateErrorWithLastError('UpdateResource failed (4)');
  746. finally
  747. FreeMem(NewGroupIconDir);
  748. end;
  749. finally
  750. FreeLibrary(M);
  751. end;
  752. except
  753. EndUpdateResource(H, True); { discard changes }
  754. raise;
  755. end;
  756. if not EndUpdateResource(H, False) then
  757. ResUpdateErrorWithLastError('EndUpdateResource failed');
  758. finally
  759. FreeMem(Ico);
  760. end;
  761. end;
  762. procedure PreventCOMCTL32Sideloading(const F: TCustomFile);
  763. const
  764. DependencyStartTag: AnsiString = '<dependency>';
  765. DependencyEndTag: AnsiString = '</dependency>';
  766. FileStartTag: AnsiString = '<file name="';
  767. COMCTL32Entry: AnsiString = '<file name="comctl32.dll" loadFrom="%SystemRoot%\system32\" />'#13#10;
  768. var
  769. S: AnsiString;
  770. P,Q,R: Integer;
  771. begin
  772. { Read the manifest resource into a string }
  773. SetString(S, nil, SeekToResourceData(F, 24, 1));
  774. var Offset := F.Position;
  775. F.ReadBuffer(S[1], Length(S));
  776. { Locate and update the <dependency> tag }
  777. P := Pos(DependencyStartTag, S);
  778. if P = 0 then
  779. ResUpdateError('<dependency> tag not found');
  780. Q := Pos(DependencyEndTag, S);
  781. if Q <= P then
  782. ResUpdateError('<dependency> end tag not found');
  783. Q := Q+Length(DependencyEndTag);
  784. if Length(COMCTL32Entry) > Q-P then
  785. ResUpdateError('<dependency> tag shorter than replacement');
  786. R := Pos(FileStartTag, S);
  787. if R <= Q then
  788. ResUpdateError('<dependency> end tag after <file>?');
  789. Inc(Offset, P-1);
  790. F.Seek(Offset);
  791. F.WriteAnsiString(AnsiString(Format('%*s', [Q-P-Length(COMCTL32Entry), ' '])));
  792. F.WriteAnsiString(AnsiString(Copy(S, Q, R-Q)));
  793. F.WriteAnsiString(COMCTL32Entry);
  794. end;
  795. end.