逆向序列号生成算法(三)

 接着上次的分析,继续分析下去,对于这个CrackMe,其要求是写出注册机,那么我们就根据其注册码的验证过程,进行逆向分析,首先找到最关键的一个爆破点,我们先看看这个爆破点的位置,如图1可见最后的验证关键点其实在那个MagicNum的值上面,因为用户名是我们给定的,属于已知值,而序列号则是可以由我们构造的也可以认为是已知值,唯一不确定的就是那个MagicNum了,而这个值却跟我们输入的用户名及序列号密切相关。因此切实找到:MagicNum(魔数)如图2与用户名、序列号之间的关系就是最后写出注册机的关键所在了。

逆向序列号生成算法(三)_第1张图片
(图1)

接下来我们大致看看这三者之间的关系 :最后的注册码正确与否取决于序列号的最后四个字符,即:我们设dword ptr[402168]为a,

dword ptr[40216c]为b,dword ptr[402179]为c,需要算出的最后四个字节为nResult,其必须满足,nResult的值必须都在ASCII码可打印表示范围内(PS:不然你无法输入对应的字符......),且nResult == a^b^MagicNum|0x40404040&0x77777777^c。其中的a\b\c皆可由我们构造,而MagciNum则与我们构造的这些数相关,到底有些什么关系呢?我们接下来看看MagicNum与我们构造的用户名及序列号之间的关系。

逆向序列号生成算法(三)_第2张图片

(图2)

其中的edi可以是用户名算出值亦可为序列号算出值。(此值即为Step1过程中算出的值),整个递归过程与edi值有关。通过阅读程序,我们需要找到edi与递归次数之间的直接关系,这样我们可以想办控制魔数的自增次数从而控制住魔数的值。通过在给定用户名的情况下,利用edi与魔数之间的关系,构造出满足条件的序列号(这个过程可以控制在可容忍范围内的穷举)。在分析的过程中比较棘手的地方是就是递归调用。OK,看到这里基本定位了关键点所在。我们先暂停一下,仅从分析如何快速制作出可行的注册机的角度来尝试一下,在这个角度上,首先要保证递归函数调用后的返回值ECX为1(这个时候关于递归与魔数的关系就可以不用考虑了),把这个条件分析出来。然后只用去利用:nResult == a^b^MagicNum|0x40404040&0x77777777这个值,自己去构造符合条件的剩余的序列号部分即可。例如:nResult值已知的情况下,设序列号剩下的两个字节分别为X1、X2,要求nResult^X1==X2,且X1,X2都在['0','~']这个范围内。根据这个思路可以写出如下的注册机:

// KeyGen.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include 
#include 
#include 
#include 
#pragma warning(disable:4244)

typedef unsigned long DWORD;
typedef unsigned int  UINT;

//////////////////////////////////////////////////////////////////////////
//函数声明部分
//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
//扩展用户名到16字节
void ExtendNameTo16Bytes( char *pStrName );

//验证序列号Step1
void CalculateKeyStep1( const char *pStrName, const char *pStrSerialNum, 
                        DWORD &outTempNum1, DWORD &outTempNum2 );

//抽离的递归函数
void Func( DWORD nTempNum1, DWORD nTempNum2,
           UINT &uCount, DWORD &nMagicNum );

//验证序列号Step2
bool CalculateKeyStep2( DWORD nTempNum1, DWORD nTempNum2, 
                        const char *pStrName, char *szLast8BytesSerials );

//构造最后的8字节序列号
bool GenerateCorrect8BytesSerial( const DWORD nSrc, char szOUT[] );

//N个字节字符串生成器
void GenerateNBytesStrings( const char *pStrUserName, char *pStr, 
                            const UINT nBytes );

//////////////////////////////////////////////////////////////////////////
//函数实现部分
//////////////////////////////////////////////////////////////////////////
void ExtendNameTo16Bytes( char *pStrName )
{
    assert( NULL != pStrName );
    if ( NULL == pStrName ) return;
    size_t nStrLen = strlen(pStrName);
    if ( nStrLen >= 16 ) return;
    size_t nNeedtoExtend = 16 - nStrLen;
    int nCount = 0;
    while ( nNeedtoExtend-- )
    {
        *(pStrName+nStrLen+nCount) = *(pStrName+nCount);
        ++nCount;
    }
}

void CalculateKeyStep1( const char *pStrName, const char *pStrSerialNum, 
                        DWORD &outTempNum1, DWORD &outTempNum2 )
{
    assert( NULL != pStrName );
    assert( NULL != pStrSerialNum );
    if ( NULL == pStrName || NULL == pStrSerialNum )
        return;

    outTempNum1 =  *(DWORD*)pStrName;
    outTempNum2 = *(DWORD*)(pStrName+4);
    DWORD nSerialTemp1 = *(DWORD*)pStrSerialNum;
    DWORD nSerialTemp2 = *(DWORD*)(pStrSerialNum+4);

    outTempNum1 ^= nSerialTemp1;
    outTempNum2 ^= nSerialTemp2;

    outTempNum1 &= 0x7F3F1F0F; //0111 1111-0011 1111-0001 1111-0000 1111
                               //x31=0,x23=0,x15=0,x7=0
    outTempNum2 &= 0x07030100; //0111 0000-0011 0000-0001 0000-0000 0000
                               //x31=0,x23=0,x15=0,x7=0

    for (int idx=0; idx<8; ++idx)
    {
        std::bitset<32>  vBtsEAX( outTempNum1<  vBtsEBX( outTempNum2<  vBtsR(0);
        vBtsR[7] = vBtsEAX[31];
        vBtsR[6] = vBtsEAX[23];
        vBtsR[5] = vBtsEAX[15];
        vBtsR[4] = vBtsEAX[7];
        vBtsR[3] = vBtsEBX[31];
        vBtsR[2] = vBtsEBX[23];
        vBtsR[1] = vBtsEBX[15];
        vBtsR[0] = vBtsEBX[7];
        int nValue = vBtsR.to_ulong();
        if (idx <= 3)
        {
            nValue <<= 8*(3-idx);
            outTempNum1 ^= nValue;
        }
        else
        {
            nValue <<= 8*(7-idx);
            outTempNum2 ^=nValue;
        }
    }
}

void Func( DWORD nTempNum1, DWORD nTempNum2, 
           UINT &uCount, DWORD &nMagicNum )
{
    if ( uCount <= 0x80 ) return;
    UINT uTMCount = uCount;
    DWORD nValue = nTempNum1;
    uCount &= 0xff;       //取最低一个字节,根据下面程序的执行
    if ( uCount > 8 )     //其实为取CL的低四位.
    {
        nValue = nTempNum2;
        uCount >>= 4;     //取CL高4位.
    }
    ////////////////////////////////////////////////
    //nValue为DWORD,占4个字节,如下图示:
    //|---|---|---|---|
    //| 1 | 2 | 3 | 4 |
    //|___|___|___|___|
    //
    UINT uRotateCount = (UINT)(log((float)uCount)/log(2.0f));
    switch( uRotateCount%4 )
    {
    case 1:
        nValue >>= 16; //|---|
        break;         //| 2 |
                       //|___|
    case 2:
        nValue >>= 8;  //|---|
        break;         //| 3 |
                       //|___|
    case 3:
        break;         //|---|
                       //| 4 |
                       //|___|
    default:
        nValue >>= 24; //|---|
        break;         //| 1 |
                       //|___|
    }
    uCount = (uTMCount&0xff00)>>8;
    nValue &=uCount;
    uCount = 0x80;
    while ( uCount&nValue ? 1:uCount>>=1 )
    {
        if ( !(uCount&nValue) ) continue;
        nValue ^= uCount;
        uCount ^= ((uCount&0xff)<<8);
        uTMCount &= 0xff00;
        uTMCount ^= uCount;
        ++nMagicNum;
        Func( nTempNum1, nTempNum2, uTMCount, nMagicNum );
        uCount = 0x80;
    }
    uCount = uTMCount; //In order to retrieve the return value
}

bool GenerateCorrect8BytesSerial( const DWORD nSrc, char szOUT[] )
{
    if ( NULL == szOUT )
        return false;

    char cCount = '0';
    char cV1(0);
    char cV2(0);
    char cV3(0);
    char cV4(0);
    char cS1(0);
    char cS2(0);
    char cS3(0);
    char cS4(0);
    while ( cCount <= '~' )
    {
        //第一字节
        cV1 = cCount^((nSrc&0xff000000)>>24);
        if ( 0 == cS1 && cV1 >= '0' && cV1 <= '~' )
            cS1 = cCount;

        //第二字节
        cV2 = cCount^((nSrc&0xff0000)>>16);
        if ( 0 == cS2 &&cV2 >= '0' && cV2 <= '~' )
            cS2 = cCount;

        //第三字节
        cV3 = cCount^((nSrc&0xff00)>>8);
        if ( 0 == cS3 && cV3 >= '0' && cV3 <= '~' )
            cS3 = cCount;

        //第四字节
        cV4 = cCount^((nSrc&0xff));
        if ( 0 == cS4 &&cV4 >= '0' && cV4 <= '~' )
            cS4 = cCount;

        if ( 0 != cS1 && 0 != cS2 && 0 != cS3 && 0 != cS4 )
        {
            szOUT[0] = cS1;
            szOUT[1] = cS2;
            szOUT[2] = cS3;
            szOUT[3] = cS4;
            szOUT[4] = cV4;
            szOUT[5] = cV3;
            szOUT[6] = cV2;
            szOUT[7] = cV1;
            return true;
        }
        ++cCount;
    }
    return false;
}

bool CalculateKeyStep2( DWORD nTempNum1, DWORD nTempNum2, 
                        const char *pStrName, char *szLast8BytesSerials )
{
    if ( NULL == szLast8BytesSerials || NULL == pStrName )
        return false;

    DWORD nMagicNum = 0xfedcba98;
    UINT  uCount = 0xff01;
    Func( nTempNum1, nTempNum2, uCount,nMagicNum );
    if ( 1 == uCount )
    {
        nTempNum1 = *(DWORD*)(pStrName+8);
        nTempNum2 = *(DWORD*)(pStrName+ 0xc);
        nTempNum1 ^= nTempNum2; 
        nTempNum1 ^= nMagicNum;
        nTempNum1 |= 0x40404040;
        nTempNum1 &= 0x77777777;
        return GenerateCorrect8BytesSerial( nTempNum1, szLast8BytesSerials );
    }
    return false;
}


void GenerateNBytesStrings( const char *pStrUserName, char *pStr, 
                            const UINT nBytes )
{
    assert( NULL != pStr );
    if( NULL ==  pStr ) return;
    
    while ( pStr[nBytes-1] <= '~' )
    {
        //////////////////////////////////////////////////////////////////////////
        //Add codes for what you want to deal with
        DWORD nT1(0), nT2(0);
        CalculateKeyStep1( pStrUserName, pStr, nT1, nT2 );
        char szLast8BytesSerials[9] = { 0 };
        if ( CalculateKeyStep2( nT1, nT2, pStrUserName, szLast8BytesSerials ) )
        {
            printf("%s's Serial is: %s%s\n", pStrUserName, pStr, szLast8BytesSerials );
        }
          //////////////////////////////////////////////////////////////////////////
        ++pStr[0];
        for(UINT idx=0; idx '~' )
            {
                pStr[idx] = '0';
                pStr[idx+1]++;
            }
        }
    }
}


int _tmain(int argc, _TCHAR* argv[])
{
    const char szHint0[] = "***********This is the KeyGen for CycleCrackMe***********";
    const char szHint1[] = "Please Input your name:";
    std::cout << szHint0 << std::endl;
    std::cout << szHint1 << std::endl;
    char szUserName[32] = { 0 };
    std::cin >> szUserName;
    std::cout << "Please Waiting......" << std::endl;
    ExtendNameTo16Bytes( (char*)szUserName );
    char szSerialFirst8Bytes[] = "00000000";
    GenerateNBytesStrings( szUserName, szSerialFirst8Bytes, 8 );
    system("pause");
    return 0;
}
测试结果如下:

逆向序列号生成算法(三)_第3张图片

此次仅仅是利用了注册算法的进行的部分逆运算获取的注册码,且在程序实现过程中没有达成对所有存在的注册码的穷举,因此,需要对注册机程序做进一步的修改,使之达到既定的要求。

To be continued.......


你可能感兴趣的:(逆向C++,Windows平台相关)