首页
社区
课程
招聘
[原创]浅析国内某反外挂系统!
发表于: 2008-1-9 10:14 23891

[原创]浅析国内某反外挂系统!

2008-1-9 10:14
23891

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

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的,故该游戏的外挂比较少,也是该系统的功能,
浅析到此为止,如有深入的分析和见解,请发到论坛上来吧!


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 7
支持
分享
最新回复 (38)
雪    币: 193
活跃值: (1389)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
2
看到了Call eax让我想到了某某公司了
2008-1-9 10:21
0
雪    币: 1946
活跃值: (248)
能力值: (RANK:330 )
在线值:
发帖
回帖
粉丝
3
顶了再好好学习一下

原来是这东西,呵呵。。。
2008-1-9 10:39
0
雪    币: 223
活跃值: (70)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
4
很黄很暴力。。
2008-1-9 10:57
0
雪    币: 66
活跃值: (15)
能力值: ( LV9,RANK:330 )
在线值:
发帖
回帖
粉丝
5
确实, 非常好diy, 全民diy
2008-1-9 11:02
0
雪    币: 87
活跃值: (47)
能力值: ( LV12,RANK:250 )
在线值:
发帖
回帖
粉丝
6
学习了。
call eax这东西比较讨厌,无法静态分析。
2008-1-9 11:34
0
雪    币: 6075
活跃值: (2236)
能力值: (RANK:1060 )
在线值:
发帖
回帖
粉丝
7
跟某游戏的XXX包有一拼。
2008-1-9 15:09
0
雪    币: 6
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
具体的说..这是哪个游戏的?
2008-1-10 02:17
0
雪    币: 160
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
不喜欢游戏,学习一下
2008-1-10 08:36
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
恩。看过以后觉得不错。顶上去吧~
2008-1-10 10:32
0
雪    币: 208
活跃值: (40)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
11
call eax,有模仿yb的嫌疑啊,看来还是抄韩国人的
2008-1-10 15:07
0
雪    币: 231
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
学习。。。。
支持
2008-1-10 23:35
0
雪    币: 427
活跃值: (412)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
1)代码crc
2)窗体判断
3)hookdll判断

对懂驱动的人,用处不大吧,甚至可以开发出通用破解库。
2008-1-11 13:01
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
什么游戏的啊?
2008-1-13 17:53
0
雪    币: 206
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
该游戏,服务器经常会发些检测外挂的程序代码来,搂主发的这是客户段对这些代码操作过程,
这样服务器可以自由变换检测的代码让做外挂的防不胜防!
2008-1-14 20:16
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
难道就是传说中的完美XXX
2008-1-15 16:40
0
雪    币: 427
活跃值: (412)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
这在CS的CD反外挂早用过,不动用驱动,就用随机混淆技术,产生不同样本,此反外挂基本没有什么作为。
2008-1-15 19:04
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
同问,哪个公司的????
2008-1-15 20:52
0
雪    币: 203
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
不知道会不会是ZT或MH..
2008-1-16 07:15
0
雪    币: 740
活跃值: (952)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
20
问一下:call eax是怎么实现的?eax记得call不了(在MASM里面)……
2008-1-30 18:26
0
雪    币: 6075
活跃值: (2236)
能力值: (RANK:1060 )
在线值:
发帖
回帖
粉丝
21
要么你记错了,要么你写错了,如果都不是,那就assume eax:nothing
2008-1-30 19:38
0
雪    币: 740
活跃值: (952)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
22
感激不尽:)
2008-1-30 19:43
0
雪    币: 212
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
不用驱动,人家检查HOOKDLL, 你混淆什么哦。
2008-1-30 20:53
0
雪    币: 321
活跃值: (271)
能力值: ( LV13,RANK:1050 )
在线值:
发帖
回帖
粉丝
24
顶起来再慢慢学习
2008-1-30 22:35
0
雪    币: 215
活跃值: (19)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
完美最近在赤壁里在用了这个吧。。不过感觉没多大用...
2008-2-5 11:53
0
游客
登录 | 注册 方可回帖
返回
//