Shared.ResUpdateFunc.pas 14 KB

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