逆向XX引擎:库文件解密

火绒所有的库文件都使用同一种方法解密的,具体解密算法都是在libxsse_30中实现的.
数据库头部数据都是通用的,结构如下:

struct   
database_header
{
    _DWORD DRAV;
    _DWORD SFHY;
    _WORD lowVersion;
    _WORD hiVersion;
    _QWORD timeStamp;
    _DWORD db_attr;
    _DWORD recordCount;
    _DWORD propSize;
    _DWORD origin_size; //未加密前的数据大小
    _DWORD compress_size;//zlib压缩数据的大小
    _DWORD key2[32];
    char dbName[8];
    _DWORD end[4];//0结尾
};

pset.db的如图

dbName是数据库的类型,找了几个库看了下,发现有如下几种

DB:RAW    解密后就是原始数据
DB:HWL和DB:TROJ 是hash类的特征,格式比较简单
DB:PSET和DB:PROP 特征库,解密后数据是按照一定格式组织的

读取头部0xc0个字节。

database_header* dh = &a1->db;
int readytes = stream->pread(stream, dh, 0xC0);
if (!readytes)
    return 0;
if (readytes != 0xc0)
    return -5;

首先判断是不是火绒的病毒库,通过文件magic

//判断db文件的magic
if (dh->DRAV != 'DRAV' || dh->SFHY != 'SFHY' || a3 && (_WORD)(a3 + 2) != HIWORD(a1->db.lowVersion))
return -9;

简单的异或生成另一个key。然后生成的key再通过gen_new_xor_key函数算法生成另一个key。这个函数比较复杂就不说了。

unsigned char g_xor_key[260] = {
    0x42, 0xE6, 0x70, 0x7B, 0x08, 0x64, 0x27, 0x04, 0xFD, 0x7C, 0xC2, 0x73, 0x6C, 0x8A, 0x1B, 0x8F,
    0x0C, 0x36, 0x1E, 0x3C, 0x91, 0x10, 0x28, 0x28, 0x8C, 0xD1, 0xD7, 0x4D, 0x5D, 0x93, 0xEB, 0x0D,
    0xC2, 0x16, 0xDA, 0xBC, 0x5C, 0xA8, 0x1A, 0xD9, 0x45, 0xD4, 0xE9, 0x03, 0x47, 0x85, 0x77, 0x35,
    0x6E, 0x92, 0xF5, 0x24, 0x6E, 0x4D, 0xFA, 0x9A, 0x12, 0xC8, 0x3B, 0x91, 0x35, 0xFA, 0x21, 0x41,
    0x13, 0xF5, 0x26, 0x89, 0xF5, 0xC3, 0x03, 0x93, 0xC6, 0x81, 0x1D, 0xA0, 0xF4, 0xA1, 0x48, 0x1A,
    0x77, 0xC6, 0xD7, 0xD8, 0xE4, 0xA7, 0x3C, 0x96, 0x6E, 0x73, 0xA6, 0xDD, 0xD9, 0xB5, 0x6D, 0x38,
    0x4D, 0x28, 0xB9, 0xC9, 0x63, 0x6C, 0x9C, 0x54, 0x74, 0x00, 0xAC, 0xC9, 0x67, 0xD2, 0x8B, 0x14,
    0xB5, 0x26, 0xBE, 0x1F, 0x5A, 0xB1, 0xDE, 0xFB, 0xEB, 0xE9, 0x18, 0xF3, 0x7A, 0xF3, 0x0B, 0xC8,
    0x47, 0x42, 0x64, 0xC6, 0xC8, 0x81, 0x2B, 0x2A, 0xB2, 0xC6, 0xCB, 0x5B, 0x6B, 0xF6, 0x9C, 0x99,
    0x4D, 0x45, 0x6C, 0xF8, 0x04, 0x85, 0x30, 0x2E, 0xCC, 0xFA, 0xE9, 0x1F, 0x85, 0x3B, 0x2E, 0x5C,
    0x0A, 0x7F, 0x10, 0x07, 0xB8, 0xAA, 0xAF, 0xB1, 0xCB, 0x07, 0x1E, 0xEE, 0xFA, 0x4E, 0xD9, 0x1D,
    0xB5, 0x45, 0x98, 0x2C, 0x04, 0x8C, 0x6B, 0x6A, 0xD8, 0xCA, 0x39, 0xA4, 0x8E, 0x3C, 0xD9, 0x97,
    0xF1, 0xDC, 0x7C, 0x6A, 0x65, 0xF1, 0x85, 0x1A, 0xA3, 0xBE, 0xFB, 0xC3, 0x1C, 0x67, 0x24, 0x88,
    0x5D, 0xCD, 0x62, 0x10, 0x8D, 0xB4, 0xA1, 0xDE, 0xFC, 0x62, 0x91, 0x87, 0x3A, 0x2B, 0x3E, 0x33,
    0x9B, 0x0C, 0xE3, 0x61, 0x03, 0x49, 0xCE, 0x16, 0xAB, 0xFE, 0x07, 0xDF, 0x9C, 0xBF, 0x68, 0x84,
    0xC5, 0x5C, 0xA1, 0xAD, 0x00, 0x0D, 0xE8, 0x12, 0x41, 0xF2, 0x21, 0xDB, 0xB6, 0xA3, 0x26, 0x81,
    0x47, 0xDD, 0xF4, 0xA3
    };

    //异或计算key
    char key1[260] = {};
    memcpy(key1, g_xor_key, sizeof(g_xor_key));
    for (int i = 0; i < 0x101; ++i)
        * (_DWORD*)& key1[i] ^= 0xF00DBEEF * (i ^ 0xC0DEC0DE);
    unsigned int rt = gen_new_xor_key(&key_size, &xor_gen_key, a1->db.key2, 0x80, key1);

现在开始解密被加密的数据,使用最后生成的key

void* origin_buffer = malloc(a1->db.origin_size);
if (!origin_buffer)
    return -12;

int compress_size = a1->db.compress_size;
if (compress_size)
{
    //!压缩的数据先异或解密
    char* compress_buf = (char*)malloc(compress_size);
    if (!compress_buf)
        return -1;
    //读取压缩数据, 在文件偏移0xc0
    stream->pread(a2, compress_buf, compress_size);
    //异或解密数据
    if (a1->db.db_attr >= 0x80000000 && compress_size)
    {
        for (int i = 0; i < compress_size; i++)
        {
            compress_buf[i] ^= xor_gen_key[(i & 7) + 0x14];
        }
    }
    //////////////////////////////////////////////////////////////////////////
    //计算hash是为了验证文件有没有被修改
    //rhash_sha1_update(&ctx, (const unsigned char*)compress_buf, a1->db.compress_size);

    //unsigned char sha1[0x14] = {};
    //rhash_sha1_final(&ctx, (unsigned char*)& sha1);

    //////////////////////////////////////////////////////////////////////////
    //!原代码还会比较sha1和xor_gen_key的前0x10字节是否相同,去掉了
    if (a1->db.db_attr & 0x40000000)
    {
        //zlib解压
        rt = uncompress((Bytef*)origin_buffer, (uLongf*)& a1->db.origin_size, (Bytef*)compress_buf, compress_size);
    }
    /////将解压的文件写入文件
}

uncompress是zlib的解压缩函数,只要hook这个地方就能把解密后的数据dump出来

下图就是解密后的prop.db和pset.db

文章目录
,