逆向XX引擎(二):troj.db和hwl.db

hash计算方法

‘troj.db’和’hwl.db’使用相同的hash算法计算,具体实现算法再libxsse_50这个函数。对应的hash分为两段,
格式为’xxxxx,xxxxx’,匹配的时候是先匹配第一段然后接着匹配第二段

对于一些脚本类的样本,因为主要都是文本数据,只是简单的算了下crc32.

 

   v11 = v20;
    for ( j = 0; j < v11; ++j )
     {                                                                                       
      v13 = v22[j];
      if ( v13 != 13 && v13 != 10 && v13 != 9 && v13 != 32 )
      {
        v14 = v22[j];
        if ( (unsigned int)(v13 - 65) <= 0x19 )
          v14 = v13 + 32;
        v21 = v14;
        i = rhash_get_crc32((unsigned int)&v21, i, 1);
       }
     }                                                                                       

pe样本hash计算

第一段hash计算如下,以文件大小来计算初始crc值,然后每隔0x400取一个字节算crc32

 _QWORD cinit = size >> 12;
i = rhash_get_crc32(0, (unsigned char*)& cinit, 8); 
for (i = rhash_get_crc32(i, (unsigned char*)buf, readed_bytes); addr < totalCalcSize; i = rhash_get_crc32(i, (unsigned char*)& ch, 1)) { if (pe->stream->read(pe->stream, addr, &ch, 1) != 1) break; addr += 0x400; }

第二段的计算就有点复杂了。
主要就是先把pe中一些结构清0,然后计算hash。

checksum和timestamp

int magic = pe->peHead.magic;
int lfanew = pe->dosHeader.lfanew;
//将pe种checksum和timestamp字段填0
stream->write(stream, lfanew + 0x58, &zero_val, 4);
stream->write(stream, lfanew + 0x8, &zero_val, 4);

导出表的size字段

int pe_va = magic != 0x20B ? 0x108 : 0x118;
petab* tab = (petab*)((char*)& pe->ref + pe_va);
int fva = 0;
if (tab->rva && tab->size)
{
    //导出表的的size清空
    fva = pe->vtbl->va2fva(pe, tab->rva);
    stream->write(stream, fva + 4, &zero_val, 4);
}

security 数据目录清空

//security 数据目录清空
pe_va = magic == 0x20b ? lfanew + 0xA8 : lfanew + 0x98;

stream->write(stream, pe_va, &zero_val, 0x8);

debug 数据目录清0

//debug dir
                pe_va = magic != 0x20B ? 0x138 : 0x148;
                tab = (petab*)((char*)& pe->ref + pe_va);
                if (tab->rva && tab->size)
                {
                    if (tab->size >= 0x1c)
                    {
                        fva = pe->vtbl->va2fva(pe, tab->rva);
                        int dbgdir_size = dbgdir.size;
                        do
                        {
                            if (stream->read(stream, fva, (char*)& dbgdir, 0x1C) != 0x1c)
                                break;

                            if (dbgdir.file_offset && dbgdir.size &&
                                dbgdir.file_offset < stream->fileSize.QuadPart &&
                                dbgdir.file_offset + dbgdir.size <= stream->fileSize.QuadPart &&
                                dbgdir.size>0)
                            {
                                //往dbgdir写入0
                                char ch = 0;
                                for (int i = 0; i++; i < dbgdir.size)
                                {
                                    if (stream->write(stream, i + dbgdir.file_offset, &ch, 1) != 1)
                                        break;
                                }
                            }
                            dbgdir_size -= 0x1c;
                            stream->write(stream, fva + 4, &zero_val, 0x8);
                            fva += 0x1c;
                        } while (dbgdir_size < 0x1C);
                    }
                }

接着计算整个文件的Crc32,下面这个函数是libxsse.dll里封装的一个计算crc32的函数

void   calc_file_sha1_with_fva(libxsse_stream* stream, libxsse_pe* pe, int offset, int size, sha1_ctx* ctx)
{
    int left = 0;
    if (size <= 0)
        return;

    byte* msg = pe->buffer;
    _QWORD readSize = 0;
    do
    {
        //读取pe数据用作msg
        readSize = stream->read(stream, offset, msg, min(0x10000, size));
        if (readSize > 0)
        {
            left = size - readSize;
            offset += readSize;
            rhash_sha1_update(ctx, msg, readSize);
            size = left;
        }
    } while (left > 0);

    return;
}

计算NT头部

int    va = pe->peHead.hdrsize + 0x18 + pe->dosHeader.lfanew;
if (va > 0)
{
    bufSize = va;
    calc_file_sha1_with_fva(stream, pe, offset, bufSize, &ctx);
}

计算区段

//计算节表的hash
pesection sec;

for (int i = 0; i < pe->peHead.nobjs; ++i)
{
    int read_sec_size = stream->read(stream, va, &sec, 0x28);
    if (read_sec_size > 0)
    {
        rhash_sha1_update(&ctx, (unsigned char*)& sec, 0x28);
        bufSize = min(sec.s_psize, 0x10000);
        offset = sec.s_scnptr;
        calc_file_sha1_with_fva(stream, pe, offset, bufSize, &ctx);
    }
    if (read_sec_size != 0x28)
        break;
    va += 0x28;
}
文章目录
  1. 1. hash计算方法
    1. 1.1. pe样本hash计算
      1. 1.1.1. checksum和timestamp
      2. 1.1.2. 导出表的size字段
      3. 1.1.3. security 数据目录清空
      4. 1.1.4. debug 数据目录清0
      5. 1.1.5. 计算NT头部
      6. 1.1.6. 计算区段
,