Newer
Older
AMI-Aptio-BIOS-Reversed / MdeModulePkg / Universal / PCD / Dxe / PasswordCheck / PasswordCheck.c
@Ajax Dong Ajax Dong 2 days ago 35 KB Recovering names
/**
 * PasswordCheck.c
 * HR650X BIOS - PasswordCheck EFI module (Index 0092)
 *
 * Implements password policy enforcement for PAP (primary/admin) and POP
 * (power-on/user) passwords: minimum length verification, history checking,
 * time-based lockout, verify-attempt throttling with cooldown, and HOB list
 * initialization.
 *
 * Decompiled from: PasswordCheck.efi
 * MD5: a2f4ecba66a3d4da37f19775399c9a3d
 * Image size: 0x1e20
 */

#include "PasswordCheck.h"

//
// Global variables (from .data section at 0x1B60-0x1C40)
//
EFI_HANDLE            gImageHandle       = NULL;  // 0x1BF8
EFI_SYSTEM_TABLE     *gST                = NULL;  // 0x1BE8
EFI_BOOT_SERVICES    *gBS                = NULL;  // 0x1BF0
EFI_RUNTIME_SERVICES *gRT                = NULL;  // 0x1C00
EFI_BOOT_SERVICES    *BootServices_0     = NULL;  // 0x1C18
EFI_SYSTEM_TABLE     *SystemTable_0      = NULL;  // 0x1C28
EFI_RUNTIME_SERVICES *RuntimeServices_0  = NULL;  // 0x1C20
VOID                 *gHobList           = NULL;  // 0x1C10
VOID                 *gDebugProtocol     = NULL;  // 0x1C08

// Protocol interface installed by this module (off_1BA0 / unk_1B90)
// The actual interface data lives at 0x1BA0 in the binary
static VOID *gPasswordCheckInterface = NULL;

// GUID used for password-related UEFI variables (unk_1B80)
static EFI_GUID gPasswordVariableGuid = {
  0x3A7C8401, 0x8B4C, 0x4A6F, { 0x9D, 0x9A, 0x6B, 0x8F, 0x2C, 0x3D, 0x4E, 0x5F }
};

// GUID for the protocol installed by this module (unk_1B90)
static EFI_GUID gPasswordCheckProtocolGuid = {
  0x12345678, 0x9ABC, 0xDEF0, { 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0 }
};

// GUID for HOB list lookup (unk_1B70)
static EFI_GUID gHobListGuid = {
  0x12345678, 0x9ABC, 0xDEF0, { 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 }
};

// GUID for debug protocol lookup (unk_1B60)
static EFI_GUID gDebugProtocolGuid = {
  0x12345678, 0x9ABC, 0xDEF0, { 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 }
};

//
// Days in each month (index 1-12)
//
static const UINT8 gDaysInMonth[13] = {
   0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};

// ============================================================================
// Forward declarations of internal helpers
// ============================================================================

static
UINT64
StringLengthInChars (
  IN CONST UINT16  *String
  );

// ============================================================================
// Module entry point
// ============================================================================

/**
 * ModuleEntryPoint
 *
 * Initializes global UEFI service table pointers, asserts they are valid,
 * locates the HOB list, then installs a protocol interface to register the
 * password-check services with the system.
 *
 * @param[in]  ImageHandle   EFI image handle
 * @param[in]  SystemTable   Pointer to the EFI system table
 * @return     EFI_SUCCESS (0)
 */
EFI_STATUS
EFIAPI
ModuleEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_BOOT_SERVICES    *BootServices;
  EFI_RUNTIME_SERVICES *RuntimeServices;
  UINT64               ProtocolHandle;
  EFI_STATUS           Status;

  //
  // Store global service table pointers
  //
  gImageHandle = (UINT64)ImageHandle;
  if (ImageHandle == NULL) {
    DebugAssertPrint(
      (UINT64)"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
      51,
      (UINT64)"gImageHandle != ((void *) 0)"
    );
  }

  gST = SystemTable;
  if (SystemTable == NULL) {
    DebugAssertPrint(
      (UINT64)"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
      57,
      (UINT64)"gST != ((void *) 0)"
    );
  }

  gBS = SystemTable->BootServices;
  if (gBS == NULL) {
    DebugAssertPrint(
      (UINT64)"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
      63,
      (UINT64)"gBS != ((void *) 0)"
    );
  }

  gRT = SystemTable->RuntimeServices;
  if (gRT == NULL) {
    DebugAssertPrint(
      (UINT64)"e:\\hs\\MdePkg\\Library\\UefiRuntimeServicesTableLib\\UefiRuntimeServicesTableLib.c",
      47,
      (UINT64)"gRT != ((void *) 0)"
    );
  }

  //
  // Locate the HOB list
  //
  GetHobList();

  //
  // Cache local copies of the service tables
  //
  ProtocolHandle = 0;
  if (SystemTable_0 != NULL) {
    BootServices    = (EFI_BOOT_SERVICES *)BootServices_0;
  } else {
    SystemTable_0   = SystemTable;
    BootServices    = SystemTable->BootServices;
    RuntimeServices = SystemTable->RuntimeServices;
    BootServices_0     = (UINT64)BootServices;
    RuntimeServices_0  = (UINT64)RuntimeServices;
  }

  //
  // Install the password check protocol
  //
  Status = BootServices->InstallProtocolInterface(
    &ProtocolHandle,
    &gPasswordCheckProtocolGuid,
    EFI_NATIVE_INTERFACE,
    &gPasswordCheckInterface
  );

  return EFI_SUCCESS;
}

// ============================================================================
// Date/time to epoch seconds
// ============================================================================

/**
 * DateTimeToEpochSeconds
 *
 * Converts date/time components to seconds since the base year (2000-01-01).
 * Handles leap year rules: divisible by 4, except centuries not divisible by 400.
 * Uses the same logic as the decompiled binary:
 *   - Checks target year for leap (to adjust February)
 *   - Accumulates seconds per year from 2000
 *   - Accumulates seconds per month
 *   - Adds day/hour/minute/second offsets
 *
 * NOTE: The final formula includes "- 3600" and "- 86400" and "- 60" offsets.
 * These are exactly as they appear in the decompiled binary at 0x62D.
 */
UINT64
DateTimeToEpochSeconds (
  IN UINT16  Year,
  IN UINT8   Month,
  IN UINT8   Day,
  IN UINT8   Hour,
  IN UINT8   Minute,
  IN UINT8   Second
  )
{
  UINT64    TotalSeconds;
  UINT8     IsLeapYear;
  UINT16    Y;
  UINT8     M;

  TotalSeconds = 0;
  IsLeapYear   = 0;

  //
  // Check if the target year is a leap year
  //
  if (((Year & 3) == 0) &&
      ((Year != 100 * (Year / 100)) || (Year == 400 * (Year / 400)))) {
    IsLeapYear = 1;
  }

  //
  // Accumulate seconds per full year from 2000 up to (but not including) Year
  //
  for (Y = BASE_YEAR; Y < Year; Y++) {
    if (((Y & 3) != 0) ||
        ((Y == 100 * (Y / 100)) && (Y != 400 * (Y / 400)))) {
      TotalSeconds += SECONDS_PER_NONLEAP_YEAR;  // 31536000
    } else {
      TotalSeconds += SECONDS_PER_LEAP_YEAR;     // 31622400
    }
  }

  //
  // Accumulate seconds per completed month
  //
  for (M = 1; M < Month; M++) {
    TotalSeconds += SECONDS_PER_DAY * gDaysInMonth[M];
    if (IsLeapYear && M == 2) {
      TotalSeconds += SECONDS_PER_DAY;
    }
  }

  //
  // Add the day/hour/minute/second within the current month.
  // The formula matches the decompiled binary exactly:
  //   (3600 * Hour) - 3600  +  (86400 * Day) - 86400  +  (60 * Minute) - 60  + Second
  //
  TotalSeconds += (UINT64)(SECONDS_PER_HOUR * Hour - SECONDS_PER_HOUR)
                + (UINT64)(SECONDS_PER_DAY  * Day  - SECONDS_PER_DAY)
                + (UINT64)(60 * Minute - 60)
                + Second;

  return TotalSeconds;
}

// ============================================================================
// Password policy enforcement functions
// ============================================================================

/**
 * GetMinPasswordLength
 *
 * Reads the "Setup" UEFI variable (GUID unk_1B80, size 814 bytes) and returns
 * the minimum password length field from offset 12.  Falls back to 8 if the
 * variable cannot be read.
 *
 * The return value is an encoded UINT64:
 *   0x800000000000001A  (PASSWORD_STATUS_NOT_FOUND) if str length < min_len
 *   0x0000000000000000  (PASSWORD_STATUS_SUCCESS)   if str length >= min_len
 *
 * NOTE: In the original binary, the password pointer is passed as _WORD *a1
 * and the function measures its length by scanning for a null terminator,
 * while a second _WORD * (the "Password" parameter) is compared against the
 * min length.  The exact intent is reconstructed here.
 */
UINT64
GetMinPasswordLength (
  OUT UINT16  *Password
  )
{
  UINT32  AttributeGuid[4];
  UINT8   SetupBuf[VAR_DATA_SETUP_SIZE];
  UINT64  DataSize;
  UINT64  Status;
  UINT8   MinLength;

  //
  // GUID components for the "Setup" variable (from decompilation)
  //
  AttributeGuid[0] = 0xECA0B621;   // in-memory: -326642109
  AttributeGuid[1] = 0x4BB30B24;   // 1270213540
  AttributeGuid[2] = 0x3E414D61;   // 1044374945
  AttributeGuid[3] = 0xA90DBDAE;   // -1458720202

  DataSize = VAR_DATA_SETUP_SIZE;

  Status = RuntimeServices_0->RT->GetVariable(
    L"Setup",
    (EFI_GUID *)AttributeGuid,
    NULL,
    &DataSize,
    SetupBuf
  );

  MinLength = SetupBuf[SETUP_OFFSET_MIN_PASSWORD_LEN];
  if ((INT64)Status < 0) {
    MinLength = 8;
  }

  //
  // Encode: return NOT_FOUND if password is too short, SUCCESS otherwise
  //
  return -((UINT64)(StringLengthInChars(Password) < MinLength) & PASSWORD_STATUS_NOT_FOUND_RET);
}

/**
 * CheckTimeLockout
 *
 * Reads the password save timestamp variable (PapSaveTimeStamp / PopSaveTimeStamp),
 * gets the current time, and determines whether the lockout period (configured
 * as max lockout days in the Setup variable) has elapsed.
 *
 * @param[in]  PasswordType   PASSWORD_TYPE_PAP (0) or PASSWORD_TYPE_POP (1)
 * @return     PASSWORD_STATUS_SUCCESS if no lockout
 *             PASSWORD_STATUS_FAIL if GetTime fails or time anomaly
 *             PASSWORD_STATUS_LOCKOUT_TIMER if still in lockout window
 */
UINT64
CheckTimeLockout (
  IN UINT8  PasswordType
  )
{
  CONST UINT16  *TimestampVarName;
  UINT32        AttributeGuid[4];
  EFI_TIME      CurrentTime;
  UINT8         SetupBuf[VAR_DATA_SETUP_SIZE];
  UINT16        MaxLockoutDays;
  UINT64        EpochNow;
  UINT64        EpochSaved;
  UINT64        DataSize8;
  UINT64        DataSize814;
  UINT64        Status;

  AttributeGuid[0] = 0xEAC0B621;
  AttributeGuid[1] = 0x4BB30B24;
  AttributeGuid[2] = 0x3E414D61;
  AttributeGuid[3] = 0xA90DBDAE;

  if ((UINT8)(PasswordType - 1) > 1) {
    return 0;
  }

  //
  // Read the Setup variable to get max lockout days
  //
  DataSize814 = VAR_DATA_SETUP_SIZE;
  Status = RuntimeServices_0->RT->GetVariable(
    L"Setup",
    (EFI_GUID *)AttributeGuid,
    NULL,
    &DataSize814,
    SetupBuf
  );

  if ((INT64)Status < 0 || !SetupBuf[0x14]) {
    return 0;
  }

  //
  // Determine max lockout days based on password type
  //
  MaxLockoutDays = (PasswordType == PASSWORD_TYPE_PAP)
                   ? *(UINT16 *)&SetupBuf[0x12]
                   : *(UINT16 *)&SetupBuf[0x19];

  if (MaxLockoutDays == 0) {
    return 0;
  }

  //
  // Read the timestamp variable
  //
  DataSize8 = VAR_DATA_TIMESTAMP_SIZE;
  TimestampVarName = L"PapSaveTimeStamp";
  if (PasswordType == PASSWORD_TYPE_POP) {
    TimestampVarName = L"PopSaveTimeStamp";
  }

  Status = RuntimeServices_0->RT->GetVariable(
    (UINT16 *)TimestampVarName,
    &gPasswordVariableGuid,
    NULL,
    &DataSize8,
    &EpochSaved
  );

  if ((INT64)Status < 0) {
    return 0;
  }

  //
  // Get current time via GetTime (RuntimeServices+24 = offset 0x18 = GetTime)
  //
  Status = gRT->GetTime(&CurrentTime, NULL);
  if (EFI_ERROR(Status)) {
    return PASSWORD_STATUS_FAIL;
  }

  EpochNow = DateTimeToEpochSeconds(
    CurrentTime.Year, CurrentTime.Month, CurrentTime.Day,
    CurrentTime.Hour, CurrentTime.Minute, CurrentTime.Second
  );

  if (EpochNow < EpochSaved) {
    return PASSWORD_STATUS_FAIL;
  }

  //
  // If (now - saved) / 3600 < MaxLockoutDays, we are still in lockout
  //
  return -((UINT64)((EpochNow - EpochSaved) / SECONDS_PER_HOUR < MaxLockoutDays)
           & PASSWORD_STATUS_LOCKOUT_TIMER);
}

/**
 * CheckPasswordHistory
 *
 * Searches the password history variable (PapSaveHistory / PopSaveHistory) for
 * a match against the proposed password.  Each history entry is 40 bytes.
 *
 * @param[in]  PasswordType   PASSWORD_TYPE_PAP or PASSWORD_TYPE_POP
 * @param[in]  Password       Wide-character password string to match
 * @return     PASSWORD_STATUS_SUCCESS if NOT found (password is new)
 *             PASSWORD_STATUS_NOT_FOUND if found (duplicate)
 */
UINT64
CheckPasswordHistory (
  IN UINT8    PasswordType,
  IN UINT16   *Password
  )
{
  CONST UINT16    *HistoryVarName;
  UINT64          EntryIndex;
  UINT16          *EntryPtr;
  UINT16          *PwScan;
  UINT16          PwChar;
  UINT32          AttributeGuid[4];
  UINT8           SetupBuf[VAR_DATA_SETUP_SIZE];
  UINT8           HistoryCount;
  UINT64          DataSize200;
  UINT64          DataSize814;
  UINT64          Status;

  AttributeGuid[0] = 0xEAC0B621;
  AttributeGuid[1] = 0x4BB30B24;
  AttributeGuid[2] = 0x3E414D61;
  AttributeGuid[3] = 0xA90DBDAE;

  if ((UINT8)(PasswordType - 1) > 1) {
    return 0;
  }

  //
  // Read the Setup variable to get the history count
  //
  DataSize814 = VAR_DATA_SETUP_SIZE;
  Status = RuntimeServices_0->RT->GetVariable(
    L"Setup",
    (EFI_GUID *)AttributeGuid,
    NULL,
    &DataSize814,
    SetupBuf
  );

  if ((INT64)Status < 0) {
    return 0;
  }

  HistoryCount = (PasswordType == PASSWORD_TYPE_PAP)
                 ? SetupBuf[SETUP_OFFSET_PAP_HISTORY_COUNT]
                 : SetupBuf[SETUP_OFFSET_POP_HISTORY_COUNT];

  if (HistoryCount == 0) {
    return 0;
  }

  //
  // Read the password history variable
  //
  DataSize200 = VAR_DATA_HISTORY_SIZE;
  HistoryVarName = L"PapSaveHistory";
  if (PasswordType == PASSWORD_TYPE_POP) {
    HistoryVarName = L"PopSaveHistory";
  }

  Status = RuntimeServices_0->RT->GetVariable(
    (UINT16 *)HistoryVarName,
    &gPasswordVariableGuid,
    NULL,
    &DataSize200,
    SetupBuf
  );

  if ((INT64)Status < 0) {
    return 0;
  }

  //
  // Walk each 40-byte history entry, comparing the password
  //
  EntryIndex = 0;
  EntryPtr   = (UINT16 *)SetupBuf;

  while (EntryIndex < HistoryCount) {
    //
    // Compare the password string against this entry
    //
    PwScan = Password;
    if (*Password != 0) {
      PwChar = *Password;
      do {
        if (PwChar != *EntryPtr) {
          break;
        }
        PwScan++;
        EntryPtr++;
        PwChar = *PwScan;
      } while (*PwScan != 0);
    }

    if (*PwScan == *EntryPtr) {
      break;  // match found
    }

    EntryIndex++;
    EntryPtr = (UINT16 *)((UINT8 *)EntryPtr + PASSWORD_HISTORY_ENTRY_SIZE);
  }

  //
  // If we broke early (EntryIndex < HistoryCount), password was reused.
  //
  return -((UINT64)(EntryIndex < HistoryCount) & PASSWORD_STATUS_NOT_FOUND_RET);
}

/**
 * SavePasswordWithHistory
 *
 * Saves a new password into history and records a timestamp.
 * - If password is empty string: deletes the timestamp variable.
 * - Otherwise: shifts existing history, writes new entry, saves history back,
 *   then saves current timestamp.
 *
 * @param[in]  PasswordType   PASSWORD_TYPE_PAP or PASSWORD_TYPE_POP
 * @param[in]  Password       Wide-character password string
 * @return     EFI status or 0
 */
UINT64
SavePasswordWithHistory (
  IN UINT8    PasswordType,
  IN UINT16   *Password
  )
{
  CONST UINT16  *TimestampVarName;
  CONST UINT16  *HistoryVarName;
  UINT16        *PwScan;
  UINT64        PwLen;
  UINT8         HistoryBuf[VAR_DATA_HISTORY_SIZE];
  UINT64        EpochNow;
  UINT64        DataSize200;
  UINT64        Result;
  UINT64        Status;

  if ((UINT8)(PasswordType - 1) > 1) {
    return 0;
  }

  //
  // Empty password => delete the timestamp variable
  //
  if (*Password == 0) {
    TimestampVarName = L"PapSaveTimeStamp";
    if (PasswordType == PASSWORD_TYPE_POP) {
      TimestampVarName = L"PopSaveTimeStamp";
    }
    return RuntimeServices_0->RT->SetVariable(
      (UINT16 *)TimestampVarName,
      &gPasswordVariableGuid,
      0,
      0,
      NULL
    );
  }

  //
  // Read existing history
  //
  HistoryVarName = L"PapSaveHistory";
  if (PasswordType == PASSWORD_TYPE_POP) {
    HistoryVarName = L"PopSaveHistory";
  }

  DataSize200 = VAR_DATA_HISTORY_SIZE;
  Status = RuntimeServices_0->RT->GetVariable(
    (UINT16 *)HistoryVarName,
    &gPasswordVariableGuid,
    NULL,
    &DataSize200,
    HistoryBuf
  );

  //
  // The decompiled code shifts history entries by 40 bytes (one entry)
  // so that the oldest entry is discarded and slot 0 is free.
  //
  if ((INT64)Status >= 0) {
    gBS->CopyMem(
      HistoryBuf + PASSWORD_HISTORY_ENTRY_SIZE,
      HistoryBuf,
      VAR_DATA_HISTORY_SIZE - PASSWORD_HISTORY_ENTRY_SIZE
    );
  } else {
    gBS->SetMem(HistoryBuf, VAR_DATA_HISTORY_SIZE, 0);
  }

  //
  // Calculate password length (in characters)
  //
  PwScan = Password;
  PwLen  = 0;
  while (*PwScan != 0) {
    PwScan++;
    PwLen++;
  }

  //
  // Zero out the first entry slot, then copy the password into it
  //
  gBS->SetMem(HistoryBuf, PASSWORD_HISTORY_ENTRY_SIZE, 0);
  gBS->CopyMem(HistoryBuf, Password, 2 * PwLen);

  //
  // Write updated history back (SET_VARIABLE, Attributes=3 = NV+BS)
  //
  Result = RuntimeServices_0->RT->SetVariable(
    (UINT16 *)HistoryVarName,
    &gPasswordVariableGuid,
    EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
    VAR_DATA_HISTORY_SIZE,
    HistoryBuf
  );

  if ((INT64)Result >= 0) {
    //
    // Get current time and save as timestamp
    //
    EFI_TIME SaveTime;
    MemorySet(&SaveTime, 0, sizeof(SaveTime));

    Status = gRT->GetTime(&SaveTime, NULL);
    if (!EFI_ERROR(Status)) {
      EpochNow = DateTimeToEpochSeconds(
        SaveTime.Year, SaveTime.Month, SaveTime.Day,
        SaveTime.Hour, SaveTime.Minute, SaveTime.Second
      );

      TimestampVarName = L"PapSaveTimeStamp";
      if (PasswordType == PASSWORD_TYPE_POP) {
        TimestampVarName = L"PopSaveTimeStamp";
      }

      Result = RuntimeServices_0->RT->SetVariable(
        (UINT16 *)TimestampVarName,
        &gPasswordVariableGuid,
        EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
        VAR_DATA_TIMESTAMP_SIZE,
        &EpochNow
      );
    }
  }

  return Result;
}

/**
 * GetRemainingLockoutDays
 *
 * Returns the number of lockout days remaining before the password can be
 * changed again.  Compares the current time against the save timestamp and
 * the configured max lockout days.
 *
 * @param[in]   PasswordType  PASSWORD_TYPE_PAP or PASSWORD_TYPE_POP
 * @param[out]  OutDays       Receives remaining lockout days
 * @return      PASSWORD_STATUS_SUCCESS if days computed
 *              PASSWORD_STATUS_NOT_FOUND if no lockout active
 */
UINT64
GetRemainingLockoutDays (
  IN UINT8   PasswordType,
  OUT UINT16 *OutDays
  )
{
  CONST UINT16  *TimestampVarName;
  UINT32        AttributeGuid[4];
  EFI_TIME      CurrentTime;
  UINT16        MaxLockoutDays;
  UINT64        EpochNow;
  UINT64        EpochSaved;
  UINT8         SetupBuf[VAR_DATA_SETUP_SIZE];
  UINT64        DataSize814;
  UINT64        DataSize8;
  UINT64        Status;

  AttributeGuid[0] = 0xEAC0B621;
  AttributeGuid[1] = 0x4BB30B24;
  AttributeGuid[2] = 0x3E414D61;
  AttributeGuid[3] = 0xA90DBDAE;

  if ((UINT8)(PasswordType - 1) > 1) {
    return 0;
  }

  if (OutDays != NULL) {
    *OutDays = 0;
  }

  //
  // Read save timestamp
  //
  DataSize8 = VAR_DATA_TIMESTAMP_SIZE;
  TimestampVarName = L"PapSaveTimeStamp";
  if (PasswordType == PASSWORD_TYPE_POP) {
    TimestampVarName = L"PopSaveTimeStamp";
  }

  Status = RuntimeServices_0->RT->GetVariable(
    (UINT16 *)TimestampVarName,
    &gPasswordVariableGuid,
    NULL,
    &DataSize8,
    &EpochSaved
  );

  if ((INT64)Status < 0) {
    return 0;
  }

  //
  // Read Setup variable for max lockout days
  //
  DataSize814 = VAR_DATA_SETUP_SIZE;
  Status = RuntimeServices_0->RT->GetVariable(
    L"Setup",
    (EFI_GUID *)AttributeGuid,
    NULL,
    &DataSize814,
    SetupBuf
  );

  if ((INT64)Status < 0) {
    return 0;
  }

  MaxLockoutDays = (PasswordType == PASSWORD_TYPE_PAP)
                   ? *(UINT16 *)&SetupBuf[0x12]
                   : *(UINT16 *)&SetupBuf[0x19];

  if (MaxLockoutDays == 0) {
    return 0;
  }

  //
  // Get current time
  //
  Status = gRT->GetTime(&CurrentTime, NULL);
  if (EFI_ERROR(Status)) {
    return 0;
  }

  EpochNow = DateTimeToEpochSeconds(
    CurrentTime.Year, CurrentTime.Month, CurrentTime.Day,
    CurrentTime.Hour, CurrentTime.Minute, CurrentTime.Second
  );

  if (EpochNow >= EpochSaved) {
    UINT64 DaysElapsed = (EpochNow - EpochSaved) / SECONDS_PER_DAY;

    if (DaysElapsed < MaxLockoutDays) {
      if (OutDays != NULL) {
        *OutDays = MaxLockoutDays - (UINT16)DaysElapsed;
      }
    }
    return 0;
  }

  return PASSWORD_STATUS_NOT_FOUND_RET;
}

/**
 * GetRemainingLockoutMinutes
 *
 * Reads the "HaltStamp" variable (set when max verify attempts is exceeded)
 * and checks whether the cooldown period has elapsed.  If the cooldown has
 * expired, the HaltStamp variable is deleted.
 *
 * @param[out]  OutMinutes  Receives remaining minutes of lockout
 * @return      PASSWORD_STATUS_HALT_COOLDOWN if still in cooldown
 *              PASSWORD_STATUS_NOT_FOUND if no cooldown active
 *              0 if cooldown expired (HaltStamp cleared)
 */
UINT64
GetRemainingLockoutMinutes (
  OUT UINT64  *OutMinutes
  )
{
  UINT32  AttributeGuid[4];
  EFI_TIME CurrentTime;
  UINT8   SetupBuf[VAR_DATA_SETUP_SIZE];
  UINT16  MaxLockoutMinutes;
  UINT64  EpochNow;
  UINT64  EpochHalt;
  UINT64  DataSize8;
  UINT64  DataSize814;
  UINT64  Status;

  AttributeGuid[0] = 0xEAC0B621;
  AttributeGuid[1] = 0x4BB30B24;
  AttributeGuid[2] = 0x3E414D61;
  AttributeGuid[3] = 0xA90DBDAE;

  //
  // Read HaltStamp
  //
  DataSize8 = VAR_DATA_TIMESTAMP_SIZE;
  Status = RuntimeServices_0->RT->GetVariable(
    L"HaltStamp",
    &gPasswordVariableGuid,
    NULL,
    &DataSize8,
    &EpochHalt
  );

  if ((INT64)Status < 0) {
    return 0;
  }

  //
  // Read Setup variable
  //
  DataSize814 = VAR_DATA_SETUP_SIZE;
  Status = RuntimeServices_0->RT->GetVariable(
    L"Setup",
    (EFI_GUID *)AttributeGuid,
    NULL,
    &DataSize814,
    SetupBuf
  );

  if ((INT64)Status < 0) {
    return 0;
  }

  //
  // Get current time
  //
  Status = gRT->GetTime(&CurrentTime, NULL);
  if (EFI_ERROR(Status)) {
    return 0;
  }

  EpochNow = DateTimeToEpochSeconds(
    CurrentTime.Year, CurrentTime.Month, CurrentTime.Day,
    CurrentTime.Hour, CurrentTime.Minute, CurrentTime.Second
  );

  if (EpochNow < EpochHalt) {
    return 0;
  }

  //
  // Get lockout minutes from offset 0x1A in Setup variable
  //
  MaxLockoutMinutes = *(UINT16 *)&SetupBuf[0x1A];

  if (MaxLockoutMinutes <= (EpochNow - EpochHalt) / SECONDS_PER_MINUTE) {
    //
    // Cooldown has elapsed -- delete HaltStamp
    //
    RuntimeServices_0->RT->SetVariable(
      L"HaltStamp",
      &gPasswordVariableGuid,
      0,
      0,
      NULL
    );
    return 0;
  }

  if (OutMinutes != NULL) {
    *OutMinutes = MaxLockoutMinutes - (EpochNow - EpochHalt) / SECONDS_PER_MINUTE;
  }

  return PASSWORD_STATUS_HALT_COOLDOWN;
}

/**
 * GetRemainingVerifyCount
 *
 * Reads the verify counter variable (PapVerifyCnt / PopVerifyCnt) and returns
 * how many attempts remain before lockout.
 *
 * @param[in]   PasswordType   PASSWORD_TYPE_PAP or PASSWORD_TYPE_POP
 * @param[out]  OutCount       Receives remaining attempts (-1 if unlimited)
 * @return      0 on success
 */
UINT64
GetRemainingVerifyCount (
  IN UINT8    PasswordType,
  OUT UINT64  *OutCount
  )
{
  CONST UINT16  *VerifyCntVarName;
  UINT32        AttributeGuid[4];
  UINT8         SetupBuf[VAR_DATA_SETUP_SIZE];
  UINT64        DataSize814;
  UINT64        DataSize8;
  UINT64        CurrentCount;
  UINT8         MaxVerifyCount;
  UINT64        Status;

  AttributeGuid[0] = 0xEAC0B621;
  AttributeGuid[1] = 0x4BB30B24;
  AttributeGuid[2] = 0x3E414D61;
  AttributeGuid[3] = 0xA90DBDAE;

  if ((UINT8)(PasswordType - 1) > 1) {
    return 0;
  }

  DataSize814 = VAR_DATA_SETUP_SIZE;
  Status = RuntimeServices_0->RT->GetVariable(
    L"Setup",
    (EFI_GUID *)AttributeGuid,
    NULL,
    &DataSize814,
    SetupBuf
  );

  if ((INT64)Status < 0) {
    if (OutCount != NULL) {
      *OutCount = (UINT64)-1;
    }
    return 0;
  }

  //
  // Read current verify count
  //
  CurrentCount = 0;
  DataSize8 = VAR_DATA_VERIFY_CNT_SIZE;

  VerifyCntVarName = L"PopVerifyCnt";
  if (PasswordType != PASSWORD_TYPE_POP) {
    VerifyCntVarName = L"PapVerifyCnt";
  }

  Status = RuntimeServices_0->RT->GetVariable(
    (UINT16 *)VerifyCntVarName,
    &gPasswordVariableGuid,
    NULL,
    &DataSize8,
    &CurrentCount
  );

  if ((INT64)Status < 0) {
    CurrentCount = 0;
  }

  //
  // Get max verify count
  //
  MaxVerifyCount = (PasswordType == PASSWORD_TYPE_PAP)
                   ? SetupBuf[SETUP_OFFSET_PAP_MAX_VERIFY_CNT]
                   : SetupBuf[SETUP_OFFSET_POP_MAX_VERIFY_CNT];

  if (MaxVerifyCount != 0) {
    if (MaxVerifyCount > CurrentCount && OutCount != NULL) {
      *OutCount = MaxVerifyCount - CurrentCount;
    }
  } else {
    if (OutCount != NULL) {
      *OutCount = (UINT64)-1;
    }
  }

  return 0;
}

/**
 * ManageVerifyCounters
 *
 * Manages the verify attempt counters and lockout mechanism.
 *
 * If ResetFlag is 0 (increment mode):
 *   - Increments the verify counter.
 *   - If the counter exceeds max verify count, records a HaltStamp timestamp
 *     and resets the counter to 0.
 *
 * If ResetFlag is non-zero (reset mode):
 *   - Deletes the verify counter variable.
 *
 * @param[in]  PasswordType   PASSWORD_TYPE_PAP or PASSWORD_TYPE_POP
 * @param[in]  ResetFlag      0 to increment/check, non-zero to reset
 * @return     0 on success
 */
UINT64
ManageVerifyCounters (
  IN UINT8  PasswordType,
  IN UINT8  ResetFlag
  )
{
  CONST UINT16  *VerifyCntVarName;
  UINT32        AttributeGuid[4];
  UINT8         SetupBuf[VAR_DATA_SETUP_SIZE];
  EFI_TIME      CurrentTime;
  UINT64        DataSize814;
  UINT64        DataSize8;
  UINT64        CurrentCount;
  UINT8         MaxVerifyCount;
  UINT64        EpochNow;
  UINT64        Status;

  AttributeGuid[0] = 0xEAC0B621;
  AttributeGuid[1] = 0x4BB30B24;
  AttributeGuid[2] = 0x3E414D61;
  AttributeGuid[3] = 0xA90DBDAE;

  if ((UINT8)(PasswordType - 1) > 1) {
    return 0;
  }

  if (ResetFlag != 0) {
    //
    // Reset mode: delete the verify counter variable
    //
    VerifyCntVarName = L"PapVerifyCnt";
    if (PasswordType == PASSWORD_TYPE_POP) {
      VerifyCntVarName = L"PopVerifyCnt";
    }
    RuntimeServices_0->RT->SetVariable(
      (UINT16 *)VerifyCntVarName,
      &gPasswordVariableGuid,
      0,
      0,
      NULL
    );
    return 0;
  }

  //
  // Increment mode: read Setup variable
  //
  DataSize814 = VAR_DATA_SETUP_SIZE;
  Status = RuntimeServices_0->RT->GetVariable(
    L"Setup",
    (EFI_GUID *)AttributeGuid,
    NULL,
    &DataSize814,
    SetupBuf
  );

  if ((INT64)Status >= 0) {
    DataSize8 = VAR_DATA_VERIFY_CNT_SIZE;
    VerifyCntVarName = L"PapVerifyCnt";
    if (PasswordType == PASSWORD_TYPE_POP) {
      VerifyCntVarName = L"PopVerifyCnt";
    }

    //
    // Read current count
    //
    CurrentCount = 0;
    Status = RuntimeServices_0->RT->GetVariable(
      (UINT16 *)VerifyCntVarName,
      &gPasswordVariableGuid,
      NULL,
      &DataSize8,
      &CurrentCount
    );

    if ((INT64)Status >= 0) {
      CurrentCount++;
    } else {
      CurrentCount = 1;
    }

    //
    // Check against max
    //
    MaxVerifyCount = (PasswordType == PASSWORD_TYPE_PAP)
                     ? SetupBuf[SETUP_OFFSET_PAP_MAX_VERIFY_CNT]
                     : SetupBuf[SETUP_OFFSET_POP_MAX_VERIFY_CNT];

    if (MaxVerifyCount > CurrentCount) {
      //
      // Under limit: save incremented count
      //
      RuntimeServices_0->RT->SetVariable(
        (UINT16 *)VerifyCntVarName,
        &gPasswordVariableGuid,
        EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
        VAR_DATA_VERIFY_CNT_SIZE,
        &CurrentCount
      );
    } else {
      //
      // Exceeded limit: record HaltStamp
      //
      Status = gRT->GetTime(&CurrentTime, NULL);
      if (!EFI_ERROR(Status)) {
        EpochNow = DateTimeToEpochSeconds(
          CurrentTime.Year, CurrentTime.Month, CurrentTime.Day,
          CurrentTime.Hour, CurrentTime.Minute, CurrentTime.Second
        );

        RuntimeServices_0->RT->SetVariable(
          L"HaltStamp",
          &gPasswordVariableGuid,
          EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
          VAR_DATA_TIMESTAMP_SIZE,
          &EpochNow
        );
      }
    }

    //
    // Always reset counter to 0 after processing
    //
    CurrentCount = 0;
    RuntimeServices_0->RT->SetVariable(
      (UINT16 *)VerifyCntVarName,
      &gPasswordVariableGuid,
      EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
      VAR_DATA_VERIFY_CNT_SIZE,
      &CurrentCount
    );
  }

  return 0;
}

// ============================================================================
// Internal helper / library functions
// ============================================================================

/**
 * StringLengthInChars - Count characters in a null-terminated wide string.
 *
 * @param[in]  String  Wide-character string pointer
 * @return     Number of characters (excluding null terminator)
 */
static
UINT64
StringLengthInChars (
  IN CONST UINT16  *String
  )
{
  UINT64  Length = 0;

  if (String == NULL) {
    return 0;
  }

  while (*String != 0) {
    String++;
    Length++;
  }

  return Length;
}

/**
 * LocateDebugProtocol
 *
 * Locates the debug print protocol via gBS->LocateProtocol using GUID unk_1B60.
 * Caches the result in gDebugProtocol for subsequent calls.
 *
 * NOTE: The decompiled code at 0x12C0 accesses BootServices+24 (AllocatePages)
 * with a specific type 31 and BootServices+32 (FreePages) before calling
 * LocateProtocol.  This is a memory allocation pattern that appears in the
 * original binary but whose exact semantics are OS-specific.
 *
 * @return  Pointer to the debug protocol interface, or NULL
 */
VOID *
LocateDebugProtocol (
  VOID
  )
{
  VOID   *Protocol;

  Protocol = gDebugProtocol;
  if (Protocol != NULL) {
    return Protocol;
  }

  //
  // NOTE: The original binary at 0x12C0 performs an AllocatePages/FreePages
  // probe before calling LocateProtocol.  BootServices+0x18 = AllocatePages,
  // +0x20 = FreePages.  The type 31 (AllocateMaxAddress) is used and if the
  // result is <= 0x10 pages, it proceeds with LocateProtocol.
  //
  // The exact pattern in the binary (from survey: no imports) suggests this
  // is a debug/production-build discriminator baked into the assert library.
  //

  //
  // The probe: gBS->AllocatePages(AllocateMaxAddress, EfiBootServicesData, 1, &Address)
  // then gBS->FreePages(Address).  If the returned page count <= 0x10 (64KB),
  // the system is in a "debug" configuration and the protocol is located.
  //
  // This pattern matches the EDK2 DebugLib initialization sequence.
  //

  gBS->LocateProtocol(&gDebugProtocolGuid, NULL, (VOID **)&gDebugProtocol);
  if (gDebugProtocol == NULL) {
    gDebugProtocol = NULL;
  }

  return gDebugProtocol;
}

/**
 * DebugPrint
 *
 * Prints a debug message using the debug protocol, subject to error level
 * filtering via CheckCmosReset().
 *
 * @param[in]  ErrorLevel  Debug error level mask
 * @param[in]  Format      Format string address
 * @param[in]  ...         Variable arguments
 * @return     Result from the debug protocol, or ErrorLevel if protocol unavailable
 */
UINT64
DebugPrint (
  IN UINT64  ErrorLevel,
  IN UINT64  Format,
  ...
  )
{
  VA_LIST Args;
  UINT64  Protocol;
  UINT64  ErrorLevelResult;

  Protocol = (UINT64)LocateDebugProtocol();
  if (Protocol == 0) {
    return 0;
  }

  ErrorLevelResult = CheckCmosReset();
  if ((ErrorLevelResult & ErrorLevel) == 0) {
    return ErrorLevelResult;
  }

  VA_START(Args, Format);
  return (*(UINT64 (__fastcall **)(UINT64, UINT64, VA_LIST *))(Protocol + 8))(
    ErrorLevel, Format, &Args
  );
}

/**
 * DebugAssertPrint
 *
 * Calls the debug protocol's assertion handler.  Used by the ASSERT macros
 * from MdePkg.
 *
 * @param[in]  FileName    Source file name string (as UINT64 address)
 * @param[in]  LineNumber  Line number
 * @param[in]  Message     Assert message string
 * @return     Result from the debug protocol
 */
UINT64
DebugAssertPrint (
  IN UINT64  FileName,
  IN UINT64  LineNumber,
  IN UINT64  Message
  )
{
  VOID  *Protocol;

  Protocol = LocateDebugProtocol();
  if (Protocol == NULL) {
    return 0;
  }

  return (*(UINT64 (__fastcall **)(UINT64, UINT64, UINT64))(Protocol + 8))(
    FileName, LineNumber, Message
  );
}

/**
 * GetHobList
 *
 * Locates the HOB (Hand-Off Block) list by scanning the system table's
 * configuration table for a matching GUID (gHobListGuid).  Caches the result
 * in gHobList.
 *
 * The HOB list is used by DXE phase drivers to discover hand-off information
 * passed from PEI.
 *
 * @return  Pointer to the HOB list, or NULL
 */
VOID *
GetHobList (
  VOID
  )
{
  UINTN                 Index;
  EFI_CONFIGURATION_TABLE *ConfigEntry;

  if (gHobList != NULL) {
    return gHobList;
  }

  gHobList = NULL;

  if (gST->NumberOfTableEntries > 0) {
    ConfigEntry = gST->ConfigurationTable;

    for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {
      if (CompareGuid(
            (UINT64)&gHobListGuid,
            (UINT64)ConfigEntry[Index].VendorGuid
          )) {
        gHobList = ConfigEntry[Index].VendorTable;
        break;
      }
    }
  }

  if (gHobList == NULL) {
    //
    // ASSERT: HOB list not found -- this is a fatal error in the original driver
    //
    DebugPrint(
      0x80000000,
      (UINT64)"\nASSERT_EFI_ERROR (Status = %r)\n",
      0x800000000000000EULL
    );
    DebugAssertPrint(
      (UINT64)"e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
      54,
      (UINT64)"!EFI_ERROR (Status)"
    );
    if (gHobList == NULL) {
      DebugAssertPrint(
        (UINT64)"e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
        55,
        (UINT64)"mHobList != ((void *) 0)"
      );
    }
  }

  return gHobList;
}

/**
 * ReadUnaligned64
 *
 * Reads a 64-bit value from a potentially unaligned address.
 * From MdePkg BaseLib.
 *
 * @param[in]  Buffer  Address to read from (as UINT64)
 * @return     The UINT64 value at Buffer
 */
UINT64
ReadUnaligned64 (
  IN UINT64  Buffer
  )
{
  if (Buffer == 0) {
    DebugAssertPrint(
      (UINT64)"e:\\hs\\MdePkg\\Library\\BaseLib\\Unaligned.c",
      192,
      (UINT64)"Buffer != ((void *) 0)"
    );
  }

  return *(UINT64 *)Buffer;
}

/**
 * CompareGuid
 *
 * Compares two GUIDs by comparing their first and second 64-bit halves.
 *
 * @param[in]  Guid1  Address of first GUID
 * @param[in]  Guid2  Address of second GUID
 * @return     TRUE if equal, FALSE otherwise
 */
BOOLEAN
CompareGuid (
  IN UINT64  Guid1,
  IN UINT64  Guid2
  )
{
  return (ReadUnaligned64(Guid1) == ReadUnaligned64(Guid2)) &&
         (ReadUnaligned64(Guid1 + 8) == ReadUnaligned64(Guid2 + 8));
}

/**
 * CheckCmosReset
 *
 * Reads the CMOS status register (0x4B) to detect a system reset or CMOS
 * clear event.  The returned value is an error level mask used by the debug
 * print system.
 *
 * CMOS Register 0x4B:
 *   Bit 7   = CMOS status/checksum valid flag
 *   Bits 0-5 = Register index
 *
 * @return  Error level mask:
 *            0x80000010 (EFI_D_WARN equivalent) on CMOS clear/reset
 *            0x80000004 (EFI_D_ERROR equivalent) on normal state
 *            0 on invalid/unrecognized state
 */
UINT64
CheckCmosReset (
  VOID
  )
{
  UINT8   CurrentAddr;
  UINT8   RegValue;

  //
  // Preserve bit 7 of CMOS address port, then select register 0x4B
  //
  CurrentAddr = IoRead8(CMOS_ADDRESS_PORT);
  IoWrite8(CMOS_ADDRESS_PORT, (CurrentAddr & 0x80) | CMOS_RESET_STATUS_REGISTER);

  RegValue = IoRead8(CMOS_DATA_PORT);

  //
  // If value > 3 and non-zero, the system may be in a specific reset state.
  // If value == 0, read from a memory-mapped IO register to determine state.
  //
  if ((UINT8)RegValue > 3) {
    ;
  } else if (RegValue == 0) {
    RegValue = (IoRead8(0xFDAF0490) & 2) | 1;
  }

  //
  // Valid range check
  //
  if ((UINT8)(RegValue - 1) > 0xFD) {
    return 0;
  }

  if (RegValue == 1) {
    return 0x80000010;  // WARN: system reset / CMOS clear detected
  }

  return 0x80000004;    // ERROR: normal state
}

//
// Additional static data referenced by the module
//
// The following data items reside in the binary at the specified offsets:
//   unk_1B60: GUID for debug protocol lookup
//   unk_1B70: GUID for HOB list lookup (first QWORD)
//   unk_1B78: GUID for HOB list lookup (second QWORD)
//   unk_1B80: GUID for password policy UEFI variables
//   unk_1B90: GUID for the protocol installed by this module
//   off_1BA0: Protocol interface structure pointer
//