resedit-js
resedit-js is a library that manipulates resouces contained by Windows Executable files. All implementations are written in JavaScript (TypeScript), so there are no further restrictions for running environment.
This library is not tested well for modifying and/or signing executables yet. Please be careful with the emitted binaries.
To use in command line, consider using resedit-js-cli.
The demo page: resedit demo
Install
npm install resedit
Migrate from v1.x to v2.x
- If you use from ES module (.mjs) and load by using
import
, no need for migration. - If you use from ES module (.mjs) and load by using
require
(Node.js: via createRequire
), replace with import
statement: import * as ResEdit from 'resedit'
. - If you use from CommonJS module (.cjs) and load by using
require
, choose followings:
- Convert CommonJS module to ES module and replace
require
call with import
statement - Use
resedit/cjs
module and call load
function (see below)
- If you use TypeScript,
- For
"module": "ES2015"
or higher, no need for migration. - For
"module": "CommonJS"
, import resedit/cjs
and call load
function
The sample of using resedit/cjs
in CommonJS module is:
const { load } = require('resedit/cjs');
load().then((ResEdit) => {
});
Similarly, the sample of using resedit/cjs
in TypeScript CommonJS module is:
import { type ResEdit, load } from 'resedit/cjs';
load().then((RE: typeof ResEdit) => {
});
Supported formats
- Windows Executables (PE Format, such as
.exe
and .dll
), both 32-bit and 64-bit, are supported.
- Executables for 16-bit Windows is not supported.
.res
file is not supported now.- PNG-based icon data is supported on
require('resedit').Resource.IconGroupEntry
class.
Parsing signed executables
- Parsing signed executables (by using Authenticode or etc.) is not allowed by default and an exception will be thrown if
NtExecutable.from
receives a signed binary. - To parse signed,
{ ignoreCert: true }
object must be passed to the second argument of NtExecutable.from
. - Although the base executable data is signed,
NtExecutable.generate
will generate unsigned executable binary. If you want to re-sign it, you must use generate-function with signing (see below) or any other signing tool such as Microsoft signtool
.
Signing executables with resedit-js
resedit-js provides basic signing process generateExecutableWithSign
function, which is based on Authenticode specification and related RFCs.
To keep resedit-js generic library, the followings are required to use signing process.
- Encryption / calculating hash (digest) process (e.g. Node.js built-in
crypto
module)
- A private key data is implicitly required to encrypt data.
- DER-format certificate binary (such as
*.cer
file data or *.p7b
file data with DER-format), which is paired with the private key used by encryption process. - (optional) Generating timestamp data, especially communicating with TSA server (e.g. HTTP/HTTPS API)
These requirements are represented as SignerObject
. The caller of generateExecutableWithSign
function must implement this object to sign executables.
An example code is here: signTest.mjs
Note that resedit-js only provides basic signing process, and provides as beta version. For example adding more attributes/informations to certificates are not supported now.
Some digest algorithms, such as SHA3 algorithms, might not be supported by current Windows.
Notes
- It is not strongly recommended that the destination executable file is equal to the source executable file (which is not an intermediate data).
Examples
For more APIs, please see dist
directory of the package. And, some test codes may help you for usages.
import * as PELibrary from 'pe-library';
import * as ResEdit from 'resedit';
import * as fs from 'fs';
const data = fs.readFileSync('MyApp.exe');
const exe = PELibrary.NtExecutable.from(data);
const res = PELibrary.NtExecutableResource.from(exe);
const iconFile = ResEdit.Data.IconFile.from(fs.readFileSync('MyIcon.ico'));
ResEdit.Resource.IconGroupEntry.replaceIconsForResource(
res.entries,
101,
1033,
iconFile.icons.map((item) => item.data)
);
const viList = ResEdit.Resource.VersionInfo.fromEntries(res.entries);
const vi = viList[0];
vi.setFileVersion(1, 0, 0, 0, 1033);
vi.setStringValues(
{ lang: 1033, codepage: 1200 },
{
FileDescription: 'My application',
ProductName: 'My product',
}
);
vi.outputToResourceEntries(res.entries);
res.outputResource(exe);
const newBinary = exe.generate();
fs.writeFileSync('MyApp_modified.exe', Buffer.from(newBinary));
License
MIT License