首页
社区
课程
招聘
4
[原创]第2弹
发表于: 2016-2-19 16:50 17970

[原创]第2弹

2016-2-19 16:50
17970

warning:文中贴有大量代码。
前言:
  上次说了一种动态的检测方案,这次则说说网游中比较常见的反外挂解决方案吧。一般来说有些公司有钱有技术,投入人力物力自己研发一套反外挂系统出来自己用。而有些公司技术实力没这么强,没有相关的专业人员,就会购买现成的反外挂系统来使用,比如常见的nProtect GameGuard,HackShield,XignCode3等等。又有些公司会出于技术沉淀或某些原因,既会购买成熟的反外挂系统,也会投入精力自己进行研发反外挂系统。今天要讲的就是一个基于nProctect + 自研发反外挂 的双层反外挂系统。
正文:
  开始之前有必要谈谈nProctect,这个反外挂系统是韩国人开发的,目前应该来说应算得上流行的吧,我先简述下这东西是怎么用的。首先你自己得搭建一个用于他更新版本的HTTP服务器,然后把FTP账户密码发给他们的技术人员,他们会往上面上传网页文件,等到他们上传好后,他们的人会通知你叫你把client跟server端的编译环境信息发给他们,他们会发把用于client跟server用的lib文件还有ini文件发给你,然后就可以开始写代码测试啦。
如果不考虑np的cs模块的话,它的开启开始比较简单的,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
//////////////////////////////////////////////////////////////////////////////////
bool CProtectGameGuard::initializeGameGuard()
{
    // 不要启动GameGuard
    std::string cmdline = my_t2utf8(GetCommandLine());
    bool isDebugMode = cmdline.find(" nonp") != std::string::npos;
    if ( isDebugMode==true )
    {
        return true;
    }
 
    //ShowWindow((HWND)gGlobalClient->getHWND(),SW_HIDE);
 
    mNPGameLib = new CNPGameLib(_T("DanXing"));
    DWORD dwResult = mNPGameLib->Init();
 
    if (dwResult != NPGAMEMON_SUCCESS)
    {
        // Result of GameGuard initialization error
        switch (dwResult)
        {
        case NPGAMEMON_ERROR_EXIST:        
            break;
        case NPGAMEMON_ERROR_GAME_EXIST:
            break;
        case NPGAMEMON_ERROR_INIT:
            break;
        case NPGAMEMON_ERROR_AUTH_GAMEGUARD:
            break;
        case NPGAMEMON_ERROR_NFOUND_GG:
            break;
        case NPGAMEMON_ERROR_AUTH_INI:
            break;
        case NPGAMEMON_ERROR_NFOUND_INI:
            break;
        case NPGAMEMON_ERROR_CRYPTOAPI:
            break;
        case NPGAMEMON_ERROR_EXECUTE:
            break;
        case NPGAMEMON_ERROR_ILLEGAL_PRG:
            break;
        case NPGMUP_ERROR_ABORT:
            break;
        case NPGMUP_ERROR_CONNECT:
            break;
        case NPGAMEMON_ERROR_GAMEGUARD:
            break;
        case NPGMUP_ERROR_PARAM:
            break;
        case NPGMUP_ERROR_INIT:
            break;
        case NPGMUP_ERROR_DOWNCFG:
            break;
        case NPGMUP_ERROR_AUTH:
            break;
        case NPGAMEMON_ERROR_NPSCAN:
            break;
        case NPGG_ERROR_COLLISION:
            break;
        default:
            break;
        }  
 
        char msg[512];
        sprintf( msg,_GT("nProtect GameGuard 初始化产生错误:%d"),dwResult );
        SendLogToServer( dwResult,msg,strlen(msg) );
        MessageBox( (HWND)gGlobalClient->getHWND(),my_utf82t(msg), my_utf82t(_GT("初始化失败")),MB_OK );
 
        char faq_url[256];
        sprintf( faq_url,"http://danxing/service/gameguard_faq.html#A%d",dwResult );
        shellExecute( "open",faq_url );
 
        g_pApp->exit();
 
        return false;
    }
 
    mNPGameLib->SetHwnd( (HWND)gGlobalClient->getHWND() );
    mNPGameLib->Send(my_utf82t(GetMacAddress()));
 
    gGlobalClient->getTimerAxis()->SetTimer( 0,100,this );
    gGlobalClient->getMessageDispatch()->registerMessageHandler( MSG_MODULEID_GAMEGUARD,this );
    return true;
}
1
2
3
4
5
6
7
7.1.2.为什么需要CS认证
  
    当GameGuard 被强行终止,游戏用户试图运用被修改的客户端连接服务器时;
    当GameGuard 因篡改后的客户端而无法运行时;
    当黑客程序连接服务器而非客户端连接服务器时;
    当较老GameGuard版本和黑客程序同时运行,GameGuard不能更新时。
    服务器内安装CS认证后,能够确认GameGaurd是否正常运行,并最终解决以上四项漏洞。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
//////////////////////////////////////////////////////////////////////////////////
void CProtectGameGuard::OnTimer( unsigned long dwTimerID )
{
    if ( mNPGameLib==0 )
    {
        return ;
    }
 
    // 检查错误
    if ( GetTickCount()-mLastCheckTime>1000*10 )
    {
        mLastCheckTime = GetTickCount();
        DWORD dwReturn = mNPGameLib->Check();
 
        if (dwReturn != NPGAMEMON_SUCCESS)
        {
            char msg[256];
            sprintf( msg,"npgl.Check() fail.error code=%d",dwReturn);
            MessageBox((HWND)gGlobalClient->getHWND(),my_utf82t(msg),_T("error"),MB_OK);
            g_pApp->exit();
        }
    }
 
    // 弹出错误信息
    if ( mErrorCode!=0 )
    {
        switch (mErrorCode)
        {
        case NPGAMEMON_COMM_ERROR:
            MessageBox( (HWND)gGlobalClient->getHWND(),my_utf82t(_GT("nProtect GameGuard非正常关闭")),my_utf82t(_GT("防外挂保护")),MB_OK );
            break;
        case NPGAMEMON_INIT_ERROR:
            MessageBox( (HWND)gGlobalClient->getHWND(),my_utf82t(_GT("nProtect GameGuard初始化失败")),my_utf82t(_GT("防外挂保护")),MB_OK );
            break;
        case NPGAMEMON_SPEEDHACK:
            MessageBox( (HWND)gGlobalClient->getHWND(),my_utf82t(_GT("nProtect GameGuard检测到加速程序,请关闭后再启动游戏")),my_utf82t(_GT("防外挂保护")),MB_OK );
            break;
        case NPGAMEMON_GAMEHACK_KILLED:
        case NPGAMEMON_GAMEHACK_DETECT:
            MessageBox( (HWND)gGlobalClient->getHWND(),my_utf82t(_GT("nProtect GameGuard检测到非法程序,请关闭可疑程序后再启动游戏")),my_utf82t(_GT("防外挂保护")),MB_OK );
            break;
        case NPGAMEMON_GAMEHACK_DOUBT:
            MessageBox( (HWND)gGlobalClient->getHWND(),my_utf82t(_GT("nProtect GameGuard检测到可疑程序,请关闭后再启动游戏")),my_utf82t(_GT("防外挂保护")),MB_OK );
            break;
        default:
            break;
        }
 
        mErrorCode = 0;
        g_pApp->exit();
    }
 
    // 看看有没有数据包要发送
    obuf128 * pBuf = 0;
    while( mRequestSendQueue.pop(pBuf) )
    {
        if ( pBuf!=0 && gGlobalClient->getNetConnection() )
        {
            gGlobalClient->getNetConnection()->SendData( pBuf->begin(),pBuf->size() );
            delete pBuf;
            pBuf = 0;
        }
    }  
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//////////////////////////////////////////////////////////////////////////////////
void CProtectGameGuard::onMessage(ulong actionId, SGameMsgHead* head, void* data, size_t len)
{
    if ( mNPGameLib==0 )
    {
        return;
    }
 
    switch( head->wKeyAction )
    {
    case MSG_GATEWAY_GAMEGUARD_AUTH:
        {
        if ( len!=sizeof(GG_AUTH_DATA) || data==0 )
            return;
 
        GG_AUTH_DATA * pAuthData = (GG_AUTH_DATA *)data;
        mNPGameLib->Auth2(pAuthData);
        }
        break;
    default:
        break;
    }
}

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

上传的附件:
收藏
免费 4
支持
分享
赞赏记录
参与人
雪币
留言
时间
飘零丶
为你点赞~
2024-5-31 06:34
shinratensei
为你点赞~
2024-5-31 06:27
PLEBFE
为你点赞~
2023-2-22 02:50
老牜lyh
为你点赞~
2018-11-25 20:24
最新回复 (44)
雪    币: 967
活跃值: (1138)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
2
说话你别不爱听,代码又不是你写的,你给整套代码 上传上来就行了啊
大家都看得懂代码
严重拉低 李处长在我们心目中的水平
2016-2-19 17:18
0
雪    币: 272
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
这些都是套话,是保护软件公司忽悠游戏公司买单的。 游戏本身有无数漏洞可以利用,外部再怎么包裹都无济于事。 只觉得行为检测有点用 ,其他什么进程、线程、模块、注入检测都是然并卵。

你看这发的这个东西,其实强度来自于VM,如果不加VM,NP整个体系都会被小菜轻易瓦解,加了VM也会被大牛瓦解。 所以说研究这研究那。
2016-2-19 17:26
0
雪    币: 163
活跃值: (103)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
4
网上源码很多,我只是提出了关键的代码分析。不浪费大家时间,毕竟一套游戏的源码的反外挂系统只是一小部分,既然你想直接看源码,大可可以直接去网上搜索。既然不感兴趣,又何必浪费时间看帖子呢,兄台你说是吗
2016-2-19 17:32
0
雪    币: 163
活跃值: (103)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
5
如果说不从源码级别来看,单纯的逆向这些VM过后的代码,又是否会觉得就这么简单呢
2016-2-19 17:40
0
雪    币: 8014
活跃值: (2463)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
文章有条条理,是否原创,我都支持了,赞一个。
2016-2-19 22:01
0
雪    币: 248
活跃值: (3789)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
CS认证就是传说中的心跳算号,剩下的就是见招拆招了
2016-2-19 22:08
0
雪    币: 35
活跃值: (23)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
不错不错支持楼主继续发帖
2016-2-20 00:13
0
雪    币: 8
活跃值: (21)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
这种资料越多越好
2016-2-20 00:43
0
雪    币: 33
活跃值: (254)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
10
算是扫盲贴吧,支持一个
2016-2-20 01:08
0
雪    币: 135
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
支持楼主多爆料啊!!!
2016-2-20 06:23
0
雪    币: 144
活跃值: (42)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
12
说话你也别不爱听,你这个“大家”用的太重了,我们还真不敢让您代表.
说实话,你这种聊天方式也严重拉低了,大家发帖交流学习的意愿
2016-2-20 07:50
0
雪    币: 324
活跃值: (60)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
13
支持楼主分享精神,虽然也是看不懂代码
2016-2-20 07:59
0
雪    币: 27
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
不错的帖子,顶上
2016-2-20 19:06
0
雪    币: 129
活跃值: (338)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
xed
15
看了心跳部分和上一篇的云代码下载检测。有完整的资料或专题就好了。
另外行为监测的帖子也很少。思路大家都知道了。但缺乏具体实例的分析。
来看热闹的。
2016-2-22 01:15
0
雪    币: 15
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
帖子很有用啊  二楼么必要打击
2016-2-22 01:44
0
雪    币: 665
活跃值: (454)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
路过,我打酱油...
2016-2-27 05:47
0
雪    币: 24
活跃值: (1363)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
楼主辛辛苦苦的普及知识,应该支持才对,不管有没有源码,都应该支持
2016-2-29 09:38
0
雪    币: 1564
活跃值: (3567)
能力值: ( LV13,RANK:420 )
在线值:
发帖
回帖
粉丝
19
多年第一泡~
2016-2-29 09:55
0
雪    币: 7160
活跃值: (4296)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
写得不错, 可以从源码方面看到np的流程, 感谢
2016-2-29 15:41
0
雪    币: 14
活跃值: (285)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
哥们,你最好还是发点重量级的代码,而不是研究怎么用NP的SDK。比如GAMEMON.DES的实现细节。你说的这个CSAuth2及最新的CSAuth3的实现细节,有点这些代码和分析,算是有点说服力,要不您这个代码,说个不好听的,连个多开算号都搞不定。
2016-2-29 16:50
0
雪    币: 14
活跃值: (285)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
unsigned __int8 *__usercall CSAuth2<eax>(int a1<eax>, int pNPDataBuffer, int pNPResultBuffer)
{
  unsigned __int8 *result; // eax@1
  int v4; // eax@2
  const unsigned __int8 *v5; // eax@2
  const unsigned __int8 *v6; // eax@3
  unsigned int v7; // [sp-10h] [bp-30h]@1
  int v8; // [sp+4h] [bp-1Ch]@1
  unsigned int *v9; // [sp+8h] [bp-18h]@1
  int v10; // [sp+10h] [bp-10h]@1
  int (__cdecl *v11)(int, int, int); // [sp+14h] [bp-Ch]@1
  unsigned int v12; // [sp+18h] [bp-8h]@1
  int v13; // [sp+1Ch] [bp-4h]@1
  int v14; // [sp+20h] [bp+0h]@1

  v11 = _except_handler4;
  v10 = a1;
  v12 = dword_530074 ^ (unsigned int)dword_50AFD8;
  v7 = (unsigned int)&v14 ^ dword_530074;
  v9 = &v7;
  v8 = 0;
  v13 = 0;
  result = (unsigned __int8 *)nProtectDeCode(pNPDataBuffer, pNPResultBuffer);
  if ( !byte_53E408 )
  {
    sub_43DEA0(v7);
    v4 = GetNPString(&byte_512718);
    PrintfLog(&dword_529D40, v4);
    v5 = (const unsigned __int8 *)GetNPString(&byte_512704);
    if ( _mbsstr(&byte_53E408, v5)
      || (v6 = (const unsigned __int8 *)GetNPString(&byte_5126F0), (result = _mbsstr(&byte_53E408, v6)) != 0) )
      result = (unsigned __int8 *)PrintfNPLog(4002, "NPGGERR_NPGM_CSERR");
  }
  return result;
}
2016-2-29 16:57
0
雪    币: 14
活跃值: (285)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
___:004D06E2 ; int __cdecl nProtectDeCode(int pNPDataBuffer, int pNPResultBuffer)
___:004D06E2 nProtectDeCode  proc near               ; CODE XREF: CSAuth2+43p
___:004D06E2
___:004D06E2 var_4           = dword ptr -4
___:004D06E2 pNPDataBuffer   = dword ptr  8
___:004D06E2 pNPResultBuffer = dword ptr  0Ch
___:004D06E2
___:004D06E2                 push    ebp
___:004D06E3                 mov     ebp, esp
___:004D06E5                 push    ecx
___:004D06E6                 mov     eax, [ebp+pNPResultBuffer]
___:004D06E9                 mov     dword ptr [eax], 10060h
___:004D06EF                 call    nProtectRand
___:004D06F4                 mov     ecx, [ebp+pNPResultBuffer]
___:004D06F7                 mov     [ecx+4], eax
___:004D06FA                 call    nProtectRand
___:004D06FF                 mov     edx, [ebp+pNPResultBuffer]
___:004D0702                 mov     [edx+8], eax
___:004D0705                 call    nProtectRand
___:004D070A                 mov     ecx, [ebp+pNPResultBuffer]
___:004D070D                 mov     [ecx+0Ch], eax
___:004D0710                 mov     edx, [ebp+pNPDataBuffer]
___:004D0713                 push    edx
___:004D0714                 call    nProtectKeyDecode
___:004D0719                 add     esp, 4
___:004D071C                 mov     ecx, [ebp+pNPDataBuffer]
___:004D071F                 mov     edx, [ecx]
___:004D0721                 mov     [ebp+var_4], edx
___:004D0724                 cmp     [ebp+var_4], 1F4h ; switch 501 cases
___:004D072B                 ja      loc_4D303A      ; default
___:004D0731                 mov     eax, [ebp+var_4]
___:004D0734                 jmp     off_4D304A[eax*4] ; switch jump
___:004D073B
___:004D073B loc_4D073B:                             ; DATA XREF: nProtectDeCode:off_4D304Ao
___:004D073B                 mov     ecx, [ebp+pNPResultBuffer] ; jumptable 004D0734 case 0
___:004D073E                 push    ecx
___:004D073F                 mov     edx, [ebp+pNPDataBuffer]
___:004D0742                 push    edx
___:004D0743                 call    sub_4D3876
___:004D0748                 add     esp, 8
___:004D074B                 jmp     loc_4D303A      ; default
___:004D0750 ; ---------------------------------------------------------------------------
___:004D0750
___:004D0750 loc_4D0750:                             ; CODE XREF: nProtectDeCode+52j
___:004D0750                                         ; DATA XREF: nProtectDeCode:off_4D304Ao
___:004D0750                 mov     eax, [ebp+pNPResultBuffer] ; jumptable 004D0734 case 1
___:004D0753                 push    eax
___:004D0754                 mov     ecx, [ebp+pNPDataBuffer]
___:004D0757                 push    ecx
___:004D0758                 call    loc_4D3999
___:004D075D                 add     esp, 8
___:004D0760                 jmp     loc_4D303A      ; default
___:004D0765 ; ---------------------------------------------------------------------------
___
2016-2-29 17:00
0
雪    币: 1564
活跃值: (3567)
能力值: ( LV13,RANK:420 )
在线值:
发帖
回帖
粉丝
24
这篇文章主要还是写的是NP的整个流程,了解NP的框架。,更底层的细节和以及如何对抗感兴趣的朋友可以自行深入研究一下,就不用盲目的去OD中乱搞些VM过的二进制代码了:eek。

另外感谢下处女帖给了我~
2016-2-29 17:07
0
雪    币: 23
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
25
OMG. Thanks for sharing. XD
2016-2-29 19:27
0
游客
登录 | 注册 方可回帖
返回

账号登录
验证码登录

忘记密码?
没有账号?立即免费注册