找回密码
 注册
搜索
[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程[批处理精品]批处理版照片整理器
[批处理精品]纯批处理备份&还原驱动[批处理精品]CMD命令50条不能说的秘密[在线下载]第三方命令行工具[在线帮助]VBScript / JScript 在线参考
查看: 42|回复: 0

[技术讨论] [Perl]Chrome浏览器 cookies 数据提取和解密

[复制链接]
发表于 昨天 16:37 | 显示全部楼层 |阅读模式
本帖最后由 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

  1. #!/usr/bin/perl -w

  2. use File::Copy qw(copy);
  3. use DBI;
  4. use Win32::API;

  5. use strict;
  6. use warnings;

  7. print ("Decrypting cookies...\n") && &fix_cookies && print ("Cookies decrypted!\n");

  8. sub fix_cookies
  9. {
  10.         #Chrome has been encrypting cookie values since Chrome..33?
  11.         #We need to decrypt the value before we can use it.

  12.         my $chrome_cookie_file = 'C:/Users/'.$ENV{"USERNAME"}.'/AppData/Local/Google/Chrome/User Data/Default/Cookies';
  13.         copy($chrome_cookie_file, 'Cookies') || die "Failed to move files: $!";;

  14.         my $dbc = DBI->connect("dbi:SQLite:dbname=Cookies", '', '', { RaiseError => 1, AutoCommit => 0});

  15.         my @rows = @{$dbc->selectall_arrayref("SELECT host_key, name, value, encrypted_value FROM cookies")};
  16.         foreach my $row (@rows){
  17.                 my ($host_key, $name, $value, $encrypted_value) = @{$row};

  18.                 my $new_value = decryptData($encrypted_value) || $value || '0';

  19.                 #This is optional, but it allows us to use any session cookies that may exist at the time of running this.
  20.                 #This is assuming that you will be generating a new decrypted session file whenever you run your script.
  21.                 my $sth = $dbc->prepare(qq{
  22.                         UPDATE cookies SET value = ?, has_expires = 1, expires_utc = 99999999999999999, is_persistent = 1
  23.                         WHERE host_key = ?
  24.                         AND name = ?
  25.                 });
  26.                 $sth->execute($new_value, $host_key, $name);
  27.         }

  28.         $dbc->commit(); #SQLite is slow at excuting one row at a time. SEE: http://stackoverflow.com/a/8882184
  29.         $dbc->disconnect();
  30. }

  31. sub decryptData
  32. {
  33.         #Cleaned up version of http://www.perlmonks.org/?node_id=776481
  34.         my $encryptedData = shift;

  35.         if($encryptedData eq ''){ return undef; } #avoid errors...

  36.         my $pDataIn = pack('LL', length($encryptedData)+1, unpack('L!', pack('P', $encryptedData)));

  37.         my $DataOut;
  38.         my $pDataOut = pack('LL', 0, 0);

  39.         my $CryptUnprotectData = Win32::API->new('Crypt32', 'CryptUnprotectData', ['P', 'P', 'P', 'P', 'P', 'N', 'P'], 'N');
  40.         if($CryptUnprotectData->Call($pDataIn, pack('L', 0), 0, pack('L', 0), pack('L4', 16, 0, 0, unpack('L!', pack('P', 0))), 0, $pDataOut)){
  41.                 my($len, $ptr) = unpack('LL', $pDataOut);
  42.                 $DataOut = unpack('P'.$len, pack('L!', $ptr));
  43.                 return $DataOut;
  44.         }else{
  45.                 return undef;
  46.         }
  47. }
复制代码


脚本行为:先复制cookies到当前目录,读取并尝试解密,将解密数据重新写入数据库。
实测decrypt后的new_value值为0,解密失败。

在这个基础上做了一些修改, 实现了获取 aliexpress.com cookies 的模块
  1. package GetAliExCookies;

  2. use utf8;
  3. use Encode;
  4. use File::Slurp;
  5. use Mojo::UserAgent;
  6. use MIME::Base64;

  7. # metacpan 搜索 GCM 找到该模块
  8. use Crypt::AuthEnc::GCM qw(gcm_encrypt_authenticate gcm_decrypt_verify);
  9. use DBI;
  10. use Win32::API;
  11. use File::Copy;
  12. use JSON qw/from_json to_json/;
  13. STDOUT->autoflush(1);

  14. sub get_ck_str
  15. {
  16.     my $f1 = "C:/Users/". $ENV{"USERNAME"} ."/AppData/Local/Google/Chrome/User Data/Local State";
  17.     my $data = from_json( scalar(read_file($f1)) );
  18.     # encrypt_key 前5位是 "DPAPI"
  19.     my $enc_key = decode_base64( $data->{'os_crypt'}{'encrypted_key'} );
  20.     $enc_key = substr($enc_key, 5);
  21.     my $dec_key = decryptData( $enc_key );
  22.     #print_hex($dec_key);

  23.     # -M $f: time minus file modification time, in days.
  24.     #if ( (not -e "Cookies") or (-M "Cookies") > 7 ) {
  25.         # 2021-12 update path C:\Users\${USERNAME}\AppData\Local\Google\Chrome\User Data\Default\Network\Cookies
  26.         my $chrome_cookie_file = 'C:/Users/'.$ENV{"USERNAME"}.'/AppData/Local/Google/Chrome/User Data/Default/Network/Cookies';
  27.         copy($chrome_cookie_file, 'Cookies') || die "Failed to get cookies: $!";;
  28.     #}

  29.     my $dbc = DBI->connect("dbi:SQLite:dbname=Cookies", '', '', { RaiseError => 1, AutoCommit => 0});

  30.     my %names = map {$_ => 1} qw/
  31.         _lang
  32.         acs_usuc_t
  33.         aep_usuc_t
  34.         xman_us_t
  35.         aep_usuc_f
  36.         xman_us_f
  37.         xman_t
  38.         intl_locale
  39.         /;
  40.     my $ckstr = "";
  41.     my @rows = @{$dbc->selectall_arrayref("SELECT host_key, name, value, encrypted_value FROM cookies")};
  42.     for my $row (@rows)
  43.     {
  44.         my ($host_key, $name, $value, $encrypted_value) = @{$row};
  45.         next unless ( $host_key eq ".aliexpress.com" );
  46.         next unless ( exists $names{$name} );

  47.         # 加密数据以 v10 的 ASCII 编码(即 0x763130)开始,然后是 12 字节的随机数、实际密文,最后是 16 字节的认证标签。 可以按如下方式分离各个组件:
  48.         my $nonce = substr($encrypted_value, 3, 12);
  49.         my $ciphertext = substr($encrypted_value, 3+12, -16);
  50.         my $tag = substr($encrypted_value, -16);

  51.         my $plaintext = gcm_decrypt_verify('AES', $dec_key, $nonce, undef, $ciphertext, $tag);
  52.         #printf "%s = %s\n", $name, $plaintext;

  53.         $ckstr .= "${name}=${plaintext}; ";

  54.     }

  55.     #$dbc->commit(); #SQLite is slow at excuting one row at a time. SEE: http://stackoverflow.com/a/8882184
  56.     $dbc->disconnect();

  57.     return $ckstr;
  58. }

  59. sub decryptData
  60. {
  61.     #Cleaned up version of http://www.perlmonks.org/?node_id=776481
  62.     my $encryptedData = shift;
  63.     if($encryptedData eq ''){ return undef; } #avoid errors...
  64.     my $pDataIn = pack('LL', length($encryptedData)+1, unpack('L!', pack('P', $encryptedData)));

  65.     my $DataOut;
  66.     my $pDataOut = pack('LL', 0, 0);

  67.     my $CryptUnprotectData = Win32::API->new('Crypt32', 'CryptUnprotectData', ['P', 'P', 'P', 'P', 'P', 'N', 'P'], 'N');
  68.     if($CryptUnprotectData->Call($pDataIn, pack('L', 0), 0, pack('L', 0), pack('L4', 16, 0, 0, unpack('L!', pack('P', 0))), 0, $pDataOut)){
  69.         my($len, $ptr) = unpack('LL', $pDataOut);
  70.         $DataOut = unpack('P'.$len, pack('L!', $ptr));
  71.         return $DataOut;
  72.     }else{
  73.         return undef;
  74.     }
  75. }

  76. 1;
复制代码
您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|手机版|小黑屋|批处理之家 ( 渝ICP备10000708号 )

GMT+8, 2026-3-20 03:33 , Processed in 0.017252 second(s), 8 queries , File On.

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表