众所周知,PCHunter是一款前沿的AntiRootkit软件,是我们查看系统信息的好伙伴。然而在不久前,我发现之前申请试用的授权已经到期了,我发现这一点也不cool。这才有了这篇文章.


软件64为程序,能够正常被x64dbg加载或附加,无反调试功能
软件采用的是license文件注册的方式,license的文件名是pchunter.ek,尝试用notepad++打开,发现一串长度为256的注册码
可以合理猜测注册方式是读license文件里的注册码,然后进行一系列的解密运算得到授权时间等相关信息,以供程序进行授权检测。那我们的方向就很简单了,对程序下CreateFile 断点,使程序断在参数是license路径的地方,通过栈回溯,发现在0x000000014007282C处调用,之后接着调用GetFileSize、ReadFile等API。很明显,我们找到关键位置了。

那我不装了,直接上IDA吧!
通过动静结合的分析,我锁定了验证license的函数在 sub_14007C570 里。

其中sub_140072720从授权文件读取0x200个字节并做了相关的运算,v21从0x200变成0x100,但是sub_1400435F0 进行解密操作的时候buffer还是原来的,并没有截断的操作。因此我没有具体分析sub_140072720函数,有兴趣的同学可以试着分析一下,看看里面做了什么运算。我重点分析的是sub_1400435F0函数。分析情况如下:
上面就是解密的相关流程,至于你问我为什么知道第二部分是AES解密,我只能说我在sub_140042B70里面发现了下面这一段特征
据说是一种AES 128 非标准实现的特征。
并且分析的过程中也没有发现iv的相关参数。那抱着试一试的心态,我就尝试用AES_ECB模式进行解密,还真让我解出来了。
程序buffer解密前后对比如下:
解密前:

解密后:

通过分析,buffer[0x50]开始的16个字节的数据就是授权时间的信息。这个是UTC格式的时间,可以从验证授权时间的逻辑里面调用的API得到这个结论。
讲讲思路吧。那就是对原授权解密的buffer中的buffer[50]做修改大小0x10的数据,然后用原密钥"ShouJiErShiSiShi"以 AES ECB模式进行加密。最后逆向第一轮解密的算法,生成符合要求的序列号就可以了。
这里就不放代码了,有兴趣的就自己分析写注册机,没有兴趣的就用我生成的授权文件吧。 
PS:AES加密可以用openssl,之前面试的时候面试官问我有了解或者用过这个库吗,我说没有,然而现在我可以说我用过了Zzz。
没有参考,全靠自己分析。
B63E065F31A65170C7DD99538A7AB937CC9742B392A81E49327C26E74FB6F10FCCFF22E0278F11685E7B04010B3BAB00CCFF22E0278F11685E7B04010B3BAB00400AAB14BCF37048C028C5F498E990F44DF556BC4F347975F048AFCBBB87942A1722F48B7784D578693ED0C055B3EC878A096DEADC93133D26CE32DBAC9B9487
B63E065F31A65170C7DD99538A7AB937CC9742B392A81E49327C26E74FB6F10FCCFF22E0278F11685E7B04010B3BAB00CCFF22E0278F11685E7B04010B3BAB00400AAB14BCF37048C028C5F498E990F44DF556BC4F347975F048AFCBBB87942A1722F48B7784D578693ED0C055B3EC878A096DEADC93133D26CE32DBAC9B9487
__int64 __fastcall sub_1400435F0(__int64 pInputbuffer, int a2, FILETIME *a3, _DWORD *a4, FILETIME *a5, _DWORD *a6)
{
__int64 nKeyLen; // rbx
char *v10; // rax
__int64 v11; // rcx
int nIndex; // er10
char *pOut; // r8
unsigned int i; // er9
BYTE v15; // dl
unsigned __int8 v16; // cl
__int64 result; // rax
int pOutBuffer[124]; // [rsp+20h] [rbp-358h] BYREF
DWORD aKey[8]; // [rsp+210h] [rbp-168h] BYREF
char v20[80]; // [rsp+230h] [rbp-148h] BYREF
DWORD v21; // [rsp+280h] [rbp-F8h]
int v22; // [rsp+284h] [rbp-F4h]
DWORD v23; // [rsp+288h] [rbp-F0h]
int v24; // [rsp+28Ch] [rbp-ECh]
nKeyLen = -1i64;
if ( a2 != 0x100 || !pInputbuffer ) //对输入的参数进行验证
return 0xFFFFFFFFi64;
//接下来这一段 Memset 0 一块0x80的空间
v10 = v20;
v11 = 2i64;
do
{
*(_QWORD *)v10 = 0i64;
*((_QWORD *)v10 + 1) = 0i64;
*((_QWORD *)v10 + 2) = 0i64;
v10 += 0x40;
*((_QWORD *)v10 - 5) = 0i64;
*((_QWORD *)v10 - 4) = 0i64;
*((_QWORD *)v10 - 3) = 0i64;
*((_QWORD *)v10 - 2) = 0i64;
*((_QWORD *)v10 - 1) = 0i64;
--v11;
}
while ( v11 );
// 1)第一次解密
//对读进来的0x200大小的buffer进行第一次解密运算 得到0x80大小的buffer1,并且放到上面的初始化的空间中
*(_DWORD *)v10 = 0;
nIndex = 0;
pOut = v20;
for ( i = 1; i < 0x101; i += 2 )
{
v15 = *(_BYTE *)((unsigned int)(2 * nIndex) + pInputbuffer);
if ( (unsigned __int8)(v15 - '0') > 9u )
{
if ( (unsigned __int8)(v15 - 'A') <= 5u )
v15 -= 0x37;
}
else
{
v15 -= '0';
}
if ( v15 > 0xFu )
break;
v16 = *(_BYTE *)(i + pInputbuffer);
*pOut = 16 * v15;
if ( (unsigned __int8)(v16 - '0') > 9u )
{
if ( (unsigned __int8)(v16 - 'A') <= 5u )
v16 -= 55;
}
else
{
v16 -= '0';
}
if ( v16 > 0xFu )
break;
*pOut |= v16;
++nIndex;
++pOut;
}
if ( nIndex != 128 )
return 0xFFFFFFFFi64;
// 2)AES解密
//初始化AES解密的KEY
strcpy((char *)aKey, "ShouJiErShiSiShi");
BYTE1(aKey[4]) = 0;
HIWORD(aKey[4]) = 0;
sub_140043040();
//计算Key的长度
do
++nKeyLen;
while ( *((_BYTE *)aKey + nKeyLen) );
//AES ECB 模式解密
sub_140042B70((DWORD *)pOutBuffer, aKey, nKeyLen);
sub_1400411E0((unsigned int *)pOutBuffer, (__int64)v20, (__int64)v20, 0x80u);
result = 0i64;
//获取授权时间
a3->dwLowDateTime = v21;
*a4 = v22;
a5->dwLowDateTime = v23;
*a6 = v24;
return result;
}
__int64 __fastcall sub_1400435F0(__int64 pInputbuffer, int a2, FILETIME *a3, _DWORD *a4, FILETIME *a5, _DWORD *a6)
{
__int64 nKeyLen; // rbx
char *v10; // rax
__int64 v11; // rcx
int nIndex; // er10
char *pOut; // r8
unsigned int i; // er9
BYTE v15; // dl
unsigned __int8 v16; // cl
__int64 result; // rax
int pOutBuffer[124]; // [rsp+20h] [rbp-358h] BYREF
DWORD aKey[8]; // [rsp+210h] [rbp-168h] BYREF
char v20[80]; // [rsp+230h] [rbp-148h] BYREF
DWORD v21; // [rsp+280h] [rbp-F8h]
int v22; // [rsp+284h] [rbp-F4h]
DWORD v23; // [rsp+288h] [rbp-F0h]
int v24; // [rsp+28Ch] [rbp-ECh]
nKeyLen = -1i64;
if ( a2 != 0x100 || !pInputbuffer ) //对输入的参数进行验证
return 0xFFFFFFFFi64;
//接下来这一段 Memset 0 一块0x80的空间
v10 = v20;
v11 = 2i64;
do
{
*(_QWORD *)v10 = 0i64;
*((_QWORD *)v10 + 1) = 0i64;
*((_QWORD *)v10 + 2) = 0i64;
v10 += 0x40;
*((_QWORD *)v10 - 5) = 0i64;
*((_QWORD *)v10 - 4) = 0i64;
*((_QWORD *)v10 - 3) = 0i64;
*((_QWORD *)v10 - 2) = 0i64;
*((_QWORD *)v10 - 1) = 0i64;
--v11;
}
while ( v11 );
// 1)第一次解密
//对读进来的0x200大小的buffer进行第一次解密运算 得到0x80大小的buffer1,并且放到上面的初始化的空间中
*(_DWORD *)v10 = 0;
nIndex = 0;
pOut = v20;
for ( i = 1; i < 0x101; i += 2 )
{
v15 = *(_BYTE *)((unsigned int)(2 * nIndex) + pInputbuffer);
if ( (unsigned __int8)(v15 - '0') > 9u )
{
if ( (unsigned __int8)(v15 - 'A') <= 5u )
v15 -= 0x37;
}
else
{
v15 -= '0';
传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!