前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何绕过Easy DeadCheat

如何绕过Easy DeadCheat

原创
作者头像
franket
发布2020-12-03 11:31:10
18K0
发布2020-12-03 11:31:10
举报
文章被收录于专栏:技术杂记
代码语言:javascript
复制
hare_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
      Curl_cookie_clearsess(data->cookies);
      Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
    }
    else if(strcasecompare(argptr, "FLUSH")) {
      /* flush cookies to file, takes care of the locking */
      Curl_flush_cookies(data, 0);
    }
    else if(strcasecompare(argptr, "RELOAD")) {
      /* reload cookies from file */
      Curl_cookie_loadfiles(data);
      break;
    }

可能性是无止境。我们有很多内联常量和许多字符串引用可供我们使用,以在内存中找到函数。该函数从以下程序集开始:

代码语言:javascript
复制
mov qword ptr ss:[rsp+8],rbx
mov qword ptr ss:[rsp+10],rbp
mov qword ptr ss:[rsp+18],rsi
push rdi
sub rsp,30
xor ebp,ebp
mov rsi,r8
mov rbx,rcx
cmp edx,D2

如我们所见,寄存器edx将包含常量。根据设置的值,它将分支到不同的代码以应用传递给函数的选项。我们主要是在寻找常数0x400x51。从原始源代码中我们知道,第三个参数包含该值,在这种情况下,它将为truefalse。第三个寄存器是,r8并包含指向持有该标志的存储器的指针。我们可以使用x64dbg的条件断点在触发时自动修补内存:

在下图中,您可以看到edx设置为,0x40并且该寄存器r8指向内存中的修补值。

您可能想知道:“但是我们正在触及这个过程吗?”。没错,就是这样,这就是为什么我要在这里停止:)我目前正在为EasyAntiCheat(在上面的屏幕快照中一直在使用)的用户模式模拟器进行开发,我们将在其中介绍更多调试功能。文章。

实验

回顾过去,我希望我曾经尝试过这样做。从Daylight的记录看Dead,很明显,即使在最新版本中,它们也一定弄乱了一些琐碎的东西。游戏使用虚幻引擎4。所有游戏资产都存储在所谓的“ pak”文件中。检查这些文件的字符串已经揭示了很多其他信息。如果我告诉您“ Dead by Daylight”不对“ pak”文件进行任何完整性检查,您会相信我吗?对。有两种方法可以解决此问题,要么给QuickBMS打个镜头,要么使用十六进制编辑器。在本文中,我们将使用十六进制编辑器方法,将QuickBMS的操作留给读者练习。好处是您可以修补所有游戏中的配置文件和资产,使您可以真正接近完整的wallhack。我们将要编辑的文件称为,pakchunk0-WindowsNoEditor.pak并托管与作弊目的相关的主要配置文件。查找SSL并将内容擦除为空字节。

这是所有正在使用的固定密钥的转储:

代码语言:javascript
复制
+PinnedPublicKeys=".dev.bhvrdbd.com:++MBgDH5WGvL9Bcn5Be30cRcL0f5O+NyoXuWtQdX1aI=;PiEjPYP2N0QUoKvrwZZjmvSLIl0bBGJgKUevOeNowEM="
+PinnedPublicKeys=".qa.bhvrdbd.com:++MBgDH5WGvL9Bcn5Be30cRcL0f5O+NyoXuWtQdX1aI=;tBLmAw0lCqG3/5sn6ooVk9JNdIcptJb0iXoi4qkAqUo="
+PinnedPublicKeys=".stage.bhvrdbd.com:++MBgDH5WGvL9Bcn5Be30cRcL0f5O+NyoXuWtQdX1aI=;OBU5+MqEy/LV95MgQf23LGpAaaYElBvALjPW7AgmMNo="
+PinnedPublicKeys=".ptb.bhvrdbd.com:++MBgDH5WGvL9Bcn5Be30cRcL0f5O+NyoXuWtQdX1aI=;ICaQHYr/VHjpTY6UKcm8FUtWnUMe9q6WNrzr+WDuUls="
+PinnedPublicKeys=".cert.bhvrdbd.com:++MBgDH5WGvL9Bcn5Be30cRcL0f5O+NyoXuWtQdX1aI=;pvf7WXymw2xK8n6YTqblRrt3vwe2mSuGmAk8buiF2C4="
+PinnedPublicKeys=".management.live.bhvrdbd.com:++MBgDH5WGvL9Bcn5Be30cRcL0f5O+NyoXuWtQdX1aI=;EXrEe/XXp1o4/nVmcqCaG/BSgVR3OzhVUG8/X4kRCCU="
+PinnedPublicKeys="steam.live.bhvrdbd.com:++MBgDH5WGvL9Bcn5Be30cRcL0f5O+NyoXuWtQdX1aI=;EXrEe/XXp1o4/nVmcqCaG/BSgVR3OzhVUG8/X4kRCCU="

无论如何,保存文件,打开代理(在我的情况下为mitmweb)并运行游戏!

嗅探解密

在此阶段,您应该能够看到请求。如您所见,还有一个对终结点的新请求clientVersion/check。还记得我之前提到的按键吗?这些密钥被发送到该端点,从而验证我们bhvrSession是否被允许向游戏服务器发送进一步的请求。但是,我们不必担心它们,因为游戏可以毫无问题地为我们做到这一点。

最终,我们将FullProfile/binary在此阶段以一个GET请求结束该请求,该请求将获取当前玩家的个人资料数据。

代码语言:javascript
复制
GET https://latest.live.dbd.bhvronline.com/api/v1/players/me/states/FullProfile/binary HTTP/1.1
...

DbdDAgAC0Yh1kjiaxVnUM1aTDgjahR1BTCe3iiAWGiwQk+4OCPnSOU6mzbfid9Ag6883sQKbf6G9jRiYUD9DQUmA4TmT6yPBznJEcxhzvp+W/QhcgXhPLsD6o8CWt1iMcV8uStjBH3W6r+Bk0COJ5SSSOdKNU8

此数据包含库存,这对于欺骗项目特别有趣。POST请求使用了非常相似的端点,该请求上传了清单。可以对清单进行解密,编辑,加密,然后将其发送到服务器。这使我们能够获得所需的物品和所需的额外津贴。为了使事情变得清晰,以下是它们解密配置文件的方式:

代码语言:javascript
复制
cipher  = AES.new(b"5BCC2D6A95D4DF04A005504E59A9B36E", AES.MODE_ECB)
profile = flow.response.content.decode()[8:]
profile = base64.b64decode(profile)
profile = cipher.decrypt(profile)
profile = "".join([chr(c + 1) for c in profile])
profile = base64.b64decode(profile[8:])
profile = profile[4:len(profile)]
profile = zlib.decompress(profile).decode("utf16")
profile = json.loads(profile)

加密的工作方式相同,只是相反。这也留给读者练习。

RTM

还有另一个请求发送到并命名/getUrl为终结点,它为您提供了一个Websocket终结点的URL。有很多信息在传递,其中一些处于游戏状态:)不幸的是,一次只能连接一个客户端,但是这不会阻止您代理Websocket连接。这超出了范围,仅作为信息提及,我们将以更有趣的方式进行此操作。尽管如此,此处仍提供示例代码。

预测杀手

最近出现了一些帖子,人们在白天通过Deadlight日志文件轮询“死者”,以找出在大厅的凶手。这很有趣,但是根本不应该编写这样的代码。特别是如果作者声称它是出于“学习目的”,则不会:

代码语言:javascript
复制
yourId2 = (str(x2[len(x2) - 2].split('\n[')[0]))[46:]
yourId3 = (str(x2[len(x2) - 3].split('\n[')[0]))[46:]
yourId4 = (str(x2[len(x2) - 4].split('\n[')[0]))[46:]
yourId5 = (str(x2[len(x2) - 5].split('\n[')[0]))[46:]
verifyKiller2 = (((((str(x2[len(x2) - 2].split('GameFlow: Verbose:')[0]))[509:]).split('\n'))[0]).split('_'))[0]
verifyKiller3 = (((((str(x2[len(x2) - 3].split('GameFlow: Verbose:')[0]))[509:]).split('\n'))[0]).split('_'))[0]
verifyKiller4 = (((((str(x2[len(x2) - 4].split('GameFlow: Verbose:')[0]))[509:]).split('\n'))[0]).split('_'))[0]
verifyKiller5 = (((((str(x2[len(x2) - 5].split('GameFlow: Verbose:')[0]))[509:]).split('\n'))[0]).split('_'))[0]

让我们以正确的方式做。在我们前面的调试会话中,您可能已经注意到游戏将字符串打印到x64dbg的“ Log”选项卡中。这是因为Windows将DbgPrint附加的所有调试字符串重定向到调试器。但是,实际上可以在没有调试器的情况下访问这些字符串,而我最近刚从DebugView中撤消了这样做的方法。您可以在我的个人GitHub上找到反向项目。基本思想是访问Windows的消息缓冲区(DBWIN_BUFFER)并等待某些事件(DBWIN_BUFFER_READYDBWIN_DATA_READY)。这使我们能够与游戏日志进行实时交互,而不必轮询任何文本文件并意外地滤除不相关的旧信息。利用非常基本的正则表达式,我们可以确定杀手(即使他在大厅里改变了角色,而原始脚本无法做到!)以及他的Steam个人资料。选项无穷!这是实际情况的细分版本:

代码语言:javascript
复制
const auto process_id = find_process("DeadByDaylight-Win64-Shipping.exe");

const std::regex character_id_pattern("Spawn new pawn characterId (\\d+)\\.");
const std::regex steam_id_pattern("Session:GameSession PlayerId:([0-9\\-a-z]+)\\|(\\d+)");
const std::regex killer_pattern("MatchMembersA=\\[\\\"([a-z0-9\\-]+)\\\"\\]");
auto buffer_ready = open_event(
    EVENT_ALL_ACCESS,
    L"DBWIN_BUFFER_READY");
auto data_ready = open_event(
    SYNCHRONIZE,
    L"DBWIN_DATA_READY");
auto file = open_mapping(
    L"DBWIN_BUFFER");
auto buffer = reinterpret_cast<message*>(
    wrapper::map_view_of_file(
        file,
        SECTION_MAP_READ,
        0, 0, 0));

std::string killer_id;

while (wrapper::wait_for_single_object(
    data_ready,
    INFINITE) == WAIT_OBJECT_0)
{
    if (buffer->process_id == process_id)
    {
        auto message = std::string(buffer->data);
        std::smatch matches;

        if (std::regex_search(message, matches, character_id_pattern))
        {
            auto character_id = std::stoi(matches[1].str());
            auto killer = KILLERS.find(character_id);
            if (killer != KILLERS.end())
            {
                std::cout << "Killer: " << killer->second << std::endl;
            }
        }
        else if (std::regex_search(message, matches, steam_id_pattern))
        {
            auto player_id = matches[1].str();
            auto steam_id = matches[2].str();
            if (player_id == killer_id)
            {
                std::cout << "Killer Steam Profile: https://steamcommunity.com/profiles/" << steam_id << std::endl;
            }
        }
        else if (std::regex_search(message, matches, killer_pattern))
        {
            killer_id = matches[1].str();
            std::cout << "Found Killer PlayerID: " << killer_id << std::endl;
        }
    }

    wrapper::set_event(buffer_ready);
}

wrapper::unmap_view_of_file(buffer);
wrapper::close_handle(file);
wrapper::close_handle(buffer_ready);
wrapper::close_handle(data_ready);

该代码仅用于演示目的,您可以在此处找到该项目。

排名上升

在研究过程中,我创建了一个脚本(authenticator.py),该脚本可自动进行信息转储(例如bhvrSession)并解密清单。数据正在单独的JSON文件中写入。创建文件后,您可以自由运行脚本levelup.pyrankup.py。该脚本发送基本POST请求到端点/api/v1/ranks/pips/api/v1/extensions/playerLevels/earnPlayerXp分别。所需的JSON主体如下(与上述顺序相同):

代码语言:javascript
复制
{
    "data": {
        "consecutiveMatch": 1,
        "emblemQualities": [
            "Iridescent",
            "Iridescent",
            "Iridescent",
            "Iridescent"
        ],
        "isFirstMatch": true,
        "levelVersion": 1337,
        "matchTime": 1000,
        "platformVersion": "steam",
        "playerType": "survivor"
    }
}
代码语言:javascript
复制
{
    "forceReset": true,
    "killerPips": 2,
    "survivorPips": 2
}

最后的话

总结一下,我想说的是,还有更多的方法可以探索,并且发布的代码可以通过许多方式进行改进。我认为最重要的收获是,如果使用过多的调试事件/字符串可能会非常危险。我相信“行为”的一般方法和工作都很好。但是,某些细节仍然有改进的余地,例如修复其反热检查所有游戏文件。我没有提到的另一个重要说明是pak文件未使用加密密钥。虚幻引擎4具有使用特定密钥加密所有游戏文件的功能。当然,可以对它进行反向工程,但是尽管如此,它仍使作弊变得更加困难。

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

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

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

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

评论
作者已关闭评论
0 条评论
热度
最新
推荐阅读
目录
  • 实验
  • 嗅探解密
  • RTM
  • 预测杀手
  • 排名上升
  • 最后的话
相关产品与服务
云服务器
云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档