[转载]手把手用C++解密Chrome80版本数据库
谷歌瀏覽器Google Chrome 80正式版例行更新詳細版本80.0.3987.163。Google Chrome瀏覽器又稱谷歌瀏覽器采用Chromium內核全球最受歡迎的免費網頁瀏覽器追求速度、隱私安全的網絡瀏覽器。
先說下吧。chrome80以前的版本是直接可以通過DPAPI來進行解密的。關于DPAPI 大家可以 看這里的介紹
DPAPI是Windows系統級對數據進行加解密的一種接口無需自實現加解密代碼微軟已經提供了經過驗證的高質量加解密算法提供了用戶態的接口對密鑰的推導存儲數據加解密實現透明并提供較高的安全保證
DPAPI提供了兩個用戶態接口`CryptProtectData`加密數據`CryptUnprotectData`解密數據加密后的數據由應用程序負責安全存儲應用無需解析加密后的數據格式。但是加密后的數據存儲需要一定的機制因為該數據可以被其他任何進程用來解密當然`CryptProtectData`也提供了用戶輸入額外`數據`來參與對用戶數據進行加密的參數但依然無法放于暴力破解。
總體來說程序可以使用DPAPI來對自己敏感的數據進行加解密也可持久化存儲程序或系統重啟后可解密密文獲取原文。如果應用程序對此敏感數據只是暫存于內存為了防止被黑客dump內存后進行破解也對此數據無需進行持久化存儲微軟還提供了加解密內存的接口`CryptProtectMemory`和`CryptUnprotectMemory`。加解密內存的接口并可指定`Flag`對此內存加解密的聲明周期做控制詳細見`Memory加密及優缺點`章節
廢話不多說我們且來看看新版的Chrome是怎么一個加密流程。首先。我們需要大致清楚新版chrome用到的加密。無非就是2個 劃重點
DPAPI
AES-GCM
先給大家看一用python寫的解密吧
aes.py
importos
importsys
importsqlite3
fromurllib.parseimporturlencode
importjson,base64
importaesgcm
importbinascii
defdpapi_decrypt(encrypted):
importctypes
importctypes.wintypes
classDATA_BLOB(ctypes.Structure):
_fields_=[('cbData',ctypes.wintypes.DWORD),
('pbData',ctypes.POINTER(ctypes.c_char))]
p=ctypes.create_string_buffer(encrypted,len(encrypted))
blobin=DATA_BLOB(ctypes.sizeof(p),p)
blobout=DATA_BLOB()
retval=ctypes.windll.crypt32.CryptUnprotectData(
ctypes.byref(blobin),None,None,None,None,0,ctypes.byref(blobout))
ifnotretval:
raisectypes.WinError()
result=ctypes.string_at(blobout.pbData,blobout.cbData)
ctypes.windll.kernel32.LocalFree(blobout.pbData)
returnresult
defaes_decrypt(encrypted_txt):
encrypted_txt=binascii.unhexlify(encrypted_txt)
encoded_key="RFBBUEkBAAAA0Iyd3wEV0RGMegDAT8KX6wEAAAAFBcVfgeqrR6TWICu+11nQAAAAAAIAAAAAABBmAAAAAQAAIAAAADGFDG3ftjedfJDzI98JL+tPfbE3tgNumX5v+PGs9eEgAAAAAA6AAAAAAgAAIAAAAHMoKUPxu+eC153jdAcreqzjPCvccip33ZQPvnOZstQBMAAAAFCQh824CftlmS+gbu8NK1Gev4EVvODPwV6T33S9AXilInJ26Z09nTULJE3pF+9XtEAAAACndz8ZGF2V7IMxQDK6kFAk6wOUv/Bx9hZhZtiyu2urYfKYbCPvMSWg4e9+/oQrEL2NEG+fFjX/EP6SrLzE8Xqy"
encrypted_key=base64.b64decode(encoded_key)
print("encrypted_key="+encrypted_key.hex()+"Len="+str(len(encrypted_key))+"
");
encrypted_key=encrypted_key[5:]
print("encrypted_key="+encrypted_key.hex()+"
");
key=dpapi_decrypt(encrypted_key)
print("key="+key.hex()+"
");
nonce=encrypted_txt[3:15]
print("nonce="+nonce.hex()+"
");
cipher=aesgcm.get_cipher(key)
##print("cipher="+cipher.hex()+"
");
print("encrypted_txt="+encrypted_txt[15:].hex()+"
");
returnaesgcm.decrypt(cipher,encrypted_txt[15:],nonce)
print(aes_decrypt("76313068C3E4957EC879AD4483CBFA7476E7B77C035D8355A5D73FCFA9A87007D908896061DDD79471"))
然后是aes-gcm
importos
importsys
fromcryptography.hazmat.backendsimportdefault_backend
fromcryptography.hazmat.primitives.ciphersimport(
Cipher,algorithms,modes
)
NONCE_BYTE_SIZE=12
defencrypt(cipher,plaintext,nonce):
cipher.mode=modes.GCM(nonce)
encryptor=cipher.encryptor()
ciphertext=encryptor.update(plaintext)
return(cipher,ciphertext,nonce)
defdecrypt(cipher,ciphertext,nonce):
cipher.mode=modes.GCM(nonce)
decryptor=cipher.decryptor()
returndecryptor.update(ciphertext)
defget_cipher(key):
cipher=Cipher(
algorithms.AES(key),
None,
backend=default_backend()
)
returncipher
如此即可解密。說下簡單的流程吧。
大致流程從C:UsersopsAppDataLocalGoogleChromeUserDataLocalState這個Json中讀取一個值os_crypt下的encrypted_key
然后取解密秘鑰(encrypted_key)去除前5個字符再通過對其dpapi解密出這個值保存為key.并且截取15位去除前3位字符保存為Nonce.
然后使用asegcm進行解密key最終使用aesgcm解密。
大致就是如此。為了證明我的屁眼代碼可以用。上一個圖。稍等。。我去安裝下chrome80。。。影子系統還原了。。。我安裝好了
取下encrypted_key和被加密的value的HEX。在這之前 我們先看下加密的內容
包含V10和V11的是chrme80的加密。好了 我們來找找freebuf的值
把加密值HEX[v10mC1^?I~\`ql>t^c+EO0bJKp1YRn?F$O]一下得到7631306D43A786939231E0A4D6DC5E**BB497E5C60716CFEFDDB3E74A7ABE2E5F1BAF45EF5F163BC2BB**54F9D30624A4B708D310C168894FFEC189C8959526ECBAD46EF1D7FD224B6868FA64F83CD
然后我們用python解密一下
可看到了解密成功。下一篇用C++來實現自動化解密
幾個注意點
Cookie位于User Data/Default下的Cookies文件 改名為Cookies.db即可用sqllite進行查詢和查看
上一篇實現了python的簡單解密。這一次我們來用C++實現自動化。在這之前 我們需要用到兩個C++庫
repaidjson
cryptopp
編譯環境為VS2013.這兩個庫不多做介紹,rapidjson是騰訊的一個開源json解析庫。發揮的作用不大,就是解析個json。另外就是cryptopp。嗯。。很牛逼。
解析下大致流程:
1:獲取local state文件位置
2:獲取加密的key(base64編碼)
3:解析sqllite文件
4:DPAPI解密
5:ase-gcm解密
關于Aes-gcm 需要用到 KEY IV 以及被加密的字符串 梳理下這幾個參數的流程:
KEY = local state = > os_crypt => Encrypted_key => Base64Decode(encrypted_key) => 去除首位5個字符 => DPAPI解密
IV = 被加密的字符串掐頭去尾
chiper = 被加密的字符串去頭
一般來說 安裝的這些配置文件都在LOCAL_APPDATA下。可以使用SHGetSpecialFolderPath(NULL, szBuffer, CSIDL_LOCAL_APPDATA, FALSE);
來獲取這個路徑。然后starcat組合一下字符串得到路徑 部分代碼如下:
char szBuffer[MAX_PATH];
if (EncryptBaseKey == "")
{
string jsonstr;
SHGetSpecialFolderPath(NULL, szBuffer, CSIDL_LOCAL_APPDATA, FALSE);
strcat(szBuffer, "\Google\Chrome\User Data\Local State");
jsonstr = readfile(szBuffer);
Document root;
root.Parse(jsonstr.c_str());
Value& infoArray = root["os_crypt"];
EncryptBaseKey = infoArray["encrypted_key"].GetString();
}
return EncryptBaseKey;
這里就獲取了加密的秘鑰。但是有一點。如果是非80版本 是不存在os_crypt的,這里使用的rapidjson就會拋出異常。但不影響。只需要在使用sqllite查詢的時候 接管一下字符串,看看是不是包含v10或者v11即可。如果你使用的和我一樣代碼。請注意大小寫V10和v10的區別。
string e_str = argv[i];
if (strstr(e_str.c_str(), "v10") != NULL || strstr(e_str.c_str(), "v11") != NULL)
{
string DecryptVaule=NewDecrypt(argv[i]);
strcpy(enc_value_a, DecryptVaule.c_str());
}
else{
DecryptPass(argv[i], enc_value, 2048);
_snprintf_s(enc_value_a, sizeof(enc_value_a), _TRUNCATE, "%S", enc_value);
}
緊接著就是對他進行base64解密。這里我用的是cryptopp 先放一下新版解密函數
std::string static NewDecrypt(CHAR *cryptData)
{
string EncryptValue = cryptData;
string Encoded,Decoded;
string key,iv,chiper;
string recovered;//也就是解密的KEY
WCHAR enc_value[2048];
char enc_value_a[2048];
ZeroMemory(enc_value, sizeof(enc_value));
ZeroMemory(enc_value_a, sizeof(enc_value_a));
//-----------------------初始化幾個要用到加密字符串的變量----------------------------------//
iv = EncryptValue;
chiper = EncryptValue;
//---------------------------------------------------------//
StringSource((BYTE*)EncryptValue.c_str(), EncryptValue.size(), true,
new HexEncoder(
new StringSink(Encoded)));
EncryptValue = Encoded;
Encoded.clear();
//---------------------------------------------------------//
key = GetEncryptKEY();
StringSource((BYTE*)key.c_str(), key.size(), true,
new Base64Decoder(
new StringSink(Decoded)));
key = Decoded;
key = key.substr(5);//去除首位5個字符
Decoded.clear();
DecryptPass((char*)key.c_str(), enc_value, 2048);
_snprintf_s(enc_value_a, sizeof(enc_value_a), _TRUNCATE, "%S", enc_value);
key = enc_value_a;
StringSource((BYTE*)key.c_str(),key.size(), true,
new HexEncoder(
new StringSink(Encoded)));
key = Encoded;
Encoded.clear();
//KEY解密完畢 開始處理Nonce 也就是IV
iv =iv.substr(3,12);
StringSource((BYTE*)iv.c_str(), iv.size(), true,
new HexEncoder(
new StringSink(Encoded)));
iv = Encoded;
Encoded.clear();
//---------------------------------------------------------//
//開始處理chiper
if (chiper.size() < 30){ return "wu xiao zi fu chuan....."; }
StringSource((BYTE*)chiper.c_str(), chiper.size(), true,
new HexEncoder(
new StringSink(Encoded)));
chiper = Encoded;
Encoded.clear();
chiper = chiper.substr(30);//因為是HEX 占了2個字節
//---------------------------------------------------------//
//進行AES_GCM
try
{
StringSource((BYTE*)iv.c_str(), iv.size(), true,
new HexDecoder(
new StringSink(Decoded)
) // HexEncoder
); // StringSource
iv = Decoded;
Decoded.clear();
StringSource((BYTE*)key.c_str(), key.size(), true,
new HexDecoder(
new StringSink(Decoded)
) // HexEncoder
); // StringSource
key = Decoded;
Decoded.clear();
StringSource((BYTE*)chiper.c_str(), chiper.size(), true,
new HexDecoder(
new StringSink(Decoded)
) // HexEncoder
); // StringSource
chiper = Decoded;
Decoded.clear();
cout << chiper << endl;
GCM< AES >::Decryption d;
d.SetKeyWithIV((BYTE*)key.c_str(), key.size(), (BYTE*)iv.c_str(), iv.size());
StringSource s(chiper, true,
new AuthenticatedDecryptionFilter(d,
new StringSink(recovered)
) // StreamTransformationFilter
); // StringSource
cout << "recovered text: " << recovered << endl;
}
catch (const CryptoPP::Exception& e)
{
cerr << e.what() << endl;
//exit(1);
}
return recovered;
}
先base64解碼一下
key = GetEncryptKEY();
StringSource((BYTE*)key.c_str(), key.size(), true,
new Base64Decoder(
new StringSink(Decoded)));
key = Decoded;
key = key.substr(5);//去除首位5個字符
Decoded.clear();
如此可以得到這一樣一個字符串
這是沒有去除字符的情況下,這個時候去除之后 即祛除了首位的DPAPI 如此便獲得了一個初步解密的KEY。但在這之后,我們還需要對這個KEY做一次解密,因為這個時候的KEY還不能真正算是解密的KEY 他還需要進行一次DPAPI解密
DPAPI的解密函數部分代碼如下:
DATA_BLOB input;
input.pbData = (BYTE*)(cryptData);
DATA_BLOB output;
DWORD blen;
for(blen=128; blen<=2048; blen+=16) {
input.cbData = blen;
if (CryptUnprotectData(&input, NULL, NULL, NULL, NULL, 0, &output))
break;
}
if (blen>=2048)
return 0;
CHAR *decrypted = (CHAR *)malloc(clearSize);
if (!decrypted) {
LocalFree(output.pbData);
return 0;
}
memset(decrypted, 0, clearSize);
memcpy(decrypted, output.pbData, (clearSize < output.cbData) ? clearSize - 1 : output.cbData);
_snwprintf_s(clearData, clearSize, _TRUNCATE, L"%S", decrypted);
free(decrypted);
LocalFree(output.pbData);
return 1;
在解密之后我們可以得到:
然后我們對加密字符串進行處理,取出iv和chiper。再使用aes-gcm解密即可。
iv =iv.substr(3,12);
StringSource((BYTE*)iv.c_str(), iv.size(), true,
new HexEncoder(
new StringSink(Encoded)));
iv = Encoded;
Encoded.clear();
//---------------------------------------------------------//
//開始處理chiper
if (chiper.size() < 30){ return "wu xiao zi fu chuan....."; }
StringSource((BYTE*)chiper.c_str(), chiper.size(), true,
new HexEncoder(
new StringSink(Encoded)));
chiper = Encoded;
Encoded.clear();
chiper = chiper.substr(30);//因為是HEX 占了2個字節
解密
try
{
StringSource((BYTE*)iv.c_str(), iv.size(), true,
new HexDecoder(
new StringSink(Decoded)
) // HexEncoder
); // StringSource
iv = Decoded;
Decoded.clear();
StringSource((BYTE*)key.c_str(), key.size(), true,
new HexDecoder(
new StringSink(Decoded)
) // HexEncoder
); // StringSource
key = Decoded;
Decoded.clear();
StringSource((BYTE*)chiper.c_str(), chiper.size(), true,
new HexDecoder(
new StringSink(Decoded)
) // HexEncoder
); // StringSource
chiper = Decoded;
Decoded.clear();
cout << chiper << endl;
GCM< AES >::Decryption d;
d.SetKeyWithIV((BYTE*)key.c_str(), key.size(), (BYTE*)iv.c_str(), iv.size());
StringSource s(chiper, true,
new AuthenticatedDecryptionFilter(d,
new StringSink(recovered)
) // StreamTransformationFilter
); // StringSource
cout << "recovered text: " << recovered << endl;
}
catch (const CryptoPP::Exception& e)
{
cerr << e.what() << endl;
//exit(1);
}
return recovered;
最終獻上Demo源碼
// Chrome80解密Demo.cpp : 定義控制臺應用程序的入口點。
//
#include "stdafx.h"
#include <string>
#include <fstream>
#include <iostream>
/*********************************
加密庫頭存放在這
*********************************/
#include "cryptoppase64.h"
using CryptoPP::Base64Decoder;
using CryptoPP::Base64Encoder;
#include "cryptopp/hex.h"
using CryptoPP::HexEncoder;
using CryptoPP::HexDecoder;
#include "cryptopp/filters.h"
using CryptoPP::StringSink;
using CryptoPP::StringSource;
using CryptoPP::AuthenticatedEncryptionFilter;
using CryptoPP::AuthenticatedDecryptionFilter;
#include "cryptopp/aes.h"
using CryptoPP::AES;
#include "cryptopp/gcm.h"
using CryptoPP::GCM;
#include "cryptopp/secblock.h"
using CryptoPP::SecByteBlock;
/*********************************
加密庫頭加載完畢
*********************************/
using namespace std;
#pragma comment(lib,"userenv.lib")
#pragma comment(lib,"cryptlib.lib")
#pragma comment(lib,"Crypt32.lib")
//RFBBUEkBAAAA0Iyd3wEV0RGMegDAT8KX6wEAAAAFBcVfgeqrR6TWICu+11nQAAAAAAIAAAAAABBmAAAAAQAAIAAAAJxLse8lqGAP4o493iTyljEUUF9y76AAoprRgHJwesCyAAAAAA6AAAAAAgAAIAAAAFtTd4B22Ky/x2LVgQUSaKku2rCvsv+FiMFj+lGN8LmZMAAAANBlkfPhV/zVaMALHr0gK6dM7nFsfNTv6bfFKCyKbIorgbBnjfKp+K5MVz9iizYVs0AAAACihmRGBIQ6oDkgjzCk+9AhePof4eUhB98pb7UlbGgssV2fnGRrBYQHW8Gyyp9W4pojyn9J7GQixtdCIPBwEW92
//763130954DBA6D89BBAB2FF4A4460AEA7B823BA5BAF01B2B5E2CECDED5855F6E1E7B57946599C6ACD7D60F4B03FC11D5F7C6A39FA59FBF33D7
int DecryptPass(CHAR *cryptData, WCHAR *clearData, UINT clearSize)
{
DATA_BLOB input;
input.pbData = (BYTE*)(cryptData);
DATA_BLOB output;
DWORD blen;
for (blen = 128; blen <= 2048; blen += 16) {
input.cbData = blen;
if (CryptUnprotectData(&input, NULL, NULL, NULL, NULL, 0, &output))
break;
}
if (blen >= 2048)
return 0;
CHAR *decrypted = (CHAR *)malloc(clearSize);
if (!decrypted) {
LocalFree(output.pbData);
return 0;
}
memset(decrypted, 0, clearSize);
memcpy(decrypted, output.pbData, (clearSize < output.cbData) ? clearSize - 1 : output.cbData);
_snwprintf_s(clearData, clearSize, _TRUNCATE, L"%S", decrypted);
free(decrypted);
LocalFree(output.pbData);
return 1;
}
int _tmain(int argc, _TCHAR* argv[])
{
string EncryptValue;
string key, iv, chiper, recovered;
string Decoded, Encoded;
WCHAR enc_value[2048];
char enc_value_a[2048];
ZeroMemory(enc_value, sizeof(enc_value));
ZeroMemory(enc_value_a, sizeof(enc_value_a));
cout << "請輸入EncryptKEY[BASE64]:" << endl;
cin >> key;
cout << "請輸入EncryptValue[HEX]:" << endl;
cin >> EncryptValue;
cout << "<---------------開始解密流程--------------->
" << endl;
//開始賦值
iv = EncryptValue;
chiper = EncryptValue;
StringSource((BYTE*)key.c_str(), key.size(), true,
new Base64Decoder(
new StringSink(Decoded)));
key = Decoded;
Decoded.clear();
cout << "1:EncryptKEY 進行Base64解密:
" << key << "
" << endl;
key = key.substr(5);
cout << "2:EncryptKEY 去除首5個字符:
" << key << "
" << endl;
DecryptPass((char*)key.c_str(), enc_value, 2048);
_snprintf_s(enc_value_a, sizeof(enc_value_a), _TRUNCATE, "%S", enc_value);
key = enc_value_a;
cout << "3:EncryptKEY 進行DPAPI解密:
" << key << "
" << endl;
StringSource((BYTE*)key.c_str(), key.size(), true,
new HexEncoder(
new StringSink(Encoded)));
key = Encoded;
Encoded.clear();
cout << "4:對已經通過DPAPI的EncryptKEY 進行HEX編碼:
" << key << "
" << endl;
StringSource((BYTE*)iv.c_str(), iv.size(), true,
new HexDecoder(
new StringSink(Decoded)));
iv = Decoded;
Decoded.clear();
iv=iv.substr(3, 15);
StringSource((BYTE*)iv.c_str(), iv.size(), true,
new HexEncoder(
new StringSink(Encoded)));
iv = Encoded;
Encoded.clear();
iv = iv.substr(0,iv.size()-6);
cout << "5:對要解密的字符串進行反HEX編碼 也就是解碼 并且截取之后再次 進行HEX編碼 賦值給iv:
" << iv << "
" << endl;
chiper = chiper.substr(30);
cout << "6:對要解密的字符串進行截取末尾15:
" << chiper << "
" << endl;
try
{
StringSource((BYTE*)iv.c_str(), iv.size(), true,
new HexDecoder(
new StringSink(Decoded)
) // HexEncoder
); // StringSource
iv = Decoded;
Decoded.clear();
StringSource((BYTE*)key.c_str(), key.size(), true,
new HexDecoder(
new StringSink(Decoded)
) // HexEncoder
); // StringSource
key = Decoded;
Decoded.clear();
StringSource((BYTE*)chiper.c_str(), chiper.size(), true,
new HexDecoder(
new StringSink(Decoded)
) // HexEncoder
); // StringSource
chiper = Decoded;
Decoded.clear();
cout << chiper << endl;
GCM< AES >::Decryption d;
d.SetKeyWithIV((BYTE*)key.c_str(), key.size(), (BYTE*)iv.c_str(), iv.size());
StringSource s(chiper, true,
new AuthenticatedDecryptionFilter(d,
new StringSink(recovered)
) // StreamTransformationFilter
); // StringSource
cout << "7:最終解密文本為:
" << recovered << "
" << endl;
}
catch (const CryptoPP::Exception& e)
{
cerr << e.what() << endl;
//exit(1);
}
system("pause");
return 0;
}
附上一張解密靚照
核對下解密的密文是否正確
歡迎的加入群:480257002
*本文作者:hijacking,轉載請注明來自FreeBuf.COM
總結
以上是生活随笔為你收集整理的[转载]手把手用C++解密Chrome80版本数据库的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【转】Unity3D研究院之使用Xama
- 下一篇: 平均池化-最大池化-全局池化