Disclaimer: This article is for educational and research purposes only. Modifying commercial software may violate licenses or laws. Use these techniques responsibly, the goal here is to understand how Electron apps are built, debug issues, or customize behavior in a safe context.
I’ve been diving into Electron app reverse engineering lately, and I’m honestly surprised by how straightforward the process is. While most Electron apps are essentially glorified web frontends making API calls (with critical logic enforced server-side), there’s still plenty of value in cracking them open. Whether you’re trying to bypass license checks, modify UI behavior, or just satisfying curiosity, unpacking Electron apps is surprisingly accessible.
Locating the ASAR archive
On Windows, resources usually live in:
C:\Users\\<user>\\AppData\\Local\\<app>\\resources\\
On macOS, they’re inside the app bundle:
<App>.app/Contents/Resources/
Some apps also declare integrity checks in their Info.plist. On macOS, you can inspect them with PlistBuddy:
/usr/libexec/PlistBuddy -c "Print :ElectronAsarIntegrity" Termius.app/Contents/Info.plist
Dict {
Resources/app.asar = Dict {
hash = 21378a34c05cba766f2b87b851626e964ffe6c7ff8590bffc8543863d670c8aa
algorithm = SHA256
}
}
Not all apps include this, some skip Info.plist integrity data and rely only on runtime checks.
Extracting ASAR files
The heart of any Electron app is the .asar file - Atom Shell Archive format that bundles all the JavaScript, HTML, CSS, and assets into a single archive. Think of it as a specialized ZIP file optimized for Electron.
Extraction is simple with the asar CLI:
# Install globally if you haven't already
npm install -g asar
# Extract everything
asar extract app.asar ./extracted_app
# Or grab specific files
asar extract-file app.asar main.js
I prefer using npx to avoid global installs cluttering my system:
npx asar extract app.asar ./extracted_app
Alternative: use Azar7z with 7-Zip if you prefer GUI tools. Python users might also like asar-tools.
Once unpacked, you’ll see a familiar web-app structure with minified JavaScript, HTML templates, and assets. The entry point is often main.js or index.js.
Making sense of minified code
The extracted JavaScript is usually minified and obfuscated. A quick prettify step makes it much more readable:
npx prettier --write extracted_app/main.js
After prettifying, you can actually understand what the app is doing. I often find interesting API endpoints, authentication logic, or even hardcoded secrets that developers thought were “hidden” in the binary.
Debugging the app
There are two types of debugging. One is to open remote debug, start the electron application in the command line, add the parameter –remote-debugging-port=xxxx to open the remote debugging port, and then attach it to the chrome chrome://inspect/ page.
Another way is to modify the code to open the console. To open the console in electron, you must use BrowserWindow to open it. BrowserWindow().webContents.openDevTools() So the global search for BrowserWindow( to find the code for creating the window, and locate it here:
yt.linux() && (p.icon = Ch), yt.macOS() && ((p.titleBarStyle = “customButtonsOnHover”), this.type === “primary” && (p.trafficLightPosition = { x: 9, y: 17 }), this.type === “primary” && ((p.show = !1), (p.vibancy = “hud”), (p.backgroundColor = “#00000000”), (p.visualEffectState = “active”))), (this.browserWindow = new q.BrowserWindow(p)), PE.enable(this.browserWindow.webContents), // Add: this.browserWindow.webContents.openDevTools(), (this.id = this.browserWindow.id), this.browserWindow.on(“page-title-updated”, (v) => v.preventDefault() ), this.browserWindow.webContents.setWindowOpenHandler(
Repacking the ASAR archive
Once you’ve made your modifications, you can minify (here using terser) the code again if you want to maintain the original file sizes:
npx terser extracted_app/main.js -o extracted_app/main.js --compress --mangle
And then you can repack the ASAR archive:
asar pack extracted_app app.asar
For apps with native modules, I sometimes need to leave certain files unpacked:
asar pack extracted_app app.asar --unpack "*.node"
This creates both app.asar and app.asar.unpacked directory containing the native binaries.
Handling integrity checks part 1
This is where things get tricky. Modern Electron apps often implement integrity checks to prevent exactly what we’re doing. When I try to repack and run a modified app. If you run a modified app and see:
[40181:0824/143525.740030:FATAL:asar_util.cc(164)] Integrity check failed for asar archive (21378a34c05cba766f2b87b851626e964ffe6c7ff8590bffc8543863d670c8aa vs ec96f212196f42e4f5261ad3deb01004f27c81bc53e4bd602ddaf2c5e427d795)
zsh: trace trap Termius.app/Contents/MacOS/Termius
That means the archive’s hash doesn’t match. The simplest fix: update the Info.plist with the new hash:
The good news? Electron helpfully tells us exactly what the new hash should be. I can update the Info.plist file with the correct hash:
/usr/libexec/PlistBuddy -c "Set :ElectronAsarIntegrity:Resources/app.asar:hash ec96f212196f42e4f5261ad3deb01004f27c81bc53e4bd602ddaf2c5e427d795" Termius.app/Contents/Info.plist
You can also calculate the hash yourself. Electron hashes only the ASAR header, not the full file:
const fs = require('fs');
const asar = require('@electron/asar');
const crypto = require('crypto');
function calculateAsarHash(filePath) {
console.log(`[+] Calculating hash for ${filePath}`);
try {
const rawHeader = asar.getRawHeader(filePath);
const hash = crypto.createHash('sha256')
.update(rawHeader.headerString)
.digest('hex');
console.log(`[+] New ASAR hash: ${hash}`);
return hash;
} catch (error) {
console.error(`[!] Error: ${error.message}`);
return null;
}
}
// Usage
const newHash = calculateAsarHash('./app.asar');
Note: some apps also implement runtime integrity checks in JavaScript code. In such cases, patching Info.plist isn’t enough, you’ll need to inspect the verification logic in the unpacked source.
Handling integrity checks part 2
The Electron project provides the tool Fuses to enforce integrity checking on executable script components.
For a subset of Electron functionality it makes sense to disable certain features for an entire application. Fuses are the solution to this problem, at a high level they are “magic bits” in the Electron binary that can be flipped when packaging your Electron app to enable / disable certain features / restrictions. Source: Electron Documentation
These fuses are not on by default, and must be explicitly enabled by the developer.
Here are two exemples bellow:
npx @electron/fuses read --app /Applications/Notion.app/Contents/MacOS/Notion
Analyzing app: Notion
Fuse Version: v1
RunAsNode is Disabled
EnableCookieEncryption is Enabled
EnableNodeOptionsEnvironmentVariable is Disabled
EnableNodeCliInspectArguments is Disabled
EnableEmbeddedAsarIntegrityValidation is Enabled
OnlyLoadAppFromAsar is Enabled
LoadBrowserProcessSpecificV8Snapshot is Disabled
GrantFileProtocolExtraPrivileges is Enabled
npx @electron/fuses read --app /Applications/Termius.app/Contents/MacOS/Termius
Analyzing app: Termius
Fuse Version: v1
RunAsNode is Enabled
EnableCookieEncryption is Disabled
EnableNodeOptionsEnvironmentVariable is Enabled
EnableNodeCliInspectArguments is Enabled
EnableEmbeddedAsarIntegrityValidation is Disabled
OnlyLoadAppFromAsar is Disabled
LoadBrowserProcessSpecificV8Snapshot is Disabled
Let’s take a closer look at the ASAR-related fuses:
EnableEmbeddedAsarIntegrityValidation: Electron enforces integrity checks (hash/signature) on files loaded from withinapp.asar.OnlyLoadAppFromAsar: Electron restricts loading application files exclusively from the.asararchive, preventing execution of unpacked JavaScript files from disk.
With these settings, Notion applies stricter controls by requiring code to remain inside the .asar and enforcing integrity validation, while Termius is more permissive and allows running unpacked files.
Combined with OS-level code signing, these mechanisms help ensure the application executes only the code originally distributed by the developer.
If you want to read more about abuse of Electron applications without integrity checking, you can check MITRE ATT&CK technique entry T1218.015. I also came across Loki a “command and control (C2) framework written in Node.js, built to script-jack vulnerable Electron apps”
V8 heap snapshot vulnerabilities
Some apps use V8 heap snapshots to speed up startup times. Here is how it works:
The JavaScript specification includes a lot of built-in functionality, from math functions to a full-featured regular expression engine. Every newly-created V8 context has these functions available from the start. […] Fortunately, V8 uses a shortcut to speed things up: just like thawing a frozen pizza for a quick dinner, we deserialize a previously-prepared snapshot directly into the heap to get an initialized context. On a regular desktop computer, this can bring the time to create a context from 40 ms down to less than 2 ms. On an average mobile phone, this could mean a difference between 270 ms and 10 ms. Source: V8 Blog
Electron apps, like other Chromium-based software, use V8 heap snapshots to accelerate startup by restoring pre-initialized JavaScript engine state. These snapshots are loaded into V8 isolates for main, preload, and renderer processes. Although heap snapshots aren’t directly executable, they can be manipulated to alter built-in JavaScript functions, potentially enabling code execution if an attacker finds a suitable gadget.
Historically, Electron’s integrity checks (EnableEmbeddedAsarIntegrityValidation, OnlyLoadAppFromAsar) overlooked heap snapshots, leaving them unprotected. Chromium itself also skips integrity validation for these files. This oversight means that if an app is installed in a user-writable location (like %AppData%\Local on Windows or /Applications on macOS), an attacker with write access can inject a backdoor via a tampered heap snapshot—bypassing OS code-signing and integrity checks.
This issue is tracked as CVE-2025-55305. For a practical demonstration and deeper technical details, see the Trail of Bits post: Subverting Code Integrity Checks to Locally Backdoor Signal, 1Password, Slack, and More.
Have fun :p