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
import os
import sys
import sqlite3
from urllib.parse import urlencode
import json, base64
import aesgcm
import binascii
def dpapi_decrypt(encrypted):
 import ctypes
 import ctypes.wintypes

 class DATA_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))
 if not retval:
 raise ctypes.WinError()
 result = ctypes.string_at(blobout.pbData, blobout.cbData)
 ctypes.windll.kernel32.LocalFree(blobout.pbData)
 return result
def aes_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))+"\r\n");
 encrypted_key = encrypted_key[5:]
 print("encrypted_key="+encrypted_key.hex()+"\r\n");
 key = dpapi_decrypt(encrypted_key)
 print("key="+key.hex()+"\r\n");
 nonce = encrypted_txt[3:15]
 print("nonce="+nonce.hex()+"\r\n");
 cipher = aesgcm.get_cipher(key)
 ##print("cipher="+cipher.hex()+"\r\n");
 print("encrypted_txt="+encrypted_txt[15:].hex()+"\r\n");
 return aesgcm.decrypt(cipher,encrypted_txt[15:],nonce)
print(aes_decrypt("76313068C3E4957EC879AD4483CBFA7476E7B77C035D8355A5D73FCFA9A87007D908896061DDD79471"))

然后是aes-gcm

import os
import sys

from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.ciphers import (
 Cipher, algorithms, modes
)

NONCE_BYTE_SIZE = 12

def encrypt(cipher, plaintext, nonce):
 cipher.mode = modes.GCM(nonce)
 encryptor = cipher.encryptor()
 ciphertext = encryptor.update(plaintext)
 return (cipher, ciphertext, nonce)

def decrypt(cipher, ciphertext, nonce):
 cipher.mode = modes.GCM(nonce)
 decryptor = cipher.decryptor()
 return decryptor.update(ciphertext)

def get_cipher(key):
 cipher = Cipher(
 algorithms.AES(key),
 None,
 backend=default_backend()
 )
 return cipher

如此即可解密。说下简单的流程吧。

大致流程从C:\Users\0ops\AppData\Local\Google\Chrome\UserData\LocalState这个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();

解密

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 "cryptopp\base64.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 << "<---------------开始解密流程--------------->\r\n" << 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解密:\r\n" << key << "\r\n" << endl;
	key = key.substr(5);
	cout << "2:EncryptKEY 去除首5个字符:\r\n" << key << "\r\n" << 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解密:\r\n" << key << "\r\n" << endl;
	StringSource((BYTE*)key.c_str(), key.size(), true,
		new HexEncoder(
		new StringSink(Encoded)));
	key = Encoded;
	Encoded.clear();
	cout << "4:对已经通过DPAPI的EncryptKEY 进行HEX编码:\r\n" << key << "\r\n" << 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:\r\n" << iv << "\r\n" << endl;
	chiper = chiper.substr(30);
	cout << "6:对要解密的字符串进行截取末尾15:\r\n" << chiper << "\r\n" << 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:最终解密文本为:\r\n" << recovered << "\r\n" << endl;
	}
	catch (const CryptoPP::Exception& e)
	{
		cerr << e.what() << endl;
		//exit(1);
	}
	system("pause");
	return 0;
}

附上一张解密靓照

核对下解密的密文是否正确

到此这篇关于C++解密Chrome80版本数据库的方法示例代码的文章就介绍到这了,更多相关c++ 解密Chrome80数据库内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++加密解密php代码的方法

    本文实例讲述了C++加密解密php代码的方法.分享给大家供大家参考.具体实现方法如下: #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" #include "string.h" char * key = "abcd"; PHP_FUNCTION(encode){ long key_len = strlen(key); c

  • C++解密Chrome80版本数据库的方法示例代码

    谷歌浏览器Google Chrome 80正式版例行更新详细版本80.0.3987.163.Google Chrome浏览器又称谷歌浏览器采用Chromium内核全球最受欢迎的免费网页浏览器追求速度.隐私安全的网络浏览器. 先说下吧.chrome80以前的版本是直接可以通过DPAPI来进行解密的.关于DPAPI 大家可以 看这里的介绍 DPAPI是Windows系统级对数据进行加解密的一种接口无需自实现加解密代码微软已经提供了经过验证的高质量加解密算法提供了用户态的接口对密钥的推导存储数据加解密

  • 解密 Linux 版本信息的方法

    显示和解释有关 Linux 版本的信息比看起来要复杂一些. 与引用一个简单的版本号不同,识别 Linux 版本有很多种方法.即使只是快速查看一下 uname 命令的输出,也可以告诉你一些信息.这些信息是什么,它告诉你什么? 在本文中,我们将认真研究 uname 命令的输出以及其他一些命令和文件提供的版本说明. 使用 uname 每当在 Linux 系统终端窗口中执行命令 uname -a 时,都会显示很多信息.那是因为这个小小的 a 告诉 uname 命令你想查看该命令能提供的全部输出.结果显示

  • Java调用微信支付功能的方法示例代码

    Java 使用微信支付 前言百度搜了一下微信支付,都描述的不太好,于是乎打算自己写一个案例,希望以后拿来直接改造使用. 因为涉及二维码的前端显示,所以有前端的内容 一. 准备工作 所需微信公众号信息配置 APPID:绑定支付的APPID(必须配置) MCHID:商户号(必须配置) KEY:商户支付密钥,参考开户邮件设置(必须配置) APPSECRET:公众帐号secert(仅JSAPI支付的时候需要配置) 我这个案例用的是尚硅谷一位老师提供的,这里不方便提供出来,需要大家自己找,或者公司提供 二

  • Oracle查看表结构的几种方法示例代码

    1,DESCRIBE 命令 使用方法如下: SQL> describe nchar_tst(nchar_tst为表名) 显示的结果如下: 名称 是否为空? 类型 ----------------------------------------- -------- ---------------------------- NAME NCHAR(6) ADDR NVARCHAR2(16) SAL NUMBER(9,2) 2,DBMS_METADATA.GET_DDL包 使用方法如下: SQL> S

  • Java 添加Word目录的2种方法示例代码详解

    目录是一种能够快速.有效地帮助读者了解文档或书籍主要内容的方式.在Word中,插入目录首先需要设置相应段落的大纲级别,根据大纲级别来生成目录表.本文中生成目录分2种情况来进行: 1.文档没有设置大纲级别,生成目录前需要手动设置 2.文档已设置大纲级别,通过域代码生成目录 使用工具: •Free Spire.Doc for Java 2.0.0 (免费版) •IntelliJ IDEA 工具获取途径1:通过官网下载jar文件包,解压并导入jar文件到IDEA程序. 工具获取途径2:通过Maven仓

  • 基于Java的MathML转图片的方法(示例代码)

    Maven依赖: <dependency> <groupId>de.rototor.jeuclid</groupId> <artifactId>jeuclid-core</artifactId> <version>3.1.14</version> </dependency> 示例: @Test public void testMathMlToImg() throws IOException { //MathML

  • Spring BeanUtils忽略空值拷贝的方法示例代码

    目录 简介 获取null属性名(工具类) 示例 工具类 Entity Controller 测试 其他文件 其他网址 简介 说明 本文用示例介绍Spring(SpringBoot)如何使用BeanUtils拷贝对象属性(忽略空值). BeanUtils类所在的包 有两个包都提供了BeanUtils类: Spring的(推荐):org.springframework.beans.BeanUtilsApache的:org.apache.commons.beanutils.BeanUtils 忽略nu

  • SQL Server 2008及更高版本数据库恢复方法之日志尾部备份

    经常看到有人误删数据,或者误操作,特别是update和delete的时候没有加where,然后就喊爹喊娘了.人非圣贤孰能无过,做错可以理解,但不能纵容,这个以后再说,现在先来解决问题. 遇到这种情况,一般都是没有做备份,不然也不会来发问了.首先要冷静,否则会有更大的灾难.直到你放弃. 解决方法: 对于这类问题,主要是找回误操作之前的数据,在2008之前,有个很出名的工具Log Exploer,听说还挺好用的,这个网上大把教程,这里就不多说了.但是唯一遗憾的是,不支持2008及更高版本,这时除了其

  • Python实现批量读取图片并存入mongodb数据库的方法示例

    本文实例讲述了Python实现批量读取图片并存入mongodb数据库的方法.分享给大家供大家参考,具体如下: 我的图片放在E:\image\中,然后使用python将图片读取然后,显示一张,存入取一张(可以注释掉显示图片的语句),通过Gridfs的方式存入图片.代码如下: # --* coding=utf-8 *-- from cStringIO import StringIO from pymongo import MongoClient import gridfs import os imp

  • C#使用checkedListBox1控件链接数据库的方法示例

    本文实例讲述了C#使用checkedListBox1控件链接数据库的方法.分享给大家供大家参考,具体如下: 在数据库中创建三个表: 学生信息表  爱好表   学生爱好表 结果让学生的信息和爱好同时显示到C#窗体上面 using System; using System.Collections; using System.Collections.Generic; using System.Data; using System.Diagnostics; using System.Data.Sql;

随机推荐