Skip to content

Instantly share code, notes, and snippets.

@lamchau
Last active March 26, 2024 05:35
Show Gist options
  • Save lamchau/718aa36a6e78c2a744ff017709efcf58 to your computer and use it in GitHub Desktop.
Save lamchau/718aa36a6e78c2a744ff017709efcf58 to your computer and use it in GitHub Desktop.
[Chrome/Tampermonkey] Extract *.user.js scripts from LevelDB backup

Restoring Tampermonkey scripts

Chrome stores .user.js scripts in a .ldb, which isn't in a user accessible format to recovery. On some versions of macOS the provided python script can't compile the leveldb package. As a workaround, we can use the node level package to recover our userscripts.

Prerequisite

brew install node
npm install level

Usage

node extract.js "/Users/$(whoami)/Library/Application Support/Google/Chrome Canary/backup/Default/Default/Local Extension Settings/dhdgffkkebhmkfjojejmpbldmpobfkfo"
const fs = require('fs');
const path = require('path');
const level = require('level');
const directory = path.resolve(process.argv.length > 2
? process.argv[2]
: 'dhdgffkkebhmkfjojejmpbldmpobfkfo');
const db = level(directory);
const stream = db.createReadStream();
console.log(`Loading: ${directory}`);
const date = new Date().toISOString();
const outputDirectory = path.resolve(
path.join(
__dirname,
date.slice(0, date.indexOf('T'))
)
);
if (!fs.existsSync(outputDirectory)) {
fs.mkdirSync(outputDirectory, true);
}
console.log(`Saving to: ${outputDirectory}`);
stream.on('data', data => {
const { key, value } = data;
try {
if (value.includes('UserScript')) {
const uuid = key.slice(key.indexOf('#') + 1);
const parsed = JSON.parse(value).value;
const filename = path.join(outputDirectory, `${uuid}.user.js`);
fs.writeFile(filename, parsed, () => {
console.log(`Saved: ${path.basename(filename)}`);
});
}
} catch (err) {
}
});
@wkurten87
Copy link

wkurten87 commented Nov 15, 2022

for those that are looking for this solution in november/22 i have updated this script to work with newer versions, hope this helps somebody!

console.log(`--> LOG - INICIO --------------------`);
const fs = require('fs');
const path = require('path');

const directory = path.resolve(process.argv.length > 2
  ? process.argv[2]
  : 'bhmmomiinigofkjcapegjjndpbikblnp');

const { Level } = require('level')
const db = new Level(directory, { valueEncoding: 'json' });
console.log(`--> LOG - Tentando abrir a base`);
db.open(main);

const { EntryStream } = require('level-read-stream')
const re = new RegExp(`^@source(.*)$`);

function main(e){
	if (e){
		console.log(`--> LOG - ERROR: ${e}`);
	} else {

	console.log(`--> LOG - db status: ${db.status}`);

	console.log(`--> LOG - Loading: ${directory}`);
	const date = new Date().toISOString();
	const outputDirectory = path.resolve(
	  path.join(
		__dirname,
		date.slice(0, date.indexOf('T'))
	  )
	);

	if (!fs.existsSync(outputDirectory)) {
	  fs.mkdirSync(outputDirectory, true);
	}

	var stream = new EntryStream(db);
	stream.on('data', data => {
	  const { key, value } = data;
	  
	  try {
		console.log(`--> LOG - Saving to: ${outputDirectory}`);
		if (re.test(key)){
			console.log(`--> LOG - ACHOU! ->> key: ${key} - value ${value} `);
			
			const uuid = key.slice(key.indexOf('#') + 1);
			console.log(`--> LOG - uuid: ${uuid}`);
			
			//console.log('--> LOG - JSON Stringy' + JSON.stringify(value));
			const parsed = JSON.stringify(value);
			
			const filename = path.join(outputDirectory, `${uuid}.user.js`);
			console.log(`--> LOG - filename: ${filename}`);
			
			fs.writeFile(filename, parsed, () => {
			console.log(`--> LOG - Saved: ${path.basename(filename)}`);
			});
		}
	  } catch (err) {
			console.log(`--> LOG - ERROR: ${err}`);
	  }
	});

	console.log(`--> LOG - FIM --------------------`);
	}
}

@omicr0n
Copy link

omicr0n commented Apr 16, 2023

Updated script above works, thank you. For anyone else that plans to use it you have to run npm install level-read-stream as well before executing it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment