• Stars
    star
    202
  • Rank 193,691 (Top 4 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created over 5 years ago
  • Updated 10 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Protect asar archive files from extraction

asarmor

CLI tool and library that can patch and encrypt your asar file to protect it from extraction (e.g by executing asar extract). This is not bulletproof, but can be useful as a first level of protection.

installation

Install as local library: $ npm install --save-dev asarmor

Install as global CLI app: $ npm install -g asarmor

usage

CLI

$ asarmor --help

Usage: asarmor [options]

Options:
  -V, --version                  output the version number
  -a, --archive <archive>        input asar file (required)
  -o, --output <output>          output asar file (required)
  -b, --backup                   create backup
  -r, --restore                  restore backup
  -bl, --bloat [gigabytes]       add huge random files to disk on extraction attempt
  -t, --trashify [junkfiles...]  add fake files to the archive
  -e, --encrypt <src>            encrypt file contents
  -ek, --key <file path>         key file to use for encryption
  -h, --help                     display help for command

Examples:
  $ asarmor -a app.asar -o asarmor.asar --bloat 1000
  $ asarmor -a app.asar -o asarmor.asar --trashify bee-movie.txt foo.js bar.ts
  $ asarmor -a app.asar -o asarmor.asar --trashify --backup
  $ asarmor -a app.asar --restore

library

const asarmor = require('asarmor');
const { encrypt } = require('asarmor/encryption');

(async () => {
  // Encrypt the contents of the asar archive.
  await encrypt({
    src: './dist',     // source or transpiled code to encrypt
    dst: './app.asar', // target asar file
  });

  // Read & parse the (optionally encrypted) asar file.
  // This can take a while depending on the size of your file.
  const archive = await asarmor.open('app.asar');

  // Create a backup, which can be restored at any point in time through CLI or code.
  await archive.createBackup({backupPath: '~/Documents/backups/app.asar.backup'});

  // Apply customized trash patch.
  // The trash patch by itself will make sure `asar extract` fails.
  archive.patch(asarmor.createTrashPatch({
    filenames: ['foo', 'bar'],
    beforeWrite: (filename) => {
      const extensions = ['js', 'ts', 'tsx', 'txt'];
      const extension = extensions[Math.floor(Math.random() * extensions.length)];
      return filename + '.' + extension;
    }
  }));

  // Apply customized bloat patch.
  // The bloat patch by itself will write randomness to disk on extraction attempt.
  archive.patch(asarmor.createBloatPatch(50)); // adds 50 GB of bloat in total

  // Write changes back to disk.
  const outputPath = await archive.write('app.asar');
  console.log('successfully wrote changes to ' + outputPath);
})();

advanced

const asarmor = require('asarmor');

(async () => {
  const archive = await asarmor.open('app.asar');

  // Apply a fully customized patch.
  // Play around with the different values to see what works best for you.
  archive.patch({
    header: {
      files: {
        'foo.js': {offset: 0, size: -999},
        'bar.js': {offset: -123, size: 1337},
      }
    },
  });

  // Write result back to file.
  await archive.write('protected.asar');
})();

electron-builder

You can easily include asarmor in your packaging process using an afterPack hook:

const asarmor = require('asarmor');
const { join } = require("path");

exports.default = async ({ appOutDir, packager }) => {
  try {
    const asarPath = join(packager.getResourcesDir(appOutDir), 'app.asar');
    console.log(`asarmor applying patches to ${asarPath}`);
    const archive = await asarmor.open(asarPath);
    archive.patch(); // apply default patches
    await archive.write(asarPath);
  } catch (err) {
    console.error(err);
  }
};

encryption

Asarmor now finally supports file content encryption. No Electron recompilation required! Huge thanks to toyobayashi's wonderful electron-asar-encrypt-demo for making this possible. I won't be going into too many details on how this works exactly. If you're interested in the details I higly recommend you to check out the electron-asar-encrypt-demo repository.

There's a few more steps involved to make this work, though. See example/electron if you'd like to skip ahead to the code.

Steps:

  1. Update afterPack.js:
exports.default = async ({ appOutDir, packager }) => {
  try {
+   const asarPath = join(packager.getResourcesDir(appOutDir), 'app.asar');
+   
+   // encrypt file contents first
+   const src = join(packager.info.projectDir, 'release', 'app');
+   const dst = asarPath;
+   console.log(`asarmor encrypting contents of ${src} to ${dst}`);
+   await encrypt({
+     // path to your source code (e.g. src, build or dist)
+     src,
+     // destination asar file to write to
+     dst,
+     // path to the encryption key file; asarmor should generate a new one every time it's installed as a dev-dependency.
+     keyFilePath: join(__dirname, '..', 'node_modules', 'asarmor', 'src', 'encryption', 'key.txt'),
+   });
+
+   // then patch the header
-   const asarPath = join(packager.getResourcesDir(appOutDir), 'app.asar');
    console.log(`asarmor applying patches to ${asarPath}`);
    const archive = await asarmor.open(asarPath);
    archive.patch(); // apply default patches
    await archive.write(asarPath);
  } catch (err) {
    console.error(err);
  }
};
  1. Create beforePack.js:
const { join } = require('path');
const { copyFile } = require('fs/promises');

exports.default = async (context) => {
  try {
    console.log('copying native dependencies');

    const release = join(__dirname, '..', 'node_modules', 'asarmor', 'Release');

    // copy main.node from asarmor to our dist/build/release folder; this will become the entrypoint later on.
    await copyFile(
      join(release, 'main.node'),
      join(
        context.packager.info.projectDir,
        'release',
        'app',
        'dist',
        'main',
        'main.node'
      )
    );

    // copy renderer.node to our dist/build/release folder; the render process will be bootstrapped from the main process later on.
    await copyFile(
      join(release, 'renderer.node'),
      join(
        context.packager.info.projectDir,
        'release',
        'app',
        'dist',
        'renderer',
        'renderer.node'
      )
    );
  } catch (err) {
    console.error(err);
  }
};

Don't forget to update package.json as well:

"afterPack": "./afterPack.js",
+ "beforePack": "./beforePack.js",
  1. Update your project's package.json entrypoint:
+ "main": "./dist/main/main.node",
- "main": "./dist/main/main.js",
  1. Load any hooks at the start of the main process file (optional):
// main.ts
import { hookNodeModulesAsar } from 'asarmor/src/encryption/hooks';

// load hooks at the start of the file
hookNodeModulesAsar(); // enables resolution of non-encrypted dependencies from node_modules.asar
  1. Update your BrowserWindow.webPreferences configuration settings:
const mainWindow = new BrowserWindow({
    // ...
    webPreferences: {
      nodeIntegration: true,   // MUST BE ENABLED
      contextIsolation: false, // MUST BE DISABLED
    },
  });
  1. Bootstrap the render process:
await mainWindow.webContents.executeJavaScript(`!function () {
  require('../renderer/renderer.node');
  require('../renderer/renderer.js');
}()`);
  1. Export a default function in the main process, accepting the decryption key as a paramater.
module.exports = function bootstrap(k: Uint8Array) {
  // sanity check
  if (!Array.isArray(k) || k.length === 0) {
    throw new Error('Failed to bootstrap application.');
  }

  // key should be valid at this point, but you can access it here to perform additional checks.
  console.log('decryption key: ' + k);

  // start the app
  if (!process.env.ELECTRON_RUN_AS_NODE) {
    app
      .whenReady()
      .then(() => {
        createWindow();
        app.on('activate', () => {
          if (mainWindow === null) createWindow();
        });
      })
      .catch(console.log);
  } else {
    console.error('failed to bootstrap main process');
  }
};

examples

See examples for detailed code examples.

FAQ

Do protections affect my (electron) app performance?

It depends. If you have a huge archive and applied encryption, then yes. Otherwise, electron should still be able read your asar file at the same speed as if nothing changed. The same should be true for other frameworks that utilise the asar format (unless the implementation differs drastically for some reason, which is out of my control).

support

Found a bug or have a question? Open an issue if it doesn't exist yet. Pull Requests are welcome, but please open an issue first if you're adding major changes!

related projects

More Repositories

1

burp-awesome-tls

Fixes Burp Suite's poor TLS stack. Bypass WAF, spoof any browser.
Java
827
star
2

stremio-streaming-server

Small docker image to run Stremio's streaming server standalone
Shell
102
star
3

aternos-discord-bot

Discord bot to start & stop a Minecraft server automatically
Go
78
star
4

Chainforger

Proxy scraper for proxychains
Python
42
star
5

stremio-addons

All of my stremio addons in one monorepo
TypeScript
29
star
6

gotcha

High level HTTP client with a got-like API
Go
24
star
7

pms-android

Unoffical PimpMyStremio android app
HTML
22
star
8

ja3rp

Reverse proxy server to filter traffic based on JA3 fingerprint/hash
Go
22
star
9

PyDeobfuscator

Deobfuscate obfuscated python files (WIP)
Python
19
star
10

ml-crypto-trading-bot

Experimental cryptocurrency trading bot using Machine Learning and Rust
Rust
19
star
11

aternos-api

Unofficial aternos.org API/library to start & stop servers programmatically
Go
18
star
12

stremio-discord

Local addon that shows the movie or series you're watching in Discord
JavaScript
18
star
13

voltra

Cross-market volatile cryptocurrency trading bot
Go
16
star
14

cronet-rs

Chromium Network Stack (cronet) bindings
Rust
15
star
15

stremio-addon-sdk-rs

Stremio addon SDK using rust, stremio-core and hyper
Rust
14
star
16

CronetSharp

C# library to interact with the Chromium Network Stack through bindings
C#
14
star
17

cookie-api

Cookie API to generate PX/Akamai cookie.
JavaScript
14
star
18

enhanced_future_builder

Small FutureBuilder wrapper to improve readabiltity
Dart
11
star
19

PreMiD-rs

Alternative PreMiD backend but without the bloat
Rust
10
star
20

DecentHttpClient

Highly configurable HTTP client
C#
8
star
21

electron-typescript-react-sass

Boilerplate for an Electron application with TypeScript, React and SASS
JavaScript
6
star
22

cronet-binaries

Cross-platform binaries for cronet (Chromium Network Stack)
C++
6
star
23

stremio-podcasts-addon

[MOVED] #1 Pocasts Addon for Stremio - High Quality streams from multiple sources
JavaScript
3
star
24

deeplx-node

Port of deeplx: an unofficial package to translate text using DeepL
TypeScript
2
star
25

GOI-cheats

Cheats for Getting Over It with Bennett Foddy
C#
2
star
26

stub-and-builder-example

Visual Basic
2
star
27

pimpmystremio-docker

Docker image for PimpMyStremio (Small ~ Alpine based)
Dockerfile
2
star
28

stremio-horriblesubs-addon

[MOVED] Stremio addon for streaming anime torrents from horriblesubs.info
JavaScript
2
star
29

whenwas

Calculate how many years, months, days, hours, minutes, seconds ago a date is
Dart
2
star
30

paulo

Bot for the r/StremioAddons discord
TypeScript
2
star
31

sshtunnel-env

configurable SSH tunnel using environment variables
Dockerfile
2
star
32

stremio-rarbg-torrents

[MOVED] Watch movies & Series from RARBG on Stremio
JavaScript
1
star
33

stremio-1337x-torrents

[MOVED] Torrents from 1337x for Stremio
JavaScript
1
star
34

flagstruct

Write golang command line flags within your struct like a pro
Go
1
star
35

EasyScraper

Scrape websites through a GUI
PHP
1
star
36

mtgaredeemer

Automatic code redeemer for MTG Arena
C#
1
star
37

yamldb

Simple disk-backed key-value store for YAML files
Go
1
star
38

jackett

Jackett API Implementation.
TypeScript
1
star
39

EcoBot

Plant trees by doing nothing
Python
1
star
40

FamePHP

Facebook messenger bot framework
PHP
1
star
41

VPNbook-Password-Retriever

Retrieve username & password for VPNbook authentication
C#
1
star
42

CanvasPaint

Userscript to change course cover images in Canvas LMS (for students)
JavaScript
1
star
43

advent-of-code-2022

My advent of code solutions in Rust (2022)
Rust
1
star
44

stremio-dlive-addon

[MOVED] Stremio addon to view livestreams from dlive.tv
JavaScript
1
star
45

native-to-big

Convert JS native math expressions to Big objects from the big.js library
TypeScript
1
star
46

stremio-consumet-addon

Stremio addon that provides anime and asian movies from Consumet
TypeScript
1
star
47

CsBugmenot

Library to scrape username and passwords for various sites from BugMeNot
C#
1
star
48

sleeyax

About me
1
star
49

EnhancedDiscord-Plugins

My EnhancedDiscord plugins
JavaScript
1
star
50

brainfucked

Totally overkill Brainfuck interpreter, parser & tokenizer written in Dart
Dart
1
star
51

advent-of-code-2023

My advent of code solutions in Rust (2023)
Rust
1
star