Fwd: Donation of source code for testing Windows CSP / KSP vs real implementation

Frank Morgner <[email protected]> Wed, 3 Jun 2026 11:59:37 +0200
Newsgroups gmane.comp.encryption.opensc.devel
Message-ID <[email protected]>
This is a multi-part message in MIME format.
--------------CMpnZc4mniyJIb4rYpAh0MRF
Content-Type: multipart/alternative;
 boundary="------------sDCQ0TKKR5Rkkuu0Tz4q9ivA"

--------------sDCQ0TKKR5Rkkuu0Tz4q9ivA
Content-Type: text/plain; charset=UTF-8; format=flowed
Content-Transfer-Encoding: 8bit

Hi all!

please find below a message from our friend Vincent Le Toux, who has 
difficulties accessing the mailing list.

Best regards,
Frank.

------------------------------------------------------------------------
*Von:* Vincent Le Toux <[email protected]>
*Gesendet:* Freitag, 15. Mai 2026 15:43
*Betreff:* Fwd: Donation of source code for testing Windows CSP / KSP vs 
real implementation
Hi,

Maybe you can forward to the opensc mailing as I'm not allowed (anymore 
?) to post on it.
(still receiving the messages)

br
Vincent LE TOUX

---------- Forwarded message ---------
De : *Vincent Le Toux* <[email protected]>
Date: ven. 15 mai 2026 à 15:40
Subject: Donation of source code for testing Windows CSP / KSP vs real 
implementation
To: OpenSC-devel <[email protected]>


Hello,

Testing an implementation of a smart card in a windows environment is 
quite difficult.
Note: Franck has shared in the past an implementation of java cards 
applet using jcardsim.

While I'm slowly recovering from some health issues, I've tested one of 
my GIDS implementations with the windows KSP / CSP.
I'm calling in my unit tests the Windows Base CSP & KSP which then calls 
my hooked implementation of SCard* functions which I redirect them to my 
implementation.
This is far easier than copy-pasting the APDU in unit tests.

I don't think OpenSC can directly benefit from it, but you never know.

What I'm sharing:
- how to hook and unhook SCard function in the Windows Base CSP / KSP 
and minidriver
(import table and delay library)
- the SCard Implementation (Initially written by IA but tested by me 
manually)
And also ...
How to provide the PIN at container creation without a dialog
(you can pass the PIN to sign / decrypt but not for container creation)
=> I'm hooking CredUIPromptForCredentials to provide my hardcoded PIN
No IA knows how to do that properly.

Maybe this can be adapted to test the OpenSC minidriver out of the usual 
cmck.exe test program. Just replace the dll name 
in HookGetCardTypeProviderNameW for that purpose and map SCardTransmit 
to a smart card implementation.

Also I noted a pain point during my unit tests: set a different cardid 
for each test to avoid any cache issues.

Hope that'll help someone in the future.

br
-- 
---
Vincent
--------------sDCQ0TKKR5Rkkuu0Tz4q9ivA
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: 8bit

<!DOCTYPE html>
<html>
  <head>

    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  </head>
  <body dir="ltr">
    <div class="moz-forward-container">Hi all!</div>
    <div class="moz-forward-container"><br>
    </div>
    <div class="moz-forward-container">please find below a message from
      our friend Vincent Le Toux, who has difficulties accessing the
      mailing list.</div>
    <div class="moz-forward-container"><br>
    </div>
    <div class="moz-forward-container">Best regards,</div>
    <div class="moz-forward-container">Frank.<br>
      <style type="text/css" style="display:none;">P {margin-top:0;margin-bottom:0;}</style>
      <div
style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);"
        class="elementToProof">
        <br>
      </div>
      <hr style="display:inline-block;width:98%" tabindex="-1">
      <div id="divRplyFwdMsg" dir="ltr"><font face="Calibri, sans-serif"
          style="font-size:11pt" color="#000000"><b>Von:</b> Vincent Le
          Toux <a class="moz-txt-link-rfc2396E" href="mailto:[email protected]">&lt;[email protected]&gt;</a><br>
          <b>Gesendet:</b> Freitag, 15. Mai 2026 15:43<br>
          <b>Betreff:</b> Fwd: Donation of source code for testing
          Windows CSP / KSP vs real implementation</font>
        <div> </div>
      </div>
      <div>
        <div dir="ltr">
          <div>Hi,</div>
          <div><br>
          </div>
          <div>Maybe you can forward to the opensc mailing as I'm not
            allowed (anymore ?) to post on it.</div>
          <div>(still receiving the messages)</div>
          <div><br>
          </div>
          <div>br</div>
          <div>Vincent LE TOUX</div>
          <div dir="ltr"><br>
            <div class="x_gmail_quote">
              <div dir="ltr" class="x_gmail_attr">---------- Forwarded
                message ---------<br>
                De : <b class="x_gmail_sendername" dir="auto">Vincent
                  Le Toux</b> <span dir="auto">
                  &lt;<a href="mailto:[email protected]"
                    target="_blank" moz-do-not-send="true"
                    class="moz-txt-link-freetext">[email protected]</a>&gt;</span><br>
                Date: ven. 15 mai 2026 à 15:40<br>
                Subject: Donation of source code for testing Windows CSP
                / KSP vs real implementation<br>
                To: OpenSC-devel &lt;<a
                  href="mailto:[email protected]"
                  target="_blank" moz-do-not-send="true"
                  class="moz-txt-link-freetext">[email protected]</a>&gt;<br>
              </div>
              <br>
              <br>
              <div dir="ltr">
                <div>Hello,</div>
                <div><br>
                </div>
                <div>Testing an implementation of a smart card in a
                  windows environment is quite difficult.</div>
                <div>Note: Franck has shared in the past an
                  implementation of java cards applet using jcardsim.</div>
                <div><br>
                </div>
                <div>While I'm slowly recovering from some health
                  issues, I've tested one of my GIDS implementations
                  with the windows KSP / CSP.</div>
                <div>I'm calling in my unit tests the Windows Base CSP
                  &amp; KSP which then calls my hooked implementation of
                  SCard* functions which I redirect them to my
                  implementation.</div>
                <div>This is far easier than copy-pasting the APDU in
                  unit tests.</div>
                <div><br>
                </div>
                <div>I don't think OpenSC can directly benefit from it,
                  but you never know.</div>
                <div><br>
                </div>
                <div>What I'm sharing:</div>
                <div>- how to hook and unhook SCard function in the
                  Windows Base CSP / KSP and minidriver</div>
                <div>(import table and delay library)</div>
                <div>- the SCard Implementation (Initially written by IA
                  but tested by me manually)</div>
                <div>And also ...</div>
                <div>How to provide the PIN at container creation
                  without a dialog</div>
                <div>(you can pass the PIN to sign / decrypt but not for
                  container creation)</div>
                <div>=&gt; I'm hooking CredUIPromptForCredentials to
                  provide my hardcoded PIN</div>
                <div>No IA knows how to do that properly.</div>
                <div><br>
                </div>
                <div>Maybe this can be adapted to test the OpenSC
                  minidriver out of the usual cmck.exe test program.
                  Just replace the dll name
                  in HookGetCardTypeProviderNameW for that purpose and
                  map SCardTransmit to a smart card implementation.</div>
                <div><br>
                </div>
                <div>Also I noted a pain point during my unit tests: set
                  a different cardid for each test to avoid any cache
                  issues.</div>
                <div><br>
                </div>
                <div>Hope that'll help someone in the future.</div>
                <div><br>
                </div>
                br<br>
                <div><span class="x_gmail_signature_prefix">-- </span><br>
                  <div dir="ltr" class="x_gmail_signature"
                    data-smartmail="gmail_signature">---<br>
                    Vincent</div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </body>
</html>

--------------sDCQ0TKKR5Rkkuu0Tz4q9ivA--
--------------CMpnZc4mniyJIb4rYpAh0MRF
Content-Type: text/x-c-code; charset=windows-1252; name="KSPTests.cpp"
Content-Disposition: attachment; filename="KSPTests.cpp"
Content-Transfer-Encoding: 8bit

#include "pch.h"
#include <ncrypt.h>
#include <CppUnitTest.h>
#include <vector>
#include <algorithm>
#include "MsCryptoBase.h"

#pragma comment(lib, "ncrypt")

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

// Configuration des tests
#define TEST_PIN L"12345678"
#ifndef SCARD_PROVIDER_CARD_MODULE
#define SCARD_PROVIDER_CARD_MODULE 0x80000001
#endif

namespace GidsTests
{
	TEST_CLASS(KspTests), public MsCryptoBase
	{
		TEST_METHOD_INITIALIZE(SetUp) {
			Trace(TRACE_LEVEL_INFO, L"--- [SetUp] setup KSP tests ---");
			TraceOff();
			setUp();
			createcard();
			setUpHook();
			TraceOn();
		}

		TEST_METHOD_CLEANUP(TearDown) {
			Trace(TRACE_LEVEL_INFO, L"--- [TearDown] Cleaning KSP tests ---");
			TraceOff();
			tearDownHook();
			tearDown();
			TraceOn();
		}

	public:

		// 1. ENUMERATE EMPTY CARD
		TEST_METHOD(Test_KSP_1_EmptyEnumerate) {
			Trace(TRACE_LEVEL_INFO, L"[Test] 1. Enumerate Keys");
			NCRYPT_PROV_HANDLE hProv = NULL;
			SECURITY_STATUS status = NCryptOpenStorageProvider(&hProv, MS_SMART_CARD_KEY_STORAGE_PROVIDER, 0);

			if (status != ERROR_SUCCESS)
			{
				Trace(TRACE_LEVEL_ERROR, L"   [!] NCryptOpenStorageProvider failed: 0x%08X", status);
				Assert::Fail(L"Échec ouverture Provider.");
			}

			NCryptKeyName* pKey = NULL;
			PVOID pState = NULL;

			Trace(TRACE_LEVEL_INFO, L"   [Info] Début énumération...");
			while ((status = NCryptEnumKeys(hProv, NULL, &pKey, &pState, 0)) == ERROR_SUCCESS) 
			{
				Trace(TRACE_LEVEL_INFO, L"   [Key Found] %s", pKey->pszName);
				NCryptFreeBuffer(pKey);
			}

			if (status != (SECURITY_STATUS)NTE_NO_MORE_ITEMS) {
				Trace(TRACE_LEVEL_ERROR, L"   [!] NCryptEnumKeys erreur inattendue: 0x%08X", status);
				if (pState) NCryptFreeBuffer(pState);
				NCryptFreeObject(hProv);
				Assert::Fail(L"Échec durant l'énumération.");
			}

			Trace(TRACE_LEVEL_INFO, L"   [OK] Fin d'énumération nominale.");
			if (pState) NCryptFreeBuffer(pState);
			NCryptFreeObject(hProv);
		}

		// 2. CAPABILITIES (DYNAMIC LENGTHS)
		TEST_METHOD(Test_KSP_2_Capabilities) {
			Trace(TRACE_LEVEL_INFO, L"[Test] 2. KSP Capabilities");
			NCRYPT_PROV_HANDLE hProv = NULL;
			NCRYPT_KEY_HANDLE hTempKey = 0;
			SECURITY_STATUS status = NCryptOpenStorageProvider(&hProv, MS_SMART_CARD_KEY_STORAGE_PROVIDER, 0);
			if (status != ERROR_SUCCESS) {
				Trace(TRACE_LEVEL_ERROR, L"   [!] Open failed: 0x%08X", status);
				Assert::AreEqual(ERROR_SUCCESS, status);
			}

			// not persisted !
			status = NCryptCreatePersistedKey(hProv, &hTempKey, NCRYPT_RSA_ALGORITHM, NULL, 0, 0);
			if (status != ERROR_SUCCESS) {
				Trace(TRACE_LEVEL_ERROR, L"   [!] NCryptCreatePersistedKey failed: 0x%08X", status);
				Assert::AreEqual(ERROR_SUCCESS, status);
			}


			NCRYPT_SUPPORTED_LENGTHS len;
			DWORD cb;
			status = NCryptGetProperty(hTempKey, NCRYPT_LENGTHS_PROPERTY, (BYTE*)&len, sizeof(len), &cb, 0);

			if (status != ERROR_SUCCESS) {
				Trace(TRACE_LEVEL_ERROR, L"   [!] GetProperty(LENGTHS) failed: 0x%08X", status);
				NCryptFreeObject(hTempKey);
				NCryptFreeObject(hProv);
				Assert::AreEqual(ERROR_SUCCESS, status);
			}

			Trace(TRACE_LEVEL_INFO, L"   [Caps] RSA: Min=%d, Max=%d, Step=%d", len.dwMinLength, len.dwMaxLength, len.dwIncrement);
			NCryptFreeObject(hTempKey);
			NCryptFreeObject(hProv);
		}

		// 3. RSA MATRIX (All Detected Sizes)
		TEST_METHOD(Test_KSP_3_RSAMatrix) {
			Trace(TRACE_LEVEL_INFO, L"[Test] 3. RSA Matrix");
			NCRYPT_PROV_HANDLE hProv = NULL;
			NCRYPT_KEY_HANDLE hTempKey = 0;

			SECURITY_STATUS status = NCryptOpenStorageProvider(&hProv, MS_SMART_CARD_KEY_STORAGE_PROVIDER, 0);
			if (status != ERROR_SUCCESS) {
				Trace(TRACE_LEVEL_ERROR, L"   [!] Open failed: 0x%08X", status);
				Assert::AreEqual(ERROR_SUCCESS, status);
			}


			// not persisted !
			status = NCryptCreatePersistedKey(hProv, &hTempKey, NCRYPT_RSA_ALGORITHM, NULL, 0, 0);
			if (status != ERROR_SUCCESS) {
				Trace(TRACE_LEVEL_ERROR, L"   [!] NCryptCreatePersistedKey failed: 0x%08X", status);
				Assert::AreEqual(ERROR_SUCCESS, status);
			}

			NCRYPT_SUPPORTED_LENGTHS len;
			DWORD cb;
			status = NCryptGetProperty(hTempKey, NCRYPT_LENGTHS_PROPERTY, (BYTE*)&len, sizeof(len), &cb, 0);
			if (status != ERROR_SUCCESS) {
				Trace(TRACE_LEVEL_ERROR, L"   [!] GetProperty(LENGTHS) failed: 0x%08X", status);
				NCryptFreeObject(hTempKey);
				NCryptFreeObject(hProv);
				Assert::AreEqual(ERROR_SUCCESS, status);
			}
			NCryptFreeObject(hTempKey);

			for (DWORD dwSize = 1024; dwSize <= 2048; dwSize += 1024) {
				NCRYPT_KEY_HANDLE hKey = NULL;
				Trace(TRACE_LEVEL_INFO, L"   [Action] Création clé RSA %d bits", dwSize);

				status = NCryptCreatePersistedKey(hProv, &hKey, BCRYPT_RSA_ALGORITHM, L"KspMatrix", 0, 0);
				if (status != ERROR_SUCCESS) {
					Trace(TRACE_LEVEL_ERROR, L"   [!] Create failed: 0x%08X", status);
					Assert::AreEqual(ERROR_SUCCESS, status);
				}

				DWORD dwKeySize = dwSize;
				status = NCryptSetProperty(hKey, NCRYPT_LENGTH_PROPERTY,
					(BYTE*)&dwKeySize, sizeof(dwKeySize), 0);
				if (status != ERROR_SUCCESS) {
					Trace(TRACE_LEVEL_ERROR, L"   [!] KeySize set failed: 0x%08X", status);
					Assert::AreEqual(ERROR_SUCCESS, status);
				}

				Trace(TRACE_LEVEL_INFO, L"   [Action] Injection PIN...");
				status = NCryptSetProperty(hKey, NCRYPT_PIN_PROPERTY, (BYTE*)TEST_PIN, (DWORD)(wcslen(TEST_PIN) + 1) * sizeof(wchar_t), 0);
				if (status != ERROR_SUCCESS) {
					Trace(TRACE_LEVEL_ERROR, L"   [!] PIN set failed: 0x%08X", status);
					Assert::AreEqual(ERROR_SUCCESS, status);
				}

				status = NCryptFinalizeKey(hKey, 0);
				if (status != ERROR_SUCCESS) {
					Trace(TRACE_LEVEL_ERROR, L"   [!] Finalize failed (%d bits): 0x%08X", dwSize, status);
					NCryptFreeObject(hKey);
					Assert::AreEqual(ERROR_SUCCESS, status);
				}

				Trace(TRACE_LEVEL_INFO, L"   [Action] Suppression clé %d bits", dwSize);
				status = NCryptDeleteKey(hKey, 0);
				if (status != ERROR_SUCCESS)
				{
					Trace(TRACE_LEVEL_ERROR, L"   [!] Delete failed: 0x%08X", status);
					Assert::AreEqual(ERROR_SUCCESS, status);
				}
			}
			NCryptFreeObject(hProv);
		}



		// 4. SIGNATURE MATRIX (All Hash Algos + PSS/PKCS1)
		TEST_METHOD(Test_KSP_4_SignatureMatrix) {
			Trace(TRACE_LEVEL_INFO, L"[Test] 4. Signature Matrix");
			struct HashInfo {
				const wchar_t* pszAlg;
				DWORD          cbHash;
			};

			static const HashInfo hashes[] = {
				{ BCRYPT_SHA1_ALGORITHM,   20 },
				{ BCRYPT_SHA256_ALGORITHM, 32 },
				{ BCRYPT_SHA384_ALGORITHM, 48 },
				{ BCRYPT_SHA512_ALGORITHM, 64 },
			};


			NCRYPT_PROV_HANDLE hP = NULL;
			NCRYPT_KEY_HANDLE hK = NULL;
			SECURITY_STATUS status;

			status = NCryptOpenStorageProvider(&hP, MS_SMART_CARD_KEY_STORAGE_PROVIDER, 0);
			if (status != ERROR_SUCCESS) { Assert::AreEqual(ERROR_SUCCESS, status); }

			status = NCryptCreatePersistedKey(hP, &hK, BCRYPT_RSA_ALGORITHM, L"KspSignMatrix", 0, 0);
			if (status != ERROR_SUCCESS) {
				Trace(TRACE_LEVEL_ERROR, L"   [!] Create failed: 0x%08X", status);
				Assert::AreEqual(ERROR_SUCCESS, status);
			}

			DWORD dwKeySize = 2048;
			status = NCryptSetProperty(hK, NCRYPT_LENGTH_PROPERTY,
				(BYTE*)&dwKeySize, sizeof(dwKeySize), 0);
			if (status != ERROR_SUCCESS) {
				Trace(TRACE_LEVEL_ERROR, L"   [!] KeySize set failed: 0x%08X", status);
				Assert::AreEqual(ERROR_SUCCESS, status);
			}

			Trace(TRACE_LEVEL_INFO, L"   [Action] Injection PIN...");
			status = NCryptSetProperty(hK, NCRYPT_PIN_PROPERTY, (BYTE*)TEST_PIN, (DWORD)(wcslen(TEST_PIN) + 1) * sizeof(wchar_t), 0);
			if (status != ERROR_SUCCESS) {
				Trace(TRACE_LEVEL_ERROR, L"   [!] PIN set failed: 0x%08X", status);
				Assert::AreEqual(ERROR_SUCCESS, status);
			}

			status = NCryptFinalizeKey(hK, 0);
			if (status != ERROR_SUCCESS)
			{
				Trace(TRACE_LEVEL_ERROR, L"   [!] Finalize failed: 0x%08X", status);
				Assert::AreEqual(ERROR_SUCCESS, status);
			}

			for (auto& h : hashes) {
				BYTE hash[64] = {};
				memset(hash, 0x01, h.cbHash); // fake hash de la bonne taille
				BYTE sig[512] = {};
				DWORD cbSig = 0;

				Trace(TRACE_LEVEL_INFO, L"   [Sign] Hash: %s (%d bytes)", h.pszAlg, h.cbHash);

				// PKCS1
				BCRYPT_PKCS1_PADDING_INFO pkcs1 = { h.pszAlg };
				status = NCryptSignHash(hK, &pkcs1, hash, h.cbHash, sig, sizeof(sig), &cbSig, NCRYPT_PAD_PKCS1_FLAG);
				if (status != ERROR_SUCCESS) {
					Trace(TRACE_LEVEL_ERROR, L"   [!] PKCS1 %s failed: 0x%08X", h.pszAlg, status);
					Assert::AreEqual(ERROR_SUCCESS, status);
				}
				status = NCryptVerifySignature(hK, &pkcs1, hash, h.cbHash, sig, cbSig, NCRYPT_PAD_PKCS1_FLAG);
				if (status != ERROR_SUCCESS) {
					Trace(TRACE_LEVEL_ERROR, L"   [!] PKCS1 %s failed: 0x%08X", h.pszAlg, status);
					Assert::AreEqual(ERROR_SUCCESS, status);
				}
				Trace(TRACE_LEVEL_INFO, L"   [OK] PKCS1 sig: %d bytes", cbSig);

				/*BCRYPT_OAEP_PADDING_INFO oaep = { h.pszAlg, NULL, NULL };
				status = NCryptSignHash(hK, &oaep, hash, h.cbHash, sig, sizeof(sig), &cbSig, NCRYPT_PAD_OAEP_FLAG);
				if (status != ERROR_SUCCESS) {
					Trace(TRACE_LEVEL_ERROR, L"   [!] OEAP %s failed: 0x%08X", h.pszAlg, status);
					Assert::AreEqual(ERROR_SUCCESS, status);
				}
				status = NCryptVerifySignature(hK, &oaep, hash, h.cbHash, sig, cbSig, NCRYPT_PAD_OAEP_FLAG);
				if (status != ERROR_SUCCESS) {
					Trace(TRACE_LEVEL_ERROR, L"   [!] OEAP %s failed: 0x%08X", h.pszAlg, status);
					Assert::AreEqual(ERROR_SUCCESS, status);
				}
				Trace(TRACE_LEVEL_INFO, L"   [OK] OEAP sig: %d bytes", cbSig);*/
			}

			NCryptDeleteKey(hK, 0);
			NCryptFreeObject(hP);
		}

		//// 5. MODE RAW (NCRYPT_NO_PADDING_FLAG)
		//TEST_METHOD(Test_KSP_5_RawSign) {
		//	Trace(TRACE_LEVEL_INFO, L"[Test] 5. RAW Sign");
		//	NCRYPT_PROV_HANDLE hP = NULL; NCRYPT_KEY_HANDLE hK = NULL;

		//	SECURITY_STATUS status = NCryptOpenStorageProvider(&hP, MS_SMART_CARD_KEY_STORAGE_PROVIDER, 0);
		//	if (status != ERROR_SUCCESS) {
		//		Trace(TRACE_LEVEL_ERROR, L"   [!] Open failed: 0x%08X", status);
		//		Assert::AreEqual(ERROR_SUCCESS, status);
		//	}

		//	status = NCryptCreatePersistedKey(hP, &hK, BCRYPT_RSA_ALGORITHM, L"KspRaw", 0, 0);
		//	if (status != ERROR_SUCCESS) {
		//		Trace(TRACE_LEVEL_ERROR, L"   [!] Create failed: 0x%08X", status);
		//		Assert::AreEqual(ERROR_SUCCESS, status);
		//	}

		//	DWORD dwKeySize = 2048;
		//	status = NCryptSetProperty(hK, NCRYPT_LENGTH_PROPERTY,
		//		(BYTE*)&dwKeySize, sizeof(dwKeySize), 0);
		//	if (status != ERROR_SUCCESS) {
		//		Trace(TRACE_LEVEL_ERROR, L"   [!] KeySize set failed: 0x%08X", status);
		//		Assert::AreEqual(ERROR_SUCCESS, status);
		//	}

		//	status = NCryptFinalizeKey(hK, 0);
		//	if (status != ERROR_SUCCESS)
		//	{
		//		Trace(TRACE_LEVEL_ERROR, L"   [!] Finalize failed: 0x%08X", status);
		//		Assert::AreEqual(ERROR_SUCCESS, status);
		//	}

		//	BYTE data[32] = { 0xEE }, sig[2048/8];
		//	DWORD cbSig = sizeof(sig);

		//	Trace(TRACE_LEVEL_INFO, L"   [Action] Sign RAW...");
		//	status = NCryptSignHash(hK, NULL, data, sizeof(data), sig, cbSig, &cbSig, NCRYPT_NO_PADDING_FLAG);
		//	if (status != ERROR_SUCCESS)
		//	{
		//		Trace(TRACE_LEVEL_ERROR, L"   [!] RAW Sign failed: 0x%08X", status);
		//		Assert::AreEqual(ERROR_SUCCESS, status);
		//	}
		//	status = NCryptVerifySignature(hK, NULL, data, sizeof(data), sig, cbSig, NCRYPT_NO_PADDING_FLAG);
		//	if (status != ERROR_SUCCESS) {
		//		Trace(TRACE_LEVEL_ERROR, L"   [!] RAW Verify failed: 0x%08X", status);
		//		Assert::AreEqual(ERROR_SUCCESS, status);
		//	}

		//	NCryptDeleteKey(hK, 0);
		//	NCryptFreeObject(hP);
		//}

		// 6. CRYPT / DECRYPT
		TEST_METHOD(Test_KSP_6_CryptDecrypt) {
			Trace(TRACE_LEVEL_INFO, L"[Test] 6. Crypt/Decrypt");
			NCRYPT_PROV_HANDLE hP = NULL; NCRYPT_KEY_HANDLE hK = NULL;

			SECURITY_STATUS status = NCryptOpenStorageProvider(&hP, MS_SMART_CARD_KEY_STORAGE_PROVIDER, 0);
			if (status != ERROR_SUCCESS) {
				Trace(TRACE_LEVEL_ERROR, L"   [!] Open failed: 0x%08X", status);
				Assert::AreEqual(ERROR_SUCCESS, status);
			}

			status = NCryptCreatePersistedKey(hP, &hK, BCRYPT_RSA_ALGORITHM, L"KspCrypt", 0, 0);
			if (status != ERROR_SUCCESS) {
				Trace(TRACE_LEVEL_ERROR, L"   [!] Create failed: 0x%08X", status);
				Assert::AreEqual(ERROR_SUCCESS, status);
			}

			DWORD dwKeySize = 2048;
			status = NCryptSetProperty(hK, NCRYPT_LENGTH_PROPERTY,
				(BYTE*)&dwKeySize, sizeof(dwKeySize), 0);
			if (status != ERROR_SUCCESS) {
				Trace(TRACE_LEVEL_ERROR, L"   [!] KeySize set failed: 0x%08X", status);
				Assert::AreEqual(ERROR_SUCCESS, status);
			}

			status = NCryptFinalizeKey(hK, 0);
			if (status != ERROR_SUCCESS)
			{
				Trace(TRACE_LEVEL_ERROR, L"   [!] Finalize failed: 0x%08X", status);
				Assert::AreEqual(ERROR_SUCCESS, status);
			}

			BYTE plain[32] = { 0x55 }, cipher[512], output[512];
			DWORD cbC, cbO;

			Trace(TRACE_LEVEL_INFO, L"   [Action] Encryption...");
			status = NCryptEncrypt(hK, plain, 32, NULL, cipher, 512, &cbC, NCRYPT_PAD_PKCS1_FLAG);
			if (status != ERROR_SUCCESS)
			{
				Trace(TRACE_LEVEL_ERROR, L"   [!] Encrypt failed: 0x%08X", status);
				Assert::AreEqual(ERROR_SUCCESS, status);
			}

			Trace(TRACE_LEVEL_INFO, L"   [Action] Decryption...");
			status = NCryptDecrypt(hK, cipher, cbC, NULL, output, 512, &cbO, NCRYPT_PAD_PKCS1_FLAG);

			if (status != ERROR_SUCCESS)
			{
				Trace(TRACE_LEVEL_ERROR, L"   [!] Decrypt failed: 0x%08X", status);
				NCryptDeleteKey(hK, 0); 
				Assert::AreEqual(ERROR_SUCCESS, status);
			}

			NCryptDeleteKey(hK, 0);
			NCryptFreeObject(hP);
		}

		// 7. CMAPFILE STRESS (Permutations)
		TEST_METHOD(Test_KSP_7_CMapStress) {
			Trace(TRACE_LEVEL_INFO, L"[Test] 7. CMap Stress");
			NCRYPT_PROV_HANDLE hP = NULL;
			SECURITY_STATUS status = NCryptOpenStorageProvider(&hP, MS_SMART_CARD_KEY_STORAGE_PROVIDER, 0);
			if (status != ERROR_SUCCESS) {
				Trace(TRACE_LEVEL_ERROR, L"   [!] Open failed: 0x%08X", status);
				Assert::AreEqual(ERROR_SUCCESS, status);
			}

			std::vector<std::wstring> names = { L"K1", L"K2", L"K3" };
			std::vector<int> p = { 0, 1, 2 };
			int iter = 0;

			do {
				iter++;
				Trace(TRACE_LEVEL_INFO, L"   [Stress] Iteration %d", iter);
				for (int i : p) {
					NCRYPT_KEY_HANDLE k = NULL;
					status = NCryptCreatePersistedKey(hP, &k, BCRYPT_RSA_ALGORITHM, names[i].c_str(), 0, 0);
					if (status != ERROR_SUCCESS) {
						Trace(TRACE_LEVEL_ERROR, L"   [!] Create failed: 0x%08X", status);
						Assert::AreEqual(ERROR_SUCCESS, status);
					}
					status = NCryptFinalizeKey(k, 0);
					if (status != ERROR_SUCCESS)
					{
						Trace(TRACE_LEVEL_ERROR, L"   [!] Finalize failed: 0x%08X", status);
						Assert::AreEqual(ERROR_SUCCESS, status);
					}
					NCryptFreeObject(k);
				}
				for (int i : p) {
					NCRYPT_KEY_HANDLE k = NULL;
					status = NCryptOpenKey(hP, &k, names[i].c_str(), 0, 0);
					if (status == ERROR_SUCCESS) {
						NCryptDeleteKey(k, 0);
					}
					else
					{
					  Trace(TRACE_LEVEL_ERROR, L"   [!] Failed to re-open %s: 0x%08X", names[i].c_str(), status);
					  Assert::AreEqual(ERROR_SUCCESS, status);
					}
				}
			} while (std::next_permutation(p.begin(), p.end()));

			NCryptFreeObject(hP);
			Trace(TRACE_LEVEL_INFO, L"   [OK] Stress test terminé.");
		}
	};
}
--------------CMpnZc4mniyJIb4rYpAh0MRF
Content-Type: text/x-c-code; charset=windows-1252; name="CSPTests.cpp"
Content-Disposition: attachment; filename="CSPTests.cpp"
Content-Transfer-Encoding: 8bit

#include "pch.h"
#include <wincrypt.h>
#include <ncrypt.h>
#include <CppUnitTest.h>
#include <vector>
#include <algorithm>
#include "mscryptobase.h"

#pragma comment(lib, "crypt32")
#pragma comment(lib, "ncrypt")

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

#define TEST_PIN L"123456078"

namespace GidsTests
{
	TEST_CLASS(CspTests), public MsCryptoBase
	{
	private:

		/**
		 * @brief Génère un conteneur sur une carte à puce via le BaseCSP.
		 *
		 * @param phProv      [Out] Handle vers le fournisseur de services cryptographiques.
		 * @param pszCont     [In]  Nom du conteneur (peut être NULL pour le conteneur par défaut).
		 * @param dwKeySize   [In]  Taille de la clé (ex: 2048).
		 * @return BOOL       TRUE si succès, FALSE sinon.
		 */
		BOOL GenerateContainerWithPin(HCRYPTPROV * phProv, LPCWSTR pszCont, DWORD dwKeySize)
		{
			DWORD dwFlags = CRYPT_NEWKEYSET; // Création d'un nouveau conteneur

			// 1. Acquisition du contexte
			// On utilise MS_SMART_CARD_PROV pour forcer l'usage de la carte à puce via le BaseCSP
			if (!CryptAcquireContextW(
					phProv,
					pszCont,
				MS_SCARD_PROV,
					PROV_RSA_FULL,
					dwFlags))
			{
				DWORD err = GetLastError();

				// Si le conteneur existe déjà, on tente de l'ouvrir sans le flag NEWKEYSET
				if (err == NTE_EXISTS) {
					Trace(TRACE_LEVEL_WARNING, L"Conteneur existant, ouverture simple...");
					if (!CryptAcquireContextW(phProv, pszCont, MS_SCARD_PROV, PROV_RSA_FULL, 0)) {
						Trace(TRACE_LEVEL_ERROR, L"Erreur CryptAcquireContext (Open): 0x%08X", GetLastError());
						return FALSE;
					}
				}
				else
				{
				  Trace(TRACE_LEVEL_ERROR, L"Erreur CryptAcquireContext (New): 0x%08X", err);
				  return FALSE;
			  }
		  }

		  Trace(TRACE_LEVEL_INFO, L"Contexte acquis pour le conteneur: %s", pszCont ? pszCont : L"Default");

		  // 2. Génération de la paire de clés (RSA)
		  // Le flag CRYPT_EXPORTABLE est souvent ignoré par les Smart Cards pour des raisons de sécurité.
		  // dwKeySize est passé dans les 16 bits de poids fort.
		  DWORD dwGenFlags = (dwKeySize << 16);

		  HCRYPTKEY hKey = NULL;
		  if (!CryptGenKey(*phProv, AT_KEYEXCHANGE, dwGenFlags, &hKey))
		  {
			  DWORD err = GetLastError();
			  Trace(TRACE_LEVEL_ERROR, L"Erreur CryptGenKey: 0x%08X", err);

			  // Nettoyage en cas d'échec
			  CryptReleaseContext(*phProv, 0);
			  *phProv = NULL;
			  return FALSE;
		  }

		  Trace(TRACE_LEVEL_INFO, L"Paire de clés de %u bits générée avec succès.", dwKeySize);

		  // 3. Nettoyage du handle de clé (on garde le handle du provider phProv)
		  CryptDestroyKey(hKey);

		  return TRUE;
	   }

		   TEST_METHOD_INITIALIZE(SetUp) {
			   Trace(TRACE_LEVEL_INFO, L"--- [SetUp] setup CSP tests ---");
			   //TraceOff();
			   setUp();
			   createcard();
			   setUpHook();
			   TraceOn();
		   }

		   TEST_METHOD_CLEANUP(TearDown) {
			   Trace(TRACE_LEVEL_INFO, L"--- [TearDown] Cleaning CSP test ---");
			   //TraceOff();
			   tearDownHook();
			   tearDown();
			   TraceOn();
		   }

		   // 1. ENUMERATE EMPTY CARD
		   TEST_METHOD(Test_CSP_1_EmptyEnumerate) {
			   HCRYPTPROV hProv = NULL;
			   char szContainer[MAX_PATH];
			   DWORD cb = MAX_PATH, dwFlags = CRYPT_FIRST, dwCount = 0;

			   if (!CryptAcquireContextW(&hProv, NULL, MS_SCARD_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
				   Trace(TRACE_LEVEL_ERROR, L"   [!] Acquire Verify failed: 0x%08X", GetLastError());
				   Assert::Fail();
			   }
			   while (CryptGetProvParam(hProv, PP_ENUMCONTAINERS, (BYTE*)szContainer, &cb, dwFlags)) {
				   dwFlags = 0; cb = MAX_PATH; dwCount++;
			   }
			   CryptReleaseContext(hProv, 0);
		   }

		   // 3. RSA MATRIX
		   TEST_METHOD(Test_CSP_3_RSAMatrix) {
			   const wchar_t* pszCont = L"RSA_Matrix_Test";
			   std::vector<DWORD> sizes = { 1024, 2048 };

			   for (DWORD dwLen : sizes) {
				   HCRYPTPROV hCont = NULL;
				   HCRYPTKEY hKey = NULL;
				   Trace(TRACE_LEVEL_INFO, L"--- Testing RSA size: %d bits ---", dwLen);

				   if (!GenerateContainerWithPin(&hCont, pszCont, dwLen)) {
					   Assert::Fail(L"Generation failed.");
				   }

				   CERT_NAME_BLOB sib = { 0 };
				   LPCWSTR pszSubject = L"CN=Test";

				   if (!CertStrToNameW(X509_ASN_ENCODING, pszSubject, CERT_X500_NAME_STR,
					   NULL, NULL, &sib.cbData, NULL)) 
				   {
					   Trace(TRACE_LEVEL_ERROR, L"   [!] CertStrToName size failed: 0x%08X", GetLastError());
					   CryptReleaseContext(hCont, 0);
					   Assert::Fail();
				   }
				   sib.pbData = (BYTE*)malloc(sib.cbData);
				   if (!CertStrToNameW(X509_ASN_ENCODING, pszSubject, CERT_X500_NAME_STR,
					   NULL, sib.pbData, &sib.cbData, NULL)) 
				   {
					   Trace(TRACE_LEVEL_ERROR, L"   [!] CertStrToName encode failed: 0x%08X", GetLastError());
					   free(sib.pbData);
					   CryptReleaseContext(hCont, 0);
					   Assert::Fail();
				   }

				   CRYPT_KEY_PROV_INFO KeyProvInfo = { 0 };

				   KeyProvInfo.pwszContainerName = (PWSTR)pszCont;
				   KeyProvInfo.pwszProvName = (LPWSTR)MS_SCARD_PROV;   // Ou MS_SMART_CARD_PROV
				   KeyProvInfo.dwProvType = PROV_RSA_FULL;
				   KeyProvInfo.dwFlags = 0;
				   KeyProvInfo.cProvParam = 0;
				   KeyProvInfo.rgProvParam = NULL;
				   KeyProvInfo.dwKeySpec = AT_KEYEXCHANGE;

				   PCCERT_CONTEXT pCert = CertCreateSelfSignCertificate(hCont, &sib, 0, &KeyProvInfo, NULL, NULL, NULL, NULL);
				   free(sib.pbData);

				   if (!pCert) 
				   {
					   DWORD dwErr = GetLastError();
					   Trace(TRACE_LEVEL_ERROR, L"   [!] SelfSign failed: 0x%08X", dwErr);
					   CryptReleaseContext(hCont, 0);
					   Assert::Fail();
				   }

				   if (!CryptGetUserKey(hCont, AT_KEYEXCHANGE, &hKey)) 
				   {
					   DWORD dwErr = GetLastError();
					   Trace(TRACE_LEVEL_ERROR, L"   [!] CryptGetUserKey failed: 0x%08X", dwErr);
					   CryptReleaseContext(hCont, 0);
					   Assert::Fail();
				   }
				   if (!CryptSetKeyParam(
					   hKey,
					   KP_CERTIFICATE,
					   pCert->pbCertEncoded,
					   0))
				   {
					   DWORD err = GetLastError();
					   Trace(TRACE_LEVEL_ERROR, L"Erreur CryptSetKeyParam(KP_CERTIFICATE): 0x%08X", err);
				   }
				   CertFreeCertificateContext(pCert);
				   CryptDestroyKey(hKey);
				   CryptReleaseContext(hCont, 0);
				   CryptAcquireContextW(&hCont, pszCont, MS_SCARD_PROV, PROV_RSA_FULL, CRYPT_DELETEKEYSET);
			   }
		   }

		   // 4. SIGNATURE MATRIX
		   TEST_METHOD(Test_CSP_4_SignatureMatrix) {
			   HCRYPTPROV hProv = NULL;
			   ALG_ID hashAlgs[] = { CALG_SHA1, CALG_SHA_256, CALG_SHA_512 };
			   const wchar_t* pszCont = L"Sign_Matrix_Test";

			   if (!GenerateContainerWithPin(&hProv, pszCont, 2048))
			   {
				   Assert::Fail(); 
			   }

			   for (ALG_ID algId : hashAlgs) {
				   HCRYPTHASH hHash = NULL;
				   if (!CryptCreateHash(hProv, algId, 0, 0, &hHash))
				   {
					   Trace(TRACE_LEVEL_ERROR, L"   [!] Hash init failed: 0x%08X", GetLastError());
					   Assert::Fail();
				   }
				   BYTE sig[256]; DWORD cbSig = sizeof(sig);
				   if (!CryptSignHash(hHash, AT_KEYEXCHANGE, NULL, 0, sig, &cbSig))
				   {
					   DWORD dwErr = GetLastError();
					   Trace(TRACE_LEVEL_ERROR, L"   [!] Sign failed for 0x%X: 0x%08X", algId, dwErr);
					   CryptDestroyHash(hHash); Assert::Fail();
				   }
				   Trace(TRACE_LEVEL_INFO, L"   [OK] Signature success (0x%X)", algId);
				   CryptDestroyHash(hHash);
			   }
			   CryptReleaseContext(hProv, 0);
			   CryptAcquireContextW(&hProv, pszCont, MS_SCARD_PROV, PROV_RSA_FULL, CRYPT_DELETEKEYSET);
		   }

		   // 5. MODE RAW
		   TEST_METHOD(Test_CSP_5_RawSign) {
			   HCRYPTPROV hProv = NULL;
			   const wchar_t* pszCont = L"Raw_Test";
			   HCRYPTKEY hKey = NULL;

			   if (!GenerateContainerWithPin(&hProv, pszCont, 2048))
			   {
				   Assert::Fail();
			   }

			  
			   if (!CryptGetUserKey(hProv, AT_KEYEXCHANGE, &hKey))
			   {
				   Trace(TRACE_LEVEL_ERROR, L" [!] CryptGetUserKey failed: 0x%08X", GetLastError());
				   Assert::Fail();
			   }
			   for (DWORD dwI = 0; dwI < 3; dwI++) {
				   HCRYPTHASH hHash = NULL;

				   if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash))
				   {
					   Trace(TRACE_LEVEL_ERROR, L" [!] CryptCreateHash failed: 0x%08X", GetLastError());
					   Assert::Fail();
				   }
				   // 3. Injection d'une valeur de hash factice (32 octets pour SHA-256)
				   BYTE mockHash[32] = { 0xAA, 0xBB, 0xCC, (BYTE) dwI }; // Le reste à 0
				   if (!CryptSetHashParam(hHash, HP_HASHVAL, mockHash, 0))
				   {
					   Trace(TRACE_LEVEL_ERROR, L" [!] CryptSetHashParam failed: 0x%08X", GetLastError());
					   Assert::Fail();
				   }

				   DWORD cbSig = 0;
				   if (!CryptSignHash(hHash, AT_KEYEXCHANGE, NULL, CRYPT_NOHASHOID, NULL, &cbSig))
				   {
					   Trace(TRACE_LEVEL_INFO, L"   [Info] RAW probe: 0x%08X, size: %d", GetLastError(), cbSig);
					   Assert::Fail();
				   }
				   std::vector<BYTE> signature(cbSig);
				   if (!CryptSignHash(hHash, AT_KEYEXCHANGE, NULL, CRYPT_NOHASHOID, signature.data(), &cbSig))
				   {
					   Trace(TRACE_LEVEL_ERROR, L" [!] CryptSignHash (sign) failed: 0x%08X", GetLastError());
					   Assert::Fail();
				   }
				  
				   if (!CryptVerifySignature(hHash, signature.data(), cbSig, hKey, NULL, CRYPT_NOHASHOID))
				   {
					   Trace(TRACE_LEVEL_ERROR, L" [!] CryptVerifySignature (sign) failed: 0x%08X", GetLastError());
					   Assert::Fail();
				   }
				   if (hHash) CryptDestroyHash(hHash);
			   }
			   if (hKey) CryptDestroyKey(hKey);

			   CryptReleaseContext(hProv, 0);
			   CryptAcquireContextW(&hProv, pszCont, MS_SCARD_PROV, PROV_RSA_FULL, CRYPT_DELETEKEYSET);
		   }

		   // 6. CRYPT / DECRYPT
		   TEST_METHOD(Test_CSP_6_CryptDecrypt) {
			   HCRYPTPROV hProv = NULL; HCRYPTKEY hKey = NULL;
			   const wchar_t* pszCont = L"Crypt_Test";

			   if (!GenerateContainerWithPin(&hProv, pszCont, 2048))
			   {
				   Assert::Fail();
			   }

			   if (!CryptGetUserKey(hProv, AT_KEYEXCHANGE, &hKey))
			   {
				   Trace(TRACE_LEVEL_ERROR, L"   [!] GetUserKey failed: 0x%08X", GetLastError());
				   Assert::Fail();
			   }

			   BYTE data[256] = { 0x11, 0x22, 0x33, 0x44 };
			   DWORD cbData = 4;
			   DWORD dwBufLen = 256;
			   if (!CryptEncrypt(hKey, 0, TRUE, 0, data, &cbData, dwBufLen))
			   {
				   Trace(TRACE_LEVEL_ERROR, L"   [!] Encrypt failed: 0x%08X", GetLastError());
				   Assert::Fail();

			   }
			   if (!CryptDecrypt(hKey, 0, TRUE, 0, data, &cbData))
			   {
				   Trace(TRACE_LEVEL_ERROR, L"   [!] Decrypt failed: 0x%08X", GetLastError());
				   Assert::Fail();
			   }
			   Assert::AreEqual((DWORD)4, cbData);
			   Assert::AreEqual((BYTE)0x11, data[0]);

			   CryptDestroyKey(hKey);
			   CryptReleaseContext(hProv, 0);
			   CryptAcquireContextW(&hProv, pszCont, MS_SCARD_PROV, PROV_RSA_FULL, CRYPT_DELETEKEYSET);
		   }

		   // 7. CMAPFILE STRESS
		   TEST_METHOD(Test_CSP_7_CMapStress) {
			   std::vector<std::wstring> names = { L"ST1", L"ST2", L"ST3", L"ST4" };
			   HCRYPTPROV h = NULL;

			   for (auto& name : names) 
			   {
				   if (!GenerateContainerWithPin(&h, name.c_str(), 1024)) Assert::Fail();
				   CryptReleaseContext(h, 0);
			   }
			   for (auto& name : names)
			   {
				   if (!CryptAcquireContextW(&h, name.c_str(), MS_SCARD_PROV, PROV_RSA_FULL, CRYPT_DELETEKEYSET)) 
				   {
					   Trace(TRACE_LEVEL_ERROR, L"   [!] Delete failed: 0x%08X", GetLastError());
					   Assert::Fail();
				   }
			   }
		   }
	};
}
--------------CMpnZc4mniyJIb4rYpAh0MRF
Content-Type: text/x-c-code; charset=UTF-8; name="MsCryptoBase.cpp"
Content-Disposition: attachment; filename="MsCryptoBase.cpp"
Content-Transfer-Encoding: base64

77u/I2luY2x1ZGUgInBjaC5oIg0KI2luY2x1ZGUgIk1zQ3J5cHRvQmFzZS5oIg0KDQojaW5j
bHVkZSA8bWFwPg0KI2luY2x1ZGUgPHZlY3Rvcj4NCiNpbmNsdWRlIDxzdHJpbmc+DQojaW5j
bHVkZSA8bnRzZWNhcGkuaD4NCiNpbmNsdWRlIDxPYmpiYXNlLmg+DQojaW5jbHVkZSAiLi4v
RUlEVmlydHVhbFNtYXJ0Q2FyZE1pbmlkcml2ZXJUZXN0L2NhcmRtb2QuaCINCg0KLy8gU3Ry
dWN0dXJlIHBvdXIgc3RvY2tlciBsZXMgZG9ubsOpZXMgZHUgY2FjaGUNCnN0cnVjdCBDYWNo
ZUVudHJ5IHsNCglzdGQ6OnZlY3RvcjxCWVRFPiBkYXRhOw0KfTsNCg0KLy8gU2ltdWxhdGlv
biBkdSBjYWNoZSA6IENsw6kgPSBzekxvb2t1cE5hbWUgKHBvdXIgbGEgc2ltcGxpY2l0w6kg
ZHUgdGVzdCkNCnN0YXRpYyBzdGQ6Om1hcDxzdGQ6OndzdHJpbmcsIENhY2hlRW50cnk+IGdf
U2ltdWxhdGVkQ2FjaGU7DQoNCiNkZWZpbmUgVEhFQVRSIHsweDNCLDB4OEMsMHgwMSwweDRE
LDB4NzksMHg1MywweDZELDB4NjEsMHg3MiwweDc0LDB4NEMsMHg2RiwweDY3LDB4NkYsMHg2
RSwweEE1fQ0KI2RlZmluZSBTQ0FSRF9QUk9WSURFUl9DQVJEX01PRFVMRSAweDgwMDAwMDAx
DQoNCk1zQ3J5cHRvQmFzZSogZ19wU3RhdGljSW5zdGFuY2UgPSBudWxscHRyOw0KDQpIb29r
RW50cnkgTXNDcnlwdG9CYXNlOjpzX0hvb2tzW10gPSB7DQoJeyJXaW5zY2FyZC5kbGwiLCAi
U0NhcmRUcmFuc21pdCIsIChQUk9DKU1zQ3J5cHRvQmFzZTo6SG9va1RyYW5zbWl0LCBOVUxM
LCBmYWxzZX0sDQoJeyJXaW5zY2FyZC5kbGwiLCAiU0NhcmRDb25uZWN0VyIsIChQUk9DKU1z
Q3J5cHRvQmFzZTo6SG9va0Nvbm5lY3RXLCBOVUxMLCBmYWxzZX0sDQoJeyJXaW5zY2FyZC5k
bGwiLCAiU0NhcmREaXNjb25uZWN0IiwgKFBST0MpTXNDcnlwdG9CYXNlOjpIb29rRGlzY29u
bmVjdCwgTlVMTCwgZmFsc2V9LA0KCXsiV2luc2NhcmQuZGxsIiwgIlNDYXJkU3RhdHVzVyIs
IChQUk9DKU1zQ3J5cHRvQmFzZTo6SG9va1N0YXR1c1csIE5VTEwsIGZhbHNlfSwNCgl7Ildp
bnNjYXJkLmRsbCIsICJTQ2FyZExpc3RSZWFkZXJzVyIsIChQUk9DKU1zQ3J5cHRvQmFzZTo6
SG9va0xpc3RSZWFkZXJzVywgTlVMTCwgZmFsc2V9LA0KCXsiV2luc2NhcmQuZGxsIiwgIlND
YXJkQmVnaW5UcmFuc2FjdGlvbiIsIChQUk9DKU1zQ3J5cHRvQmFzZTo6SG9va0JlZ2luVHJh
bnMsIE5VTEwsIGZhbHNlfSwNCgl7IldpbnNjYXJkLmRsbCIsICJTQ2FyZEVuZFRyYW5zYWN0
aW9uIiwgKFBST0MpTXNDcnlwdG9CYXNlOjpIb29rRW5kVHJhbnMsIE5VTEwsIGZhbHNlfSwN
Cgl7IldpbnNjYXJkLmRsbCIsICJTQ2FyZENvbnRyb2wiLCAoUFJPQylNc0NyeXB0b0Jhc2U6
Okhvb2tDb250cm9sLCBOVUxMLCBmYWxzZX0sDQoJeyJXaW5zY2FyZC5kbGwiLCAiU0NhcmRF
c3RhYmxpc2hDb250ZXh0IiwgKFBST0MpTXNDcnlwdG9CYXNlOjpIb29rRXN0YWJsaXNoQ29u
dGV4dCwgTlVMTCwgZmFsc2V9LA0KCXsiV2luc2NhcmQuZGxsIiwgIlNDYXJkUmVsZWFzZUNv
bnRleHQiLCAoUFJPQylNc0NyeXB0b0Jhc2U6Okhvb2tSZWxlYXNlQ29udGV4dCwgTlVMTCwg
ZmFsc2V9LA0KCXsiV2luc2NhcmQuZGxsIiwgIlNDYXJkR2V0U3RhdHVzQ2hhbmdlVyIsIChQ
Uk9DKU1zQ3J5cHRvQmFzZTo6SG9va0dldFN0YXR1c0NoYW5nZVcsIE5VTEwsIGZhbHNlfSwN
Cgl7IldpbnNjYXJkLmRsbCIsICJTQ2FyZEZyZWVNZW1vcnkiLCAoUFJPQylNc0NyeXB0b0Jh
c2U6Okhvb2tGcmVlTWVtb3J5LCBOVUxMLCBmYWxzZX0sDQoJeyJXaW5zY2FyZC5kbGwiLCAi
U0NhcmRDYW5jZWwiLCAoUFJPQylNc0NyeXB0b0Jhc2U6Okhvb2tDYW5jZWwsIE5VTEwsIGZh
bHNlfSwNCgl7IldpbnNjYXJkLmRsbCIsICJTQ2FyZFJlY29ubmVjdCIsIChQUk9DKU1zQ3J5
cHRvQmFzZTo6SG9va1JlY29ubmVjdCwgTlVMTCwgZmFsc2V9LA0KCXsiV2luc2NhcmQuZGxs
IiwgIlNDYXJkQXVkaXQiLCAoUFJPQylNc0NyeXB0b0Jhc2U6Okhvb2tBdWRpdCwgTlVMTCwg
ZmFsc2V9LA0KCXsiV2luc2NhcmQuZGxsIiwgIlNDYXJkTGlzdENhcmRzVyIsIChQUk9DKU1z
Q3J5cHRvQmFzZTo6SG9va0xpc3RDYXJkc1csIE5VTEwsIGZhbHNlfSwNCgl7IldpbnNjYXJk
LmRsbCIsICJTQ2FyZFJlYWRDYWNoZVciLCAoUFJPQylNc0NyeXB0b0Jhc2U6Okhvb2tSZWFk
Q2FjaGVXLCBOVUxMLCBmYWxzZX0sDQoJeyJXaW5zY2FyZC5kbGwiLCAiU0NhcmRXcml0ZUNh
Y2hlVyIsIChQUk9DKU1zQ3J5cHRvQmFzZTo6SG9va1dyaXRlQ2FjaGVXLCBOVUxMLCBmYWxz
ZX0sDQoJeyJXaW5zY2FyZC5kbGwiLCAiU0NhcmRHZXRDYXJkVHlwZVByb3ZpZGVyTmFtZVci
LCAoUFJPQylNc0NyeXB0b0Jhc2U6Okhvb2tHZXRDYXJkVHlwZVByb3ZpZGVyTmFtZVcsIE5V
TEwsIGZhbHNlfSwNCgl7IldpbnNjYXJkLmRsbCIsICJTQ2FyZEdldFJlYWRlckljb25XIiwg
KFBST0MpTXNDcnlwdG9CYXNlOjpIb29rR2V0UmVhZGVySWNvblcsIE5VTEwsIGZhbHNlfSwN
Cgl7IldpbnNjYXJkLmRsbCIsICJTQ2FyZEdldERldmljZVR5cGVJZFciLCAoUFJPQylNc0Ny
eXB0b0Jhc2U6Okhvb2tHZXREZXZpY2VUeXBlSWRXLCBOVUxMLCBmYWxzZX0sDQoJeyJXaW5z
Y2FyZC5kbGwiLCAiU0NhcmRBY2Nlc3NTdGFydGVkRXZlbnQiLCAoUFJPQylNc0NyeXB0b0Jh
c2U6Okhvb2tBY2Nlc3NTdGFydGVkRXZlbnQsIE5VTEwsIGZhbHNlfSwNCgl7IldpbnNjYXJk
LmRsbCIsICJTQ2FyZFJlbGVhc2VTdGFydGVkRXZlbnQiLCAoUFJPQylNc0NyeXB0b0Jhc2U6
Okhvb2tSZWxlYXNlU3RhcnRlZEV2ZW50LCBOVUxMLCBmYWxzZX0sDQoJeyJXaW5zY2FyZC5k
bGwiLCAiU0NhcmRHZXRSZWFkZXJEZXZpY2VJbnN0YW5jZUlkVyIsIChQUk9DKU1zQ3J5cHRv
QmFzZTo6SG9va0dldFJlYWRlckRldmljZUluc3RhbmNlSWRXLCBOVUxMLCBmYWxzZX0sDQoJ
eyJXaW5zY2FyZC5kbGwiLCAiZ19yZ1NDYXJkVDBQY2kiLCBOVUxMLCBOVUxMLCB0cnVlfSwN
Cgl7IldpbnNjYXJkLmRsbCIsICJnX3JnU0NhcmRUMVBjaSIsIE5VTEwsIE5VTEwsIHRydWV9
LA0KCXsiY3JlZHVpLmRsbCIsICAgIkNyZWRVSVByb21wdEZvcldpbmRvd3NDcmVkZW50aWFs
c1ciLCAgKFBST0MpTXNDcnlwdG9CYXNlOjpIb29rQ3JlZFVJUHJvbXB0Rm9yV2luZG93c0Ny
ZWRlbnRpYWxzLCBOVUxMLCBmYWxzZX0sDQp9Ow0KDQpNc0NyeXB0b0Jhc2UqIE1zQ3J5cHRv
QmFzZTo6RnJvbUhhbmRsZShTQ0FSREhBTkRMRSBoKSB7DQoJDQoJTXNDcnlwdG9CYXNlKiBw
b2ludGVyID0gcmVpbnRlcnByZXRfY2FzdDxNc0NyeXB0b0Jhc2UqPihoKTsNCglpZiAocG9p
bnRlciAhPSBnX3BTdGF0aWNJbnN0YW5jZSkNCgl7DQoJCXJldHVybiBOVUxMOw0KCX0NCgly
ZXR1cm4gcG9pbnRlcjsNCn0NCg0Kdm9pZCBNc0NyeXB0b0Jhc2U6OnNldFVwSG9vaygpIHsN
CglUcmFjZShUUkFDRV9MRVZFTF9JTkZPLCBMIj09PSBbTXNDcnlwdG9CYXNlXSBEw6lidXQg
U0VUVVAgPT09Iik7DQoNCglmb3IgKGF1dG8mIGVudHJ5IDogc19Ib29rcykgew0KCQlITU9E
VUxFIGhNb2QgPSBHZXRNb2R1bGVIYW5kbGVBKGVudHJ5LnBzekRsbE5hbWUpOw0KCQlpZiAo
IWhNb2QpIGhNb2QgPSBMb2FkTGlicmFyeUEoZW50cnkucHN6RGxsTmFtZSk7DQoJCWlmICgh
aE1vZCkgew0KCQkJVHJhY2UoVFJBQ0VfTEVWRUxfV0FSTklORywgTCIgICBbIV0gRExMIG5v
biB0cm91dsOpZSA6ICVTIiwgZW50cnkucHN6RGxsTmFtZSk7DQoJCQljb250aW51ZTsNCgkJ
fQ0KCQllbnRyeS5wZm5PcmlnaW5hbCA9IEdldFByb2NBZGRyZXNzKGhNb2QsIGVudHJ5LnBz
ekZ1bmNOYW1lKTsNCgkJaWYgKCFlbnRyeS5wZm5PcmlnaW5hbCkNCgkJCVRyYWNlKFRSQUNF
X0xFVkVMX1dBUk5JTkcsIEwiICAgWyFdIFN5bWJvbGUgbm9uIHRyb3V2w6kgOiAlUyElUyIs
DQoJCQkJZW50cnkucHN6RGxsTmFtZSwgZW50cnkucHN6RnVuY05hbWUpOw0KCX0NCg0KCW1f
aE1pbmlkcml2ZXIgPSBMb2FkTGlicmFyeShMIm1zY2xtZC5kbGwiKTsNCgltX2hCYXNlQ3Nw
ID0gTG9hZExpYnJhcnkoTCJiYXNlY3NwLmRsbCIpOw0KCW1faEtzcCA9IExvYWRMaWJyYXJ5
KEwic2Nrc3AuZGxsIik7DQoNCglQYXRjaElBVChtX2hNaW5pZHJpdmVyLCBmYWxzZSk7DQoJ
UGF0Y2hJQVQobV9oS3NwLCBmYWxzZSk7DQoJUGF0Y2hJQVQobV9oQmFzZUNzcCwgZmFsc2Up
Ow0KDQoNCglnX3BTdGF0aWNJbnN0YW5jZSA9IHRoaXM7DQoJVHJhY2UoVFJBQ0VfTEVWRUxf
SU5GTywgTCI9PT0gW01zQ3J5cHRvQmFzZV0gRmluIFNFVFVQID09PSIpOw0KfQ0KDQp2b2lk
IE1zQ3J5cHRvQmFzZTo6dGVhckRvd25Ib29rKCkgew0KCVRyYWNlKFRSQUNFX0xFVkVMX0lO
Rk8sIEwiPT09IFtNc0NyeXB0b0Jhc2VdIETDqWJ1dCBURUFSRE9XTiA9PT0iKTsNCglQYXRj
aElBVChtX2hNaW5pZHJpdmVyLCB0cnVlKTsNCglQYXRjaElBVChtX2hCYXNlQ3NwLCB0cnVl
KTsNCglQYXRjaElBVChtX2hLc3AsIHRydWUpOw0KCWdfcFN0YXRpY0luc3RhbmNlID0gbnVs
bHB0cjsNCglnX1NpbXVsYXRlZENhY2hlLmNsZWFyKCk7DQoJLy8gVGVjaG5pcXVlIGRlICJz
d2FwIiBwb3VyIGZvcmNlciBsYSBkw6lzYWxsb2NhdGlvbiBkZSBsYSBjYXBhY2l0w6kgZGUg
bGEgbWFwDQoJc3RkOjptYXA8c3RkOjp3c3RyaW5nLCBDYWNoZUVudHJ5PigpLnN3YXAoZ19T
aW11bGF0ZWRDYWNoZSk7DQp9DQoNCnZvaWQgTXNDcnlwdG9CYXNlOjpQYXRjaElBVChITU9E
VUxFIGhUYXJnZXREbGwsIGJvb2wgcmVzdG9yZSkgew0KCWlmICghaFRhcmdldERsbCkgcmV0
dXJuOw0KDQoJV0NIQVIgc3pQYXRoW01BWF9QQVRIXTsNCglHZXRNb2R1bGVGaWxlTmFtZSho
VGFyZ2V0RGxsLCBzelBhdGgsIE1BWF9QQVRIKTsNCglzdGQ6OndzdHJpbmcgZGxsTmFtZSA9
IHN6UGF0aDsNCglkbGxOYW1lID0gZGxsTmFtZS5zdWJzdHIoZGxsTmFtZS5maW5kX2xhc3Rf
b2YoTCJcXCIpICsgMSk7DQoNCglUcmFjZShUUkFDRV9MRVZFTF9JTkZPLCBMIi0tLSBBbmFs
eXNlIElBVCA6ICVzIC0tLSIsIGRsbE5hbWUuY19zdHIoKSk7DQoNCglQSU1BR0VfRE9TX0hF
QURFUiBwRG9zSGVhZGVyID0gKFBJTUFHRV9ET1NfSEVBREVSKWhUYXJnZXREbGw7DQoJUElN
QUdFX05UX0hFQURFUlMgcE50SGVhZGVycyA9IChQSU1BR0VfTlRfSEVBREVSUykoKEJZVEUq
KWhUYXJnZXREbGwgKyBwRG9zSGVhZGVyLT5lX2xmYW5ldyk7DQoNCgkvLyAxLiBTdGFuZGFy
ZCBJbXBvcnRzDQoJRFdPUkQgaW1wb3J0QWRkciA9IHBOdEhlYWRlcnMtPk9wdGlvbmFsSGVh
ZGVyLkRhdGFEaXJlY3RvcnlbSU1BR0VfRElSRUNUT1JZX0VOVFJZX0lNUE9SVF0uVmlydHVh
bEFkZHJlc3M7DQoJaWYgKGltcG9ydEFkZHIpIHsNCgkJUElNQUdFX0lNUE9SVF9ERVNDUklQ
VE9SIHBJbXBvcnREZXNjID0gKFBJTUFHRV9JTVBPUlRfREVTQ1JJUFRPUikoKEJZVEUqKWhU
YXJnZXREbGwgKyBpbXBvcnRBZGRyKTsNCgkJZm9yICg7IHBJbXBvcnREZXNjLT5OYW1lOyBw
SW1wb3J0RGVzYysrKSB7DQoJCQlMUENTVFIgc3pNb2ROYW1lID0gKExQQ1NUUikoKEJZVEUq
KWhUYXJnZXREbGwgKyBwSW1wb3J0RGVzYy0+TmFtZSk7DQoJCQlib29sIGJNYXRjaCA9IGZh
bHNlOw0KCQkJZm9yIChhdXRvJiBlbnRyeSA6IHNfSG9va3MpDQoJCQkJaWYgKF9zdHJpY21w
KHN6TW9kTmFtZSwgZW50cnkucHN6RGxsTmFtZSkgPT0gMCkgeyBiTWF0Y2ggPSB0cnVlOyBi
cmVhazsgfQ0KCQkJaWYgKGJNYXRjaCkgew0KCQkJCVRyYWNlKFRSQUNFX0xFVkVMX0lORk8s
IEwiICAgW1NlY3Rpb25dICVTIFN0YW5kYXJkIEltcG9ydHMgZMOpdGVjdMOpcy4iLCBzek1v
ZE5hbWUpOw0KCQkJCUFwcGx5UGF0Y2hTdGFuZGFyZChoVGFyZ2V0RGxsLCBwSW1wb3J0RGVz
YywgcmVzdG9yZSwgZGxsTmFtZS5jX3N0cigpKTsNCgkJCX0NCgkJfQ0KCX0NCg0KCS8vIDIu
IERlbGF5LUxvYWQgSW1wb3J0cw0KCURXT1JEIGRlbGF5QWRkciA9IHBOdEhlYWRlcnMtPk9w
dGlvbmFsSGVhZGVyLkRhdGFEaXJlY3RvcnlbSU1BR0VfRElSRUNUT1JZX0VOVFJZX0RFTEFZ
X0lNUE9SVF0uVmlydHVhbEFkZHJlc3M7DQoJaWYgKGRlbGF5QWRkcikgew0KCQlQSU1BR0Vf
REVMQVlMT0FEX0RFU0NSSVBUT1IgcERlbGF5RGVzYyA9IChQSU1BR0VfREVMQVlMT0FEX0RF
U0NSSVBUT1IpKChCWVRFKiloVGFyZ2V0RGxsICsgZGVsYXlBZGRyKTsNCgkJZm9yICg7IHBE
ZWxheURlc2MtPkRsbE5hbWVSVkE7IHBEZWxheURlc2MrKykgew0KCQkJTFBDU1RSIHN6TW9k
TmFtZSA9IChMUENTVFIpKChCWVRFKiloVGFyZ2V0RGxsICsgcERlbGF5RGVzYy0+RGxsTmFt
ZVJWQSk7DQoJCQlib29sIGJNYXRjaCA9IGZhbHNlOw0KCQkJZm9yIChhdXRvJiBlbnRyeSA6
IHNfSG9va3MpDQoJCQkJaWYgKF9zdHJpY21wKHN6TW9kTmFtZSwgZW50cnkucHN6RGxsTmFt
ZSkgPT0gMCkgeyBiTWF0Y2ggPSB0cnVlOyBicmVhazsgfQ0KCQkJaWYgKGJNYXRjaCkgew0K
CQkJCVRyYWNlKFRSQUNFX0xFVkVMX0lORk8sIEwiICAgW1NlY3Rpb25dICVTIERlbGF5LUxv
YWQgSW1wb3J0cyBkw6l0ZWN0w6lzLiIsIHN6TW9kTmFtZSk7DQoJCQkJQXBwbHlQYXRjaERl
bGF5TG9hZChoVGFyZ2V0RGxsLCBwRGVsYXlEZXNjLCByZXN0b3JlLCBkbGxOYW1lLmNfc3Ry
KCkpOw0KCQkJfQ0KCQl9DQoJfQ0KfQ0KDQp2b2lkIE1zQ3J5cHRvQmFzZTo6QXBwbHlQYXRj
aFN0YW5kYXJkKEhNT0RVTEUgaFRhcmdldERsbCwgUElNQUdFX0lNUE9SVF9ERVNDUklQVE9S
IHBJbXBvcnREZXNjLCBib29sIHJlc3RvcmUsIGNvbnN0IHdjaGFyX3QqIGRsbE5hbWUpIHsN
CglQSU1BR0VfVEhVTktfREFUQSBwVGh1bmsgPSAoUElNQUdFX1RIVU5LX0RBVEEpKChCWVRF
KiloVGFyZ2V0RGxsICsgcEltcG9ydERlc2MtPkZpcnN0VGh1bmspOw0KCVBJTUFHRV9USFVO
S19EQVRBIHBPcmlnVGh1bmsgPSAoUElNQUdFX1RIVU5LX0RBVEEpKChCWVRFKiloVGFyZ2V0
RGxsICsgcEltcG9ydERlc2MtPk9yaWdpbmFsRmlyc3RUaHVuayk7DQoJaW50IGNvdW50ID0g
MDsNCglmb3IgKDsgcFRodW5rLT51MS5GdW5jdGlvbjsgcFRodW5rKyssIHBPcmlnVGh1bmsr
Kykgew0KCQlQUk9DKiBwcGZuID0gKFBST0MqKSZwVGh1bmstPnUxLkZ1bmN0aW9uOw0KDQoJ
CS8vIFLDqWN1cMOpcmVyIGxlIG5vbSBkZSBsYSBmb25jdGlvbiBpbXBvcnTDqWUgdmlhIE9y
aWdpbmFsRmlyc3RUaHVuaw0KCQlMUENTVFIgcHN6RnVuY05hbWUgPSBudWxscHRyOw0KCQlp
ZiAocE9yaWdUaHVuayAmJiAhKHBPcmlnVGh1bmstPnUxLk9yZGluYWwgJiBJTUFHRV9PUkRJ
TkFMX0ZMQUcpKSB7DQoJCQlQSU1BR0VfSU1QT1JUX0JZX05BTUUgcElibiA9IChQSU1BR0Vf
SU1QT1JUX0JZX05BTUUpKChCWVRFKiloVGFyZ2V0RGxsICsgcE9yaWdUaHVuay0+dTEuQWRk
cmVzc09mRGF0YSk7DQoJCQlwc3pGdW5jTmFtZSA9IHBJYm4tPk5hbWU7DQoJCX0NCg0KCQli
b29sIGJIb29rZWQgPSBmYWxzZTsNCgkJZm9yIChhdXRvJiBlbnRyeSA6IHNfSG9va3MpIHsN
CgkJCWlmIChlbnRyeS5pc0RhdGEpIHsNCgkJCQlpZiAoKnBwZm4gPT0gZW50cnkucGZuT3Jp
Z2luYWwpDQoJCQkJCVRyYWNlKFRSQUNFX0xFVkVMX0lORk8sIEwiICAgICAgW0RBVEFdICVT
IGlkZW50aWZpZWQgJXAiLCBlbnRyeS5wc3pGdW5jTmFtZSwgKnBwZm4pOw0KCQkJCWNvbnRp
bnVlOw0KCQkJfQ0KCQkJUFJPQyBwZm5Ub0ZpbmQgPSByZXN0b3JlID8gZW50cnkucGZuSG9v
ayA6IGVudHJ5LnBmbk9yaWdpbmFsOw0KCQkJUFJPQyBwZm5Ub1JlcGxhY2UgPSByZXN0b3Jl
ID8gZW50cnkucGZuT3JpZ2luYWwgOiBlbnRyeS5wZm5Ib29rOw0KCQkJaWYgKCpwcGZuID09
IHBmblRvRmluZCAmJiBwZm5Ub0ZpbmQgIT0gTlVMTCkgew0KCQkJCURXT1JEIG9sZDsNCgkJ
CQlWaXJ0dWFsUHJvdGVjdChwcGZuLCBzaXplb2YoUFJPQyksIFBBR0VfRVhFQ1VURV9SRUFE
V1JJVEUsICZvbGQpOw0KCQkJCSpwcGZuID0gcGZuVG9SZXBsYWNlOw0KCQkJCVZpcnR1YWxQ
cm90ZWN0KHBwZm4sIHNpemVvZihQUk9DKSwgb2xkLCAmb2xkKTsNCgkJCQlUcmFjZShUUkFD
RV9MRVZFTF9JTkZPLCBMIiAgICAgIFtPSy1TVERdICVTICVzIiwgZW50cnkucHN6RnVuY05h
bWUsIHJlc3RvcmUgPyBMInJlc3RvcmVkIiA6IEwicGF0Y2hlZCIpOw0KCQkJCWNvdW50Kys7
DQoJCQkJYkhvb2tlZCA9IHRydWU7DQoJCQkJYnJlYWs7DQoJCQl9DQoJCX0NCg0KCQkvLyDi
hpAgTk9VVkVBVSA6IHNpZ25hbGVyIHRvdXQgaW1wb3J0IFdpbnNjYXJkIG5vbiBob29rw6kN
CgkJaWYgKCFiSG9va2VkICYmICFyZXN0b3JlICYmIHBzekZ1bmNOYW1lKSB7DQoJCQlUcmFj
ZShUUkFDRV9MRVZFTF9XQVJOSU5HLCBMIiAgICAgIFshXSBJbXBvcnQgTk9UIEhPT0tFRCA6
ICVTIOKGkiByZWFsIFdpbnNjYXJkIGNhbGwgcG9zc2libGUgISIsIHBzekZ1bmNOYW1lKTsN
CgkJfQ0KCX0NCglpZiAoY291bnQgPT0gMCkNCgkJVHJhY2UoVFJBQ0VfTEVWRUxfSU5GTywg
TCIgICAgICBbSW5mb10gTm8gYWRkcmVzcyBtYXRjaCBpbiB0aGlzIFN0YW5kYXJkIHNlY3Rp
b24uIik7DQp9DQoNCnZvaWQgTXNDcnlwdG9CYXNlOjpBcHBseVBhdGNoRGVsYXlMb2FkKEhN
T0RVTEUgaFRhcmdldERsbCwgUElNQUdFX0RFTEFZTE9BRF9ERVNDUklQVE9SIHBEZWxheURl
c2MsIGJvb2wgcmVzdG9yZSwgY29uc3Qgd2NoYXJfdCogZGxsTmFtZSkgew0KCS8vIENhbGN1
bCBkZSBsYSBiYXNlIGR1IG1vZHVsZSBwb3VyIHRyYW5zZm9ybWVyIGxlcyBSVkEgZW4gYWRy
ZXNzZXMgcsOpZWxsZXMNCglCWVRFKiBwQmFzZSA9IChCWVRFKiloVGFyZ2V0RGxsOw0KDQoJ
Ly8gUsOpY3Vww6lyYXRpb24gZGVzIHRhYmxlcyB2aWEgUlZBIChSZWxhdGl2ZSBWaXJ0dWFs
IEFkZHJlc3MpDQoJLy8gSU5UOiBUYWJsZSBkZXMgbm9tcyAoSW1wb3J0IE5hbWUgVGFibGUp
DQoJLy8gSUFUOiBUYWJsZSBkZXMgYWRyZXNzZXMgKEltcG9ydCBBZGRyZXNzVGFibGUpIC0g
Q29udGllbnQgbGVzIFN0dWJzIGF2YW50IHLDqXNvbHV0aW9uDQoJUElNQUdFX1RIVU5LX0RB
VEEgcE5hbWVUaHVuayA9IChQSU1BR0VfVEhVTktfREFUQSkocEJhc2UgKyBwRGVsYXlEZXNj
LT5JbXBvcnROYW1lVGFibGVSVkEpOw0KCVBJTUFHRV9USFVOS19EQVRBIHBBZGRyVGh1bmsg
PSAoUElNQUdFX1RIVU5LX0RBVEEpKHBCYXNlICsgcERlbGF5RGVzYy0+SW1wb3J0QWRkcmVz
c1RhYmxlUlZBKTsNCg0KCVRyYWNlKFRSQUNFX0xFVkVMX0lORk8sIEwiICAgICAgW0RlbGF5
TG9hZC1TY2FuXSBBbmFseXNlIGRlICVzIChCYXNlOiAlcCwgSU5UIFJWQTogMHglWCkiLA0K
CQlkbGxOYW1lLCBwQmFzZSwgcERlbGF5RGVzYy0+SW1wb3J0TmFtZVRhYmxlUlZBKTsNCg0K
CWludCBwYXRjaENvdW50ID0gMDsNCg0KCS8vIE9uIHBhcmNvdXJ0IGxhIHRhYmxlIGRlcyBu
b21zIChJTlQpDQoJZm9yICg7IHBOYW1lVGh1bmstPnUxLkFkZHJlc3NPZkRhdGE7IHBOYW1l
VGh1bmsrKywgcEFkZHJUaHVuaysrKSB7DQoNCgkJLy8gU2tpcCBzaSBpbXBvcnQgcGFyIE9y
ZGluYWwgKGxlIGJpdCBkZSBwb2lkcyBmb3J0IGVzdCDDoCAxKQ0KCQlpZiAoSU1BR0VfU05B
UF9CWV9PUkRJTkFMKHBOYW1lVGh1bmstPnUxLk9yZGluYWwpKSB7DQoJCQljb250aW51ZTsN
CgkJfQ0KDQoJCS8vIEFjY8OocyBhdSBub20gZGUgbGEgZm9uY3Rpb24gdmlhIGxhIFJWQSBB
ZGRyZXNzT2ZEYXRhDQoJCVBJTUFHRV9JTVBPUlRfQllfTkFNRSBwSW1wb3J0TmFtZSA9IChQ
SU1BR0VfSU1QT1JUX0JZX05BTUUpKHBCYXNlICsgcE5hbWVUaHVuay0+dTEuQWRkcmVzc09m
RGF0YSk7DQoJCUxQQ1NUUiBwc3pGdW5jTmFtZSA9IChMUENTVFIpcEltcG9ydE5hbWUtPk5h
bWU7DQoNCgkJLy8gcHBmbiBwb2ludGUgc3VyIGwnZW50csOpZSBkZSBsJ0lBVCAoNCBvY3Rl
dHMgZW4geDg2LCA4IG9jdGV0cyBlbiB4NjQpDQoJCVBST0MqIHBwZm4gPSAoUFJPQyopJnBB
ZGRyVGh1bmstPnUxLkZ1bmN0aW9uOw0KDQoJCS8vIENvbXBhcmFpc29uIGF2ZWMgbm90cmUg
dGFibGUgZGUgaG9va3MNCgkJZm9yIChhdXRvJiBlbnRyeSA6IHNfSG9va3MpIHsNCgkJCWlm
ICghZW50cnkuaXNEYXRhICYmIF9zdHJpY21wKHBzekZ1bmNOYW1lLCBlbnRyeS5wc3pGdW5j
TmFtZSkgPT0gMCkgew0KDQoJCQkJUFJPQyBwZm5Ub1JlcGxhY2UgPSByZXN0b3JlID8gZW50
cnkucGZuT3JpZ2luYWwgOiBlbnRyeS5wZm5Ib29rOw0KCQkJCWlmICghcGZuVG9SZXBsYWNl
KSB7DQoJCQkJCVRyYWNlKFRSQUNFX0xFVkVMX1dBUk5JTkcsIEwiICAgICAgICAgWyFdICVT
IDogcGZuVG9SZXBsYWNlIGVzdCBOVUxMIiwgcHN6RnVuY05hbWUpOw0KCQkJCQljb250aW51
ZTsNCgkJCQl9DQoNCgkJCQkvLyBWw6lyaWZpY2F0aW9uIGQnw6l2aXRlbWVudCA6IHNpIGTD
qWrDoCBwYXRjaMOpIChwb3VyIMOpdml0ZXIgbGVzIMOpY3JpdHVyZXMgaW51dGlsZXMpDQoJ
CQkJaWYgKCpwcGZuID09IHBmblRvUmVwbGFjZSkgew0KCQkJCQljb250aW51ZTsNCgkJCQl9
DQoNCgkJCQkvLyAtLS0gUEFUQ0ggTcOJTU9JUkUgLS0tDQoJCQkJRFdPUkQgb2xkUHJvdGVj
dDsNCgkJCQkvLyBPbiB1dGlsaXNlIHNpemVvZihQUk9DKSBwb3VyIMOqdHJlIGFnbm9zdGlx
dWUgeDg2ICg0KSAvIHg2NCAoOCkNCgkJCQlpZiAoVmlydHVhbFByb3RlY3QocHBmbiwgc2l6
ZW9mKFBST0MpLCBQQUdFX0VYRUNVVEVfUkVBRFdSSVRFLCAmb2xkUHJvdGVjdCkpIHsNCg0K
CQkJCQlUcmFjZShUUkFDRV9MRVZFTF9WRVJCT1NFLCBMIiAgICAgICAgIFtERUJVR10gJVMg
OiBBbmNpZW5uZSB2YWxldXIgPSAlcCwgTm91dmVsbGUgPSAlcCIsDQoJCQkJCQlwc3pGdW5j
TmFtZSwgKnBwZm4sIHBmblRvUmVwbGFjZSk7DQoNCgkJCQkJKnBwZm4gPSBwZm5Ub1JlcGxh
Y2U7DQoNCgkJCQkJVmlydHVhbFByb3RlY3QocHBmbiwgc2l6ZW9mKFBST0MpLCBvbGRQcm90
ZWN0LCAmb2xkUHJvdGVjdCk7DQoNCgkJCQkJVHJhY2UoVFJBQ0VfTEVWRUxfSU5GTywgTCIg
ICAgICAgICBbT0stREVMQVldICVTICVzIGRhbnMgJXMiLA0KCQkJCQkJcHN6RnVuY05hbWUs
IHJlc3RvcmUgPyBMInJlc3RhdXLDqWUiIDogTCJwYXRjaMOpZSIsIGRsbE5hbWUpOw0KDQoJ
CQkJCXBhdGNoQ291bnQrKzsNCgkJCQl9DQoJCQkJZWxzZSB7DQoJCQkJCVRyYWNlKFRSQUNF
X0xFVkVMX0VSUk9SLCBMIiAgICAgICAgIFtFUlJFVVJdIFZpcnR1YWxQcm90ZWN0IMOpY2hv
dcOpIHBvdXIgJVMgKEVycm9yOiAlZCkiLA0KCQkJCQkJcHN6RnVuY05hbWUsIEdldExhc3RF
cnJvcigpKTsNCgkJCQl9DQoJCQl9DQoJCX0NCgl9DQoNCglpZiAocGF0Y2hDb3VudCA+IDAp
IHsNCgkJVHJhY2UoVFJBQ0VfTEVWRUxfSU5GTywgTCIgICAgICBbQmlsYW5dICVkIGZvbmN0
aW9ucyBwYXRjaMOpZXMgZGFucyBsYSBzZWN0aW9uIERlbGF5LUxvYWQgZGUgJXMiLCBwYXRj
aENvdW50LCBkbGxOYW1lKTsNCgl9DQp9DQovLyAtLS0gTE9HSVFVRSBERVMgSE9PS1MgQVZF
QyBUUkFDRSAtLS0NCg0KTE9ORyBXSU5BUEkgTXNDcnlwdG9CYXNlOjpIb29rQ29ubmVjdFco
DQoJX0luXyBTQ0FSRENPTlRFWFQgaENvbnRleHQsDQoJX0luXyBMUENXU1RSIHN6UmVhZGVy
LA0KCV9Jbl8gRFdPUkQgZHdTaGFyZU1vZGUsDQoJX0luXyBEV09SRCBkd1ByZWZlcnJlZFBy
b3RvY29scywNCglfT3V0XyBMUFNDQVJESEFORExFIHBoQ2FyZCwNCglfT3V0XyBMUERXT1JE
IHBkd0FjdGl2ZVByb3RvY29sKQ0Kew0KCVRyYWNlKFRSQUNFX0xFVkVMX0lORk8sIEwiLT4g
W0hvb2tdIFNDYXJkQ29ubmVjdFcgJXAgc3VyICVzIiwgaENvbnRleHQsIHN6UmVhZGVyKTsN
CglpZiAocGhDYXJkKSAqcGhDYXJkID0gcmVpbnRlcnByZXRfY2FzdDxTQ0FSREhBTkRMRT4o
Z19wU3RhdGljSW5zdGFuY2UpOw0KCWlmIChwZHdBY3RpdmVQcm90b2NvbCkgKnBkd0FjdGl2
ZVByb3RvY29sID0gU0NBUkRfUFJPVE9DT0xfVDE7DQoJRnJvbUhhbmRsZSgqcGhDYXJkKTsN
CglyZXR1cm4gU0NBUkRfU19TVUNDRVNTOw0KfQ0KDQpMT05HIFdJTkFQSSBNc0NyeXB0b0Jh
c2U6Okhvb2tUcmFuc21pdCgNCglfSW5fIFNDQVJESEFORExFIGhDYXJkLA0KCV9Jbl8gTFBD
U0NBUkRfSU9fUkVRVUVTVCBwaW9TZW5kUGNpLA0KCV9Jbl9yZWFkc18oY2JTZW5kTGVuZ3Ro
KSBMUENCWVRFIHBiU2VuZEJ1ZmZlciwNCglfSW5fIERXT1JEIGNiU2VuZExlbmd0aCwNCglf
SW5vdXRfb3B0XyBMUENTQ0FSRF9JT19SRVFVRVNUIHBpb1JlY3ZQY2ksDQoJX091dF93cml0
ZXNfYnl0ZXNfdG9fb3B0XygqcGNiUmVjdkxlbmd0aCwgKnBjYlJlY3ZMZW5ndGgpIExQQllU
RSBwYlJlY3ZCdWZmZXIsDQoJX0lub3V0XyBMUERXT1JEIHBjYlJlY3ZMZW5ndGgpDQp7DQoJ
VHJhY2UoVFJBQ0VfTEVWRUxfSU5GTywgTCItPiBbSG9va10gU0NhcmRUcmFuc21pdCAlcCAo
VGFpbGxlIDogJWQpIiwgaENhcmQsIGNiU2VuZExlbmd0aCk7DQoJaWYgKCFGcm9tSGFuZGxl
KGhDYXJkKSkgcmV0dXJuIFNDQVJEX0VfQ09NTV9EQVRBX0xPU1Q7DQoNCglCT09MIHJldCA9
IEZyb21IYW5kbGUoaENhcmQpLT5leGVjdXRlUmF3KChQQllURSlwYlNlbmRCdWZmZXIsIGNi
U2VuZExlbmd0aCwgcGJSZWN2QnVmZmVyLCBwY2JSZWN2TGVuZ3RoLCBGQUxTRSwgMHg5MDAw
KTsNCglpZiAoIXJldCkNCgl7DQoJCUFzc2VydDo6RmFpbCgpOw0KCQlyZXR1cm4gU0NBUkRf
RV9DT01NX0RBVEFfTE9TVDsNCgl9DQoJcmV0dXJuICBTQ0FSRF9TX1NVQ0NFU1M7DQp9DQoN
CkxPTkcgV0lOQVBJIE1zQ3J5cHRvQmFzZTo6SG9va1N0YXR1c1coDQoJX0luXyBTQ0FSREhB
TkRMRSBoQ2FyZCwNCglfT3V0X3dyaXRlc19vcHRfKCpwY2NoUmVhZGVyTGVuKSBMUFdTVFIg
bXN6UmVhZGVyTmFtZXMsDQoJX0lub3V0X29wdF8gTFBEV09SRCBwY2NoUmVhZGVyTGVuLA0K
CV9PdXRfb3B0XyBMUERXT1JEIHBkd1N0YXRlLA0KCV9PdXRfb3B0XyBMUERXT1JEIHBkd1By
b3RvY29sLA0KCV9PdXRfd3JpdGVzX29wdF8oKnBjYkF0ckxlbikgTFBCWVRFIHBiQXRyLA0K
CV9Jbm91dF9vcHRfIExQRFdPUkQgcGNiQXRyTGVuKQ0Kew0KCVRyYWNlKFRSQUNFX0xFVkVM
X0lORk8sIEwiLT4gW0hvb2tdIFNDYXJkU3RhdHVzVyAlcCIsIGhDYXJkKTsNCglpZiAoIUZy
b21IYW5kbGUoaENhcmQpKSByZXR1cm4gU0NBUkRfRV9DT01NX0RBVEFfTE9TVDsNCgkvLyAx
LiBEw6lmaW5pdGlvbiBkZXMgY29uc3RhbnRlcyBkZSByZXRvdXIgKGNvaMOpcmVudGVzIGF2
ZWMgTGlzdFJlYWRlcnMgZXQgbCdBVFIgZMOpZmluaSkNCgljb25zdCB3Y2hhcl90KiBzelJl
YWRlck5hbWUgPSBMIkdJRFNfRW11bGF0b3JfMFwwIjsNCglEV09SRCBkd1JlYWRlckxlbiA9
IChEV09SRCl3Y3NsZW4oc3pSZWFkZXJOYW1lKSArIDE7DQoJQllURSBhdHJbXSA9IFRIRUFU
UjsNCglEV09SRCBkd0F0ckxlbiA9IHNpemVvZihhdHIpOw0KDQoJLy8gMi4gR2VzdGlvbiBk
dSBub20gZHUgbGVjdGV1ciAoc3pSZHIgLyBwY2NoKQ0KCWlmIChwY2NoUmVhZGVyTGVuKSB7
DQoJCWlmIChtc3pSZWFkZXJOYW1lcyA9PSBOVUxMKSB7DQoJCQkqcGNjaFJlYWRlckxlbiA9
IGR3UmVhZGVyTGVuOw0KCQl9DQoJCWVsc2UgaWYgKCpwY2NoUmVhZGVyTGVuID09IFNDQVJE
X0FVVE9BTExPQ0FURSkgew0KCQkJTFBXU1RSKiBwcHdzeiA9IHJlaW50ZXJwcmV0X2Nhc3Q8
TFBXU1RSKj4obXN6UmVhZGVyTmFtZXMpOw0KCQkJKnBwd3N6ID0gKExQV1NUUilIZWFwQWxs
b2MoR2V0UHJvY2Vzc0hlYXAoKSwgSEVBUF9aRVJPX01FTU9SWSwgZHdSZWFkZXJMZW4gKiBz
aXplb2Yod2NoYXJfdCkpOw0KCQkJVHJhY2UoVFJBQ0VfTEVWRUxfVkVSQk9TRSwgTCIgICBb
RGVidWddIFNDYXJkU3RhdHVzVyA6IEFsbG9jIG9mICVwICVkIGJ5dGVzIiwgKnBwd3N6LCBk
d1JlYWRlckxlbiAqIHNpemVvZih3Y2hhcl90KSk7DQoJCQlpZiAoKnBwd3N6KSB7DQoJCQkJ
bWVtY3B5KCpwcHdzeiwgc3pSZWFkZXJOYW1lLCBkd1JlYWRlckxlbiAqIHNpemVvZih3Y2hh
cl90KSk7DQoJCQkJKnBjY2hSZWFkZXJMZW4gPSBkd1JlYWRlckxlbjsNCgkJCX0NCgkJfQ0K
CQllbHNlIGlmICgqcGNjaFJlYWRlckxlbiA+PSBkd1JlYWRlckxlbikgew0KCQkJbWVtY3B5
KG1zelJlYWRlck5hbWVzLCBzelJlYWRlck5hbWUsIGR3UmVhZGVyTGVuICogc2l6ZW9mKHdj
aGFyX3QpKTsNCgkJCSpwY2NoUmVhZGVyTGVuID0gZHdSZWFkZXJMZW47DQoJCX0NCgkJZWxz
ZSB7DQoJCQkqcGNjaFJlYWRlckxlbiA9IGR3UmVhZGVyTGVuOw0KCQkJcmV0dXJuIFNDQVJE
X0VfSU5TVUZGSUNJRU5UX0JVRkZFUjsNCgkJfQ0KCX0NCg0KCS8vIDMuIEdlc3Rpb24gZGUg
bCdBVFIgKHBiQXRyIC8gcGNiQXRyKQ0KCWlmIChwY2JBdHJMZW4pIHsNCgkJaWYgKHBiQXRy
ID09IE5VTEwpIHsNCgkJCSpwY2JBdHJMZW4gPSBkd0F0ckxlbjsNCgkJfQ0KCQllbHNlIGlm
ICgqcGNiQXRyTGVuID09IFNDQVJEX0FVVE9BTExPQ0FURSkgew0KCQkJTFBCWVRFKiBwcEF0
ciA9IHJlaW50ZXJwcmV0X2Nhc3Q8TFBCWVRFKj4ocGJBdHIpOw0KCQkJKnBwQXRyID0gKExQ
QllURSlIZWFwQWxsb2MoR2V0UHJvY2Vzc0hlYXAoKSwgSEVBUF9aRVJPX01FTU9SWSwgZHdB
dHJMZW4pOw0KCQkJVHJhY2UoVFJBQ0VfTEVWRUxfVkVSQk9TRSwgTCIgICBbRGVidWddIFND
YXJkU3RhdHVzVyA6IEFsbG9jIG9mICVwICVkIGJ5dGVzIiwgKnBwQXRyLCBkd0F0ckxlbik7
DQoJCQlpZiAoKnBwQXRyKSB7DQoJCQkJbWVtY3B5KCpwcEF0ciwgYXRyLCBkd0F0ckxlbik7
DQoJCQkJKnBjYkF0ckxlbiA9IGR3QXRyTGVuOw0KCQkJfQ0KCQl9DQoJCWVsc2UgaWYgKCpw
Y2JBdHJMZW4gPj0gZHdBdHJMZW4pIHsNCgkJCW1lbWNweShwYkF0ciwgYXRyLCBkd0F0ckxl
bik7DQoJCQkqcGNiQXRyTGVuID0gZHdBdHJMZW47DQoJCX0NCgkJZWxzZSB7DQoJCQkqcGNi
QXRyTGVuID0gZHdBdHJMZW47DQoJCQlyZXR1cm4gU0NBUkRfRV9JTlNVRkZJQ0lFTlRfQlVG
RkVSOw0KCQl9DQoJfQ0KDQoJLy8gNC4gw4l0YXRzIGV0IFByb3RvY29sZXMNCglpZiAocGR3
U3RhdGUpIHsNCgkJLy8gU0NBUkRfU1BFQ0lGSUMgaW5kaXF1ZSBxdWUgbGEgY2FydGUgZXN0
IHByw6p0ZSBwb3VyIGRlcyBBUERVIChNaW5pZHJpdmVyIGNoYXJnw6kpDQoJCSpwZHdTdGF0
ZSA9IFNDQVJEX1NQRUNJRklDOw0KCX0NCg0KCWlmIChwZHdQcm90b2NvbCkgew0KCQkqcGR3
UHJvdG9jb2wgPSBTQ0FSRF9QUk9UT0NPTF9UMTsNCgl9DQoNCglyZXR1cm4gU0NBUkRfU19T
VUNDRVNTOw0KfQ0KDQpMT05HIFdJTkFQSSBNc0NyeXB0b0Jhc2U6Okhvb2tHZXRTdGF0dXND
aGFuZ2VXKA0KCV9Jbl8gU0NBUkRDT05URVhUIGhDb250ZXh0LA0KCV9Jbl8gRFdPUkQgZHdU
aW1lb3V0LA0KCV9Jbm91dF91cGRhdGVzXyhjUmVhZGVycykgTFBTQ0FSRF9SRUFERVJTVEFU
RVcgcmdSZWFkZXJTdGF0ZXMsDQoJX0luXyBEV09SRCBjUmVhZGVycykNCnsNCglUcmFjZShU
UkFDRV9MRVZFTF9JTkZPLCBMIi0+IFtIb29rXSBTQ2FyZEdldFN0YXR1c0NoYW5nZVcgJXAg
KE5iOiAlZCkiLCBoQ29udGV4dCwgY1JlYWRlcnMpOw0KDQoJY29uc3Qgd2NoYXJfdCogc3pN
eVJlYWRlciA9IEwiR0lEU19FbXVsYXRvcl8wIjsNCglCWVRFIGF0cltdID0gVEhFQVRSOw0K
DQoJZm9yIChEV09SRCBpID0gMDsgaSA8IGNSZWFkZXJzOyBpKyspDQoJew0KCQkvLyBPbiB2
w6lyaWZpZSBzaSBjJ2VzdCBub3RyZSBsZWN0ZXVyIMOpbXVsw6kNCgkJaWYgKHJnUmVhZGVy
U3RhdGVzW2ldLnN6UmVhZGVyICYmIHdjc3N0cihyZ1JlYWRlclN0YXRlc1tpXS5zelJlYWRl
ciwgc3pNeVJlYWRlcikpDQoJCXsNCgkJCS8vIEwnw6l0YXQgInLDqWVsIiBkYW5zIG5vdHJl
IMOpbXVsYXRldXIgZXN0IDogUHLDqXNlbnQgKyBNdXRlDQoJCQlEV09SRCBkd1JlYWxTdGF0
ZSA9IChTQ0FSRF9TVEFURV9QUkVTRU5UIHwgU0NBUkRfU1RBVEVfTVVURSkgKyAweDEwMDAw
Ow0KDQoJCQkvLyBPbiBjb21wYXJlIGF2ZWMgY2UgcXVlIGwnYXBwbGljYXRpb24gcGVuc2Ug
YXZvaXIgKGR3Q3VycmVudFN0YXRlKQ0KCQkJLy8gU2kgbCdhcHBsaWNhdGlvbiBuZSBzYWl0
IHJpZW4gKFNDQVJEX1NUQVRFX1VOQVdBUkUpLCBvbiBmb3JjZSBsZSBjaGFuZ2VtZW50Lg0K
CQkJaWYgKHJnUmVhZGVyU3RhdGVzW2ldLmR3Q3VycmVudFN0YXRlID09IFNDQVJEX1NUQVRF
X1VOQVdBUkUgfHwNCgkJCQkocmdSZWFkZXJTdGF0ZXNbaV0uZHdDdXJyZW50U3RhdGUgJiBT
Q0FSRF9TVEFURV9QUkVTRU5UKSA9PSAwKQ0KCQkJew0KCQkJCXJnUmVhZGVyU3RhdGVzW2ld
LmR3RXZlbnRTdGF0ZSA9IFNDQVJEX1NUQVRFX0NIQU5HRUQgfCBkd1JlYWxTdGF0ZTsNCgkJ
CQlUcmFjZShUUkFDRV9MRVZFTF9JTkZPLCBMIiAgIFtTdGF0dXNdIFByZW1pZXIgc2lnbmFs
ZW1lbnQgOiBDYXJ0ZSBwcsOpc2VudGUuIik7DQoJCQl9DQoJCQllbHNlDQoJCQl7DQoJCQkJ
Ly8gTCdhcHBsaWNhdGlvbiBzYWl0IGTDqWrDoCBxdWUgbGEgY2FydGUgZXN0IGzDoCwgb24g
bmUgbWV0IFBBUyBsZSBiaXQgQ0hBTkdFRA0KCQkJCXJnUmVhZGVyU3RhdGVzW2ldLmR3RXZl
bnRTdGF0ZSA9IGR3UmVhbFN0YXRlOw0KCQkJfQ0KDQoJCQkvLyBPbiByZW1wbGl0IHN5c3TD
qW1hdGlxdWVtZW50IGwnQVRSIGV0IGxlIG5vbWJyZSBkJ29jdGV0cw0KCQkJcmdSZWFkZXJT
dGF0ZXNbaV0uY2JBdHIgPSBzaXplb2YoYXRyKTsNCgkJCW1lbWNweShyZ1JlYWRlclN0YXRl
c1tpXS5yZ2JBdHIsIGF0ciwgc2l6ZW9mKGF0cikpOw0KCQl9DQoJCWVsc2UNCgkJew0KCQkJ
Ly8gR2VzdGlvbiBkZXMgYXV0cmVzIGxlY3RldXJzIChQblAsIGV0Yy4pIHBvdXIgw6l2aXRl
ciBkZSBibG9xdWVyIGxlIHN5c3TDqG1lDQoJCQlyZ1JlYWRlclN0YXRlc1tpXS5kd0V2ZW50
U3RhdGUgPSBTQ0FSRF9TVEFURV9FTVBUWTsNCgkJfQ0KCX0NCg0KCXJldHVybiBTQ0FSRF9T
X1NVQ0NFU1M7DQp9DQoNCi8vI2RlZmluZSBDTV9JT0NUTF9HRVRfRkVBVFVSRV9SRVFVRVNU
IFNDQVJEX0NUTF9DT0RFKDM1MDApDQpMT05HIFdJTkFQSSBNc0NyeXB0b0Jhc2U6Okhvb2tD
b250cm9sKA0KCV9Jbl8gU0NBUkRIQU5ETEUgaENhcmQsDQoJX0luXyBEV09SRCBkd0NvbnRy
b2xDb2RlLA0KCV9Jbl9yZWFkc19ieXRlc19vcHRfKGNiSW5CdWZmZXJTaXplKSBMUENWT0lE
IGxwSW5CdWZmZXIsDQoJX0luXyBEV09SRCBjYkluQnVmZmVyU2l6ZSwNCglfT3V0X3dyaXRl
c19ieXRlc190b19vcHRfKGNiT3V0QnVmZmVyU2l6ZSwgKmxwQnl0ZXNSZXR1cm5lZCkgTFBW
T0lEIGxwT3V0QnVmZmVyLA0KCV9Jbl8gRFdPUkQgY2JPdXRCdWZmZXJTaXplLA0KCV9PdXRf
IExQRFdPUkQgbHBCeXRlc1JldHVybmVkKQ0Kew0KCVRyYWNlKFRSQUNFX0xFVkVMX0lORk8s
IEwiLT4gW0hvb2tdIFNDYXJkQ29udHJvbCAlcCAoQ29kZSA6IDB4JTA4WCkiLCBoQ2FyZCwg
ZHdDb250cm9sQ29kZSk7DQoJaWYgKCFGcm9tSGFuZGxlKGhDYXJkKSkgcmV0dXJuIFNDQVJE
X0VfQ09NTV9EQVRBX0xPU1Q7DQoNCglpZiAobHBCeXRlc1JldHVybmVkKSAqbHBCeXRlc1Jl
dHVybmVkID0gMDsNCg0KCXN3aXRjaCAoZHdDb250cm9sQ29kZSkNCgl7DQoJY2FzZSBJT0NU
TF9TTUFSVENBUkRfR0VUX0ZFQVRVUkVfUkVRVUVTVDoNCgl7DQoJCVRyYWNlKFRSQUNFX0xF
VkVMX0lORk8sIEwiSU9DVExfU01BUlRDQVJEX0dFVF9GRUFUVVJFX1JFUVVFU1QiKTsNCg0K
CQkvLyBTdHJ1Y3R1cmUgZCd1bmUgZW50csOpZSBkYW5zIGxhIHLDqXBvbnNlIEdFVF9GRUFU
VVJFX1JFUVVFU1QNCgkJLy8gQ2hhcXVlIGZlYXR1cmUgZXN0IGVuY29kw6llIHNlbG9uIFBD
L1NDIFBhcnQgMTAgOg0KCQkvLyAgIDEgb2N0ZXQgIDogdGFnIChmZWF0dXJlIGNvZGUpDQoJ
CS8vICAgMSBvY3RldCAgOiBsZW5ndGggKHRvdWpvdXJzIDQpDQoJCS8vICAgNCBvY3RldHMg
OiBJT0NUTCBhc3NvY2nDqSAoYmlnLWVuZGlhbikNCiNwcmFnbWEgcGFjayhwdXNoLCAxKQ0K
CQlzdHJ1Y3QgRkVBVFVSRV9FTlRSWSB7DQoJCQlCWVRFICB0YWc7DQoJCQlCWVRFICBsZW5n
dGg7ICAgLy8gdG91am91cnMgMHgwNA0KCQkJRFdPUkQgaW9jdGw7ICAgIC8vIGJpZy1lbmRp
YW4gIQ0KCQl9Ow0KI3ByYWdtYSBwYWNrKHBvcCkNCg0KCQkvLyBGZWF0dXJlcyByZXRvdXJu
w6llcyA6IHVuaXF1ZW1lbnQgbGVzIGZlYXR1cmVzIGRlIGJhc2UNCgkJLy8gc2FucyBQSU5Q
QUQgKHBhcyBkZSBGRUFUVVJFX1ZFUklGWV9QSU5fU1RBUlQvRklOSVNILCBldGMuKQ0KCQkv
LyBPbiBleHBvc2Ugc2V1bGVtZW50IEZFQVRVUkVfRVhFQ1VURV9QQUNFIHNpIGJlc29pbiwN
CgkJLy8gb3UgcmllbiBkdSB0b3V0IHNpIGxhIGNhcnRlIG4nZW4gYSBwYXMgYmVzb2luLg0K
CQkvLw0KCQkvLyBJY2kgb24gcmV0b3VybmUgdW5lIGxpc3RlIHZpZGUgKGFjY8OocyBkaXJl
Y3QgY2FydGUsIHBhcyBkZSBmZWF0dXJlIFBJTlBBRCkNCgkJLy8gPT4gcsOpcG9uc2UgdmFs
aWRlIG1haXMgdmlkZSA6IGxwQnl0ZXNSZXR1cm5lZCA9IDAsIFNDQVJEX1NfU1VDQ0VTUw0K
CQkvLw0KCQkvLyBTaSB0dSB2ZXV4IGV4cG9zZXIgZGVzIGZlYXR1cmVzIHNww6ljaWZpcXVl
cywgZMOpY29tbWVudGUgbGVzIGVudHLDqWVzIHZvdWx1ZXMuDQoNCgkJLy9zdGF0aWMgY29u
c3QgRkVBVFVSRV9FTlRSWSBmZWF0dXJlc1tdID0gew0KCQkvLyAgICAvLyBFeGVtcGxlIDog
RkVBVFVSRV9HRVRfVExWX1BST1BFUlRJRVMgPSAweDEyDQoJCS8vICAgIC8vIHsgMHgxMiwg
MHgwNCwgX2J5dGVzd2FwX3Vsb25nKElPQ1RMX0ZFQVRVUkVfR0VUX1RMVl9QUk9QRVJUSUVT
KSB9LA0KDQoJCS8vICAgIC8vIEV4ZW1wbGUgOiBGRUFUVVJFX0VYRUNVVEVfUEFDRSA9IDB4
MjANCgkJLy8gICAgLy8geyAweDIwLCAweDA0LCBfYnl0ZXN3YXBfdWxvbmcoSU9DVExfRkVB
VFVSRV9FWEVDVVRFX1BBQ0UpIH0sDQoJCS8vfTsNCg0KCQlEV09SRCBjYk5lZWRlZCA9IChE
V09SRCkoMC8qc2l6ZW9mKGZlYXR1cmVzKSovKTsNCg0KCQkvLyBDYXMgOiBsZSBjYWxsZXIg
c29uZGUgbGEgdGFpbGxlIChscE91dEJ1ZmZlciA9PSBOVUxMIG91IGNiT3V0QnVmZmVyU2l6
ZSA9PSAwKQ0KCQlpZiAobHBPdXRCdWZmZXIgPT0gTlVMTCB8fCBjYk91dEJ1ZmZlclNpemUg
PT0gMCkNCgkJew0KCQkJaWYgKGxwQnl0ZXNSZXR1cm5lZCkgKmxwQnl0ZXNSZXR1cm5lZCA9
IGNiTmVlZGVkOw0KCQkJcmV0dXJuIFNDQVJEX1NfU1VDQ0VTUzsNCgkJfQ0KDQoJCS8vIEJ1
ZmZlciB0cm9wIHBldGl0DQoJCWlmIChjYk91dEJ1ZmZlclNpemUgPCBjYk5lZWRlZCkNCgkJ
ew0KCQkJaWYgKGxwQnl0ZXNSZXR1cm5lZCkgKmxwQnl0ZXNSZXR1cm5lZCA9IGNiTmVlZGVk
Ow0KCQkJcmV0dXJuIFNDQVJEX0VfSU5TVUZGSUNJRU5UX0JVRkZFUjsNCgkJfQ0KDQoJCS8v
IENvcGllIGRlIGxhIHLDqXBvbnNlDQoJICAgLyogaWYgKGNiTmVlZGVkID4gMCkNCgkJCW1l
bWNweShscE91dEJ1ZmZlciwgZmVhdHVyZXMsIGNiTmVlZGVkKTsqLw0KDQoJCWlmIChscEJ5
dGVzUmV0dXJuZWQpICpscEJ5dGVzUmV0dXJuZWQgPSBjYk5lZWRlZDsNCgkJcmV0dXJuIFND
QVJEX1NfU1VDQ0VTUzsNCgl9DQoNCgljYXNlIElPQ1RMX1NNQVJUQ0FSRF9HRVRfQVRUUklC
VVRFOg0KCQlUcmFjZShUUkFDRV9MRVZFTF9JTkZPLCBMIklPQ1RMX1NNQVJUQ0FSRF9HRVRf
QVRUUklCVVRFIik7DQoJCS8vIMOAIGltcGzDqW1lbnRlciBzZWxvbiB0ZXMgYmVzb2lucw0K
CQlyZXR1cm4gU0NBUkRfRV9OT1RfUkVBRFk7DQoNCglkZWZhdWx0Og0KCQlUcmFjZShUUkFD
RV9MRVZFTF9XQVJOSU5HLCBMIltIb29rXSBTQ2FyZENvbnRyb2wgOiBjb2RlIGluY29ubnUg
MHglMDhYIiwgZHdDb250cm9sQ29kZSk7DQoJCXJldHVybiBTQ0FSRF9FX05PVF9SRUFEWTsN
Cgl9DQp9DQoNCkxPTkcgV0lOQVBJIE1zQ3J5cHRvQmFzZTo6SG9va0xpc3RSZWFkZXJzVygN
CglfSW5fIFNDQVJEQ09OVEVYVCBoQ29udGV4dCwNCglfSW5fb3B0XyBMUENXU1RSIG1zekdy
b3VwcywNCglfT3V0X3dyaXRlc19vcHRfKCpwY2NoUmVhZGVycykgTFBXU1RSIG1zelJlYWRl
cnMsDQoJX0lub3V0XyBMUERXT1JEIHBjY2hSZWFkZXJzKQ0Kew0KCVRyYWNlKFRSQUNFX0xF
VkVMX0lORk8sIEwiLT4gW0hvb2tdIFNDYXJkTGlzdFJlYWRlcnNXICVwIiwgaENvbnRleHQp
Ow0KDQoJY29uc3Qgd2NoYXJfdCogcmVhZGVycyA9IEwiR0lEU19FbXVsYXRvcl8wXDAiOw0K
CS8vIFRhaWxsZSBlbiBjYXJhY3TDqHJlcyBpbmNsdWFudCBsZSBkb3VibGUgbnVsIGZpbmFs
DQoJRFdPUkQgbGVuID0gKERXT1JEKXdjc2xlbihyZWFkZXJzKSArIDI7DQoNCglpZiAocGNj
aFJlYWRlcnMgPT0gTlVMTCkNCgl7DQoJCXJldHVybiBTQ0FSRF9FX0lOVkFMSURfUEFSQU1F
VEVSOw0KCX0NCg0KCS8vIENhcyAxIDogTCd1dGlsaXNhdGV1ciB2ZXV0IGp1c3RlIGNvbm5h
w650cmUgbGEgdGFpbGxlIG7DqWNlc3NhaXJlDQoJaWYgKG1zelJlYWRlcnMgPT0gTlVMTCkN
Cgl7DQoJCSpwY2NoUmVhZGVycyA9IGxlbjsNCgkJcmV0dXJuIFNDQVJEX1NfU1VDQ0VTUzsN
Cgl9DQoNCgkvLyBDYXMgMiA6IFNDQVJEX0FVVE9BTExPQ0FURSAobXN6UmRyIGVzdCBlbiBm
YWl0IHVuIHBvaW50ZXVyIHZlcnMgdW4gTFBXU1RSKQ0KCWlmICgqcGNjaFJlYWRlcnMgPT0g
U0NBUkRfQVVUT0FMTE9DQVRFKQ0KCXsNCgkJVHJhY2UoVFJBQ0VfTEVWRUxfSU5GTywgTCIg
ICBbSW5mb10gU0NhcmRMaXN0UmVhZGVyc1cgOiBBbGxvY2F0aW9uIGF1dG9tYXRpcXVlIGRl
bWFuZMOpZSIpOw0KDQoJCS8vIE9uIGRvaXQgYWxsb3VlciB2aWEgU0NhcmRGcmVlTWVtb3J5
IGNvbXBhdGlibGUgKHNvdXZlbnQgSGVhcEFsbG9jIHN1ciBsZSB0YXMgcGFyIGTDqWZhdXQp
DQoJCS8vIE1haXMgcG91ciDDqnRyZSByaWdvdXJldXgsIG9uIGFsbG91ZSB1bmUgem9uZSBx
dWUgbGUgQ1NQIHBvdXJyYSBsaWLDqXJlci4NCgkJTFBXU1RSKiBwcG1zelJkciA9IHJlaW50
ZXJwcmV0X2Nhc3Q8TFBXU1RSKj4obXN6UmVhZGVycyk7DQoJCSpwcG1zelJkciA9IChMUFdT
VFIpSGVhcEFsbG9jKEdldFByb2Nlc3NIZWFwKCksIEhFQVBfWkVST19NRU1PUlksIGxlbiAq
IHNpemVvZih3Y2hhcl90KSk7DQoJCVRyYWNlKFRSQUNFX0xFVkVMX1ZFUkJPU0UsIEwiICAg
W0RlYnVnXSBTQ2FyZExpc3RSZWFkZXJzVyA6IEFsbG9jIG9mICVwICVkIGJ5dGVzIiwgKnBw
bXN6UmRyLCBsZW4gKiBzaXplb2Yod2NoYXJfdCkpOw0KDQoJCWlmICgqcHBtc3pSZHIgPT0g
TlVMTCkNCgkJew0KCQkJcmV0dXJuIFNDQVJEX0VfTk9fTUVNT1JZOw0KCQl9DQoNCgkJbWVt
Y3B5KCpwcG1zelJkciwgcmVhZGVycywgbGVuICogc2l6ZW9mKHdjaGFyX3QpKTsNCgkJKnBj
Y2hSZWFkZXJzID0gbGVuOw0KCQlyZXR1cm4gU0NBUkRfU19TVUNDRVNTOw0KCX0NCg0KCS8v
IENhcyAzIDogQnVmZmVyIGZvdXJuaSBwYXIgbCd1dGlsaXNhdGV1cg0KCWlmICgqcGNjaFJl
YWRlcnMgPCBsZW4pDQoJew0KCQkqcGNjaFJlYWRlcnMgPSBsZW47DQoJCXJldHVybiBTQ0FS
RF9FX0lOU1VGRklDSUVOVF9CVUZGRVI7DQoJfQ0KDQoJbWVtY3B5KG1zelJlYWRlcnMsIHJl
YWRlcnMsIGxlbiAqIHNpemVvZih3Y2hhcl90KSk7DQoJKnBjY2hSZWFkZXJzID0gbGVuOw0K
DQoJcmV0dXJuIFNDQVJEX1NfU1VDQ0VTUzsNCn0NCg0KLy8gLS0tIEhvb2tzICJwYXNzLXRo
cm91Z2giIGZvcm1hdMOpcyBwb3VyIGxlIERlYnVnZ2luZyAtLS0NCg0KTE9ORyBXSU5BUEkg
TXNDcnlwdG9CYXNlOjpIb29rRXN0YWJsaXNoQ29udGV4dCgNCglfSW5fIERXT1JEIGR3U2Nv
cGUsDQoJX1Jlc2VydmVkXyBMUENWT0lEIHB2UmVzZXJ2ZWQxLA0KCV9SZXNlcnZlZF8gTFBD
Vk9JRCBwdlJlc2VydmVkMiwNCglfT3V0XyBMUFNDQVJEQ09OVEVYVCBwaENvbnRleHQpDQp7
DQoJVHJhY2UoVFJBQ0VfTEVWRUxfSU5GTywgTCItPiBbSG9va10gU0NhcmRFc3RhYmxpc2hD
b250ZXh0Iik7DQoJaWYgKHBoQ29udGV4dCkNCgl7DQoJCVVMT05HX1BUUiB1bmlxdWVJZCA9
IChVTE9OR19QVFIpR2V0VGlja0NvdW50NjQoKTsNCgkJVHJhY2UoVFJBQ0VfTEVWRUxfVkVS
Qk9TRSwgTCJjb250ZXh0OiAlcCIsIHVuaXF1ZUlkKTsNCgkJKnBoQ29udGV4dCA9IChTQ0FS
RENPTlRFWFQpdW5pcXVlSWQ7DQoJfQ0KCXJldHVybiBTQ0FSRF9TX1NVQ0NFU1M7DQp9DQoN
CkxPTkcgV0lOQVBJIE1zQ3J5cHRvQmFzZTo6SG9va1JlYWRDYWNoZVcoDQoJX0luXyBTQ0FS
RENPTlRFWFQgaENvbnRleHQsDQoJX0luXyBVVUlEKiBwQ2FyZElkZW50aWZpZXIsDQoJX0lu
XyBEV09SRCBGcmVzaG5lc3NDb3VudGVyLA0KCV9Jbl8gTFBXU1RSIHN6TG9va3VwTmFtZSwN
CglfT3V0X3dyaXRlc19ieXRlc18oKnBjYkRhdGFMZW4pIFBCWVRFIHBiRGF0YSwNCglfSW5v
dXRfIExQRFdPUkQgcGNiRGF0YUxlbikNCnsNCglUcmFjZShUUkFDRV9MRVZFTF9JTkZPLCBM
IiAgIFtEZWJ1Z10gU0NhcmRSZWFkQ2FjaGVXICVwOiBLZXk9JXMgcGNiRGF0YUxlbj0ldSAl
UyIsIGhDb250ZXh0LCBzekxvb2t1cE5hbWUsICpwY2JEYXRhTGVuLCAqcGNiRGF0YUxlbiA9
PSBTQ0FSRF9BVVRPQUxMT0NBVEUgPyAiU0NBUkRfQVVUT0FMTE9DQVRFIiA6ICIiKTsNCg0K
CWlmICghc3pMb29rdXBOYW1lIHx8ICFwY2JEYXRhTGVuKQ0KCQlyZXR1cm4gU0NBUkRfRV9J
TlZBTElEX1BBUkFNRVRFUjsNCg0KCS8vIDEuIFJlY2hlcmNoZSBkYW5zIHZvdHJlIHNpbXVs
YXRldXIgKGNmLiByw6lwb25zZSBwcsOpY8OpZGVudGUpDQoJYXV0byBpdCA9IGdfU2ltdWxh
dGVkQ2FjaGUuZmluZChzekxvb2t1cE5hbWUpOw0KCWlmIChpdCA9PSBnX1NpbXVsYXRlZENh
Y2hlLmVuZCgpKQ0KCXsNCgkJVHJhY2UoVFJBQ0VfTEVWRUxfSU5GTywgTCJTQ0FSRF9XX0NB
Q0hFX0lURU1fTk9UX0ZPVU5EIik7DQoJCXJldHVybiBTQ0FSRF9XX0NBQ0hFX0lURU1fTk9U
X0ZPVU5EOw0KCX0NCg0KCWNvbnN0IGF1dG8mIGVudHJ5ID0gaXQtPnNlY29uZDsNCglEV09S
RCBpbnRlcm5hbFNpemUgPSBzdGF0aWNfY2FzdDxEV09SRD4oZW50cnkuZGF0YS5zaXplKCkp
Ow0KDQoJLy8gMi4gR2VzdGlvbiBkZSBsJ0FVVE9BTExPQ0FURQ0KCWlmICgqcGNiRGF0YUxl
biA9PSBTQ0FSRF9BVVRPQUxMT0NBVEUpDQoJew0KCQlUcmFjZShUUkFDRV9MRVZFTF9JTkZP
LCBMIlNDQVJEX0FVVE9BTExPQ0FURSIpOw0KCQkvLyBPbiBhbGxvdWUgbGEgbcOpbW9pcmUg
KHNpbXVsZSBsZSBjb21wb3J0ZW1lbnQgZHUgc2VydmljZSBTbWFydCBDYXJkKQ0KCQkvLyBO
b3RlOiBEYW5zIHVuIHZyYWkgaG9vaywgb24gZGV2cmFpdCB1dGlsaXNlciBsYSBtw6ptZSBo
ZWFwIHF1ZSBTQ2FyZEZyZWVNZW1vcnkNCgkJRFdPUkQgYWxpZ25lZFNpemUgPSAoaW50ZXJu
YWxTaXplICsgMTUpICYgfjE1OyAvLyBBbGlnbsOpIHN1ciAxNiBvY3RldHMNCgkJUEJZVEUg
cEFsbG9jYXRlZCA9IChQQllURSlIZWFwQWxsb2MoR2V0UHJvY2Vzc0hlYXAoKSwgSEVBUF9a
RVJPX01FTU9SWSwgYWxpZ25lZFNpemUpOw0KCQlUcmFjZShUUkFDRV9MRVZFTF9WRVJCT1NF
LCBMIiAgIFtEZWJ1Z10gU0NhcmRSZWFkQ2FjaGVXIDogQWxsb2Mgb2YgJXAgJWQgYnl0ZXMi
LCBwQWxsb2NhdGVkLCBhbGlnbmVkU2l6ZSk7DQoJCWlmICghcEFsbG9jYXRlZCkNCgkJew0K
CQkJcmV0dXJuIFNDQVJEX0VfTk9fTUVNT1JZOw0KCQl9DQoNCgkJbWVtY3B5KHBBbGxvY2F0
ZWQsIGVudHJ5LmRhdGEuZGF0YSgpLCBpbnRlcm5hbFNpemUpOw0KDQoJCS8vIE9uIMOpY3Jp
dCBsZSBwb2ludGV1ciBhbGxvdcOpIMOgIGwnYWRyZXNzZSBwb2ludMOpZSBwYXIgcGJEYXRh
DQoJCS8vIHBiRGF0YSBlc3QgZW4gZmFpdCB1biBQQllURSogZMOpZ3Vpc8OpIGVuIFBCWVRF
DQoJCSooKFBCWVRFKilwYkRhdGEpID0gcEFsbG9jYXRlZDsNCgkJKnBjYkRhdGFMZW4gPSBp
bnRlcm5hbFNpemU7DQoNCgkJcmV0dXJuIFNDQVJEX1NfU1VDQ0VTUzsNCgl9DQoNCgkvLyAz
LiBDYXMgY2xhc3NpcXVlIChCdWZmZXIgTlVMTCBwb3VyIHRhaWxsZSBvdSBCdWZmZXIgcHLD
qS1hbGxvdcOpKQ0KCWlmIChwYkRhdGEgPT0gTlVMTCkNCgl7DQoJCSpwY2JEYXRhTGVuID0g
aW50ZXJuYWxTaXplOw0KCQlUcmFjZShUUkFDRV9MRVZFTF9JTkZPLCBMInNpemUgb25seSIp
Ow0KCQlyZXR1cm4gU0NBUkRfU19TVUNDRVNTOw0KCX0NCg0KCWlmICgqcGNiRGF0YUxlbiA8
IGludGVybmFsU2l6ZSkNCgl7DQoJCSpwY2JEYXRhTGVuID0gaW50ZXJuYWxTaXplOw0KCQlU
cmFjZShUUkFDRV9MRVZFTF9JTkZPLCBMIlNDQVJEX0VfSU5TVUZGSUNJRU5UX0JVRkZFUiIp
Ow0KCQlyZXR1cm4gU0NBUkRfRV9JTlNVRkZJQ0lFTlRfQlVGRkVSOw0KCX0NCg0KCW1lbWNw
eShwYkRhdGEsIGVudHJ5LmRhdGEuZGF0YSgpLCBpbnRlcm5hbFNpemUpOw0KCSpwY2JEYXRh
TGVuID0gaW50ZXJuYWxTaXplOw0KCVRyYWNlKFRSQUNFX0xFVkVMX0lORk8sIEwiU0NBUkRf
U19TVUNDRVNTIFNpemUgPSAlZCIsIGludGVybmFsU2l6ZSk7DQoNCglyZXR1cm4gU0NBUkRf
U19TVUNDRVNTOw0KfQ0KDQoNCg0KTE9ORyBXSU5BUEkgTXNDcnlwdG9CYXNlOjpIb29rV3Jp
dGVDYWNoZVcoDQoJX0luXyBTQ0FSRENPTlRFWFQgaENvbnRleHQsDQoJX0luXyBVVUlEKiBw
Q2FyZElkZW50aWZpZXIsDQoJX0luXyBEV09SRCBGcmVzaG5lc3NDb3VudGVyLA0KCV9Jbl8g
TFBXU1RSIHN6TG9va3VwTmFtZSwNCglfSW5fcmVhZHNfYnl0ZXNfKGNiRGF0YUxlbikgUEJZ
VEUgcGJEYXRhLA0KCV9Jbl8gRFdPUkQgY2JEYXRhTGVuKQ0Kew0KCVRyYWNlKFRSQUNFX0xF
VkVMX0lORk8sIEwiLT4gW0hvb2tdIFNDYXJkV3JpdGVDYWNoZVcgJXAgKE5hbWU6ICVzLCBT
aXplOiAldSkiLCBoQ29udGV4dCwgc3pMb29rdXBOYW1lLCBjYkRhdGFMZW4pOw0KDQoJaWYg
KCFzekxvb2t1cE5hbWUgfHwgKCFwYkRhdGEgJiYgY2JEYXRhTGVuID4gMCkpIHsNCgkJcmV0
dXJuIFNDQVJEX0VfSU5WQUxJRF9QQVJBTUVURVI7DQoJfQ0KDQoJLy8gT24gc3RvY2tlIGxl
cyBkb25uw6llcyBkYW5zIG5vdHJlIG1hcA0KCUNhY2hlRW50cnkgZW50cnk7DQoJZW50cnku
ZGF0YS5hc3NpZ24ocGJEYXRhLCBwYkRhdGEgKyBjYkRhdGFMZW4pOw0KCWdfU2ltdWxhdGVk
Q2FjaGVbc3pMb29rdXBOYW1lXSA9IGVudHJ5Ow0KDQoJcmV0dXJuIFNDQVJEX1NfU1VDQ0VT
UzsNCn0NCg0KTE9ORyBXSU5BUEkgTXNDcnlwdG9CYXNlOjpIb29rRGlzY29ubmVjdCgNCglf
SW5fIFNDQVJESEFORExFIGhDYXJkLA0KCV9Jbl8gRFdPUkQgZHdEaXNwb3NpdGlvbikNCnsN
CglUcmFjZShUUkFDRV9MRVZFTF9JTkZPLCBMIi0+IFtIb29rXSBTQ2FyZERpc2Nvbm5lY3Qg
JXAiLCBoQ2FyZCk7DQoJaWYgKCFGcm9tSGFuZGxlKGhDYXJkKSkgcmV0dXJuIFNDQVJEX0Vf
Q09NTV9EQVRBX0xPU1Q7DQoJcmV0dXJuIFNDQVJEX1NfU1VDQ0VTUzsNCn0NCg0KTE9ORyBX
SU5BUEkgTXNDcnlwdG9CYXNlOjpIb29rQmVnaW5UcmFucygNCglfSW5fIFNDQVJESEFORExF
IGhDYXJkKQ0Kew0KCVRyYWNlKFRSQUNFX0xFVkVMX0lORk8sIEwiLT4gW0hvb2tdIFNDYXJk
QmVnaW5UcmFuc2FjdGlvbiAlcCIsIGhDYXJkKTsNCglpZiAoIUZyb21IYW5kbGUoaENhcmQp
KSByZXR1cm4gU0NBUkRfRV9DT01NX0RBVEFfTE9TVDsNCglyZXR1cm4gU0NBUkRfU19TVUND
RVNTOw0KfQ0KDQpMT05HIFdJTkFQSSBNc0NyeXB0b0Jhc2U6Okhvb2tFbmRUcmFucygNCglf
SW5fIFNDQVJESEFORExFIGhDYXJkLA0KCV9Jbl8gRFdPUkQgZHdEaXNwb3NpdGlvbikNCnsN
CglUcmFjZShUUkFDRV9MRVZFTF9JTkZPLCBMIi0+IFtIb29rXSBTQ2FyZEVuZFRyYW5zYWN0
aW9uICVwIiwgaENhcmQpOw0KCWlmICghRnJvbUhhbmRsZShoQ2FyZCkpIHJldHVybiBTQ0FS
RF9FX0NPTU1fREFUQV9MT1NUOw0KCXJldHVybiBTQ0FSRF9TX1NVQ0NFU1M7DQp9DQoNCkxP
TkcgV0lOQVBJIE1zQ3J5cHRvQmFzZTo6SG9va1JlbGVhc2VDb250ZXh0KA0KCV9Jbl8gU0NB
UkRDT05URVhUIGhDb250ZXh0KQ0Kew0KCVRyYWNlKFRSQUNFX0xFVkVMX0lORk8sIEwiLT4g
W0hvb2tdIFNDYXJkUmVsZWFzZUNvbnRleHQgJXAiLCBoQ29udGV4dCk7DQoJcmV0dXJuIFND
QVJEX1NfU1VDQ0VTUzsNCn0NCg0KTE9ORyBXSU5BUEkgTXNDcnlwdG9CYXNlOjpIb29rRnJl
ZU1lbW9yeSgNCglfSW5fIFNDQVJEQ09OVEVYVCBoQ29udGV4dCwNCglfSW5fIExQQ1ZPSUQg
cHZNZW0pDQp7DQoJVHJhY2UoVFJBQ0VfTEVWRUxfSU5GTywgTCItPiBbSG9va10gU0NhcmRG
cmVlTWVtb3J5ICVwICVwIiwgaENvbnRleHQsIHB2TWVtKTsNCglIZWFwRnJlZShHZXRQcm9j
ZXNzSGVhcCgpLCAwLCAoTFBWT0lEKXB2TWVtKTsNCglyZXR1cm4gU0NBUkRfU19TVUNDRVNT
Ow0KfQ0KDQpMT05HIFdJTkFQSSBNc0NyeXB0b0Jhc2U6Okhvb2tDYW5jZWwoDQoJX0luXyBT
Q0FSRENPTlRFWFQgaENvbnRleHQpDQp7DQoJVHJhY2UoVFJBQ0VfTEVWRUxfSU5GTywgTCIt
PiBbSG9va10gU0NhcmRDYW5jZWwgJXAiLCBoQ29udGV4dCk7DQoJcmV0dXJuIFNDQVJEX1Nf
U1VDQ0VTUzsNCn0NCg0KTE9ORyBXSU5BUEkgTXNDcnlwdG9CYXNlOjpIb29rUmVjb25uZWN0
KA0KCV9Jbl8gU0NBUkRIQU5ETEUgaENhcmQsDQoJX0luXyBEV09SRCBkd1NoYXJlTW9kZSwN
CglfSW5fIERXT1JEIGR3UHJlZmVycmVkUHJvdG9jb2xzLA0KCV9Jbl8gRFdPUkQgZHdJbml0
aWFsaXphdGlvbiwNCglfT3V0X29wdF8gTFBEV09SRCBwZHdBY3RpdmVQcm90b2NvbCkNCnsN
CglUcmFjZShUUkFDRV9MRVZFTF9JTkZPLCBMIi0+IFtIb29rXSBTQ2FyZFJlY29ubmVjdCAl
cCIsIGhDYXJkKTsNCglpZiAoIUZyb21IYW5kbGUoaENhcmQpKSByZXR1cm4gU0NBUkRfRV9D
T01NX0RBVEFfTE9TVDsNCglyZXR1cm4gU0NBUkRfU19TVUNDRVNTOw0KfQ0KDQpMT05HIFdJ
TkFQSSBNc0NyeXB0b0Jhc2U6Okhvb2tBdWRpdCgNCglfSW5fIFNDQVJEQ09OVEVYVCBoQ29u
dGV4dCwNCglfSW5fIERXT1JEIGR3RXZlbnQpDQp7DQoJVHJhY2UoVFJBQ0VfTEVWRUxfSU5G
TywgTCItPiBbSG9va10gU0NhcmRBdWRpdCAlcCIsIGhDb250ZXh0KTsNCglyZXR1cm4gU0NB
UkRfU19TVUNDRVNTOw0KfQ0KDQpMT05HIFdJTkFQSSBNc0NyeXB0b0Jhc2U6Okhvb2tMaXN0
Q2FyZHNXKA0KCV9Jbl8gU0NBUkRDT05URVhUIGhDb250ZXh0LA0KCV9Jbl9vcHRfIExQQ0JZ
VEUgcGJBdHIsDQoJX0luX3JlYWRzX29wdF8oY2d1aWQpIExQQ0dVSUQgcmdxdWlkLA0KCV9J
bl8gRFdPUkQgY2d1aWQsDQoJX091dF93cml0ZXNfb3B0XygqcGNjaENhcmRzKSBMUFdTVFIg
bXN6Q2FyZHMsDQoJX0lub3V0XyBMUERXT1JEIHBjY2hDYXJkcykNCnsNCglUcmFjZShUUkFD
RV9MRVZFTF9JTkZPLCBMIi0+IFtIb29rXSBTQ2FyZExpc3RDYXJkc1cgJXAiLCBoQ29udGV4
dCk7DQoNCgkvLyBMZSBub20gb2ZmaWNpZWwgcG91ciBsZSBwcm9maWwgZ8OpbsOpcmlxdWUg
TWljcm9zb2Z0IEdJRFMNCgljb25zdCB3Y2hhcl90KiBzekNhcmROYW1lID0gTCJJZGVudGl0
eSBEZXZpY2UgKE1pY3Jvc29mdCBHZW5lcmljIFByb2ZpbGUpXDAiOw0KDQoJLy8gVGFpbGxl
IDogbG9uZ3VldXIgZHUgdGV4dGUgKyAxIG51bCBkZSBmaW4gZGUgY2hhw65uZSArIDEgbnVs
IGRlIGZpbiBkZSBtdWx0aS1zdHJpbmcNCglEV09SRCBsZW4gPSAoRFdPUkQpd2NzbGVuKHN6
Q2FyZE5hbWUpICsgMjsNCg0KCWlmIChwY2NoQ2FyZHMgPT0gTlVMTCkNCgl7DQoJCXJldHVy
biBTQ0FSRF9FX0lOVkFMSURfUEFSQU1FVEVSOw0KCX0NCg0KCS8vIENhcyAxIDogUmVxdcOq
dGUgZGUgdGFpbGxlDQoJaWYgKG1zekNhcmRzID09IE5VTEwpDQoJew0KCQkqcGNjaENhcmRz
ID0gbGVuOw0KCQlUcmFjZShUUkFDRV9MRVZFTF9WRVJCT1NFLCBMIiAgIFtJbmZvXSBTQ2Fy
ZExpc3RDYXJkc1cgOiBSZXRvdXJuZSB0YWlsbGUgcmVxdWlzZSAlZCIsIGxlbik7DQoJCXJl
dHVybiBTQ0FSRF9TX1NVQ0NFU1M7DQoJfQ0KDQoJLy8gQ2FzIDIgOiBBdXRvLWFsbG9jYXRp
b24gKFNDQVJEX0FVVE9BTExPQ0FURSA9IC0xKQ0KCWlmICgqcGNjaENhcmRzID09IFNDQVJE
X0FVVE9BTExPQ0FURSkNCgl7DQoJCVRyYWNlKFRSQUNFX0xFVkVMX0lORk8sIEwiICAgW0lu
Zm9dIFNDYXJkTGlzdENhcmRzVyA6IEFsbG9jYXRpb24gYXV0b21hdGlxdWUiKTsNCg0KCQlM
UFdTVFIqIHBwbXN6Q2FyZHMgPSByZWludGVycHJldF9jYXN0PExQV1NUUio+KG1zekNhcmRz
KTsNCgkJKnBwbXN6Q2FyZHMgPSAoTFBXU1RSKUhlYXBBbGxvYyhHZXRQcm9jZXNzSGVhcCgp
LCBIRUFQX1pFUk9fTUVNT1JZLCBsZW4gKiBzaXplb2Yod2NoYXJfdCkpOw0KCQlUcmFjZShU
UkFDRV9MRVZFTF9WRVJCT1NFLCBMIiAgIFtEZWJ1Z10gU0NhcmRMaXN0Q2FyZHNXIDogQWxs
b2Mgb2YgJXAgJWQgYnl0ZXMiLCAqcHBtc3pDYXJkcywgbGVuICogc2l6ZW9mKHdjaGFyX3Qp
KTsNCgkJaWYgKCpwcG1zekNhcmRzID09IE5VTEwpDQoJCXsNCgkJCXJldHVybiBTQ0FSRF9F
X05PX01FTU9SWTsNCgkJfQ0KDQoJCW1lbWNweSgqcHBtc3pDYXJkcywgc3pDYXJkTmFtZSwg
bGVuICogc2l6ZW9mKHdjaGFyX3QpKTsNCgkJKnBjY2hDYXJkcyA9IGxlbjsNCgkJcmV0dXJu
IFNDQVJEX1NfU1VDQ0VTUzsNCgl9DQoNCgkvLyBDYXMgMyA6IEJ1ZmZlciBmb3VybmkgdHJv
cCBwZXRpdA0KCWlmICgqcGNjaENhcmRzIDwgbGVuKQ0KCXsNCgkJVHJhY2UoVFJBQ0VfTEVW
RUxfV0FSTklORywgTCIgICBbIV0gU0NhcmRMaXN0Q2FyZHNXIDogQnVmZmVyIGluc3VmZmlz
YW50ICglZCA8ICVkKSIsICpwY2NoQ2FyZHMsIGxlbik7DQoJCSpwY2NoQ2FyZHMgPSBsZW47
DQoJCXJldHVybiBTQ0FSRF9FX0lOU1VGRklDSUVOVF9CVUZGRVI7DQoJfQ0KDQoJLy8gQ2Fz
IDQgOiBDb3BpZSBkYW5zIGxlIGJ1ZmZlciBmb3VybmkNCgltZW1jcHkobXN6Q2FyZHMsIHN6
Q2FyZE5hbWUsIGxlbiAqIHNpemVvZih3Y2hhcl90KSk7DQoJKnBjY2hDYXJkcyA9IGxlbjsN
Cg0KCXJldHVybiBTQ0FSRF9TX1NVQ0NFU1M7DQp9DQoNCkxPTkcgV0lOQVBJIE1zQ3J5cHRv
QmFzZTo6SG9va0dldENhcmRUeXBlUHJvdmlkZXJOYW1lVygNCglfSW5fIFNDQVJEQ09OVEVY
VCBoQ29udGV4dCwNCglfSW5fIExQQ1dTVFIgc3pDYXJkTmFtZSwNCglfSW5fIERXT1JEIGR3
UHJvdmlkZXJJZCwNCglfT3V0X3dyaXRlc19vcHRfKCpwY2NoUHJvdmlkZXIpIExQV1NUUiBz
elByb3ZpZGVyLA0KCV9Jbm91dF8gTFBEV09SRCBwY2NoUHJvdmlkZXIpDQp7DQoJVHJhY2Uo
VFJBQ0VfTEVWRUxfSU5GTywgTCItPiBbSG9va10gU0NhcmRHZXRDYXJkVHlwZVByb3ZpZGVy
TmFtZVcgJXAgKFByb3ZpZGVySWQ6ICVkKSIsIGhDb250ZXh0LCBkd1Byb3ZpZGVySWQpOw0K
DQoJLy8gTm91cyBuZSByw6lwb25kb25zIHF1ZSBwb3VyIGxlIG1vZHVsZSBkZSBjYXJ0ZSAo
TWluaWRyaXZlcikNCglpZiAoZHdQcm92aWRlcklkICE9IFNDQVJEX1BST1ZJREVSX0NBUkRf
TU9EVUxFKQ0KCXsNCgkJVHJhY2UoVFJBQ0VfTEVWRUxfV0FSTklORywgTCIgICBbIV0gUHJv
dmlkZXJJZCAlZCBub24gZ8OpcsOpIChvbiBhdHRlbmQgU0NBUkRfUFJPVklERVJfQ0FSRF9N
T0RVTEU9MHgwMSkiLCBkd1Byb3ZpZGVySWQpOw0KCQlyZXR1cm4gU0NBUkRfRV9JTlZBTElE
X1ZBTFVFOw0KCX0NCg0KCS8vIExlIG5vbSBkZSBsYSBETEwgZHUgTWluaWRyaXZlciBHSURT
IHN0YW5kYXJkDQoJY29uc3Qgd2NoYXJfdCogc3pEbGxOYW1lID0gTCJtc2NsbWQuZGxsIjsN
CgkvL2NvbnN0IHdjaGFyX3QqIHN6RGxsTmFtZSA9IEwiR2lkc1Rlc3RzLmRsbCI7ICAvLyB0
YSBETEwgZGUgdGVzdA0KCURXT1JEIGxlbiA9IChEV09SRCl3Y3NsZW4oc3pEbGxOYW1lKSAr
IDE7IC8vICsxIHBvdXIgbGUgbnVsIGZpbmFsDQoNCglpZiAocGNjaFByb3ZpZGVyID09IE5V
TEwpDQoJew0KCQlyZXR1cm4gU0NBUkRfRV9JTlZBTElEX1BBUkFNRVRFUjsNCgl9DQoNCgkv
LyBDYXMgMSA6IERlbWFuZGUgZGUgdGFpbGxlDQoJaWYgKHN6UHJvdmlkZXIgPT0gTlVMTCkN
Cgl7DQoJCSpwY2NoUHJvdmlkZXIgPSBsZW47DQoJCXJldHVybiBTQ0FSRF9TX1NVQ0NFU1M7
DQoJfQ0KDQoJLy8gQ2FzIDIgOiBBdXRvLWFsbG9jYXRpb24NCglpZiAoKnBjY2hQcm92aWRl
ciA9PSBTQ0FSRF9BVVRPQUxMT0NBVEUpDQoJew0KCQlUcmFjZShUUkFDRV9MRVZFTF9JTkZP
LCBMIiAgIFtJbmZvXSBTQ2FyZEdldENhcmRUeXBlUHJvdmlkZXJOYW1lVyA6IEFsbG9jYXRp
b24gYXV0b21hdGlxdWUiKTsNCg0KCQlMUFdTVFIqIHBwc3pQcm92aWRlciA9IHJlaW50ZXJw
cmV0X2Nhc3Q8TFBXU1RSKj4oc3pQcm92aWRlcik7DQoJCSpwcHN6UHJvdmlkZXIgPSAoTFBX
U1RSKUhlYXBBbGxvYyhHZXRQcm9jZXNzSGVhcCgpLCBIRUFQX1pFUk9fTUVNT1JZLCBsZW4g
KiBzaXplb2Yod2NoYXJfdCkpOw0KCQlUcmFjZShUUkFDRV9MRVZFTF9WRVJCT1NFLCBMIiAg
IFtEZWJ1Z10gU0NhcmRHZXRDYXJkVHlwZVByb3ZpZGVyTmFtZVcgOiBBbGxvYyBvZiAlcCAl
ZCBieXRlcyIsICpwcHN6UHJvdmlkZXIsIGxlbiAqIHNpemVvZih3Y2hhcl90KSk7DQoJCWlm
ICgqcHBzelByb3ZpZGVyID09IE5VTEwpDQoJCXsNCgkJCXJldHVybiBTQ0FSRF9FX05PX01F
TU9SWTsNCgkJfQ0KDQoJCW1lbWNweSgqcHBzelByb3ZpZGVyLCBzekRsbE5hbWUsIGxlbiAq
IHNpemVvZih3Y2hhcl90KSk7DQoJCSpwY2NoUHJvdmlkZXIgPSBsZW47DQoJCXJldHVybiBT
Q0FSRF9TX1NVQ0NFU1M7DQoJfQ0KDQoJLy8gQ2FzIDMgOiBCdWZmZXIgdHJvcCBwZXRpdA0K
CWlmICgqcGNjaFByb3ZpZGVyIDwgbGVuKQ0KCXsNCgkJVHJhY2UoVFJBQ0VfTEVWRUxfV0FS
TklORywgTCIgICBbIV0gU0NhcmRHZXRDYXJkVHlwZVByb3ZpZGVyTmFtZVcgOiBCdWZmZXIg
dHJvcCBwZXRpdCAoJWQgPCAlZCkiLCAqcGNjaFByb3ZpZGVyLCBsZW4pOw0KCQkqcGNjaFBy
b3ZpZGVyID0gbGVuOw0KCQlyZXR1cm4gU0NBUkRfRV9JTlNVRkZJQ0lFTlRfQlVGRkVSOw0K
CX0NCg0KCS8vIENhcyA0IDogQ29waWUgc3RhbmRhcmQNCgltZW1jcHkoc3pQcm92aWRlciwg
c3pEbGxOYW1lLCBsZW4gKiBzaXplb2Yod2NoYXJfdCkpOw0KCSpwY2NoUHJvdmlkZXIgPSBs
ZW47DQoNCglUcmFjZShUUkFDRV9MRVZFTF9JTkZPLCBMIiAgIFtPS10gU0NhcmRHZXRDYXJk
VHlwZVByb3ZpZGVyTmFtZVcgOiBSZW52b2llICdtc2NsbWQuZGxsJyIpOw0KCXJldHVybiBT
Q0FSRF9TX1NVQ0NFU1M7DQp9DQoNCkxPTkcgV0lOQVBJIE1zQ3J5cHRvQmFzZTo6SG9va0dl
dFJlYWRlckljb25XKA0KCV9Jbl8gU0NBUkRDT05URVhUIGhDb250ZXh0LA0KCV9Jbl8gTFBD
V1NUUiBzelJlYWRlck5hbWUsDQoJX091dF93cml0ZXNfYnl0ZXNfb3B0XygqcGNiSWNvbikg
TFBCWVRFIHBiSWNvbiwNCglfSW5vdXRfb3B0XyBMUERXT1JEIHBjYkljb24pDQp7DQoJVHJh
Y2UoVFJBQ0VfTEVWRUxfSU5GTywgTCItPiBbSG9va10gU0NhcmRHZXRSZWFkZXJJY29uVyAl
cCIsIGhDb250ZXh0KTsNCglyZXR1cm4gU0NBUkRfRV9OT19TRVJWSUNFOw0KfQ0KDQpMT05H
IFdJTkFQSSBNc0NyeXB0b0Jhc2U6Okhvb2tHZXREZXZpY2VUeXBlSWRXKA0KCV9Jbl8gU0NB
UkRDT05URVhUIGhDb250ZXh0LA0KCV9Jbl8gTFBDV1NUUiBzelJlYWRlck5hbWUsDQoJX091
dF8gTFBEV09SRCBwZHdEZXZpY2VUeXBlSWQpDQp7DQoJVHJhY2UoVFJBQ0VfTEVWRUxfSU5G
TywgTCItPiBbSG9va10gU0NhcmRHZXREZXZpY2VUeXBlSWRXICVwIiwgaENvbnRleHQpOw0K
CWlmIChwZHdEZXZpY2VUeXBlSWQpDQoJCSpwZHdEZXZpY2VUeXBlSWQgPSBTQ0FSRF9SRUFE
RVJfVFlQRV9VU0I7DQoJcmV0dXJuIFNDQVJEX1NfU1VDQ0VTUzsNCn0NCg0KLy8gVHlwZWRl
ZiBkZSBsYSBmb25jdGlvbiByw6llbGxlDQp0eXBlZGVmIERXT1JEKFdJTkFQSSogUEZOX0Ny
ZWRVSVByb21wdEZvcldpbmRvd3NDcmVkZW50aWFscykoDQoJX0luX29wdF8gIFBDUkVEVUlf
SU5GT1cgcFVpSW5mbywNCglfSW5fICAgICAgRFdPUkQgICAgICAgICBkd0F1dGhFcnJvciwN
CglfSW5vdXRfICAgVUxPTkcqIHB1bEF1dGhQYWNrYWdlLA0KCV9Jbl9vcHRfICBMUENWT0lE
ICAgICAgIHB2SW5BdXRoQnVmZmVyLA0KCV9Jbl8gICAgICBVTE9ORyAgICAgICAgIHVsSW5B
dXRoQnVmZmVyU2l6ZSwNCglfT3V0XyAgICAgTFBWT0lEKiBwcHZPdXRBdXRoQnVmZmVyLA0K
CV9PdXRfICAgICBVTE9ORyogcHVsT3V0QXV0aEJ1ZmZlclNpemUsDQoJX0lub3V0XyAgIEJP
T0wqIHBmU2F2ZSwNCglfSW5fICAgICAgRFdPUkQgICAgICAgICBkd0ZsYWdzDQoJKTsNCg0K
dHlwZWRlZiBzdHJ1Y3QgX0tFUkJfU01BUlRDQVJEX0NTUF9JTkZPIHsNCglEV09SRCBkd0Nz
cEluZm9MZW47DQoJRFdPUkQgTWVzc2FnZVR5cGU7DQoJdW5pb24gew0KCQlQVk9JRCAgIENv
bnRleHRJbmZvcm1hdGlvbjsNCgkJVUxPTkc2NCBTcGFjZUhvbGRlckZvcldvdzY0Ow0KCX07
DQoJRFdPUkQgZmxhZ3M7DQoJRFdPUkQgS2V5U3BlYzsNCglVTE9ORyBuQ2FyZE5hbWVPZmZz
ZXQ7DQoJVUxPTkcgblJlYWRlck5hbWVPZmZzZXQ7DQoJVUxPTkcgbkNvbnRhaW5lck5hbWVP
ZmZzZXQ7DQoJVUxPTkcgbkNTUE5hbWVPZmZzZXQ7DQoJVENIQVIgYkJ1ZmZlcjsNCn0gS0VS
Ql9TTUFSVENBUkRfQ1NQX0lORk8sICogUEtFUkJfU01BUlRDQVJEX0NTUF9JTkZPOw0KDQpE
V09SRCBXSU5BUEkgTXNDcnlwdG9CYXNlOjpIb29rQ3JlZFVJUHJvbXB0Rm9yV2luZG93c0Ny
ZWRlbnRpYWxzKA0KCV9Jbl9vcHRfICBQQ1JFRFVJX0lORk9XIHBVaUluZm8sDQoJX0luXyAg
ICAgIERXT1JEICAgICAgICAgZHdBdXRoRXJyb3IsDQoJX0lub3V0XyAgIFVMT05HKiBwdWxB
dXRoUGFja2FnZSwNCglfSW5fb3B0XyAgTFBDVk9JRCAgICAgICBwdkluQXV0aEJ1ZmZlciwN
CglfSW5fICAgICAgVUxPTkcgICAgICAgICB1bEluQXV0aEJ1ZmZlclNpemUsDQoJX091dF8g
ICAgIExQVk9JRCogcHB2T3V0QXV0aEJ1ZmZlciwNCglfT3V0XyAgICAgVUxPTkcqIHB1bE91
dEF1dGhCdWZmZXJTaXplLA0KCV9Jbm91dF8gICBCT09MKiBwZlNhdmUsDQoJX0luXyAgICAg
IERXT1JEICAgICAgICAgZHdGbGFncykNCnsNCglUcmFjZShUUkFDRV9MRVZFTF9JTkZPLCBM
Ii0+IFtIb29rXSBDcmVkVUlQcm9tcHRGb3JXaW5kb3dzQ3JlZGVudGlhbHMiKTsNCg0KCWF1
dG8gcGZuUmVhbCA9IEdldE9yaWdpbmFsPFBGTl9DcmVkVUlQcm9tcHRGb3JXaW5kb3dzQ3Jl
ZGVudGlhbHM+KA0KCQkiQ3JlZFVJUHJvbXB0Rm9yV2luZG93c0NyZWRlbnRpYWxzVyIpOw0K
DQoJUENBUkRfREFUQSBkYXRhID0gKFBDQVJEX0RBVEEpcHVsQXV0aFBhY2thZ2U7DQoJLy8g
VsOpcmlmaWVyIHF1ZSBjJ2VzdCBiaWVuIHVuIGFwcGVsIEJhc2VDU1AgcG91ciBQSU4gc21h
cnQgY2FyZA0KCS8vIChmbGFncyA9IENSRURVSVdJTl9JTl9DUkVEX09OTFkgPSAweDIwKQ0K
CWlmICghKGR3RmxhZ3MgJiBDUkVEVUlXSU5fSU5fQ1JFRF9PTkxZKSB8fCAhcHZJbkF1dGhC
dWZmZXIgfHwgIXBwdk91dEF1dGhCdWZmZXIpDQoJew0KCQkvLyBQYXMgdW4gYXBwZWwgUElO
IHNtYXJ0IGNhcmQgLSBsYWlzc2VyIHBhc3Nlcg0KCQlEV09SRCBzdGF0dXMgPSBwZm5SZWFs
KA0KCQkJcFVpSW5mbywgZHdBdXRoRXJyb3IsIHB1bEF1dGhQYWNrYWdlLA0KCQkJcHZJbkF1
dGhCdWZmZXIsIHVsSW5BdXRoQnVmZmVyU2l6ZSwNCgkJCXBwdk91dEF1dGhCdWZmZXIsIHB1
bE91dEF1dGhCdWZmZXJTaXplLA0KCQkJcGZTYXZlLCBkd0ZsYWdzKTsNCg0KCQlLRVJCX0NF
UlRJRklDQVRFX0xPR09OKiBkZWJ1Z2xvZ29uID0gKEtFUkJfQ0VSVElGSUNBVEVfTE9HT04q
KSpwcHZPdXRBdXRoQnVmZmVyOw0KCQlyZXR1cm4gc3RhdHVzOw0KCX0NCg0KCVRyYWNlKFRS
QUNFX0xFVkVMX0lORk8sIEwiICAgW0hvb2tdIEluamVjdGlvbiBQSU4gYXV0b21hdGlxdWUg
KGJ5cGFzcyBkaWFsb2cpIik7DQoJS0VSQl9DRVJUSUZJQ0FURV9MT0dPTiogZGVidWdsb2dv
biA9IChLRVJCX0NFUlRJRklDQVRFX0xPR09OKilwdkluQXV0aEJ1ZmZlcjsNCgkvLyBMZSBi
dWZmZXIgZCdlbnRyw6llIGVzdCB1biBLRVJCX0NFUlRJRklDQVRFX0xPR09ODQoJLy8gT24g
ZG9pdCBjb25zdHJ1aXJlIGxlIGJ1ZmZlciBkZSBzb3J0aWUgYXZlYyBsZSBQSU4gaW5qZWN0
w6kNCgkvLyBMZSBidWZmZXIgZGUgc29ydGllIGVzdCBpZGVudGlxdWUgw6AgbCdlbnRyw6ll
IG1haXMgYXZlYyBsZSBQSU4gcmVuc2VpZ27DqQ0KDQoJLy8gUsOpY3Vww6lyZXIgbGUgUElO
IGRlIHRlc3QgY29uZmlndXLDqQ0KCS8vTFBDV1NUUiB3c3pQaW4gPSBMIjEyMzQ1Njc4Ijsg
ICAvLyB0b24gUElOIGRlIHRlc3QsIGV4OiBMIjEyMzQ1Njc4Ig0KCUxQQ1NUUiAgc3pQaW4g
PSAiMTIzNDU2NzgiOyAgLy8gdmVyc2lvbiBBTlNJIDogIjEyMzQ1Njc4Ig0KCURXT1JEICAg
Y2JQaW4gPSAoRFdPUkQpc3RybGVuKHN6UGluKTsNCg0KCS8vIEFsbG91ZXIgbGUgYnVmZmVy
IGRlIHNvcnRpZSA9IGNvcGllIGR1IGJ1ZmZlciBkJ2VudHLDqWUNCgkvLyBCYXNlQ1NQIGxp
YsOocmUgY2UgYnVmZmVyIGF2ZWMgQ29UYXNrTWVtRnJlZQ0KCVVMT05HIGNiT3V0ID0gdWxJ
bkF1dGhCdWZmZXJTaXplICsgY2JQaW4gKyBzaXplb2YoQ0hBUik7DQoJTFBCWVRFIHBiT3V0
ID0gKExQQllURSlDb1Rhc2tNZW1BbGxvYyhjYk91dCk7DQoJaWYgKCFwYk91dCkNCgkJcmV0
dXJuIEVSUk9SX09VVE9GTUVNT1JZOw0KDQoJbWVtY3B5KHBiT3V0LCBwdkluQXV0aEJ1ZmZl
ciwgdWxJbkF1dGhCdWZmZXJTaXplKTsNCg0KCS8vIExlIEtFUkJfQ0VSVElGSUNBVEVfTE9H
T04gZW4gc29ydGllIGRvaXQgYXZvaXIgbGUgUElOIHJlbnNlaWduw6kNCgkvLyBkYW5zIGxl
IGNoYW1wIFBpbiAoVU5JQ09ERV9TVFJJTkcpIG91IGRpcmVjdGVtZW50IGVuIEFOU0kgc2Vs
b24gbGEgdmVyc2lvbg0KCS8vIFN0cnVjdHVyZSA6IEtFUkJfQ0VSVElGSUNBVEVfTE9HT04g
Y29tbWVuY2UgcGFyIE1lc3NhZ2VUeXBlIChEV09SRCkNCgkvLyBzdWl2aSBkZXMgY2hhbXBz
IC0gbGUgUElOIGVzdCDDoCBsYSBmaW4gZHUgYnVmZmVyIGVuIEFOU0kNCgkvLw0KCS8vIEZv
cm1hdCBhdHRlbmR1IHBhciBCYXNlQ1NQIGVuIHNvcnRpZSA6DQoJLy8gICBbIEtFUkJfQ0VS
VElGSUNBVEVfTE9HT04gXVsgZG9ubsOpZXMgdmFyaWFibGVzIF1bIFBJTiBBTlNJIFwwIF0N
CgkvLw0KCS8vIE9uIHBhdGNoIGxlIGNoYW1wIFBpbi5CdWZmZXIgcG91ciBwb2ludGVyIHZl
cnMgbGEgZmluIGR1IGJ1ZmZlcg0KCS8vIGV0IFBpbi5MZW5ndGggLyBQaW4uTWF4aW11bUxl
bmd0aCBhdmVjIGxhIHRhaWxsZSBkdSBQSU4gQU5TSQ0KDQoJS0VSQl9DRVJUSUZJQ0FURV9M
T0dPTiogcExvZ29uID0gKEtFUkJfQ0VSVElGSUNBVEVfTE9HT04qKXBiT3V0Ow0KCUtFUkJf
U01BUlRDQVJEX0NTUF9JTkZPKiBwSW5mbyA9IChLRVJCX1NNQVJUQ0FSRF9DU1BfSU5GTyop
KChQQllURSlwdkluQXV0aEJ1ZmZlciArIChVTE9OR19QVFIpKChLRVJCX0NFUlRJRklDQVRF
X0xPR09OKilwdkluQXV0aEJ1ZmZlciktPkNzcERhdGEpOw0KCS8vIExlIFBJTiBBTlNJIGVz
dCDDqWNyaXQgw6AgbGEgZmluIGR1IGJ1ZmZlciBkZSBzb3J0aWUNCglMUEJZVEUgcGJQaW5E
c3QgPSBwYk91dCArIHVsSW5BdXRoQnVmZmVyU2l6ZTsNCgltZW1jcHkocGJQaW5Ec3QsIHN6
UGluLCBjYlBpbiArIDEpOw0KDQoJLy8gUmVuc2VpZ25lciBsZSBVTklDT0RFX1NUUklORyBQ
aW4gYXZlYyBsb25ndWV1cnMgQU5TSQ0KCS8vIChCYXNlQ1NQIGludGVycHLDqHRlIFBpbiBj
b21tZSBBTlNJIG1hbGdyw6kgbGUgdHlwZSBVTklDT0RFX1NUUklORykNCglwTG9nb24tPlBp
bi5MZW5ndGggPSAoVVNIT1JUKWNiUGluOw0KCXBMb2dvbi0+UGluLk1heGltdW1MZW5ndGgg
PSAoVVNIT1JUKShjYlBpbiArIDEpOw0KCXBMb2dvbi0+UGluLkJ1ZmZlciA9IChQV1NUUiko
KFVMT05HX1BUUilwYlBpbkRzdCAtIChVTE9OR19QVFIpcExvZ29uKTsgLy8gb2Zmc2V0IHLD
qWVsIGVuIG3DqW1vaXJlDQoNCgkqcHB2T3V0QXV0aEJ1ZmZlciA9IHBiT3V0Ow0KCSpwdWxP
dXRBdXRoQnVmZmVyU2l6ZSA9IGNiT3V0Ow0KDQoJaWYgKHBmU2F2ZSkgKnBmU2F2ZSA9IEZB
TFNFOw0KDQoJVHJhY2UoVFJBQ0VfTEVWRUxfSU5GTywgTCIgICBbSG9va10gUElOIGluamVj
dMOpICglZCBieXRlcykiLCBjYlBpbik7DQoJcmV0dXJuIEVSUk9SX1NVQ0NFU1M7DQp9DQoN
CkhBTkRMRSBXSU5BUEkgTXNDcnlwdG9CYXNlOjpIb29rQWNjZXNzU3RhcnRlZEV2ZW50KCkN
CnsNCglUcmFjZShUUkFDRV9MRVZFTF9JTkZPLCBMIi0+IFtIb29rXSBTQ2FyZEFjY2Vzc1N0
YXJ0ZWRFdmVudCIpOw0KDQoJc3RhdGljIEhBTkRMRSBoRXZlbnQgPSBDcmVhdGVFdmVudChO
VUxMLCBUUlVFLCBUUlVFLCBMIkdsb2JhbFxcU0NhcmQkUmVzTWdyRGVmYXVsdFN0YXJ0ZWQk
Iik7DQoNCglyZXR1cm4gaEV2ZW50Ow0KfQ0KDQpWT0lEIFdJTkFQSSBNc0NyeXB0b0Jhc2U6
Okhvb2tSZWxlYXNlU3RhcnRlZEV2ZW50KCkNCnsNCglUcmFjZShUUkFDRV9MRVZFTF9JTkZP
LCBMIi0+IFtIb29rXSBTQ2FyZFJlbGVhc2VTdGFydGVkRXZlbnQiKTsNCn0NCg0KTE9ORyBX
SU5BUEkgTXNDcnlwdG9CYXNlOjpIb29rR2V0UmVhZGVyRGV2aWNlSW5zdGFuY2VJZFcoDQoJ
X0luXyBTQ0FSRENPTlRFWFQgaENvbnRleHQsDQoJX0luXyBMUENXU1RSIHN6UmVhZGVyTmFt
ZSwNCglfT3V0X3dyaXRlc19vcHRfKCpwY2NoRGV2aWNlSW5zdGFuY2VJZCkgTFBXU1RSIHN6
RGV2aWNlSW5zdGFuY2VJZCwNCglfSW5vdXRfIExQRFdPUkQgcGNjaERldmljZUluc3RhbmNl
SWQpDQp7DQoJVHJhY2UoVFJBQ0VfTEVWRUxfSU5GTywgTCItPiBbSG9va10gU0NhcmRHZXRS
ZWFkZXJEZXZpY2VJbnN0YW5jZUlkVyAoJXMpIiwgc3pSZWFkZXJOYW1lKTsNCg0KCS8vIElk
ZW50aWZpYW50IGQnaW5zdGFuY2UgZmFjdGljZSBwb3VyIHNpbXVsYXRldXINCgljb25zdCB3
Y2hhcl90KiBtb2NrSWQgPSBMIlVTQlxcVklEXzAwMDAmUElEXzAwMDBcXEZBS0VfUkVBREVS
XzAxIjsNCglEV09SRCBkd0xlbiA9IChEV09SRCl3Y3NsZW4obW9ja0lkKSArIDE7DQoNCglp
ZiAoc3pEZXZpY2VJbnN0YW5jZUlkID09IE5VTEwpIHsNCgkJKnBjY2hEZXZpY2VJbnN0YW5j
ZUlkID0gZHdMZW47DQoJCXJldHVybiBTQ0FSRF9TX1NVQ0NFU1M7DQoJfQ0KDQoJaWYgKCpw
Y2NoRGV2aWNlSW5zdGFuY2VJZCA8IGR3TGVuKSB7DQoJCSpwY2NoRGV2aWNlSW5zdGFuY2VJ
ZCA9IGR3TGVuOw0KCQlyZXR1cm4gU0NBUkRfRV9JTlNVRkZJQ0lFTlRfQlVGRkVSOw0KCX0N
Cg0KCXdjc2NweV9zKHN6RGV2aWNlSW5zdGFuY2VJZCwgKnBjY2hEZXZpY2VJbnN0YW5jZUlk
LCBtb2NrSWQpOw0KCSpwY2NoRGV2aWNlSW5zdGFuY2VJZCA9IGR3TGVuOw0KDQoJcmV0dXJu
IFNDQVJEX1NfU1VDQ0VTUzsNCn0NCg0KUEZOX0NBUkRfQUNRVUlSRV9DT05URVhUIE1zQ3J5
cHRvQmFzZTo6c19wZm5PcmlnaW5hbENhcmRBY3F1aXJlQ29udGV4dCA9IG51bGxwdHI7DQpQ
Rk5fQ0FSRF9SRUFEX0ZJTEUgTXNDcnlwdG9CYXNlOjpzX3Bmbk9yaWdpbmFsQ2FyZFJlYWRG
aWxlID0gbnVsbHB0cjsNClBGTl9DU1BfQ0FDSEVfTE9PS1VQX0ZJTEUgTXNDcnlwdG9CYXNl
OjpzX3Bmbk9yaWdpbmFsQ3NwQ2FjaGVMb29rdXBGaWxlID0gbnVsbHB0cjsNClBGTl9DU1Bf
Q0FDSEVfREVMRVRFX0ZJTEUgTXNDcnlwdG9CYXNlOjpzX3Bmbk9yaWdpbmFsQ3NwQ2FjaGVE
ZWxldGVGaWxlID0gbnVsbHB0cjsNClBGTl9DU1BfQ0FDSEVfQUREX0ZJTEUgICAgTXNDcnlw
dG9CYXNlOjpzX3Bmbk9yaWdpbmFsQ3NwQ2FjaGVBZGRGaWxlID0gbnVsbHB0cjsNClBGTl9D
U1BfQUxMT0MgICAgICAgICAgICAgTXNDcnlwdG9CYXNlOjpzX3Bmbk9yaWdpbmFsQ3NwQWxs
b2MgPSBudWxscHRyOw0KUEZOX0NTUF9SRUFMTE9DICAgICAgICAgICBNc0NyeXB0b0Jhc2U6
OnNfcGZuT3JpZ2luYWxDc3BSZWFsbG9jID0gbnVsbHB0cjsNClBGTl9DU1BfRlJFRSAgICAg
ICAgICAgICAgTXNDcnlwdG9CYXNlOjpzX3Bmbk9yaWdpbmFsQ3NwRnJlZSA9IG51bGxwdHI7
DQoNCg0KVk9JRCBNc0NyeXB0b0Jhc2U6OlNldHVwTWluaWRyaXZlckhvb2tzKF9Jbl8gUENB
UkRfREFUQSAgcENhcmREYXRhKQ0Kew0KCWlmIChwQ2FyZERhdGEtPnBmbkNzcENhY2hlTG9v
a3VwRmlsZSAhPSBNc0NyeXB0b0Jhc2U6Okhvb2tDc3BDYWNoZUxvb2t1cEZpbGUpDQoJew0K
CQlzX3Bmbk9yaWdpbmFsQ3NwQ2FjaGVMb29rdXBGaWxlID0gcENhcmREYXRhLT5wZm5Dc3BD
YWNoZUxvb2t1cEZpbGU7DQoJCXBDYXJkRGF0YS0+cGZuQ3NwQ2FjaGVMb29rdXBGaWxlID0g
TXNDcnlwdG9CYXNlOjpIb29rQ3NwQ2FjaGVMb29rdXBGaWxlOw0KDQoJCXNfcGZuT3JpZ2lu
YWxDc3BDYWNoZURlbGV0ZUZpbGUgPSBwQ2FyZERhdGEtPnBmbkNzcENhY2hlRGVsZXRlRmls
ZTsNCgkJcENhcmREYXRhLT5wZm5Dc3BDYWNoZURlbGV0ZUZpbGUgPSBNc0NyeXB0b0Jhc2U6
Okhvb2tDc3BDYWNoZURlbGV0ZUZpbGU7DQoNCgkJc19wZm5PcmlnaW5hbENzcENhY2hlQWRk
RmlsZSA9IHBDYXJkRGF0YS0+cGZuQ3NwQ2FjaGVBZGRGaWxlOw0KCQlwQ2FyZERhdGEtPnBm
bkNzcENhY2hlQWRkRmlsZSA9IE1zQ3J5cHRvQmFzZTo6SG9va0NzcENhY2hlQWRkRmlsZTsN
Cg0KCQlzX3Bmbk9yaWdpbmFsQ3NwQWxsb2MgPSBwQ2FyZERhdGEtPnBmbkNzcEFsbG9jOw0K
CQlwQ2FyZERhdGEtPnBmbkNzcEFsbG9jID0gTXNDcnlwdG9CYXNlOjpIb29rQ3NwQWxsb2M7
DQoNCgkJc19wZm5PcmlnaW5hbENzcFJlYWxsb2MgPSBwQ2FyZERhdGEtPnBmbkNzcFJlQWxs
b2M7DQoJCXBDYXJkRGF0YS0+cGZuQ3NwUmVBbGxvYyA9IE1zQ3J5cHRvQmFzZTo6SG9va0Nz
cFJlYWxsb2M7DQoNCgkJc19wZm5PcmlnaW5hbENzcEZyZWUgPSBwQ2FyZERhdGEtPnBmbkNz
cEZyZWU7DQoJCXBDYXJkRGF0YS0+cGZuQ3NwRnJlZSA9IE1zQ3J5cHRvQmFzZTo6SG9va0Nz
cEZyZWU7DQoNCgl9DQp9DQoNCg0KRFdPUkQgV0lOQVBJIE1zQ3J5cHRvQmFzZTo6SG9va0Nh
cmRSZWFkRmlsZSgNCglfSW5fIFBDQVJEX0RBVEEgIHBDYXJkRGF0YSwNCglfSW5fIExQU1RS
ICAgICAgIHBzekRpcmVjdG9yeU5hbWUsDQoJX0luXyBMUFNUUiAgICAgICBwc3pGaWxlTmFt
ZSwNCglfSW5fIERXT1JEICAgICAgIGR3RmxhZ3MsDQoJX091dF93cml0ZXNfYnl0ZXNfb3B0
XygqcGNiRGF0YSkgUEJZVEUqIHBwYkRhdGEsDQoJX091dF8gUERXT1JEICAgICBwY2JEYXRh
KQ0Kew0KCVNldHVwTWluaWRyaXZlckhvb2tzKHBDYXJkRGF0YSk7DQoNCglEV09SRCBkd1Jl
dCA9IHNfcGZuT3JpZ2luYWxDYXJkUmVhZEZpbGUoDQoJCXBDYXJkRGF0YSwgcHN6RGlyZWN0
b3J5TmFtZSwgcHN6RmlsZU5hbWUsIGR3RmxhZ3MsIHBwYkRhdGEsIHBjYkRhdGEpOw0KDQoJ
aWYgKGR3UmV0ID09IEVSUk9SX1NVQ0NFU1MgJiYgcHBiRGF0YSAmJiAqcHBiRGF0YSAmJiBw
Y2JEYXRhKSB7DQoJCVRyYWNlKFRSQUNFX0xFVkVMX0lORk8sDQoJCQlMIltIb29rQ2FyZFJl
YWRGaWxlXSBkaXI9JVMgZmlsZT0lUyByZXQ9JXUgc2l6ZT0ldSIsDQoJCQlwc3pEaXJlY3Rv
cnlOYW1lID8gcHN6RGlyZWN0b3J5TmFtZSA6ICIobnVsbCkiLA0KCQkJcHN6RmlsZU5hbWUg
PyBwc3pGaWxlTmFtZSA6ICIobnVsbCkiLA0KCQkJZHdSZXQsICpwY2JEYXRhKTsNCgkJLy8g
RHVtcCBkZXMgcHJlbWllcnMgb2N0ZXRzDQoJCURXT1JEIGNiRHVtcCA9IG1pbigqcGNiRGF0
YSwgMzJ1KTsNCgkJd2NoYXJfdCBzekhleFsxMjhdID0ge307DQoJCWZvciAoRFdPUkQgaSA9
IDA7IGkgPCBjYkR1bXA7IGkrKykNCgkJCXN3cHJpbnRmX3Moc3pIZXggKyBpICogMywgNCwg
TCIlMDJYICIsICgqcHBiRGF0YSlbaV0pOw0KCQlUcmFjZShUUkFDRV9MRVZFTF9JTkZPLCBM
IltIb29rQ2FyZFJlYWRGaWxlXSBkYXRhOiAlcyIsIHN6SGV4KTsNCgl9DQoJZWxzZSB7DQoJ
CVRyYWNlKFRSQUNFX0xFVkVMX1dBUk5JTkcsDQoJCQlMIltIb29rQ2FyZFJlYWRGaWxlXSBk
aXI9JVMgZmlsZT0lUyBGQUlMRUQgcmV0PTB4JTA4WCIsDQoJCQlwc3pEaXJlY3RvcnlOYW1l
ID8gcHN6RGlyZWN0b3J5TmFtZSA6ICIobnVsbCkiLA0KCQkJcHN6RmlsZU5hbWUgPyBwc3pG
aWxlTmFtZSA6ICIobnVsbCkiLA0KCQkJZHdSZXQpOw0KCX0NCglyZXR1cm4gZHdSZXQ7DQp9
DQoNCg0KDQoNCi8vIE9uIHNpbXVsZSBxdWUgbGUgZmljaGllciBuJ2V4aXN0ZSBqYW1haXMg
ZGFucyBsZSBjYWNoZQ0KRFdPUkQgV0lOQVBJIE1zQ3J5cHRvQmFzZTo6SG9va0NzcENhY2hl
TG9va3VwRmlsZSgNCglfSW5fICAgICAgICBQVk9JRCAgIHB2Q2FjaGVDb250ZXh0LA0KCV9J
bl8gICAgICAgIExQV1NUUiAgd3N6VGFnLA0KCV9Jbl8gICAgICAgIERXT1JEICAgZHdGbGFn
cywNCglfT3V0XyAgICAgICBQQllURSogcHBiRGF0YSwNCglfT3V0XyAgICAgICBQRFdPUkQg
IHBjYkRhdGEpDQp7DQoJVHJhY2UoVFJBQ0VfTEVWRUxfSU5GTywgTCItPiBbSG9va10gQ3Nw
Q2FjaGVMb29rdXBGaWxlIChCWVBBU1MpIHRhZz0lcyIsIHdzelRhZyk7DQoNCgkvLyBTw6lj
dXJpdMOpIDogb24gaW5pdGlhbGlzZSBsZXMgc29ydGllcyDDoCB6w6lybw0KCWlmIChwcGJE
YXRhKSAqcHBiRGF0YSA9IG51bGxwdHI7DQoJaWYgKHBjYkRhdGEpICpwY2JEYXRhID0gMDsN
Cg0KCS8vIE9uIHJldG91cm5lICJGaWNoaWVyIG5vbiB0cm91dsOpIiBwb3VyIGZvcmNlciBs
ZSBDU1Agw6AgcmVjYWxjdWxlci9yZWxpcmUgbGVzIGRvbm7DqWVzDQoJcmV0dXJuIENSWVBU
X0VfTk9UX0ZPVU5EOw0KfQ0KDQovLyBPbiBzaW11bGUgdW5lIHN1cHByZXNzaW9uIHLDqXVz
c2llIHNhbnMgcmllbiBmYWlyZQ0KRFdPUkQgV0lOQVBJIE1zQ3J5cHRvQmFzZTo6SG9va0Nz
cENhY2hlRGVsZXRlRmlsZSgNCglfSW5fIFBWT0lEICBwdkNhY2hlQ29udGV4dCwNCglfSW5f
IExQV1NUUiB3c3pUYWcsDQoJX0luXyBEV09SRCAgZHdGbGFncykNCnsNCglUcmFjZShUUkFD
RV9MRVZFTF9JTkZPLCBMIi0+IFtIb29rXSBDc3BDYWNoZURlbGV0ZUZpbGUgKEJZUEFTUykg
dGFnPSVzIiwgd3N6VGFnKTsNCg0KCS8vIE9uIHJldG91cm5lIEVSUk9SX1NVQ0NFU1MgcG91
ciBxdWUgbCdhcHBlbGFudCBwZW5zZSBxdWUgYydlc3QgZmFpdA0KCXJldHVybiBFUlJPUl9T
VUNDRVNTOw0KfQ0KDQovLyBPbiBpZ25vcmUgbCdham91dCBhdSBjYWNoZQ0KRFdPUkQgV0lO
QVBJIE1zQ3J5cHRvQmFzZTo6SG9va0NzcENhY2hlQWRkRmlsZSgNCglfSW5fIFBWT0lEICBw
dkNhY2hlQ29udGV4dCwNCglfSW5fIExQV1NUUiB3c3pUYWcsDQoJX0luXyBEV09SRCAgZHdG
bGFncywNCglfSW5fcmVhZHNfYnl0ZXNfKGNiRGF0YSkgUEJZVEUgcGJEYXRhLA0KCV9Jbl8g
RFdPUkQgIGNiRGF0YSkNCnsNCglUcmFjZShUUkFDRV9MRVZFTF9JTkZPLCBMIi0+IFtIb29r
XSBDc3BDYWNoZUFkZEZpbGUgKEJZUEFTUykgdGFnPSVzIHNpemU9JXUiLCB3c3pUYWcsIGNi
RGF0YSk7DQoNCgkvLyBPbiBuZSBmYWl0IHJpZW4gYXZlYyBsZXMgZG9ubsOpZXMgcGJEYXRh
Lg0KCS8vIE9uIHJldG91cm5lIEVSUk9SX1NVQ0NFU1MgcG91ciBuZSBwYXMgYmxvcXVlciBs
ZSBmbHV4IGRlIGwnYXBwbGljYXRpb24uDQoJcmV0dXJuIEVSUk9SX1NVQ0NFU1M7DQp9DQoN
CkxQVk9JRCBXSU5BUEkgTXNDcnlwdG9CYXNlOjpIb29rQ3NwQWxsb2MoX0luXyBTSVpFX1Qg
Y2JTaXplKQ0Kew0KCUxQVk9JRCBwUHRyID0gc19wZm5PcmlnaW5hbENzcEFsbG9jKGNiU2l6
ZSk7DQoJVHJhY2UoVFJBQ0VfTEVWRUxfVkVSQk9TRSwgTCJbSG9va10gQ3NwQWxsb2Mgc2l6
ZT0lenUgLT4gcHRyPSVwIiwgY2JTaXplLCBwUHRyKTsNCglyZXR1cm4gcFB0cjsNCn0NCg0K
TFBWT0lEIFdJTkFQSSBNc0NyeXB0b0Jhc2U6Okhvb2tDc3BSZWFsbG9jKF9Jbl8gTFBWT0lE
IHB2LCBfSW5fIFNJWkVfVCBjYlNpemUpDQp7DQoJTFBWT0lEIHBOZXdQdHIgPSBzX3Bmbk9y
aWdpbmFsQ3NwUmVhbGxvYyhwdiwgY2JTaXplKTsNCglUcmFjZShUUkFDRV9MRVZFTF9WRVJC
T1NFLCBMIltIb29rXSBDc3BSZWFsbG9jIG9sZD0lcCBzaXplPSV6dSAtPiBuZXc9JXAiLCBw
diwgY2JTaXplLCBwTmV3UHRyKTsNCglyZXR1cm4gcE5ld1B0cjsNCn0NCg0KVk9JRCBXSU5B
UEkgTXNDcnlwdG9CYXNlOjpIb29rQ3NwRnJlZShfSW5fIExQVk9JRCBwdikNCnsNCglUcmFj
ZShUUkFDRV9MRVZFTF9WRVJCT1NFLCBMIltIb29rXSBDc3BGcmVlIHB0cj0lcCIsIHB2KTsN
CglzX3Bmbk9yaWdpbmFsQ3NwRnJlZShwdik7DQp9DQoNCg0KLy8gUG9pbnRldXIgdmVycyBs
J29yaWdpbmFsIHBvdXIgdm9zIGJlc29pbnMNClBGTl9DQVJEX1JFQURfRklMRSBzX3Bmbk9y
aWdpbmFsQ2FyZFJlYWRGaWxlID0gbnVsbHB0cjsNCg0KTE9ORyBDQUxMQkFDSyBIYXJkd2Fy
ZUJyZWFrcG9pbnRIYW5kbGVyKFBFWENFUFRJT05fUE9JTlRFUlMgRXhjZXB0aW9uSW5mbykg
ew0KCWlmIChFeGNlcHRpb25JbmZvLT5FeGNlcHRpb25SZWNvcmQtPkV4Y2VwdGlvbkNvZGUg
PT0gRVhDRVBUSU9OX1NJTkdMRV9TVEVQKSB7DQoJCS8vIE9uIHbDqXJpZmllIHNpIGMnZXN0
IGJpZW4gbm90cmUgRFIwIHF1aSBhIGTDqWNsZW5jaMOpIGwnZXhjZXB0aW9uDQoJCWlmIChF
eGNlcHRpb25JbmZvLT5Db250ZXh0UmVjb3JkLT5EcjYgJiAweDEpIHsNCg0KCQkJLy8gTCdh
ZHJlc3NlIHN1cnZlaWxsw6llIGVzdCBkYW5zIERyMA0KCQkJdm9pZCoqIHBwZm5UYXJnZXQg
PSAodm9pZCoqKUV4Y2VwdGlvbkluZm8tPkNvbnRleHRSZWNvcmQtPkRyMDsNCg0KCQkJLy8g
LS0tIFLDiUNVUMOJUkFUSU9OIERFIExBIFZBTEVVUiAtLS0NCgkJCWlmICgqcHBmblRhcmdl
dCAhPSBNc0NyeXB0b0Jhc2U6Okhvb2tDYXJkUmVhZEZpbGUpDQoJCQkJc19wZm5PcmlnaW5h
bENhcmRSZWFkRmlsZSA9IChQRk5fQ0FSRF9SRUFEX0ZJTEUpKnBwZm5UYXJnZXQ7DQoNCgkJ
CS8vIC0tLSBMT0cgLyBUUkFDRSAtLS0NCgkJCS8vIE5vdGU6IEF0dGVudGlvbiBhdXggZm9u
Y3Rpb25zIGJsb3F1YW50ZXMgZGFucyB1biBoYW5kbGVyIGQnZXhjZXB0aW9uDQoJCQkvLyBU
cmFjZShUUkFDRV9MRVZFTF9JTkZPLCBMIkludGVyY2VwdMOpICEgTm91dmVsbGUgdmFsZXVy
IDogJXAiLCBzX3Bmbk9yaWdpbmFsQ2FyZFJlYWRGaWxlKTsNCg0KCQkJLy8gLS0tIElOSkVD
VElPTiBERSBWT1RSRSBIT09LIC0tLQ0KCQkJLy8gU2kgdm91cyB2b3VsZXogZm9yY2VyIHZv
dHJlIHByb3ByZSBmb25jdGlvbiBpbW3DqWRpYXRlbWVudCA6DQoJCQkvLyAqcHBmblRhcmdl
dCA9ICh2b2lkKilNb25Ib29rQ2FyZFJlYWRGaWxlOw0KDQoJCQkvLyBPbiBuZXR0b2llIERy
NiBwb3VyIGFjcXVpdHRlciBsJ8OpdsOobmVtZW50DQoJCQlFeGNlcHRpb25JbmZvLT5Db250
ZXh0UmVjb3JkLT5EcjYgJj0gfjB4MTsNCg0KCQkJcmV0dXJuIEVYQ0VQVElPTl9DT05USU5V
RV9FWEVDVVRJT047DQoJCX0NCgl9DQoJcmV0dXJuIEVYQ0VQVElPTl9DT05USU5VRV9TRUFS
Q0g7DQp9DQoNCnZvaWQgU2V0V3JpdGVCcmVha3BvaW50KHZvaWQqIGFkZHJlc3MpIHsNCgkv
LyAxLiBFbnJlZ2lzdHJlciBsZSBoYW5kbGVyIChwcmlvcml0w6kgaGF1dGUpDQoJQWRkVmVj
dG9yZWRFeGNlcHRpb25IYW5kbGVyKDEsIEhhcmR3YXJlQnJlYWtwb2ludEhhbmRsZXIpOw0K
DQoJQ09OVEVYVCBjdHggPSB7IDAgfTsNCgljdHguQ29udGV4dEZsYWdzID0gQ09OVEVYVF9E
RUJVR19SRUdJU1RFUlM7DQoJSEFORExFIGhUaHJlYWQgPSBHZXRDdXJyZW50VGhyZWFkKCk7
DQoNCglpZiAoR2V0VGhyZWFkQ29udGV4dChoVGhyZWFkLCAmY3R4KSkgew0KCQljdHguRHIw
ID0gKERXT1JEX1BUUilhZGRyZXNzOw0KDQoJCS8vIENvbmZpZ3VyYXRpb24gZGUgRFI3IDoN
CgkJLy8gYml0IDAgIChMMCkgOiBBY3RpdmUgRFIwIGxvY2FsZW1lbnQgPSAxDQoJCS8vIGJp
dCAxNi0xNyAoUlcwKSA6IDAxID0gQnJlYWsgb24gV3JpdGUgKG91IDExIHBvdXIgUmVhZC9X
cml0ZSkNCgkJLy8gYml0IDE4LTE5IChMRU4wKSA6IDExID0gOCBieXRlcyAoeDY0KSBvdSAx
MCA9IDIgYnl0ZXMsIDAwID0gMSBieXRlDQoJCWN0eC5EcjcgfD0gMHgxOyAgICAgICAgICAv
LyBMMCA9IDENCgkJY3R4LkRyNyB8PSAoMHgxIDw8IDE2KTsgIC8vIFJXMCA9IDAxIChXcml0
ZSkNCgkJY3R4LkRyNyB8PSAoMHgzIDw8IDE4KTsgIC8vIExFTjAgPSAxMSAoOCBieXRlcykN
Cg0KCQlTZXRUaHJlYWRDb250ZXh0KGhUaHJlYWQsICZjdHgpOw0KCX0NCn0NCg0KLy8gcmVw
bGFjZSB0aGUgZGxsIG5hbWUgaW4gU0NhcmQgSG9va0dldENhcmRUeXBlUHJvdmlkZXJOYW1l
VyB0byBnZXQgaW50byB0aGlzIGRsbA0KDQpEV09SRCBXSU5BUEkgTXNDcnlwdG9CYXNlOjpI
b29rQ2FyZEFjcXVpcmVDb250ZXh0KA0KCV9Jbm91dF8gUENBUkRfREFUQSBwQ2FyZERhdGEs
DQoJX0luXyAgICBEV09SRCAgICAgIGR3RmxhZ3MpDQp7DQoJLy8gQ2hhcmdlciBsZSB2cmFp
IG1pbmlkcml2ZXINCglzdGF0aWMgSE1PRFVMRSBoTXNjbG1kID0gTG9hZExpYnJhcnlXKEwi
bXNjbG1kLmRsbCIpOw0KCXN0YXRpYyBQRk5fQ0FSRF9BQ1FVSVJFX0NPTlRFWFQgcGZuUmVh
bCA9DQoJCShQRk5fQ0FSRF9BQ1FVSVJFX0NPTlRFWFQpR2V0UHJvY0FkZHJlc3MoaE1zY2xt
ZCwgIkNhcmRBY3F1aXJlQ29udGV4dCIpOw0KDQoJaWYgKHBDYXJkRGF0YSkgew0KCQkvLyBz
ZXR1cCBpbnRlcnJ1cHQgb25jZSB0aGUgcG9pbnRlciBpcyBzZXQNCgkJLy8gT24gcG9zZSBs
ZSBwacOoZ2Ugc3VyIGwnYWRyZXNzZSBkdSBwb2ludGV1ciBkZSBmb25jdGlvbg0KCQlTZXRX
cml0ZUJyZWFrcG9pbnQoJihwQ2FyZERhdGEtPnBmbkNhcmRSZWFkRmlsZSkpOw0KCX0NCg0K
CURXT1JEIGR3UmV0ID0gcGZuUmVhbChwQ2FyZERhdGEsIGR3RmxhZ3MpOw0KDQoJaWYgKGR3
UmV0ID09IEVSUk9SX1NVQ0NFU1MgJiYgcENhcmREYXRhKSB7DQoJCXNfcGZuT3JpZ2luYWxD
YXJkUmVhZEZpbGUgPSBwQ2FyZERhdGEtPnBmbkNhcmRSZWFkRmlsZTsNCgkJcENhcmREYXRh
LT5wZm5DYXJkUmVhZEZpbGUgPSBIb29rQ2FyZFJlYWRGaWxlOw0KCX0NCglyZXR1cm4gZHdS
ZXQ7DQp9DQoNCg==
--------------CMpnZc4mniyJIb4rYpAh0MRF
Content-Type: text/x-c-code; charset=windows-1252; name="MsCryptoBase.h"
Content-Disposition: attachment; filename="MsCryptoBase.h"
Content-Transfer-Encoding: 8bit

#pragma once

#include "pch.h"
#include "GidsTestBase.h" 
#include <winscard.h>
#include <vector>
#include <string>
#include <wincred.h>
#include "../EIDVirtualSmartCardMinidriverTest/cardmod.h"

// Forward declaration pour le bootstrapping
class MsCryptoBase;
extern MsCryptoBase* g_pStaticInstance;

// Structure pour la table des hooks
struct HookEntry {
    LPCSTR pszDllName;
    LPCSTR pszFuncName;
    PROC pfnHook;
    PROC pfnOriginal;
    bool isData; // True pour les variables comme g_rgSCardT1Pci
};

class MsCryptoBase : public GidsBaseTestClass
{
public:
    virtual void setUpHook();
    virtual void tearDownHook();

protected:
    HMODULE m_hMinidriver = NULL;
    HMODULE m_hBaseCsp = NULL;
    HMODULE m_hKsp = NULL;

    static MsCryptoBase* FromHandle(SCARDHANDLE h);

    template<typename T>
    static T GetOriginal(LPCSTR pszFuncName) {
        for (auto& entry : MsCryptoBase::s_Hooks)
            if (_stricmp(entry.pszFuncName, pszFuncName) == 0)
                return (T)entry.pfnOriginal;
        return nullptr;
    }

public:
    // --- Prototypes des Hooks ---
// Gestion du contexte
    static LONG WINAPI HookEstablishContext(
        _In_ DWORD dwScope,
        _Reserved_ LPCVOID pvReserved1,
        _Reserved_ LPCVOID pvReserved2,
        _Out_ LPSCARDCONTEXT phContext);

    static LONG WINAPI HookReleaseContext(
        _In_ SCARDCONTEXT hContext);

    static LONG WINAPI HookFreeMemory(
        _In_ SCARDCONTEXT hContext,
        _In_ LPCVOID pvMem);

    static LONG WINAPI HookCancel(
        _In_ SCARDCONTEXT hContext);

    // Gestion des lecteurs et cartes
    static LONG WINAPI HookConnectW(
        _In_ SCARDCONTEXT hContext,
        _In_ LPCWSTR szReader,
        _In_ DWORD dwShareMode,
        _In_ DWORD dwPreferredProtocols,
        _Out_ LPSCARDHANDLE phCard,
        _Out_ LPDWORD pdwActiveProtocol);

    static LONG WINAPI HookReconnect(
        _In_ SCARDHANDLE hCard,
        _In_ DWORD dwShareMode,
        _In_ DWORD dwPreferredProtocols,
        _In_ DWORD dwInitialization,
        _Out_opt_ LPDWORD pdwActiveProtocol);

    static LONG WINAPI HookDisconnect(
        _In_ SCARDHANDLE hCard,
        _In_ DWORD dwDisposition);

    static LONG WINAPI HookStatusW(
        _In_ SCARDHANDLE hCard,
        _Out_writes_opt_(*pcchReaderLen) LPWSTR mszReaderNames,
        _Inout_opt_ LPDWORD pcchReaderLen,
        _Out_opt_ LPDWORD pdwState,
        _Out_opt_ LPDWORD pdwProtocol,
        _Out_writes_opt_(*pcbAtrLen) LPBYTE pbAtr,
        _Inout_opt_ LPDWORD pcbAtrLen);

    static LONG WINAPI HookGetStatusChangeW(
        _In_ SCARDCONTEXT hContext,
        _In_ DWORD dwTimeout,
        _Inout_updates_(cReaders) LPSCARD_READERSTATEW rgReaderStates,
        _In_ DWORD cReaders);

    // Communications (IO)
    static LONG WINAPI HookTransmit(
        _In_ SCARDHANDLE hCard,
        _In_ LPCSCARD_IO_REQUEST pioSendPci,
        _In_reads_(cbSendLength) LPCBYTE pbSendBuffer,
        _In_ DWORD cbSendLength,
        _Inout_opt_ LPCSCARD_IO_REQUEST pioRecvPci,
        _Out_writes_bytes_to_opt_(*pcbRecvLength, *pcbRecvLength) LPBYTE pbRecvBuffer,
        _Inout_ LPDWORD pcbRecvLength);

    static LONG WINAPI HookControl(
        _In_ SCARDHANDLE hCard,
        _In_ DWORD dwControlCode,
        _In_reads_bytes_opt_(cbInBufferSize) LPCVOID lpInBuffer,
        _In_ DWORD cbInBufferSize,
        _Out_writes_bytes_to_opt_(cbOutBufferSize, *lpBytesReturned) LPVOID lpOutBuffer,
        _In_ DWORD cbOutBufferSize,
        _Out_ LPDWORD lpBytesReturned);

    // Transactions
    static LONG WINAPI HookBeginTrans(
        _In_ SCARDHANDLE hCard);

    static LONG WINAPI HookEndTrans(
        _In_ SCARDHANDLE hCard,
        _In_ DWORD dwDisposition);

    // Énumération et Recherche
    static LONG WINAPI HookListReadersW(
        _In_ SCARDCONTEXT hContext,
        _In_opt_ LPCWSTR mszGroups,
        _Out_writes_opt_(*pcchReaders) LPWSTR mszReaders,
        _Inout_ LPDWORD pcchReaders);

    static LONG WINAPI HookListCardsW(
        _In_ SCARDCONTEXT hContext,
        _In_opt_ LPCBYTE pbAtr,
        _In_reads_opt_(cguid) LPCGUID rgquid,
        _In_ DWORD cguid,
        _Out_writes_opt_(*pcchCards) LPWSTR mszCards,
        _Inout_ LPDWORD pcchCards);

    // Cache et Métadonnées
    static LONG WINAPI HookReadCacheW(
        _In_ SCARDCONTEXT hContext,
        _In_ UUID* pCardIdentifier,
        _In_ DWORD FreshnessCounter,
        _In_ LPWSTR szLookupName,
        _Out_writes_bytes_(*pcbDataLen) PBYTE pbData,
        _Inout_ LPDWORD pcbDataLen);

    static LONG WINAPI HookWriteCacheW(
        _In_ SCARDCONTEXT hContext,
        _In_ UUID* pCardIdentifier,
        _In_ DWORD FreshnessCounter,
        _In_ LPWSTR szLookupName,
        _In_reads_bytes_(cbDataLen) PBYTE pbData,
        _In_ DWORD cbDataLen);

    static LONG WINAPI HookGetCardTypeProviderNameW(
        _In_ SCARDCONTEXT hContext,
        _In_ LPCWSTR szCardName,
        _In_ DWORD dwProviderId,
        _Out_writes_opt_(*pcchProvider) LPWSTR szProvider,
        _Inout_ LPDWORD pcchProvider);

    // Divers et Extension
    static LONG WINAPI HookGetReaderIconW(
        _In_ SCARDCONTEXT hContext,
        _In_ LPCWSTR szReaderName,
        _Out_writes_bytes_opt_(*pcbIcon) LPBYTE pbIcon,
        _Inout_opt_ LPDWORD pcbIcon);

    static LONG WINAPI HookGetDeviceTypeIdW(
        _In_ SCARDCONTEXT hContext,
        _In_ LPCWSTR szReaderName,
        _Out_ LPDWORD pdwDeviceTypeId);

    static LONG WINAPI HookAudit(
        _In_ SCARDCONTEXT hContext,
        _In_ DWORD dwEvent);

    static HANDLE WINAPI HookAccessStartedEvent();

    static VOID WINAPI HookReleaseStartedEvent();

    static LONG WINAPI HookGetReaderDeviceInstanceIdW(
        _In_ SCARDCONTEXT hContext,
        _In_ LPCWSTR szReaderName,
        _Out_writes_opt_(*pcchDeviceInstanceId) LPWSTR szDeviceInstanceId,
        _Inout_ LPDWORD pcchDeviceInstanceId);

    static DWORD WINAPI HookCredUIPromptForWindowsCredentials(
        _In_opt_  PCREDUI_INFOW pUiInfo,
        _In_      DWORD         dwAuthError,
        _Inout_   ULONG* pulAuthPackage,
        _In_opt_  LPCVOID       pvInAuthBuffer,
        _In_      ULONG         ulInAuthBufferSize,
        _Out_     LPVOID* ppvOutAuthBuffer,
        _Out_     ULONG* pulOutAuthBufferSize,
        _Inout_   BOOL* pfSave,
        _In_      DWORD         dwFlags);

    static PFN_CARD_READ_FILE s_pfnOriginalCardReadFile;
    static PFN_CSP_CACHE_DELETE_FILE s_pfnOriginalCspCacheDeleteFile;
    static PFN_CSP_CACHE_ADD_FILE    s_pfnOriginalCspCacheAddFile;
    static PFN_CSP_ALLOC             s_pfnOriginalCspAlloc;
    static PFN_CSP_REALLOC           s_pfnOriginalCspRealloc;
    static PFN_CSP_FREE              s_pfnOriginalCspFree;
    static PFN_CARD_ACQUIRE_CONTEXT s_pfnOriginalCardAcquireContext;
    static PFN_CSP_CACHE_LOOKUP_FILE s_pfnOriginalCspCacheLookupFile;


    static DWORD WINAPI HookCardAcquireContext(
        _Inout_ PCARD_DATA pCardData,
        _In_    DWORD      dwFlags);


    static DWORD WINAPI HookCspCacheLookupFile(
        _In_                            PVOID   pvCacheContext,
        _In_                            LPWSTR  wszTag,
        _In_                            DWORD   dwFlags,
        /*_Deref_out_bcount_(*pcbData)*/    PBYTE* ppbData,
        _Out_                           PDWORD  pcbData);

    static DWORD WINAPI HookCardReadFile(
        _In_ PCARD_DATA  pCardData,
        _In_ LPSTR       pszDirectoryName,
        _In_ LPSTR       pszFileName,
        _In_ DWORD       dwFlags,
        _Out_writes_bytes_opt_(*pcbData) PBYTE* ppbData,
        _Out_ PDWORD     pcbData);

    static DWORD WINAPI HookCspCacheDeleteFile(
        _In_ PVOID  pvCacheContext,
        _In_ LPWSTR wszTag,
        _In_ DWORD  dwFlags);

    static DWORD WINAPI HookCspCacheAddFile(
        _In_ PVOID  pvCacheContext,
        _In_ LPWSTR wszTag,
        _In_ DWORD  dwFlags,
        _In_reads_bytes_(cbData) PBYTE pbData,
        _In_ DWORD  cbData);

    static LPVOID WINAPI HookCspAlloc(_In_ SIZE_T cbSize);

    static LPVOID WINAPI HookCspRealloc(_In_ LPVOID pv, _In_ SIZE_T cbSize);

    static VOID WINAPI HookCspFree(_In_ LPVOID pv);

    static VOID SetupMinidriverHooks(_In_ PCARD_DATA  pCardData);
private:
    static HookEntry s_Hooks[];

    // --- Logique de Patching IAT ---
    void PatchIAT(HMODULE hTargetDll, bool restore);
    void ApplyPatchStandard(HMODULE hTargetDll, PIMAGE_IMPORT_DESCRIPTOR pImportDesc, bool restore, const wchar_t* dllName);
    void ApplyPatchDelayLoad(HMODULE hTargetDll, PIMAGE_DELAYLOAD_DESCRIPTOR pDelayDesc, bool restore, const wchar_t* dllName);
};

--------------CMpnZc4mniyJIb4rYpAh0MRF
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline


--------------CMpnZc4mniyJIb4rYpAh0MRF
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

_______________________________________________
Opensc-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/opensc-devel

--------------CMpnZc4mniyJIb4rYpAh0MRF--