本帖最后由 fyc132 于 2018-9-26 18:49 编辑
0x1: 某反外挂一直在打开文件,我们对ReadFile 下一个断点,很快就命中了
查看下调用者发现是在 tcj 中,读取的大小是PAGE_SIZE * 10
对读入的数据下一个硬件断点
看了栈,实际上TCJ 读完文件,就直接进来了,IDA 分析下这个函数
分析它的子函数可以很容易看到,这就是一个 MD5 的函数
看下 TCJ 循环READFILE 的逻辑
实际上就是循环读取文件内容,每次 PAGE_SIZE * 10 大小,得到最终的MD5 值放到参数中
我们接着看 TCJ得到了文件的MD5 后要继续做了什么,如果成功得到了文件的MD5 ,那么就进入了第二个函数
该函数逻辑如下,其作用是获取整个文件的 murmur哈希值和文件大小
名单最终我们得到了 2 个值
那么,程序已经获取了目标程序的 MD5 值,HASH值,和文件大小。接着
作为参数调用。 IDA 回顾下之前的逻辑,值得一提的是,在以后操作都完成后,释放才会文件句柄
我们接着分析 HandleFileHash,对输入的MD5 参数下一个硬件断点
发现断在了这个地方。回溯往上
第二次中中断发现在比较 MD5 的值
可以得出这段代码是先把 MD5 归类成一个ID ,然后在一个链表中查找相同ID 的数据,这样做的目的是加速搜索
观察它的结构,发现已经存在于数据库中了,看来这个数据库存放的都是 TCJ 扫描过的所有文件的信息。
接着,我们回到上一个地方,发现它查找到对应的数据库后,就会判断数据库中的 + 0×11字段是否匹配,如果匹配就修改8 为当前的时间
这里的英文查找到 MD5 后
而且有趣的的是,不止一个 MD5 数据库,还有一个hash数据库
,
从这 2 个不同的地方,我们就可以知道,有2 搜索方式1. 搜索方式为 md5 的时候,先获取md5 的桶ID ,再(object+ 0x60)的一个数组索引对应桶ID 的链表 下面我们接着看搜索方式为 hash 和大小的时候,
跟 MD5 搜索类似,给hash计算桶INDEX,再算表。不过是另外一个表,以下我简称这2 个表一个为HASH快查表,一个MD5 快查表。这个函数是搜索的对应的表,不过对应的表又指向相同的数据库。
MD5 快查表的一项
HASH查表快的一项
数据库:
既然知道,这里放着的是 TCJ 扫描过后的缓存,那么如果数据库中没有我们的记录,肯定是会插入的,往上继续回溯
如果没有找到对应的数据库,那么就调用 add_cache_list 添加到2 个表中去。
通过调用参数,我们可以确定最终的数据库结构和对应的大小了0×28
分析了这么多,我们写一个程序 DUMP 一下这2 份表吧
[C++] 纯文本查看 复制代码 #include <windows.h>
#include <cstdio>
#pragma pack(1)
struct SFileSig
{
DWORD murmur_hash;
DWORD file_size;
DWORD64 time;
BYTE level;
BYTE id;
DWORD md5[4];
};
#pragma pack()
struct SMd5Item
{
SMd5Item* p1;
SMd5Item* p2;
DWORD md5[4];
SFileSig* database;
};
struct SHashItem
{
SHashItem* p1;
SHashItem* p2;
DWORD murmur_hash;
DWORD file_size;
SFileSig* database;
};
struct STableMd5
{
DWORD p1;
SMd5Item* list;
};
struct STableHash
{
DWORD p1;
SHashItem* list;
};
void DumpDatabase(const SFileSig* sig) {
printf("----------------------------\n");
printf("murmur_hash:%08X\n", sig->murmur_hash);
printf("file_size:%08X\n", sig->file_size);
printf("update-time:%lu\n", sig->time);
printf("level:%02X\n", sig->level);
printf("id:%02x\n", sig->id);
printf("md5:%08X%08X%08X%08X\n", sig->md5[0], sig->md5[1], sig->md5[2], sig->md5[3]);
printf("-------------------------\n");
}
int __stdcall DllMain(_In_ void* _DllHandle, _In_ unsigned long _Reason, _In_opt_ void* _Reserved) {
if (_Reason == DLL_PROCESS_ATTACH) {
const auto md5_fast_list = reinterpret_cast<STableMd5 *>(0x0C23BC60);
for (int index = 0; index < 0x201; index++) {
if (md5_fast_list[index].list) {
for (auto iter = md5_fast_list[index].list; ; iter = iter->p1) {
if (iter->database == nullptr || iter->md5[0] == 0)break;
printf("[FastMd5List]md5:%08X%08X%08X%08X\n", iter->md5[0], iter->md5[1], iter->md5[2],
iter->md5[3]);
DumpDatabase(iter->database);
if (iter->p1 == md5_fast_list[index].list) break;
}
}
}
const auto hash_fast_list = reinterpret_cast<STableHash *>(0x0C23E488);
for (int index = 0; index < 0x201; index++) {
if (hash_fast_list[index].list) {
for (auto iter = hash_fast_list[index].list; ; iter = iter->p1) {
if (iter->p1 == hash_fast_list[index].list)
break;
printf("[FastHashList]hash:%08X ,file size:%08x\n", iter->murmur_hash, iter->file_size);
DumpDatabase(iter->database);
}
}
}
}
return 0;
}
那么tcj维护这个大表做什么呢
欲知后事如何 |