<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: bekoo</title>
    <description>The latest articles on DEV Community by bekoo (@bekoo).</description>
    <link>https://dev.to/bekoo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F545869%2F6daa2575-150d-4508-a9fd-e47b5d767588.jpeg</url>
      <title>DEV Community: bekoo</title>
      <link>https://dev.to/bekoo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bekoo"/>
    <language>en</language>
    <item>
      <title>Manipulating QEMU Hardwares with Bootkit</title>
      <dc:creator>bekoo</dc:creator>
      <pubDate>Fri, 19 Jun 2026 19:54:34 +0000</pubDate>
      <link>https://dev.to/bekoo/manipulating-qemu-hardwares-with-bootkit-4gl7</link>
      <guid>https://dev.to/bekoo/manipulating-qemu-hardwares-with-bootkit-4gl7</guid>
      <description>&lt;p&gt;Boot a fresh QEMU instance and run &lt;strong&gt;dmidecode -t 1&lt;/strong&gt; inside it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;System Information
    Manufacturer: QEMU
    Product Name: Standard PC (Q35 + ICH9, 2009)
    Version: pc-q35-8.2
    Serial Number: Not Specified
    UUID: ...
    Family: Not Specified
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That output is not just cosmetic. Every piece of software that has a reason to know whether it is running inside a virtual machine reads exactly this table — EDRs, anti-cheat engines, sandbox detectors, hypervisor fingerprinting routines. The string "QEMU" in the Manufacturer field is, for most of them, a hard stop. Analysis terminates, behavior changes, the sample goes quiet.&lt;/p&gt;

&lt;p&gt;The question is where this data comes from and whether it can be changed before any of those tools get a chance to read it. The answer to the second part is yes — and the mechanism is a DXE driver that runs inside the firmware, before the OS loader is even invoked. This is what makes it a bootkit in the strict sense: code that executes at the firmware level and shapes what the operating system inherits.&lt;/p&gt;

&lt;h2&gt;
  
  
  What SMBIOS Actually Is
&lt;/h2&gt;

&lt;p&gt;SMBIOS — System Management BIOS — is a specification maintained by DMTF. It defines a standard format for firmware to expose hardware identity information to the operating system. The spec has nothing to do with BIOS in the sense of boot firmware. It is purely a data contract: a layout agreement that says "here is where we put the machine's identity, and here is the structure you use to read it."&lt;/p&gt;

&lt;p&gt;At some point during POST, the firmware constructs a contiguous block of records in physical memory and registers its location in one of two places depending on the SMBIOS version. For versions below 3.0, a 32-bit entry point structure sits somewhere between &lt;strong&gt;0xF0000&lt;/strong&gt; and 0xFFFFF** carrying the signature &lt;strong&gt;&lt;em&gt;SM&lt;/em&gt;&lt;/strong&gt;. For 3.0 and above, a 64-bit entry point with &lt;strong&gt;&lt;em&gt;SM3&lt;/em&gt;&lt;/strong&gt; can sit anywhere in the low 4GB. The OS finds this entry point, pulls the table address out of it, and from that point on it has everything it needs.&lt;/p&gt;

&lt;p&gt;The table itself is a flat list. No index, no hash map, no random access mechanism. Records sit in memory back to back, and the only way to find a specific one is to walk from the beginning.&lt;/p&gt;

&lt;p&gt;Each record is called a structure, and each structure has a type number. Type 0 is BIOS information. Type 1 is system information — manufacturer, product name, serial number, UUID. Type 2 is baseboard. Type 4 is processor. The list goes on to Type 127, which is the End-of-Table marker. When you hit 127, you stop walking.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Single Record's Memory Layout
&lt;/h2&gt;

&lt;p&gt;Each structure has two distinct sections sitting contiguously in memory.&lt;/p&gt;

&lt;p&gt;The first section is the &lt;strong&gt;formatted region&lt;/strong&gt;: a fixed-length header followed by typed fields. The header is always four bytes — one byte for the type, one for the length of the formatted region, two for a handle that uniquely identifies this record. After the header come the actual fields, and their sizes and meanings are defined by the spec per type. For Type 1, the formatted region is 27 bytes total.&lt;/p&gt;

&lt;p&gt;The second section is the &lt;strong&gt;string pool&lt;/strong&gt;. Immediately after the last byte of the formatted region, the firmware writes a sequence of null-terminated ASCII strings back to back. The entire pool is terminated by a double null — an empty string that signals the end.&lt;/p&gt;

&lt;p&gt;Here is the part that trips people up: the fields in the formatted region do not hold strings. They hold one-byte indices. The &lt;strong&gt;Manufacturer&lt;/strong&gt; field in Type 1 is not &lt;strong&gt;"QEMU"&lt;/strong&gt; — it is &lt;strong&gt;0x00&lt;/strong&gt;, meaning "first string in the pool." &lt;strong&gt;ProductName&lt;/strong&gt; is &lt;strong&gt;0x02&lt;/strong&gt;. &lt;strong&gt;SerialNumber&lt;/strong&gt; is &lt;strong&gt;0x03&lt;/strong&gt;. An index of zero means the field is not populated.&lt;/p&gt;

&lt;p&gt;So in memory, QEMU's Type 1 record looks roughly like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ Type=1 | Len=27 | Handle=0x0001 | Manufacturer=1 | ProductName=2 | Version=3 | ... ]
[ "QEMU\0" ][ "Standard PC (Q35 + ICH9, 2009)\0" ][ "pc-q35-8.2\0" ][ \0 ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To read &lt;strong&gt;Manufacturer&lt;/strong&gt;, you skip &lt;strong&gt;Len&lt;/strong&gt; bytes from the start of the record to land in the string pool, then walk forward past &lt;strong&gt;(index - 1)&lt;/strong&gt; null terminators to reach the target string. This is not an abstraction — this is literally what every tool that reads SMBIOS does at the bottom of its call stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Structure Exists
&lt;/h2&gt;

&lt;p&gt;The split between fixed fields and a string pool is not accidental. The fixed region has a known, spec-defined size per type, which means a parser can jump over any record it does not understand without reading its contents — it just takes &lt;strong&gt;Hdr.Length&lt;/strong&gt; bytes, skips to the string pool terminator, and moves on. This forward-compatibility property is intentional. A parser written against SMBIOS 2.4 can safely traverse a 3.1 structure it has never seen before.&lt;/p&gt;

&lt;p&gt;The string pool being variable-length and trailing means the spec does not have to reserve fixed-width character buffers inside the struct for every string field. Serial numbers vary. Product names vary. Packing them into the formatted region would either waste space with padding or impose arbitrary length limits. The trailing pool sidesteps both problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Code
&lt;/h2&gt;

&lt;p&gt;Now that the memory layout is clear, the driver makes considerably more sense.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;Uefi.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;IndustryStandard/SmBios.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;Library/BaseMemoryLib.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;Library/UefiBootServicesTableLib.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;Library/UefiLib.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;Protocol/Smbios.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;"CloakProfile.h"&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;EFI_SMBIOS_HANDLE&lt;/span&gt; &lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;UINT8&lt;/span&gt;             &lt;span class="n"&gt;StrNo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;CONST&lt;/span&gt; &lt;span class="n"&gt;CHAR8&lt;/span&gt;      &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Val&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;CLOAK_STR&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cp"&gt;#define CLOAK_MAX_STR 64
&lt;/span&gt;&lt;span class="n"&gt;STATIC&lt;/span&gt; &lt;span class="n"&gt;CLOAK_STR&lt;/span&gt; &lt;span class="n"&gt;mStr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;CLOAK_MAX_STR&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="n"&gt;STATIC&lt;/span&gt; &lt;span class="n"&gt;UINTN&lt;/span&gt;     &lt;span class="n"&gt;mStrCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cp"&gt;#define CLOAK_PUSH(H, IDX, VAL)                                  \
  do {                                                           \
    if ((IDX) != 0 &amp;amp;&amp;amp; mStrCount &amp;lt; CLOAK_MAX_STR) {               \
      mStr[mStrCount].Handle = (H);                              \
      mStr[mStrCount].StrNo  = (UINT8)(IDX);                     \
      mStr[mStrCount].Val    = (VAL);                            \
      mStrCount++;                                               \
    }                                                            \
  } while (0)
&lt;/span&gt;
&lt;span class="n"&gt;EFI_STATUS&lt;/span&gt;
&lt;span class="n"&gt;EFIAPI&lt;/span&gt;
&lt;span class="nf"&gt;VmCloakDxeEntryPoint&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;EFI_HANDLE&lt;/span&gt;        &lt;span class="n"&gt;ImageHandle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;EFI_SYSTEM_TABLE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;SystemTable&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;EFI_SMBIOS_PROTOCOL&lt;/span&gt;     &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;SmBios&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;EFI_STATUS&lt;/span&gt;               &lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;EFI_SMBIOS_HANDLE&lt;/span&gt;        &lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;EFI_SMBIOS_TABLE_HEADER&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Rec&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;SMBIOS_TABLE_TYPE1&lt;/span&gt;      &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gBS&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;LocateProtocol&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;gEfiSmbiosProtocolGuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VOID&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;SmBios&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EFI_ERROR&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Print&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;L"Failed to locate SMBIOS protocol: %r&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;mStrCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;Handle&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SMBIOS_HANDLE_PI_RESERVED&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SmBios&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;GetNext&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SmBios&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Rec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;EFI_SUCCESS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Rec&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;EFI_SMBIOS_TYPE_SYSTEM_INFORMATION&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SMBIOS_TABLE_TYPE1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;Rec&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;UINT8&lt;/span&gt; &lt;span class="n"&gt;Uuid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CLOAK_SYS_UUID&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;CopyMem&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Uuid&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="n"&gt;CLOAK_PUSH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Manufacturer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CLOAK_SYS_MANUFACTURER&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;CLOAK_PUSH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ProductName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="n"&gt;CLOAK_SYS_PRODUCT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;CLOAK_PUSH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="n"&gt;CLOAK_SYS_VERSION&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;CLOAK_PUSH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;SerialNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CLOAK_SYS_SERIAL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;CLOAK_PUSH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;SKUNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="n"&gt;CLOAK_SYS_SKU&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;CLOAK_PUSH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Family&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="n"&gt;CLOAK_SYS_FAMILY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UINTN&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;mStrCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;EFI_SMBIOS_HANDLE&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mStr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;UINTN&lt;/span&gt;             &lt;span class="n"&gt;sn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mStr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;StrNo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;SmBios&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;UpdateString&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SmBios&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;sn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CHAR8&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;mStr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Val&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// UpdateString may have reallocated — Type pointer is now stale. Re-fetch.&lt;/span&gt;
  &lt;span class="n"&gt;Handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SMBIOS_HANDLE_PI_RESERVED&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SmBios&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;GetNext&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SmBios&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Rec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;EFI_SUCCESS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Rec&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;EFI_SMBIOS_TYPE_SYSTEM_INFORMATION&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SMBIOS_TABLE_TYPE1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;Rec&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;CHAR8&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;StrPtr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CHAR8&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Hdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;UINT8&lt;/span&gt;  &lt;span class="n"&gt;Idx&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Manufacturer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UINT8&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;Idx&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;StrPtr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;StrPtr&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="n"&gt;StrPtr&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;Print&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;L"Manufacturer after update: %a&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StrPtr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;EFI_SUCCESS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Locating the Protocol
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gBS&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;LocateProtocol&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;gEfiSmbiosProtocolGuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VOID&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;SmBios&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The driver's first move is finding &lt;strong&gt;EFI_SMBIOS_PROTOCOL&lt;/strong&gt;. This is the firmware's own interface for reading and modifying SMBIOS records at runtime — before the OS is handed control. LocateProtocol walks the installed protocol list and hands back a pointer to the implementation. If this call fails, there is nothing to work with and the driver bails immediately.&lt;/p&gt;

&lt;h3&gt;
  
  
  Walking the Table
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;Handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SMBIOS_HANDLE_PI_RESERVED&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SmBios&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;GetNext&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SmBios&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Rec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;EFI_SUCCESS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Rec&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;EFI_SMBIOS_TYPE_SYSTEM_INFORMATION&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;SMBIOS_HANDLE_PI_RESERVED&lt;/strong&gt; is the sentinel value that tells &lt;strong&gt;GetNext&lt;/strong&gt; to start from the beginning of the table. Each call advances &lt;strong&gt;Handle&lt;/strong&gt; to the next record and writes the record pointer into &lt;strong&gt;Rec&lt;/strong&gt;. This is the same traversal described in the layout section — no shortcut exists, so the driver walks every record until it finds what it needs. The switch on &lt;strong&gt;Rec-&amp;gt;Type&lt;/strong&gt; means everything except Type 1 passes through untouched.&lt;/p&gt;

&lt;h3&gt;
  
  
  UUID vs. Strings — Two Different Problems
&lt;/h3&gt;

&lt;p&gt;Inside the Type 1 handler, the driver deals with two categories of fields.&lt;/p&gt;

&lt;p&gt;UUID is a binary field — 16 raw bytes embedded directly in the formatted region. It has no pool index, it does not live in the string section. The write is direct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;UINT8&lt;/span&gt; &lt;span class="n"&gt;Uuid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CLOAK_SYS_UUID&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;CopyMem&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Uuid&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CopyMem overwrites the bytes in place. No pool traversal, no protocol call needed.&lt;/p&gt;

&lt;p&gt;String fields are a different problem. &lt;strong&gt;Manufacturer&lt;/strong&gt;, &lt;strong&gt;ProductName&lt;/strong&gt;, &lt;strong&gt;SerialNumber&lt;/strong&gt; and the rest are one-byte indices into the string pool. To change what they resolve to, you cannot touch the index byte — it already points to the right slot. You have to rewrite the actual string sitting at that slot in the pool, which may be a different length than what was there before. That is &lt;strong&gt;UpdateString&lt;/strong&gt;'s job, and it is the reason the queue exists.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Queue — Why Not Call UpdateString Immediately
&lt;/h3&gt;

&lt;p&gt;When &lt;strong&gt;UpdateString&lt;/strong&gt; replaces a string with one of a different length, it has to resize the record in memory. The firmware may reallocate the entire block. If it does, &lt;strong&gt;Type&lt;/strong&gt; — the pointer the driver is currently holding — becomes stale. It points to an address the firmware has already moved on from.&lt;/p&gt;

&lt;p&gt;The driver avoids this by never calling &lt;strong&gt;UpdateString&lt;/strong&gt; during the traversal loop. Every pending update is queued into &lt;strong&gt;mStr&lt;/strong&gt; instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#define CLOAK_PUSH(H, IDX, VAL)                                  \
  do {                                                           \
    if ((IDX) != 0 &amp;amp;&amp;amp; mStrCount &amp;lt; CLOAK_MAX_STR) {               \
      mStr[mStrCount].Handle = (H);                              \
      mStr[mStrCount].StrNo  = (UINT8)(IDX);                     \
      mStr[mStrCount].Val    = (VAL);                            \
      mStrCount++;                                               \
    }                                                            \
  } while (0)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;IDX != 0&lt;/strong&gt; guards against unpopulated fields — index zero means "not present" in the SMBIOS spec, and passing it to &lt;strong&gt;UpdateString&lt;/strong&gt; is undefined territory. Everything else is pushed onto the queue with its handle, its string slot number, and the replacement value.&lt;/p&gt;

&lt;p&gt;Once the traversal loop exits and &lt;strong&gt;Type&lt;/strong&gt; is no longer being touched, the driver drains the queue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UINTN&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;mStrCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;EFI_SMBIOS_HANDLE&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mStr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;UINTN&lt;/span&gt;             &lt;span class="n"&gt;sn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mStr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;StrNo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;SmBios&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;UpdateString&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SmBios&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;sn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CHAR8&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;mStr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Val&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reallocation can now happen freely. Nothing holds a live pointer into the record at this point.&lt;/p&gt;

&lt;h3&gt;
  
  
  Refreshing the Pointer and Verifying
&lt;/h3&gt;

&lt;p&gt;After UpdateString runs, the old Type pointer cannot be trusted regardless of whether reallocation actually occurred — the driver has no way to determine that. So it runs GetNext again from scratch to obtain a fresh pointer to the Type 1 record, then manually traverses the string pool to confirm the write landed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;CHAR8&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;StrPtr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CHAR8&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Hdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;UINT8&lt;/span&gt;  &lt;span class="n"&gt;Idx&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Manufacturer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UINT8&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;Idx&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;StrPtr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;StrPtr&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;StrPtr&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;Print&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;L"Manufacturer after update: %a&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StrPtr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Type + Hdr.Length lands exactly at the start of the string pool — the formatted region ends there and strings begin immediately after. The loop skips forward (Manufacturer - 1) null terminators to reach the correct slot. The inner &lt;strong&gt;while (*StrPtr) StrPtr++&lt;/strong&gt; walks past the current string's characters; the StrPtr++ after it steps over the null terminator. What remains at StrPtr is the target string, read back directly from the record the firmware now holds.&lt;/p&gt;

&lt;p&gt;Here's the result from the shell: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcpapqoqb0188l05iqheu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcpapqoqb0188l05iqheu.png" alt=" " width="429" height="117"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or we can check out this in System information tool of Windows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyihjara6hhvrmeqg63co.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyihjara6hhvrmeqg63co.png" alt=" " width="460" height="92"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Although i'm poor, i've ROG STRIX :&amp;gt;  &lt;/p&gt;

</description>
      <category>bootkit</category>
      <category>edk2</category>
    </item>
    <item>
      <title>[My Debugger Journey] Managing Execution Modes with NASM</title>
      <dc:creator>bekoo</dc:creator>
      <pubDate>Fri, 12 Jun 2026 07:40:16 +0000</pubDate>
      <link>https://dev.to/bekoo/my-debugger-journey-managing-execution-modes-with-nasm-10a1</link>
      <guid>https://dev.to/bekoo/my-debugger-journey-managing-execution-modes-with-nasm-10a1</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Starting from Reset Vector&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;I've already documented &lt;strong&gt;Reset Vector&lt;/strong&gt; in &lt;a href="https://0xbekoo.github.io/blogs/a-journey-from-reset-vector/" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;. You can read this header from my blog in detail. &lt;/p&gt;

&lt;p&gt;Every x86-64 processor starts its life in a mode that has more in common with an 8086 from 1978 than with the machine you're actually running. At power-on, the hardware enforces a rigid initialization contract: CS is loaded with a base of 0xFFFF0000, the instruction pointer sits at 0xFFF0, and the resulting physical address — 0xFFFFFFF0 — is the reset vector, the first fetch the processor will ever make. From that point, control belongs to firmware. (Vol. 3A, §9.1.4 — Processor State After Reset).&lt;/p&gt;

&lt;p&gt;What makes this interesting from a systems perspective is that the processor isn't truly in Real Mode at this stage — it's in a transitional state where the CS descriptor cache holds a base of 0xFFFF0000, but the visible CS register still reads 0xF000. The first far jump drops A20–A31 and collapses the processor into the lower megabyte, at which point you're in conventional 16-bit Real Mode with the full segment:offset constraint: physical address = segment × 16 + offset, maximum addressable space 1MB. (Vol. 3A, §9.1 — Processor State After Reset; i386 Reference §10.2.3).&lt;/p&gt;

&lt;p&gt;Here's a figure from the intel's volume:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg2bzpkdi41d44lmlie7k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg2bzpkdi41d44lmlie7k.png" alt=" " width="800" height="478"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can only know the state of the processor via CR0 register.&lt;/p&gt;

&lt;h3&gt;
  
  
  Real-Address Mode and the Segmented Memory Model
&lt;/h3&gt;

&lt;p&gt;Real-Address Mode predates Protected Mode entirely. Before the 80286 introduced hardware-level memory protection and privilege rings, it was the only execution mode available on x86 hardware. The addressing model has not changed since the 8086: a 16-bit segment register is shifted left by 4 bits and added to a 16-bit offset to produce a physical address.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Physical Address = (Segment × 16) + Offset&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since both values are 16-bit, the maximum expressible address is 0xFFFF × 16 + 0xFFFF = 0x10FFEF — but the address bus on the original 8086 was 20 bits wide, so anything above 0x000FFFFF wraps. The addressable range is effectively capped at 1MB.&lt;/p&gt;

&lt;p&gt;As a concrete example: if CS holds 0x2000 and IP holds 0x1234, the physical address resolves as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0x2000 × 16 = 0x20000
0x20000 + 0x1234 = 0x21234
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This calculation applies to every segment register — CS, DS, ES, SS — and to every memory access the processor makes in Real Mode. There is no page table, no descriptor table, no privilege check. The segment value is a raw base, not an index into any structure. &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Calculating 0xFFFF0000 Address&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;First step is to calculate the address. &lt;/p&gt;

&lt;p&gt;In all x86 processors, Real-Address Mode exists for one reason: backward compatibility with the 8086. It predates Protected Mode entirely, and every x86 processor since has initialized into it — not because it is useful at boot, but because the architecture requires it. The addressing model is unchanged from 1978: a 16-bit segment register is shifted left by 4 bits and added to a 16-bit offset, producing a 20-bit physical address. The addressable range is capped at 0x000FFFFF — the first 1MB.&lt;/p&gt;

&lt;p&gt;This creates an immediate contradiction. The reset vector sits at 0xFFFFFFF0, the top of the 32-bit address space — well outside what Real Mode addressing can express. A processor that is both in Real Mode and fetching from 0xFFFFFFF0 should not be possible under the standard model.&lt;/p&gt;

&lt;p&gt;The resolution is in the segment register's hidden descriptor cache:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs3xaukndxc4ffaz2rezg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs3xaukndxc4ffaz2rezg.png" alt=" " width="800" height="464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's the trick: : Upon reset, the CS register’s Visible Selector is set to 0xF000, but the processor forcibly sets the CS Hidden Base Address to FFFF_0000 (Don’t forget Real Mode’s Segment:Offset addresses).&lt;/p&gt;

&lt;p&gt;At reset, the visible CS register holds 0xF000, but the processor loads the cache with a base of 0xFFFF0000 rather than the value the normal shift-and-add calculation would produce. This base is hardwired into the reset state and bypasses the Real Mode formula entirely. &lt;/p&gt;

&lt;p&gt;Therefore, the processor calculates the Reset Vector by combining the Hidden Base of CS with the value of EIP:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;CS Base (0xFFFF0000) + EIP (0xFFF0) = 0xFFFFFFF0&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can also see this in QEMU:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4theaghzd4csxlxdesf7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4theaghzd4csxlxdesf7.png" alt=" " width="544" height="230"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the reset, Paging (Bit 31) is disabled and Protection (Bit 0) is not enabled. If this Protection bit were 1, then it would indicate that the processer is in Protection Mode. Also we can see the result of CR0 in GDB:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjk65clnuxxu4yj3zzv0n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjk65clnuxxu4yj3zzv0n.png" alt=" " width="722" height="88"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now the important section will come. &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Fetching Instruction from the Reset Vector&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Once the processor resolves 0xFFFFFFF0 and places it on the address bus, it has no knowledge of what backs that address. The processor issues the fetch; where it lands is entirely the chipset's decision.&lt;/p&gt;

&lt;p&gt;The chipset — historically the Southbridge, on modern platforms the PCH (Platform Controller Hub) — sits between the processor and every peripheral on the board and monitors every address the processor puts on the bus. When it sees a request targeting the high-memory range around 0xFFFFFFF0, it does not forward that request to DRAM. DRAM has not been initialized at this point and would return garbage. Instead, the chipset routes the request to the SPI flash ROM carrying the BIOS or UEFI firmware.&lt;/p&gt;

&lt;p&gt;The mechanism that governs this routing is a chipset configuration register: FWH_DEC_EN1 (Firmware Hub Decode Enable 1), located in the LPC (Low Pin Count) Interface Bridge register space. Its function is straightforward — it defines which physical memory ranges the chipset should redirect to the Firmware Hub rather than system RAM. At power-on, this register is preset by hardware to cover the reset vector region, so the very first fetch the processor issues is silently redirected to ROM before any software has had a chance to configure anything. The Intel 5 Series / 3400 Series Chipset datasheet documents this at page 487:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhes62uzuf98rdm6tfild.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhes62uzuf98rdm6tfild.png" alt=" " width="800" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The mechanism works as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Decoding Ranges:&lt;/strong&gt; The FWH_DEC_EN1 register contains specific bits (like Bit 7, Bit 6, etc.) that correspond to different memory segments at the top of the 4GB address space (e.g., the range FFFF_0000h to FFFF_FFFFh).&lt;/li&gt;
&lt;/ul&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Redirection:&lt;/strong&gt; When the system resets, the hardware defaults for these bits are typically set to “Enabled”. This ensures that when the CPU requests 0xFFFFFFF0, the chipset instantly recognizes this address falls within the “BIOS Range.”&lt;/li&gt;
&lt;/ul&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fetching:&lt;/strong&gt; Instead of sending the request to the DRAM controller, the chipset asserts the LPC (or SPI) bus signals, activating the Flash ROM chip.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's a diagram:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy5bi8f6vl4a2wqwh68og.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy5bi8f6vl4a2wqwh68og.png" alt=" " width="799" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In short, the area the processor wants to read (0xFFFFFF0) is actually located in the BIOS chip, &lt;strong&gt;not in RAM&lt;/strong&gt;, and the instruction is taken from this address in the BIOS chip within the Reset Vector and given to the processor.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Switching from Real Mode to Protection Mode&lt;/strong&gt;
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;0x0 - A20 Gate&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;The 8086 had a 20-bit address bus — lines A0 through A19. This imposed a hard ceiling of 2²⁰ bytes, or exactly 1MB, on the physical address space. There was no 21st line. Any address calculation that produced a value above 0xFFFFF would silently lose bit 20, causing the address to wrap back into the bottom of the memory map. This was not a bug in the 8086 — it was a physical constraint of the hardware.&lt;/p&gt;

&lt;p&gt;The problem surfaced with the 80286. Its address bus was 24 bits wide, meaning bit 20 now had a real wire behind it and addresses above 0xFFFFF resolved correctly. Wraparound no longer happened. This broke a class of DOS-era software that had come to depend on it — specifically, programs that constructed segment:offset pairs deliberately producing addresses just above 0x100000, relying on the wraparound to land back in low memory. On the 80286, those same addresses resolved to their actual targets above 1MB instead, and the programs failed.&lt;/p&gt;

&lt;p&gt;The hardware fix was the A20 gate: a logic circuit, originally wired through the 8042 keyboard controller, that could force address line 20 to zero regardless of what the processor put on the bus. With the gate closed, bit 20 was always 0, wraparound was preserved, and legacy software ran correctly. With the gate open, bit 20 propagated normally and the full address space above 1MB became accessible.&lt;/p&gt;

&lt;p&gt;We can simply open this gate with NASM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="c1"&gt;; 0x92 - A20&lt;/span&gt;
&lt;span class="nf"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;al&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x92&lt;/span&gt;  &lt;span class="c1"&gt;; Get the current value &lt;/span&gt;
&lt;span class="nf"&gt;or&lt;/span&gt; &lt;span class="nb"&gt;al&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x02&lt;/span&gt;  &lt;span class="c1"&gt;; Open A20&lt;/span&gt;
&lt;span class="nf"&gt;and&lt;/span&gt; &lt;span class="nb"&gt;al&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0xFE&lt;/span&gt;
&lt;span class="nf"&gt;out&lt;/span&gt; &lt;span class="mh"&gt;0x92&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;al&lt;/span&gt; &lt;span class="c1"&gt;; Write the bit again&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is required for Protection Mode.&lt;/p&gt;

&lt;h4&gt;
  
  
  0x1 - GDT and Switching to Protected Mode
&lt;/h4&gt;

&lt;p&gt;With A20 open, the next requirement before touching CR0.PE is a valid Global Descriptor Table. In Real Mode, segment registers hold raw base values. In Protected Mode, they become selectors — 16-bit indices into the GDT — and the processor uses the descriptor at that index to determine the segment's base address, limit, and access rights. Attempting to set CR0.PE without a loaded GDT means the first segment register access in Protected Mode will reference garbage, producing a &lt;strong&gt;#GP&lt;/strong&gt; or a silent wrong-base calculation.&lt;/p&gt;

&lt;p&gt;The GDT used here defines five descriptors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="nl"&gt;gdt_start:&lt;/span&gt;
    &lt;span class="kd"&gt;dq&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;                                    &lt;span class="c1"&gt;; 0x00 null (required)&lt;/span&gt;
    &lt;span class="kd"&gt;dw&lt;/span&gt; &lt;span class="mh"&gt;0xFFFF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x0000&lt;/span&gt;
    &lt;span class="kd"&gt;db&lt;/span&gt; &lt;span class="mh"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mb"&gt;10011010b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mb"&gt;11001111b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x00&lt;/span&gt;     &lt;span class="c1"&gt;; 0x08 code32&lt;/span&gt;
    &lt;span class="kd"&gt;dw&lt;/span&gt; &lt;span class="mh"&gt;0xFFFF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x0000&lt;/span&gt;
    &lt;span class="kd"&gt;db&lt;/span&gt; &lt;span class="mh"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mb"&gt;10010010b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mb"&gt;11001111b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x00&lt;/span&gt;     &lt;span class="c1"&gt;; 0x10 data32&lt;/span&gt;
    &lt;span class="kd"&gt;dw&lt;/span&gt; &lt;span class="mh"&gt;0xFFFF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x0000&lt;/span&gt;
    &lt;span class="kd"&gt;db&lt;/span&gt; &lt;span class="mh"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mb"&gt;10011010b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mb"&gt;10101111b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x00&lt;/span&gt;     &lt;span class="c1"&gt;; 0x18 code64  (L=1)&lt;/span&gt;
    &lt;span class="kd"&gt;dw&lt;/span&gt; &lt;span class="mh"&gt;0xFFFF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x0000&lt;/span&gt;
    &lt;span class="kd"&gt;db&lt;/span&gt; &lt;span class="mh"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mb"&gt;10010010b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mb"&gt;11001111b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x00&lt;/span&gt;     &lt;span class="c1"&gt;; 0x20 data64&lt;/span&gt;
&lt;span class="nl"&gt;gdt_end:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The null descriptor at offset 0 is mandatory — the processor reserves it and any selector that resolves to it raises &lt;strong&gt;#GP&lt;/strong&gt;. The 32-bit code and data descriptors (0x08, 0x10) have &lt;strong&gt;D/B=1&lt;/strong&gt;, placing the processor in 32-bit default operand and address size when CS holds &lt;strong&gt;0x08&lt;/strong&gt;. The 64-bit code descriptor at &lt;strong&gt;0x18&lt;/strong&gt; has &lt;strong&gt;L=1&lt;/strong&gt; and &lt;strong&gt;D/B=0&lt;/strong&gt; — this is the bit that tells the processor to decode instructions as 64-bit after the final far jump.&lt;/p&gt;

&lt;p&gt;The descriptor table is loaded into GDTR with &lt;strong&gt;lgdt&lt;/strong&gt;, then &lt;strong&gt;CR0.PE&lt;/strong&gt; is set and control transfers immediately to the 32-bit code selector:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="nf"&gt;lgdt&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;gdt_desc&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;eax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;cr0&lt;/span&gt;
&lt;span class="nf"&gt;or&lt;/span&gt;  &lt;span class="nb"&gt;eax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;cr0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;eax&lt;/span&gt;
&lt;span class="nf"&gt;jmp&lt;/span&gt; &lt;span class="mh"&gt;0x08&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;pMode32&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The far jump is not optional. Setting &lt;strong&gt;CR0.PE&lt;/strong&gt; does not flush the prefetch queue. Instructions fetched under Real Mode decode rules may still be in the pipeline, and executing them in Protected Mode produces undefined behavior. The far jump serializes the processor and reloads CS from the GDT, putting the descriptor cache into a consistent state for Protected Mode operation. &lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;0x2 - Paging and the Transition to Long Mode&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Protected Mode with paging disabled gives a flat 32-bit physical address space — useful for setup, but insufficient for Long Mode. The architecture requires paging to be active before Long Mode can be enabled, and it requires PAE (Physical Address Extension) specifically, since Long Mode uses 4-level page tables with 64-bit entries.&lt;/p&gt;

&lt;p&gt;The page table structure is built at fixed physical addresses before any control register is touched:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;edi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x13000&lt;/span&gt;
&lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;ecx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x3000&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="nf"&gt;xor&lt;/span&gt; &lt;span class="nb"&gt;eax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;eax&lt;/span&gt;
&lt;span class="nf"&gt;rep&lt;/span&gt; &lt;span class="nv"&gt;stosd&lt;/span&gt;            &lt;span class="c1"&gt;; zero PML4 + PDPT + PD (3 × 4KB)&lt;/span&gt;

&lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="kt"&gt;dword&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mh"&gt;0x13000&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mh"&gt;0x14003&lt;/span&gt;  &lt;span class="c1"&gt;; PML4[0] → PDPT (present + writable)&lt;/span&gt;
&lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="kt"&gt;dword&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mh"&gt;0x14000&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mh"&gt;0x15003&lt;/span&gt;  &lt;span class="c1"&gt;; PDPT[0] → PD   (present + writable)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Page Directory is filled with 512 entries, each mapping a 2MB physical region:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;    &lt;span class="c1"&gt;; [...]&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;edi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x15000&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;eax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x83&lt;/span&gt;           &lt;span class="c1"&gt;; P=1, W=1, PS=1 (2MB page)&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;ecx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;

&lt;span class="nl"&gt;.pd:&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="kt"&gt;dword&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;edi&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;   &lt;span class="nb"&gt;eax&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="kt"&gt;dword&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;edi&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="nf"&gt;add&lt;/span&gt; &lt;span class="nb"&gt;eax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x200000&lt;/span&gt;
    &lt;span class="nf"&gt;add&lt;/span&gt; &lt;span class="nb"&gt;edi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;
    &lt;span class="nf"&gt;loop&lt;/span&gt; &lt;span class="nv"&gt;.pd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;PS&lt;/strong&gt; bit (bit 7) in a Page Directory entry signals a 2MB huge page, bypassing the Page Table level entirely. The result is a flat identity map covering the first 1GB of physical memory — virtual address equals physical address throughout. This keeps all subsequent absolute references valid without any address fixup.&lt;/p&gt;

&lt;p&gt;With the page tables in place, the Long Mode activation sequence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;eax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x13000&lt;/span&gt;
&lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;cr3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;eax&lt;/span&gt;            &lt;span class="c1"&gt;; point CR3 at PML4&lt;/span&gt;

&lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;eax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;cr4&lt;/span&gt;
&lt;span class="nf"&gt;or&lt;/span&gt;  &lt;span class="nb"&gt;eax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;cr4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;eax&lt;/span&gt;            &lt;span class="c1"&gt;; CR4.PAE = 1&lt;/span&gt;

&lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;ecx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xC0000080&lt;/span&gt;     &lt;span class="c1"&gt;; IA32_EFER MSR&lt;/span&gt;
&lt;span class="nf"&gt;rdmsr&lt;/span&gt;
&lt;span class="nf"&gt;or&lt;/span&gt;  &lt;span class="nb"&gt;eax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;wrmsr&lt;/span&gt;                   &lt;span class="c1"&gt;; EFER.LME = 1&lt;/span&gt;

&lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;eax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;cr0&lt;/span&gt;
&lt;span class="nf"&gt;or&lt;/span&gt;  &lt;span class="nb"&gt;eax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;cr0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;eax&lt;/span&gt;            &lt;span class="c1"&gt;; CR0.PG = 1 → hardware sets EFER.LMA&lt;/span&gt;

&lt;span class="nf"&gt;jmp&lt;/span&gt; &lt;span class="mh"&gt;0x18&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;lmode64&lt;/span&gt;        &lt;span class="c1"&gt;; CS = 0x18 (L=1) → 64-bit decode active&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The order is strictly enforced by the hardware. Setting &lt;strong&gt;EFER.LME&lt;/strong&gt; before &lt;strong&gt;CR4.PAE&lt;/strong&gt; is set raises &lt;strong&gt;#GP&lt;/strong&gt;. Setting &lt;strong&gt;CR0.PG&lt;/strong&gt; last is the trigger that causes the processor to atomically assert &lt;strong&gt;EFER.LMA&lt;/strong&gt; — Long Mode Active. Until the far jump loads the 0x18 descriptor with &lt;strong&gt;L=1&lt;/strong&gt;, the processor is technically in compatibility mode: Long Mode paging is active, but instruction decode is still 32-bit. The far jump is what makes it 64-bit.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;0x3 - IDT and Exception Handling&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;The IDT in Long Mode serves the same purpose as the IVT in Real Mode, but the mechanism is entirely different. Each entry is 16 bytes, encoding a 64-bit handler address split across three fields, a code segment selector, an IST index, and a type/attribute byte. The processor locates the IDT through IDTR, loaded with &lt;strong&gt;lidt&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The IDT is placed at physical &lt;strong&gt;0x10000&lt;/strong&gt;. All 256 entries are first populated with a &lt;strong&gt;CatchAll&lt;/strong&gt; handler that does nothing but &lt;strong&gt;iretq&lt;/strong&gt;, then vector 1 — the &lt;strong&gt;#DB&lt;/strong&gt; (Debug Exception) vector — is overwritten with the actual handler address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="no"&gt;IDT_BASE&lt;/span&gt;&lt;span class="kd"&gt; equ&lt;/span&gt; &lt;span class="mh"&gt;0x10000&lt;/span&gt;

&lt;span class="nl"&gt;setup_idt:&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;IDT_BASE&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;
    &lt;span class="nf"&gt;xor&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;
    &lt;span class="nf"&gt;rep&lt;/span&gt; &lt;span class="nv"&gt;stosq&lt;/span&gt;               &lt;span class="c1"&gt;; zero the entire IDT&lt;/span&gt;

    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;IDT_BASE&lt;/span&gt;
&lt;span class="nl"&gt;.fill:&lt;/span&gt;
    &lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="nv"&gt;.write_gate&lt;/span&gt;        &lt;span class="c1"&gt;; write CatchAll to every entry&lt;/span&gt;
    &lt;span class="nf"&gt;add&lt;/span&gt;  &lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;
    &lt;span class="nf"&gt;loop&lt;/span&gt; &lt;span class="nv"&gt;.fill&lt;/span&gt;

    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;IDT_BASE&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;DbHandler&lt;/span&gt;
    &lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="nv"&gt;.write_gate_rax&lt;/span&gt;    &lt;span class="c1"&gt;; overwrite vector 1 with DbHandler&lt;/span&gt;

    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="kt"&gt;word&lt;/span&gt;  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;idt_limit&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="kt"&gt;qword&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;idt_base_addr&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;IDT_BASE&lt;/span&gt;
    &lt;span class="nf"&gt;lidt&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;idt_limit&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nf"&gt;ret&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each gate is written with selector &lt;strong&gt;0x18&lt;/strong&gt; (the 64-bit code descriptor), IST index 0, and type byte &lt;strong&gt;0x8E&lt;/strong&gt;: Present, DPL=0, 64-bit interrupt gate. The interrupt gate type means the processor clears IF on entry, preventing nested interrupts from arriving while the handler is running.&lt;/p&gt;

</description>
      <category>nasm</category>
      <category>debugger</category>
      <category>debug</category>
      <category>registers</category>
    </item>
    <item>
      <title>[My Debugger Journey] Working on Debug Registers with NASM</title>
      <dc:creator>bekoo</dc:creator>
      <pubDate>Wed, 10 Jun 2026 07:00:24 +0000</pubDate>
      <link>https://dev.to/bekoo/working-on-debug-register-with-nasm-ol3</link>
      <guid>https://dev.to/bekoo/working-on-debug-register-with-nasm-ol3</guid>
      <description>&lt;h3&gt;
  
  
  &lt;strong&gt;Debug Registers&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Both X86 and X64 architectures provide debug registers. In the processor, the debug registers can be accessed through DR0 - DR7 registers and also through MSR's. &lt;/p&gt;

&lt;p&gt;Debug registers are different from another registers. Debug registers hold the address of memory and I/O locations - or we can say them 'breakpoints' and we can set a breakpoint with these registers. To put it more simply,&lt;/p&gt;

&lt;p&gt;Here's a figure from the intel's volume:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fny1j14sg8oghjbc1chfv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fny1j14sg8oghjbc1chfv.png" alt=" " width="800" height="779"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Especially, in our case, DR6 and DR7 are really important. We will see them in detail. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DR0–DR3 Registers&lt;/strong&gt;: These are the breakpoint address registers. Each holds a 64-bit linear address defining one hardware breakpoint slot. The processor supports up to four concurrent hardware breakpoints, one per slot. These registers are read/write and can be updated at any privilege level when CR4.DE is clear, or restricted to ring 0 when it is set.&lt;/li&gt;
&lt;/ul&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DR4 and DR5 Registers&lt;/strong&gt; These registers are legacy aliases. When CR4.DE is clear, accessing DR4 or DR5 silently redirects to DR6 and DR7 respectively. When CR4.DE is set, the access raises an #UD exception. In practice, DR4 and DR5 should be considered non-existent on modern systems.&lt;/li&gt;
&lt;/ul&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DR6 Register&lt;/strong&gt;: DR6 is the debug status register. The processor writes to it when a #DB exception fires, encoding the reason for the exception. Software reads DR6 to determine which breakpoint triggered and why. Critically, the processor never clears DR6 — the exception handler is responsible for zeroing it before returning, otherwise stale bits from a previous exception can cause misdiagnosis.&lt;/li&gt;
&lt;/ul&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DR7 Register:&lt;/strong&gt; DR7 is the debug control register. It arms or disarms each of the four breakpoint slots and defines, per slot, two properties: the trigger condition (instruction execution, data write, or data read/write) and the operand width to match (1, 2, 4, or 8 bytes). DR7 is the primary interface through which a debugger configures hardware breakpoints.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Detecting Breakpoints&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;When a memory address is written to DR0–DR3, the processor halts execution upon reaching it — not through software polling, but via a dedicated hardware comparator circuit operating autonomously at the silicon level.&lt;/p&gt;

&lt;p&gt;The internal design of this circuit is not publicly documented in modern processors, but Intel's US Patent &lt;a href="https://image-ppubs.uspto.gov/dirsearch-public/print/downloadPdf/5694589" rel="noopener noreferrer"&gt;5,694,589&lt;/a&gt; (filed 1995, granted 1997) provides a detailed architectural description of its Pentium-era implementation, and serves as the primary reference for this section.&lt;/p&gt;

&lt;p&gt;The patent addresses a scaling problem specific to superscalar execution. When multiple instructions are decoded in parallel each clock cycle, the conventional design requires one comparator per decode pipeline — each performing the identical comparison against DR0–DR3. The patent resolves this by moving the comparison upstream to the instruction fetch stage, where a single comparator unit covers all decode pipelines regardless of dispatch width.&lt;/p&gt;

&lt;p&gt;In the patent, figure 2 shows the problem:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7bi22ohd1ag4vj91wp9o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7bi22ohd1ag4vj91wp9o.png" alt=" " width="712" height="1016"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;FIG. 2 lays out the problem directly. Each decode pipeline — ID₁ through IDₙ — carries its own breakpoint detection block, each one housing M comparators wired against DR0–DR3. To make this concrete: the IFU fetches a 16-byte aligned block from the instruction cache. That block is handed to the Instruction Length Decoder, which splits it into individual instructions and distributes them across N decode pipelines. Each pipeline then performs its own independent comparison:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Fetch block @ 0x00401000  (16 bytes)
┌──────────────────────────────────────────────┐
│ instr₁       instr₂       instr₃   ...  instrₙ│
│ 0x00401000   0x00401003   0x00401006    ...    │
└──────────────────────────────────────────────┘
        │             │            │
        ▼             ▼            ▼
    BPDET₁        BPDET₂       BPDET₃
  0x00401000    0x00401003   0x00401006
  == DR0–DR3?   == DR0–DR3?  == DR0–DR3?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a processor dispatching N instructions per cycle, that means N identical comparison circuits firing every cycle against the same four registers. The hardware cost scales with dispatch width, not with any architectural necessity.&lt;/p&gt;

&lt;p&gt;Figure 3 in the patent shows the invention:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm2pr3prs7wwcg1y6mgag.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm2pr3prs7wwcg1y6mgag.png" alt=" " width="745" height="1098"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;FIG. 3 eliminates the redundancy at its source. Block 200, the breakpoint detection logic, is pulled out of the decode stage and repositioned adjacent to the IFU, operating directly on the Fetch IP. The comparison happens once, upstream, before the instruction stream has been split across decode pipelines. The patent does this by comparing only the upper 28 bits of the Fetch IP against DR0–DR3 — the lower 4 bits are then expanded through a 4:16 decoder to identify which byte within the 16-byte fetch block carries the breakpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DR0          = 0x00401000
               ├── upper 28-bit ──┤ ├─ lower 4 ─┤
                  0x0040100            0x0

Fetch IP     = 0x00401000
               ├── upper 28-bit ──┤
                  0x0040100            → match

Lower 4-bit  = 0x0  →  4:16 decoder  →  bit 0
                                         │
                                         ▼
                              0x00401000 is the first byte
                              of the fetch block → #DB
                              before decode pipelines
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A MUX sits between the detection block and the Instruction Length Decoder — if the fetch address matches a debug register, the signal is resolved there. The decode pipelines in FIG. 3 have no breakpoint logic under them at all.&lt;/p&gt;

&lt;p&gt;To put it more simply, the address written to DR0–DR3 is not a hint or a flag — it is a direct input to the comparator circuit. The moment &lt;em&gt;mov dr0, rax&lt;/em&gt; executes, that address is live in hardware. Every subsequent fetch is checked against it.&lt;/p&gt;

&lt;p&gt;The IFU fetches a 16-byte aligned block from the instruction cache. The Instruction Length Decoder determines the boundary of each instruction within that block — x86 instructions are variable-length, anywhere from 1 to 15 bytes — and distributes them across the decode pipelines. Each pipeline takes one instruction and converts it from binary opcode into micro-operations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Fetch block @ 0x00401000:
[48 89 C8]  [48 83 EC 08]  [FF D0]
   3 byte        4 byte      2 byte

ID₁ → 0x00401000 → mov rax, rcx
ID₂ → 0x00401003 → sub rsp, 8
ID₃ → 0x00401007 → call rax
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In FIG. 2, each pipeline checks its own instruction address against DR0–DR3 independently. ID₁ compares &lt;em&gt;0x00401000&lt;/em&gt;, ID₂ compares &lt;em&gt;0x00401003&lt;/em&gt;, ID₃ compares &lt;em&gt;0x00401007&lt;/em&gt; — the same four registers, checked N times per cycle.&lt;/p&gt;

&lt;p&gt;In FIG. 3, the check is pulled upstream to the fetch boundary. The upper 28 bits of the Fetch IP are compared against DR0–DR3 before the block reaches the length decoder. The lower 4 bits identify which byte within the 16-byte block is armed, resolved through a 4:16 decoder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DR0 = 0x00401003
      ├─ upper 28-bit ─┤ ├─ lower 4 ─┤
         0x0040100          0x3

Fetch IP = 0x00401000
           ├─ upper 28-bit ─┤
              0x0040100          → match

Lower 4-bit = 0x3  →  4:16 decoder  →  bit 3
                                         │
                                         ▼
                              0x00401003 is the fourth byte
                              of the fetch block → #DB
                              before decode pipelines
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By the time an instruction reaches any decode unit, the hardware has already decided whether that address is armed. The &lt;em&gt;#DB&lt;/em&gt; is not generated at dispatch or at execution — it is generated at fetch. The instruction never advances further. No instruction is modified, no execution path is altered, no software layer is consulted. The comparator either matches or it does not, and that determination is made before anything downstream has a chance to observe the instruction at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Of course, things are quite different with modern processors today, but I’ve used this resource solely as a reference to help us understand the basic background.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Generating #DB Exception&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;CPU generates #DB when a breakpoint is triggered. This #DB, in fact, is an exception from IDT (Interrupt descriptor table). &lt;/p&gt;

&lt;p&gt;When the CPU encounters a breakpoint condition — whether from a hardware debug register match, a single-step TF flag, or a data watchpoint — it vectors through IDT vector 1 to the #DB handler. The descriptor at IDT[1] holds the gate type, DPL, and the handler's segment:offset. Before transferring control, the processor pushes the exception frame onto the stack: RFLAGS, CS, RIP, and — since #DB is a fault/trap hybrid — optionally an error code, though #DB itself carries none.&lt;/p&gt;

&lt;p&gt;What makes #DB interesting architecturally is its dual nature. It is simultaneously a fault (for instruction breakpoints: RIP points at the faulting instruction) and a trap for the breakpoins. &lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Adding our Handler into IDT&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Here's nasm assembly project that adds our handler into IDT[1]:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="no"&gt;IDT_BASE&lt;/span&gt;&lt;span class="kd"&gt; equ&lt;/span&gt; &lt;span class="mh"&gt;0x10000&lt;/span&gt;
&lt;span class="nl"&gt;idt_limit:&lt;/span&gt;      &lt;span class="kd"&gt;dw&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="nl"&gt;idt_base_addr:&lt;/span&gt;  &lt;span class="kd"&gt;dq&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="nl"&gt;setup_idt:&lt;/span&gt;
    &lt;span class="c1"&gt;; Clear the IDT&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;IDT_BASE&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;&lt;span class="p"&gt;,(&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; 
    &lt;span class="nf"&gt;xor&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rax&lt;/span&gt;
    &lt;span class="nf"&gt;rep&lt;/span&gt; &lt;span class="nv"&gt;stosq&lt;/span&gt; 

    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;IDT_BASE&lt;/span&gt;

&lt;span class="nl"&gt;.fill:&lt;/span&gt; 
    &lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="nv"&gt;.write_gate&lt;/span&gt;
    &lt;span class="nf"&gt;add&lt;/span&gt; &lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;
    &lt;span class="nf"&gt;loop&lt;/span&gt; &lt;span class="nv"&gt;.fill&lt;/span&gt;

    &lt;span class="c1"&gt;; Now we can write our handle to IDT&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;IDT_BASE&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;DbHandler&lt;/span&gt;
    &lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="nv"&gt;.write_gate_rax&lt;/span&gt;

    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="kt"&gt;word&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;idt_limit&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="kt"&gt;qword&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;idt_base_addr&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;IDT_BASE&lt;/span&gt;
    &lt;span class="nf"&gt;lidt&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;idt_limit&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nf"&gt;ret&lt;/span&gt;

&lt;span class="nl"&gt;.write_gate:&lt;/span&gt;
    &lt;span class="nf"&gt;push&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt;  &lt;span class="nb"&gt;rax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;CatchAll&lt;/span&gt;
    &lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="nv"&gt;.write_gate_rax&lt;/span&gt;
    &lt;span class="nf"&gt;pop&lt;/span&gt;  &lt;span class="nb"&gt;rax&lt;/span&gt;
    &lt;span class="nf"&gt;ret&lt;/span&gt;

&lt;span class="nl"&gt;.write_gate_rax:&lt;/span&gt;
    &lt;span class="nf"&gt;push&lt;/span&gt; &lt;span class="nb"&gt;rbx&lt;/span&gt;
    &lt;span class="nf"&gt;push&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt;  &lt;span class="nb"&gt;rbx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt;  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="nb"&gt;bx&lt;/span&gt;            &lt;span class="c1"&gt;; offset[15:0]&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt;  &lt;span class="kt"&gt;word&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mh"&gt;0x18&lt;/span&gt;      &lt;span class="c1"&gt;; code64 selector&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt;  &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mh"&gt;0x00&lt;/span&gt;      &lt;span class="c1"&gt;; IST=0&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt;  &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mh"&gt;0x8E&lt;/span&gt;      &lt;span class="c1"&gt;; P=1 DPL=0 64-bit interrupt gate&lt;/span&gt;
    &lt;span class="nf"&gt;shr&lt;/span&gt;  &lt;span class="nb"&gt;rbx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt;  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="nb"&gt;bx&lt;/span&gt;            &lt;span class="c1"&gt;; offset[31:16]&lt;/span&gt;
    &lt;span class="nf"&gt;shr&lt;/span&gt;  &lt;span class="nb"&gt;rbx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt;  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="nb"&gt;ebx&lt;/span&gt;           &lt;span class="c1"&gt;; offset[63:32]&lt;/span&gt;
    &lt;span class="nf"&gt;pop&lt;/span&gt;  &lt;span class="nb"&gt;rax&lt;/span&gt;
    &lt;span class="nf"&gt;pop&lt;/span&gt;  &lt;span class="nb"&gt;rbx&lt;/span&gt;
    &lt;span class="nf"&gt;ret&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's a diagram for this project:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmgbtuh4wtxhmgftzmya6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmgbtuh4wtxhmgftzmya6.png" alt=" " width="685" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After clearing the IDT and filling all 256 entries with the catch-all handler, the code installs the actual #DB handler at IDT[1] — vector 1 being the architecturally reserved slot for debug exceptions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;IDT_BASE&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;DbHandler&lt;/span&gt;
&lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="nv"&gt;.write_gate_rax&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each IDT entry in 64-bit mode is 16 bytes. The * 16 offset lands rdi exactly at the second descriptor slot.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;.write_gate_rax&lt;/strong&gt; routine constructs the descriptor manually, field by field, conforming to the 64-bit interrupt gate format defined in the SDM Vol. 3A 6.14:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="nf"&gt;mov&lt;/span&gt;  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="nb"&gt;bx&lt;/span&gt;          &lt;span class="c1"&gt;; offset[15:0]&lt;/span&gt;
&lt;span class="nf"&gt;mov&lt;/span&gt;  &lt;span class="kt"&gt;word&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mh"&gt;0x18&lt;/span&gt;    &lt;span class="c1"&gt;; segment selector&lt;/span&gt;
&lt;span class="nf"&gt;mov&lt;/span&gt;  &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mh"&gt;0x00&lt;/span&gt;    &lt;span class="c1"&gt;; IST = 0&lt;/span&gt;
&lt;span class="nf"&gt;mov&lt;/span&gt;  &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mh"&gt;0x8E&lt;/span&gt;    &lt;span class="c1"&gt;; type/attr byte&lt;/span&gt;
&lt;span class="nf"&gt;shr&lt;/span&gt;  &lt;span class="nb"&gt;rbx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;
&lt;span class="nf"&gt;mov&lt;/span&gt;  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="nb"&gt;bx&lt;/span&gt;          &lt;span class="c1"&gt;; offset[31:16]&lt;/span&gt;
&lt;span class="nf"&gt;shr&lt;/span&gt;  &lt;span class="nb"&gt;rbx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;
&lt;span class="nf"&gt;mov&lt;/span&gt;  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="nb"&gt;ebx&lt;/span&gt;         &lt;span class="c1"&gt;; offset[63:32]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The handler's 64-bit address is split across three non-contiguous fields — [0:1], [6:7], [8:11] — because the descriptor format inherited its layout from the original protected mode design and extended it in place rather than redesigning it.&lt;/p&gt;

&lt;p&gt;0x18 at offset +2 is the code segment selector. In a typical GDT layout this is the third descriptor (index 3, 3 * 8 = 0x18), a 64-bit code segment. The processor loads CS from this field on gate entry, which determines the privilege level and addressing mode of the handler.&lt;/p&gt;

&lt;p&gt;Lastly, LIDT expects a 10-byte memory operand: a 2-byte limit followed immediately by an 8-byte linear base address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="kt"&gt;word&lt;/span&gt;  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;idt_limit&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;     &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="kt"&gt;qword&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;idt_base_addr&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;IDT_BASE&lt;/span&gt;
&lt;span class="nf"&gt;lidt&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;idt_limit&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The limit is always (n * 16) - 1 for n descriptors — off-by-one because the limit is the last valid byte offset, not the count. If the CPU receives a vector whose offset exceeds the limit, it generates a #GP(vector*8+2) rather than dispatching the handler, so undersizing the limit is a hard fault condition.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Triggering a Breakpoint with DR7 Register&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;DR6 and DR7 registers are the major player in a debugger. If you want to set a condition for a breakpoint, you need to use DR7. If you check the result of a trigger, you need to check the result of DR6. &lt;/p&gt;

&lt;p&gt;There are many condition to trigger a breakpoint. Here are them: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Execution breakpoint (fetch)&lt;/strong&gt;: The processor triggers #DB when the instruction pointer reaches the address stored in a DR slot. This is the closest hardware equivalent to a traditional software breakpoint (int3), with one critical difference: the target instruction is never modified. The condition code for this type is 00b in the DR7 condition field.&lt;/li&gt;
&lt;/ul&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Data write breakpoint:&lt;/strong&gt; The processor monitors the specified address for write operations. When any instruction writes to that address — regardless of which register or addressing mode is used — #DB fires after the write completes. Condition code: 01b.
I/O read/write breakpoint. When CR4.DE is set, the processor can watch a specific I/O port address. Any IN or OUT instruction targeting that port raises #DB. This condition is only available when CR4.DE is enabled; without it, the encoding is reserved. Condition code: &lt;strong&gt;10b&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;



&lt;ul&gt;
&lt;li&gt;Data read/write breakpoint. The broadest condition. #DB fires on any read or write to the watched address, whether the access is a load, store, or read-modify-write. Instruction fetches are explicitly excluded — the processor does not trigger on fetch even if the instruction pointer passes through the watched region. Condition code: &lt;strong&gt;11b&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One constraint applies across all conditions:&lt;/strong&gt; The breakpoint address in DR0–DR3 must be aligned to the access width defined in DR7. A 4-byte watchpoint on an unaligned address produces undefined behavior on some microarchitectures and silently fails to trigger on others.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can also benefit from the intel's volume:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F19i0a0e34xfpwvjzo331.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F19i0a0e34xfpwvjzo331.png" alt=" " width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Using DR7 in NASM Assembly&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;We can simply trigger a breakpoint with NASM Assembly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="k"&gt;BITS&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;
&lt;span class="nl"&gt;lmode64:&lt;/span&gt;
    &lt;span class="c1"&gt;; [...]&lt;/span&gt;

    &lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="nv"&gt;setup_idt&lt;/span&gt;

    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rsi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;msgIDTOK&lt;/span&gt;
    &lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="nv"&gt;serial_print&lt;/span&gt;

    &lt;span class="c1"&gt;; Write DebugTarget function to DR0&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;DebugTarget&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;dr0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rax&lt;/span&gt;

    &lt;span class="c1"&gt;; Add 0x00000001 value to DR7 (Execution)&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x00000001&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;dr7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rax&lt;/span&gt;

    &lt;span class="c1"&gt;; Trigger the breakpoint &lt;/span&gt;
    &lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="nv"&gt;DebugTarget&lt;/span&gt;

    &lt;span class="nf"&gt;hlt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, we just configure the IDT table, write the function address (DebugTarget) to DR0 register, prepare condition for the breakpoint in DR7 register. &lt;strong&gt;0x00000001&lt;/strong&gt; means &lt;strong&gt;"trigger the breakpoint if the address is executed"&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;My DbHandler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;
&lt;span class="nf"&gt;msgHello&lt;/span&gt;  &lt;span class="nv"&gt;db&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;I&lt;/span&gt; &lt;span class="nv"&gt;love&lt;/span&gt; &lt;span class="nv"&gt;bare&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;metal&lt;/span&gt; &lt;span class="nv"&gt;coding&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="nl"&gt;DbHandler:&lt;/span&gt;
    &lt;span class="nf"&gt;push&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;
    &lt;span class="nf"&gt;push&lt;/span&gt; &lt;span class="nb"&gt;rbx&lt;/span&gt;
    &lt;span class="nf"&gt;push&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;
    &lt;span class="nf"&gt;push&lt;/span&gt; &lt;span class="nb"&gt;rdx&lt;/span&gt;
    &lt;span class="nf"&gt;push&lt;/span&gt; &lt;span class="nb"&gt;rsi&lt;/span&gt;
    &lt;span class="nf"&gt;push&lt;/span&gt; &lt;span class="nb"&gt;rdi&lt;/span&gt;
    &lt;span class="nf"&gt;push&lt;/span&gt; &lt;span class="nb"&gt;rbp&lt;/span&gt;
    &lt;span class="nf"&gt;push&lt;/span&gt; &lt;span class="nv"&gt;r8&lt;/span&gt;
    &lt;span class="nf"&gt;push&lt;/span&gt; &lt;span class="nv"&gt;r9&lt;/span&gt;
    &lt;span class="nf"&gt;push&lt;/span&gt; &lt;span class="nv"&gt;r10&lt;/span&gt;
    &lt;span class="nf"&gt;push&lt;/span&gt; &lt;span class="nv"&gt;r11&lt;/span&gt;
    &lt;span class="nf"&gt;push&lt;/span&gt; &lt;span class="nv"&gt;r12&lt;/span&gt;
    &lt;span class="nf"&gt;push&lt;/span&gt; &lt;span class="nv"&gt;r13&lt;/span&gt;
    &lt;span class="nf"&gt;push&lt;/span&gt; &lt;span class="nv"&gt;r14&lt;/span&gt;
    &lt;span class="nf"&gt;push&lt;/span&gt; &lt;span class="nv"&gt;r15&lt;/span&gt;

    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rsi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;msgHello&lt;/span&gt;
    &lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="nv"&gt;serial_print&lt;/span&gt;

    &lt;span class="nf"&gt;xor&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rax&lt;/span&gt; 
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;dr7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rax&lt;/span&gt;

&lt;span class="nl"&gt;Exit:&lt;/span&gt;
    &lt;span class="nf"&gt;pop&lt;/span&gt; &lt;span class="nv"&gt;r15&lt;/span&gt;
    &lt;span class="nf"&gt;pop&lt;/span&gt; &lt;span class="nv"&gt;r14&lt;/span&gt;
    &lt;span class="nf"&gt;pop&lt;/span&gt; &lt;span class="nv"&gt;r13&lt;/span&gt;
    &lt;span class="nf"&gt;pop&lt;/span&gt; &lt;span class="nv"&gt;r12&lt;/span&gt;
    &lt;span class="nf"&gt;pop&lt;/span&gt; &lt;span class="nv"&gt;r11&lt;/span&gt;
    &lt;span class="nf"&gt;pop&lt;/span&gt; &lt;span class="nv"&gt;r10&lt;/span&gt;
    &lt;span class="nf"&gt;pop&lt;/span&gt; &lt;span class="nv"&gt;r9&lt;/span&gt;
    &lt;span class="nf"&gt;pop&lt;/span&gt; &lt;span class="nv"&gt;r8&lt;/span&gt;
    &lt;span class="nf"&gt;pop&lt;/span&gt; &lt;span class="nb"&gt;rbp&lt;/span&gt;
    &lt;span class="nf"&gt;pop&lt;/span&gt; &lt;span class="nb"&gt;rdi&lt;/span&gt;
    &lt;span class="nf"&gt;pop&lt;/span&gt; &lt;span class="nb"&gt;rsi&lt;/span&gt;
    &lt;span class="nf"&gt;pop&lt;/span&gt; &lt;span class="nb"&gt;rdx&lt;/span&gt;
    &lt;span class="nf"&gt;pop&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;
    &lt;span class="nf"&gt;pop&lt;/span&gt; &lt;span class="nb"&gt;rbx&lt;/span&gt;
    &lt;span class="nf"&gt;pop&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;
    &lt;span class="nf"&gt;iretq&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the handler, just a message is printed and reset the value of DR7 register. &lt;/p&gt;

&lt;p&gt;Here's the result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjyvwzkqavi30smbaoqp0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjyvwzkqavi30smbaoqp0.png" alt=" " width="384" height="105"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is simple using of DR7 register. &lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Setting the Writing Condition to a Breakpoint&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;We can continue to take advantage of the DR7's features.&lt;/p&gt;

&lt;p&gt;DR7 isn't a single-bit register. The 0x00000001 we used earlier is the minimal configuration — it only enables the local breakpoint condition for DR0.&lt;/p&gt;

&lt;p&gt;Each debug register (DR0–DR3) has its own enable bit pair and condition field. This means four independent breakpoints can be active simultaneously, each with a different address, type, and access width.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Condition Encoding (R/W bits):&lt;/strong&gt; The R/W field for each breakpoint slot controls what kind of access triggers the exception:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;; R/W = 00 → execution breakpoint (instruction fetch)
; R/W = 01 → write watchpoint
; R/W = 10 → I/O read/write (CR4.DE must be set)
; R/W = 11 → read/write watchpoint (not on execution)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;LEN Fields:&lt;/strong&gt; For execution breakpoints, LEN must always be 00. For data watchpoints, LEN encodes the access width:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LEN = 00 → 1 byte
LEN = 01 → 2 bytes
LEN = 10 → 8 bytes (64-bit mode only)
LEN = 11 → 4 bytes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now here's an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="nl"&gt;watch_var:&lt;/span&gt;  &lt;span class="kd"&gt;dd&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="k"&gt;BITS&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;
&lt;span class="nl"&gt;lmode64:&lt;/span&gt;
    &lt;span class="c1"&gt;; [...]&lt;/span&gt;

    &lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="nv"&gt;setup_idt&lt;/span&gt;

    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rsi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;msgIDTOK&lt;/span&gt;
    &lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="nv"&gt;serial_print&lt;/span&gt;

    &lt;span class="c1"&gt;; DR1 = write watchpoint on a data address&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;watch_var&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;dr1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;

    &lt;span class="c1"&gt;; DR7 encoding:&lt;/span&gt;
    &lt;span class="c1"&gt;; L0=1 (enable DR0 local), R/W0=00, LEN0=00  → bits [0], [17:16], [19:18]&lt;/span&gt;
    &lt;span class="c1"&gt;; L1=1 (enable DR1 local), R/W1=01, LEN1=11  → bits [2], [21:20], [23:22]&lt;/span&gt;
    &lt;span class="c1"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;; L0   = bit 0  = 1&lt;/span&gt;
    &lt;span class="c1"&gt;; L1   = bit 2  = 1&lt;/span&gt;
    &lt;span class="c1"&gt;; R/W1 = bits [21:20] = 01&lt;/span&gt;
    &lt;span class="c1"&gt;; LEN1 = bits [23:22] = 11&lt;/span&gt;
    &lt;span class="c1"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;; 0000 0000 1101 0000 0000 0000 0000 0101&lt;/span&gt;

    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x00D00005&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;dr7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;

    &lt;span class="c1"&gt;; Now trigger the breakpoint&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x1&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="kt"&gt;qword&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;watch_var&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="nb"&gt;rax&lt;/span&gt;


    &lt;span class="nf"&gt;hlt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The flow is same. We prepare DR1 and DR7 registers and trigger the breakpoint. Here's the result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F61wg205d5aqdarsrzrpi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F61wg205d5aqdarsrzrpi.png" alt=" " width="442" height="149"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The examples covered here represent only the surface of what the debug register architecture exposes. DR0–DR3 accept any linear address — code, data, or I/O — and DR7 encodes the full condition space: execution, write, read/write, access width, local and global scope, all independently configurable per register. The hardware does the rest without kernel involvement, without modifying the target code, and without observable overhead.&lt;/p&gt;

&lt;p&gt;For anyone going deeper, Intel's Software Developer's Manual Volume 3, Chapter 19 (CHAPTER 19 - DEBUG, BRANCH PROFILE, TSC AND INTEL® RESOURCE DIRECTOR) is the authoritative reference — it covers the full DR7 encoding, DR6 status semantics, interaction with single-step mode, and the edge cases that matter when building anything serious on top of this mechanism.&lt;/p&gt;

</description>
      <category>nasm</category>
      <category>debugger</category>
      <category>debug</category>
      <category>registers</category>
    </item>
    <item>
      <title>[My Debugger Journey] Building a Disassembler with NASM</title>
      <dc:creator>bekoo</dc:creator>
      <pubDate>Sun, 07 Jun 2026 09:20:06 +0000</pubDate>
      <link>https://dev.to/bekoo/building-a-disassembler-with-nasm-bo9</link>
      <guid>https://dev.to/bekoo/building-a-disassembler-with-nasm-bo9</guid>
      <description>&lt;h1&gt;
  
  
  How a Disassembler Reads Machine Code
&lt;/h1&gt;

&lt;p&gt;Have you ever wondered how a CPU sees and executes your code? Not the high-level abstractions, not the compiler output — the raw bytes sitting in memory. Let's go deep.&lt;/p&gt;

&lt;h2&gt;
  
  
  Instruction Encodings
&lt;/h2&gt;

&lt;p&gt;In x64 and IA-32 architectures, every instruction is a sequence of bytes following a specific format. An instruction consists of &lt;strong&gt;Instruction Prefixes&lt;/strong&gt;, &lt;strong&gt;Opcode&lt;/strong&gt;, &lt;strong&gt;ModR/M&lt;/strong&gt;, &lt;strong&gt;SIB&lt;/strong&gt;, &lt;strong&gt;Displacement&lt;/strong&gt; and &lt;strong&gt;Immediate&lt;/strong&gt;. We will cover each one.&lt;/p&gt;

&lt;p&gt;Here's the figure directly from Intel's Volume 2:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F42zg3wakzatk50j5htce.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F42zg3wakzatk50j5htce.png" alt="Instruction format" width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not every instruction uses all of these fields. Some instructions are a single byte. Others stretch to 15. The decoder has to figure out where one instruction ends and the next begins — purely from the byte stream, with no delimiters, no markers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Instruction Prefixes
&lt;/h2&gt;

&lt;p&gt;A prefix modifies the behavior of the following instruction without being an instruction itself. Intel groups them into four groups.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Group 1 — Lock and Repeat&lt;/strong&gt;: The LOCK prefix (F0) forces exclusive access to shared memory. In a multiprocessor environment, two cores can read and write the same address simultaneously, corrupting data. LOCK holds the memory bus until the operation completes. F2 and F3 are the repeat prefixes — REPNE/REPNZ and REP/REPE/REPZ respectively, typically used with string operations.&lt;/li&gt;
&lt;/ul&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Group 2 — Segment Override&lt;/strong&gt;: Redirects memory access to a specified segment register. 2E overrides to CS, 36 to SS, 3E to DS, 26 to ES, 64 to FS, and 65 to GS. Largely irrelevant in 64-bit mode where segmentation is mostly flat, but the decoder still has to consume and skip them.&lt;/li&gt;
&lt;/ul&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Group 3 — Operand Size Override&lt;/strong&gt;: &lt;strong&gt;66&lt;/strong&gt; switches the operand size between 16-bit and 32-bit. In a 32-bit context, prefixing an instruction with 66 narrows the operand to 16-bit — this is how mov ax, 1 and mov eax, 1 share the same opcode but differ by a single prefix byte.&lt;/li&gt;
&lt;/ul&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Group 4 — Address Size Override&lt;/strong&gt;: &lt;strong&gt;67&lt;/strong&gt; switches the address size between 32-bit and 64-bit. Rarely used in practice but the decoder must handle it — a disassembler that ignores 67 will miscalculate the size of any displacement or memory operand that follows.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In 64-bit mode, the REX prefix (0x40–0x4F) follows these four groups. REX.W=1 promotes the operand to 64-bit. That 0x48 byte you see before most 64-bit instructions is exactly this — it tells the CPU to treat the following instruction as a 64-bit operation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Opcode
&lt;/h2&gt;

&lt;p&gt;A primary opcode can be 1, 2, or 3 bytes in length. Most common instructions fit in a single byte — &lt;code&gt;0x90&lt;/code&gt; is NOP, &lt;code&gt;0xC3&lt;/code&gt; is RET, &lt;code&gt;0x89&lt;/code&gt; is MOV. The problem is that x86 only has 256 possible values in a single byte, and that space filled up quickly.&lt;/p&gt;

&lt;p&gt;When Intel ran out of room, they introduced escape sequences. &lt;strong&gt;0x0F&lt;/strong&gt; is the most common — it tells the decoder "don't treat this as an opcode, read the next byte too." So &lt;strong&gt;0x0F 0xA3&lt;/strong&gt; together means BT (Bit Test), a two-byte opcode. The &lt;code&gt;0x0F&lt;/code&gt; byte itself carries no instruction — it's purely a table switch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="nf"&gt;x89&lt;/span&gt;        &lt;span class="err"&gt;→&lt;/span&gt;  &lt;span class="nv"&gt;MOV&lt;/span&gt;   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="nv"&gt;opcode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="nf"&gt;x0F&lt;/span&gt; &lt;span class="mh"&gt;0xA3&lt;/span&gt;   &lt;span class="err"&gt;→&lt;/span&gt;  &lt;span class="nv"&gt;BT&lt;/span&gt;    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="nv"&gt;opcode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x0F&lt;/span&gt; &lt;span class="nv"&gt;is&lt;/span&gt; &lt;span class="nv"&gt;the&lt;/span&gt; &lt;span class="nb"&gt;es&lt;/span&gt;&lt;span class="nv"&gt;cape&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But, we cannot escape with these information :&amp;gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Mandatory Prefix&lt;/strong&gt; and &lt;strong&gt;Escape Prefix&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;An opcode also can consist of &lt;strong&gt;a mandatory prefix&lt;/strong&gt; and &lt;strong&gt;Escape Prefix:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mandatory Prefix:&lt;/strong&gt; A prefix that is part of the opcode itself, not a modifier. Unlike Group 1-4 prefixes which change behavior, a mandatory prefix changes the identity of the instruction entirely. &lt;/li&gt;
&lt;/ul&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Escape Prefix:&lt;/strong&gt; A byte that switches the decoder into an extended opcode table. 0x0F is the most common — alone it unlocks a second table of 256 opcodes. When that table also fills up, a second escape byte follows: 0x0F 0x38 and 0x0F 0x3A open two additional tables. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, an opcode can start with these values (CVTDQ2PD):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="nl"&gt;CVTDQ2PD:&lt;/span&gt; &lt;span class="nf"&gt;F3&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;F&lt;/span&gt; &lt;span class="nv"&gt;E6&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;CVTDQ2PD&lt;/strong&gt; instruction is one of the instructions that requires a &lt;strong&gt;mandatory prefix&lt;/strong&gt; and begins with &lt;strong&gt;0xF3&lt;/strong&gt;. This is the mandatory prefix, while the value &lt;strong&gt;0x0F&lt;/strong&gt; is the escape prefix. Finally, &lt;strong&gt;0xE6&lt;/strong&gt; is the command itself. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;By the way, the mandatory prefix can also be 0x66 and 0xF2.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But there are instructions that don't requires &lt;strong&gt;Mandatory Prefix&lt;/strong&gt;. Here are a few examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="nf"&gt;F&lt;/span&gt; &lt;span class="mi"&gt;28&lt;/span&gt;   &lt;span class="err"&gt;→&lt;/span&gt;  &lt;span class="nv"&gt;MOVAPS&lt;/span&gt;   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;move&lt;/span&gt; &lt;span class="nb"&gt;al&lt;/span&gt;&lt;span class="nv"&gt;igned&lt;/span&gt; &lt;span class="nv"&gt;packed&lt;/span&gt; &lt;span class="nb"&gt;si&lt;/span&gt;&lt;span class="nv"&gt;ngle&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;precision&lt;/span&gt; &lt;span class="nv"&gt;floats&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="nf"&gt;F&lt;/span&gt; &lt;span class="mi"&gt;58&lt;/span&gt;   &lt;span class="err"&gt;→&lt;/span&gt;  &lt;span class="nv"&gt;ADDPS&lt;/span&gt;    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;add&lt;/span&gt; &lt;span class="nv"&gt;packed&lt;/span&gt; &lt;span class="nb"&gt;si&lt;/span&gt;&lt;span class="nv"&gt;ngle&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;precision&lt;/span&gt; &lt;span class="nv"&gt;floats&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="nf"&gt;F&lt;/span&gt; &lt;span class="mi"&gt;54&lt;/span&gt;   &lt;span class="err"&gt;→&lt;/span&gt;  &lt;span class="nv"&gt;ANDPS&lt;/span&gt;    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;bitwise&lt;/span&gt; &lt;span class="nv"&gt;AND&lt;/span&gt; &lt;span class="nv"&gt;of&lt;/span&gt; &lt;span class="nv"&gt;packed&lt;/span&gt; &lt;span class="nv"&gt;floats&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="nf"&gt;F&lt;/span&gt; &lt;span class="mi"&gt;59&lt;/span&gt;   &lt;span class="err"&gt;→&lt;/span&gt;  &lt;span class="nv"&gt;MULPS&lt;/span&gt;    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;multiply&lt;/span&gt; &lt;span class="nv"&gt;packed&lt;/span&gt; &lt;span class="nb"&gt;si&lt;/span&gt;&lt;span class="nv"&gt;ngle&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;precision&lt;/span&gt; &lt;span class="nv"&gt;floats&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, they start with 0x0F (escape prefix). &lt;/p&gt;

&lt;p&gt;I've prepared a nasm project that shows the differences between SIMD, AVX2 and X64 instructions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BITS 64

section .data
    align 16
    xmm_a   dd 1.0, 2.0, 3.0, 4.0
    xmm_b   dd 10.0, 20.0, 30.0, 40.0

    align 32
    avx_a   dd 1, 2, 3, 4, 5, 6, 7, 8
    avx_b   dd 10, 20, 30, 40, 50, 60, 70, 80

section .text
global _start

_start:
    ; X64 Register -&amp;gt; Prefix is 0x48
    mov rax,0x5000000000

    ; Mandatory Prefix Instruction: 0xF3 
    cvtdq2pd xmm1, xmm1

    ; Non-Mandatory Prefix: 0x0F
    movaps xmm0, [xmm_a]   ; xmm0 = [1.0, 2.0, 3.0, 4.0]

    ; AVX2 Register (256-bytes) Prefix: 0xC5
    vmovdqu ymm0, [avx_a]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are X64, SIMD and AVX2 registers, so we can see the different. Let's see the instructions with objdump:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flduoduzd19r9eihwhnd1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flduoduzd19r9eihwhnd1.png" alt=" " width="799" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Focus on the first bytes. They have different prefix. &lt;/p&gt;

&lt;h2&gt;
  
  
  ModR/M
&lt;/h2&gt;

&lt;p&gt;After the opcode, many instructions include a ModR/M byte. Not all — NOP, RET, HLT need no operands and carry no ModR/M. But any instruction that needs to specify "which register" or "which memory location" requires one.&lt;/p&gt;

&lt;p&gt;The ModR/M byte encodes three fields packed into 8 bits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; 7   6   5   4   3   2   1   0
┌───────┬───────────┬───────────┐
│  Mod  │    Reg    │    R/M    │
│  2bit │   3bit    │   3bit    │
└───────┴───────────┴───────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Mod&lt;/strong&gt; (bits 7:6) — tells the decoder how to interpret R/M:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mod&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;00&lt;/td&gt;
&lt;td&gt;memory, no displacement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;01&lt;/td&gt;
&lt;td&gt;memory + 1-byte displacement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;memory + 4-byte displacement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;register direct, no memory&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Reg&lt;/strong&gt; (bits 5:3) — encodes the register operand index (0–7, extended to 8–15 via REX.R).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;R/M&lt;/strong&gt; (bits 2:0) — encodes the second operand. When Mod=11, it's a register index. When Mod≠11, it encodes the base register for a memory access — or triggers SIB when R/M=4.&lt;/p&gt;

&lt;h3&gt;
  
  
  A concrete example
&lt;/h3&gt;

&lt;p&gt;Take the instruction &lt;em&gt;mov rbp, rsp&lt;/em&gt;. In memory it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="err"&gt;48&lt;/span&gt;  &lt;span class="err"&gt;89&lt;/span&gt;  &lt;span class="nf"&gt;E5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;0x48&lt;/strong&gt; — REX prefix, W=1, meaning 64-bit operand&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;0x89&lt;/strong&gt; — opcode: MOV r/m, reg&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;0xE5&lt;/strong&gt; — ModR/M byte&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Breaking down 0xE5 value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;E5 = 1110 0101
     ^^  ^^^  ^^^
     11  100  101
     Mod  Reg  R/M
     =3   =4   =5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mod=3 → register direct, no memory access, no displacement.&lt;br&gt;&lt;br&gt;
Reg=4 → RSP (source).&lt;br&gt;&lt;br&gt;
R/M=5 → RBP (destination).&lt;/p&gt;

&lt;p&gt;Combined with 0x89 (MOV r/m ← reg) and REX.W=1 (64-bit), the decoder produces: &lt;em&gt;mov rbp, rsp&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Building a Disassembler with NASM&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;As you can see before the chapter, creating a disassembler is really tough task. This means that we can't determine the opcodes with just increasing the bytes. There can be different byte for the instruction. And we must extract them. &lt;/p&gt;

&lt;p&gt;In this chapter, i've shared a piece code of my nasm project. This is simple disassembler function, which determines opcodes and instruction for mov rbp,rsp. In this way, we can understand the basic of a disassembler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="c1"&gt;; ...&lt;/span&gt;

&lt;span class="c1"&gt;; Messages&lt;/span&gt;
&lt;span class="nf"&gt;str_mov&lt;/span&gt;        &lt;span class="nv"&gt;db&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;found&lt;/span&gt; &lt;span class="nv"&gt;mov&lt;/span&gt; &lt;span class="nv"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;opcode&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="s"&gt;", 13, 10, 0
str_mod_reg    db "&lt;/span&gt;&lt;span class="nv"&gt;found&lt;/span&gt; &lt;span class="nv"&gt;reg&lt;/span&gt; &lt;span class="nv"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;ModRM&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="s"&gt;", 13, 10, 0 
str_mod_disp8  db "&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;mem&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nb"&gt;disp&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="s"&gt;", 0
str_mod_disp32 db "&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;mem&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nb"&gt;disp&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="s"&gt;", 0
str_mod_mem    db "&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="nf"&gt;str_newline&lt;/span&gt;    &lt;span class="nv"&gt;db&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="nf"&gt;opcodes&lt;/span&gt; &lt;span class="nv"&gt;db&lt;/span&gt; &lt;span class="mh"&gt;0x48&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x89&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xE5&lt;/span&gt;   &lt;span class="c1"&gt;; mov rbp, rsp&lt;/span&gt;
        &lt;span class="kd"&gt;db&lt;/span&gt; &lt;span class="mh"&gt;0x90&lt;/span&gt;               &lt;span class="c1"&gt;; nop&lt;/span&gt;
        &lt;span class="kd"&gt;db&lt;/span&gt; &lt;span class="mh"&gt;0x48&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xC0&lt;/span&gt;   &lt;span class="c1"&gt;; xor rax, rax&lt;/span&gt;
        &lt;span class="kd"&gt;db&lt;/span&gt; &lt;span class="mh"&gt;0xC3&lt;/span&gt;               &lt;span class="c1"&gt;; ret&lt;/span&gt;

&lt;span class="k"&gt;BITS&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;
&lt;span class="nl"&gt;_start64:&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt;  &lt;span class="nb"&gt;ax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x20&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt;  &lt;span class="nb"&gt;ds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;ax&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt;  &lt;span class="nb"&gt;es&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;ax&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt;  &lt;span class="nb"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;ax&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt;  &lt;span class="nb"&gt;gs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;ax&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt;  &lt;span class="nb"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;ax&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt;  &lt;span class="nb"&gt;rsp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x90000&lt;/span&gt;

    &lt;span class="c1"&gt;; ... &lt;/span&gt;

    &lt;span class="nf"&gt;xor&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rbx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;opcodes&lt;/span&gt;

&lt;span class="nl"&gt;.loop:&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;al&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;rbx&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="nf"&gt;cmp&lt;/span&gt; &lt;span class="nb"&gt;al&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x48&lt;/span&gt;
    &lt;span class="nf"&gt;jz&lt;/span&gt; &lt;span class="nv"&gt;.prefix_found&lt;/span&gt;

    &lt;span class="nf"&gt;cmp&lt;/span&gt; &lt;span class="nb"&gt;al&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xC3&lt;/span&gt;
    &lt;span class="nf"&gt;jz&lt;/span&gt; &lt;span class="nv"&gt;.exit&lt;/span&gt;

    &lt;span class="nf"&gt;cmp&lt;/span&gt; &lt;span class="nb"&gt;al&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x89&lt;/span&gt;
    &lt;span class="nf"&gt;jz&lt;/span&gt; &lt;span class="nv"&gt;.mov_found&lt;/span&gt;

    &lt;span class="c1"&gt;; unknown opcode — skip and continue&lt;/span&gt;
    &lt;span class="nf"&gt;inc&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;
    &lt;span class="nf"&gt;jmp&lt;/span&gt; &lt;span class="nv"&gt;.loop&lt;/span&gt;

&lt;span class="nl"&gt;.mov_found:&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rsi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;str_mov&lt;/span&gt;
    &lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="nv"&gt;serial_print&lt;/span&gt;

    &lt;span class="nf"&gt;inc&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;al&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;rbx&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;r8b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;al&lt;/span&gt;
    &lt;span class="nf"&gt;shr&lt;/span&gt; &lt;span class="nb"&gt;al&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;
    &lt;span class="nf"&gt;cmp&lt;/span&gt; &lt;span class="nb"&gt;al&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
    &lt;span class="nf"&gt;je&lt;/span&gt; &lt;span class="nv"&gt;.mod_reg&lt;/span&gt;

    &lt;span class="nf"&gt;cmp&lt;/span&gt; &lt;span class="nb"&gt;al&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="nf"&gt;je&lt;/span&gt; &lt;span class="nv"&gt;.mod_disp8&lt;/span&gt;

    &lt;span class="nf"&gt;cmp&lt;/span&gt; &lt;span class="nb"&gt;al&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="nf"&gt;je&lt;/span&gt; &lt;span class="nv"&gt;.mod_disp32&lt;/span&gt;

    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rsi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;str_mod_mem&lt;/span&gt;
    &lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="nv"&gt;serial_print&lt;/span&gt;
    &lt;span class="nf"&gt;inc&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;
    &lt;span class="nf"&gt;jmp&lt;/span&gt; &lt;span class="nv"&gt;.done_print&lt;/span&gt;

&lt;span class="nl"&gt;.mod_reg:&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rsi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;str_mod_reg&lt;/span&gt;
    &lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="nv"&gt;serial_print&lt;/span&gt;
    &lt;span class="nf"&gt;inc&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;
    &lt;span class="nf"&gt;jmp&lt;/span&gt; &lt;span class="nv"&gt;.done_print&lt;/span&gt;

&lt;span class="nl"&gt;.mod_disp8:&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rsi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;str_mod_disp8&lt;/span&gt;
    &lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="nv"&gt;serial_print&lt;/span&gt;
    &lt;span class="nf"&gt;add&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="nf"&gt;jmp&lt;/span&gt; &lt;span class="nv"&gt;.done_print&lt;/span&gt;

&lt;span class="nl"&gt;.mod_disp32:&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rsi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;str_mod_disp32&lt;/span&gt;
    &lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="nv"&gt;serial_print&lt;/span&gt;
    &lt;span class="nf"&gt;add&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
    &lt;span class="nf"&gt;jmp&lt;/span&gt; &lt;span class="nv"&gt;.done_print&lt;/span&gt;

&lt;span class="nl"&gt;.done_print:&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rsi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;str_newline&lt;/span&gt;
    &lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="nv"&gt;serial_print&lt;/span&gt;
    &lt;span class="nf"&gt;jmp&lt;/span&gt; &lt;span class="nv"&gt;.loop&lt;/span&gt;

&lt;span class="nl"&gt;.prefix_found:&lt;/span&gt;
    &lt;span class="nf"&gt;inc&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;
    &lt;span class="nf"&gt;jmp&lt;/span&gt; &lt;span class="nv"&gt;.loop&lt;/span&gt;

&lt;span class="nl"&gt;.exit:&lt;/span&gt;
    &lt;span class="nf"&gt;movzx&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;rbx&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="nv"&gt;serial_hex64&lt;/span&gt;

&lt;span class="nl"&gt;.halt:&lt;/span&gt;
    &lt;span class="nf"&gt;hlt&lt;/span&gt;
    &lt;span class="nf"&gt;jmp&lt;/span&gt; &lt;span class="nv"&gt;.halt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fskvz1rsgf0vvd902m1c9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fskvz1rsgf0vvd902m1c9.png" alt=" " width="472" height="178"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, we can determine mov instruction and its registers. &lt;/p&gt;

&lt;p&gt;This is really simple disassembler. Let's enhance it with this plan: Remember that we saw Prefixes and escape bytes. In the disassembler we will extract them. i've updated the opcodes variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="nf"&gt;opcodes&lt;/span&gt; &lt;span class="nv"&gt;db&lt;/span&gt; &lt;span class="mh"&gt;0x48&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x89&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xE5&lt;/span&gt; &lt;span class="c1"&gt;; mov rbp, rsp&lt;/span&gt;
        &lt;span class="kd"&gt;db&lt;/span&gt; &lt;span class="mh"&gt;0x90&lt;/span&gt; &lt;span class="c1"&gt;; nop&lt;/span&gt;
        &lt;span class="kd"&gt;db&lt;/span&gt; &lt;span class="mh"&gt;0x48&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xC0&lt;/span&gt; &lt;span class="c1"&gt;; xor rax, rax&lt;/span&gt;
        &lt;span class="kd"&gt;db&lt;/span&gt; &lt;span class="mh"&gt;0xf3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x0f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xe6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xc9&lt;/span&gt; &lt;span class="c1"&gt;; cvtdq2pd xmm1,xmm1&lt;/span&gt;
        &lt;span class="kd"&gt;db&lt;/span&gt; &lt;span class="mh"&gt;0x0f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x28&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x04&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x00&lt;/span&gt; &lt;span class="c1"&gt;; movaps xmm0,XMMWORD PTR ds:0x402000&lt;/span&gt;
        &lt;span class="kd"&gt;db&lt;/span&gt; &lt;span class="mh"&gt;0xC3&lt;/span&gt; &lt;span class="c1"&gt;; ret&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have different prefixes to extract the opcode. We can handle it with this way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="nl"&gt;.loop:&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;al&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;rbx&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;; Check the mandatory prefix&lt;/span&gt;
    &lt;span class="nf"&gt;cmp&lt;/span&gt; &lt;span class="nb"&gt;al&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0xF3&lt;/span&gt; 
    &lt;span class="nf"&gt;jz&lt;/span&gt; &lt;span class="nv"&gt;.found_mandatory&lt;/span&gt;

    &lt;span class="c1"&gt;; Check the Escape Byte&lt;/span&gt;
    &lt;span class="nf"&gt;cmp&lt;/span&gt; &lt;span class="nb"&gt;al&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x0F&lt;/span&gt;
    &lt;span class="nf"&gt;jz&lt;/span&gt; &lt;span class="nv"&gt;.found_escape&lt;/span&gt;

&lt;span class="nl"&gt;.continue:&lt;/span&gt;
    &lt;span class="nf"&gt;cmp&lt;/span&gt; &lt;span class="nb"&gt;al&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x48&lt;/span&gt;
    &lt;span class="nf"&gt;jz&lt;/span&gt; &lt;span class="nv"&gt;.prefix_found&lt;/span&gt;

    &lt;span class="nf"&gt;cmp&lt;/span&gt; &lt;span class="nb"&gt;al&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0xC3&lt;/span&gt;
    &lt;span class="nf"&gt;jz&lt;/span&gt; &lt;span class="nv"&gt;.exit&lt;/span&gt;

    &lt;span class="nf"&gt;cmp&lt;/span&gt; &lt;span class="nb"&gt;al&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x89&lt;/span&gt;
    &lt;span class="nf"&gt;jz&lt;/span&gt; &lt;span class="nv"&gt;.mov_found&lt;/span&gt;

    &lt;span class="c1"&gt;; unknown opcode — skip and continue&lt;/span&gt;
    &lt;span class="nf"&gt;inc&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;
    &lt;span class="nf"&gt;jmp&lt;/span&gt; &lt;span class="nv"&gt;.loop&lt;/span&gt;

&lt;span class="nl"&gt;.found_mandatory:&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rsi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;str_mandatory&lt;/span&gt;
    &lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="nv"&gt;serial_print&lt;/span&gt;

    &lt;span class="c1"&gt;; We need to check the escape byte&lt;/span&gt;
    &lt;span class="nf"&gt;inc&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;al&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;rbx&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nf"&gt;cmp&lt;/span&gt; &lt;span class="nb"&gt;al&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x0F&lt;/span&gt;
    &lt;span class="nf"&gt;jz&lt;/span&gt; &lt;span class="nv"&gt;.found_escape&lt;/span&gt;

    &lt;span class="c1"&gt;; if it is not escape byte, then find the opcode&lt;/span&gt;
    &lt;span class="nf"&gt;jmp&lt;/span&gt; &lt;span class="nv"&gt;.continue&lt;/span&gt;

&lt;span class="nl"&gt;.found_escape:&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rsi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;str_escape&lt;/span&gt;
    &lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="nv"&gt;serial_print&lt;/span&gt;

    &lt;span class="c1"&gt;; Now we can find the instruction&lt;/span&gt;
    &lt;span class="nf"&gt;inc&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;al&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;rbx&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="nf"&gt;jmp&lt;/span&gt; &lt;span class="nv"&gt;.continue&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's the output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo7fy866yeidh09xdwmgi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo7fy866yeidh09xdwmgi.png" alt=" " width="396" height="201"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, there are two messages &lt;strong&gt;found escape byte (0x0F)&lt;/strong&gt;. Because two instruction contains 0x0F value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="c1"&gt;; cvtdq2pd xmm1,xmm1&lt;/span&gt;
&lt;span class="kd"&gt;db&lt;/span&gt; &lt;span class="mh"&gt;0xf3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x0f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xe6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xc9&lt;/span&gt; 

&lt;span class="c1"&gt;; movaps xmm0,XMMWORD PTR ds:0x402000&lt;/span&gt;
&lt;span class="kd"&gt;db&lt;/span&gt; &lt;span class="mh"&gt;0x0f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x28&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x04&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x00&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this way, we can handle them.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;There are many possibilities for a disassembler.&lt;/p&gt;

&lt;p&gt;What I’ve shown you here are all very simple examples. As you can see from the first heading, there are many prefix groupings and so on, and as a debugger developer, we need to extract through them. Doing this with languages like NASM is tedious, but it makes the job much easier. &lt;/p&gt;

</description>
      <category>debugger</category>
      <category>nasm</category>
      <category>disassembly</category>
      <category>assembly</category>
    </item>
    <item>
      <title>[My Debugger Journey] Working on Single-Step Breakpoints in a Debugger</title>
      <dc:creator>bekoo</dc:creator>
      <pubDate>Wed, 03 Jun 2026 22:50:54 +0000</pubDate>
      <link>https://dev.to/bekoo/working-on-single-step-breakpoints-in-a-debugger-2f48</link>
      <guid>https://dev.to/bekoo/working-on-single-step-breakpoints-in-a-debugger-2f48</guid>
      <description>&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;Single-step breakpoints are fundamental debugging mechanism that allows execution to pause after every single instruction. Unlike hardware breakpoints which trigger at a specific address, single-step mode traces every instruction sequentially.&lt;/p&gt;

&lt;p&gt;In this post, we will see these breakpoints in detail with NASM + QEMU.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Single-Step Breakpoints and Trap Flag&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;A debugger can determine single-step breakpoints with DR6 register. DR6 register holds BS (Single-Step Bit) and a debugger can use this bit. BS is 14 bit of DR6 register:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fii0b5yd0znaodrfp3dwf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fii0b5yd0znaodrfp3dwf.png" alt=" " width="800" height="111"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But that's not all. This bit just shows the breakpoint is triggered in 'single-step progress'. We can't change if we want 'single-step'. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TF (Trap Flag)&lt;/strong&gt; is the real mechanism behind single-step execution. It lives at bit 8 of the RFLAGS register and cannot be set directly with a MOV instruction. Instead, we manipulate it indirectly through the stack.&lt;/p&gt;

&lt;p&gt;When a #DB exception occurs, the CPU pushes the current RFLAGS onto &lt;br&gt;
the stack as part of the exception frame. This saved copy is what &lt;strong&gt;IRETQ&lt;/strong&gt; will restore when returning from the handler. By modifying bit 8 of this saved RFLAGS before executing IRETQ, we control whether single-step continues after the handler returns.&lt;/p&gt;

&lt;p&gt;The CPU automatically clears TF before delivering the #DB exception. This is by design — without this behavior, the handler itself would be single-stepped, causing an infinite recursive exception loop. By clearing TF on entry and only restoring it in the saved RFLAGS, we ensure single-step applies only to the code being debugged, not to the handler.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Simple Project in NASM&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Here's an example from my NASM Project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DbHandler:
    push rax
    push rbx
    push rcx
    push rdx
    push rsi
    push rdi
    push rbp
    push r8
    push r9
    push r10
    push r11
    push r12
    push r13
    push r14
    push r15

    mov rbx, dr6

    ; Start single-step progress
    test rbx, (1 &amp;lt;&amp;lt; 0)
    jnz .hw_bp_hit

    ; BS set → single-step fired
    ; After the first execution of #DB (with TF=1),
    ; CPU will set BS bit of DR6
    test rbx, (1 &amp;lt;&amp;lt; 14)
    jnz .single_step


    jmp Exit

.hw_bp_hit:
    xor  rax, rax
    mov  dr6, rax
    xor  rax, rax
    mov  dr7, rax

    ; Enable TF flag and exit (CPU will generate #DB again)
    or   qword [rsp + 136], (1 &amp;lt;&amp;lt; 8)
    jmp  Exit

.single_step:
    ; In each single-step, write the value of RIP 
    mov rax, [rsp + 120]
    call serial_hex64

    dec qword [step_count]
    jz .stop_stepping

    ; Pass 1 to TF (for Single-Step)
    or qword [rsp + 136], (1 &amp;lt;&amp;lt; 8)
    jmp Exit

.stop_stepping:
    ; Pass 0 to stop
    and qword [rsp + 136], ~(1 &amp;lt;&amp;lt; 8)
    xor rax, rax
    mov dr6, rax
    jmp Exit

Exit:
    pop r15
    pop r14
    pop r13
    pop r12
    pop r11
    pop r10
    pop r9
    pop r8
    pop rbp
    pop rdi
    pop rsi
    pop rdx
    pop rcx
    pop rbx
    pop rax
    iretq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The idea is simple: In the first execution of #DB exception, we set TF flag to 1 and exit from the handler. Then CPU generates #DB exception with BS=1. In the handler, we check this bit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;; After the first execution of #DB (with TF=1),
; CPU will set BS bit of DR6
test rbx, (1 &amp;lt;&amp;lt; 14)
jnz .single_step
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;strong&gt;.single-step&lt;/strong&gt;, we just print the value of RIP, decrease the step_count and pass 1 to TF flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.single_step:
    ; In each single-step, write the value of RIP 
    mov rax, [rsp + 120]
    call serial_hex64

    ; step_count = 0x5
    dec qword [step_count]
    jz .stop_stepping

    ; Pass 1 to TF (for Single-Step)
    or qword [rsp + 136], (1 &amp;lt;&amp;lt; 8)
    jmp Exit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we can see, the idea is simple. Here's the result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg7my5t6om0aqkyusxv1y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg7my5t6om0aqkyusxv1y.png" alt=" " width="410" height="219"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I assigned the value 0x5 to the step_count variable, so each step is performed based on this value. Also my breakpoint is based on DebugTarget function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DebugTarget:
    mov  rax, 0x1
    mov  rbx, 0x2
    add  rax, rbx
    mov  rcx, rax
    mov  rdx, 0x0
    ret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>debugger</category>
      <category>debugging</category>
      <category>nasm</category>
      <category>assembly</category>
    </item>
    <item>
      <title>UEFI to Windows Communication via NVRAM Variables</title>
      <dc:creator>bekoo</dc:creator>
      <pubDate>Sun, 20 Jul 2025 21:19:28 +0000</pubDate>
      <link>https://dev.to/bekoo/uefi-to-windows-communication-via-nvram-variables-3lin</link>
      <guid>https://dev.to/bekoo/uefi-to-windows-communication-via-nvram-variables-3lin</guid>
      <description>&lt;p&gt;In this article, we will discuss how to establish communication between a UEFI driver and a Windows driver. Specifically, we’ll explore how to use NVRAM-based UEFI variables to exchange data between the two environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What is NVRAM?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;NVRAM (Non-Volatile RAM) is a key component in UEFI-based systems, used to store persistent configuration and system state data. Unlike regular RAM, NVRAM retains its contents across reboots and power-offs, making it essential for managing firmware-level settings.&lt;/p&gt;

&lt;p&gt;In UEFI, NVRAM is organized into variables—structured key-value pairs that include metadata (attributes) defining how and when they can be accessed. These variables are used for critical system functions such as boot management, secure boot key storage, hardware configuration, and OEM-specific settings. &lt;br&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;UEFI Variables&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;UEFI Variables &lt;strong&gt;is specified with a combination of a GUID and Unicode String&lt;/strong&gt;. The GUID of a variable can prevent name collisions between different vendors. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Boot Manager Chapter&lt;/strong&gt; of UEFI Specification defines the EFIGLOBAL_VARIABLE_GUID, also known &lt;em&gt;gEfiGlobalVariableGuid&lt;/em&gt; in EDK II. If we want to use a global variable, such as SecureBoot Status  in our UEFI Driver, we need to benefit from the gEfiGlobalVariableGuid. Here are some global variable list from &lt;a href="https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Guid/GlobalVariable.h" rel="noopener noreferrer"&gt;EDK II Repo&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;EFI_PLATFORM_LANG_CODES_VARIABLE_NAME  L"PlatformLangCodes"&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;EFI_BOOT_CURRENT_VARIABLE_NAME  L"BootCurrent"&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EFI_SIGNATURE_SUPPORT_NAME  L"SignatureSupport"&lt;/strong&gt; 
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;[...] &lt;/p&gt;

&lt;p&gt;Each UEFI Variable has attributes that the describe visibility persistence. Here's list of the attributes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;BOOTSERVICE_ACCESS&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The variable has permissions for write and read access at the pre-boot before &lt;strong&gt;ExitBootServices()&lt;/strong&gt; called, which means that the variable is not available after called ExitBootServices() and the contents of the variable will be deleted on the next system reset. &lt;br&gt;&lt;br&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;BOOTSERVICE_ACCESS | RUNTIME_ACCESS&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The variable has permissions for write and read access before ExitBootServices() called, but its contents will remain as &lt;strong&gt;read-only&lt;/strong&gt; when ExitBootServices() called, also the contents will be deleted on the next system reboot. &lt;br&gt;&lt;br&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;NON_VOLATILE | BOOTSERVICE_ACCESS&lt;/em&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The variable has permissions for write and read access before ExitBootServices() called and its contents are persistent through system reset. &lt;br&gt;&lt;br&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;NON_VOLATILE | BOOTSERVICE_ACCESS | RUNTIME_ACCESS&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The variable has permissions for write and read in both pre-boot and OS Runtime environment. Its contents are persistent through system reset. &lt;br&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Access UEFI Variables with Services&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;When a UEFI Driver store configuration information via UEFI Variables, it can access them with services provided by &lt;em&gt;EFI_HII_CONFIG_ACCESS_PROTOCOL&lt;/em&gt; The Services &lt;em&gt;SetVariable()&lt;/em&gt; and &lt;em&gt;GetVariable()&lt;/em&gt; are used to set and get configuration information. We will see these two services through the article. &lt;br&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;1 - SetVariable()&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;SetVariable()&lt;/em&gt; service sets the contents of the variable, also it can be used to create a new variable, modify the variable or to delete an existing variable. Here are parameters of the service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt; &lt;span class="k"&gt;typedef&lt;/span&gt;
 &lt;span class="n"&gt;EFI_STATUS&lt;/span&gt;
 &lt;span class="nf"&gt;SetVariable&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="n"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;CHAR16&lt;/span&gt;            &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;VariableName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;EFI_GUID&lt;/span&gt;          &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;VendorGuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;UINT32&lt;/span&gt;            &lt;span class="n"&gt;Attributes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;UINTN&lt;/span&gt;             &lt;span class="n"&gt;DataSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;VOID&lt;/span&gt;              &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's create a project for &lt;strong&gt;SetVariable&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;Uefi.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;Library/UefiApplicationEntryPoint.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;Library/UefiLib.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;Library/PcdLib.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;Library/UefiBootServicesTableLib.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;Library/UefiRuntimeServicesTableLib.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="n"&gt;EFI_STATUS&lt;/span&gt; &lt;span class="n"&gt;EFIAPI&lt;/span&gt; &lt;span class="nf"&gt;UefiMain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EFI_HANDLE&lt;/span&gt; &lt;span class="n"&gt;ImageHandle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EFI_SYSTEM_TABLE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;SystemTable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;CHAR16&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;VariableName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;L"MyUEFIVar"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;EFI_GUID&lt;/span&gt; &lt;span class="n"&gt;VendorGuid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mh"&gt;0xa1b2c3d4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x1234&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x5678&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mh"&gt;0x9a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0xbc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0xde&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0xf1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x67&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x89&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="n"&gt;UINT8&lt;/span&gt; &lt;span class="n"&gt;ContentOfVariable&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mh"&gt;0xDE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xAD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xBE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xEF&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="n"&gt;UINTN&lt;/span&gt; &lt;span class="n"&gt;DataSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ContentOfVariable&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;EFI_STATUS&lt;/span&gt; &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EFI_SUCCESS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;UINT32&lt;/span&gt; &lt;span class="n"&gt;Attributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EFI_VARIABLE_NON_VOLATILE&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
                      &lt;span class="n"&gt;EFI_VARIABLE_BOOTSERVICE_ACCESS&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
                      &lt;span class="n"&gt;EFI_VARIABLE_RUNTIME_ACCESS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


  &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gRT&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;SetVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;VariableName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;VendorGuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;Attributes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;DataSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ContentOfVariable&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EFI_ERROR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;Print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;L"Write Operation Failed!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;Print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;L"The content successfully written!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;EFI_SUCCESS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code, we wrote the content of ContentOfVariable. Firstly, we created a variable name, GUID and values for UEFI Variable, then we executed the SetVariable. As a result of these processes, our UEFI Variable has these information: &lt;br&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UEFI Variable Name: &lt;strong&gt;MyUEFIVar&lt;/strong&gt; 
&lt;/li&gt;
&lt;li&gt;GUID: &lt;strong&gt;A1B2C3D4-1234-5678-9ABC-DEF123456789&lt;/strong&gt; 
&lt;/li&gt;
&lt;li&gt;Content: &lt;strong&gt;0xDEADBEEF&lt;/strong&gt; 
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo5p2m9towf4bm5b5c9xq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo5p2m9towf4bm5b5c9xq.png" alt=" " width="308" height="102"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we can access it with these information. &lt;/p&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;2 - GetVariable()&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;GetVariable()&lt;/em&gt; service get content of the variable. Here are parameters of the service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;typedef&lt;/span&gt;
&lt;span class="n"&gt;EFI_STATUS&lt;/span&gt;
&lt;span class="nf"&gt;GetVariable&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;CHAR16&lt;/span&gt;           &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;VariableName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;EFI_GUID&lt;/span&gt;         &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;VendorGuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;OUT&lt;/span&gt; &lt;span class="n"&gt;UINT32&lt;/span&gt;          &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Attributes&lt;/span&gt; &lt;span class="n"&gt;OPTIONAL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;OUT&lt;/span&gt; &lt;span class="n"&gt;UINTN&lt;/span&gt;        &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;DataSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;OUT&lt;/span&gt; &lt;span class="n"&gt;VOID&lt;/span&gt;            &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt; &lt;span class="n"&gt;OPTIONAL&lt;/span&gt;
 &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's develop the project with GetVariable service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;Uefi.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;Library/UefiApplicationEntryPoint.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;Library/UefiLib.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;Library/PcdLib.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;Library/UefiBootServicesTableLib.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;Library/UefiRuntimeServicesTableLib.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="n"&gt;EFI_STATUS&lt;/span&gt; &lt;span class="n"&gt;EFIAPI&lt;/span&gt; &lt;span class="nf"&gt;UefiMain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EFI_HANDLE&lt;/span&gt; &lt;span class="n"&gt;ImageHandle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EFI_SYSTEM_TABLE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;SystemTable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;CHAR16&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;VariableName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;L"MyUEFIVar"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;EFI_GUID&lt;/span&gt; &lt;span class="n"&gt;VendorGuid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mh"&gt;0xa1b2c3d4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x1234&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x5678&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mh"&gt;0x9a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0xbc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0xde&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0xf1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x67&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x89&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="n"&gt;UINT8&lt;/span&gt; &lt;span class="n"&gt;ContentOfVariable&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mh"&gt;0xDE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xAD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xBE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xEF&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="n"&gt;UINTN&lt;/span&gt; &lt;span class="n"&gt;DataSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ContentOfVariable&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;EFI_STATUS&lt;/span&gt; &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EFI_SUCCESS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;UINT32&lt;/span&gt; &lt;span class="n"&gt;Attributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EFI_VARIABLE_NON_VOLATILE&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
                      &lt;span class="n"&gt;EFI_VARIABLE_BOOTSERVICE_ACCESS&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
                      &lt;span class="n"&gt;EFI_VARIABLE_RUNTIME_ACCESS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


  &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gRT&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;SetVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;VariableName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;VendorGuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;Attributes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;DataSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ContentOfVariable&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EFI_ERROR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;Print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;L"Failed to write!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;Print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;L"The content successfully written!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;UINT8&lt;/span&gt; &lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="n"&gt;DataSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gRT&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;GetVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;VariableName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;VendorGuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Attributes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;DataSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EFI_ERROR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;L"Failed to read the variable!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;DataSize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;L"Content: 0x%02x&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;EFI_SUCCESS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After &lt;strong&gt;SetVariable&lt;/strong&gt; called, we called GetVariable service to get content of the our UEFI Variable, then we printed the result. Here's the result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F30hdkysywu6gbixsz5gu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F30hdkysywu6gbixsz5gu.png" alt=" " width="280" height="141"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And now we can focus our Windows Driver. &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Coding Windows Driver&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Essentially there's a routine which we can use for our purpose in ntoskrnl. &lt;strong&gt;ExGetFirmwareEnvironmentVariable&lt;/strong&gt; can be used to read UEFI Variables. So coding of the windows driver will not be difficult. We can call this routine with UEFI Variable information:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;ntddk.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="n"&gt;NTSTATUS&lt;/span&gt; &lt;span class="nf"&gt;DriverUnload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PDRIVER_OBJECT&lt;/span&gt; &lt;span class="n"&gt;DriverObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;UNREFERENCED_PARAMETER&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DriverObject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;DbgPrint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Driver unloaded.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;STATUS_SUCCESS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;NTSTATUS&lt;/span&gt; &lt;span class="nf"&gt;DriverEntry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PDRIVER_OBJECT&lt;/span&gt; &lt;span class="n"&gt;DriverObject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PUNICODE_STRING&lt;/span&gt; &lt;span class="n"&gt;RegistryPath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;UNREFERENCED_PARAMETER&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RegistryPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;DriverObject&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;DriverUnload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DriverUnload&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;UNICODE_STRING&lt;/span&gt; &lt;span class="n"&gt;VariableName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RTL_CONSTANT_STRING&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;L"MyUEFIVar"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;UINT8&lt;/span&gt; &lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="n"&gt;ULONG&lt;/span&gt; &lt;span class="n"&gt;BufferSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;GUID&lt;/span&gt; &lt;span class="n"&gt;VendorGuid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="mh"&gt;0xa1b2c3d4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x1234&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x5678&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mh"&gt;0x9a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0xbc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0xde&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0xf1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x67&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x89&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="n"&gt;NTSTATUS&lt;/span&gt; &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;STATUS_SUCCESS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ExGetFirmwareEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;VariableName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;VendorGuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;BufferSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;NT_SUCCESS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;DbgPrintEx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Failed to Read Data!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ULONG&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;BufferSize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;DbgPrintEx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"The value from UEFI Variable: 0x%02x&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;STATUS_SUCCESS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the project, we accessed the content of the our UEFI Variable with ExGetFirmwareEnvironmentVariable routine. Here's result: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdxnopxwqwklzf1a12fhl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdxnopxwqwklzf1a12fhl.png" alt=" " width="508" height="157"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When ExGetFirmwareEnvironmentVariable called, The following routines are called after the routine: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6u4uh6jb3hnrtnv04s8n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6u4uh6jb3hnrtnv04s8n.png" alt=" " width="800" height="930"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First of all it calls &lt;strong&gt;ExpGetFirmwareEnvironmentVariable&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0nmurpujfa9j2ow98koi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0nmurpujfa9j2ow98koi.png" alt=" " width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And then the parameters of the ExGetFirmwareEnvironmentVariable are passed to IoGetEnvironmentVariableEx:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fngop2d72jht8804cc33m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fngop2d72jht8804cc33m.png" alt=" " width="416" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Creating UEFI Variable from Windows Driver&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Also we can create and set UEFI Variable from Windows Driver. &lt;strong&gt;ExSetFirmwareEnvironmentVariable&lt;/strong&gt; can be used for these purpose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;ntddk.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#pragma warning(disable: 4057)
&lt;/span&gt;
&lt;span class="cp"&gt;#define EFI_VARIABLE_NON_VOLATILE        0x00000001
#define EFI_VARIABLE_BOOTSERVICE_ACCESS  0x00000002
#define EFI_VARIABLE_RUNTIME_ACCESS      0x00000004
&lt;/span&gt;
&lt;span class="n"&gt;NTSTATUS&lt;/span&gt; &lt;span class="nf"&gt;DriverUnload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PDRIVER_OBJECT&lt;/span&gt; &lt;span class="n"&gt;DriverObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;UNREFERENCED_PARAMETER&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DriverObject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;DbgPrintEx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Driver unloaded.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;STATUS_SUCCESS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;NTSTATUS&lt;/span&gt; &lt;span class="nf"&gt;DriverEntry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PDRIVER_OBJECT&lt;/span&gt; &lt;span class="n"&gt;DriverObject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PUNICODE_STRING&lt;/span&gt; &lt;span class="n"&gt;RegistryPath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;UNREFERENCED_PARAMETER&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RegistryPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;DriverObject&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;DriverUnload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DriverUnload&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;UNICODE_STRING&lt;/span&gt; &lt;span class="n"&gt;VariableName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RTL_CONSTANT_STRING&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;L"YUPIIIIIII"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;WCHAR&lt;/span&gt; &lt;span class="n"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;L"HELLOOO from Windows!!"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;ULONG&lt;/span&gt; &lt;span class="n"&gt;BufferSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;GUID&lt;/span&gt; &lt;span class="n"&gt;VendorGuid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="mh"&gt;0xa1b2c3d4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x1234&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x5678&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mh"&gt;0x9a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0xbc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0xde&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0xf1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x67&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x89&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="n"&gt;NTSTATUS&lt;/span&gt; &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;STATUS_SUCCESS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;UINT32&lt;/span&gt; &lt;span class="n"&gt;Attributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EFI_VARIABLE_NON_VOLATILE&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;EFI_VARIABLE_BOOTSERVICE_ACCESS&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;EFI_VARIABLE_RUNTIME_ACCESS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ExSetFirmwareEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;VariableName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;VendorGuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BufferSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Attributes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;NT_SUCCESS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;DbgPrintEx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Failed to write the value! Error Code: 0x%x&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;DbgPrintEx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Done!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;STATUS_SUCCESS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And UEFI Driver:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;Uefi.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;Library/UefiApplicationEntryPoint.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;Library/UefiLib.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;Library/PcdLib.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;Library/UefiBootServicesTableLib.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;Library/UefiRuntimeServicesTableLib.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="n"&gt;EFI_STATUS&lt;/span&gt; &lt;span class="n"&gt;EFIAPI&lt;/span&gt; &lt;span class="nf"&gt;UefiMain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EFI_HANDLE&lt;/span&gt; &lt;span class="n"&gt;ImageHandle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EFI_SYSTEM_TABLE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;SystemTable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;CHAR16&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;VariableName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;L"YUPIIIIIII"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;EFI_GUID&lt;/span&gt; &lt;span class="n"&gt;VendorGuid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mh"&gt;0xa1b2c3d4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x1234&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x5678&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mh"&gt;0x9a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0xbc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0xde&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0xf1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x67&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x89&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="n"&gt;CHAR16&lt;/span&gt; &lt;span class="n"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="n"&gt;UINTN&lt;/span&gt; &lt;span class="n"&gt;DataSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;EFI_STATUS&lt;/span&gt; &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EFI_SUCCESS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;UINT32&lt;/span&gt; &lt;span class="n"&gt;Attributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EFI_VARIABLE_NON_VOLATILE&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
                      &lt;span class="n"&gt;EFI_VARIABLE_BOOTSERVICE_ACCESS&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
                      &lt;span class="n"&gt;EFI_VARIABLE_RUNTIME_ACCESS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gRT&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;GetVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VariableName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;VendorGuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Attributes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;DataSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EFI_ERROR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;Print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;L"Failed to read the data!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;Print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;L"The Content: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;EFI_SUCCESS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foam21nu15p8g5e5b6sig.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foam21nu15p8g5e5b6sig.png" alt=" " width="310" height="93"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In this article, we demonstrated how UEFI and Windows drivers can communicate with each other using NVRAM variables. By leveraging SetVariable and GetVariable in UEFI, and ExGetFirmwareEnvironmentVariable / ExSetFirmwareEnvironmentVariable in Windows, we established a persistent and reliable data channel between the two environments.&lt;/p&gt;

&lt;p&gt;This method enables developers to build seamless integrations between firmware and the OS, allowing for flexible configurations, feature toggles, or telemetry mechanisms across reboots.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;References&lt;/strong&gt;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://uefi.org/specs/UEFI/2.10/08_Services_Runtime_Services.html" rel="noopener noreferrer"&gt;UEFI.org - Runtime Services&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://tianocore-docs.github.io/edk2-UefiDriverWritersGuide/draft/5_uefi_services/52_services_that_uefi_drivers_rarely_use/525_getvariable_and_setvariable.html" rel="noopener noreferrer"&gt;TianoCore Docs - GetVariable() and SetVariable()&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>uefi</category>
      <category>edk2</category>
      <category>windev</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
