123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418 |
- import ImageDataDirectoryArray from './format/ImageDataDirectoryArray.js';
- import ImageDirectoryEntry from './format/ImageDirectoryEntry.js';
- import ImageDosHeader from './format/ImageDosHeader.js';
- import ImageNtHeaders from './format/ImageNtHeaders.js';
- import ImageSectionHeaderArray from './format/ImageSectionHeaderArray.js';
- import { allocatePartialBinary, calculateCheckSumForPE, cloneObject, cloneToArrayBuffer, roundUp, } from './util/functions.js';
- import { makeEmptyNtExecutableBinary } from './util/generate.js';
- var NtExecutable = /** @class */ (function () {
- function NtExecutable(_headers, _sections, _ex) {
- this._headers = _headers;
- this._sections = _sections;
- this._ex = _ex;
- var dh = ImageDosHeader.from(_headers);
- var nh = ImageNtHeaders.from(_headers, dh.newHeaderAddress);
- this._dh = dh;
- this._nh = nh;
- this._dda = nh.optionalHeaderDataDirectory;
- _sections.sort(function (a, b) {
- var ra = a.info.pointerToRawData;
- var rb = a.info.pointerToRawData;
- if (ra !== rb) {
- return ra - rb;
- }
- var va = a.info.virtualAddress;
- var vb = b.info.virtualAddress;
- if (va === vb) {
- return a.info.virtualSize - b.info.virtualSize;
- }
- return va - vb;
- });
- }
- /**
- * Creates an NtExecutable instance with an 'empty' executable binary.
- * @param is32Bit set true if the binary is for 32-bit (default: false)
- * @param isDLL set true if the binary is DLL (default: true)
- * @return NtExecutable instance
- */
- NtExecutable.createEmpty = function (is32Bit, isDLL) {
- if (is32Bit === void 0) { is32Bit = false; }
- if (isDLL === void 0) { isDLL = true; }
- return this.from(makeEmptyNtExecutableBinary(is32Bit, isDLL));
- };
- /**
- * Parse the binary and create NtExecutable instance.
- * An error will be thrown if the binary data is invalid
- * @param bin binary data
- * @param options additional option for parsing
- * @return NtExecutable instance
- */
- NtExecutable.from = function (bin, options) {
- var dh = ImageDosHeader.from(bin);
- var nh = ImageNtHeaders.from(bin, dh.newHeaderAddress);
- if (!dh.isValid() || !nh.isValid()) {
- throw new TypeError('Invalid binary format');
- }
- if (nh.fileHeader.numberOfSymbols > 0) {
- throw new Error('Binary with symbols is not supported now');
- }
- var fileAlignment = nh.optionalHeader.fileAlignment;
- var securityEntry = nh.optionalHeaderDataDirectory.get(ImageDirectoryEntry.Certificate);
- if (securityEntry.size > 0) {
- // Signed executables should be parsed only when `ignoreCert` is true
- if (!(options === null || options === void 0 ? void 0 : options.ignoreCert)) {
- throw new Error('Parsing signed executable binary is not allowed by default.');
- }
- }
- var secOff = dh.newHeaderAddress + nh.getSectionHeaderOffset();
- var secCount = nh.fileHeader.numberOfSections;
- var sections = [];
- var tempSectionHeaderBinary = allocatePartialBinary(bin, secOff, secCount * ImageSectionHeaderArray.itemSize);
- var secArray = ImageSectionHeaderArray.from(tempSectionHeaderBinary, secCount, 0);
- var lastOffset = roundUp(secOff + secCount * ImageSectionHeaderArray.itemSize, fileAlignment);
- // console.log(`from data size 0x${bin.byteLength.toString(16)}:`);
- secArray.forEach(function (info) {
- if (!info.pointerToRawData || !info.sizeOfRawData) {
- info.pointerToRawData = 0;
- info.sizeOfRawData = 0;
- sections.push({
- info: info,
- data: null,
- });
- }
- else {
- // console.log(` section ${info.name}: 0x${info.pointerToRawData.toString(16)}, size = 0x${info.sizeOfRawData.toString(16)}`);
- var secBin = allocatePartialBinary(bin, info.pointerToRawData, info.sizeOfRawData);
- sections.push({
- info: info,
- data: secBin,
- });
- var secEndOffset = roundUp(info.pointerToRawData + info.sizeOfRawData, fileAlignment);
- if (secEndOffset > lastOffset) {
- lastOffset = secEndOffset;
- }
- }
- });
- // the size of DOS and NT headers is equal to section offset
- var headers = allocatePartialBinary(bin, 0, secOff);
- // extra data
- var exData = null;
- var lastExDataOffset = bin.byteLength;
- // It may contain that both extra data and certificate data are available.
- // In this case the extra data is followed by the certificate data.
- if (securityEntry.size > 0) {
- lastExDataOffset = securityEntry.virtualAddress;
- }
- if (lastOffset < lastExDataOffset) {
- exData = allocatePartialBinary(bin, lastOffset, lastExDataOffset - lastOffset);
- }
- return new NtExecutable(headers, sections, exData);
- };
- /**
- * Returns whether the executable is for 32-bit architecture
- */
- NtExecutable.prototype.is32bit = function () {
- return this._nh.is32bit();
- };
- NtExecutable.prototype.getTotalHeaderSize = function () {
- return this._headers.byteLength;
- };
- Object.defineProperty(NtExecutable.prototype, "dosHeader", {
- get: function () {
- return this._dh;
- },
- enumerable: false,
- configurable: true
- });
- Object.defineProperty(NtExecutable.prototype, "newHeader", {
- get: function () {
- return this._nh;
- },
- enumerable: false,
- configurable: true
- });
- NtExecutable.prototype.getRawHeader = function () {
- return this._headers;
- };
- NtExecutable.prototype.getImageBase = function () {
- return this._nh.optionalHeader.imageBase;
- };
- NtExecutable.prototype.getFileAlignment = function () {
- return this._nh.optionalHeader.fileAlignment;
- };
- NtExecutable.prototype.getSectionAlignment = function () {
- return this._nh.optionalHeader.sectionAlignment;
- };
- /**
- * Return all sections. The returned array is sorted by raw address.
- */
- NtExecutable.prototype.getAllSections = function () {
- return this._sections;
- };
- /**
- * Return the section data from ImageDirectoryEntry enum value.
- * @note
- * The returned instance is equal to the value in {@link getAllSections}'s return value.
- */
- NtExecutable.prototype.getSectionByEntry = function (entry) {
- var dd = this._dda.get(entry);
- var r = this._sections
- .filter(function (sec) {
- var vaEnd = sec.info.virtualAddress + sec.info.virtualSize;
- return (dd.virtualAddress >= sec.info.virtualAddress &&
- dd.virtualAddress < vaEnd);
- })
- .shift();
- return r !== undefined ? r : null;
- };
- /**
- * Set the section data from ImageDirectoryEntry enum value.
- * If entry is found, then replaces the secion data. If not found, then adds the section data.
- *
- * NOTE: 'virtualAddress' and 'pointerToRawData' of section object is ignored
- * and calculated automatically. 'virtualSize' and 'sizeOfRawData' are used, but
- * if the 'section.data.byteLength' is larger than 'sizeOfRawData', then
- * these members are replaced.
- *
- * @param entry ImageDirectoryEntry enum value for the section
- * @param section the section data, or null to remove the section
- */
- NtExecutable.prototype.setSectionByEntry = function (entry, section) {
- var sec = section
- ? { data: section.data, info: section.info }
- : null;
- var dd = this._dda.get(entry);
- var hasEntry = dd.size > 0;
- if (!sec) {
- if (!hasEntry) {
- // no need to replace
- }
- else {
- // clear entry
- this._dda.set(entry, { size: 0, virtualAddress: 0 });
- var len = this._sections.length;
- for (var i = 0; i < len; ++i) {
- var sec_1 = this._sections[i];
- var vaStart = sec_1.info.virtualAddress;
- var vaLast = vaStart + sec_1.info.virtualSize;
- if (dd.virtualAddress >= vaStart &&
- dd.virtualAddress < vaLast) {
- this._sections.splice(i, 1);
- // section count changed
- this._nh.fileHeader.numberOfSections =
- this._sections.length;
- break;
- }
- }
- }
- }
- else {
- var rawSize = !sec.data ? 0 : sec.data.byteLength;
- var fileAlign = this._nh.optionalHeader.fileAlignment;
- var secAlign = this._nh.optionalHeader.sectionAlignment;
- var alignedFileSize = !sec.data ? 0 : roundUp(rawSize, fileAlign);
- var alignedSecSize = !sec.data
- ? 0
- : roundUp(sec.info.virtualSize, secAlign);
- if (sec.info.sizeOfRawData < alignedFileSize) {
- sec.info.sizeOfRawData = alignedFileSize;
- }
- else {
- alignedFileSize = sec.info.sizeOfRawData;
- }
- if (!hasEntry) {
- var virtAddr_1 = 0;
- var rawAddr_1 = roundUp(this._headers.byteLength, fileAlign);
- // get largest addresses
- this._sections.forEach(function (secExist) {
- if (secExist.info.pointerToRawData) {
- if (rawAddr_1 <= secExist.info.pointerToRawData) {
- rawAddr_1 =
- secExist.info.pointerToRawData +
- secExist.info.sizeOfRawData;
- }
- }
- if (virtAddr_1 <= secExist.info.virtualAddress) {
- virtAddr_1 =
- secExist.info.virtualAddress +
- secExist.info.virtualSize;
- }
- });
- if (!alignedFileSize) {
- rawAddr_1 = 0;
- }
- if (!virtAddr_1) {
- virtAddr_1 = this.newHeader.optionalHeader.baseOfCode;
- }
- virtAddr_1 = roundUp(virtAddr_1, secAlign);
- sec.info.pointerToRawData = rawAddr_1;
- sec.info.virtualAddress = virtAddr_1;
- // add entry
- this._dda.set(entry, {
- size: rawSize,
- virtualAddress: virtAddr_1,
- });
- this._sections.push(sec);
- // section count changed
- this._nh.fileHeader.numberOfSections = this._sections.length;
- // change image size
- this._nh.optionalHeader.sizeOfImage = roundUp(virtAddr_1 + alignedSecSize, this._nh.optionalHeader.sectionAlignment);
- }
- else {
- // replace entry
- this.replaceSectionImpl(dd.virtualAddress, sec.info, sec.data);
- }
- }
- };
- /**
- * Returns the extra data in the executable, or `null` if nothing.
- * You can rewrite the returned buffer without using `setExtraData` if
- * the size of the new data is equal to the old data.
- */
- NtExecutable.prototype.getExtraData = function () {
- return this._ex;
- };
- /**
- * Specifies the new extra data in the executable.
- * The specified buffer will be cloned and you can release it after calling this method.
- * @param bin buffer containing the new data
- * @note
- * The extra data will not be aligned by `NtExecutable`.
- */
- NtExecutable.prototype.setExtraData = function (bin) {
- if (bin === null) {
- this._ex = null;
- }
- else {
- this._ex = cloneToArrayBuffer(bin);
- }
- };
- /**
- * Generates the executable binary data.
- */
- NtExecutable.prototype.generate = function (paddingSize) {
- // calculate binary size
- var dh = this._dh;
- var nh = this._nh;
- var secOff = dh.newHeaderAddress + nh.getSectionHeaderOffset();
- var size = secOff;
- size += this._sections.length * ImageSectionHeaderArray.itemSize;
- var align = nh.optionalHeader.fileAlignment;
- size = roundUp(size, align);
- this._sections.forEach(function (sec) {
- if (!sec.info.pointerToRawData) {
- return;
- }
- var lastOff = sec.info.pointerToRawData + sec.info.sizeOfRawData;
- if (size < lastOff) {
- size = lastOff;
- size = roundUp(size, align);
- }
- });
- var lastPosition = size;
- if (this._ex !== null) {
- size += this._ex.byteLength;
- }
- if (typeof paddingSize === 'number') {
- size += paddingSize;
- }
- // make buffer
- var bin = new ArrayBuffer(size);
- var u8bin = new Uint8Array(bin);
- u8bin.set(new Uint8Array(this._headers, 0, secOff));
- // reset Security section offset (eliminate it)
- ImageDataDirectoryArray.from(bin, dh.newHeaderAddress + nh.getDataDirectoryOffset()).set(ImageDirectoryEntry.Certificate, {
- size: 0,
- virtualAddress: 0,
- });
- var secArray = ImageSectionHeaderArray.from(bin, this._sections.length, secOff);
- this._sections.forEach(function (sec, i) {
- if (!sec.data) {
- sec.info.pointerToRawData = 0;
- sec.info.sizeOfRawData = 0;
- }
- secArray.set(i, sec.info);
- if (!sec.data || !sec.info.pointerToRawData) {
- return;
- }
- u8bin.set(new Uint8Array(sec.data), sec.info.pointerToRawData);
- });
- if (this._ex !== null) {
- u8bin.set(new Uint8Array(this._ex), lastPosition);
- }
- // re-calc checksum
- if (nh.optionalHeader.checkSum !== 0) {
- calculateCheckSumForPE(bin, true);
- }
- return bin;
- };
- NtExecutable.prototype.rearrangeSections = function (rawAddressStart, rawDiff, virtualAddressStart, virtualDiff) {
- if (!rawDiff && !virtualDiff) {
- return;
- }
- var nh = this._nh;
- var secAlign = nh.optionalHeader.sectionAlignment;
- var dirs = this._dda;
- var len = this._sections.length;
- var lastVirtAddress = 0;
- for (var i = 0; i < len; ++i) {
- var sec = this._sections[i];
- var virtAddr = sec.info.virtualAddress;
- if (virtualDiff && virtAddr >= virtualAddressStart) {
- var iDir = dirs.findIndexByVirtualAddress(virtAddr);
- virtAddr += virtualDiff;
- if (iDir !== null) {
- dirs.set(iDir, {
- virtualAddress: virtAddr,
- size: sec.info.virtualSize,
- });
- }
- sec.info.virtualAddress = virtAddr;
- }
- var fileAddr = sec.info.pointerToRawData;
- if (rawDiff && fileAddr >= rawAddressStart) {
- sec.info.pointerToRawData = fileAddr + rawDiff;
- }
- lastVirtAddress = roundUp(sec.info.virtualAddress + sec.info.virtualSize, secAlign);
- }
- // fix image size from last virtual address
- nh.optionalHeader.sizeOfImage = lastVirtAddress;
- };
- // NOTE: info.virtualSize must be valid
- NtExecutable.prototype.replaceSectionImpl = function (virtualAddress, info, data) {
- var len = this._sections.length;
- for (var i = 0; i < len; ++i) {
- var s = this._sections[i];
- // console.log(`replaceSectionImpl: ${virtualAddress} <--> ${s.info.virtualAddress}`);
- if (s.info.virtualAddress === virtualAddress) {
- // console.log(` found`);
- var secAlign = this._nh.optionalHeader.sectionAlignment;
- var fileAddr = s.info.pointerToRawData;
- var oldFileAddr = fileAddr + s.info.sizeOfRawData;
- var oldVirtAddr = virtualAddress + roundUp(s.info.virtualSize, secAlign);
- s.info = cloneObject(info);
- s.info.virtualAddress = virtualAddress;
- s.info.pointerToRawData = fileAddr;
- s.data = data;
- // shift addresses
- var newFileAddr = fileAddr + info.sizeOfRawData;
- var newVirtAddr = virtualAddress + roundUp(info.virtualSize, secAlign);
- this.rearrangeSections(oldFileAddr, newFileAddr - oldFileAddr, oldVirtAddr, newVirtAddr - oldVirtAddr);
- // BLOCK: rewrite DataDirectory entry for specified virtualAddress
- {
- var dirs = this._dda;
- var iDir = dirs.findIndexByVirtualAddress(virtualAddress);
- if (iDir !== null) {
- dirs.set(iDir, {
- virtualAddress: virtualAddress,
- size: info.virtualSize,
- });
- }
- }
- break;
- }
- }
- };
- return NtExecutable;
- }());
- export default NtExecutable;
|