<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="atom.xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://hefung.github.io/mas/blog</id>
    <title>MAS Blog</title>
    <updated>2025-12-16T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://hefung.github.io/mas/blog"/>
    <subtitle>MAS Blog</subtitle>
    <icon>https://hefung.github.io/mas/img/favicon.ico</icon>
    <entry>
        <title type="html"><![CDATA[Moving Forward]]></title>
        <id>https://hefung.github.io/mas/blog/briefing</id>
        <link href="https://hefung.github.io/mas/blog/briefing"/>
        <updated>2025-12-16T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Introduction]]></summary>
        <content type="html"><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="introduction">Introduction<a href="https://hefung.github.io/mas/blog/briefing#introduction" class="hash-link" aria-label="Direct link to Introduction" title="Direct link to Introduction" translate="no">​</a></h2>
<p>We are constantly looking for ways to improve the reliability, usability, and efficiency of the script, as per our goal of creating the best Windows activator out there. As such, we'd like to announce some changes that we want to make sometime in the future.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="future-plans">Future Plans<a href="https://hefung.github.io/mas/blog/briefing#future-plans" class="hash-link" aria-label="Direct link to Future Plans" title="Direct link to Future Plans" translate="no">​</a></h2>
<p>The vast majority of MAS has been written in batch since its inception. As the project grew, however, it became increasingly harder to maintain, read, and debug the code. In many parts of MAS, we have to invoke PowerShell as a new process whenever batch can't accommodate what we want to do. The batch script has really started showing its age, and it's about time we move on to something better.</p>
<p>We have finally decided to begin work on a complete port of the script to PowerShell. This will allow us to achieve things that were previously impossible or infeasible:</p>
<ul>
<li class=""><strong>Better Performance:</strong> MAS is currently severely bottlenecked by the frequent need to start separate PowerShell processes. PowerShell takes a while to fully initialize; when you have to invoke it frequently, it wastes a lot of time.</li>
<li class=""><strong>Feature Set:</strong> It will allow us to add new features that would have been difficult to implement using batch.</li>
<li class=""><strong>Readable Codebase:</strong> Porting the script to PowerShell will allow us to adhere to modern standards for code organization, allowing more of our users to explore or audit the code much more comfortably.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="changes-to-operating-system-support">Changes to Operating System Support<a href="https://hefung.github.io/mas/blog/briefing#changes-to-operating-system-support" class="hash-link" aria-label="Direct link to Changes to Operating System Support" title="Direct link to Changes to Operating System Support" translate="no">​</a></h2>
<p>The current version of MAS is to be considered "feature-complete." It has full support for Windows Vista onwards, and at this point, the only updates it receives are related to maintenance or minor improvements.</p>
<p>As such, we have decided that it's best for the PowerShell port <strong>to not include support for Windows 8.1 and older</strong>. By focusing on modern Windows versions, we can cut down on the bloat of the current script considerably and reduce the time needed to complete the port's development.</p>
<p>We do understand that this script is very important to some of you for activating older versions of Windows, which is why the legacy batch version will still be available for download on our GitHub. This does not mean that we will continue to support it after the PowerShell port comes to fruition. However, by the time the PowerShell port is completed, support from Microsoft for these old versions will be completely gone. This means that the legacy script should continue to work on these old versions without requiring future updates from us, as no new system changes will be introduced that could break it.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="when">When?<a href="https://hefung.github.io/mas/blog/briefing#when" class="hash-link" aria-label="Direct link to When?" title="Direct link to When?" translate="no">​</a></h2>
<p>We don't have a concrete date for when the PowerShell port will be released to the public. We are all people who work on the project for free in our spare time, and many factors can drastically affect development time. Until the new version is ready, the batch version of the script will continue to be fully supported.</p>
<p>It is likely that we will have a brief public beta-testing period before deprecating the batch version for good, so the switch won't be too swift.</p>
<p>We really appreciate your patience while we work towards our goal of making the script even better than it already is. Take care.</p>]]></content>
        <author>
            <name>Lyssa</name>
            <uri>https://github.com/thecatontheceiling</uri>
        </author>
        <category label="MAS" term="MAS"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[The &s that (temporarily) killed TSforge]]></title>
        <id>https://hefung.github.io/mas/blog/pesky-ampersands</id>
        <link href="https://hefung.github.io/mas/blog/pesky-ampersands"/>
        <updated>2025-07-08T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Introduction]]></summary>
        <content type="html"><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="introduction">Introduction<a href="https://hefung.github.io/mas/blog/pesky-ampersands#introduction" class="hash-link" aria-label="Direct link to Introduction" title="Direct link to Introduction" translate="no">​</a></h2>
<p>A recent Windows update killed our new activation exploit, <a href="https://massgrave.dev/tsforge" target="_blank" rel="noopener noreferrer" class="">TSforge</a>... or so we thought. I will break down what went wrong, how we worked around the problem, and in the process, make a point about the apparent decline in Microsoft's code quality control.</p>
<!-- -->
<p>When we released <a href="https://massgrave.dev/tsforge" target="_blank" rel="noopener noreferrer" class="">TSforge</a> back in February, it served as an entrypoint for <a href="https://massgrave.dev/tsforge#zerocid--kms4k--avma4k" target="_blank" rel="noopener noreferrer" class="">multiple activation methods</a>. Many <a href="https://www.techspot.com/news/106819-hacker-group-releases-updated-tool-activate-almost-all.html" target="_blank" rel="noopener noreferrer" class="">news</a> <a href="https://www.androidauthority.com/mas-activate-windows-office-v3-tsforge-3527610/" target="_blank" rel="noopener noreferrer" class="">websites</a> wrote articles about it on release, giving it a ton of publicity.</p>
<p>One of the main goals of TSforge was to enable the free activation of the Windows 10 <a href="https://learn.microsoft.com/en-us/windows/whats-new/extended-security-updates" target="_blank" rel="noopener noreferrer" class="">Extended Security Updates program</a>, giving thousands a simple way to keep using their Windows 10 system safely for a few more years without additional fees.</p>
<p>It was also the first permanent activation method for Windows versions earlier than Windows 10 that didn't require <a href="https://forums.mydigitallife.net/threads/windows-loader-download.58464/" target="_blank" rel="noopener noreferrer" class="">installing a bootkit into your system to inject an SLIC table into ACPI</a>, a process known to sometimes cause issues with booting the system.</p>
<p>Initially, <a href="https://massgrave.dev/" target="_blank" rel="noopener noreferrer" class="">MAS</a> integrated only one TSforge-based activation method: ZeroCID. ZeroCID was chosen because, due to how it works, it could activate <a href="https://massgrave.dev/tsforge#supported-products" target="_blank" rel="noopener noreferrer" class=""><strong>basically everything permanently</strong></a> and it was tested by us rigorously before release to ensure reliability.</p>
<p>Everything was seemingly going fine with the release... until it wasn't.</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</div><div class="admonitionContent_BuS1"><p>This post focuses on the bug and the Software Protection Platform (SPP) DRM system in general as it appears in Windows 11. Specific details may differ in other Windows versions.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-incident">The Incident<a href="https://hefung.github.io/mas/blog/pesky-ampersands#the-incident" class="hash-link" aria-label="Direct link to The Incident" title="Direct link to The Incident" translate="no">​</a></h2>
<p>Merely a month later, ZeroCID was confirmed patched on the then-latest Windows 11 Insider build, 27802:</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/failedact-2b4aad61a6f877c07ca1d1b4076221e1.png" width="1136" height="587" class="img_ev3q"></p>
<p>To put this into perspective, Microsoft going out of its way to patch (or even care about) activation exploits was <a href="https://www.bleepingcomputer.com/news/security/microsoft-support-cracks-windows-for-customer-after-activation-fails/" target="_blank" rel="noopener noreferrer" class="">unheard of</a> since the release of Windows 10 back in 2015.</p>
<p>Whenever Microsoft "patched" an activation method, it was almost always an unintentional side effect of an unrelated change. The first version of HWID (our most used activation method to date), for example, was patched by Microsoft unintentionally due to them <a href="https://devicepartner.microsoft.com/en-us/communications/comm-windows-ends-installation-path-for-free-windows-7-8-upgrade" target="_blank" rel="noopener noreferrer" class="">finally remembering to cut off the free Windows 7 to Windows 10 upgrade that was supposed to end 7 years prior</a>. The first version of HWID just so happened to rely on this free upgrade path to grant activation. Fortunately we managed to promptly update it 2 days later to un-patch it. Thanks Bill Gates.</p>
<p>But this time, we weren't so sure. Our immediate conclusion to this was simple: the party was finally over. Microsoft had caught on and patched the exploit intentionally for once. This theory seemed even more likely because the news articles written about TSforge had put us directly in the spotlight, directly in Microsoft's line of sight. It would make sense if Microsoft finally got someone to do something about the new developments in the Windows piracy department, as an attempt to protect their reputation.</p>
<p>But that theory quickly fell apart.</p>
<p>As explained previously, TSforge is simply an entrypoint. We made multiple activation methods that used said entrypoint to function. ZeroCID was now broken, but there also existed KMS4k, which KMS activates Windows for a staggering ~4000 years. We proceeded to test KMS4k on the new, problematic build and it ended up working flawlessly.</p>
<p>This didn't make a lot of sense. Why would Microsoft make such an incomplete, effectively duct taped attempt to patch an exploit of this magnitude? This was nowhere near a comprehensive fix; it was a targeted hit on only one of the activation methods made possible by TSforge. It didn't feel like a deliberate patch; it felt like an accident. We had to know what really happened.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="context">Context<a href="https://hefung.github.io/mas/blog/pesky-ampersands#context" class="hash-link" aria-label="Direct link to Context" title="Direct link to Context" translate="no">​</a></h2>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>What is ZeroCID?</div><div class="admonitionContent_BuS1"><p>ZeroCID is an activation method that abuses the caching mechanism of Microsoft's legitimate "phone activation" system.</p><p>It works by creating fake cache entries that trick the Software Protection Platform (SPP) into granting activation without performing the actual cryptographic validation of the Confirmation ID (CID).</p></div></div>
<p>The legitimate phone activation process, which ZeroCID exploits, works like this:</p>
<p>Windows provides you with an "Installation ID" (IID), which you're expected to provide to Microsoft by calling them. They give you back a "Confirmation ID" (CID) which you can use to activate Windows. The CID is cryptographically checked by Windows against a public key stored in the license file of the license you're trying to activate.</p>
<p>Under the hood, SPP stores "cache" entries containing a hash of the IID and CID pair in the "trusted store" (an AES encrypted file containing activation data) upon an attempt to phone activate. This is done to save CPU cycles in the future by avoiding repeated cryptographic checks. Instead, SPP refers to the cache every time it needs to quickly determine whether activation should be granted or not.</p>
<p>This performance optimization is what ZeroCID abuses to activate Windows since, if there's valid cache entries in the trusted store, SPP will no longer try to validate the input CID and will blindly trust the cache, assuming that everything is, indeed, valid. When you use ZeroCID, it manually adds its own cache entries into the trusted store (where it calculates the hash as if the CID were all 0s, hence the name).</p>
<p>Here's one of the cache entries opened in a hex editor, with the relevant section of the entry selected:</p>
<p><img decoding="async" loading="lazy" alt="image" src="data:image/png;base64,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" width="555" height="80" class="img_ev3q"></p>
<p>The first 4 bytes store the length of the hash, and the rest of the highlighted bytes are the hash itself. The hash is created by concatenating the IID and the CID, then SHA256 hashing the final string.</p>
<p>This known, predictable behavior was our baseline when developing the activation methods that used TSforge. Now, it was time to see what was changed.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="investigation">Investigation<a href="https://hefung.github.io/mas/blog/pesky-ampersands#investigation" class="hash-link" aria-label="Direct link to Investigation" title="Direct link to Investigation" translate="no">​</a></h2>
<p>On the next day, <a href="https://github.com/WitherOrNot" target="_blank" rel="noopener noreferrer" class="">WitherOrNot</a> began looking into what happened, mostly by trying to figure out what code was changed in SPP in 27802 compared to the previous build. Around the same time, <a href="https://github.com/asdcorp" target="_blank" rel="noopener noreferrer" class="">asdcorp</a> also started looking into the incident and began inspecting the trusted store on the new build, trying to see if data was being stored differently.</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>What is the trusted store?</div><div class="admonitionContent_BuS1"><p>The trusted store is a file created by SPP using a proprietary format. It's designed to securely store important activation-related data and prevent users from modifying the information inside of it. However, TSforge makes it possible to easily modify the trusted store according to our needs.</p></div></div>
<p>Eventually, it was noticed that the IID + CID pair hashes in the cache entries seemed to be calculated differently. At the time, we didn't know what exactly was being hashed, or if a workaround was even possible.</p>
<p>But then, yet another oddity was noticed. After some more time, we noticed that the hashes saved in the cache entries seemed to <strong>change every single time the user checked the activation status</strong>. SPP seemed to now be spamming Event Viewer logs with the following message as well:</p>
<p><img decoding="async" loading="lazy" alt="image" src="data:image/png;base64,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" width="609" height="161" class="img_ev3q"></p>
<p>This was very unusual, because the above Event Viewer log is only supposed to be sent if the IID and CID pair were re-validated cryptographically. This observation implied that they were essentially feeding garbage to the part of the code that generates the hash, which means that SPP would be <strong>forced</strong> to regenerate the hashes every single time the user performs an operation, therefore, succesfully hackpatching ZeroCID, which depended on SPP not checking the input CID if the cache was present. The cache would now always fail to validate due to the hashes never matching the IID + CID pair provided to SPP by the user.</p>
<p>This was deeply puzzling. It would be an extremely strange way to patch the exploit, to the point that it came across as a bit of an absurd conclusion based on the limited data we had. WitherOrNot doubted this theory and decided to confirm it himself by firing up a debugger (x64dbg), then starting to look for the function which was creating the hash. Surely we were mistaken and something else was going on.</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/debugger1-95ebd1a2392c314bac67ee745b5776ab.png" width="1920" height="358" class="img_ev3q"></p>
<p>Here's the debugger, with a breakpoint set on the <code>BcryptHashData</code> function which has been hit. You can see the arguments it's receiving on the right hand side. Even from this one screenshot, the issue is pretty clearly visible. For those unfamiliar with C-like languages, the ampersand (&amp;) you see here before the second argument means we're passing a memory address <em>to</em> the data, not the data itself.</p>
<p>Yep, they're <strong>hashing the memory address of the data instead of the data itself</strong>. Which means the calculated hash will <em>always</em> be different every time SPP runs this check. This is effectively <strong>RNG-based hashing.</strong> They actually make this mistake twice, on both the IID and the CID.</p>
<p>To illustrate this more clearly, here's a simplified pseudocode representation of how you're supposed to do this correctly with BCrypt:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">Correct implementation of the changes</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// Get the IID and the CID</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">LPWSTR iid_data </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">get_iid</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">LPWSTR cid_data </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">get_cid</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Create a handle for the SHA256 algorithm</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">hash_handle </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">BCryptCreateHash</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 1. Hash the IID</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">BCryptHashData</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">hash_handle</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> iid_data</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> size_of_iid</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 2. Hash the CID</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">BCryptHashData</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">hash_handle</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cid_data</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> size_of_cid</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 3. Get the resulting hash</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">BCryptFinishHash</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">hash_handle</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> final_hash_buffer</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<p>This is what Windows 11 does instead:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// Get the IID and the CID</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">LPWSTR iid_data </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">get_iid</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">LPWSTR cid_data </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">get_cid</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Create a handle for the SHA256 algorithm</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">hash_handle </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">BCryptCreateHash</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 1. Hash the **address** of the IID data buffer</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">BCryptHashData</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">hash_handle</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain">iid_data</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> size_of_iid</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 2. Hash the **address** of the CID data buffer</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">BCryptHashData</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">hash_handle</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain">cid_data</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> size_of_cid</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// The resulting hash is now useless due to the memory locations always being different</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">BCryptFinishHash</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">hash_handle</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> final_hash_buffer</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<p>Even if they made this mistake once, it still would have singlehandedly killed the phone activation cache, and by extension, ZeroCID. The cache would be generated with a completely random hash, and on the next check, it would fail validation because the memory locations of these inputs are very volatile and bound to change each time.</p>
<p>As mentioned previously, ZeroCID depends on SPP reading the cache, validating it successfully, then skipping cryptographic validation. If you legitimately phone activate Windows, you would be mostly unaffected by this bug. This is because, even though the cache would always fail to pass the hash checks, since the input IID and CID are actually valid, SPP can simply perform the cryptographic revalidations necessary every single time without issue.</p>
<p>Of course, this is inefficient. But, as we know from Windows 11, Microsoft doesn't care much about optimizing their OS anymore. If it isn't a massive problem, it's not important enough to be fixed.</p>
<p>Before the release of our workaround method (dubbed StaticCID), we attempted to report this bug to Microsoft and bring it to their attention using the Feedback Hub, where your filed issue will have a very slim chance to be seen by anyone who can fix the bug until your post gets a large amount of upvotes. We sought other possible avenues to get <em>someone</em> to remove the damn &amp;'s from the code, with no luck.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="really">Really?<a href="https://hefung.github.io/mas/blog/pesky-ampersands#really" class="hash-link" aria-label="Direct link to Really?" title="Direct link to Really?" translate="no">​</a></h2>
<p>Now, you might ask a reasonable question: "Why?". To be perfectly candid, I am not really sure.</p>
<p>Compared to the Windows 10 version of sppobjs.dll (the SPP "plugin" that performs this crypto check and writes the cache), it seems like the changes made to the now-problematic function was part of an effort, possibly by a single engineer, to transition this part of the code from using SPP's internal hashing implementations to using the external, Microsoft-made library called <a href="https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/" target="_blank" rel="noopener noreferrer" class="">Bcrypt</a> instead:</p>
<p><img decoding="async" loading="lazy" alt="screenshot of two IDA windows, comparing the same function - courtesy of WitherOrNot" src="https://hefung.github.io/mas/assets/images/func_comparison-52bb58cef7af451e3f59f73033e1000c.png" width="1700" height="824" class="img_ev3q"></p>
<p>It's not clear why this "refactoring" was done, considering that this code had otherwise been untouched for more than a decade. Maybe the goal was to "optimize" this hash check in some way? In the era of modern CPUs, this makes little sense. To simplify the code? Maybe. But, as I said, this code had been untouched for <strong>a decade</strong>. This seems like changing things just for the sake of changing things.</p>
<p>A change like this <em>should</em> have been carefully reviewed. Clearly, it wasn't.</p>
<p>The Windows Git repository has thousands of branches, and changes get reverse-integrated into different branches, slowly making their way down to whatever is the release branch. This bug got past the initial code review, made its way from the Windows Insider Canary channel, to the Dev channel, to the beta channel (already making the change very likely to reach stable builds), then it was finally shipped with the June cumulative updates.</p>
<p>This bug slipped past <em>at least</em> 4 branches, completely unacknowledged, leaving us hopeless that Microsoft would do anything to fix this slip up.</p>
<p>And yes, we kept checking if Microsoft used their chance to notice the bug or not after every major build.</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/fuckuing-50a54ed09f9d974beff662f5ba68a88d.png" width="998" height="645" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="turning-the-tables">Turning the Tables<a href="https://hefung.github.io/mas/blog/pesky-ampersands#turning-the-tables" class="hash-link" aria-label="Direct link to Turning the Tables" title="Direct link to Turning the Tables" translate="no">​</a></h2>
<p>Many of you may have read our <a href="https://massgrave.dev/blog/tsforge" target="_blank" rel="noopener noreferrer" class="">TSforge blog</a> from a few months ago, where we explained the process of creating the exploit. What wasn't described there was how we became aware of yet another method during development (approximately November 2024), which ultimately didn't end up being shipped with the final product due to fears of potential instability. After we became aware of the patch, we quickly went back to take another look at this method and start testing it further.</p>
<p>The method is quite interesting. Effectively, it mimics real phone activation as closely as possible while still managing to activate Windows for free. How, exactly? Let's explain.</p>
<p>We can start by taking a look at the <code>SppPkeyPhoneActivationData</code> block in the trusted store for the currently installed license to see what it consists of:</p>
<p><img decoding="async" loading="lazy" alt="image" src="data:image/png;base64,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" width="919" height="192" class="img_ev3q"></p>
<p>The information located in this block is what SPP uses during phone activation, and it's always written when a product key is being installed. This block contains information about said product key, namely:</p>
<ul>
<li class="">The group ID.</li>
<li class="">The serial number.</li>
<li class="">The "security" value.</li>
</ul>
<p>What these values represent isn't important, what <em>is</em> important is that, in theory, this is all the information we need to successfully phone activate Windows! We can simply rip these values out of a genuine, working key and manually insert this information into the trusted store.</p>
<p>After doing the above, SPP will generate an IID based on data ripped from a genuine product key, rather than data from the generic product key that Windows comes with by default. This means that Microsoft will allow us to use our newly obtained, activatable IID to get a CID, allowing us to phone activate Windows for free.</p>
<p>At first glance, the non-trivial part of this is getting the CID autonomously. Obviously, it is far from ideal to force our users to call Microsoft on the phone and provide them with the IID manually, so we needed a better way.</p>
<p>Luckily, a Microsoft tool already exists called the <a href="https://learn.microsoft.com/en-us/windows/deployment/volume-activation/introduction-vamt" target="_blank" rel="noopener noreferrer" class="">Volume Activation Management Tool</a> (VAMT), which has functionality for <a href="https://learn.microsoft.com/en-us/windows/deployment/volume-activation/proxy-activation-vamt" target="_blank" rel="noopener noreferrer" class="">"proxy activating"</a> Windows systems.</p>
<p>Proxy activation essentially performs the equivalent of phone activation in a way which allows for activating many client computers isolated from the outer internet, while avoiding having to call any phone numbers. A master "VAMT Host" computer, which does have internet access, acquires the CID online from an online endpoint and applies it to the client computers to activate them.</p>
<p>Some people have reverse-engineered how VAMT does this, and there's a really nice tool called <a href="https://github.com/dadorner-msft/activationws" target="_blank" rel="noopener noreferrer" class="">ActivationWs</a> which uses the same endpoints that VAMT uses to acquire the CID. StaticCID uses this tool to get the CID, then it deposits the CID, successfully pulling off a weird, but effective mix of phone and online activation.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="quality-is-job-none">Quality is Job None<a href="https://hefung.github.io/mas/blog/pesky-ampersands#quality-is-job-none" class="hash-link" aria-label="Direct link to Quality is Job None" title="Direct link to Quality is Job None" translate="no">​</a></h2>
<p>The new StaticCID method is now introduced into <a href="https://massgrave.dev/" target="_blank" rel="noopener noreferrer" class="">MAS</a> and is working flawlessly. Previously, MAS only offered ZeroCID, with no option to use anything else. This was initially done due to ZeroCID being powerful enough to overshadow everything else made possible by the discovery of TSforge. Now, we are being more cautious due to Microsoft seemingly messing up even the oldest parts of the codebase without reason and have made 3 methods accessible instead of only one: StaticCID, ZeroCID and the previously-mentioned KMS4k.</p>
<p>The script automatically chooses which method is best for the current OS, while also allowing the user to manually specify which one they prefer. The script automatically chooses StaticCID as the default option on Windows 10 as well, due to concerns about the bug being backported to older Windows versions. We've observed Microsoft do this a few times before, even though <a href="https://techcommunity.microsoft.com/blog/windows-itpro-blog/windows-client-roadmap-update-april-2023/3805227" target="_blank" rel="noopener noreferrer" class="">Windows 10 isn't supposed to get new feature updates anymore</a>, so it feels like a worthwhile precaution.</p>
<p>This incident suggests a troubling trend in Microsoft's development and code quality assurance pipeline. Only time will tell what else they'll inadvertently break and fail to fix. However, we will continue to adapt to these changes and deliver the best experience possible to our users, regardless of what Microsoft does next.</p>]]></content>
        <author>
            <name>Lyssa</name>
            <uri>https://github.com/thecatontheceiling</uri>
        </author>
        <category label="Windows" term="Windows"/>
        <category label="Activation" term="Activation"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[TSforge]]></title>
        <id>https://hefung.github.io/mas/blog/tsforge</id>
        <link href="https://hefung.github.io/mas/blog/tsforge"/>
        <updated>2025-02-14T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[By WitherOrNot]]></summary>
        <content type="html"><![CDATA[<p>By WitherOrNot<br>
<!-- -->Edited by May</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="introduction">Introduction<a href="https://hefung.github.io/mas/blog/tsforge#introduction" class="hash-link" aria-label="Direct link to Introduction" title="Direct link to Introduction" translate="no">​</a></h2>
<p>2025 marks nearly 20 years since the introduction of Windows' current DRM system, the Software Protection Platform (SPP). With it serving as the primary gateway to activation since <a href="https://betawiki.net/wiki/Windows_Vista_build_5212_(winmain)" target="_blank" rel="noopener noreferrer" class="">early in Windows Vista's development</a>, many have come up with clever ways of tricking it, from <a href="https://www.mydigitallife.net/activate-64-bit-windows-vista-ultimate-and-x64-with-timerstop-v2a-crack-plus-2099-trick/" target="_blank" rel="noopener noreferrer" class="">resetting grace period timers</a> to <a href="https://forums.mydigitallife.net/threads/kmsemulator-kms-client-and-server-emulation-source.41010/" target="_blank" rel="noopener noreferrer" class="">emulating KMS servers</a> to <a href="https://forums.mydigitallife.net/threads/windows-loader-download.58464/" target="_blank" rel="noopener noreferrer" class="">hooking bootloaders</a>. While all of these systems abuse various activation methods, there has never been an exploit that directly attacked SPP itself... until now.</p>
<!-- -->
<p>In this blogpost, we introduce <a href="https://github.com/massgravel/TSforge" target="_blank" rel="noopener noreferrer" class="">TSforge</a>, one of our most powerful activation exploits ever. Capable of activating every edition of every version of Windows since Windows 7, as well as every Windows addon and Office version since Office 2013, TSforge is both the most complex and most wide-reaching exploit we've implemented in MAS to date, rivaled only by our ill-fated <a href="https://massgrave.dev/blog/keyhole" target="_blank" rel="noopener noreferrer" class="">Keyhole</a> exploit. Aside from discussing how TSforge works, I'll also be discussing the wild path we took to discover and understand it, as well as some of the fun things we did with it along the way.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="spp">SPP<a href="https://hefung.github.io/mas/blog/tsforge#spp" class="hash-link" aria-label="Direct link to SPP" title="Direct link to SPP" translate="no">​</a></h2>
<p>SPP is a very complex system with several moving parts involved. To keep things simple, we'll only focus on the parts relevant to this exploit:</p>
<ul>
<li class=""><code>sppsvc.exe</code>/<code>slsvc.exe</code> - The primary usermode service for SPP, responsible for managing licenses and activation statuses</li>
<li class=""><code>spsys.sys</code> - (Windows Vista/7 only) Responsible for storing sensitive activation information, merged into sppsvc since Windows 8</li>
<li class=""><code>sppobjs.dll</code> - A plugin library containing most of the logic for product key and confirmation ID validation</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="cid-trick">CID Trick<a href="https://hefung.github.io/mas/blog/tsforge#cid-trick" class="hash-link" aria-label="Direct link to CID Trick" title="Direct link to CID Trick" translate="no">​</a></h2>
<p>Our first hint of a universal SPP exploit was in 2023, with the discovery of a method we called the "CID trick", which allowed us to deposit fake confirmation IDs.</p>
<p>Confirmation IDs (CIDs) are numeric values used for <a href="https://support.microsoft.com/en-us/windows/product-activation-for-windows-online-support-telephone-numbers-35f6a805-1259-88b4-f5e9-b52cccef91a0" target="_blank" rel="noopener noreferrer" class="">activating Windows by phone</a>, either through a dialog or with the <code>slmgr /atp</code> command. Because phone activation can be done without being connected to any network, all Windows editions and addons have at least one licensing channel that can be phone-activated, including otherwise uncrackable products like KMS servers and ESU addons.</p>
<p>Recognizing this, asdcorp decided to investigate how CID validation worked internally. After doing a simple in-memory patch to make any provided CID valid, they noticed it had some odd effects:</p>
<!-- -->
<!--$--><video style="display:block;width:100%;height:auto" src="/cidtrick.mp4" controls=""></video><!--/$-->
<p>As shown in the video, patching the CID validation code in <code>sppobjs.dll</code> allowed us to use a CID made of all zeroes for activation. Crucially though, this activation remained even after clearing the patch by restarting sppsvc. To us, this suggested something very important: <strong>whatever data SPP saves to remember that it's activated is never validated after being written</strong>.</p>
<p>This was a very exciting discovery for us, as it meant that if we could write this same data, we could easily activate any copy of Windows without needing to use debuggers or <a href="https://github.com/itm4n/PPLcontrol" target="_blank" rel="noopener noreferrer" class="">exploit kernel drivers</a>. More importantly for me, it also meant that this method could possibly be extended to work on older versions like <a href="https://en.wikipedia.org/wiki/Special_interest_(autism)" target="_blank" rel="noopener noreferrer" class="">Windows 7</a> and 8, where patching SPP code at runtime is much more difficult.</p>
<p>In order make this a viable method, though, we needed to answer these questions:</p>
<ul>
<li class="">Where is activation data being written?</li>
<li class="">What data is written during the CID trick that causes activation?</li>
<li class="">How is this data encoded?</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="initial-work">Initial Work<a href="https://hefung.github.io/mas/blog/tsforge#initial-work" class="hash-link" aria-label="Direct link to Initial Work" title="Direct link to Initial Work" translate="no">​</a></h2>
<p>From prior investigations, we had a partial answer to the first question. Observing sppsvc using <a href="https://learn.microsoft.com/en-us/sysinternals/downloads/procmon" target="_blank" rel="noopener noreferrer" class="">Process Monitor</a>, we could see exactly where it was storing activation data:</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/procmon1-10ad0a84d790c8f3e3e8b60799c6fee2.png" width="778" height="788" class="img_ev3q"></p>
<p>On Windows 8.1 and 10, we observed that data is mainly stored in the following locations:</p>
<ul>
<li class=""><code>C:\Windows\System32\spp\store\2.0\data.dat</code></li>
<li class=""><code>C:\Windows\System32\spp\store\2.0\tokens.dat</code></li>
<li class=""><code>HKEY_LOCAL_MACHINE\SYSTEM\WPA</code></li>
</ul>
<p>Windows 8 is almost identical, except that the <code>.dat</code> files are stored under <code>C:\Windows\System32\spp\store</code>.</p>
<p>Windows 7, however, is a very different story:</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/procmon2-2daa519d6aa38b6b643ca05174f551a4.png" width="1050" height="185" class="img_ev3q"></p>
<p>Here, I saw references to the following locations:</p>
<ul>
<li class=""><code>C:\Windows\System32\7B296FB0-376B-497e-B012-9C450E1B7327-5P-0.C7483456-A289-439d-8115-601632D005A0</code></li>
<li class=""><code>C:\Windows\System32\7B296FB0-376B-497e-B012-9C450E1B7327-5P-1.C7483456-A289-439d-8115-601632D005A0</code></li>
<li class=""><code>C:\Windows\ServiceProfiles\NetworkService\AppData\Roaming\Microsoft\SoftwareProtectionPlatform\tokens.dat</code></li>
<li class=""><code>HKEY_LOCAL_MACHINE\SYSTEM\WPA</code></li>
</ul>
<p>More strangely, though, I found that sppsvc wouldn't write to either of the "7B296..." files or WPA registry keys directly. Instead, it would use the <a href="https://learn.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-deviceiocontrol" target="_blank" rel="noopener noreferrer" class=""><code>DeviceIoControl</code></a> method to call a driver known as <code>spsys.sys</code>. This driver would then handle writing to "7B296..." files and WPA registry keys.</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/procmon3-a95a3eb71cead9fd8abd818b05436ae0.png" width="645" height="436" class="img_ev3q"></p>
<p>In comparing these files and registry keys, I saw quite a few similarities. The <code>tokens.dat</code> files were mostly uninteresting at first, since across all versions, these files seemed to just hold the contents of the XML licenses in a similarly named folder: <code>C:\Windows\System32\spp\tokens</code>.</p>
<p>The "7B296..." and <code>data.dat</code> files seemed to serve similar roles, as these files were not only encrypted, but they seemed to have some kind of hash or signature included as well. Corrupting or deleting these files would uninstall all installed product keys and reset all other activation data (rearm counts, KMS client counts, etc.). On Windows 7, it would also show this lovely error message:</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/unauth_error-9180013bd8058b5b7f593aabb0b31a54.png" width="388" height="271" class="img_ev3q"></p>
<p>Setting aside the question of how I can't be authorized to make changes to my own computer, after installing a product key, I get a notification for "tampering with the trusted store" across several versions:</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/tamper-a5a3fd54fcac11b05b62213fa7aa4e1a.png" width="451" height="689" class="img_ev3q"></p>
<p>Putting all this together, it would seem like the "7B296..." files and <code>data.dat</code> both serve as storage for important activation data, and they seem to be referred to internally as the "trusted store".</p>
<p>A similar process of brute-force tampering with the WPA registry keys showed that they were somehow linked with the trusted store. New keys with seemingly encrypted data were added under <code>HKEY_LOCAL_MACHINE\SYSTEM\WPA</code> periodically, as well as after significant licensing changes. Additionally, thanks to the <a href="https://www.tiraniddo.dev/2017/07/locking-your-registry-keys-for-fun-and.html" target="_blank" rel="noopener noreferrer" class=""><code>NtLockProductActivationKeys</code></a> function, these keys were entirely read-only and unable to be deleted, unless you messed with them from a Windows PE environment. Tampering with or deleting these keys caused similar "license tampering" errors to appear, but if we copied these keys along with trusted store files from one installation to another, sppsvc didn't seem to complain about tampering anymore.</p>
<p>From all of this work, we learned the following things:</p>
<ul>
<li class="">Critical activation data like product keys and rearm counts are stored in something known as the "trusted store"</li>
<li class="">The trusted store's data is held in encrypted files</li>
<li class="">This data is somehow linked with seemingly encrypted registry keys under <code>HKLM\SYSTEM\WPA</code></li>
</ul>
<p>Unfortunately, we didn't know much more than this for quite a long time. My work on deobfuscating both <a href="https://github.com/UMSKT/peacestone" target="_blank" rel="noopener noreferrer" class="">older</a> and <a href="https://github.com/WitherOrNot/warbird-docs" target="_blank" rel="noopener noreferrer" class="">newer</a> versions of sppsvc helped us in confirming some of our theories, but without an understanding of <code>spsys.sys</code>, they didn't contribute much. In the meantime, we built an automated version of the CID trick, using a custom kernel driver to patch sppsvc without adjusting its <a href="https://www.alex-ionescu.com/why-protected-processes-are-a-bad-idea/" target="_blank" rel="noopener noreferrer" class="">protected process</a> status, which helped greatly with testing CID trick.</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/miieow1-bc902294d67d7440ea56bf8aeb2a1410.png" width="1447" height="814" class="img_ev3q"></p>
<p>Aside from this, though, we mostly shelved this work in favor of investigating CLiP, which seemed to have more promising avenues for exploitation.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="when-it-leaks-it-floods">When It Leaks, It Floods<a href="https://hefung.github.io/mas/blog/tsforge#when-it-leaks-it-floods" class="hash-link" aria-label="Direct link to When It Leaks, It Floods" title="Direct link to When It Leaks, It Floods" translate="no">​</a></h2>
<p>Although we focus heavily on Windows piracy, many of us MASSGRAVE members are also interested in its development history, or more specifically, its various pre-release beta builds. Studying and messing with these builds is not only fun as a novelty, but the artifacts that get left in during development can help us learn a lot about how Windows works.</p>
<p>While discussing activation mechanisms of recently leaked Windows 8 builds with some beta conoisseurs, I was casually blindsided with a couple major reveals:</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/convo1-1fca4b221ac524f5141bb4c687a4ef5b.png" width="866" height="281" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/convo2-dca0fba0b168c21154605933b13b6a38.png" width="618" height="51" class="img_ev3q"></p>
<p>Although <a href="https://betawiki.net/wiki/Windows_8_build_7792" target="_blank" rel="noopener noreferrer" class="">build 7792</a> and <a href="https://betawiki.net/wiki/Windows_8_build_7850" target="_blank" rel="noopener noreferrer" class="">build 7850</a> were on the path to Windows 8 development, their build numbers were close enough to Windows 7 (build 7600) that I was hopeful for some new information on spsys. Indeed, within a <a href="https://archive.org/details/Win8_7850_x64fre_symbols" target="_blank" rel="noopener noreferrer" class="">symbol archive for build 7850</a>, I found symbols for spsys, along with the rest of the activation subsystem.</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/symlist-0120b9be31eca04146a9e8b584b0081b.png" width="303" height="456" class="img_ev3q"></p>
<p>Build 7792 also had a version of spsys that was entirely unobfuscated, just as advertised, but with no symbols. Build 7850's spsys, on the other hand, had full symbols along with full obfuscation. While I wasn't able to have my cake and eat it too, this pairing was still an incredibly lucky finding, so I decided to use it to figure out how the trusted store works.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="spells-and-curses">Spells and Curses<a href="https://hefung.github.io/mas/blog/tsforge#spells-and-curses" class="hash-link" aria-label="Direct link to Spells and Curses" title="Direct link to Spells and Curses" translate="no">​</a></h2>
<p>As usual with lucky breaks, this one came with strings attached. The biggest one was that, unlike with any other normal Windows build, kernel debugging with WinDBG didn't work at all on build 7792, as this was one of the earliest ARM ports of Windows.</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/convo3-57d94ca239286346bddea7650793e161.png" width="645" height="290" class="img_ev3q"></p>
<p>So, although I had a completely deobfuscated copy of spsys without any annoying anti-debug features, I had very few options to actually debug it. Since this build was being emulated in QEMU, I still had the option of using its built-in GDB debugging server, but this would be very difficult to use, as I would have to manually locate the kernel and drivers in memory to do anything useful. Luckily, I was able to get in touch with Rairii, who was more familiar with this debugging method thanks to his work on <a href="https://github.com/Wack0/tegra2_qemu_woa" target="_blank" rel="noopener noreferrer" class="">emulating early Windows on ARM</a>.</p>
<p>Unfortunately, I knew little about QEMU OS debugging, or ARM, or very low-level Windows internals, so his advice was a bit difficult to follow at first...</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/convo4-c74ea5ff1460ca70acba01b95050a186.png" width="625" height="300" class="img_ev3q"></p>
<p>Compounding the problem was my choice to write custom tooling for this project, using a GDB client library to control QEMU and automatically break whenever spsys loaded. Much like most of my attempts at custom tooling, this ended up being a gigantic waste of time that ultimately didn't even work in the end, not least due to my inexperience with GDB in general. After wasting even more time trying to add Windows support to GDB through its <a href="https://sourceware.org/gdb/current/onlinedocs/gdb.html/Python-API.html#Python-API" target="_blank" rel="noopener noreferrer" class="">Python API</a>, I ended up biting the bullet and choosing the devil I knew, the IDA Pro debugger.</p>
<p>Yes, it's the least "appropriate" choice for debugging a kernel or debugging QEMU (or really anything at all), but what was more important to me was that I'm familiar with it. Using the various kernel structs I grabbed and modified from <a href="https://betawiki.net/wiki/Windows_8_build_7915" target="_blank" rel="noopener noreferrer" class="">build 7915</a>'s symbol set, I was able to crap out a script that would breakpoint on various kernel functions and grab important data, like the kernel base address and list of loaded kernel modules. By programmatically breaking on <code>IopLoadDriver</code>, I could even automatically update the module list as each driver was loaded.</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/modlist1-2476cfe71f7f0a2f05ec8450b0394a79.png" width="1258" height="208" class="img_ev3q"></p>
<p>However, even with this monitoring system, I still couldn't catch the moment that <code>spsys.sys</code> loaded. It was only after checking the code for its loader, <code>spldr.sys</code>, that I spotted an interesting call to <code>ZwSetSystemInformation</code>:</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/spsys_ld-e313cf44ff083cfcb206663aaab113d9.png" width="554" height="456" class="img_ev3q"></p>
<p>Referencing <a href="https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/sysinfo/set.htm" target="_blank" rel="noopener noreferrer" class="">Geoff Chappell's table</a>, I saw that this corresponded to <code>SystemLoadGdiDriverInSystemSpace</code>, which, of course, has it's own branch in the kernel code that can load drivers without invoking <code>IopLoadDriver</code>.</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/kbranch-f99ad330681d6a4a7bb067c01fb8ee7a.png" width="1300" height="640" class="img_ev3q"></p>
<p>After breaking at this branch as well, I could finally catch <code>spsys.sys</code> as soon as it loaded!</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/spsys_gotcha-7a7dc96ddab33650b87cd66ec65ba195.png" width="1240" height="239" class="img_ev3q"></p>
<p>All this work, mind you, ended up implementing what would've been just <code>sxe ld spsys</code> if WinDBG worked. Regardless, I was now able to catch and debug build 7792's spsys right as it loaded, without the annoying obfuscation and anti-debug in the way.</p>
<p>Speaking of obfuscation, I still needed to unpack build 7850's spsys to make any sense of it. Fortunately, this was much easier, since I could search for where calls to encrypted functions are made.</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/spsys_7850-33b5529d63c23ffffcc8ef49b09e7c8d.png" width="1235" height="524" class="img_ev3q"></p>
<p>Then, I just had to break at these calls in the kernel debugger and dump the driver from memory, and I got all of the the code decrypted fairly easily. From here, it was just a long process of transferring symbols by eye, and I was finally at square one.</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/labeled_funcs-578ac6d98b7f0192d9aeb561a9a07883.png" width="619" height="592" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="progress-at-last">Progress At Last<a href="https://hefung.github.io/mas/blog/tsforge#progress-at-last" class="hash-link" aria-label="Direct link to Progress At Last" title="Direct link to Progress At Last" translate="no">​</a></h2>
<p>With all of the functions labeled, I noticed an interesting pattern in the code that generates the trusted store file, which seems to be called the "physical store". Rather than the typical approach of storing encrypted data in a separate buffer, Microsoft seemed to opt for doing their encryption in-place.</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/encrypt_data-9b0a9639f17ab94cb861910eb06f7e50.png" width="444" height="322" class="img_ev3q"></p>
<p>This probably prevents some kind of side-channel attack, but more importantly, it means that I can "decrypt" the physical store by simply skipping this call in the debugger and letting spsys write the un-encrypted contents to the disk. And so I did:</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/decrypt_ps-77eb9e086a76e1e664380b17875849ba.png" width="619" height="596" class="img_ev3q"></p>
<p>asdcorp and abbodi1406 immediately went to work, figuring out the data format and what kind of data is stored within. Meanwhile, I was focused on replicating this trick on Windows 10, where analysis would be far easier, not least due to having an actual debugger (x64dbg) available. Conveniently, the function HIDHash had some very unique constants in it:</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/hidhash1-8da1f877e6c018e4a0bbbd6f4438fd4f.png" width="892" height="240" class="img_ev3q"></p>
<p>Searching for these constants in Windows 10's sppsvc led me to the same function in sppsvc:</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/hidhash2-dcac2f4c547e5aa5bcf3afd4dc62db0b.png" width="786" height="260" class="img_ev3q"></p>
<p>As I found out, most of spsys's code ended up being included in sppsvc on Windows 10. Comparing these codebases, I found all of the encryption, decryption, signature check, and hash check routines. Patching all of these routines out in the debugger, we could get sppsvc to not only decrypt its <code>data.dat</code> for us, but also to load and accept any modifications we made in it.</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/rearm_42069-29b14a468de8f45c41f497652972c8a4.png" width="428" height="440" class="img_ev3q"></p>
<p>In the process of testing these patches, we ended up finding the long-lost product key for <a href="https://betawiki.net/wiki/Feature_lockout_in_Windows#Windows_8" target="_blank" rel="noopener noreferrer" class="">Redpill</a>, a feature lockout system for beta releases of Windows 8.</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/redpill_key-aa7dc6655d0277408739ecb3852ea411.png" width="266" height="506" class="img_ev3q"></p>
<p>With some experimentation and a bit of assistance from me, asdcorp managed to reproduce the CID trick, but this time without patching the CID verification routine.</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/cidtrick2-d36508f74165c74441629a3c42f4fb9e.png" width="426" height="425" class="img_ev3q"></p>
<p>From here, asdcorp and Lyssa figured out and tested even more exploits, including a method to KMS-activate offline for over 4000 years.</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/kms4k-d17c5965bb4759bd88e9a141d5654716.png" width="1576" height="840" class="img_ev3q"></p>
<p>Although these results marked significant progress, we still needed to use a debugger to test these methods, since we had no way to decrypt and re-encrypt the physical store by ourselves. So, my next task was to figure out how to do just that.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="private-key-derivation">Private Key Derivation<a href="https://hefung.github.io/mas/blog/tsforge#private-key-derivation" class="hash-link" aria-label="Direct link to Private Key Derivation" title="Direct link to Private Key Derivation" translate="no">​</a></h2>
<p>I knew from looking at spsys that the only real key I needed to derive was an RSA key, which encrypts an AES key that encrypts the physical store data. I also knew from tests conducted with asdcorp that there were only two such keys: one for production/official beta versions and one for internal testing/Windows Insider versions. In the absence of raw key data, we found this out with a highly advanced method: copying physical store files, editing its version, and waiting for sppsvc to crash horrifically, signaling that it could decrypt but not parse the swapped file.</p>
<p>Examining the RSA decryption routine, called <code>SpModExpPrv</code>, I found a interpreter for a bytecode system known as <code>ExecCodes</code>. With a lot of drudgery and regexes, I was able to write a simulator of this system in Python.</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/execcodes-ff40348525144ecf36e32d9ac0ea8734.png" width="389" height="751" class="img_ev3q"></p>
<p>Observing the output of this simulation, I realized that all of this obfuscation covered up a technique I had vaguely heard of before, known as <a href="https://en.wikipedia.org/wiki/Addition-chain_exponentiation" target="_blank" rel="noopener noreferrer" class="">addition-chain exponentiation</a>. With a bit of thinking, I realized that I could just track and dump the inputs and outputs of each modular multiplication and use this to derive the private key. All it took was x64dbg logging and a few more lines of python:</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/keyderiv-f0d0d86ad18e8ba5596c9c2a9b93d33a.png" width="1143" height="402" class="img_ev3q"></p>
<p>And at last, I had the complete production key for all of SPP, from Windows Vista to Windows 11.</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/prodkey-5d116835c8bb008a61eb3a39bbc0d74a.png" width="531" height="270" class="img_ev3q"></p>
<p>Deriving the test key took a little while longer, due to some weird differences in how the modular multiplications were implemented, but eventually, we had every SPP private key we needed.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="nailing-the-coffin">Nailing The Coffin<a href="https://hefung.github.io/mas/blog/tsforge#nailing-the-coffin" class="hash-link" aria-label="Direct link to Nailing The Coffin" title="Direct link to Nailing The Coffin" translate="no">​</a></h2>
<p>With the private key in hand, we were able to activate almost any edition we wanted with ease. There was still one bit of trouble, though, and that was in obtaining generic keys to enable activation.</p>
<p>On Windows 8 and up, it's rather <a href="https://github.com/awuctl/licensing-stuff" target="_blank" rel="noopener noreferrer" class="">trivial</a> to generate generic keys for any channel of any edition you wanted. However, Windows 7 and CSVLKs (KMS host keys) up to Server 2022 used <a href="https://github.com/UMSKT/writeups/blob/main/PKEY2005.md" target="_blank" rel="noopener noreferrer" class="">PKEY2005</a>, a much more complicated encoding system that needed private keys to generate even generic keys. Since I didn't have any cryptographic tricks left up my sleeve, we decided the best way through this problem was around.</p>
<p>Within the physical store are large blobs for each product key, containing various pre-computed information, such as the product IDs and phone activation data. Additionally, we found that the token store (<code>tokens.dat</code>) also contains metadata tying product keys to the current edition of Windows. Therefore, we figured that simply replicating this data was enough to trick Windows into thinking a key was installed, and for once, we were right.</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/igpk-ebbb1b697ce9643ffdb2fcd017c101f4.png" width="394" height="538" class="img_ev3q"></p>
<p>In the meantime, while developing a programmatic method for the new CID trick, which we now called ZeroCID, we were having trouble with the HWID data we needed to write into the physical store. Originally, we tried using a <a href="https://github.com/laomms/HwidGenerator" target="_blank" rel="noopener noreferrer" class="">C# port of GatherOsState's HWID derivation</a>, but this ended up failing to validate in some rare cases. Since we had few options to debug or fix this port, asdcorp decided to create an HWID value that would apply to all hardware, and yet again, it worked perfectly, even allowing transfer of activation between machines.</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/uhwid-3f6b3b2f9925c2b177d2b0168f1615ac.png" width="1397" height="500" class="img_ev3q"></p>
<p>With the HWID validation and PKEY2005 defeated, we had now almost entirely defeated SPP's offline protections.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="closing-thoughts">Closing Thoughts<a href="https://hefung.github.io/mas/blog/tsforge#closing-thoughts" class="hash-link" aria-label="Direct link to Closing Thoughts" title="Direct link to Closing Thoughts" translate="no">​</a></h2>
<p>Even with the amount of damage we were able to do to SPP with a debugger and a hex editor, I still think it's a rather advanced and well-built DRM system. Its internals certainly improve upon those of Windows XP's DRM, which, despite whatever some might tell you, was rather poorly designed. It also manages to defend itself fairly well against the most common attacks, such as resetting evaluation timers by swapping physical store files. Additionally, there are still parts of SPP that we haven't managed to crack, like the signed XML licenses used to define its behavior. After all, it's not like there's another <a href="https://github.com/massgravel/MIIEow" target="_blank" rel="noopener noreferrer" class="">trivial patch</a> that can bypass their signature checks...</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/starterd-37d3ba0aaba92c59276f7b697fb5a2e2.png" width="1747" height="859" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="credits">Credits<a href="https://hefung.github.io/mas/blog/tsforge#credits" class="hash-link" aria-label="Direct link to Credits" title="Direct link to Credits" translate="no">​</a></h2>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="core-research-and-development">Core Research and Development<a href="https://hefung.github.io/mas/blog/tsforge#core-research-and-development" class="hash-link" aria-label="Direct link to Core Research and Development" title="Direct link to Core Research and Development" translate="no">​</a></h4>
<ul>
<li class="">WitherOrNot - Lead tool development, reverse engineering, testing</li>
<li class="">asdcorp - Initial demonstrations, reverse engineering, tool development, testing</li>
<li class="">abbodi1406 - Reverse engineering, development, testing</li>
<li class="">Lyssa - Reverse engineering, tool development, testing</li>
</ul>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="other-contributions">Other Contributions<a href="https://hefung.github.io/mas/blog/tsforge#other-contributions" class="hash-link" aria-label="Direct link to Other Contributions" title="Direct link to Other Contributions" translate="no">​</a></h4>
<ul>
<li class="">May - Code formatting, build setup</li>
</ul>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="special-thanks">Special Thanks<a href="https://hefung.github.io/mas/blog/tsforge#special-thanks" class="hash-link" aria-label="Direct link to Special Thanks" title="Direct link to Special Thanks" translate="no">​</a></h4>
<ul>
<li class="">BetaWiki - Documenting leaked beta builds used for reverse engineering</li>
<li class="">Rairii - Assistance with initial reverse engineering efforts</li>
<li class="">Microsoft - A fun challenge</li>
</ul>]]></content>
        <author>
            <name>WitherOrNot</name>
            <uri>https://github.com/WitherOrNot</uri>
        </author>
        <author>
            <name>asdcorp</name>
            <uri>https://github.com/asdcorp</uri>
        </author>
        <author>
            <name>abbodi1406</name>
            <uri>https://github.com/abbodi1406</uri>
        </author>
        <author>
            <name>Lyssa</name>
            <uri>https://github.com/thecatontheceiling</uri>
        </author>
        <category label="Windows" term="Windows"/>
        <category label="Activation" term="Activation"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Keyhole]]></title>
        <id>https://hefung.github.io/mas/blog/keyhole</id>
        <link href="https://hefung.github.io/mas/blog/keyhole"/>
        <updated>2024-09-06T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[By WitherOrNot]]></summary>
        <content type="html"><![CDATA[<p>By WitherOrNot<br>
<!-- -->Edited by May, Lyssa</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="introduction">Introduction<a href="https://hefung.github.io/mas/blog/keyhole#introduction" class="hash-link" aria-label="Direct link to Introduction" title="Direct link to Introduction" translate="no">​</a></h2>
<p>In our ongoing work to bypass Windows licensing checks, we occasionally stumble upon bugs that we choose to keep secret. This decision allows us to preserve potential future activation methods by avoiding bug fixes, while also giving us valuable tools for testing or developing new methods.</p>
<p>One such discovery, which we've named "Keyhole", turned out to be a highly effective DRM bypass. It gave users the ability to license any Microsoft Store app or any modern Windows edition with ease.</p>
<!-- -->
<p>Following the disclosure of <a href="https://msrc.microsoft.com/update-guide/vulnerability/CVE-2024-38184" target="_blank" rel="noopener noreferrer" class="">CVE-2024-38184</a> by <a href="https://talosintelligence.com/" target="_blank" rel="noopener noreferrer" class="">Cisco TALOS</a>, we have decided to share our findings on Keyhole, which we independently uncovered around the same time it was reported to Microsoft.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="clip">CLiP<a href="https://hefung.github.io/mas/blog/keyhole#clip" class="hash-link" aria-label="Direct link to CLiP" title="Direct link to CLiP" translate="no">​</a></h2>
<p>To understand this exploit, we must first understand CLiP, the Client Licensing Platform. This system was introduced with Windows 10, primarily as a way to implement DRM for Microsoft Store apps, and integrated with Windows activation, allowing users to buy digital licenses for Windows on the Microsoft Store.</p>
<p>CLiP is comprised of a few different main binaries within Windows:</p>
<ul>
<li class=""><code>clipup.exe</code> - Migrates (converts) Windows 8 store licenses, genuine tickets, and product keys to digital licenses</li>
<li class=""><code>clipsvc.dll</code> - User-mode service responsible for managing app licenses</li>
<li class=""><code>clipc.dll</code> - API used by applications to interact with CLiP</li>
<li class=""><code>clipwinrt.dll</code> - Similar to <code>clipc.dll</code> but for UWP applications utilizing <a href="https://learn.microsoft.com/en-us/windows/uwp/winrt-components" target="_blank" rel="noopener noreferrer" class="">Windows Runtime</a>.</li>
<li class=""><code>clipsp.sys</code> - Kernel-mode driver responsible for verifying licenses</li>
</ul>
<p><img decoding="async" loading="lazy" alt="clip diagram" src="https://hefung.github.io/mas/assets/images/clip_diagram-124f75d6f71b6c2618a27c1cff2b2e8d.png" width="681" height="341" class="img_ev3q"></p>
<p>Whenever a CLiP-licensed app is installed, a signed XML file containing the license information is sent to <code>clipsvc.dll</code>; once the XML signature is verified, the XML data is stored in ClipSVC's "token store" at <code>%PROGRAMDATA%\Microsoft\Windows\ClipSVC\tokens.dat</code>.</p>
<p>The signed license block is then extracted from the <code>SPLicenseBlock</code> tag and sent to <code>clipsp.sys</code> for verification. After verification, the license block is deposited in the CLiP license store at <code>HKLM\SYSTEM\CurrentControlSet\Control\{7746D80F-97E0-4E26-9543-26B41FC22F79}</code>. From there, <code>clipsp.sys</code> can then re-validate the license in the future if an app requests it using the CLiP API.</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</div><div class="admonitionContent_BuS1"><p>The CLiP license store mentioned earlier is protected so that you can't view it by default, but changing the permissions to allow yourself access is very easy.</p></div></div>
<p>As designed, this system forms a rather strong chain-of-trust that transmits only signed data from usermode applications all the way to the kernel, making it seemingly difficult to tamper with. As we will see soon, however, this is not at all the case.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-little-trolling">A Little Trolling<a href="https://hefung.github.io/mas/blog/keyhole#a-little-trolling" class="hash-link" aria-label="Direct link to A Little Trolling" title="Direct link to A Little Trolling" translate="no">​</a></h2>
<p>So far, one binary failed to receive any mention: <code>clipup.exe</code>. This is because it isn't notable when talking about Keyhole itself. However, it holds the key to messing with CLiP:</p>
<p><img decoding="async" loading="lazy" alt="ecc key" src="https://hefung.github.io/mas/assets/images/ecc_key-43ee857b98419c2aac40f465586832e7.png" width="1566" height="411" class="img_ev3q"></p>
<p>Yes, literally. A valid ECDSA key to sign XML licenses is stored in unobfuscated form, allowing anyone to very easily sign or resign XML licenses. This key is normally meant to sign temporary licenses sent to the Microsoft store to get digital licenses, but ClipSvc will happily accept it for app licenses as well.</p>
<p>This allows us to bypass ClipSvc's gatekeeping and effectively send any license blocks we want straight to ClipSp. With this, we entirely bypass the usermode level of the chain-of-trust, and now all that's left is to try and trick ClipSp.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="unpacking-clipsp">Unpacking ClipSp<a href="https://hefung.github.io/mas/blog/keyhole#unpacking-clipsp" class="hash-link" aria-label="Direct link to Unpacking ClipSp" title="Direct link to Unpacking ClipSp" translate="no">​</a></h2>
<p>ClipSp, from our analysis, is not a very well-written driver. It's full of copy-pasted code (from where will be shown soon), and seems to be rife with odd choices and compromises. In other words, it's a perfect environment for someone looking for a bypass. There's only one big issue: most of the interesting driver code is hidden using Microsoft's proprietary obfuscator, known as Warbird. In order to find and understand it, we need to "unpack" it, a.k.a. undoing the obfuscation. Luckily, this is rather straightforward thanks to some symbols for <code>clipsp.sys</code> that were available on Microsoft's servers.</p>
<p>Similar to how Warbird works <a href="https://github.com/WitherOrNot/warbird-docs" target="_blank" rel="noopener noreferrer" class="">in user-mode programs</a>, ClipSp wraps any calls to obfuscated code with an decryption and encryption function, as shown below:</p>
<p><img decoding="async" loading="lazy" alt="feistel wrapper" src="https://hefung.github.io/mas/assets/images/encrypt_decrypt-941f0cf561633d143866fd7d085b6405.png" width="1258" height="564" class="img_ev3q"></p>
<p>So, if we can manually run these decryption functions, we could access all of the hidden code. Luckily, this is quite simple to do based on a method <a href="https://github.com/KiFilterFiberContext/windows-software-policy" target="_blank" rel="noopener noreferrer" class="">by KiFilterFiberContext</a>, and with it, we are now able to finally find some bugs.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="license-blocks">License Blocks<a href="https://hefung.github.io/mas/blog/keyhole#license-blocks" class="hash-link" aria-label="Direct link to License Blocks" title="Direct link to License Blocks" translate="no">​</a></h2>
<p>License blocks, mentioned previously, are what actually hold the important license information in CLiP. Their format is <a href="https://github.com/LukeFZ/CikExtractor" target="_blank" rel="noopener noreferrer" class="">well-documented</a> and can store many kinds of data, so we figured they were a good place to start looking for bugs.</p>
<p>License blocks hold their data in a tag-length-value (TLV) format, where several smaller blocks are stored together with each holding values for their data type, the length of their data, and the data itself. For example, the TLV block highlighted below has a type of <code>0xC9</code> (License Information), a length of <code>0xA</code> (10 bytes), and 10 bytes of data.</p>
<p><img decoding="async" loading="lazy" alt="splicenseblock tlv" src="data:image/png;base64,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" width="566" height="71" class="img_ev3q"></p>
<p>At the very end of a license block, there will always be a signature block, with a type of <code>0xCC</code>. This block holds the signature of all the data before it, as well as indicating which key it was signed with. And of course, since it sits after all the data being signed, there's no way to alter any of it... right?</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-lot-of-trolling">A Lot of Trolling<a href="https://hefung.github.io/mas/blog/keyhole#a-lot-of-trolling" class="hash-link" aria-label="Direct link to A Lot of Trolling" title="Direct link to A Lot of Trolling" translate="no">​</a></h2>
<p>In the middle of experimenting with this data format, one of our members, May, had a very simple question. If the signature block signs all the data before it, what happens to the data put after it?</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://hefung.github.io/mas/assets/images/test_keyhole-2019266318a4eb86944de946d449f117.png" width="559" height="444" class="img_ev3q"></p>
<p>Above, you can see a license block for Minecraft Bedrock edition with some new data placed after it (highlighted), containing blocks copied from a Windows license. What happens if we try to install such a license?</p>
<p><img decoding="async" loading="lazy" alt="enterprise ltsc digital activation" src="https://hefung.github.io/mas/assets/images/enterprises_diglic-f46c1d1c87abacc52449035c1d5b6bce.png" width="618" height="327" class="img_ev3q"></p>
<p>As it turns out, data after the signature block isnt checked at all... and it can even override data that came before it. Whenever two blocks of the same type are stored together, the last one overrides all the others before it. So, if we want to change any license data, we can just make a block for it and put it after the signature block!</p>
<p>This method lets us make licenses for anything sold on the Microsoft Store, including Windows, from any other Microsoft Store license. And since there are so many free apps with licenses, we now had the ability to make as many as we wanted for whatever we wanted. This bug essentially punched a hole straight through CLiP's DRM, so we decided to name it "Keyhole".</p>
<p>There is only one catch: licenses that are bound to a specific device, known as "device-locked" licenses, cannot be made from device-unlocked licenses. Since Windows digital licenses are device-locked, this meant that we needed to make them from device-locked app licenses. Luckily, many apps, including games like Roblox fit this criteria.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="trolling-tutorial">Trolling Tutorial<a href="https://hefung.github.io/mas/blog/keyhole#trolling-tutorial" class="hash-link" aria-label="Direct link to Trolling Tutorial" title="Direct link to Trolling Tutorial" translate="no">​</a></h2>
<p>The steps to make any Windows license you want were now dead simple. First, install an app with a device-locked license, like Roblox.</p>
<p><img decoding="async" loading="lazy" alt="roblox" src="https://hefung.github.io/mas/assets/images/step1-d16bf8fd25e622e3c3f7c9d98b3ba682.png" width="1223" height="649" class="img_ev3q"></p>
<p>Then, using a HTTPS traffic capture tool like Fiddler, intercept the license that comes from <code>https://licensing.mp.microsoft.com/v7.0/licenses/content</code>.</p>
<p><img decoding="async" loading="lazy" alt="fiddler" src="https://hefung.github.io/mas/assets/images/step2-fdf396c3c0528430845848d9fec4c090.png" width="1473" height="745" class="img_ev3q"></p>
<p>Decode the license, then extract its license block.</p>
<p><img decoding="async" loading="lazy" alt="license block" src="https://hefung.github.io/mas/assets/images/step3-4bb684ab245e953171794fc3f802e376.png" width="567" height="278" class="img_ev3q"></p>
<p>Now, add whatever new data you need to make a new license.</p>
<p><img decoding="async" loading="lazy" alt="add keyholed data" src="https://hefung.github.io/mas/assets/images/step4-8dac9be000691c68505b8960c3efbb09.png" width="551" height="412" class="img_ev3q"></p>
<p>Then, we just package our license block into a new XML file, sign the XML, and copy it into the folder <code>C:\ProgramData\Microsoft\Windows\ClipSVC\Install\Migration</code>.</p>
<p><img decoding="async" loading="lazy" alt="license in migration folder" src="https://hefung.github.io/mas/assets/images/step5-bba94dcf5da9eb06aacb8b9e4c193903.png" width="1072" height="231" class="img_ev3q"></p>
<p>Finally, we get ClipSvc to install our license, either by restarting it, or with the command <code>clipup -p</code>.</p>
<p><img decoding="async" loading="lazy" alt="clipup -p in cmd" src="https://hefung.github.io/mas/assets/images/step6-163e0d042791c144913521165dbdfe1b.png" width="668" height="481" class="img_ev3q"></p>
<p>When we check our activation status, Windows is now permanently activated.</p>
<p><img decoding="async" loading="lazy" alt="server is digitally licensed" src="https://hefung.github.io/mas/assets/images/step7-1e7033378f98b575327e12a8b1d02f63.png" width="581" height="336" class="img_ev3q"></p>
<p>With this, we were able to do things that were previously impossible, like activating Enterprise LTSC with a digital license, or even activating a legitimate KMS server with a generic key:</p>
<p><img decoding="async" loading="lazy" alt="26100 kms server" src="https://hefung.github.io/mas/assets/images/trivial-f8da32c89ba8c4cbc9e0e8f84a31d851.png" width="404" height="632" class="img_ev3q"></p>
<p>From here, it's pretty easy to see that this simple bug completely annihilates CLiP's DRM system.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="buzzkill">Buzzkill<a href="https://hefung.github.io/mas/blog/keyhole#buzzkill" class="hash-link" aria-label="Direct link to Buzzkill" title="Direct link to Buzzkill" translate="no">​</a></h2>
<p>Having found this bug, we were quite happy that CLiP was now effectively dead. This happiness didn't last very long, though, as we recently found a <a href="https://talosintelligence.com/vulnerability_reports/TALOS-2024-1964" target="_blank" rel="noopener noreferrer" class="">vulnerability report</a> from Cisco TALOS that reported this exact bug. It was reported to Microsoft on April 8, right around when we first found it.</p>
<p><img decoding="async" loading="lazy" alt="keyhole discovery" src="https://hefung.github.io/mas/assets/images/kh_discovery-0289794fa2639c8751f8df4470f2102f.png" width="732" height="653" class="img_ev3q"></p>
<p>This raises a question though: why was a DRM bug reported as a security vulnerability? At first, CLiP licenses don't seem to have anything to do with exploitation, which caused us to think the bug had been reported for no reason other than to fix Microsoft's DRM. However, Keyhole can be used as an entry point for <a href="https://talosintelligence.com/vulnerability_reports/TALOS-2024-1988" target="_blank" rel="noopener noreferrer" class="">more serious bugs in ClipSp</a>, which prompted TALOS to make it part of their disclosure.</p>
<p>As for the fix itself, it's rather straightforward. As shown below, the current license block parser code immediately exits after encountering a signature block. This prevents it from processing blocks after the signature, completely patching Keyhole.</p>
<p><img decoding="async" loading="lazy" alt="keyhole fix" src="https://hefung.github.io/mas/assets/images/keyhole_fix-699ab276d7662fda612e611e42894193.png" width="832" height="589" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="giving-season">Giving Season<a href="https://hefung.github.io/mas/blog/keyhole#giving-season" class="hash-link" aria-label="Direct link to Giving Season" title="Direct link to Giving Season" translate="no">​</a></h2>
<p>After mourning the loss of our beloved exploit, we decided that it would only be fair to publicize our own discoveries on CLiP. So, we've released the code to <a href="https://github.com/massgravel/keyhole" target="_blank" rel="noopener noreferrer" class="">generate Keyhole licenses</a> and our <a href="https://archive.org/details/clipwinrt" target="_blank" rel="noopener noreferrer" class="">collection of CLiP binaries</a> with symbols for easier analysis. We invite you to go forth and discover more funny things in CLiP! (and <a href="https://massgrave.dev/contactus" target="_blank" rel="noopener noreferrer" class="">report them to us</a> instead of MS)</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="and-now-for-something-different">And now, for something different<a href="https://hefung.github.io/mas/blog/keyhole#and-now-for-something-different" class="hash-link" aria-label="Direct link to And now, for something different" title="Direct link to And now, for something different" translate="no">​</a></h2>
<p>I mentioned that ClipSp's buggy code was copy-pasted, but from where? Well, the "SP" part just happens to reference a certain Microsoft game console: the Xbox One!</p>
<p>The Xbox One contains a chip known as the SP, or "secure processor", based on the TPMs in modern PCs. The main job of the SP is to enforce code signing, but it also handles license verification. During our research on Keyhole, we found many associations between CLiP and the Xbox One, and began wondering how they were actually related. While looking through some leaked source code, we stumbled upon this:</p>
<p><img decoding="async" loading="lazy" alt="ValidateLicensePolicy" src="https://hefung.github.io/mas/assets/images/validatelicensepolicy-ae997c165c03ff0203bf19f92f98a85b.png" width="747" height="447" class="img_ev3q"></p>
<p>Well, this looks oddly familiar...</p>
<p><img decoding="async" loading="lazy" alt="keyhole bug in source code" src="https://hefung.github.io/mas/assets/images/src_bug-0e67719f2983a3bbd97d72a0feaf2e26.png" width="893" height="882" class="img_ev3q"></p>
<p>And there's the same bug that's in CLiP, but in Xbox code. In fact, we weren't too surprised to find this, as we found that almost all of CLiP, from the XML format of the licenses to the TLV-based license blocks, is mostly copy-pasted straight from the Xbox One's DRM system.</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</div><div class="admonitionContent_BuS1"><p>While the Xbox SP contains the same parsing bug as in ClipSp, it parses data blocks and signature-related blocks separately. As a result, Keyhole will not work on the Xbox.</p></div></div>
<p>So, to those with a console that's been <a href="https://github.com/exploits-forsale/collateral-damage" target="_blank" rel="noopener noreferrer" class="">collaterally damaged</a>, I wonder what happens if you mess with those funny-looking XML files in <code>S:\clip</code> ;)</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="credits">Credits<a href="https://hefung.github.io/mas/blog/keyhole#credits" class="hash-link" aria-label="Direct link to Credits" title="Direct link to Credits" translate="no">​</a></h2>
<p>The research covered in this blogpost was made possible by the following people/groups:</p>
<ul>
<li class="">May - Initial discovery, testing, reverse engineering</li>
<li class="">asdcorp - Testing, reverse engineering</li>
<li class="">WitherOrNot - Tool development, testing, reverse engineering, bugfix analysis</li>
<li class="">emoose, LukeFZ - License Block format documentation</li>
<li class="">KiFilterFiberContext - ClipSp unpacking</li>
<li class="">Phillippe Laulheret, Cisco TALOS - Inspiring this publication, clearing up misconceptions</li>
<li class="">Rairii - Additional info</li>
</ul>]]></content>
        <author>
            <name>WitherOrNot</name>
            <uri>https://github.com/WitherOrNot</uri>
        </author>
        <author>
            <name>May</name>
            <uri>https://github.com/ave9858</uri>
        </author>
        <category label="Windows" term="Windows"/>
        <category label="Activation" term="Activation"/>
    </entry>
</feed>