解密chrome cookies文件的encrypted_value

  • Post author:
  • Post category:其他


1.环境

.windows 8 (x64)

.chrome cookies文件(sqlite 3):%USERPROFILE%\AppData\Local\Google\Chrome\User Data\Default\Cookies

2.事由

解密chrome cookies数据的场景.

一个后台程序需要从3个平台抓取数据,每个平台的用户验证方式各不相同.利用浏览器登录后导出cookies实现自动登录.

开发一个工具程序,提供给用户,用户在浏览器登录后,运行该工具程序,导出相关网站的cookies,上传给后台服务程序.

浏览器选用chrome.

3.关于chrome的cookies

.保存在%USERPROFILE%\AppData\Local\Google\Chrome\User Data\Default\Cookies文件中

.该文件是sqlite 3数据库

.encrypted_value是加密后的blob内容

.Windows下加密采用DPAPI,

Decrypting Chrome’s cookies on windows


https://stackoverflow.com/questions/24353620/decrypting-chromes-cookies-on-windows


文中有Chromium加密函数源代码.用CryptProtectData加密

.ChromeCookiesView程序可以查看内容

ChromeCookiesView v1.46


https://nirsoft.net/utils/chrome_cookies_view.html


.浏览器运行期间,其它进程无法打开文件,报以下错误


java.sql.SQLException: [SQLITE_BUSY]  The database file is locked (database is locked)

4.Java DPAP

官网

Java Data Protection API


http://jdpapi.sourceforge.net/

下载的包有2个文件:

–jdpapi-java-1.0.jar

–jdpapi-native-1.0.dll

jdpapi-native-1.0.dll是IA 32-bit版本,直接使用报以下错误:


Exception in thread “main” java.lang.UnsatisfiedLinkError: E:\tool\jdpapi\jdpapi-native-1.0.dll: Can’t load IA 32-bit .dll on a AMD 64-bit platform

需要自己编译AMD64-bit版本.

获取源代码:


svn checkout https://svn.code.sf.net/p/jdpapi/code/ jdpapi-code


保存目录E:\tool\jdpapi\jdpapi-code(工程主目录,以下目录都是相对主目录)

其中,

-jdpapi\BUILD.txt: 编译说明.

-jdpapi\jdpapi-native\pom.xml:mvn文件

VC编译器采用Visaul Studio 2010,编译x64版本.

<compilerStartOption>增加jni.h,jni_md.h的路径.

<configuration>做以下修改:

	 <envFactoryName>
		org.codehaus.mojo.natives.msvc.MSVC2010x86AMD64EnvFactory
	</envFactoryName>
	<compilerStartOptions>
		<compilerStartOption>/LD /I"C:\Java\jdk1.8.0_91\include" /I"C:\Java\jdk1.8.0_91\include\win32"</compilerStartOption>
	</compilerStartOptions>

	<javahOS>x64</javahOS

由于使用msvc2010编译x64位程序,需要设置环境变量(搜索预期的cl.exe)

进入目录c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\amd64>

运行:

vcvars64.bat

执行命令:

cd jdpapi
mvn clean package assembly:assembly

执行后会生成jdpapi-java-1.0.1.jar.但编译jdpapi-native失败.

生成C++头文件:

执行命令:

cd jdpapi\jdpapi-java\target\classes
javah net.sourceforge.jdpapi.DPAPI


当前目录生成文件net_sourceforge_jdpapi_DPAPI.h,把头文件复制到目录:jdpapi\jdpapi-native\src\main\native

执行命令:

cd jdpapi
mvn clean package assembly:assembly


编译成功后,生成以下文件:

-jdpapi\jdpapi-java\target\jdpapi-java-1.0.1.jar

-jdpapi\jdpapi-native\target\jdpapi-native.dll

把这2个文件复制到测试目录下E:\tool\jdpapi

5.实现

采用java实现.

复制Cookies文件:Cookies_copy,用于测试.

读一条特定的cookies记录,对encrypted_value字段进行解密.通过sqlite工具或ChromeCookiesView或直接在浏览器中获取实际内容.用于解密后的比对.

解密利用Java DPAPI,通过JNI方式调用Windows DPAPI

代码如下:

package test_cookies;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import net.sourceforge.jdpapi.DataProtector;;


public class TestCookies {
	static Connection conn = null;
	static String dbPath = "C:\\Users\\Think\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\";
	static String dbName = dbPath + "Cookies_copy";

	private final DataProtector protector;
	public TestCookies() {
		this.protector = new DataProtector();
	}

	private String decrypt(byte[] data) {
		return protector.unprotect(data);
	}

	public static void main(String[] args) {
		try {
			TestCookies testCoolies = new TestCookies();
			Class.forName("org.sqlite.JDBC");
			conn = DriverManager.getConnection("jdbc:sqlite:" + dbName, null, null);

			conn.setAutoCommit(false);
			Statement stmt = conn.createStatement();
			stmt.setQueryTimeout(3); 
			String sql = String.format("select * from cookies where host_key like '%%.cainiao.com%%' and name='cna'");

			ResultSet rs = stmt.executeQuery(sql);
			while (rs.next()) {
				String name = rs.getString("name");
				String value = rs.getString("value");
				InputStream inputStream = rs.getBinaryStream("encrypted_value");
				ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
				int ch;
				while ((ch = inputStream.read()) != -1) {
					byteArrayOutputStream.write(ch);
				}
				byte[] b = byteArrayOutputStream.toByteArray();
				byteArrayOutputStream.close();

				System.out.println(String.format("name=%s value=%s encrypted_value=%s", 
						name, value,testCoolies.decrypt(b)));
			}
			rs.close();
			conn.close();
		} catch (Exception e) {
			e.printStackTrace();
		} 
	}

	static {
		System.load("e:/tool/jdpapi/jdpapi-native.dll");
	}
}

sqlite-jdbc驱动用3.23.1版本,有的版本会报以下错误:


java.sql.SQLException: not implemented by SQLite JDBC driver

pom.xml增加sqlite-jdbc依赖:

<!-- https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc -->
<dependency>
    <groupId>org.xerial</groupId>
    <artifactId>sqlite-jdbc</artifactId>
    <version>3.23.1</version>
</dependency>



版权声明:本文为wherwh原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。