Android SQLite decrypt

2016/08/26 SQLite decrypt、微信数据库解密

SQLite decrypt tools

解密数据库文件

1> C/C++

#include <jni.h>
#include <stdio.h>
#include <string.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/hmac.h>

#define ERROR(X)  {printf("[ERROR] iteration %d: ", i); printf X;fflush(stdout);}
#define PAGESIZE 1024
#define PBKDF2_ITER 64000
#define FILE_HEADER_SZ 16

JNIEXPORT jint JNICALL
Java_com_xiaoxiaoying_tools_jni_WeChatJNI_decrypt(JNIEnv *env, jobject instance, jstring pwd_,
                                           jstring inFile_, jstring outFile_) {
    const char *pass = (*env)->GetStringUTFChars(env, pwd_, 0);
    const char *infile = (*env)->GetStringUTFChars(env, inFile_, 0);
    const char *outfile = (*env)->GetStringUTFChars(env, outFile_, 0);


    int i, csz, tmp_csz, key_sz, iv_sz, block_sz, hmac_sz, reserve_sz;
    FILE *infh, *outfh;
    int read, written;
    unsigned char *inbuffer, *outbuffer, *salt, *out, *key, *iv;
    EVP_CIPHER *evp_cipher;
    EVP_CIPHER_CTX ectx;

    OpenSSL_add_all_algorithms();

    evp_cipher = (EVP_CIPHER *) EVP_get_cipherbyname("aes-256-cbc");

    key_sz = EVP_CIPHER_key_length(evp_cipher);
    key = malloc(key_sz);

    iv_sz = EVP_CIPHER_iv_length(evp_cipher);
    iv = malloc(iv_sz);

    hmac_sz = EVP_MD_size(EVP_sha1());
    block_sz = EVP_CIPHER_block_size(evp_cipher);

    reserve_sz = iv_sz + hmac_sz;
    reserve_sz = ((reserve_sz % block_sz) == 0) ? reserve_sz : ((reserve_sz / block_sz) + 1) * block_sz;

    inbuffer = (unsigned char *) malloc(PAGESIZE);
    printf("%x\n",&inbuffer);
    outbuffer = (unsigned char *) malloc(PAGESIZE);
    salt = malloc(FILE_HEADER_SZ);
    infh = fopen(infile, "rb");
    outfh = fopen(outfile, "wb");

    read = fread(inbuffer, 1, PAGESIZE, infh);  /* read the first page */
    memcpy(salt, inbuffer, FILE_HEADER_SZ); /* first 16 bytes are the random database salt */

    PKCS5_PBKDF2_HMAC_SHA1(pass, strlen(pass), salt, FILE_HEADER_SZ, PBKDF2_ITER, key_sz, key);
    memset(outbuffer, 0, PAGESIZE);
    out = outbuffer;
    unsigned char *c = inbuffer + PAGESIZE - iv_sz;
    memcpy(iv, inbuffer + PAGESIZE - reserve_sz, iv_sz); /* last iv_sz bytes are the initialization vector */

    EVP_CipherInit(&ectx, evp_cipher, NULL, NULL, 0);
    EVP_CIPHER_CTX_set_padding(&ectx, 0);
    EVP_CipherInit(&ectx, NULL, key, iv, 0);
    
    EVP_CipherUpdate(&ectx, out, &tmp_csz, inbuffer + FILE_HEADER_SZ, PAGESIZE - reserve_sz - FILE_HEADER_SZ);
    csz = tmp_csz;
    out += tmp_csz;
    EVP_CipherFinal(&ectx, out, &tmp_csz);
    csz += tmp_csz;
    EVP_CIPHER_CTX_cleanup(&ectx);

    fwrite("SQLite format 3\0", 1, FILE_HEADER_SZ, outfh);
    fwrite(outbuffer, 1, PAGESIZE - FILE_HEADER_SZ, outfh);

    printf("wrote page %d\n", 0);

    for (i = 1; (read = fread(inbuffer, 1, PAGESIZE, infh)) > 0; i++) {
        memcpy(iv, inbuffer + PAGESIZE - reserve_sz, iv_sz); /* last iv_sz bytes are the initialization vector */
        memset(outbuffer, 0, PAGESIZE);
        out = outbuffer;

        EVP_CipherInit(&ectx, evp_cipher, NULL, NULL, 0);
        EVP_CIPHER_CTX_set_padding(&ectx, 0);
        EVP_CipherInit(&ectx, NULL, key, iv, 0);
        EVP_CipherUpdate(&ectx, out, &tmp_csz, inbuffer, PAGESIZE - reserve_sz);
        csz = tmp_csz;
        out += tmp_csz;
        EVP_CipherFinal(&ectx, out, &tmp_csz);
        csz += tmp_csz;
        EVP_CIPHER_CTX_cleanup(&ectx);

        fwrite(outbuffer, 1, PAGESIZE, outfh);
        printf("wrote page %d\n", i);
    }

    fclose(infh);
    fclose(outfh);

    free(inbuffer);
    free(outbuffer);
    free(key);
    free(salt);
    free(iv);

    return 0;


}

结果解不开2.1版本的数据库,然后查找提交的 历史记录 解密2.1版本的数据库

C/C++

#include <jni.h>
#include <stdio.h>
#include <string.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/hmac.h>

#define ERROR(X)  {printf("[ERROR] iteration %d: ", i); printf X;fflush(stdout);}
#define PAGESIZE 1024
#define PBKDF2_ITER 4000
#define FILE_HEADER_SZ 16

JNIEXPORT jint JNICALL
Java_com_xiaoxiaoying_tools_jni_WeChatJNI_decrypt(JNIEnv *env, jobject instance, jstring pwd_,
                                           jstring inFile_, jstring outFile_) {
    const char *pass = (*env)->GetStringUTFChars(env, pwd_, 0);
    const char *infile = (*env)->GetStringUTFChars(env, inFile_, 0);
    const char *outfile = (*env)->GetStringUTFChars(env, outFile_, 0);


    int i, csz, tmp_csz, key_sz, iv_sz/*, block_sz, hmac_sz, reserve_sz*/;
    FILE *infh, *outfh;
    int read, written;
    unsigned char *inbuffer, *outbuffer, *salt, *out, *key, *iv;
    EVP_CIPHER *evp_cipher;
    EVP_CIPHER_CTX ectx;

//    OpenSSL_add_all_algorithms();

    evp_cipher = (EVP_CIPHER *) EVP_get_cipherbyname("aes-256-cbc");

    key_sz = EVP_CIPHER_key_length(evp_cipher);
    key = malloc(key_sz);

    iv_sz = EVP_CIPHER_iv_length(evp_cipher);
    iv = malloc(iv_sz);

//    hmac_sz = EVP_MD_size(EVP_sha1());
//
//    block_sz = EVP_CIPHER_block_size(evp_cipher);
//
//    reserve_sz = iv_sz + hmac_sz;
//    reserve_sz = ((reserve_sz % block_sz) == 0) ? reserve_sz : ((reserve_sz / block_sz) + 1) * block_sz;

    inbuffer = (unsigned char *) malloc(PAGESIZE);
    printf("%x\n",&inbuffer);
    outbuffer = (unsigned char *) malloc(PAGESIZE);
    salt = malloc(FILE_HEADER_SZ);

    infh = fopen(infile, "r");
    outfh = fopen(outfile, "w");
//    infh = fopen(infile, "rb");
//    outfh = fopen(outfile, "wb");

    read = fread(inbuffer, 1, PAGESIZE, infh);  /* read the first page */
    memcpy(salt, inbuffer, FILE_HEADER_SZ); /* first 16 bytes are the random database salt */

    PKCS5_PBKDF2_HMAC_SHA1(pass, strlen(pass), salt, FILE_HEADER_SZ, PBKDF2_ITER, key_sz, key);
    memset(outbuffer, 0, PAGESIZE);
    out = outbuffer;
    unsigned char *c = inbuffer + PAGESIZE - iv_sz;
//    printf()
    memcpy(iv, c, iv_sz); /* last iv_sz bytes are the initialization vector */
//    memcpy(iv, inbuffer + PAGESIZE - reserve_sz, iv_sz); /* last iv_sz bytes are the initialization vector */

    EVP_CipherInit(&ectx, evp_cipher, NULL, NULL, 0);
    EVP_CIPHER_CTX_set_padding(&ectx, 0);
    EVP_CipherInit(&ectx, NULL, key, iv, 0);
    EVP_CipherUpdate(&ectx, out, &tmp_csz, inbuffer + FILE_HEADER_SZ,
                     PAGESIZE - iv_sz - FILE_HEADER_SZ);
//    EVP_CipherUpdate(&ectx, out, &tmp_csz, inbuffer + FILE_HEADER_SZ, PAGESIZE - reserve_sz - FILE_HEADER_SZ);
    csz = tmp_csz;
    out += tmp_csz;
    EVP_CipherFinal(&ectx, out, &tmp_csz);
    csz += tmp_csz;
    EVP_CIPHER_CTX_cleanup(&ectx);

    fwrite("SQLite format 3\0", 1, FILE_HEADER_SZ, outfh);
    fwrite(outbuffer, 1, PAGESIZE - FILE_HEADER_SZ, outfh);

    printf("wrote page %d\n", 0);

    for (i = 1; (read = fread(inbuffer, 1, PAGESIZE, infh)) > 0; i++) {
//        memcpy(iv, inbuffer + PAGESIZE - reserve_sz, iv_sz); /* last iv_sz bytes are the initialization vector */
        memcpy(iv, inbuffer + PAGESIZE - iv_sz,
               iv_sz); /* last iv_sz bytes are the initialization vector */
        memset(outbuffer, 0, PAGESIZE);
        out = outbuffer;

        EVP_CipherInit(&ectx, evp_cipher, NULL, NULL, 0);
        EVP_CIPHER_CTX_set_padding(&ectx, 0);
        EVP_CipherInit(&ectx, NULL, key, iv, 0);
        EVP_CipherUpdate(&ectx, out, &tmp_csz, inbuffer, PAGESIZE - iv_sz);
//        EVP_CipherUpdate(&ectx, out, &tmp_csz, inbuffer, PAGESIZE - reserve_sz);
        csz = tmp_csz;
        out += tmp_csz;
        EVP_CipherFinal(&ectx, out, &tmp_csz);
        csz += tmp_csz;
        EVP_CIPHER_CTX_cleanup(&ectx);

        fwrite(outbuffer, 1, PAGESIZE, outfh);
        printf("wrote page %d\n", i);
    }

    fclose(infh);
    fclose(outfh);

    free(inbuffer);
    free(outbuffer);
    free(key);
    free(salt);
    free(iv);

    return 0;


}

2> Java 解密

app > build.gradle 中添加

compile 'org.bouncycastle:bcprov-jdk16:1.46'

    private static int PBKDF_ITER = 4000;
    private static int FILE_HEADER_SZ = 16;
    private static int IV_SIZE = 16;
    private static final int PBKDF2_KEY_SIZE = 256; // bits
    private final static String MECHANISM = "AES/CBC/NoPadding";

    public static void decryptDB(String inputPath, String outputPath, String pwd) {
        if (TextUtils.isEmpty(inputPath)) {
            throw new NullPointerException("output path is null");
        }

        if (TextUtils.isEmpty(outputPath)) {
            throw new NullPointerException("input path is null");
        }
        try {
            File file = new File(inputPath);
            RandomAccessFile accessFile = new RandomAccessFile(file, "r");
            FileOutputStream outputStream = new FileOutputStream(outputPath);

            byte[] inbuffer = new byte[1024];
            byte[] salt = new byte[FILE_HEADER_SZ];
            accessFile.read(inbuffer, 0, 1024);
            System.arraycopy(inbuffer, 0, salt, 0, FILE_HEADER_SZ);
            Key key = androidPBKDF2(pwd.toCharArray(), salt, PBKDF_ITER, true);

            byte[] ivByte = new byte[IV_SIZE];
            System.arraycopy(inbuffer, 1024 - IV_SIZE, ivByte, 0, IV_SIZE);
            Cipher cipher = Cipher.getInstance(MECHANISM);

            IvParameterSpec ivSpace = new IvParameterSpec(ivByte);
            cipher.init(Cipher.DECRYPT_MODE, key, ivSpace);
            byte[] plaintText = new byte[1024 - FILE_HEADER_SZ];
            cipher.doFinal(inbuffer, FILE_HEADER_SZ, 1024 - FILE_HEADER_SZ * 2, plaintText, 0);
            String headStr = "SQLite format 3\0";
            outputStream.write(headStr.getBytes());
            outputStream.flush();
            outputStream.write(plaintText);
            outputStream.flush();

            long blockCount = file.length() / 1024;

            for (int i = 1; i < blockCount; i++) {

                accessFile.seek(i * 1024);
                accessFile.read(inbuffer, 0, 1024);
                decrypt(inbuffer, key, outputStream);
            }


            outputStream.close();
            accessFile.close();

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private static void decrypt(byte[] inbuffer, Key key, FileOutputStream outputStream) throws Exception {
        byte[] ivByte = new byte[IV_SIZE];
        System.arraycopy(inbuffer, 1024 - FILE_HEADER_SZ, ivByte, 0, FILE_HEADER_SZ);
        Cipher cipher = Cipher.getInstance(MECHANISM);

        IvParameterSpec ivSpace = new IvParameterSpec(ivByte);
        cipher.init(Cipher.DECRYPT_MODE, key, ivSpace);
        byte[] plaintText = new byte[1024];
        cipher.doFinal(inbuffer, 0, 1024 - FILE_HEADER_SZ, plaintText, 0);
        outputStream.write(plaintText);
        outputStream.flush();
    }

    public static SecretKey androidPBKDF2(char[] pwArray, byte[] salt, int rounds, boolean useUtf8) {
        PBEParametersGenerator generator = new PKCS5S2ParametersGenerator();
        byte[] pwBytes = useUtf8 ? PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(pwArray)
                : PBEParametersGenerator.PKCS5PasswordToBytes(pwArray);
        generator.init(pwBytes, salt, rounds);
        KeyParameter params = (KeyParameter) generator.generateDerivedParameters(PBKDF2_KEY_SIZE);

        return new SecretKeySpec(params.getKey(), "AES");
    }