|
|
本帖最后由 523066680 于 2026-3-19 16:59 编辑
声明, 这个方案是2020年做的, 已经不适用于新版本的chrome. 但是网上搜寻一下也可以找到对应新版本的方案.
最初需求是通过perl mojo模块批量处理一些跨境电商的产品数据, 统计友商的信息, 当时的接口访问已经有些严格, 无法完全在本地实现模拟登录.
于是尝试通过获取浏览器一致的cookies的方式来模拟请求 (selecnium 考虑过, 只要使用这个模块, 就会不断要求验证)
cookies 数据位置:
C:/Users/'.$ENV{"USERNAME"}.'/AppData/Local/Google/Chrome/User Data/Default/Cookies
这个文件在chrome打开的情况下无法复制副本, 可以用 sqlite 打开查看键值对(加密的).
以下是一些旧的笔记
以一个时间较早的脚本作为参考(2015年), 其中 decryptData 可以用来解密 $secret_key,但其他部分已经无法获取结果。
decryptchromecookies.pl
- #!/usr/bin/perl -w
- use File::Copy qw(copy);
- use DBI;
- use Win32::API;
- use strict;
- use warnings;
- print ("Decrypting cookies...\n") && &fix_cookies && print ("Cookies decrypted!\n");
- sub fix_cookies
- {
- #Chrome has been encrypting cookie values since Chrome..33?
- #We need to decrypt the value before we can use it.
- my $chrome_cookie_file = 'C:/Users/'.$ENV{"USERNAME"}.'/AppData/Local/Google/Chrome/User Data/Default/Cookies';
- copy($chrome_cookie_file, 'Cookies') || die "Failed to move files: $!";;
- my $dbc = DBI->connect("dbi:SQLite:dbname=Cookies", '', '', { RaiseError => 1, AutoCommit => 0});
- my @rows = @{$dbc->selectall_arrayref("SELECT host_key, name, value, encrypted_value FROM cookies")};
- foreach my $row (@rows){
- my ($host_key, $name, $value, $encrypted_value) = @{$row};
- my $new_value = decryptData($encrypted_value) || $value || '0';
- #This is optional, but it allows us to use any session cookies that may exist at the time of running this.
- #This is assuming that you will be generating a new decrypted session file whenever you run your script.
- my $sth = $dbc->prepare(qq{
- UPDATE cookies SET value = ?, has_expires = 1, expires_utc = 99999999999999999, is_persistent = 1
- WHERE host_key = ?
- AND name = ?
- });
- $sth->execute($new_value, $host_key, $name);
- }
- $dbc->commit(); #SQLite is slow at excuting one row at a time. SEE: http://stackoverflow.com/a/8882184
- $dbc->disconnect();
- }
- sub decryptData
- {
- #Cleaned up version of http://www.perlmonks.org/?node_id=776481
- my $encryptedData = shift;
- if($encryptedData eq ''){ return undef; } #avoid errors...
- my $pDataIn = pack('LL', length($encryptedData)+1, unpack('L!', pack('P', $encryptedData)));
- my $DataOut;
- my $pDataOut = pack('LL', 0, 0);
- my $CryptUnprotectData = Win32::API->new('Crypt32', 'CryptUnprotectData', ['P', 'P', 'P', 'P', 'P', 'N', 'P'], 'N');
- if($CryptUnprotectData->Call($pDataIn, pack('L', 0), 0, pack('L', 0), pack('L4', 16, 0, 0, unpack('L!', pack('P', 0))), 0, $pDataOut)){
- my($len, $ptr) = unpack('LL', $pDataOut);
- $DataOut = unpack('P'.$len, pack('L!', $ptr));
- return $DataOut;
- }else{
- return undef;
- }
- }
复制代码
脚本行为:先复制cookies到当前目录,读取并尝试解密,将解密数据重新写入数据库。
实测decrypt后的new_value值为0,解密失败。
在这个基础上做了一些修改, 实现了获取 aliexpress.com cookies 的模块
- package GetAliExCookies;
- use utf8;
- use Encode;
- use File::Slurp;
- use Mojo::UserAgent;
- use MIME::Base64;
- # metacpan 搜索 GCM 找到该模块
- use Crypt::AuthEnc::GCM qw(gcm_encrypt_authenticate gcm_decrypt_verify);
- use DBI;
- use Win32::API;
- use File::Copy;
- use JSON qw/from_json to_json/;
- STDOUT->autoflush(1);
- sub get_ck_str
- {
- my $f1 = "C:/Users/". $ENV{"USERNAME"} ."/AppData/Local/Google/Chrome/User Data/Local State";
- my $data = from_json( scalar(read_file($f1)) );
- # encrypt_key 前5位是 "DPAPI"
- my $enc_key = decode_base64( $data->{'os_crypt'}{'encrypted_key'} );
- $enc_key = substr($enc_key, 5);
- my $dec_key = decryptData( $enc_key );
- #print_hex($dec_key);
- # -M $f: time minus file modification time, in days.
- #if ( (not -e "Cookies") or (-M "Cookies") > 7 ) {
- # 2021-12 update path C:\Users\${USERNAME}\AppData\Local\Google\Chrome\User Data\Default\Network\Cookies
- my $chrome_cookie_file = 'C:/Users/'.$ENV{"USERNAME"}.'/AppData/Local/Google/Chrome/User Data/Default/Network/Cookies';
- copy($chrome_cookie_file, 'Cookies') || die "Failed to get cookies: $!";;
- #}
- my $dbc = DBI->connect("dbi:SQLite:dbname=Cookies", '', '', { RaiseError => 1, AutoCommit => 0});
- my %names = map {$_ => 1} qw/
- _lang
- acs_usuc_t
- aep_usuc_t
- xman_us_t
- aep_usuc_f
- xman_us_f
- xman_t
- intl_locale
- /;
- my $ckstr = "";
- my @rows = @{$dbc->selectall_arrayref("SELECT host_key, name, value, encrypted_value FROM cookies")};
- for my $row (@rows)
- {
- my ($host_key, $name, $value, $encrypted_value) = @{$row};
- next unless ( $host_key eq ".aliexpress.com" );
- next unless ( exists $names{$name} );
- # 加密数据以 v10 的 ASCII 编码(即 0x763130)开始,然后是 12 字节的随机数、实际密文,最后是 16 字节的认证标签。 可以按如下方式分离各个组件:
- my $nonce = substr($encrypted_value, 3, 12);
- my $ciphertext = substr($encrypted_value, 3+12, -16);
- my $tag = substr($encrypted_value, -16);
- my $plaintext = gcm_decrypt_verify('AES', $dec_key, $nonce, undef, $ciphertext, $tag);
- #printf "%s = %s\n", $name, $plaintext;
- $ckstr .= "${name}=${plaintext}; ";
- }
- #$dbc->commit(); #SQLite is slow at excuting one row at a time. SEE: http://stackoverflow.com/a/8882184
- $dbc->disconnect();
- return $ckstr;
- }
- sub decryptData
- {
- #Cleaned up version of http://www.perlmonks.org/?node_id=776481
- my $encryptedData = shift;
- if($encryptedData eq ''){ return undef; } #avoid errors...
- my $pDataIn = pack('LL', length($encryptedData)+1, unpack('L!', pack('P', $encryptedData)));
- my $DataOut;
- my $pDataOut = pack('LL', 0, 0);
- my $CryptUnprotectData = Win32::API->new('Crypt32', 'CryptUnprotectData', ['P', 'P', 'P', 'P', 'P', 'N', 'P'], 'N');
- if($CryptUnprotectData->Call($pDataIn, pack('L', 0), 0, pack('L', 0), pack('L4', 16, 0, 0, unpack('L!', pack('P', 0))), 0, $pDataOut)){
- my($len, $ptr) = unpack('LL', $pDataOut);
- $DataOut = unpack('P'.$len, pack('L!', $ptr));
- return $DataOut;
- }else{
- return undef;
- }
- }
- 1;
复制代码 |
|