From b08a5767efc1c861c64611cad4525fb61526e91d Mon Sep 17 00:00:00 2001 From: Karmaz95 Date: Fri, 19 Jul 2024 18:42:49 +0200 Subject: [PATCH] Code related to Bypassing Electron Integrity article --- .../calculate_electron_asar_integrity_hash.js | 36 ++++++ .../calculate_electron_asar_integrity_hash.py | 30 +++++ .../calculate_electron_asar_integrity_hash.sh | 81 ++++++++++++ .../custom/electron_patcher.py | 116 ++++++++++++++++++ 4 files changed, 263 insertions(+) create mode 100644 App Bundle Extension/custom/calculate_electron_asar_integrity_hash.js create mode 100644 App Bundle Extension/custom/calculate_electron_asar_integrity_hash.py create mode 100644 App Bundle Extension/custom/calculate_electron_asar_integrity_hash.sh create mode 100644 App Bundle Extension/custom/electron_patcher.py diff --git a/App Bundle Extension/custom/calculate_electron_asar_integrity_hash.js b/App Bundle Extension/custom/calculate_electron_asar_integrity_hash.js new file mode 100644 index 0000000..337f2b5 --- /dev/null +++ b/App Bundle Extension/custom/calculate_electron_asar_integrity_hash.js @@ -0,0 +1,36 @@ +// https://medium.com/@karol-mazurek/cracking-macos-apps-39575dd672e0 +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var crypto = require("crypto"); +var asar = require("asar"); +var fs = require("fs"); +// Function to generate the integrity hash +var generateAsarIntegrity = function (asarPath) { + var headerString = asar.getRawHeader(asarPath).headerString; + var hash = crypto + .createHash('sha256') + .update(headerString) + .digest('hex'); + return { + algorithm: 'SHA256', + hash: hash + }; +}; +// Main script execution +var main = function () { + if (process.argv.length !== 3) { + console.error('Usage: node calculate_hash.ts '); + process.exit(1); + } + var asarPath = process.argv[2]; + // Check if the file exists + if (!fs.existsSync(asarPath)) { + console.error("File not found: ".concat(asarPath)); + process.exit(1); + } + var result = generateAsarIntegrity(asarPath); + console.log("Algorithm: ".concat(result.algorithm)); + console.log("Hash: ".concat(result.hash)); +}; +// Run the script +main() \ No newline at end of file diff --git a/App Bundle Extension/custom/calculate_electron_asar_integrity_hash.py b/App Bundle Extension/custom/calculate_electron_asar_integrity_hash.py new file mode 100644 index 0000000..46b36fe --- /dev/null +++ b/App Bundle Extension/custom/calculate_electron_asar_integrity_hash.py @@ -0,0 +1,30 @@ +import sys +import struct +import hashlib + +def getASARHeaderSize(file_path): + with open(file_path, 'rb') as f: + asar_header = f.read(16) + asar_header_size_bytes = asar_header[12:16] + header_size = struct.unpack(' $TS_FILE +import * as crypto from 'crypto'; +import * as asar from 'asar'; +import * as fs from 'fs'; + +// Function to generate the integrity hash +const generateAsarIntegrity = (asarPath: string) => { + const headerString = asar.getRawHeader(asarPath).headerString; + const hash = crypto + .createHash('sha256') + .update(headerString) + .digest('hex'); + + return { + algorithm: 'SHA256' as const, + hash + }; +}; + +// Main script execution +const main = () => { + if (process.argv.length !== 3) { + console.error('Usage: node calculate_hash.ts '); + process.exit(1); + } + + const asarPath = process.argv[2]; + + // Check if the file exists + if (!fs.existsSync(asarPath)) { + console.error(\`File not found: \${asarPath}\`); + process.exit(1); + } + + const result = generateAsarIntegrity(asarPath); + + console.log(\`Algorithm: \${result.algorithm}\`); + console.log(\`Hash: \${result.hash}\`); +}; + +// Run the script +main(); +EOL + +# Step 6: Compile TypeScript to JavaScript +echo "Compiling TypeScript to JavaScript..." +tsc $TS_FILE + +# Step 7: Run the JavaScript file +echo "Running the script with ASAR path: $ASAR_PATH" +node calculate_hash.js "$ASAR_PATH" + +echo "Done." \ No newline at end of file diff --git a/App Bundle Extension/custom/electron_patcher.py b/App Bundle Extension/custom/electron_patcher.py new file mode 100644 index 0000000..000f026 --- /dev/null +++ b/App Bundle Extension/custom/electron_patcher.py @@ -0,0 +1,116 @@ +import sys +import struct +import hashlib +import argparse +import os + +class ASARCalculator: + def __init__(self, file_path): + self.file_path = file_path + self.asar_header_size = self.getASARHeaderSize() + self.asar_header_bytes = self.readASARHeader() + self.asar_header_hash = self.calcASARHeaderHash() + + def getASARHeaderSize(self): + with open(self.file_path, 'rb') as f: + asar_header = f.read(16) + asar_header_size_bytes = asar_header[12:16] + header_size = struct.unpack(' {output_path}") + + if status_code == 0: + print(f"Dumped entitlements from {app_path} to {output_path}") + + else: + print(f"Failed to dump entitlements from {app_path} to {output_path}. Error code: {status_code}") + + def checkIfElectronAsarIntegrityIsUsed(self, app_path): + status_code = os.system(f"plutil -p {app_path}/Contents/Info.plist | grep -q ElectronAsarIntegrity") + if status_code == 0: + return True + else: + return False + + def packASAR(self, input_path, app_path): + '''Packs {input_path} directory to {output_path} asar file. + Check if ElectronAsarIntegrity is used in Info.plist, and if so, calculate hash and replace it. + Codesign the + ''' + output_path = os.path.join(app_path, "Contents/Resources/app.asar") + info_plist_path = os.path.join(app_path, "Contents/Info.plist") + + status_code = os.system(f"npx asar pack {input_path} {output_path}") + if status_code == 0: + print(f"Packed {input_path} into {output_path}") + + if self.checkIfElectronAsarIntegrityIsUsed(app_path): + print("ElectronAsarIntegrity is used in Info.plist. Calculating hash and replacing it.") + asar_calculator = ASARCalculator(output_path) + new_hash = asar_calculator.calcASARHeaderHash() + print(f"New hash: {new_hash}") + print("Replacing ElectronAsarIntegrity in Info.plist") + os.system(f"/usr/libexec/PlistBuddy -c 'Set :ElectronAsarIntegrity:Resources/app.asar:hash {new_hash}' {info_plist_path}") + + print("Resigning app") + self.dumpEntitlements(app_path) + + os.system(f"codesign --force --entitlements /tmp/extracted_entitlements.xml --sign - {app_path}") + os.remove('/tmp/extracted_entitlements.xml') + + print("Done!") + +def main(): + parser = argparse.ArgumentParser(description="ASAR File Operations") + subparsers = parser.add_subparsers(dest='command') + + # Subparser for the extract command + extract_parser = subparsers.add_parser('extract', help='Extract an ASAR file') + extract_parser.add_argument('input_path', type=str, help='Path to the ASAR file to extract') + extract_parser.add_argument('output_path', type=str, help='Directory to extract the ASAR file into') + + # Subparser for the pack command + pack_parser = subparsers.add_parser('pack', help='Pack files into an ASAR file') + pack_parser.add_argument('input_directory', type=str, help='Directory to pack into an ASAR file') + pack_parser.add_argument('output_path', type=str, help='Path to the output ASAR file') + + args = parser.parse_args() + + patcher = ASARPatcher() + + if args.command == 'extract': + patcher.extractASAR(args.input_path, args.output_path) + elif args.command == 'pack': + patcher.packASAR(args.input_directory, args.output_path) + else: + print("Invalid command. Use 'extract' or 'pack'.") + +if __name__ == "__main__": + main() \ No newline at end of file