ResUpdate.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. unit ResUpdate;
  2. {
  3. Inno Setup
  4. Copyright (C) 1997-2016 Jordan Russell
  5. Portions by Martijn Laan
  6. For conditions of distribution and use, see LICENSE.TXT.
  7. Resource update functions used by both Setup and the compiler
  8. }
  9. interface
  10. {$I VERSION.INC}
  11. uses
  12. Windows, SysUtils, FileClass;
  13. function ReadSignatureAndChecksumFields(const F: TCustomFile;
  14. var ASignatureAddress, ASignatureSize, AChecksum: DWORD): Boolean;
  15. function ReadSignatureAndChecksumFields64(const F: TCustomFile;
  16. var ASignatureAddress, ASignatureSize, AChecksum: DWORD): Boolean;
  17. function SeekToResourceData(const F: TCustomFile; const ResType, ResId: Cardinal): Cardinal;
  18. function UpdateSignatureAndChecksumFields(const F: TCustomFile;
  19. const ASignatureAddress, ASignatureSize, AChecksum: DWORD): Boolean;
  20. procedure UpdateManifestRequestedExecutionLevel(const F: TCustomFile;
  21. const RequireAdministrator: Boolean);
  22. implementation
  23. uses
  24. Int64Em;
  25. const
  26. IMAGE_NT_SIGNATURE = $00004550;
  27. IMAGE_NT_OPTIONAL_HDR32_MAGIC = $10b;
  28. IMAGE_NT_OPTIONAL_HDR64_MAGIC = $20b;
  29. IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;
  30. IMAGE_SIZEOF_SHORT_NAME = 8;
  31. IMAGE_DIRECTORY_ENTRY_RESOURCE = 2;
  32. IMAGE_DIRECTORY_ENTRY_SECURITY = 4;
  33. type
  34. PImageFileHeader = ^TImageFileHeader;
  35. TImageFileHeader = packed record
  36. Machine: Word;
  37. NumberOfSections: Word;
  38. TimeDateStamp: DWORD;
  39. PointerToSymbolTable: DWORD;
  40. NumberOfSymbols: DWORD;
  41. SizeOfOptionalHeader: Word;
  42. Characteristics: Word;
  43. end;
  44. PImageDataDirectory = ^TImageDataDirectory;
  45. TImageDataDirectory = record
  46. VirtualAddress: DWORD;
  47. Size: DWORD;
  48. end;
  49. PImageOptionalHeader = ^TImageOptionalHeader;
  50. TImageOptionalHeader = packed record
  51. { Standard fields. }
  52. Magic: Word;
  53. MajorLinkerVersion: Byte;
  54. MinorLinkerVersion: Byte;
  55. SizeOfCode: DWORD;
  56. SizeOfInitializedData: DWORD;
  57. SizeOfUninitializedData: DWORD;
  58. AddressOfEntryPoint: DWORD;
  59. BaseOfCode: DWORD;
  60. BaseOfData: DWORD;
  61. { NT additional fields. }
  62. ImageBase: DWORD;
  63. SectionAlignment: DWORD;
  64. FileAlignment: DWORD;
  65. MajorOperatingSystemVersion: Word;
  66. MinorOperatingSystemVersion: Word;
  67. MajorImageVersion: Word;
  68. MinorImageVersion: Word;
  69. MajorSubsystemVersion: Word;
  70. MinorSubsystemVersion: Word;
  71. Win32VersionValue: DWORD;
  72. SizeOfImage: DWORD;
  73. SizeOfHeaders: DWORD;
  74. CheckSum: DWORD;
  75. Subsystem: Word;
  76. DllCharacteristics: Word;
  77. SizeOfStackReserve: DWORD;
  78. SizeOfStackCommit: DWORD;
  79. SizeOfHeapReserve: DWORD;
  80. SizeOfHeapCommit: DWORD;
  81. LoaderFlags: DWORD;
  82. NumberOfRvaAndSizes: DWORD;
  83. DataDirectory: packed array[0..IMAGE_NUMBEROF_DIRECTORY_ENTRIES-1] of TImageDataDirectory;
  84. end;
  85. PImageOptionalHeader64 = ^TImageOptionalHeader64;
  86. TImageOptionalHeader64 = packed record
  87. { Standard fields. }
  88. Magic: Word;
  89. MajorLinkerVersion: Byte;
  90. MinorLinkerVersion: Byte;
  91. SizeOfCode: DWORD;
  92. SizeOfInitializedData: DWORD;
  93. SizeOfUninitializedData: DWORD;
  94. AddressOfEntryPoint: DWORD;
  95. BaseOfCode: DWORD;
  96. { NT additional fields. }
  97. ImageBase: Integer64;
  98. SectionAlignment: DWORD;
  99. FileAlignment: DWORD;
  100. MajorOperatingSystemVersion: Word;
  101. MinorOperatingSystemVersion: Word;
  102. MajorImageVersion: Word;
  103. MinorImageVersion: Word;
  104. MajorSubsystemVersion: Word;
  105. MinorSubsystemVersion: Word;
  106. Win32VersionValue: DWORD;
  107. SizeOfImage: DWORD;
  108. SizeOfHeaders: DWORD;
  109. CheckSum: DWORD;
  110. Subsystem: Word;
  111. DllCharacteristics: Word;
  112. SizeOfStackReserve: Integer64;
  113. SizeOfStackCommit: Integer64;
  114. SizeOfHeapReserve: Integer64;
  115. SizeOfHeapCommit: Integer64;
  116. LoaderFlags: DWORD;
  117. NumberOfRvaAndSizes: DWORD;
  118. DataDirectory: packed array[0..IMAGE_NUMBEROF_DIRECTORY_ENTRIES-1] of TImageDataDirectory;
  119. end;
  120. TISHMisc = packed record
  121. case Integer of
  122. 0: (PhysicalAddress: DWORD);
  123. 1: (VirtualSize: DWORD);
  124. end;
  125. PImageSectionHeader = ^TImageSectionHeader;
  126. TImageSectionHeader = packed record
  127. Name: packed array[0..IMAGE_SIZEOF_SHORT_NAME-1] of Byte;
  128. Misc: TISHMisc;
  129. VirtualAddress: DWORD;
  130. SizeOfRawData: DWORD;
  131. PointerToRawData: DWORD;
  132. PointerToRelocations: DWORD;
  133. PointerToLinenumbers: DWORD;
  134. NumberOfRelocations: Word;
  135. NumberOfLinenumbers: Word;
  136. Characteristics: DWORD;
  137. end;
  138. TImageResourceDirectory = packed record
  139. Characteristics: DWORD;
  140. TimeDateStamp: DWORD;
  141. MajorVersion: Word;
  142. MinorVersion: Word;
  143. NumberOfNamedEntries: Word;
  144. NumberOfIdEntries: Word;
  145. end;
  146. TImageResourceDirectoryEntry = packed record
  147. Id: DWORD;
  148. Offset: DWORD;
  149. end;
  150. TImageResourceDataEntry = packed record
  151. OffsetToData: DWORD;
  152. Size: DWORD;
  153. CodePage: DWORD;
  154. Reserved: DWORD;
  155. end;
  156. procedure Error(const Msg: String);
  157. begin
  158. raise Exception.Create('Resource update error: ' + Msg);
  159. end;
  160. function SeekToPEHeader(const F: TCustomFile): Boolean;
  161. var
  162. DosHeader: packed record
  163. Sig: array[0..1] of AnsiChar;
  164. Other: array[0..57] of Byte;
  165. PEHeaderOffset: LongWord;
  166. end;
  167. Sig: DWORD;
  168. begin
  169. Result := False;
  170. F.Seek(0);
  171. if F.Read(DosHeader, SizeOf(DosHeader)) = SizeOf(DosHeader) then begin
  172. if (DosHeader.Sig[0] = 'M') and (DosHeader.Sig[1] = 'Z') and
  173. (DosHeader.PEHeaderOffset <> 0) then begin
  174. F.Seek(DosHeader.PEHeaderOffset);
  175. if F.Read(Sig, SizeOf(Sig)) = SizeOf(Sig) then
  176. if Sig = IMAGE_NT_SIGNATURE then
  177. Result := True;
  178. end;
  179. end;
  180. end;
  181. function SeekToAndReadPEOptionalHeader(const F: TCustomFile;
  182. var OptHeader: TImageOptionalHeader; var OptHeaderOffset: Integer64): Boolean;
  183. var
  184. Header: TImageFileHeader;
  185. begin
  186. Result := False;
  187. if SeekToPEHeader(F) then begin
  188. if (F.Read(Header, SizeOf(Header)) = SizeOf(Header)) and
  189. (Header.SizeOfOptionalHeader = SizeOf(OptHeader)) then begin
  190. OptHeaderOffset := F.Position;
  191. if F.Read(OptHeader, SizeOf(OptHeader)) = SizeOf(OptHeader) then
  192. if OptHeader.Magic = IMAGE_NT_OPTIONAL_HDR32_MAGIC then
  193. Result := True;
  194. end;
  195. end;
  196. end;
  197. function SeekToAndReadPEOptionalHeader64(const F: TCustomFile;
  198. var OptHeader: TImageOptionalHeader64; var OptHeaderOffset: Integer64): Boolean;
  199. var
  200. Header: TImageFileHeader;
  201. begin
  202. Result := False;
  203. if SeekToPEHeader(F) then begin
  204. if (F.Read(Header, SizeOf(Header)) = SizeOf(Header)) and
  205. (Header.SizeOfOptionalHeader = SizeOf(OptHeader)) then begin
  206. OptHeaderOffset := F.Position;
  207. if F.Read(OptHeader, SizeOf(OptHeader)) = SizeOf(OptHeader) then
  208. if OptHeader.Magic = IMAGE_NT_OPTIONAL_HDR64_MAGIC then
  209. Result := True;
  210. end;
  211. end;
  212. end;
  213. procedure FindResourceSection(const F: TCustomFile;
  214. var SectionVirtualAddr, SectionPhysOffset, SectionPhysSize: Cardinal);
  215. var
  216. EXESig: Word;
  217. PEHeaderOffset, PESig: Cardinal;
  218. PEHeader: TImageFileHeader;
  219. PEOptHeader: TImageOptionalHeader;
  220. PESectionHeader: TImageSectionHeader;
  221. I: Integer;
  222. begin
  223. { Read DOS header }
  224. F.Seek(0);
  225. F.ReadBuffer(EXESig, SizeOf(EXESig));
  226. if EXESig <> $5A4D {'MZ'} then
  227. Error('File isn''t an EXE file (1)');
  228. F.Seek($3C);
  229. F.ReadBuffer(PEHeaderOffset, SizeOf(PEHeaderOffset));
  230. if PEHeaderOffset = 0 then
  231. Error('File isn''t a PE file (1)');
  232. { Read PE header & optional header }
  233. F.Seek(PEHeaderOffset);
  234. F.ReadBuffer(PESig, SizeOf(PESig));
  235. if PESig <> $00004550 {'PE'#0#0} then
  236. Error('File isn''t a PE file (2)');
  237. F.ReadBuffer(PEHeader, SizeOf(PEHeader));
  238. if PEHeader.SizeOfOptionalHeader <> SizeOf(PEOptHeader) then
  239. Error('File isn''t a PE file (3)');
  240. F.ReadBuffer(PEOptHeader, SizeOf(PEOptHeader));
  241. if PEOptHeader.Magic <> IMAGE_NT_OPTIONAL_HDR32_MAGIC then
  242. Error('File isn''t a PE file (4)');
  243. { Scan section headers for resource section }
  244. if (PEOptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = 0) or
  245. (PEOptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = 0) then
  246. Error('No resources (1)');
  247. SectionVirtualAddr := PEOptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
  248. SectionPhysOffset := 0;
  249. for I := 0 to PEHeader.NumberOfSections-1 do begin
  250. F.ReadBuffer(PESectionHeader, SizeOf(PESectionHeader));
  251. if (PESectionHeader.VirtualAddress = SectionVirtualAddr) and
  252. (PESectionHeader.SizeOfRawData <> 0) then begin
  253. SectionPhysOffset := PESectionHeader.PointerToRawData;
  254. SectionPhysSize := PESectionHeader.SizeOfRawData;
  255. Break;
  256. end;
  257. end;
  258. if SectionPhysOffset = 0 then
  259. Error('No resources (2)');
  260. end;
  261. function FindResOffset(const F: TCustomFile; const AnyId: Boolean;
  262. const Id: Cardinal; const FindSubdir: Boolean; var Offset: Cardinal): Boolean;
  263. var
  264. Dir: TImageResourceDirectory;
  265. Entry: TImageResourceDirectoryEntry;
  266. I: Integer;
  267. begin
  268. F.ReadBuffer(Dir, SizeOf(Dir));
  269. { Skip over named entries }
  270. for I := 0 to Dir.NumberOfNamedEntries-1 do
  271. F.ReadBuffer(Entry, SizeOf(Entry));
  272. { Now process ID entries }
  273. Result := False;
  274. for I := 0 to Dir.NumberOfIdEntries-1 do begin
  275. F.ReadBuffer(Entry, SizeOf(Entry));
  276. if (AnyId or (Entry.Id = Id)) and
  277. ((Entry.Offset and $80000000 <> 0) = FindSubdir) then begin
  278. Offset := Entry.Offset and $7FFFFFFF;
  279. Result := True;
  280. Break;
  281. end;
  282. end;
  283. end;
  284. function SeekToResourceData(const F: TCustomFile; const ResType, ResId: Cardinal): Cardinal;
  285. { Seeks to the specified resource's data, and returns its size. Raises an
  286. exception if the resource cannot be found. }
  287. var
  288. SectionVirtualAddr, SectionPhysOffset, SectionPhysSize, Ofs: Cardinal;
  289. DataEntry: TImageResourceDataEntry;
  290. begin
  291. FindResourceSection(F, SectionVirtualAddr, SectionPhysOffset, SectionPhysSize);
  292. { Scan the resource directory }
  293. F.Seek(SectionPhysOffset);
  294. if not FindResOffset(F, False, ResType, True, Ofs) then
  295. Error('Can''t find resource (1)');
  296. F.Seek(SectionPhysOffset + Ofs);
  297. if not FindResOffset(F, False, ResId, True, Ofs) then
  298. Error('Can''t find resource (2)');
  299. F.Seek(SectionPhysOffset + Ofs);
  300. if not FindResOffset(F, True, 0, False, Ofs) then
  301. Error('Can''t find resource (3).');
  302. F.Seek(SectionPhysOffset + Ofs);
  303. F.ReadBuffer(DataEntry, SizeOf(DataEntry));
  304. { Sanity check: DataEntry.OffsetToData is an RVA. It's technically possible
  305. for the RVA to point to a different section, but we don't support that. }
  306. if Cardinal(DataEntry.OffsetToData) < SectionVirtualAddr then
  307. Error('Invalid resource (1)');
  308. if Cardinal(DataEntry.OffsetToData - SectionVirtualAddr + DataEntry.Size) > SectionPhysSize then
  309. Error('Invalid resource (2)');
  310. { Seek to the resource }
  311. F.Seek(SectionPhysOffset + (DataEntry.OffsetToData - SectionVirtualAddr));
  312. Result := DataEntry.Size;
  313. end;
  314. procedure UpdateManifestRequestedExecutionLevel(const F: TCustomFile;
  315. const RequireAdministrator: Boolean);
  316. const
  317. ElementText: AnsiString = '<requestedExecutionLevel level="';
  318. Levels: array[Boolean] of AnsiString = (
  319. 'highestAvailable" ',
  320. 'requireAdministrator"');
  321. var
  322. S: AnsiString;
  323. Offset: Integer64;
  324. P: Integer;
  325. begin
  326. { Read the manifest resource into a string }
  327. SetString(S, nil, SeekToResourceData(F, 24, 1));
  328. Offset := F.Position;
  329. F.ReadBuffer(S[1], Length(S));
  330. { Locate and update the requestedExecutionLevel element }
  331. P := Pos(ElementText, S);
  332. if P = 0 then
  333. Error('Element not found');
  334. Inc(P, Length(ElementText));
  335. if Copy(S, P+21, 10) <> ' uiAccess=' then
  336. Error('Level too short');
  337. Inc64(Offset, P-1);
  338. F.Seek64(Offset);
  339. F.WriteAnsiString(Levels[RequireAdministrator]);
  340. end;
  341. function ReadSignatureAndChecksumFields(const F: TCustomFile;
  342. var ASignatureAddress, ASignatureSize, AChecksum: DWORD): Boolean;
  343. { Reads the signature and checksum fields in the specified file's header.
  344. If the file is not a valid PE32 executable, False is returned. }
  345. var
  346. OptHeader: TImageOptionalHeader;
  347. OptHeaderOffset: Integer64;
  348. begin
  349. Result := SeekToAndReadPEOptionalHeader(F, OptHeader, OptHeaderOffset);
  350. if Result then begin
  351. ASignatureAddress := OptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress;
  352. ASignatureSize := OptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].Size;
  353. AChecksum := OptHeader.CheckSum;
  354. end;
  355. end;
  356. function ReadSignatureAndChecksumFields64(const F: TCustomFile;
  357. var ASignatureAddress, ASignatureSize, AChecksum: DWORD): Boolean;
  358. { Reads the signature and checksum fields in the specified file's header.
  359. If the file is not a valid PE32+ executable, False is returned. }
  360. var
  361. OptHeader: TImageOptionalHeader64;
  362. OptHeaderOffset: Integer64;
  363. begin
  364. Result := SeekToAndReadPEOptionalHeader64(F, OptHeader, OptHeaderOffset);
  365. if Result then begin
  366. ASignatureAddress := OptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress;
  367. ASignatureSize := OptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].Size;
  368. AChecksum := OptHeader.CheckSum;
  369. end;
  370. end;
  371. function UpdateSignatureAndChecksumFields(const F: TCustomFile;
  372. const ASignatureAddress, ASignatureSize, AChecksum: DWORD): Boolean;
  373. { Sets the signature and checksum fields in the specified file's header.
  374. If the file is not a valid PE32 executable, False is returned. }
  375. var
  376. OptHeader: TImageOptionalHeader;
  377. OptHeaderOffset: Integer64;
  378. begin
  379. Result := SeekToAndReadPEOptionalHeader(F, OptHeader, OptHeaderOffset);
  380. if Result then begin
  381. OptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress := ASignatureAddress;
  382. OptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].Size := ASignatureSize;
  383. OptHeader.CheckSum := AChecksum;
  384. F.Seek64(OptHeaderOffset);
  385. F.WriteBuffer(OptHeader, SizeOf(OptHeader));
  386. end;
  387. end;
  388. end.