<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
<channel>
  <title>LeviMC's blog</title>
  <link>https://levimc.org/blog/</link>
  <description>Everything about development</description>
  <language>zh-CN</language>
  <atom:link href="https://levimc.org/blog/rss.xml" rel="self" type="application/rss+xml"/>
  <lastBuildDate>Sun, 11 Aug 2024 01:51:07 GMT</lastBuildDate>
  <item>
    <title>通过Patch修复BDS的空包刷屏问题</title>
    <link>https://levimc.org/blog/2024/08/11/fix-empty-packet-spam-by-patch/</link>
    <guid isPermaLink="true">https://levimc.org/blog/2024/08/11/fix-empty-packet-spam-by-patch/</guid>
    <pubDate>Sun, 11 Aug 2024 01:51:07 GMT</pubDate>
    <author>contact@levimc.org (ShrBox)</author>
    <category>Bedrock Dedicated Server</category>
    <category>LeviLamina</category>
    <description>本文将介绍我通过LeviLamina以及libhat通过Patch的方式解决Bedrock Dedicated Server(以下简称BDS)中服务端收到空包会在控制台刷ATTENTION! Received EMPTY UDP packet - potential UDP…</description>
    <content:encoded><![CDATA[<blockquote>
<p>Mojang已经在1.21.20.03中修复了此问题</p>
</blockquote>
<p>本文将介绍我通过<a href="https://github.com/LiteLDev/LeviLamina" target="_blank" rel="noopener">LeviLamina</a>以及<a href="https://github.com/BasedInc/libhat" target="_blank" rel="noopener">libhat</a>通过Patch的方式解决Bedrock Dedicated Server(以下简称BDS)中服务端收到空包会在控制台刷<code>ATTENTION! Received EMPTY UDP packet - potential UDP ports scanning.</code>的问题。</p>
<h2 id="起因">起因<a class="kit-anchor" href="https://levimc.org/blog/2024/08/11/fix-empty-packet-spam-by-patch/#起因" aria-hidden="true" tabindex="-1"><svg viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h2v2h3V2h2v2h2v2h-2v3h2v2h-2v2h-2v-2H7v2H5v-2H3V9h2V6H3V4h2zm2 4v3h3V6z"/></svg></a></h2>
<p>在BDS的早期版本中，存在着非常多的严重的远程崩服漏洞，空包崩服就是其中一种，自某个版本后Mojang便修复了此漏洞，但同时又在RakNet中拉了一坨<code>printf</code>来提醒使用者服务器收到了空包，但这又导致了另一个问题，就是攻击者可以通过频繁地发送空包来造成主线程堵塞。此问题已经由社区向反馈差不多一年时间了，Mojang仍然没有在公开的BDS版本中修复此问题，鉴于前些日子官方和MCC合作的活动服务器是由BDS+ScriptAPI驱动的，但活动服中并无此问题，由此可见Mojang早就知道BDS有这个问题，但却迟迟不修复。于是，我们只能自己动手来修复这一问题。</p>
<h2 id="寻找printf函数所在位置">寻找<code>printf</code>函数所在位置<a class="kit-anchor" href="https://levimc.org/blog/2024/08/11/fix-empty-packet-spam-by-patch/#寻找printf函数所在位置" aria-hidden="true" tabindex="-1"><svg viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h2v2h3V2h2v2h2v2h-2v3h2v2h-2v2h-2v-2H7v2H5v-2H3V9h2V6H3V4h2zm2 4v3h3V6z"/></svg></a></h2>
<p>首先编写一个发送空包到BDS端口的脚本，这里选择使用Python。</p>
<div class="kit-code" data-lang="py" data-lines="11" data-linenos="" style="--kit-gutter:2ch"><div class="kit-code-head"><span class="kit-code-title" data-kit-code-title="">Python</span><button type="button" class="kit-code-copy" data-kit-copy="" aria-label="复制代码" title="复制代码" data-copied-label="已复制"><svg class="kit-pixel-icon kit-icon-copy" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h9v1H5zM5 3h1v1H5zM13 3h1v7h-1zM2 4h9v1H2zM2 5h1v8H2zM10 5h1v8h-1zM4 6h5v1H4zM4 8h4v1H4zM11 9h3v1h-3zM4 10h5v1H4zM2 12h9v1H2z"/></svg><svg class="kit-pixel-icon kit-icon-done" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M12 3h2v1h-2zM11 4h3v1h-3zM10 5h3v1h-3zM9 6h3v1H9zM2 7h2v1H2zM8 7h3v1H8zM2 8h3v1H2zM7 8h3v1H7zM3 9h6v1H3zM4 10h4v1H4zM5 11h2v1H5z"/></svg></button></div><pre class="shiki kit-shiki" tabindex="0"><code><span class="line"><span class="k0">import</span><span class="k1"> socket</span></span>
<span class="line"></span>
<span class="line"><span class="k1">s </span><span class="k2">=</span><span class="k1"> socket</span><span class="k2">.</span><span class="k1">socket</span><span class="k2">(</span><span class="k1">socket</span><span class="k2">.</span><span class="k3">AF_INET</span><span class="k2">,</span><span class="k1"> socket</span><span class="k2">.</span><span class="k3">SOCK_DGRAM</span><span class="k2">)</span></span>
<span class="line"><span class="k1">i </span><span class="k2">=</span><span class="k3"> 0</span></span>
<span class="line"><span class="k4">while</span><span class="k1"> i </span><span class="k2">&#x3C;</span><span class="k3"> 10000</span><span class="k2">:</span><span class="k5"> # 发送10000个空包到BDS</span></span>
<span class="line"><span class="k1">    s</span><span class="k2">.</span><span class="k1">sendto</span><span class="k2">(</span></span>
<span class="line"><span class="k6">        ""</span><span class="k2">,</span></span>
<span class="line"><span class="k2">        (</span><span class="k6">"127.0.0.1"</span><span class="k2">,</span><span class="k3"> 19132</span><span class="k2">),</span></span>
<span class="line"><span class="k2">    )</span></span>
<span class="line"><span class="k1">    i </span><span class="k2">=</span><span class="k1"> i </span><span class="k2">+</span><span class="k3"> 1</span></span>
<span class="line"></span></code></pre></div>
<p>将脚本保存为<code>empty.py</code>。<br>
然后通过Visual Studio打开/挂载BDS进程，选用其它调试器例如x64dbg也可以。<br>
运行此脚本：<code>py empty.py</code><br>
此时在控制台观察到大量的<code>ATTENTION! Received EMPTY UDP packet - potential UDP ports scanning.</code>输出，通过Visual Studio或其它调试器暂停主线程，我们可以看到如下堆栈：</p>
<div class="kit-code" data-lang="cpp" data-lines="6" data-linenos="" style="--kit-gutter:1ch"><div class="kit-code-head"><span class="kit-code-title" data-kit-code-title="">C++</span><button type="button" class="kit-code-copy" data-kit-copy="" aria-label="复制代码" title="复制代码" data-copied-label="已复制"><svg class="kit-pixel-icon kit-icon-copy" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h9v1H5zM5 3h1v1H5zM13 3h1v7h-1zM2 4h9v1H2zM2 5h1v8H2zM10 5h1v8h-1zM4 6h5v1H4zM4 8h4v1H4zM11 9h3v1h-3zM4 10h5v1H4zM2 12h9v1H2z"/></svg><svg class="kit-pixel-icon kit-icon-done" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M12 3h2v1h-2zM11 4h3v1h-3zM10 5h3v1h-3zM9 6h3v1H9zM2 7h2v1H2zM8 7h3v1H8zM2 8h3v1H2zM7 8h3v1H7zM3 9h6v1H3zM4 10h4v1H4zM5 11h2v1H5z"/></svg></button></div><pre class="shiki kit-shiki" tabindex="0"><code><span class="line"><span class="k1">    ntdll</span><span class="k2">.</span><span class="k7">dll</span><span class="k2">!</span><span class="k8">NtWaitForSingleObject</span><span class="k2">()</span></span>
<span class="line"><span class="k1">    KernelBase</span><span class="k2">.</span><span class="k7">dll</span><span class="k2">!</span><span class="k8">WaitForSingleObjectEx</span><span class="k2">()</span></span>
<span class="line"><span class="k1">    bedrock_server</span><span class="k2">.</span><span class="k7">exe</span><span class="k2">!</span><span class="k9">RakNet</span><span class="k2">::</span><span class="k8">UpdateNetworkLoop</span><span class="k2">(</span><span class="k9">void</span><span class="k2"> *)</span></span>
<span class="line"><span class="k1">    ucrtbase</span><span class="k2">.</span><span class="k7">dll</span><span class="k2">!</span><span class="k8">thread_start</span><span class="k2">&#x3C;</span><span class="k9">unsigned</span><span class="k9"> int</span><span class="k1"> (</span><span class="k9">__cdecl</span><span class="k2">*</span><span class="k1">)(</span><span class="k9">void</span><span class="k2"> *</span><span class="k1">)</span><span class="k2">,</span><span class="k3">1</span><span class="k2">>()</span></span>
<span class="line"><span class="k1">    kernel32</span><span class="k2">.</span><span class="k7">dll</span><span class="k2">!</span><span class="k8">BaseThreadInitThunk</span><span class="k2">()</span></span>
<span class="line"><span class="k2">></span><span class="k1">	ntdll</span><span class="k2">.</span><span class="k7">dll</span><span class="k2">!</span><span class="k8">RtlUserThreadStart</span><span class="k2">()</span></span></code></pre></div>
<p>嗯？似乎没有看到任何有关输出流的调用？没关系，让我们再试一次。</p>
<div class="kit-code" data-lang="cpp" data-lines="19" data-linenos="" style="--kit-gutter:2ch"><div class="kit-code-head"><span class="kit-code-title" data-kit-code-title="">C++</span><button type="button" class="kit-code-copy" data-kit-copy="" aria-label="复制代码" title="复制代码" data-copied-label="已复制"><svg class="kit-pixel-icon kit-icon-copy" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h9v1H5zM5 3h1v1H5zM13 3h1v7h-1zM2 4h9v1H2zM2 5h1v8H2zM10 5h1v8h-1zM4 6h5v1H4zM4 8h4v1H4zM11 9h3v1h-3zM4 10h5v1H4zM2 12h9v1H2z"/></svg><svg class="kit-pixel-icon kit-icon-done" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M12 3h2v1h-2zM11 4h3v1h-3zM10 5h3v1h-3zM9 6h3v1H9zM2 7h2v1H2zM8 7h3v1H8zM2 8h3v1H2zM7 8h3v1H7zM3 9h6v1H3zM4 10h4v1H4zM5 11h2v1H5z"/></svg></button></div><pre class="shiki kit-shiki" tabindex="0"><code><span class="line"><span class="k2">></span><span class="k1">	ntdll</span><span class="k2">.</span><span class="k7">dll</span><span class="k2">!</span><span class="k8">NtWriteFile</span><span class="k2">()</span></span>
<span class="line"><span class="k1">    KernelBase</span><span class="k2">.</span><span class="k7">dll</span><span class="k2">!</span><span class="k8">WriteFile</span><span class="k2">()</span></span>
<span class="line"><span class="k1">    ucrtbase</span><span class="k2">.</span><span class="k7">dll</span><span class="k2">!</span><span class="k8">write_text_ansi_nolock</span><span class="k2">()</span></span>
<span class="line"><span class="k1">    ucrtbase</span><span class="k2">.</span><span class="k7">dll</span><span class="k2">!</span><span class="k8">_write_nolock</span><span class="k2">()</span></span>
<span class="line"><span class="k1">    ucrtbase</span><span class="k2">.</span><span class="k7">dll</span><span class="k2">!</span><span class="k8">_write_internal</span><span class="k2">()</span></span>
<span class="line"><span class="k1">    ucrtbase</span><span class="k2">.</span><span class="k7">dll</span><span class="k2">!</span><span class="k8">write_buffer_nolock</span><span class="k2">&#x3C;</span><span class="k9">char</span><span class="k2">>()</span></span>
<span class="line"><span class="k1">    ucrtbase</span><span class="k2">.</span><span class="k7">dll</span><span class="k2">!</span><span class="k8">common_flush_and_write_nolock</span><span class="k2">&#x3C;</span><span class="k9">char</span><span class="k2">>()</span></span>
<span class="line"><span class="k1">    ucrtbase</span><span class="k2">.</span><span class="k7">dll</span><span class="k2">!</span><span class="k9">__crt_stdio_output</span><span class="k2">::</span><span class="k9">stream_output_adapter</span><span class="k2">&#x3C;</span><span class="k9">char</span><span class="k2">>::</span><span class="k8">write_character_without_count_update</span><span class="k2">()</span></span>
<span class="line"><span class="k1">    ucrtbase</span><span class="k2">.</span><span class="k7">dll</span><span class="k2">!</span><span class="k9">__crt_stdio_output</span><span class="k2">::</span><span class="k9">output_processor</span><span class="k2">&#x3C;</span><span class="k9">char</span><span class="k2">,</span><span class="k9">__crt_stdio_output</span><span class="k2">::</span><span class="k9">stream_output_adapter</span><span class="k2">&#x3C;</span><span class="k9">char</span><span class="k2">>,</span><span class="k9">__crt_stdio_output</span><span class="k2">::</span><span class="k9">standard_base</span><span class="k2">&#x3C;</span><span class="k9">char</span><span class="k2">,</span><span class="k9">__crt_stdio_output</span><span class="k2">::</span><span class="k9">stream_output_adapter</span><span class="k2">&#x3C;</span><span class="k9">char</span><span class="k2">>>>::</span><span class="k8">process</span><span class="k2">()</span></span>
<span class="line"><span class="k1">    ucrtbase</span><span class="k2">.</span><span class="k7">dll</span><span class="k2">!&#x3C;</span><span class="k1">lambda</span><span class="k2">>(</span><span class="k9">void</span><span class="k2">)()</span></span>
<span class="line"><span class="k1">    ucrtbase</span><span class="k2">.</span><span class="k7">dll</span><span class="k2">!</span><span class="k9">__crt_seh_guarded_call</span><span class="k2">&#x3C;</span><span class="k9">int</span><span class="k2">>::</span><span class="k4">operator</span><span class="k1">()</span><span class="k2">&#x3C;&#x3C;</span><span class="k9">lambda_d854c62834386a3b23916ad6dae2782d</span><span class="k2">>,&#x3C;</span><span class="k9">lambda</span><span class="k2">></span><span class="k1">(</span><span class="k9">void</span><span class="k1">) </span><span class="k2">&#x26;,&#x3C;</span><span class="k9">lambda</span><span class="k2">></span><span class="k1">(</span><span class="k9">void</span><span class="k1">)</span><span class="k2">>()</span></span>
<span class="line"><span class="k1">    ucrtbase</span><span class="k2">.</span><span class="k7">dll</span><span class="k2">!</span><span class="k8">__stdio_common_vfprintf</span><span class="k2">()</span></span>
<span class="line"><span class="k1">    bedrock_server</span><span class="k2">.</span><span class="k7">exe</span><span class="k2">!</span><span class="k8">printf</span><span class="k2">()</span></span>
<span class="line"><span class="k1">    bedrock_server</span><span class="k2">.</span><span class="k7">exe</span><span class="k2">!</span><span class="k9">RakNet</span><span class="k2">::</span><span class="k9">RNS2_Berkley</span><span class="k2">::</span><span class="k8">RecvFromLoopInt</span><span class="k2">(</span><span class="k9">void</span><span class="k2">)</span></span>
<span class="line"><span class="k1">    bedrock_server</span><span class="k2">.</span><span class="k7">exe</span><span class="k2">!</span><span class="k9">RakNet</span><span class="k2">::</span><span class="k9">RNS2_Berkley</span><span class="k2">::</span><span class="k8">RecvFromLoop</span><span class="k2">(</span><span class="k9">void</span><span class="k2"> *)</span></span>
<span class="line"><span class="k1">    ucrtbase</span><span class="k2">.</span><span class="k7">dll</span><span class="k2">!</span><span class="k8">thread_start</span><span class="k2">&#x3C;</span><span class="k9">unsigned</span><span class="k9"> int</span><span class="k1"> (</span><span class="k9">__cdecl</span><span class="k2">*</span><span class="k1">)(</span><span class="k9">void</span><span class="k2"> *</span><span class="k1">)</span><span class="k2">,</span><span class="k3">1</span><span class="k2">>()</span></span>
<span class="line"><span class="k1">    kernel32</span><span class="k2">.</span><span class="k7">dll</span><span class="k2">!</span><span class="k8">BaseThreadInitThunk</span><span class="k2">()</span></span>
<span class="line"><span class="k1">    ntdll</span><span class="k2">.</span><span class="k7">dll</span><span class="k2">!</span><span class="k8">RtlUserThreadStart</span><span class="k2">()</span></span>
<span class="line"></span></code></pre></div>
<p>这次我们得到了我们想要的东西，我们看到了我们期望的<code>printf</code>，以及<code>printf</code>在<code>RakNet::RNS2_Berkley::RecvFromLoopInt(void)</code>中调用的有效信息，现在，我们可以打开IDA或其它同类型的反编译软件来具体查看<code>RakNet::RNS2_Berkley::RecvFromLoopInt(void)</code>中<code>printf</code>的具体位置了。</p>
<div class="kit-code" data-lang="cpp" data-lines="48" data-linenos="" style="--kit-gutter:2ch"><div class="kit-code-head"><span class="kit-code-title" data-kit-code-title="">C++</span><button type="button" class="kit-code-copy" data-kit-copy="" aria-label="复制代码" title="复制代码" data-copied-label="已复制"><svg class="kit-pixel-icon kit-icon-copy" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h9v1H5zM5 3h1v1H5zM13 3h1v7h-1zM2 4h9v1H2zM2 5h1v8H2zM10 5h1v8h-1zM4 6h5v1H4zM4 8h4v1H4zM11 9h3v1h-3zM4 10h5v1H4zM2 12h9v1H2z"/></svg><svg class="kit-pixel-icon kit-icon-done" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M12 3h2v1h-2zM11 4h3v1h-3zM10 5h3v1h-3zM9 6h3v1H9zM2 7h2v1H2zM8 7h3v1H8zM2 8h3v1H2zM7 8h3v1H7zM3 9h6v1H3zM4 10h4v1H4zM5 11h2v1H5z"/></svg></button></div><pre class="shiki kit-shiki" tabindex="0"><code><span class="line"><span class="k9">unsigned</span><span class="k9"> RakNet</span><span class="k2">::</span><span class="k9">RNS2_Berkley</span><span class="k2">::</span><span class="k8">RecvFromLoopInt</span><span class="k2">(</span><span class="k9">RakNet</span><span class="k2">::</span><span class="k9">RNS2_Berkley</span><span class="k4"> *</span><span class="k1">this</span><span class="k2">)</span></span>
<span class="line"><span class="k2">{</span></span>
<span class="line"><span class="k5">  // 略...</span></span>
<span class="line"><span class="k4">  while</span><span class="k2"> (</span><span class="k2"> !*((</span><span class="k1">_BYTE </span><span class="k2">*)</span><span class="ka">this</span><span class="k2"> +</span><span class="k3"> 260</span><span class="k2">)</span><span class="k2"> )</span></span>
<span class="line"><span class="k2">  {</span></span>
<span class="line"><span class="k1">    v5 </span><span class="k2">=</span><span class="k2"> *((</span><span class="k1">_QWORD </span><span class="k2">*)</span><span class="ka">this</span><span class="k2"> +</span><span class="k3"> 30</span><span class="k2">);</span></span>
<span class="line"><span class="k4">    if</span><span class="k2"> (</span><span class="k1"> v5 </span><span class="k2">)</span></span>
<span class="line"><span class="k2">    {</span></span>
<span class="line"><span class="k1">      v6 </span><span class="k2">=</span><span class="k2"> (*(</span><span class="k8">__int64</span><span class="k2"> (</span><span class="k1">__fastcall </span><span class="k2">**)(</span><span class="k1">__int64</span><span class="k2">,</span><span class="k4"> const</span><span class="k9"> char</span><span class="k2"> *,</span><span class="k1"> __int64</span><span class="k2">))(*(</span><span class="k1">_QWORD </span><span class="k2">*)</span><span class="k1">v5 </span><span class="k2">+</span><span class="k3"> 24i64</span><span class="k2">))(</span></span>
<span class="line"><span class="k1">             v5</span><span class="k2">,</span></span>
<span class="line"><span class="k6">             "D:</span><span class="kb">\\</span><span class="k6">a</span><span class="kb">\\</span><span class="k6">_work</span><span class="kb">\\</span><span class="k6">1</span><span class="kb">\\</span><span class="k6">s</span><span class="kb">\\</span><span class="k6">handheld</span><span class="kb">\\</span><span class="k6">src-deps</span><span class="kb">\\</span><span class="k6">raknet</span><span class="kb">\\</span><span class="k6">raknet</span><span class="kb">\\</span><span class="k6">RakNetSocket2.cpp"</span><span class="k2">,</span></span>
<span class="line"><span class="k3">             388i64</span><span class="k2">);</span></span>
<span class="line"><span class="k1">      v7 </span><span class="k2">=</span><span class="k1"> v6</span><span class="k2">;</span></span>
<span class="line"><span class="k4">      if</span><span class="k2"> (</span><span class="k1"> v6 </span><span class="k2">)</span></span>
<span class="line"><span class="k2">      {</span></span>
<span class="line"><span class="k5">        // 略...</span></span>
<span class="line"><span class="k4">        if</span><span class="k2"> (</span><span class="k1"> v12 </span><span class="k2">&#x3C;=</span><span class="k3"> 0</span><span class="k2"> )</span></span>
<span class="line"><span class="k2">        {</span></span>
<span class="line"><span class="k5">          // 略...</span></span>
<span class="line"><span class="k4">          if</span><span class="k2"> (</span><span class="k1"> v14 </span><span class="k2">)</span></span>
<span class="line"><span class="k2">            (*(</span><span class="k9">void</span><span class="k2"> (</span><span class="k1">__fastcall </span><span class="k2">**)(</span><span class="k1">__int64</span><span class="k2">,</span><span class="k1"> __int64</span><span class="k2">,</span><span class="k4"> const</span><span class="k9"> char</span><span class="k2"> *,</span><span class="k1"> __int64</span><span class="k2">))(*(</span><span class="k1">_QWORD </span><span class="k2">*)</span><span class="k1">v14 </span><span class="k2">+</span><span class="k3"> 16i64</span><span class="k2">))(</span></span>
<span class="line"><span class="k1">              v14</span><span class="k2">,</span></span>
<span class="line"><span class="k1">              v7</span><span class="k2">,</span></span>
<span class="line"><span class="k6">              "D:</span><span class="kb">\\</span><span class="k6">a</span><span class="kb">\\</span><span class="k6">_work</span><span class="kb">\\</span><span class="k6">1</span><span class="kb">\\</span><span class="k6">s</span><span class="kb">\\</span><span class="k6">handheld</span><span class="kb">\\</span><span class="k6">src-deps</span><span class="kb">\\</span><span class="k6">raknet</span><span class="kb">\\</span><span class="k6">raknet</span><span class="kb">\\</span><span class="k6">RakNetSocket2.cpp"</span><span class="k2">,</span></span>
<span class="line"><span class="k3">              408i64</span><span class="k2">);</span></span>
<span class="line"><span class="k8">          printf</span><span class="k2">(</span><span class="k6">"</span><span class="kb">\n\n</span><span class="k6">  ATTENTION! Received EMPTY UDP packet - potential UDP ports scanning.</span><span class="kb">\n\n</span><span class="k6">"</span><span class="k2">);</span><span class="k5"> // 找到Mojang拉的屎了</span></span>
<span class="line"><span class="k2">        }</span></span>
<span class="line"><span class="k4">        else</span></span>
<span class="line"><span class="k2">        {</span></span>
<span class="line"><span class="k1">          v13 </span><span class="k2">=</span><span class="k2"> *((</span><span class="k1">_QWORD </span><span class="k2">*)</span><span class="ka">this</span><span class="k2"> +</span><span class="k3"> 30</span><span class="k2">);</span></span>
<span class="line"><span class="k4">          if</span><span class="k2"> (</span><span class="k1"> v13 </span><span class="k2">)</span></span>
<span class="line"><span class="k2">            (*(</span><span class="k9">void</span><span class="k2"> (</span><span class="k1">__fastcall </span><span class="k2">**)(</span><span class="k1">__int64</span><span class="k2">,</span><span class="k1"> __int64</span><span class="k2">))(*(</span><span class="k1">_QWORD </span><span class="k2">*)</span><span class="k1">v13 </span><span class="k2">+</span><span class="k3"> 8i64</span><span class="k2">))(</span><span class="k1">v13</span><span class="k2">,</span><span class="k1"> v7</span><span class="k2">);</span></span>
<span class="line"><span class="k2">        }</span></span>
<span class="line"><span class="k2">      }</span></span>
<span class="line"><span class="k2">    }</span></span>
<span class="line"><span class="k2">  }</span></span>
<span class="line"><span class="k9">  RakNet</span><span class="k2">::</span><span class="k9">LocklessUint32_t</span><span class="k1">::</span><span class="k8">Decrement</span><span class="k2">((</span><span class="k9">RakNet</span><span class="k2">::</span><span class="k1">RNS2_Berkley </span><span class="k2">*)((</span><span class="k9">char</span><span class="k2"> *)</span><span class="ka">this</span><span class="k2"> +</span><span class="k3"> 256</span><span class="k2">));</span></span>
<span class="line"><span class="k4">  if</span><span class="k2"> (</span><span class="k1"> v18 </span><span class="k2">)</span></span>
<span class="line"><span class="k2">  {</span></span>
<span class="line"><span class="k4">    if</span><span class="k2"> (</span><span class="k8"> _InterlockedExchangeAdd</span><span class="k2">(</span><span class="k1">v18 </span><span class="k2">+</span><span class="k3"> 2</span><span class="k2">,</span><span class="k3"> 0xFFFFFFFF</span><span class="k2">)</span><span class="k2"> ==</span><span class="k3"> 1</span><span class="k2"> )</span></span>
<span class="line"><span class="k2">    {</span></span>
<span class="line"><span class="k2">      (**(</span><span class="k9">void</span><span class="k2"> (</span><span class="k1">__fastcall </span><span class="k2">***)(</span><span class="k4">volatile</span><span class="k9"> signed</span><span class="k1"> __int32 </span><span class="k2">*))</span><span class="k1">v18</span><span class="k2">)(</span><span class="k1">v18</span><span class="k2">);</span></span>
<span class="line"><span class="k4">      if</span><span class="k2"> (</span><span class="k8"> _InterlockedExchangeAdd</span><span class="k2">(</span><span class="k1">v18 </span><span class="k2">+</span><span class="k3"> 3</span><span class="k2">,</span><span class="k3"> 0xFFFFFFFF</span><span class="k2">)</span><span class="k2"> ==</span><span class="k3"> 1</span><span class="k2"> )</span></span>
<span class="line"><span class="k2">        (*(</span><span class="k9">void</span><span class="k2"> (</span><span class="k1">__fastcall </span><span class="k2">**)(</span><span class="k4">volatile</span><span class="k9"> signed</span><span class="k1"> __int32 </span><span class="k2">*))(*(</span><span class="k1">_QWORD </span><span class="k2">*)</span><span class="k1">v18 </span><span class="k2">+</span><span class="k3"> 8i64</span><span class="k2">))(</span><span class="k1">v18</span><span class="k2">);</span></span>
<span class="line"><span class="k2">    }</span></span>
<span class="line"><span class="k2">  }</span></span>
<span class="line"><span class="k4">  return</span><span class="k3"> 0i64</span><span class="k2">;</span></span>
<span class="line"><span class="k2">}</span></span></code></pre></div>
<p>通过IDA伪代码，我们可以看到Mojang确确实实在<code>RecvFromLoopInt</code>里面拉了坨大的，现在我们要做的事就是Patch掉这坨了。<br>
切换到IDA View，右键选择<strong>Synchrnoize with</strong>-><strong>Pseudocode-A</strong>，我们就可以看到罪魁祸首的汇编了：</p>
<div class="kit-code" data-lang="asm" data-lines="4" data-linenos="" style="--kit-gutter:1ch"><div class="kit-code-head"><span class="kit-code-title" data-kit-code-title="">GAS</span><button type="button" class="kit-code-copy" data-kit-copy="" aria-label="复制代码" title="复制代码" data-copied-label="已复制"><svg class="kit-pixel-icon kit-icon-copy" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h9v1H5zM5 3h1v1H5zM13 3h1v7h-1zM2 4h9v1H2zM2 5h1v8H2zM10 5h1v8h-1zM4 6h5v1H4zM4 8h4v1H4zM11 9h3v1h-3zM4 10h5v1H4zM2 12h9v1H2z"/></svg><svg class="kit-pixel-icon kit-icon-done" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M12 3h2v1h-2zM11 4h3v1h-3zM10 5h3v1h-3zM9 6h3v1H9zM2 7h2v1H2zM8 7h3v1H8zM2 8h3v1H2zM7 8h3v1H7zM3 9h6v1H3zM4 10h4v1H4zM5 11h2v1H5z"/></svg></button></div><pre class="shiki kit-shiki" tabindex="0"><code><span class="line"><span class="k8">loc_14232424B</span><span class="k2">:</span></span>
<span class="line"><span class="k2">lea</span><span class="k3">     rcx</span><span class="k1">, aAttentionRecei </span><span class="k5">; "\n\n  ATTENTION! Received EMPTY UDP pac"...</span></span>
<span class="line"><span class="k2">call</span><span class="k1">    printf</span></span>
<span class="line"><span class="k2">jmp</span><span class="k1">     short loc_1423242B9</span></span></code></pre></div>
<p>我们需要将<code>lea</code>和<code>call</code>给Patch为空字节，接下来切换到Hex View，同样右键选择<strong>Synchrnoize with</strong>-><strong>Pseudocode-A</strong>，我们就找到了罪魁祸首的16进制：</p>
<div class="kit-code" data-lang="text" data-lines="1"><div class="kit-code-head"><span class="kit-code-title" data-kit-code-title="">Text Only</span><button type="button" class="kit-code-copy" data-kit-copy="" aria-label="复制代码" title="复制代码" data-copied-label="已复制"><svg class="kit-pixel-icon kit-icon-copy" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h9v1H5zM5 3h1v1H5zM13 3h1v7h-1zM2 4h9v1H2zM2 5h1v8H2zM10 5h1v8h-1zM4 6h5v1H4zM4 8h4v1H4zM11 9h3v1h-3zM4 10h5v1H4zM2 12h9v1H2z"/></svg><svg class="kit-pixel-icon kit-icon-done" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M12 3h2v1h-2zM11 4h3v1h-3zM10 5h3v1h-3zM9 6h3v1H9zM2 7h2v1H2zM8 7h3v1H8zM2 8h3v1H2zM7 8h3v1H7zM3 9h6v1H3zM4 10h4v1H4zM5 11h2v1H5z"/></svg></button></div><pre class="shiki kit-shiki" tabindex="0"><code><span class="line"><span>48 8D 0D 9E DB B7 00 E8 D9 EB FD FF</span></span></code></pre></div>
<h2 id="通过levilamina和libhat进行patch">通过LeviLamina和libhat进行Patch<a class="kit-anchor" href="https://levimc.org/blog/2024/08/11/fix-empty-packet-spam-by-patch/#通过levilamina和libhat进行patch" aria-hidden="true" tabindex="-1"><svg viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h2v2h3V2h2v2h2v2h-2v3h2v2h-2v2h-2v-2H7v2H5v-2H3V9h2V6H3V4h2zm2 4v3h3V6z"/></svg></a></h2>
<p>现在就是我们通过LeviLamina和libhat来大展拳脚的时候了</p>
<div class="kit-code" data-lang="cpp" data-lines="17" data-linenos="" style="--kit-gutter:2ch"><div class="kit-code-head"><span class="kit-code-title" data-kit-code-title="">C++</span><button type="button" class="kit-code-copy" data-kit-copy="" aria-label="复制代码" title="复制代码" data-copied-label="已复制"><svg class="kit-pixel-icon kit-icon-copy" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h9v1H5zM5 3h1v1H5zM13 3h1v7h-1zM2 4h9v1H2zM2 5h1v8H2zM10 5h1v8h-1zM4 6h5v1H4zM4 8h4v1H4zM11 9h3v1h-3zM4 10h5v1H4zM2 12h9v1H2z"/></svg><svg class="kit-pixel-icon kit-icon-done" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M12 3h2v1h-2zM11 4h3v1h-3zM10 5h3v1h-3zM9 6h3v1H9zM2 7h2v1H2zM8 7h3v1H8zM2 8h3v1H2zM7 8h3v1H7zM3 9h6v1H3zM4 10h4v1H4zM5 11h2v1H5z"/></svg></button></div><pre class="shiki kit-shiki" tabindex="0"><code><span class="line"><span class="k0">#include </span><span class="k6">"libhat/Scanner.hpp"</span></span>
<span class="line"><span class="k0">#include </span><span class="k6">"libhat/Signature.hpp"</span></span>
<span class="line"><span class="k0">#include </span><span class="k6">"ll/api/memory/Memory.h"</span></span>
<span class="line"></span>
<span class="line"><span class="k9">void</span><span class="k8"> consoleSpamPatch</span><span class="k2">()</span><span class="k2"> {</span></span>
<span class="line"><span class="k9">    std</span><span class="k2">::</span><span class="k9">byte</span><span class="k4">*</span><span class="k1">     funcBegin </span><span class="k2">=</span><span class="k2"> (</span><span class="k9">std</span><span class="k2">::</span><span class="k1">byte</span><span class="k2">*)</span><span class="k8">LL_RESOLVE_SYMBOL</span><span class="k2">(</span><span class="k6">"?RecvFromLoopInt@RNS2_Berkley@RakNet@@IEAAIXZ"</span><span class="k2">);</span></span>
<span class="line"><span class="k4">    constexpr</span><span class="k9"> auto</span><span class="k1"> pattern   </span><span class="k2">=</span><span class="k9"> hat</span><span class="k2">::</span><span class="k8">compile_signature</span><span class="k2">&#x3C;</span><span class="k6">"48 8D 0D 9E DB B7 00 E8 D9 EB FD FF"</span><span class="k2">>();</span></span>
<span class="line"><span class="k9">    auto</span><span class="k1">           result    </span><span class="k2">=</span><span class="k9"> hat</span><span class="k2">::</span><span class="k8">find_pattern</span><span class="k2">(</span><span class="k1">funcBegin</span><span class="k2">,</span><span class="k1"> funcBegin </span><span class="k2">+</span><span class="k3"> 0x250</span><span class="k2">,</span><span class="k1"> pattern</span><span class="k2">);</span></span>
<span class="line"><span class="k4">    if</span><span class="k2"> (!</span><span class="k1">result</span><span class="k2">.</span><span class="k8">has_result</span><span class="k2">())</span><span class="k2"> {</span></span>
<span class="line"><span class="k9">        std</span><span class="k2">::</span><span class="k1">cout </span><span class="k2">&#x3C;&#x3C;</span><span class="k6"> "Can't find signature for RecvFromLoopInt</span><span class="kb">\n</span><span class="k6">"</span><span class="k2">;</span></span>
<span class="line"><span class="k2">    }</span></span>
<span class="line"><span class="k9">    std</span><span class="k2">::</span><span class="k9">byte</span><span class="k4">*</span><span class="k1"> patchLocation </span><span class="k2">=</span><span class="k2"> (</span><span class="k9">std</span><span class="k2">::</span><span class="k1">byte</span><span class="k2">*)</span><span class="k1">result</span><span class="k2">.</span><span class="k8">get</span><span class="k2">();</span></span>
<span class="line"><span class="k9">    size_t</span><span class="k1">     patchLength   </span><span class="k2">=</span><span class="k3"> 12</span><span class="k2">;</span></span>
<span class="line"><span class="k9">    ll</span><span class="k2">::</span><span class="k9">memory</span><span class="k2">::</span><span class="k8">modify</span><span class="k2">(</span><span class="k1">patchLocation</span><span class="k2">,</span><span class="k1"> oatchLength</span><span class="k2">,</span><span class="k2"> [&#x26;]()</span><span class="k2"> {</span></span>
<span class="line"><span class="k9">        std</span><span class="k2">::</span><span class="k8">memset</span><span class="k2">(</span><span class="k1">patchLocation</span><span class="k2">,</span><span class="k3"> 0x90</span><span class="k5"> /* nop */</span><span class="k2">,</span><span class="k1"> oatchLength</span><span class="k2">);</span></span>
<span class="line"><span class="k2">    });</span></span>
<span class="line"><span class="k2">}</span></span></code></pre></div>
<p>我们也可以改用模糊匹配，将一些可能会变动的字节改为<code>??</code></p>
<div class="kit-code" data-lang="cpp" data-lines="17" data-linenos="" style="--kit-gutter:2ch"><div class="kit-code-head"><span class="kit-code-title" data-kit-code-title="">C++</span><button type="button" class="kit-code-copy" data-kit-copy="" aria-label="复制代码" title="复制代码" data-copied-label="已复制"><svg class="kit-pixel-icon kit-icon-copy" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h9v1H5zM5 3h1v1H5zM13 3h1v7h-1zM2 4h9v1H2zM2 5h1v8H2zM10 5h1v8h-1zM4 6h5v1H4zM4 8h4v1H4zM11 9h3v1h-3zM4 10h5v1H4zM2 12h9v1H2z"/></svg><svg class="kit-pixel-icon kit-icon-done" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M12 3h2v1h-2zM11 4h3v1h-3zM10 5h3v1h-3zM9 6h3v1H9zM2 7h2v1H2zM8 7h3v1H8zM2 8h3v1H2zM7 8h3v1H7zM3 9h6v1H3zM4 10h4v1H4zM5 11h2v1H5z"/></svg></button></div><pre class="shiki kit-shiki" tabindex="0"><code><span class="line"><span class="k0">#include </span><span class="k6">"libhat/Scanner.hpp"</span></span>
<span class="line"><span class="k0">#include </span><span class="k6">"libhat/Signature.hpp"</span></span>
<span class="line"><span class="k0">#include </span><span class="k6">"ll/api/memory/Memory.h"</span></span>
<span class="line"></span>
<span class="line"><span class="k9">void</span><span class="k8"> consoleSpamPatch</span><span class="k2">()</span><span class="k2"> {</span></span>
<span class="line"><span class="k9">    std</span><span class="k2">::</span><span class="k9">byte</span><span class="k4">*</span><span class="k1">     funcBegin </span><span class="k2">=</span><span class="k2"> (</span><span class="k9">std</span><span class="k2">::</span><span class="k1">byte</span><span class="k2">*)</span><span class="k8">LL_RESOLVE_SYMBOL</span><span class="k2">(</span><span class="k6">"?RecvFromLoopInt@RNS2_Berkley@RakNet@@IEAAIXZ"</span><span class="k2">);</span></span>
<span class="line"><span class="k4">    constexpr</span><span class="k9"> auto</span><span class="k1"> pattern   </span><span class="k2">=</span><span class="k9"> hat</span><span class="k2">::</span><span class="k8">compile_signature</span><span class="k2">&#x3C;</span><span class="k6">"48 8D 0D ?? ?? ?? 00 E8 ?? ?? FD FF"</span><span class="k2">>();</span></span>
<span class="line"><span class="k9">    auto</span><span class="k1">           result    </span><span class="k2">=</span><span class="k9"> hat</span><span class="k2">::</span><span class="k8">find_pattern</span><span class="k2">(</span><span class="k1">funcBegin</span><span class="k2">,</span><span class="k1"> funcBegin </span><span class="k2">+</span><span class="k3"> 0x250</span><span class="k2">,</span><span class="k1"> pattern</span><span class="k2">);</span></span>
<span class="line"><span class="k4">    if</span><span class="k2"> (!</span><span class="k1">result</span><span class="k2">.</span><span class="k8">has_result</span><span class="k2">())</span><span class="k2"> {</span></span>
<span class="line"><span class="k9">        std</span><span class="k2">::</span><span class="k1">cout </span><span class="k2">&#x3C;&#x3C;</span><span class="k6"> "Can't find signature for RecvFromLoopInt</span><span class="kb">\n</span><span class="k6">"</span><span class="k2">;</span></span>
<span class="line"><span class="k2">    }</span></span>
<span class="line"><span class="k9">    std</span><span class="k2">::</span><span class="k9">byte</span><span class="k4">*</span><span class="k1"> patchLocation </span><span class="k2">=</span><span class="k2"> (</span><span class="k9">std</span><span class="k2">::</span><span class="k1">byte</span><span class="k2">*)</span><span class="k1">result</span><span class="k2">.</span><span class="k8">get</span><span class="k2">();</span></span>
<span class="line"><span class="k9">    size_t</span><span class="k1">     patchLength   </span><span class="k2">=</span><span class="k3"> 12</span><span class="k2">;</span></span>
<span class="line"><span class="k9">    ll</span><span class="k2">::</span><span class="k9">memory</span><span class="k2">::</span><span class="k8">modify</span><span class="k2">(</span><span class="k1">patchLocation</span><span class="k2">,</span><span class="k1"> oatchLength</span><span class="k2">,</span><span class="k2"> [&#x26;]()</span><span class="k2"> {</span></span>
<span class="line"><span class="k9">        std</span><span class="k2">::</span><span class="k8">memset</span><span class="k2">(</span><span class="k1">patchLocation</span><span class="k2">,</span><span class="k3"> 0x90</span><span class="k5"> /* nop */</span><span class="k2">,</span><span class="k1"> oatchLength</span><span class="k2">);</span></span>
<span class="line"><span class="k2">    });</span></span>
<span class="line"><span class="k2">}</span></span></code></pre></div>
<p>将代码嵌入Mod中，然后将Mod安装进带有LeviLamina的服务端中。<br>
重新执行发送空包的Python脚本，服务器的控制台无事发生，就证明我们的Patch已经生效了。</p>]]></content:encoded>
  </item>
  <item>
    <title>使用机器学习检测杀戮光环</title>
    <link>https://levimc.org/blog/2023/07/31/machine-learning-killaura-detect/</link>
    <guid isPermaLink="true">https://levimc.org/blog/2023/07/31/machine-learning-killaura-detect/</guid>
    <pubDate>Mon, 31 Jul 2023 06:03:00 GMT</pubDate>
    <author>contact@levimc.org (PA733)</author>
    <category>Bedrock Dedicated Server</category>
    <category>LiteLoaderBDS</category>
    <description>本文将介绍一种基于机器学习的反作弊思路，它利用玩家的行为数据来训练一个神经网络模型，从而有效地识别出作弊的玩家。</description>
    <content:encoded><![CDATA[<p>本文将介绍一种基于机器学习的反作弊思路，它利用玩家的行为数据来训练一个神经网络模型，从而有效地识别出作弊的玩家。</p>
<h2 id="引言">引言<a class="kit-anchor" href="https://levimc.org/blog/2023/07/31/machine-learning-killaura-detect/#引言" aria-hidden="true" tabindex="-1"><svg viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h2v2h3V2h2v2h2v2h-2v3h2v2h-2v2h-2v-2H7v2H5v-2H3V9h2V6H3V4h2zm2 4v3h3V6z"/></svg></a></h2>
<h3 id="杀戮光环简介">杀戮光环简介<a class="kit-anchor" href="https://levimc.org/blog/2023/07/31/machine-learning-killaura-detect/#杀戮光环简介" aria-hidden="true" tabindex="-1"><svg viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h2v2h3V2h2v2h2v2h-2v3h2v2h-2v2h-2v-2H7v2H5v-2H3V9h2V6H3V4h2zm2 4v3h3V6z"/></svg></a></h3>
<p>杀戮光环是一种外挂，它可以让玩家自动攻击周围的实体（其他玩家、怪物等）。杀戮光环的检测一直是一个难题，因为杀戮光环的行为和正常玩家的行为非常相似。<br>
目前市面上的杀戮光环检测方法主要有以下几种：</p>
<ol>
<li>基于行为的检测：分析玩家的攻击频率、攻击间隔等指标。</li>
<li>基于数据包的检测：分析玩家攻击数据包的间隔等特征。</li>
</ol>
<p>传统方法有一些局限性，比如容易受到网络延迟的影响，或者被一些高级的外挂绕过。</p>
<h2 id="特征分析">特征分析<a class="kit-anchor" href="https://levimc.org/blog/2023/07/31/machine-learning-killaura-detect/#特征分析" aria-hidden="true" tabindex="-1"><svg viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h2v2h3V2h2v2h2v2h-2v3h2v2h-2v2h-2v-2H7v2H5v-2H3V9h2V6H3V4h2zm2 4v3h3V6z"/></svg></a></h2>
<h3 id="玩家行为">玩家行为<a class="kit-anchor" href="https://levimc.org/blog/2023/07/31/machine-learning-killaura-detect/#玩家行为" aria-hidden="true" tabindex="-1"><svg viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h2v2h3V2h2v2h2v2h-2v3h2v2h-2v2h-2v-2H7v2H5v-2H3V9h2V6H3V4h2zm2 4v3h3V6z"/></svg></a></h3>
<p>玩家攻击时，通常要先瞄准目标，再点击鼠标。<br>
但杀戮光环不需要瞄准，它通过发送攻击数据包，来快速攻击周围的实体。<br>
因此，分析玩家看的方向和玩家攻击的实体的方向，有多大的偏差，即可判断玩家是否用了杀戮光环。<br>
在实战中，我们发现这个偏差与玩家的攻击距离有关，因此接下来我们将同时收集角度和距离的特征。</p>
<h3 id="数据收集">数据收集<a class="kit-anchor" href="https://levimc.org/blog/2023/07/31/machine-learning-killaura-detect/#数据收集" aria-hidden="true" tabindex="-1"><svg viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h2v2h3V2h2v2h2v2h-2v3h2v2h-2v2h-2v-2H7v2H5v-2H3V9h2V6H3V4h2zm2 4v3h3V6z"/></svg></a></h3>
<p>借助LiteLoaderBDS SDK，使用以下代码，即可获取到玩家的视线向量：</p>
<div class="kit-code" data-lang="cpp" data-lines="4" data-linenos="" style="--kit-gutter:1ch"><div class="kit-code-head"><span class="kit-code-title" data-kit-code-title="">C++</span><button type="button" class="kit-code-copy" data-kit-copy="" aria-label="复制代码" title="复制代码" data-copied-label="已复制"><svg class="kit-pixel-icon kit-icon-copy" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h9v1H5zM5 3h1v1H5zM13 3h1v7h-1zM2 4h9v1H2zM2 5h1v8H2zM10 5h1v8h-1zM4 6h5v1H4zM4 8h4v1H4zM11 9h3v1h-3zM4 10h5v1H4zM2 12h9v1H2z"/></svg><svg class="kit-pixel-icon kit-icon-done" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M12 3h2v1h-2zM11 4h3v1h-3zM10 5h3v1h-3zM9 6h3v1H9zM2 7h2v1H2zM8 7h3v1H8zM2 8h3v1H2zM7 8h3v1H7zM3 9h6v1H3zM4 10h4v1H4zM5 11h2v1H5z"/></svg></button></div><pre class="shiki kit-shiki" tabindex="0"><code><span class="line"><span class="k9">Event</span><span class="k2">::</span><span class="k9">PlayerAttackEvent</span><span class="k2">::</span><span class="k8">subscribe</span><span class="k2">([](</span><span class="k4">const</span><span class="k9"> Event</span><span class="k2">::</span><span class="k9">PlayerAttackEvent</span><span class="k4">&#x26;</span><span class="k1"> ev</span><span class="k2">)</span><span class="k2"> {</span></span>
<span class="line"><span class="k9">    Vec3</span><span class="k1"> playerLookDir </span><span class="k2">=</span><span class="k1"> ev</span><span class="k2">.</span><span class="k7">mPlayer</span><span class="k2">-></span><span class="k8">getHeadLookVector</span><span class="k2">(</span><span class="k3">1.0</span><span class="k2">);</span></span>
<span class="line"><span class="k4">    return</span><span class="k3"> true</span><span class="k2">;</span></span>
<span class="line"><span class="k2">});</span></span></code></pre></div>
<p>随后使用以下代码计算玩家到实体的向量和距离：</p>
<div class="kit-code" data-lang="cpp" data-lines="7" data-linenos="" style="--kit-gutter:1ch"><div class="kit-code-head"><span class="kit-code-title" data-kit-code-title="">C++</span><button type="button" class="kit-code-copy" data-kit-copy="" aria-label="复制代码" title="复制代码" data-copied-label="已复制"><svg class="kit-pixel-icon kit-icon-copy" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h9v1H5zM5 3h1v1H5zM13 3h1v7h-1zM2 4h9v1H2zM2 5h1v8H2zM10 5h1v8h-1zM4 6h5v1H4zM4 8h4v1H4zM11 9h3v1h-3zM4 10h5v1H4zM2 12h9v1H2z"/></svg><svg class="kit-pixel-icon kit-icon-done" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M12 3h2v1h-2zM11 4h3v1h-3zM10 5h3v1h-3zM9 6h3v1H9zM2 7h2v1H2zM8 7h3v1H8zM2 8h3v1H2zM7 8h3v1H7zM3 9h6v1H3zM4 10h4v1H4zM5 11h2v1H5z"/></svg></button></div><pre class="shiki kit-shiki" tabindex="0"><code><span class="line"><span class="k9">Event</span><span class="k2">::</span><span class="k9">PlayerAttackEvent</span><span class="k2">::</span><span class="k8">subscribe</span><span class="k2">([](</span><span class="k4">const</span><span class="k9"> Event</span><span class="k2">::</span><span class="k9">PlayerAttackEvent</span><span class="k4">&#x26;</span><span class="k1"> ev</span><span class="k2">)</span><span class="k2"> {</span></span>
<span class="line"><span class="k9">    Vec3</span><span class="k1"> playerEyeLoc </span><span class="k2">=</span><span class="k1"> ev</span><span class="k2">.</span><span class="k7">mPlayer</span><span class="k2">-></span><span class="k8">getCameraPos</span><span class="k2">();</span></span>
<span class="line"><span class="k9">    Vec3</span><span class="k1"> entityLoc </span><span class="k2">=</span><span class="k1"> ev</span><span class="k2">.</span><span class="k7">mTarget</span><span class="k2">-></span><span class="k8">getPos</span><span class="k2">();</span></span>
<span class="line"><span class="k9">    Vec3</span><span class="k1"> playerEntityDir </span><span class="k2">=</span><span class="k1"> entityLoc </span><span class="k2">-</span><span class="k1"> playerEyeLoc</span><span class="k2">;</span></span>
<span class="line"><span class="k9">    double</span><span class="k1"> playerEntityDist </span><span class="k2">=</span><span class="k1"> playerEntityDir</span><span class="k2">.</span><span class="k8">length</span><span class="k2">();</span></span>
<span class="line"><span class="k4">    return</span><span class="k3"> true</span><span class="k2">;</span></span>
<span class="line"><span class="k2">});</span></span></code></pre></div>
<p>接着，我们计算出夹角：</p>
<div class="kit-code" data-lang="cpp" data-lines="2" data-linenos="" style="--kit-gutter:1ch"><div class="kit-code-head"><span class="kit-code-title" data-kit-code-title="">C++</span><button type="button" class="kit-code-copy" data-kit-copy="" aria-label="复制代码" title="复制代码" data-copied-label="已复制"><svg class="kit-pixel-icon kit-icon-copy" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h9v1H5zM5 3h1v1H5zM13 3h1v7h-1zM2 4h9v1H2zM2 5h1v8H2zM10 5h1v8h-1zM4 6h5v1H4zM4 8h4v1H4zM11 9h3v1h-3zM4 10h5v1H4zM2 12h9v1H2z"/></svg><svg class="kit-pixel-icon kit-icon-done" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M12 3h2v1h-2zM11 4h3v1h-3zM10 5h3v1h-3zM9 6h3v1H9zM2 7h2v1H2zM8 7h3v1H8zM2 8h3v1H2zM7 8h3v1H7zM3 9h6v1H3zM4 10h4v1H4zM5 11h2v1H5z"/></svg></button></div><pre class="shiki kit-shiki" tabindex="0"><code><span class="line"><span class="k9">double</span><span class="k1"> cosAngle </span><span class="k2">=</span><span class="k1"> playerEntityDir</span><span class="k2">.</span><span class="k8">angle</span><span class="k2">(</span><span class="k1">playerLookDir</span><span class="k2">);</span></span>
<span class="line"><span class="k9">double</span><span class="k1"> angle </span><span class="k2">=</span><span class="k8"> acos</span><span class="k2">(</span><span class="k1">cosAngle</span><span class="k2">);</span></span></code></pre></div>
<p>至此，我们已经获得了关键数据。</p>
<h3 id="数据可视化">数据可视化<a class="kit-anchor" href="https://levimc.org/blog/2023/07/31/machine-learning-killaura-detect/#数据可视化" aria-hidden="true" tabindex="-1"><svg viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h2v2h3V2h2v2h2v2h-2v3h2v2h-2v2h-2v-2H7v2H5v-2H3V9h2V6H3V4h2zm2 4v3h3V6z"/></svg></a></h3>
<p>将数据导入至MATLAB，使用以下代码，即可绘制出玩家攻击距离和玩家攻击角度的散点图：</p>
<div class="kit-code" data-lang="matlab" data-lines="7" data-linenos="" style="--kit-gutter:1ch"><div class="kit-code-head"><span class="kit-code-title" data-kit-code-title="">Text Only</span><button type="button" class="kit-code-copy" data-kit-copy="" aria-label="复制代码" title="复制代码" data-copied-label="已复制"><svg class="kit-pixel-icon kit-icon-copy" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h9v1H5zM5 3h1v1H5zM13 3h1v7h-1zM2 4h9v1H2zM2 5h1v8H2zM10 5h1v8h-1zM4 6h5v1H4zM4 8h4v1H4zM11 9h3v1h-3zM4 10h5v1H4zM2 12h9v1H2z"/></svg><svg class="kit-pixel-icon kit-icon-done" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M12 3h2v1h-2zM11 4h3v1h-3zM10 5h3v1h-3zM9 6h3v1H9zM2 7h2v1H2zM8 7h3v1H8zM2 8h3v1H2zM7 8h3v1H7zM3 9h6v1H3zM4 10h4v1H4zM5 11h2v1H5z"/></svg></button></div><pre class="shiki kit-shiki" tabindex="0"><code><span class="line"><span>%% Read the csv files</span></span>
<span class="line"><span>vanillaData = readmatrix('data.desktop.vanilla.csv');</span></span>
<span class="line"><span>horionData = readmatrix('data.desktop.horion.csv');</span></span>
<span class="line"><span></span></span>
<span class="line"><span>%% Plot the data in a scatter plot</span></span>
<span class="line"><span>scatter(vanillaData, 'g');</span></span>
<span class="line"><span>scatter(horionData, 'r', 'x');</span></span></code></pre></div>
<p><img src="https://levimc.org/blog/images/2023/07/31/3RwnGp9VqgZby8t.jpg" alt="MATLAB绘制的散点图" loading="lazy" decoding="async"><br>
观察发现，两类数据有着较明显的区别。其中，原版客户端对应点分布于图像左下角；而杀戮光环对应点分布较散。</p>
<h2 id="模型训练">模型训练<a class="kit-anchor" href="https://levimc.org/blog/2023/07/31/machine-learning-killaura-detect/#模型训练" aria-hidden="true" tabindex="-1"><svg viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h2v2h3V2h2v2h2v2h-2v3h2v2h-2v2h-2v-2H7v2H5v-2H3V9h2V6H3V4h2zm2 4v3h3V6z"/></svg></a></h2>
<h3 id="神经网络的选择">神经网络的选择<a class="kit-anchor" href="https://levimc.org/blog/2023/07/31/machine-learning-killaura-detect/#神经网络的选择" aria-hidden="true" tabindex="-1"><svg viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h2v2h3V2h2v2h2v2h-2v3h2v2h-2v2h-2v-2H7v2H5v-2H3V9h2V6H3V4h2zm2 4v3h3V6z"/></svg></a></h3>
<p>在本次案例中，我们选择了DNN神经网络，原因如下：</p>
<ol>
<li>DNN网络在大量数据的情况下表现较好。在Minecraft中，玩家攻击数据收集成本较低。</li>
<li>DNN网络可以较容易地移植到C++上，减小开发难度。</li>
</ol>
<h3 id="神经网络训练">神经网络训练<a class="kit-anchor" href="https://levimc.org/blog/2023/07/31/machine-learning-killaura-detect/#神经网络训练" aria-hidden="true" tabindex="-1"><svg viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h2v2h3V2h2v2h2v2h-2v3h2v2h-2v2h-2v-2H7v2H5v-2H3V9h2V6H3V4h2zm2 4v3h3V6z"/></svg></a></h3>
<p>读入数据后，我们使用以下MATLAB代码创建一个DNN神经网络：</p>
<div class="kit-code" data-lang="matlab" data-lines="1"><div class="kit-code-head"><span class="kit-code-title" data-kit-code-title="">Text Only</span><button type="button" class="kit-code-copy" data-kit-copy="" aria-label="复制代码" title="复制代码" data-copied-label="已复制"><svg class="kit-pixel-icon kit-icon-copy" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h9v1H5zM5 3h1v1H5zM13 3h1v7h-1zM2 4h9v1H2zM2 5h1v8H2zM10 5h1v8h-1zM4 6h5v1H4zM4 8h4v1H4zM11 9h3v1h-3zM4 10h5v1H4zM2 12h9v1H2z"/></svg><svg class="kit-pixel-icon kit-icon-done" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M12 3h2v1h-2zM11 4h3v1h-3zM10 5h3v1h-3zM9 6h3v1H9zM2 7h2v1H2zM8 7h3v1H8zM2 8h3v1H2zM7 8h3v1H7zM3 9h6v1H3zM4 10h4v1H4zM5 11h2v1H5z"/></svg></button></div><pre class="shiki kit-shiki" tabindex="0"><code><span class="line"><span>net = patternnet(hiddenLayerSize);</span></span></code></pre></div>
<p>接着，开始训练：</p>
<div class="kit-code" data-lang="matlab" data-lines="1"><div class="kit-code-head"><span class="kit-code-title" data-kit-code-title="">Text Only</span><button type="button" class="kit-code-copy" data-kit-copy="" aria-label="复制代码" title="复制代码" data-copied-label="已复制"><svg class="kit-pixel-icon kit-icon-copy" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h9v1H5zM5 3h1v1H5zM13 3h1v7h-1zM2 4h9v1H2zM2 5h1v8H2zM10 5h1v8h-1zM4 6h5v1H4zM4 8h4v1H4zM11 9h3v1h-3zM4 10h5v1H4zM2 12h9v1H2z"/></svg><svg class="kit-pixel-icon kit-icon-done" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M12 3h2v1h-2zM11 4h3v1h-3zM10 5h3v1h-3zM9 6h3v1H9zM2 7h2v1H2zM8 7h3v1H8zM2 8h3v1H2zM7 8h3v1H7zM3 9h6v1H3zM4 10h4v1H4zM5 11h2v1H5z"/></svg></button></div><pre class="shiki kit-shiki" tabindex="0"><code><span class="line"><span>net = train(net, P_train, T_train);</span></span></code></pre></div>
<p>在实战中，测试集报告的分类准确度达到了87%，符合预期。</p>
<h2 id="实践">实践<a class="kit-anchor" href="https://levimc.org/blog/2023/07/31/machine-learning-killaura-detect/#实践" aria-hidden="true" tabindex="-1"><svg viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h2v2h3V2h2v2h2v2h-2v3h2v2h-2v2h-2v-2H7v2H5v-2H3V9h2V6H3V4h2zm2 4v3h3V6z"/></svg></a></h2>
<h3 id="神经网络移植">神经网络移植<a class="kit-anchor" href="https://levimc.org/blog/2023/07/31/machine-learning-killaura-detect/#神经网络移植" aria-hidden="true" tabindex="-1"><svg viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h2v2h3V2h2v2h2v2h-2v3h2v2h-2v2h-2v-2H7v2H5v-2H3V9h2V6H3V4h2zm2 4v3h3V6z"/></svg></a></h3>
<p>使用MATLAB中的<code>save</code>函数，我们可以保存训练好的模型的权重等数据。<br>
在C++中实现相关逻辑后，即可用于新数据的分类：</p>
<div class="kit-code" data-lang="cpp" data-lines="11" data-linenos="" style="--kit-gutter:2ch"><div class="kit-code-head"><span class="kit-code-title" data-kit-code-title="">C++</span><button type="button" class="kit-code-copy" data-kit-copy="" aria-label="复制代码" title="复制代码" data-copied-label="已复制"><svg class="kit-pixel-icon kit-icon-copy" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h9v1H5zM5 3h1v1H5zM13 3h1v7h-1zM2 4h9v1H2zM2 5h1v8H2zM10 5h1v8h-1zM4 6h5v1H4zM4 8h4v1H4zM11 9h3v1h-3zM4 10h5v1H4zM2 12h9v1H2z"/></svg><svg class="kit-pixel-icon kit-icon-done" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M12 3h2v1h-2zM11 4h3v1h-3zM10 5h3v1h-3zM9 6h3v1H9zM2 7h2v1H2zM8 7h3v1H8zM2 8h3v1H2zM7 8h3v1H7zM3 9h6v1H3zM4 10h4v1H4zM5 11h2v1H5z"/></svg></button></div><pre class="shiki kit-shiki" tabindex="0"><code><span class="line"><span class="k9">double</span><span class="k8"> DNN</span><span class="k2">(</span><span class="k4">const</span><span class="k9"> std</span><span class="k2">::</span><span class="k9">vector</span><span class="k2">&#x3C;</span><span class="k9">double</span><span class="k2">></span><span class="k4">&#x26;</span><span class="k1"> X</span><span class="k2">)</span><span class="k2"> {</span></span>
<span class="line"><span class="k5">    // ===== SIMULATION ========</span></span>
<span class="line"><span class="k5">    // Dimensions</span></span>
<span class="line"><span class="k9">    int</span><span class="k1"> Q </span><span class="k2">=</span><span class="k1"> X</span><span class="k2">[</span><span class="k3">0</span><span class="k2">].</span><span class="k8">size</span><span class="k2">();</span></span>
<span class="line"><span class="k5">    // Input 1</span></span>
<span class="line"><span class="k9">    std</span><span class="k2">::</span><span class="k9">vector</span><span class="k2">&#x3C;</span><span class="k9">double</span><span class="k2">></span><span class="k1"> Xp1 </span><span class="k2">=</span><span class="k8"> mapminmax_apply</span><span class="k2">(</span><span class="k1">X</span><span class="k2">,</span><span class="k1"> x1_step1</span><span class="k2">);</span></span>
<span class="line"><span class="k5">    // Layer 1</span></span>
<span class="line"><span class="k9">    double</span><span class="k1"> a1 </span><span class="k2">=</span><span class="k8"> logsigApply</span><span class="k2">(</span><span class="k9">matlab</span><span class="k2">::</span><span class="k8">matrixAddition</span><span class="k2">(</span><span class="k9">matlab</span><span class="k2">::</span><span class="k8">repmat</span><span class="k2">(</span><span class="k1">b1</span><span class="k2">,</span><span class="k1"> Q</span><span class="k2">),</span><span class="k9"> matlab</span><span class="k2">::</span><span class="k8">matrixMultiply</span><span class="k2">(</span><span class="k1">LW1_1</span><span class="k2">,</span><span class="k1"> Xp1</span><span class="k2">)));</span></span>
<span class="line"><span class="k1">    </span></span>
<span class="line"><span class="k4">    return</span><span class="k1"> a1</span><span class="k2">;</span></span>
<span class="line"><span class="k2">}</span></span></code></pre></div>
<h3 id="检测实现">检测实现<a class="kit-anchor" href="https://levimc.org/blog/2023/07/31/machine-learning-killaura-detect/#检测实现" aria-hidden="true" tabindex="-1"><svg viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h2v2h3V2h2v2h2v2h-2v3h2v2h-2v2h-2v-2H7v2H5v-2H3V9h2V6H3V4h2zm2 4v3h3V6z"/></svg></a></h3>
<p>使用 “特征分析”-“数据收集” 章节中，我们收集到玩家的攻击数据，即可进行分类：</p>
<div class="kit-code" data-lang="cpp" data-lines="4" data-linenos="" style="--kit-gutter:1ch"><div class="kit-code-head"><span class="kit-code-title" data-kit-code-title="">C++</span><button type="button" class="kit-code-copy" data-kit-copy="" aria-label="复制代码" title="复制代码" data-copied-label="已复制"><svg class="kit-pixel-icon kit-icon-copy" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h9v1H5zM5 3h1v1H5zM13 3h1v7h-1zM2 4h9v1H2zM2 5h1v8H2zM10 5h1v8h-1zM4 6h5v1H4zM4 8h4v1H4zM11 9h3v1h-3zM4 10h5v1H4zM2 12h9v1H2z"/></svg><svg class="kit-pixel-icon kit-icon-done" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M12 3h2v1h-2zM11 4h3v1h-3zM10 5h3v1h-3zM9 6h3v1H9zM2 7h2v1H2zM8 7h3v1H8zM2 8h3v1H2zM7 8h3v1H7zM3 9h6v1H3zM4 10h4v1H4zM5 11h2v1H5z"/></svg></button></div><pre class="shiki kit-shiki" tabindex="0"><code><span class="line"><span class="k9">std</span><span class="k2">::</span><span class="k1">vector</span><span class="k2">&#x3C;</span><span class="k9">double</span><span class="k2">></span><span class="k1"> playerData </span><span class="k2">=</span><span class="k2"> {};</span></span>
<span class="line"><span class="k1">playerData</span><span class="k2">.</span><span class="k8">push_back</span><span class="k2">(</span><span class="k1">playerEntityDist</span><span class="k2">);</span></span>
<span class="line"><span class="k1">playerData</span><span class="k2">.</span><span class="k8">push_back</span><span class="k2">(</span><span class="k1">angle</span><span class="k2">);</span></span>
<span class="line"><span class="k9">double</span><span class="k1"> results </span><span class="k2">=</span><span class="k8"> DNN</span><span class="k2">(</span><span class="k1">playerData</span><span class="k2">);</span></span></code></pre></div>
<p>神经网络返回数据分类为1的概率。</p>
<h2 id="总结与展望">总结与展望<a class="kit-anchor" href="https://levimc.org/blog/2023/07/31/machine-learning-killaura-detect/#总结与展望" aria-hidden="true" tabindex="-1"><svg viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h2v2h3V2h2v2h2v2h-2v3h2v2h-2v2h-2v-2H7v2H5v-2H3V9h2V6H3V4h2zm2 4v3h3V6z"/></svg></a></h2>
<h3 id="主要贡献">主要贡献<a class="kit-anchor" href="https://levimc.org/blog/2023/07/31/machine-learning-killaura-detect/#主要贡献" aria-hidden="true" tabindex="-1"><svg viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h2v2h3V2h2v2h2v2h-2v3h2v2h-2v2h-2v-2H7v2H5v-2H3V9h2V6H3V4h2zm2 4v3h3V6z"/></svg></a></h3>
<p>机器学习在游戏中的主要贡献如下：</p>
<ol>
<li>我们只需要找到玩家行为的量化方法，即可进行异常检测。</li>
<li>如果外挂进行了升级，借助机器学习较高的灵活性与可扩展性，我们可以通过增加数据量或数据维度的方式，及时地做出应对。</li>
</ol>
<h3 id="未来的发展方向">未来的发展方向<a class="kit-anchor" href="https://levimc.org/blog/2023/07/31/machine-learning-killaura-detect/#未来的发展方向" aria-hidden="true" tabindex="-1"><svg viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h2v2h3V2h2v2h2v2h-2v3h2v2h-2v2h-2v-2H7v2H5v-2H3V9h2V6H3V4h2zm2 4v3h3V6z"/></svg></a></h3>
<p>反作弊作为一种保证玩家游戏公平的手段，能够帮助广大服主建立良好的游玩体验，提高用户的满意度和留存率。<br>
但随着作弊手段的日益进步与玩家需求的变化，反作弊也面临着新的挑战与机遇，未来的发展方向主要有以下几个方面：</p>
<ol>
<li>机器学习的引入。机器学习能带来更高的检测率与更低的误报率，并及时对新兴作弊手段进行响应。</li>
<li>更多判定角度。反作弊可引入更多的玩家特征信息进行判断，进一步增强反作弊能力。</li>
</ol>
<h2 id="致谢">致谢<a class="kit-anchor" href="https://levimc.org/blog/2023/07/31/machine-learning-killaura-detect/#致谢" aria-hidden="true" tabindex="-1"><svg viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h2v2h3V2h2v2h2v2h-2v3h2v2h-2v2h-2v-2H7v2H5v-2H3V9h2V6H3V4h2zm2 4v3h3V6z"/></svg></a></h2>
<p>感谢 <a href="https://github.com/LiteLDev/" target="_blank" rel="noopener">LiteLDev</a> 的各位，他们开发出的LiteLoaderBDS极大地简化了数据采集与分析的难度。<br>
感谢 <a href="https://github.com/Futrime" target="_blank" rel="noopener">Futrime</a> ，他对本实践提出了很多建设性的意见。<br>
感谢 <a href="https://github.com/ShrBox" target="_blank" rel="noopener">ShrBox</a> 和 <a href="https://github.com/dofes" target="_blank" rel="noopener">Dofes</a> ，他们为本项目的推广提供了很大的帮助。<br>
感谢 <a href="https://github.com/KawaiiNahida" target="_blank" rel="noopener">KawaiiNahida</a>、<a href="https://github.com/Redbeanw44602" target="_blank" rel="noopener">Redbeanw44602</a>和<a href="https://github.com/RimuruChan" target="_blank" rel="noopener">RimuruChan</a> ，他们为我的开发给予了精神上的鼓励与支持。</p>]]></content:encoded>
  </item>
  <item>
    <title>蜂巢崩服问题的解决</title>
    <link>https://levimc.org/blog/2023/07/26/original-fix-BeeCrash/</link>
    <guid isPermaLink="true">https://levimc.org/blog/2023/07/26/original-fix-BeeCrash/</guid>
    <pubDate>Wed, 26 Jul 2023 10:28:00 GMT</pubDate>
    <author>contact@levimc.org (Dofes)</author>
    <category>Bedrock Dedicated Server</category>
    <category>LiteLoaderBDS</category>
    <description>记录一下BDS有关BlockEventDispatcherToken::unregister的一个崩服问题</description>
    <content:encoded><![CDATA[<p>记录一下BDS有关BlockEventDispatcherToken::unregister的一个崩服问题</p>
<h3 id="起因">起因<a class="kit-anchor" href="https://levimc.org/blog/2023/07/26/original-fix-BeeCrash/#起因" aria-hidden="true" tabindex="-1"><svg viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h2v2h3V2h2v2h2v2h-2v3h2v2h-2v2h-2v-2H7v2H5v-2H3V9h2V6H3V4h2zm2 4v3h3V6z"/></svg></a></h3>
<p>当初有人反馈,大型服务器在长期运行的时候,在关服的时候会出现崩服问题(0xc0000005错误),而崩服很显然,会导致地图损失等问题, <del>再加上当初闲得无聊</del> ,就着手开展了对这个的研究.</p>
<h3 id="研究">研究<a class="kit-anchor" href="https://levimc.org/blog/2023/07/26/original-fix-BeeCrash/#研究" aria-hidden="true" tabindex="-1"><svg viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h2v2h3V2h2v2h2v2h-2v3h2v2h-2v2h-2v-2H7v2H5v-2H3V9h2V6H3V4h2zm2 4v3h3V6z"/></svg></a></h3>
<details>  
<summary>当时的崩服Log: </summary>  
<div class="kit-code" data-lang="bash" data-lines="8" data-linenos="" style="--kit-gutter:1ch"><div class="kit-code-head"><span class="kit-code-title" data-kit-code-title="">Bash</span><button type="button" class="kit-code-copy" data-kit-copy="" aria-label="复制代码" title="复制代码" data-copied-label="已复制"><svg class="kit-pixel-icon kit-icon-copy" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h9v1H5zM5 3h1v1H5zM13 3h1v7h-1zM2 4h9v1H2zM2 5h1v8H2zM10 5h1v8h-1zM4 6h5v1H4zM4 8h4v1H4zM11 9h3v1h-3zM4 10h5v1H4zM2 12h9v1H2z"/></svg><svg class="kit-pixel-icon kit-icon-done" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M12 3h2v1h-2zM11 4h3v1h-3zM10 5h3v1h-3zM9 6h3v1H9zM2 7h2v1H2zM8 7h3v1H8zM2 8h3v1H2zM7 8h3v1H7zM3 9h6v1H3zM4 10h4v1H4zM5 11h2v1H5z"/></svg></button></div><pre class="shiki kit-shiki" tabindex="0"><code><span class="line"><span class="k8">......</span></span>
<span class="line"><span class="k2">[</span><span class="k1">2023-06-07 06:00:49.138</span><span class="k2">]</span><span class="k2"> [</span><span class="k1">info</span><span class="k2">]</span><span class="k1"> </span></span>
<span class="line"><span class="k2">[</span><span class="k1">2023-06-07 06:00:49.138</span><span class="k2">]</span><span class="k2"> [</span><span class="k1">info</span><span class="k2">]</span><span class="k1"> Last Assembly: </span></span>
<span class="line"><span class="k2">[</span><span class="k1">2023-06-07 06:00:49.138</span><span class="k2">]</span><span class="k2"> [</span><span class="k1">info</span><span class="k2">]</span><span class="k1">   0x7FF7CCAA92BE --</span><span class="k2">></span><span class="k1"> mov rax, </span><span class="k2">[</span><span class="k1">rcx+0x08</span><span class="k2">]</span></span>
<span class="line"><span class="k2">[</span><span class="k1">2023-06-07 06:00:49.138</span><span class="k2">]</span><span class="k2"> [</span><span class="k1">info</span><span class="k2">]</span><span class="k1"> </span></span>
<span class="line"><span class="k2">[</span><span class="k1">2023-06-07 06:00:49.138</span><span class="k2">]</span><span class="k2"> [</span><span class="k1">info</span><span class="k2">]</span><span class="k1"> Stacktrace: </span></span>
<span class="line"><span class="k2">[</span><span class="k1">2023-06-07 06:00:49.169</span><span class="k2">]</span><span class="k2"> [</span><span class="k1">info</span><span class="k2">]</span><span class="k5">   #0 at pc 0x7FF7CCAA9230 bedrock_server_mod.exe -> BlockEventDispatcherToken::unregister+0x8E</span></span>
<span class="line"><span class="k8">......</span></span></code></pre></div>
</details>  
<p>很明显的看到和BlockEventDispatcherToken有关, 所以显而易见的得去ida里看这个函数的问题<br>
但是由于一系列问题,最初是并没有定位到出问题的本质的 <del>(菜)</del></p>
<p>后续,在多次定位后无果,尝试去探寻一下这个的复现方法,于是在调用堆栈里发现这个的调用和BlockBreakSensorComponent有关,而这个又和数据驱动的<a href="https://learn.microsoft.com/en-us/minecraft/creator/reference/content/entityreference/examples/entitycomponents/minecraftcomponent_block_sensor?view=minecraft-bedrock-stable" target="_blank" rel="noopener">minecraft:block_sensor</a>有关.<br>
查阅可知,这个组件和蜜蜂,猪灵的攻击仇恨有关,具体就是蜂箱和金块之类的方块被破坏时,蜜蜂这些拥有该组件的实体会对破坏者产生敌意,并攻击破坏者.<br>
那这个为什么又会导致崩服呢?问题来到了实体上面.<br>
根据经验,这种情况一般是实体销毁导致的问题,于是,笔者便开始了测试,尝试在本地复现该问题.<br>
经过多次测试,发现利用蜂蜜快速死亡+反复生成的情况下,stop可以导致崩服,而且复现概率很高.</p>
<p>在查阅ida的反汇编后,得到BlockEventDispatcherToken::unregister的逻辑如下:</p>
<div class="kit-code" data-lang="cpp" data-lines="47" data-linenos="" style="--kit-gutter:2ch"><div class="kit-code-head"><span class="kit-code-title" data-kit-code-title="">C++</span><button type="button" class="kit-code-copy" data-kit-copy="" aria-label="复制代码" title="复制代码" data-copied-label="已复制"><svg class="kit-pixel-icon kit-icon-copy" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h9v1H5zM5 3h1v1H5zM13 3h1v7h-1zM2 4h9v1H2zM2 5h1v8H2zM10 5h1v8h-1zM4 6h5v1H4zM4 8h4v1H4zM11 9h3v1h-3zM4 10h5v1H4zM2 12h9v1H2z"/></svg><svg class="kit-pixel-icon kit-icon-done" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M12 3h2v1h-2zM11 4h3v1h-3zM10 5h3v1h-3zM9 6h3v1H9zM2 7h2v1H2zM8 7h3v1H8zM2 8h3v1H2zM7 8h3v1H7zM3 9h6v1H3zM4 10h4v1H4zM5 11h2v1H5z"/></svg></button></div><pre class="shiki kit-shiki" tabindex="0"><code><span class="line"><span class="k9">void</span><span class="k4"> __fastcall</span><span class="k9"> BlockEventDispatcherToken</span><span class="k2">::</span><span class="k8">unregister</span><span class="k2">(</span><span class="k9">BlockEventDispatcherToken</span><span class="k4"> *</span><span class="k1">this</span><span class="k2">)</span></span>
<span class="line"><span class="k2">{</span></span>
<span class="line"><span class="k9">  unsigned</span><span class="k9"> int</span><span class="k1"> v1</span><span class="k2">;</span><span class="k5"> // r8d</span></span>
<span class="line"><span class="k9">  _QWORD</span><span class="k4"> *</span><span class="k1">v3</span><span class="k2">;</span><span class="k5"> // r9</span></span>
<span class="line"><span class="k9">  __int64</span><span class="k1"> v4</span><span class="k2">;</span><span class="k5"> // rdx</span></span>
<span class="line"><span class="k9">  __int64</span><span class="k4"> *</span><span class="k1">v5</span><span class="k2">;</span><span class="k5"> // rcx</span></span>
<span class="line"><span class="k9">  __int64</span><span class="k1"> v6</span><span class="k2">;</span><span class="k5"> // rax</span></span>
<span class="line"><span class="k9">  __int64</span><span class="k1"> v7</span><span class="k2">;</span><span class="k5"> // rcx</span></span>
<span class="line"><span class="k9">  unsigned</span><span class="k9"> int</span><span class="k1"> v8</span><span class="k2">;</span><span class="k5"> // [rsp+20h] [rbp-18h] BYREF</span></span>
<span class="line"></span>
<span class="line"><span class="k1">  v1 </span><span class="k2">=</span><span class="k2"> *(</span><span class="k1">_DWORD </span><span class="k2">*)</span><span class="ka">this</span><span class="k2">;</span></span>
<span class="line"><span class="k4">  if</span><span class="k2"> (</span><span class="k2"> *(</span><span class="k1">_DWORD </span><span class="k2">*)</span><span class="ka">this</span><span class="k2"> ==</span><span class="k2"> -</span><span class="k3">1</span><span class="k2"> )</span></span>
<span class="line"><span class="k4">    return</span><span class="k2">;</span></span>
<span class="line"><span class="k1">  v3 </span><span class="k2">=</span><span class="k2"> (</span><span class="k1">_QWORD </span><span class="k2">*)*((</span><span class="k1">_QWORD </span><span class="k2">*)</span><span class="ka">this</span><span class="k2"> +</span><span class="k3"> 1</span><span class="k2">);</span></span>
<span class="line"><span class="k1">  v8 </span><span class="k2">=</span><span class="k2"> *(</span><span class="k1">_DWORD </span><span class="k2">*)</span><span class="ka">this</span><span class="k2">;</span></span>
<span class="line"><span class="k1">  v4 </span><span class="k2">=</span><span class="k1"> v3</span><span class="k2">[</span><span class="k3">1</span><span class="k2">];</span></span>
<span class="line"><span class="k1">  v5 </span><span class="k2">=</span><span class="k2"> (</span><span class="k1">__int64 </span><span class="k2">*)(</span><span class="k1">v3</span><span class="k2">[</span><span class="k3">3</span><span class="k2">]</span></span>
<span class="line"><span class="k2">                 +</span><span class="k3"> 16</span></span>
<span class="line"><span class="k2">                 *</span><span class="k2"> ((</span><span class="k3">0x100000001B3i64</span></span>
<span class="line"><span class="k2">                   *</span><span class="k2"> (((</span><span class="k9">unsigned</span><span class="k1"> __int64</span><span class="k2">)</span><span class="k1">v1 </span><span class="k2">>></span><span class="k3"> 24</span><span class="k2">)</span><span class="k2"> ^</span><span class="k2"> (</span><span class="k3">0x100000001B3i64</span></span>
<span class="line"><span class="k2">                                                    *</span><span class="k2"> (</span><span class="k8">BYTE2</span><span class="k2">(</span><span class="k1">v1</span><span class="k2">)</span><span class="k2"> ^</span><span class="k2"> (</span><span class="k3">0x100000001B3i64</span></span>
<span class="line"><span class="k2">                                                                  *</span><span class="k2"> ((</span><span class="k3">0x100000001B3i64</span></span>
<span class="line"><span class="k2">                                                                    *</span><span class="k2"> ((</span><span class="k9">unsigned</span><span class="k1"> __int8</span><span class="k2">)</span><span class="k1">v1 </span><span class="k2">^</span><span class="k3"> 0xCBF29CE484222325ui64</span><span class="k2">))</span><span class="k2"> ^</span><span class="k8"> BYTE1</span><span class="k2">(</span><span class="k1">v1</span><span class="k2">)))))))</span><span class="k2"> &#x26;</span><span class="k1"> v3</span><span class="k2">[</span><span class="k3">6</span><span class="k2">]));</span></span>
<span class="line"><span class="k1">  v6 </span><span class="k2">=</span><span class="k1"> v5</span><span class="k2">[</span><span class="k3">1</span><span class="k2">];</span></span>
<span class="line"><span class="k4">  if</span><span class="k2"> (</span><span class="k1"> v6 </span><span class="k2">==</span><span class="k1"> v4 </span><span class="k2">)</span></span>
<span class="line"><span class="k4">    goto</span><span class="kc"> LABEL_7</span><span class="k2">;</span></span>
<span class="line"><span class="k1">  v7 </span><span class="k2">=</span><span class="k2"> *</span><span class="k1">v5</span><span class="k2">;</span></span>
<span class="line"><span class="k4">  if</span><span class="k2"> (</span><span class="k1"> v1 </span><span class="k2">!=</span><span class="k2"> *(</span><span class="k1">_DWORD </span><span class="k2">*)(</span><span class="k1">v6 </span><span class="k2">+</span><span class="k3"> 16</span><span class="k2">)</span><span class="k2"> )</span></span>
<span class="line"><span class="k2">  {</span></span>
<span class="line"><span class="k4">    while</span><span class="k2"> (</span><span class="k1"> v6 </span><span class="k2">!=</span><span class="k1"> v7 </span><span class="k2">)</span></span>
<span class="line"><span class="k2">    {</span></span>
<span class="line"><span class="k1">      v6 </span><span class="k2">=</span><span class="k2"> *(</span><span class="k1">_QWORD </span><span class="k2">*)(</span><span class="k1">v6 </span><span class="k2">+</span><span class="k3"> 8</span><span class="k2">);</span></span>
<span class="line"><span class="k4">      if</span><span class="k2"> (</span><span class="k1"> v1 </span><span class="k2">==</span><span class="k2"> *(</span><span class="k1">_DWORD </span><span class="k2">*)(</span><span class="k1">v6 </span><span class="k2">+</span><span class="k3"> 16</span><span class="k2">)</span><span class="k2"> )</span></span>
<span class="line"><span class="k4">        goto</span><span class="kc"> LABEL_8</span><span class="k2">;</span></span>
<span class="line"><span class="k2">    }</span></span>
<span class="line"><span class="k1">LABEL_7:</span></span>
<span class="line"><span class="k1">    v6 </span><span class="k2">=</span><span class="k3"> 0i64</span><span class="k2">;</span></span>
<span class="line"><span class="k2">  }</span></span>
<span class="line"><span class="k1">LABEL_8:</span></span>
<span class="line"><span class="k4">  if</span><span class="k2"> (</span><span class="k2"> !</span><span class="k1">v6 </span><span class="k2">)</span></span>
<span class="line"><span class="k1">    v6 </span><span class="k2">=</span><span class="k1"> v3</span><span class="k2">[</span><span class="k3">1</span><span class="k2">];</span></span>
<span class="line"><span class="k4">  if</span><span class="k2"> (</span><span class="k1"> v6 </span><span class="k2">!=</span><span class="k1"> v4 </span><span class="k2">)</span></span>
<span class="line"><span class="k9">    std</span><span class="k2">::</span><span class="k9">_Hash</span><span class="k2">&#x3C;</span><span class="k9">std</span><span class="k2">::</span><span class="k9">_Umap_traits</span><span class="k2">&#x3C;</span><span class="k9">int</span><span class="k2">,</span><span class="k9">std</span><span class="k2">::</span><span class="k9">unique_ptr</span><span class="k2">&#x3C;</span><span class="k9">ListenerInfo</span><span class="k2">>,</span><span class="k9">std</span><span class="k2">::</span><span class="k9">_Uhash_compare</span><span class="k2">&#x3C;</span><span class="k9">int</span><span class="k2">,</span><span class="k9">std</span><span class="k2">::</span><span class="k9">hash</span><span class="k2">&#x3C;</span><span class="k9">int</span><span class="k2">>,</span><span class="k9">std</span><span class="k2">::</span><span class="k9">equal_to</span><span class="k2">&#x3C;</span><span class="k9">int</span><span class="k2">>>,</span><span class="k9">std</span><span class="k2">::</span><span class="k9">allocator</span><span class="k2">&#x3C;</span><span class="k9">std</span><span class="k2">::</span><span class="k9">pair</span><span class="k2">&#x3C;</span><span class="k9">int</span><span class="k4"> const</span><span class="k2">,</span><span class="k9">std</span><span class="k2">::</span><span class="k9">unique_ptr</span><span class="k2">&#x3C;</span><span class="k9">ListenerInfo</span><span class="k2">>>>,</span><span class="k3">0</span><span class="k2">>>::</span><span class="k8">_Erase</span><span class="k2">&#x3C;</span><span class="k9">int</span><span class="k2">>(</span></span>
<span class="line"><span class="k1">      v3</span><span class="k2">,</span></span>
<span class="line"><span class="k2">      &#x26;</span><span class="k1">v8</span><span class="k2">);</span></span>
<span class="line"><span class="k2">  *(</span><span class="k1">_DWORD </span><span class="k2">*)</span><span class="ka">this</span><span class="k2"> =</span><span class="k2"> -</span><span class="k3">1</span><span class="k2">;</span></span>
<span class="line"><span class="k2">}</span></span></code></pre></div>
<p>而问题出现在</p>
<div class="kit-code" data-lang="cpp" data-lines="1"><div class="kit-code-head"><span class="kit-code-title" data-kit-code-title="">C++</span><button type="button" class="kit-code-copy" data-kit-copy="" aria-label="复制代码" title="复制代码" data-copied-label="已复制"><svg class="kit-pixel-icon kit-icon-copy" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h9v1H5zM5 3h1v1H5zM13 3h1v7h-1zM2 4h9v1H2zM2 5h1v8H2zM10 5h1v8h-1zM4 6h5v1H4zM4 8h4v1H4zM11 9h3v1h-3zM4 10h5v1H4zM2 12h9v1H2z"/></svg><svg class="kit-pixel-icon kit-icon-done" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M12 3h2v1h-2zM11 4h3v1h-3zM10 5h3v1h-3zM9 6h3v1H9zM2 7h2v1H2zM8 7h3v1H8zM2 8h3v1H2zM7 8h3v1H7zM3 9h6v1H3zM4 10h4v1H4zM5 11h2v1H5z"/></svg></button></div><pre class="shiki kit-shiki" tabindex="0"><code><span class="line"><span class="k1">v6 </span><span class="k2">=</span><span class="k1"> v3</span><span class="k2">[</span><span class="k3">1</span><span class="k2">]</span></span></code></pre></div>
<p>于是,很显然,这与v3有关</p>
<h3 id="修复">修复<a class="kit-anchor" href="https://levimc.org/blog/2023/07/26/original-fix-BeeCrash/#修复" aria-hidden="true" tabindex="-1"><svg viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h2v2h3V2h2v2h2v2h-2v3h2v2h-2v2h-2v-2H7v2H5v-2H3V9h2V6H3V4h2zm2 4v3h3V6z"/></svg></a></h3>
<p>在恢复了BlockEventDispatcherToken的结构后,发现其拥有两个成员</p>
<div class="kit-code" data-lang="cpp" data-lines="4" data-linenos="" style="--kit-gutter:1ch"><div class="kit-code-head"><span class="kit-code-title" data-kit-code-title="">C++</span><button type="button" class="kit-code-copy" data-kit-copy="" aria-label="复制代码" title="复制代码" data-copied-label="已复制"><svg class="kit-pixel-icon kit-icon-copy" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h9v1H5zM5 3h1v1H5zM13 3h1v7h-1zM2 4h9v1H2zM2 5h1v8H2zM10 5h1v8h-1zM4 6h5v1H4zM4 8h4v1H4zM11 9h3v1h-3zM4 10h5v1H4zM2 12h9v1H2z"/></svg><svg class="kit-pixel-icon kit-icon-done" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M12 3h2v1h-2zM11 4h3v1h-3zM10 5h3v1h-3zM9 6h3v1H9zM2 7h2v1H2zM8 7h3v1H8zM2 8h3v1H2zM7 8h3v1H7zM3 9h6v1H3zM4 10h4v1H4zM5 11h2v1H5z"/></svg></button></div><pre class="shiki kit-shiki" tabindex="0"><code><span class="line"><span class="k4">class</span><span class="k9"> BlockEventDispatcherToken</span><span class="k2"> {</span></span>
<span class="line"><span class="k9">  ListenerHandle</span><span class="k1"> handle</span><span class="k2">;</span><span class="k1">  </span></span>
<span class="line"><span class="k9">  BlockEventDispatcher</span><span class="k4"> *</span><span class="k1">dispatcher</span><span class="k2">;</span></span>
<span class="line"><span class="k2">}</span></span></code></pre></div>
<p>而BlockEventDispatcher也有一个成员</p>
<div class="kit-code" data-lang="cpp" data-lines="3" data-linenos="" style="--kit-gutter:1ch"><div class="kit-code-head"><span class="kit-code-title" data-kit-code-title="">C++</span><button type="button" class="kit-code-copy" data-kit-copy="" aria-label="复制代码" title="复制代码" data-copied-label="已复制"><svg class="kit-pixel-icon kit-icon-copy" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h9v1H5zM5 3h1v1H5zM13 3h1v7h-1zM2 4h9v1H2zM2 5h1v8H2zM10 5h1v8h-1zM4 6h5v1H4zM4 8h4v1H4zM11 9h3v1h-3zM4 10h5v1H4zM2 12h9v1H2z"/></svg><svg class="kit-pixel-icon kit-icon-done" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M12 3h2v1h-2zM11 4h3v1h-3zM10 5h3v1h-3zM9 6h3v1H9zM2 7h2v1H2zM8 7h3v1H8zM2 8h3v1H2zM7 8h3v1H7zM3 9h6v1H3zM4 10h4v1H4zM5 11h2v1H5z"/></svg></button></div><pre class="shiki kit-shiki" tabindex="0"><code><span class="line"><span class="k4">class</span><span class="k9"> BlockEventDispatcher</span><span class="k2"> {</span></span>
<span class="line"><span class="k9">  std</span><span class="k2">::</span><span class="k9">unordered_map</span><span class="k2">&#x3C;</span><span class="k9">int</span><span class="k2">,</span><span class="k9"> std</span><span class="k2">::</span><span class="k9">unique_ptr</span><span class="k2">&#x3C;</span><span class="k9">ListenerInfo</span><span class="k2">>></span><span class="k1"> listeners</span><span class="k2">;</span></span>
<span class="line"><span class="k2">}</span></span></code></pre></div>
<p>很明显,从v3上分析,崩服问题和umap逃不了关系.<br>
于是,在unregister伪代码的研究下,我们得到了正常情况下,BlockEventDispatcherToken::unregister的大致逻辑</p>
<div class="kit-code" data-lang="cpp" data-lines="8" data-linenos="" style="--kit-gutter:1ch"><div class="kit-code-head"><span class="kit-code-title" data-kit-code-title="">C++</span><button type="button" class="kit-code-copy" data-kit-copy="" aria-label="复制代码" title="复制代码" data-copied-label="已复制"><svg class="kit-pixel-icon kit-icon-copy" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h9v1H5zM5 3h1v1H5zM13 3h1v7h-1zM2 4h9v1H2zM2 5h1v8H2zM10 5h1v8h-1zM4 6h5v1H4zM4 8h4v1H4zM11 9h3v1h-3zM4 10h5v1H4zM2 12h9v1H2z"/></svg><svg class="kit-pixel-icon kit-icon-done" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M12 3h2v1h-2zM11 4h3v1h-3zM10 5h3v1h-3zM9 6h3v1H9zM2 7h2v1H2zM8 7h3v1H8zM2 8h3v1H2zM7 8h3v1H7zM3 9h6v1H3zM4 10h4v1H4zM5 11h2v1H5z"/></svg></button></div><pre class="shiki kit-shiki" tabindex="0"><code><span class="line"><span class="k9">void</span><span class="k9"> BlockEventDispatcherToken</span><span class="k2">::</span><span class="k8">unregister</span><span class="k2">()</span><span class="k2"> {</span></span>
<span class="line"><span class="k4">  if</span><span class="k2"> (</span><span class="ka">this</span><span class="k2">-></span><span class="k7">handle</span><span class="k2"> !=</span><span class="k2"> -</span><span class="k3">1</span><span class="k2">){</span></span>
<span class="line"><span class="k9">    auto</span><span class="k1"> it </span><span class="k2">=</span><span class="ka"> this</span><span class="k2">-></span><span class="k7">dispatcher</span><span class="k2">-></span><span class="k7">listeners</span><span class="k2">.</span><span class="k8">find</span><span class="k2">(</span><span class="ka">this</span><span class="k2">-></span><span class="k7">handle</span><span class="k2">);</span></span>
<span class="line"><span class="k4">    if</span><span class="k2"> (</span><span class="k1">it </span><span class="k2">!=</span><span class="ka"> this</span><span class="k2">-></span><span class="k7">dispatcher</span><span class="k2">-></span><span class="k7">listeners</span><span class="k2">.</span><span class="k8">end</span><span class="k2">())</span></span>
<span class="line"><span class="ka">        this</span><span class="k2">-></span><span class="k7">dispatcher</span><span class="k2">-></span><span class="k7">listeners</span><span class="k2">.</span><span class="k8">erase</span><span class="k2">(</span><span class="k1">it</span><span class="k2">);</span></span>
<span class="line"><span class="ka">    this</span><span class="k2">-></span><span class="k7">handle</span><span class="k2"> =</span><span class="k2"> -</span><span class="k3">1</span><span class="k2">;</span></span>
<span class="line"><span class="k2">  }</span></span>
<span class="line"><span class="k2">}</span></span></code></pre></div>
<p>于是,我们大致知道了问题的所在,便可以着手修复工作了.</p>
<p>由于该问题的特殊性,引起该问题的本身就是内存回收和销毁的问题,而笔者并没有权限对源码进行阅读和查看,基于Windows平台本身在线程死亡后,便会自行清理,于是该问题的最容易解决的方案也就很明显了.<br>
笔者采用了直接对BlockEventDispatcherToken::unregister进行hook,自行实现了上面的伪代码中的清理部分的功能, 并且为了方便和稳定,在关服状态下便不主动对listeners进行回收,防止因为关服回收内存时候dispatcher出现问题.</p>
<div class="kit-code" data-lang="cpp" data-lines="15" data-linenos="" style="--kit-gutter:2ch"><div class="kit-code-head"><span class="kit-code-title" data-kit-code-title="">C++</span><button type="button" class="kit-code-copy" data-kit-copy="" aria-label="复制代码" title="复制代码" data-copied-label="已复制"><svg class="kit-pixel-icon kit-icon-copy" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h9v1H5zM5 3h1v1H5zM13 3h1v7h-1zM2 4h9v1H2zM2 5h1v8H2zM10 5h1v8h-1zM4 6h5v1H4zM4 8h4v1H4zM11 9h3v1h-3zM4 10h5v1H4zM2 12h9v1H2z"/></svg><svg class="kit-pixel-icon kit-icon-done" viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M12 3h2v1h-2zM11 4h3v1h-3zM10 5h3v1h-3zM9 6h3v1H9zM2 7h2v1H2zM8 7h3v1H8zM2 8h3v1H2zM7 8h3v1H7zM3 9h6v1H3zM4 10h4v1H4zM5 11h2v1H5z"/></svg></button></div><pre class="shiki kit-shiki" tabindex="0"><code><span class="line"><span class="k8">TInstanceHook</span><span class="k2">(</span><span class="k9">void</span><span class="k2">,</span><span class="k6">"?unregister@BlockEventDispatcherToken@@QEAAXXZ"</span><span class="k2">,</span><span class="k1">BlockEventDispatcherToken</span><span class="k2">){</span></span>
<span class="line"><span class="k4">  if</span><span class="k2"> (</span><span class="ka">this</span><span class="k2">-></span><span class="k7">handle</span><span class="k2"> !=</span><span class="k2"> -</span><span class="k3">1</span><span class="k2">)</span></span>
<span class="line"><span class="k2">  {</span></span>
<span class="line"><span class="k4">    if</span><span class="k2">(</span><span class="k9">ll</span><span class="k2">::</span><span class="k1">globalRuntimeConfig</span><span class="k2">.</span><span class="k7">serverStatus</span><span class="k2"> ==</span><span class="k9"> ll</span><span class="k2">::</span><span class="k9">LLServerStatus</span><span class="k2">::</span><span class="k1">Stopping</span><span class="k2">){</span></span>
<span class="line"><span class="k1">      logger</span><span class="k2">.</span><span class="k8">debug</span><span class="k2">(</span><span class="k6">"BlockEventDispatcherToken::unregister ignore unregister when server stopping"</span><span class="k2">);</span></span>
<span class="line"><span class="ka">      this</span><span class="k2">-></span><span class="k7">handle</span><span class="k2"> =</span><span class="k2"> -</span><span class="k3">1</span><span class="k2">;</span></span>
<span class="line"><span class="k4">      return</span><span class="k2">;</span></span>
<span class="line"><span class="k2">    }</span></span>
<span class="line"><span class="k9">    auto</span><span class="k1"> it </span><span class="k2">=</span><span class="ka"> this</span><span class="k2">-></span><span class="k7">dispatcher</span><span class="k2">-></span><span class="k7">listeners</span><span class="k2">.</span><span class="k8">find</span><span class="k2">(</span><span class="ka">this</span><span class="k2">-></span><span class="k7">handle</span><span class="k2">);</span></span>
<span class="line"><span class="k4">    if</span><span class="k2"> (</span><span class="k1">it </span><span class="k2">!=</span><span class="ka"> this</span><span class="k2">-></span><span class="k7">dispatcher</span><span class="k2">-></span><span class="k7">listeners</span><span class="k2">.</span><span class="k8">end</span><span class="k2">())</span></span>
<span class="line"><span class="ka">        this</span><span class="k2">-></span><span class="k7">dispatcher</span><span class="k2">-></span><span class="k7">listeners</span><span class="k2">.</span><span class="k8">erase</span><span class="k2">(</span><span class="k1">it</span><span class="k2">);</span></span>
<span class="line"><span class="ka">    this</span><span class="k2">-></span><span class="k7">handle</span><span class="k2"> =</span><span class="k2"> -</span><span class="k3">1</span><span class="k2">;</span></span>
<span class="line"><span class="k4">    return</span><span class="k2">;</span></span>
<span class="line"><span class="k2">  }</span></span>
<span class="line"><span class="k2">}</span></span></code></pre></div>
<h3 id="总结">总结<a class="kit-anchor" href="https://levimc.org/blog/2023/07/26/original-fix-BeeCrash/#总结" aria-hidden="true" tabindex="-1"><svg viewBox="0 0 16 16" width="16" height="16" shape-rendering="crispEdges" aria-hidden="true" fill="currentColor"><path d="M5 2h2v2h3V2h2v2h2v2h-2v3h2v2h-2v2h-2v-2H7v2H5v-2H3V9h2V6H3V4h2zm2 4v3h3V6z"/></svg></a></h3>
<p>该问题的出现和修复其实并没有那么快速,前期因为经验不足而走错了方向.<br>
但是,该问题的出现也让笔者对BDS的内存管理方面有了更深的认识,也算是有点收获了.</p>]]></content:encoded>
  </item>
</channel>
</rss>
