解密chrome cookies文件的encrypted_value

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>

猜你喜欢

转载自blog.csdn.net/wherwh/article/details/80780290