Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

NtExecutableResource.js 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. import ImageDirectoryEntry from './format/ImageDirectoryEntry.js';
  2. import { binaryToString, cloneObject, copyBuffer, roundUp, stringToBinary, } from './util/functions.js';
  3. function removeDuplicates(a) {
  4. return a.reduce(function (p, c) {
  5. return p.indexOf(c) >= 0 ? p : p.concat(c);
  6. }, []);
  7. }
  8. function readString(view, offset) {
  9. var length = view.getUint16(offset, true);
  10. var r = '';
  11. offset += 2;
  12. for (var i = 0; i < length; ++i) {
  13. r += String.fromCharCode(view.getUint16(offset, true));
  14. offset += 2;
  15. }
  16. return r;
  17. }
  18. function readLanguageTable(view, typeEntry, name, languageTable, cb) {
  19. var off = languageTable;
  20. var nameEntry = {
  21. name: name,
  22. languageTable: languageTable,
  23. characteristics: view.getUint32(off, true),
  24. dateTime: view.getUint32(off + 4, true),
  25. majorVersion: view.getUint16(off + 8, true),
  26. minorVersion: view.getUint16(off + 10, true),
  27. };
  28. var nameCount = view.getUint16(off + 12, true);
  29. var idCount = view.getUint16(off + 14, true);
  30. off += 16;
  31. for (var i = 0; i < nameCount; ++i) {
  32. var nameOffset = view.getUint32(off, true) & 0x7fffffff;
  33. var dataOffset = view.getUint32(off + 4, true);
  34. // ignore if the offset refers to the next table
  35. if ((dataOffset & 0x80000000) !== 0) {
  36. off += 8;
  37. continue;
  38. }
  39. var name_1 = readString(view, nameOffset);
  40. cb(typeEntry, nameEntry, { lang: name_1, dataOffset: dataOffset });
  41. off += 8;
  42. }
  43. for (var i = 0; i < idCount; ++i) {
  44. var id = view.getUint32(off, true) & 0x7fffffff;
  45. var dataOffset = view.getUint32(off + 4, true);
  46. // ignore if the offset refers to the next table
  47. if ((dataOffset & 0x80000000) !== 0) {
  48. off += 8;
  49. continue;
  50. }
  51. cb(typeEntry, nameEntry, { lang: id, dataOffset: dataOffset });
  52. off += 8;
  53. }
  54. }
  55. function readNameTable(view, type, nameTable, cb) {
  56. var off = nameTable;
  57. var typeEntry = {
  58. type: type,
  59. nameTable: nameTable,
  60. characteristics: view.getUint32(off, true),
  61. dateTime: view.getUint32(off + 4, true),
  62. majorVersion: view.getUint16(off + 8, true),
  63. minorVersion: view.getUint16(off + 10, true),
  64. };
  65. var nameCount = view.getUint16(off + 12, true);
  66. var idCount = view.getUint16(off + 14, true);
  67. off += 16;
  68. for (var i = 0; i < nameCount; ++i) {
  69. var nameOffset = view.getUint32(off, true) & 0x7fffffff;
  70. var nextTable = view.getUint32(off + 4, true);
  71. // ignore if no next table is available
  72. if (!(nextTable & 0x80000000)) {
  73. off += 8;
  74. continue;
  75. }
  76. nextTable &= 0x7fffffff;
  77. var name_2 = readString(view, nameOffset);
  78. readLanguageTable(view, typeEntry, name_2, nextTable, cb);
  79. off += 8;
  80. }
  81. for (var i = 0; i < idCount; ++i) {
  82. var id = view.getUint32(off, true) & 0x7fffffff;
  83. var nextTable = view.getUint32(off + 4, true);
  84. // ignore if no next table is available
  85. if (!(nextTable & 0x80000000)) {
  86. off += 8;
  87. continue;
  88. }
  89. nextTable &= 0x7fffffff;
  90. readLanguageTable(view, typeEntry, id, nextTable, cb);
  91. off += 8;
  92. }
  93. }
  94. function divideEntriesImplByID(r, names, entries) {
  95. var entriesByString = {};
  96. var entriesByNumber = {};
  97. entries.forEach(function (e) {
  98. if (typeof e.lang === 'string') {
  99. entriesByString[e.lang] = e;
  100. names.push(e.lang);
  101. }
  102. else {
  103. entriesByNumber[e.lang] = e;
  104. }
  105. });
  106. var strKeys = Object.keys(entriesByString);
  107. strKeys.sort().forEach(function (type) {
  108. r.s.push(entriesByString[type]);
  109. });
  110. var numKeys = Object.keys(entriesByNumber);
  111. numKeys
  112. .map(function (k) { return Number(k); })
  113. .sort(function (a, b) { return a - b; })
  114. .forEach(function (type) {
  115. r.n.push(entriesByNumber[type]);
  116. });
  117. return 16 + 8 * (strKeys.length + numKeys.length);
  118. }
  119. function divideEntriesImplByName(r, names, entries) {
  120. var entriesByString = {};
  121. var entriesByNumber = {};
  122. entries.forEach(function (e) {
  123. var _a, _b;
  124. if (typeof e.id === 'string') {
  125. var a = (_a = entriesByString[e.id]) !== null && _a !== void 0 ? _a : (entriesByString[e.id] = []);
  126. names.push(e.id);
  127. a.push(e);
  128. }
  129. else {
  130. var a = (_b = entriesByNumber[e.id]) !== null && _b !== void 0 ? _b : (entriesByNumber[e.id] = []);
  131. a.push(e);
  132. }
  133. });
  134. var sSum = Object.keys(entriesByString)
  135. .sort()
  136. .map(function (id) {
  137. var o = {
  138. id: id,
  139. s: [],
  140. n: [],
  141. };
  142. r.s.push(o);
  143. return divideEntriesImplByID(o, names, entriesByString[id]);
  144. })
  145. .reduce(function (p, c) { return p + 8 + c; }, 0);
  146. var nSum = Object.keys(entriesByNumber)
  147. .map(function (k) { return Number(k); })
  148. .sort(function (a, b) { return a - b; })
  149. .map(function (id) {
  150. var o = {
  151. id: id,
  152. s: [],
  153. n: [],
  154. };
  155. r.n.push(o);
  156. return divideEntriesImplByID(o, names, entriesByNumber[id]);
  157. })
  158. .reduce(function (p, c) { return p + 8 + c; }, 0);
  159. return 16 + sSum + nSum;
  160. }
  161. function divideEntriesImplByType(r, names, entries) {
  162. var entriesByString = {};
  163. var entriesByNumber = {};
  164. entries.forEach(function (e) {
  165. var _a, _b;
  166. if (typeof e.type === 'string') {
  167. var a = (_a = entriesByString[e.type]) !== null && _a !== void 0 ? _a : (entriesByString[e.type] = []);
  168. names.push(e.type);
  169. a.push(e);
  170. }
  171. else {
  172. var a = (_b = entriesByNumber[e.type]) !== null && _b !== void 0 ? _b : (entriesByNumber[e.type] = []);
  173. a.push(e);
  174. }
  175. });
  176. var sSum = Object.keys(entriesByString)
  177. .sort()
  178. .map(function (type) {
  179. var o = { type: type, s: [], n: [] };
  180. r.s.push(o);
  181. return divideEntriesImplByName(o, names, entriesByString[type]);
  182. })
  183. .reduce(function (p, c) { return p + 8 + c; }, 0);
  184. var nSum = Object.keys(entriesByNumber)
  185. .map(function (k) { return Number(k); })
  186. .sort(function (a, b) { return a - b; })
  187. .map(function (type) {
  188. var o = { type: type, s: [], n: [] };
  189. r.n.push(o);
  190. return divideEntriesImplByName(o, names, entriesByNumber[type]);
  191. })
  192. .reduce(function (p, c) { return p + 8 + c; }, 0);
  193. return 16 + sSum + nSum;
  194. }
  195. function calculateStringLengthForWrite(text) {
  196. var length = text.length;
  197. // limit to 65535 because the 'length' field is uint16
  198. return length > 65535 ? 65535 : length;
  199. }
  200. function getStringOffset(target, strings) {
  201. var l = strings.length;
  202. for (var i = 0; i < l; ++i) {
  203. var s = strings[i];
  204. if (s.text === target) {
  205. return s.offset;
  206. }
  207. }
  208. throw new Error('Unexpected');
  209. }
  210. /** (returns offset just after the written text) */
  211. function writeString(view, offset, text) {
  212. var length = calculateStringLengthForWrite(text);
  213. view.setUint16(offset, length, true);
  214. offset += 2;
  215. for (var i = 0; i < length; ++i) {
  216. view.setUint16(offset, text.charCodeAt(i), true);
  217. offset += 2;
  218. }
  219. return offset;
  220. }
  221. function writeLanguageTable(view, offset, strings, data) {
  222. // characteristics
  223. view.setUint32(offset, 0, true);
  224. // timestamp
  225. view.setUint32(offset + 4, 0, true);
  226. // major version / minor version
  227. view.setUint32(offset + 8, 0, true);
  228. // name entries
  229. view.setUint16(offset + 12, data.s.length, true);
  230. // id entries
  231. view.setUint16(offset + 14, data.n.length, true);
  232. offset += 16;
  233. // name entries (not in specification)
  234. data.s.forEach(function (e) {
  235. var strOff = getStringOffset(e.lang, strings);
  236. view.setUint32(offset, strOff, true);
  237. view.setUint32(offset + 4, e.offset, true);
  238. offset += 8;
  239. });
  240. // id entries
  241. data.n.forEach(function (e) {
  242. view.setUint32(offset, e.lang, true);
  243. view.setUint32(offset + 4, e.offset, true);
  244. offset += 8;
  245. });
  246. return offset;
  247. }
  248. function writeNameTable(view, offset, leafOffset, strings, data) {
  249. // characteristics
  250. view.setUint32(offset, 0, true);
  251. // timestamp
  252. view.setUint32(offset + 4, 0, true);
  253. // major version / minor version
  254. view.setUint32(offset + 8, 0, true);
  255. // name entries
  256. view.setUint16(offset + 12, data.s.length, true);
  257. // id entries
  258. view.setUint16(offset + 14, data.n.length, true);
  259. offset += 16;
  260. data.s.forEach(function (e) {
  261. e.offset = leafOffset;
  262. leafOffset = writeLanguageTable(view, leafOffset, strings, e);
  263. });
  264. data.n.forEach(function (e) {
  265. e.offset = leafOffset;
  266. leafOffset = writeLanguageTable(view, leafOffset, strings, e);
  267. });
  268. data.s.forEach(function (e) {
  269. var strOff = getStringOffset(e.id, strings);
  270. view.setUint32(offset, strOff + 0x80000000, true);
  271. view.setUint32(offset + 4, e.offset + 0x80000000, true);
  272. offset += 8;
  273. });
  274. data.n.forEach(function (e) {
  275. view.setUint32(offset, e.id, true);
  276. view.setUint32(offset + 4, e.offset + 0x80000000, true);
  277. offset += 8;
  278. });
  279. return leafOffset;
  280. }
  281. function writeTypeTable(view, offset, strings, data) {
  282. // characteristics
  283. view.setUint32(offset, 0, true);
  284. // timestamp
  285. view.setUint32(offset + 4, 0, true);
  286. // major version / minor version
  287. view.setUint32(offset + 8, 0, true);
  288. // name entries
  289. view.setUint16(offset + 12, data.s.length, true);
  290. // id entries
  291. view.setUint16(offset + 14, data.n.length, true);
  292. offset += 16;
  293. var nextTableOffset = offset + 8 * (data.s.length + data.n.length);
  294. data.s.forEach(function (e) {
  295. e.offset = nextTableOffset;
  296. nextTableOffset += 16 + 8 * (e.s.length + e.n.length);
  297. });
  298. data.n.forEach(function (e) {
  299. e.offset = nextTableOffset;
  300. nextTableOffset += 16 + 8 * (e.s.length + e.n.length);
  301. });
  302. data.s.forEach(function (e) {
  303. var strOff = getStringOffset(e.type, strings);
  304. view.setUint32(offset, strOff + 0x80000000, true);
  305. view.setUint32(offset + 4, e.offset + 0x80000000, true);
  306. offset += 8;
  307. nextTableOffset = writeNameTable(view, e.offset, nextTableOffset, strings, e);
  308. });
  309. data.n.forEach(function (e) {
  310. view.setUint32(offset, e.type, true);
  311. view.setUint32(offset + 4, e.offset + 0x80000000, true);
  312. offset += 8;
  313. nextTableOffset = writeNameTable(view, e.offset, nextTableOffset, strings, e);
  314. });
  315. return nextTableOffset;
  316. }
  317. ////////////////////////////////////////////////////////////////////////////////
  318. /** Manages resource data for NtExecutable */
  319. var NtExecutableResource = /** @class */ (function () {
  320. function NtExecutableResource() {
  321. /** The timestamp for resource */
  322. this.dateTime = 0;
  323. /** The major version data for resource */
  324. this.majorVersion = 0;
  325. /** The minor version data for resource */
  326. this.minorVersion = 0;
  327. /** Resource entries */
  328. this.entries = [];
  329. /**
  330. * The section data header of resource data (used by outputResource method).
  331. * This instance will be null if the base executable does not contain resource data.
  332. * You can override this field before calling outputResource method.
  333. * (Note that the addresses and sizes are ignored for output)
  334. */
  335. this.sectionDataHeader = null;
  336. this.originalSize = 0;
  337. }
  338. NtExecutableResource.prototype.parse = function (section, ignoreUnparsableData) {
  339. if (!section.data) {
  340. return;
  341. }
  342. var view = new DataView(section.data);
  343. // --- First: Resource Directory Table ---
  344. // (off: 0 -- Characteristics (uint32))
  345. this.dateTime = view.getUint32(4, true);
  346. this.majorVersion = view.getUint16(8, true);
  347. this.minorVersion = view.getUint16(10, true);
  348. var nameCount = view.getUint16(12, true);
  349. var idCount = view.getUint16(14, true);
  350. var off = 16;
  351. var res = [];
  352. var cb = function (t, n, l) {
  353. var off = view.getUint32(l.dataOffset, true) -
  354. section.info.virtualAddress;
  355. var size = view.getUint32(l.dataOffset + 4, true);
  356. var cp = view.getUint32(l.dataOffset + 8, true);
  357. if (off >= 0) {
  358. var bin = new Uint8Array(size);
  359. bin.set(new Uint8Array(section.data, off, size));
  360. res.push({
  361. type: t.type,
  362. id: n.name,
  363. lang: l.lang,
  364. codepage: cp,
  365. bin: bin.buffer,
  366. });
  367. }
  368. else {
  369. if (!ignoreUnparsableData) {
  370. throw new Error('Cannot parse resource directory entry; RVA seems to be invalid.');
  371. }
  372. res.push({
  373. type: t.type,
  374. id: n.name,
  375. lang: l.lang,
  376. codepage: cp,
  377. bin: new ArrayBuffer(0),
  378. rva: l.dataOffset,
  379. });
  380. }
  381. };
  382. for (var i = 0; i < nameCount; ++i) {
  383. var nameOffset = view.getUint32(off, true) & 0x7fffffff;
  384. var nextTable = view.getUint32(off + 4, true);
  385. // ignore if no next table is available
  386. if (!(nextTable & 0x80000000)) {
  387. off += 8;
  388. continue;
  389. }
  390. nextTable &= 0x7fffffff;
  391. var name_3 = readString(view, nameOffset);
  392. readNameTable(view, name_3, nextTable, cb);
  393. off += 8;
  394. }
  395. for (var i = 0; i < idCount; ++i) {
  396. var typeId = view.getUint32(off, true) & 0x7fffffff;
  397. var nextTable = view.getUint32(off + 4, true);
  398. // ignore if no next table is available
  399. if (!(nextTable & 0x80000000)) {
  400. off += 8;
  401. continue;
  402. }
  403. nextTable &= 0x7fffffff;
  404. readNameTable(view, typeId, nextTable, cb);
  405. off += 8;
  406. }
  407. this.entries = res;
  408. this.originalSize = section.data.byteLength;
  409. };
  410. /**
  411. * Parses resource data for `NtExecutable`.
  412. * This function returns valid instance even if
  413. * the executable does not have resource data.
  414. * @param exe `NtExecutable` instance
  415. * @param ignoreUnparsableData (default: false) specify true if skipping 'unparsable' (e.g. unusual format) data.
  416. * When true, the resource data may break on write operation.
  417. */
  418. NtExecutableResource.from = function (exe, ignoreUnparsableData) {
  419. if (ignoreUnparsableData === void 0) { ignoreUnparsableData = false; }
  420. var secs = []
  421. .concat(exe.getAllSections())
  422. .sort(function (a, b) { return a.info.virtualAddress - b.info.virtualAddress; });
  423. var entry = exe.getSectionByEntry(ImageDirectoryEntry.Resource);
  424. // check if the section order is supported
  425. // (not supported if any other sections except 'relocation' is available,
  426. // because the recalculation of virtual address is not simple)
  427. if (entry) {
  428. var reloc = exe.getSectionByEntry(ImageDirectoryEntry.BaseRelocation);
  429. for (var i = 0; i < secs.length; ++i) {
  430. var s = secs[i];
  431. if (s === entry) {
  432. for (var j = i + 1; j < secs.length; ++j) {
  433. if (!reloc || secs[j] !== reloc) {
  434. throw new Error('After Resource section, sections except for relocation are not supported');
  435. }
  436. }
  437. break;
  438. }
  439. }
  440. }
  441. var r = new NtExecutableResource();
  442. r.sectionDataHeader = entry ? cloneObject(entry.info) : null;
  443. if (entry) {
  444. r.parse(entry, ignoreUnparsableData);
  445. }
  446. return r;
  447. };
  448. /**
  449. * Add or replace the resource entry.
  450. * This method replaces the entry only if there is an entry with `type`, `id` and `lang` equal.
  451. */
  452. NtExecutableResource.prototype.replaceResourceEntry = function (entry) {
  453. for (var len = this.entries.length, i = 0; i < len; ++i) {
  454. var e = this.entries[i];
  455. if (e.type === entry.type &&
  456. e.id === entry.id &&
  457. e.lang === entry.lang) {
  458. this.entries[i] = entry;
  459. return;
  460. }
  461. }
  462. this.entries.push(entry);
  463. };
  464. /**
  465. * Returns all resource entries, which has specified type and id, as UTF-8 string data.
  466. * @param type Resource type
  467. * @param id Resource id
  468. * @returns an array of lang and value pair (tuple)
  469. */
  470. NtExecutableResource.prototype.getResourceEntriesAsString = function (type, id) {
  471. return this.entries
  472. .filter(function (entry) { return entry.type === type && entry.id === id; })
  473. .map(function (entry) { return [entry.lang, binaryToString(entry.bin)]; });
  474. };
  475. /**
  476. * Add or replace the resource entry with UTF-8 string data.
  477. * This method is a wrapper of {@link NtExecutableResource.replaceResourceEntry}.
  478. */
  479. NtExecutableResource.prototype.replaceResourceEntryFromString = function (type, id, lang, value) {
  480. var entry = {
  481. type: type,
  482. id: id,
  483. lang: lang,
  484. codepage: 1200,
  485. bin: stringToBinary(value),
  486. };
  487. this.replaceResourceEntry(entry);
  488. };
  489. /**
  490. * Removes resource entries which has specified type and id.
  491. */
  492. NtExecutableResource.prototype.removeResourceEntry = function (type, id, lang) {
  493. this.entries = this.entries.filter(function (entry) {
  494. return !(entry.type === type &&
  495. entry.id === id &&
  496. (typeof lang === 'undefined' || entry.lang === lang));
  497. });
  498. };
  499. /**
  500. * Generates resource data binary for NtExecutable (not for .res file)
  501. * @param virtualAddress The virtual address for the section
  502. * @param alignment File alignment value of executable
  503. * @param noGrow Set true to disallow growing resource section (throw errors if data exceeds)
  504. * @param allowShrink Set true to allow shrinking resource section (if the data size is less than original)
  505. */
  506. NtExecutableResource.prototype.generateResourceData = function (virtualAddress, alignment, noGrow, allowShrink) {
  507. if (noGrow === void 0) { noGrow = false; }
  508. if (allowShrink === void 0) { allowShrink = false; }
  509. // estimate data size and divide to output table
  510. var r = {
  511. s: [],
  512. n: [],
  513. };
  514. var strings = [];
  515. var size = divideEntriesImplByType(r, strings, this.entries);
  516. strings = removeDuplicates(strings);
  517. var stringsOffset = size;
  518. size += strings.reduce(function (prev, cur) {
  519. return prev + 2 + calculateStringLengthForWrite(cur) * 2;
  520. }, 0);
  521. size = roundUp(size, 8);
  522. var descOffset = size;
  523. size = this.entries.reduce(function (p, e) {
  524. e.offset = p;
  525. return p + 16;
  526. }, descOffset);
  527. var dataOffset = size;
  528. size = this.entries.reduce(function (p, e) {
  529. return roundUp(p, 8) + e.bin.byteLength;
  530. }, dataOffset);
  531. var alignedSize = roundUp(size, alignment);
  532. var originalAlignedSize = roundUp(this.originalSize, alignment);
  533. if (noGrow) {
  534. if (alignedSize > originalAlignedSize) {
  535. throw new Error('New resource data is larger than original');
  536. }
  537. }
  538. if (!allowShrink) {
  539. if (alignedSize < originalAlignedSize) {
  540. alignedSize = originalAlignedSize;
  541. }
  542. }
  543. // generate binary
  544. var bin = new ArrayBuffer(alignedSize);
  545. var view = new DataView(bin);
  546. var o = descOffset;
  547. var va = virtualAddress + dataOffset;
  548. this.entries.forEach(function (e) {
  549. var len = e.bin.byteLength;
  550. if (typeof e.rva !== 'undefined') {
  551. // RVA
  552. view.setUint32(o, e.rva, true);
  553. }
  554. else {
  555. va = roundUp(va, 8);
  556. // RVA
  557. view.setUint32(o, va, true);
  558. va += len;
  559. }
  560. // size
  561. view.setUint32(o + 4, len, true);
  562. // codepage
  563. view.setUint32(o + 8, e.codepage, true);
  564. // (zero)
  565. view.setUint32(o + 12, 0, true);
  566. o += 16;
  567. });
  568. o = dataOffset;
  569. this.entries.forEach(function (e) {
  570. var len = e.bin.byteLength;
  571. copyBuffer(bin, o, e.bin, 0, len);
  572. o += roundUp(len, 8);
  573. });
  574. var stringsData = [];
  575. o = stringsOffset;
  576. strings.forEach(function (s) {
  577. stringsData.push({
  578. offset: o,
  579. text: s,
  580. });
  581. o = writeString(view, o, s);
  582. });
  583. writeTypeTable(view, 0, stringsData, r);
  584. // fill with 'PADDINGX'
  585. if (alignedSize > size) {
  586. var pad = 'PADDINGX';
  587. for (var i = size, j = 0; i < alignedSize; ++i, ++j) {
  588. if (j === 8) {
  589. j = 0;
  590. }
  591. view.setUint8(i, pad.charCodeAt(j));
  592. }
  593. }
  594. return {
  595. bin: bin,
  596. rawSize: size,
  597. dataOffset: dataOffset,
  598. descEntryOffset: descOffset,
  599. descEntryCount: this.entries.length,
  600. };
  601. };
  602. /**
  603. * Writes holding resource data to specified NtExecutable instance.
  604. * @param exeDest An NtExecutable instance to write resource section to
  605. * @param noGrow Set true to disallow growing resource section (throw errors if data exceeds)
  606. * @param allowShrink Set true to allow shrinking resource section (if the data size is less than original)
  607. */
  608. NtExecutableResource.prototype.outputResource = function (exeDest, noGrow, allowShrink) {
  609. if (noGrow === void 0) { noGrow = false; }
  610. if (allowShrink === void 0) { allowShrink = false; }
  611. // make section data
  612. var fileAlign = exeDest.getFileAlignment();
  613. var sectionData;
  614. if (this.sectionDataHeader) {
  615. sectionData = {
  616. data: null,
  617. info: cloneObject(this.sectionDataHeader),
  618. };
  619. }
  620. else {
  621. sectionData = {
  622. data: null,
  623. info: {
  624. name: '.rsrc',
  625. virtualSize: 0,
  626. virtualAddress: 0,
  627. sizeOfRawData: 0,
  628. pointerToRawData: 0,
  629. pointerToRelocations: 0,
  630. pointerToLineNumbers: 0,
  631. numberOfRelocations: 0,
  632. numberOfLineNumbers: 0,
  633. characteristics: 0x40000040, // read access and initialized data
  634. },
  635. };
  636. }
  637. // first, set virtualAddress to 0 because
  638. // the virtual address is not determined now
  639. var data = this.generateResourceData(0, fileAlign, noGrow, allowShrink);
  640. sectionData.data = data.bin;
  641. sectionData.info.sizeOfRawData = data.bin.byteLength;
  642. sectionData.info.virtualSize = data.rawSize;
  643. // write as section
  644. exeDest.setSectionByEntry(ImageDirectoryEntry.Resource, sectionData);
  645. // rewrite section raw-data
  646. var generatedSection = exeDest.getSectionByEntry(ImageDirectoryEntry.Resource);
  647. var view = new DataView(generatedSection.data);
  648. // set RVA
  649. var o = data.descEntryOffset;
  650. var va = generatedSection.info.virtualAddress + data.dataOffset;
  651. for (var i = 0; i < data.descEntryCount; ++i) {
  652. var len = view.getUint32(o + 4, true);
  653. va = roundUp(va, 8);
  654. // RVA
  655. view.setUint32(o, va, true);
  656. va += len;
  657. o += 16;
  658. }
  659. };
  660. return NtExecutableResource;
  661. }());
  662. export default NtExecutableResource;