Code related to Bypassing Electron Integrity article

This commit is contained in:
Karmaz95
2024-07-19 18:42:49 +02:00
parent 8abdf68fad
commit b08a5767ef
4 changed files with 263 additions and 0 deletions

View File

@@ -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 <path_to_asar_file>');
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()

View File

@@ -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('<I', asar_header_size_bytes)[0]
return(header_size)
def readASARHeader(file_path, header_size):
with open(file_path, 'rb') as f:
f.seek(16)
asar_header = f.read(header_size)
return(asar_header)
def calcASARHeaderHash(asar_header):
return(hashlib.sha256(asar_header).hexdigest())
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: python3 ElectronAsarIntegrityCalculator.py PATH_TO_ASAR_FILE")
sys.exit(1)
file_path = sys.argv[1]
asar_header_size = getASARHeaderSize(file_path)
asar_header_bytes = readASARHeader(file_path, asar_header_size)
asar_header_hash = calcASARHeaderHash(asar_header_bytes)
print(asar_header_hash)

View File

@@ -0,0 +1,81 @@
#!/bin/bash
# This script installs the necessary tools, sets up the project, creates the TypeScript file, compiles it, and runs the script with the specified ASAR file path.
# https://medium.com/@karol-mazurek/cracking-macos-apps-39575dd672e0
# Define the TypeScript file and ASAR path
TS_FILE="calculate_hash.ts"
ASAR_PATH="$1"
# Step 1: Install Node.js and npm (if not installed)
echo "Ensure Node.js and npm are installed..."
# Step 2: Install TypeScript and ts-node globally
echo "Installing TypeScript and ts-node globally..."
npm install -g typescript ts-node
# Step 3: Initialize npm project (if not already done)
if [ ! -f "package.json" ]; then
echo "Initializing npm project..."
npm init -y
fi
# Step 4: Install dependencies
echo "Installing asar and @types/node..."
npm install asar
npm install --save-dev @types/node
# Step 5: Create TypeScript file
cat <<EOL > $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 <path_to_asar_file>');
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."

View File

@@ -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('<I', asar_header_size_bytes)[0]
return header_size
def readASARHeader(self):
with open(self.file_path, 'rb') as f:
f.seek(16)
asar_header = f.read(self.asar_header_size)
return asar_header
def calcASARHeaderHash(self):
return hashlib.sha256(self.asar_header_bytes).hexdigest()
class ASARPatcher:
def __init__(self):
pass
def extractASAR(self, app_path, output_path):
'''Extracts {input_path} asar file to {output_path} directory.'''
input_path = os.path.join(app_path, "Contents/Resources/app.asar")
status_code = os.system(f"npx asar extract {input_path} {output_path}")
if status_code == 0:
print(f"Extracted {input_path} to {output_path} directory.")
else:
print(f"Failed to extract {input_path} to {output_path} directory. Error code: {status_code}")
def dumpEntitlements(self, app_path):
output_path='/tmp/extracted_entitlements.xml'
status_code = os.system(f"codesign -d --entitlements :- {app_path} > {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()