前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >BattlEye通讯挂钩

BattlEye通讯挂钩

原创
作者头像
franket
修改2021-02-09 13:00:50
修改2021-02-09 13:00:50
3.9K0
举报
文章被收录于专栏:技术杂记技术杂记

为了与广大视频游戏黑客作斗争,反作弊系统需要从客户那里收集和处理大量信息。通常,这通常是通过将所有内容发送到服务器进行进一步分析来完成的,这使攻击者可以通过有趣的方式规避这些系统,其中之一是劫持了通信例程。

如果反作弊试图通过例如承载作弊代码的进程的名称来检测某种作弊,则通常它将解析整个进程列表并将其发送到服务器。这种将处理外包的方式可以防止作弊者对列入黑名单的流程名称进行反向工程,因为他们只能看到整个流程列表都已发送到反作弊服务器。实际上,这在反作弊社区中正变得越来越普遍,这仅由于将大量信息发送到外部服务器而引起了一些严重的隐私问题。

BattlEye是世界上安装次数最多的反作弊软件之一,它使用这种例程通过UDP将数据发送到其主服务器。此功能通常称为Battleye :: send或Battleye :: report(如我以前的文章中所述)。它带有两个参数:缓冲区和大小。发送到BattlEye服务器的每条信息都通过此功能传递,使黑客拦截非常有利可图,有可能绕过每项保护措施,因为如果黑客是通讯的中间人,则游戏无法报告异常情况。很少有作弊开发人员积极使用此方法,因为他们中的大多数人缺乏对BattlEye严重依赖的动态流模块进行反向工程和模糊处理的技术技能,但是在本文中,我将阐明如何积极利用此通信例程的方法。 ,

滥用

BattlEye的通信例程位于模块BEClient中,该模块由受保护的游戏进程动态加载。此例程使用具有两个字节头和可变内容长度的“数据包”,进行加密,然后通过UDP将其传输到BattlEye服务器。检测例程,例如定时检测单步步进这些测试的结果未经过滤就发送到BattlEye服务器进行处理,因此依赖于这种通信。如果您要挂接此功能,然后简单地修改发送的原始数据以防止服务器禁止您,将会发生什么情况?直到去年为止的很多年,当BattlEye向混淆的模块BEClient2添加Battleye :: report的完整性检查时,绝对没有。下一节将介绍此完整性检查。

减轻

对于BattlEye开发人员来说,这似乎是一个绝望的时代,因为他们发现在诸如Rainbow Six:Siege和PUBG之类的游戏中普遍存在Battleye :: report钩住了私人的,高度独家的作弊手段。理想情况下,您将对各个模块进行完整的完整性检查以确保公平竞争,但是BattlEye拥有很长的省力,创可贴修复的历史,而且也不例外。如果您愿意花费数小时最终对模块BEClient2进行虚拟化,则会注意到以下完整性检查:

代码语言:txt
复制
*(std::uint64_t*)&report_table[0x3A] = battleye::report;

if ( *(std::uint32_t*)(battleye::report + 5) == 0xCCCCCCCC && 
     *(std::uint32_t*)(battleye::report + 0x1506CA) == 0xFFF3BF25 &&
    (*(std::uint32_t*)(battleye::report + 1) != 0x1506C9 || *((std::uint8_t*)battleye::report + 0x1506CE) != 0x68) )
{
    report_table[0x43] = 1;
}

他们没有通过例如对模块代码进行哈希比较来进行完整的完整性检查,而是决定仅测试函数的某些部分,并在满足条件的情况下设置布尔值。如果我们忽略一秒钟进行完整性检查的错误尝试,如何将report_table传输到BattlEye的服务器?

代码语言:txt
复制
battleye::report(report_table, sizeof(report_table));
return;

天哪!完整性检查不仅非常原始,而且还依赖于自身的完整性!这意味着任何已经挂上通信例程并触发完整性检查的黑客都可以控制相同的精确检查facepalm的结果。实际上,绕过这真的很简单:

规避

从完整性检查中可以看到,结果存储在报表数据数组+ 0x43中。有了钩子,就可以轻松绕开它,因为没有什么可以阻止您设置完整性检查结果以适应要求:

代码语言:txt
复制
void battleye_report_hook(const std::uint8_t* buffer, const std::size_t size)
{
    // ? BECLIENT2 PACKET ?
    if (buffer[1] == PACKET_BECLIENT2) // 0x39
    {
        // SET INTEGRITY CHECK RESULT
        buffer[0x43] = true;
    }
 
    // SEND THE INFORMATION TO BATTLEYE SERVERS
    original_fn(buffer, size);
}

读者的练习:缓冲区实际上是用一个简单的xor密钥加密的(字面意思是数据包生成时的滴答计数🙂,它以纯文本格式存储在

代码语言:txt
复制
(std::uint32_t)report_table[0x1FC]

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
作者已关闭评论
0 条评论
热度
最新
推荐阅读
目录
  • 滥用
  • 减轻
  • 规避
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档