Closed ShayBox closed 1 year ago
/// https://github.com/cclgroupltd/ccl_chrome_indexeddb
fn decode_string(bytes: &[u8]) -> Result<Cow<'_, str>> {
let prefix = bytes.first().ok_or(anyhow!("No prefix found"))?;
match prefix {
0 => Ok(UTF_16LE.decode(&bytes[1..]).0),
1 => Ok(WINDOWS_1252.decode(&bytes[1..]).0),
_ => bail!("Invalid prefix"),
}
}
Would you like to contribute this as another example in the examples
directory? For example a tool to show a nice overview of what's stored in a local storage database.
Sure, this is the mostly final code from my project, which serializes data with serde_json
The path code would need to be changed to point to a browser's leveldb and the struct fields changed
You could also skip the struct and parse directly to a dynamic serde_json::Value and iterate over the db
use std::{borrow::Cow, path::PathBuf};
use anyhow::{anyhow, bail, Result};
use encoding_rs::{UTF_16LE, WINDOWS_1252};
use rusty_leveldb::{CompressionType, Options, DB};
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Wootility {
#[serde(rename = "_persist")]
pub persist: Value,
pub profiles: Value,
#[serde(rename = "wootilityConfig")]
pub wootility_config: Value,
}
impl Wootility {
pub fn get_path() -> Result<PathBuf> {
["", "-beta", "-alpha"]
.into_iter()
.map(|path| format!("wootility-lekker{path}/Local Storage/leveldb"))
.map(|path| dirs::config_dir().unwrap().join(path))
.find(|path| path.exists())
.ok_or(anyhow!("Couldn't find Wootility path"))
}
pub fn load() -> Result<Self> {
let mut db = DB::open(
Self::get_path()?,
Options {
compression_type: CompressionType::CompressionSnappy,
create_if_missing: false,
paranoid_checks: true,
..Default::default()
},
)?;
const KEY: &[u8; 22] = b"_file://\x00\x01persist:root";
let encoded = db.get(KEY).ok_or(anyhow!("Couldn't find Wootility data"))?;
let decoded = Self::decode_string(&encoded)?;
Ok(serde_json::from_str(&decoded)?)
}
/// https://github.com/cclgroupltd/ccl_chrome_indexeddb
pub fn decode_string(bytes: &[u8]) -> Result<Cow<'_, str>> {
let prefix = bytes.first().ok_or(anyhow!("Invalid length"))?;
match prefix {
0 => Ok(UTF_16LE.decode(&bytes[1..]).0),
1 => Ok(WINDOWS_1252.decode(&bytes[1..]).0),
_ => bail!("Invalid prefix"),
}
}
}
fn main() -> Result<()> {
let wootility = Wootility::load()?;
println!("{wootility:#?}");
Ok(())
}
Update
After many hours of trying to decode JSON from LevelDB from Chrome/Electron, I figured out the encoding
I found this blog post that helped point me in the right direction, but unfortunately, my data was not compressed with
lz-string
like Slack.