https://github.com/jet2jet/resedit-js
JS library editing Windows Resource data
https://github.com/jet2jet/resedit-js
authenticode nodejs pe-executable resource windows winres
Last synced: 2 months ago
JSON representation
JS library editing Windows Resource data
- Host: GitHub
- URL: https://github.com/jet2jet/resedit-js
- Owner: jet2jet
- License: mit
- Created: 2018-12-09T08:00:16.000Z (over 7 years ago)
- Default Branch: main
- Last Pushed: 2025-11-23T02:27:48.000Z (5 months ago)
- Last Synced: 2025-11-23T04:17:44.595Z (5 months ago)
- Topics: authenticode, nodejs, pe-executable, resource, windows, winres
- Language: TypeScript
- Homepage:
- Size: 2.38 MB
- Stars: 43
- Watchers: 1
- Forks: 4
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
[](https://www.npmjs.com/package/resedit)
[](https://www.npmjs.com/package/resedit)
[](https://github.com/jet2jet/resedit-js)
# resedit-js
resedit-js is a library that manipulates resouces contained by Windows Executable files. All implementations are written in JavaScript (TypeScript), without using any native binaries. resedit-js works in both Node.js environment and Web environment.
This library is not tested well for modifying and/or signing executables yet. Please be careful with the emitted binaries.
To use from command line, [resedit-js-cli](https://www.npmjs.com/package/resedit-cli) is suitable.
The demo page: [resedit demo](https://www.pg-fl.jp/program/resedit/index.en.htm)
- [Install](#install)
- [Usage](#usage)
- [Supported formats](#supported-formats)
- [Parsing signed executables](#parsing-signed-executables)
- [Signing executables with resedit-js](#signing-executables-with-resedit-js)
- [Notes](#notes)
- [Examples](#examples)
- [License](#license)
## Install
```
npm install resedit
```
## Usage
- For detailed code using `ResEdit` namespace, see [Examples](#examples).
- For more APIs, please see `dist` directory of the package. And, [some test codes](./src/test) may help you for usages.
For ESM (ES Modules):
```js
//import * as PELibrary from 'pe-library';
import * as ResEdit from 'resedit';
```
For CJS (CommonJS modules including classic Node.js scripts):
```js
//const PELibrary = require('pe-library');
const ResEdit = require('resedit');
```
> If your Node.js version is prior to v20.19.5, `require` cannot be used for `'resedit'` package and you must use `'resedit/cjs'` package as described in [Migrate from v1.x to v2.x](#migrate-from-v1x-to-v2x) (collapsed in `Migration from prior resedit versions` area).
Migration from prior resedit versions
### Migrate from v2.x to v3.x
This major version up includes 'Change requirements of Node.js version (_v20.19.5 or later_ is required)' and 'Remove TypeScript enum usage' only. If your code base (project) meets following conditions, you can safely upgrade to v3.x (without no more actions):
- You uses resedit v2.x
- If you use v1.x, please follow [Migrate from v1.x to v2.x](#migrate-from-v1x-to-v2x).
- The code base already uses Node.js v20.19.5 or later (including v22, v24, or higher)
- Node.js v18 or earlier is already end-of-life. Upgrade to v22 or v24 is recommended.
- For using Node.js v20, upgrading to v20.19.5 or later would not be difficult.
- Following enum's members are not used or used only as values: `VersionFileFlags`, `VersionFileOS`, `VersionFileDriverSubtype`, `VersionFileFontSubtype`, and `VersionFileType`
- If you use the members as types (e.g. `let x: VersionFileFlags.Debug`), rewrite with using `typeof` (e.g, `let x: typeof VersionFileFlags.Debug`).
### 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:
- If you are using under Node.js environment, _only_ update Node.js to v20.19.0 or later
- Starting from Node.js v20.19.0, you can `require` synchronous ES module. resedit does not use top-level `await`, so you can write `require('resedit')` from CommonJS module.
- 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:
```js
const { load } = require('resedit/cjs');
load().then((ResEdit) => {
// ResEdit will be the namespace object of resedit library
// (for example ResEdit.Data.IconFile is available)
});
```
Similarly, the sample of using `resedit/cjs` in TypeScript CommonJS module is:
```ts
// You can use `ResEdit` for type references (cannot be used for value references)
import { type ResEdit, load } from 'resedit/cjs';
load().then((RE: typeof ResEdit) => {
// RE will be the namespace object of resedit library
// (for example RE.Data.IconFile is available)
});
```
## 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](https://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/authenticode_pe.docx) 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`](./src/main/sign/SignerObject.ts). The caller of `generateExecutableWithSign` function must implement this object to sign executables.
An example code is here: [signTest.mjs](./examples/sign/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
```js
import * as PELibrary from 'pe-library';
import * as ResEdit from 'resedit';
import * as fs from 'fs';
// load and parse data
const data = fs.readFileSync('MyApp.exe');
// (the Node.js Buffer instance can be specified directly to NtExecutable.from)
const exe = PELibrary.NtExecutable.from(data);
const res = PELibrary.NtExecutableResource.from(exe);
// rewrite resources
// - You can use helper classes as followings:
// - ResEdit.Resource.IconGroupEntry: access icon resource data
// - ResEdit.Resource.StringTable: access string resource data
// - ResEdit.Resource.VersionInfo: access version info data
// -- replace icons
// load icon data from file
// (you can use ResEdit.Data.IconFile to parse icon data)
const iconFile = ResEdit.Data.IconFile.from(fs.readFileSync('MyIcon.ico'));
ResEdit.Resource.IconGroupEntry.replaceIconsForResource(
// destEntries
res.entries,
// iconGroupID
// - This ID is originally defined in base executable file
// (the ID list can be retrieved by `ResEdit.Resource.IconGroupEntry.fromEntries(res.entries).map((entry) => entry.id)`)
101,
// lang ('lang: 1033' means 'en-US')
1033,
// icons (map IconFileItem to IconItem/RawIconItem)
iconFile.icons.map((item) => item.data)
);
// -- replace version
const viList = ResEdit.Resource.VersionInfo.fromEntries(res.entries);
const vi = viList[0];
// setFileVersion will set `vi.fixedInfo.fileVersionMS`/`fileVersionLS` and 'FileVersion' string value
// ('1033' means 'en-US')
vi.setFileVersion(1, 0, 0, 0, 1033);
// ('lang: 1033' means 'en-US', 'codepage: 1200' is the default codepage)
vi.setStringValues(
{ lang: 1033, codepage: 1200 },
{
FileDescription: 'My application',
ProductName: 'My product',
}
);
vi.outputToResourceEntries(res.entries);
// write to another binary
res.outputResource(exe);
const newBinary = exe.generate();
fs.writeFileSync('MyApp_modified.exe', Buffer.from(newBinary));
```
## License
[MIT License](./LICENSE)