新的一年来到了,这个系列的文章也由于各种原因拖了好久,无奈地也跟着跨年了!接下来几篇重点在于URL数据文件data.dat的定位及解密过程。根据上一篇的内容,我们很容易了解到大致的定位方法及方式,现在我们就要首先从逆向源码的角度来验证一下通过API调用流程监控结果的猜想。
首先,对于文件的各种操作着手,很容易进行API断点,然后通过栈回溯的方法定位到相关的关键代码位置,这个过程相对简单,省略不表(欢迎有兴趣且不是很懂的童鞋来问);
然后,分析出通过电台索引获取密文的函数(如图1,已经加好了标签及相关注释),并逆向源码如下:
int getIDPFilePosIdx( int iIdx )
{
std::ifstream objFile( "data.idi" );
objFile.seekg( 0, std::ios::end );
int iPos = (int)objFile.tellg();
if ( -1 == iPos )
return -1;
iPos = iPos/8 - 1;
int iSeek = 0;
int iGet = 0;
if ( iPos >= 0 )
{
int iData(0);
do
{
iGet = (iPos+iSeek)/2;
objFile.seekg( iGet*8 );
objFile.read( (char*)&iData, 4 );
if ( iIdx == iData )
{
objFile.read( (char*)&iData, 4 );
objFile.close();
return iData;
}
if ( iIdx > iData)
iSeek = iGet+1;
else
iPos = iGet-1;
} while ( iPos >= iSeek );
}
objFile.close();
return -1;
}
int GetIDP_Data( int iIdx )
{
if ( iIdx > 0 )
{
std::ifstream objFile("data.idp");
objFile.seekg( 0, std::ios::end );
int iSize = (int)objFile.tellg();
objFile.seekg( 4*(iIdx-1) );
if ( objFile.tellg() < (iSize-8) )
{
int iData(0);
objFile.read( (char*)&iData, 4 );
objFile.close();
return iData;
}
}
return -1;
}
char* getDATFileData( int iIdx, int &ibuflen )
{
int iData = GetIDP_Data( iIdx );
if ( iData >= 0 )
{
int iDataGet = GetIDP_Data( iIdx+1 );
std::ifstream objFile("data.dat");
objFile.seekg( iData );
int uStrLen = iDataGet - iData -2;
char *pBuf = new char[uStrLen];
objFile.read( pBuf, uStrLen );
//get length
ibuflen = uStrLen;
objFile.close();
return pBuf;
}
return NULL;
}
运行测试结果与动态调试一致,还原结果一致:
通过对数据定位过程的分析,发现与API流程分析的结果大致相同,都是采用折半法通过电台ID进行索引定位获取位置信息,然后通过位置信息,获取对应的data.dat中的相应加密后的待解密的URLs信息。
接下来,获取了276字节长度的密文后(测试程序中VOA电台的密文),就要开始着手进行解密操作了,避开一些对文件进行格式检查完整性检查的一些函数,直接定位到关键核心的解密相关函数,如下图2,这个函数不算短,里面还还有不少功能函数在其中,工作量不算小啊~~
在分析密钥准备函数之前,先看一下定位到的解密核心函数,弄清楚到底这个Key是肿么用的,另外是怎么传过去的,还有哪些内置的Keys等等,那么首先,进入解密的核心函数——DecodeEncryptDataPartly_8Bytes,这个函数比较有意思,每次解密8个字节,循环内完成所有加密数据的解密工作。
这个8字节解密核心函数,是整个解密过程中的关键所在,也汇聚了所有相关数据及Key的要素,包括一些内置的参与运算的数据,比如说“Shagua”,作者取名的这个数据估计也是为了娱乐一下吧~:D,我们把Key准备函数放到一边,重点研究一下这个核心且关键的8字节解密函数,当然一如既往地,这个函数里面的子函数也是不少的,另外关键的那个点就是传处理后的密钥的那个,这里就不留悬念了,是通过全局(静态)变量进行的传参操作,如图:
很明显在静态状态下这个数据段里面的数据时没有初始化的,当进行动态跟踪后,很容易在PrepareKey函数执行完后,发现这个区域已经被赋值了。【应该是6个字节,图例说明数字有出入】
接下来,就针对核心的解密函数及其子函数小伙伴们进行逆向,并验证效果,先不要急,鉴于子函数较多,因此挨个挨个来,首先把他们分解一下,在DecodeEncryptDataPartly_8Bytes函数中,含三个子函数sub_525444,sub_52594C,sub_5254C4,其中sub_52594C 中又含有sub_525544、sub_525654、sub_5255D4三个子函数,一共加起来,我们需要分析还原大约六个函数,为了不让这篇文章过长看着累,在这里就割一下,在第四篇里面写六个小函数的逆向还原。