该反外挂系统是某游戏公司的,只是好奇如有得罪,请谅解及联系版主删除!

pig x因为听别人说有一牛b的反外挂系统,故而好奇分析之,由于没有壳让我十指大动,逐一分析之。

看过后,逐步了解其工作原理,现将其工作原理简单阐述下:
收到服务器返回的包,要求客户端根据包的内容进行解析和执行,将正确的结果返回服务,以到达验证的目的。
因为服务器返回的执行代码,让客户端正确加载dll后执行,所以服务器可以让你客户端执行它想检查外挂或者
其它意图的程序哦?可不可能让你执行木马?理论上是可以的,但是估计官方不会自己扇自己耳光吧!

1,首先是初始化数据,服务器返回第一包,里面包含即将要调用的dll数据和函数名称,目的是让后面的要调用的
代码作前期初始化工作。每个特殊含义的数据,都有自己的ID,后面根据ID进行解析出正确的fuc。

贴上解析数据,并进行数据解析的函数:复制内容到剪贴板代码:
DWORD _CallRetEax(LPBYTE pData)
{        

        DWORD        dwRetEax = 0xFFFFFFFF;
        INT                nPox = 0;
        CHAR*        pRecvBuffer = NULL ;
        INT                nLen;
        RetPoxLen pRetPoxLen;
        INT                n1stType = 0;
        INT                n;
        INT                n1stLen = 0;
        INT                nLastType = 0;
        INT                nTypeID = 0;
        INT                n1stTypeLen = 0;
        INT                n2scTypeLen = 0;
        INT                n2sc = 0;
        INT                nLastSum = 0;
        INT                nLast = 0;
        INT                nLastLen = 0;
        WORD        wInsertId = 0;
        CHAR*        pCallBuffer= NULL;
        DWORD        CallAddr;
        WORD        wFixPox = 0;
        WORD        wFixId = 0;
        WORD        wFindFixId = 0;

        GS_CHECKPTR(pData);
        pRecvBuffer = (CHAR*)(pData);

        nPox = 0;
        nLen = *(WORD*)(pRecvBuffer + nPox);
        nPox += 8;

        pRetPoxLen =  _RetLen(pRecvBuffer + nPox);
        nPox += pRetPoxLen.nPox;
        //大类个数
        n1stType = pRetPoxLen.nLen;

        for (n = 0; n < n1stType ; n++)
        {
                nLastLen = 0;

                pRetPoxLen =  _RetLen(pRecvBuffer + nPox);
                nPox += pRetPoxLen.nPox;
                //大类长度
                n1stLen = pRetPoxLen.nLen;

                        //大类长度
                        n1stTypeLen = *(WORD*)(pRecvBuffer + nPox);
                        nLastLen +=2;
                        n1stTypeLen -= 2;
                        //ID
                        nTypeID = *(WORD*)(pRecvBuffer + nPox + nLastLen);
                        nLastLen +=2;
                        wInsertId = _FidUseId(nTypeID);
                        if(wInsertId == 0xFFFF)
                        {
                                wInsertId = _FidNoUseId();
                        }
                        m_szRetCall[wInsertId].wId = nTypeID;
                        n1stTypeLen -= 2;
                        //读法类型
                        nLastType = *(WORD*)(pRecvBuffer + nPox + nLastLen);
                        nLastLen +=2;
                        n1stTypeLen -= 2;
                        switch(nLastType)
                        {
                        case 0://读STRING    初始化API名称+数据
                                nPox += nLastLen;
                                memcpy(m_szRetCall[wInsertId].szRetBuffer ,pRecvBuffer + nPox, n1stTypeLen);
                                m_szRetCall[wInsertId].pRetBuffer = m_szRetCall[wInsertId].szRetBuffer;
                                nPox += n1stTypeLen;
                                break;
                        case 1://DWORD+DWORD 初始化API对应关系
                                nPox += nLastLen;
                                m_szRetCall[wInsertId].w1stId = *(WORD*)(pRecvBuffer + nPox);
                                nPox +=2;
                                m_szRetCall[wInsertId].w2secId = *(WORD*)(pRecvBuffer + nPox);
                                nPox +=2;
                                break;
                        case 2://开始执行CALL
                                //nPox += nLastLen;
                                //dwRetEax = _CallBufferId(nTypeID);
                                __logger.Trace("RetEax = %p \n", m_pHook->_dwRetvalue);
                                __logger.Trace("-------------------------------------\n");
                                __logger.Trace("END\n\n");
                                __logger.Fflush();
                                break;
                        default://对CALL开始修正
                                //初始化传过来的API
                                _InitApiDate();
                                //开始修正CALL BUFFER

                                if(nLastLen < nLastType )
                                {
                                        //读出要修正地方的个数
                                        nLastSum = *(WORD*)(pRecvBuffer + nPox + nLastLen);
                                        nLastLen += 2;
                                        n1stTypeLen -= 2;

                                        //循环读出修改的内容
                                        for(nLast = 0; nLast < nLastSum; nLast++)
                                        {
                                                wFixPox = *(WORD*)(pRecvBuffer + nPox + nLastLen);
                                                nLastLen +=2;
                                                wFixId =  *(WORD*)(pRecvBuffer + nPox + nLastLen);
                                                nLastLen +=2;
                                                n1stTypeLen -= 4;
                                                //修改CALL BUFFER的API地址
                                                wFindFixId = _FidUseId(wFixId);
                                                if(wFindFixId != 0xFFFF)
                                                {
                                                        //修改为0的地方
                                                        if(m_szRetCall[wFindFixId].dwApiAddr != 0)
                                                        {
                                                                if( *(BYTE*)(pRecvBuffer + nPox + wFixPox - 1 ) == 0x0E8)
                                                                {
                                                                        //CALL XXXXX
                                                                        CallAddr = (DWORD)m_szCallBuffer;
                                                                        *(DWORD*)(pRecvBuffer + nPox + wFixPox) = (DWORD)(m_szRetCall[wFindFixId].dwApiAddr - CallAddr -(wFixPox  - nLastSum*4 - 8) - 4);
                                                                        __logger.Trace("Pox:%p    CALL %s \n",wFixPox  - nLastSum*4 - 8, m_szRetCall[_FidUseId(m_szRetCall[wFindFixId].w2secId)].szRetBuffer);
                                                                        __logger.Fflush();
                                                                }else if( *(WORD*)(pRecvBuffer + nPox + wFixPox - 2 ) == 0x15FF)
                                                                {
                                                                        //CALL [XXXXX]
                                                                        *(DWORD*)(pRecvBuffer + nPox + wFixPox) = (DWORD)&(m_szRetCall[wFindFixId].dwApiAddr);
                                                                        __logger.Trace("Pox:%p    CALL %s \n",wFixPox  - nLastSum*4 - 8, m_szRetCall[_FidUseId(m_szRetCall[wFindFixId].w2secId)].szRetBuffer);
                                                                        __logger.Fflush();
                                                                }else if( *(BYTE*)(pRecvBuffer + nPox + wFixPox - 2 ) == 0x8B || 
                                                                              *(BYTE*)(pRecvBuffer + nPox + wFixPox - 1 ) == 0xA1 )
                                                                {
                                                                        //MOV RB32 , [XXXXX]
                                                                        *(DWORD*)(pRecvBuffer + nPox + wFixPox) = (DWORD)&(m_szRetCall[wFindFixId].dwApiAddr);
                                                                        __logger.Trace("Pox:%p    MOV RB32, %s \n",wFixPox  - nLastSum*4 - 8, m_szRetCall[_FidUseId(m_szRetCall[wFindFixId].w2secId)].szRetBuffer);
                                                                        __logger.Fflush();
                                                                }else if( *(BYTE*)(pRecvBuffer + nPox + wFixPox - 1 ) == 0x0B8 ||
                                                                              *(BYTE*)(pRecvBuffer + nPox + wFixPox - 1 ) == 0x0B9 ||
                                                                                  *(BYTE*)(pRecvBuffer + nPox + wFixPox - 1 ) == 0x0BA ||
                                                                                  *(BYTE*)(pRecvBuffer + nPox + wFixPox - 1 ) == 0x0BB ||
                                                                                  *(BYTE*)(pRecvBuffer + nPox + wFixPox - 1 ) == 0x0BC ||
                                                                                  *(BYTE*)(pRecvBuffer + nPox + wFixPox - 1 ) == 0x0BD ||
                                                                                  *(BYTE*)(pRecvBuffer + nPox + wFixPox - 1 ) == 0x0BE ||
                                                                                  *(BYTE*)(pRecvBuffer + nPox + wFixPox - 1 ) == 0x0BF
                                                                                  )
                                                                {
                                                                        //MOV RB32 , XXXXX
                                                                        *(DWORD*)(pRecvBuffer + nPox + wFixPox) = (DWORD)&(m_szRetCall[wFindFixId].dwApiAddr);
                                                                        __logger.Trace("Pox:%p    MOV RB32, %p = %p \n",wFixPox  - nLastSum*4 - 8, m_szRetCall[_FidUseId(m_szRetCall[wFindFixId].w2secId)].szRetBuffer);
                                                                        __logger.Fflush();
                                                                }

                                                        }else
                                                        {
                                                                //此为读常量,非API地址的情况 (地址的地址)
                                                                *(DWORD*)(pRecvBuffer + nPox + wFixPox) = (DWORD)&(m_szRetCall[wFindFixId].pRetBuffer);
                                                            __logger.Trace("Pox:%p    [%p] = %p \n",wFixPox  - nLastSum*4 - 8,m_szRetCall[wFindFixId].szRetBuffer,*(DWORD*)m_szRetCall[wFindFixId].szRetBuffer);
                                                                __logger.Fflush();
                                                        }

                                                }else
                                                {
                                                        __logger.Trace("Pox:%p    CALL [addr] = %p \n", wFixPox  - nLastSum*4 - 8, wFixId);
                                                        __logger.Fflush();
                                                        dwRetEax = 0;
                                                        return dwRetEax;
                                                }
                                                
                                        }
                                }
                                //余下为CALL BUFFER
                                nPox += nLastLen;
                                ZeroMemory(m_szCallBuffer,sizeof(m_szCallBuffer));
                                __logger.Trace("\nCallBufferAddr: %p    CallBufferLen: %p \n\n", &m_szCallBuffer, n1stTypeLen);
                                memcpy(m_szCallBuffer ,pRecvBuffer + nPox, n1stTypeLen);
                                m_szRetCall[wInsertId].pCallBufferAddr = (CHAR*)m_szCallBuffer;
                                nPox += n1stTypeLen;
                                break;
                        }
        }

GS_EXIT:;
        return dwRetEax;
}2,经过上面的解析后,根据数据进行函数初始化复制内容到剪贴板代码:
void _InitApiDate()
{
        HINSTANCE hInstLibrary;
        CHAR*        cDllName;
        CHAR*        cApiName;
        INT                nFindId;

        for(INT i = 0;i <= CALL_BUFFER_NO; i++)
        {
                if( m_szRetCall[i].wId != 0 &&
                        m_szRetCall[i].w1stId != 0 && 
                        m_szRetCall[i].dwApiAddr == 0 &&
                        m_szRetCall[i].w2secId != 0 
                        )
                {
                        nFindId = _FidUseId(m_szRetCall[i].w1stId);
                        cDllName = m_szRetCall[nFindId].szRetBuffer;
                        hInstLibrary = LoadLibrary(cDllName);
                        nFindId = _FidUseId(m_szRetCall[i].w2secId);
                        cApiName = m_szRetCall[nFindId].szRetBuffer;
                        m_szRetCall[i].dwApiAddr = (DWORD)GetProcAddress(hInstLibrary,cApiName);
                        FreeLibrary(hInstLibrary);                        
                }
        }
        
        return;
}3,根据
case 2://开始执行CALL
去调用要执行的函数代码复制内容到剪贴板代码:
INT        _CallBufferId(DWORD dwId)
{
        CHAR*        pCallBuffer;
        INT                nRet;
        DWORD        dwFindUseId;
        BOOL        bRetPortect;
        DWORD        dwOldType;

        pCallBuffer = (CHAR*)m_szCallBuffer;
        dwFindUseId = (DWORD)_FidUseId(dwId);
        
        bRetPortect = VirtualProtect(m_szCallBuffer, 8190, PAGE_EXECUTE_READWRITE, &dwOldType);
        
        if(m_szRetCall[dwFindUseId].pCallBufferAddr != 0)
        {
                __asm
                {
                        pushad
                                pushfd
                                        mov eax, pCallBuffer
                                        call eax
                                        mov nRet, eax
                                popfd
                        popad
                }
        }
                
        bRetPortect = VirtualProtect(m_szCallBuffer, 8190, dwOldType, &dwOldType);

        return nRet;
}这里返回的eax就是反外挂需要的数据,服务器根据此数据来判断你是否作弊。到此,这反外挂系统简单流程就是如此了。
专门看了下送来执行的几个函数,发现它们在检查这些数据:
1)代码crc
2)窗体判断
3)hookdll判断
这些都是反外挂的小功能fuc,当然官方想加什么就加什么,应该非常好diy的,故该游戏的外挂比较少,也是该系统的功能,
浅析到此为止,如有深入的分析和见解,请发到论坛上来吧!