/**
* 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 (sub_48C)
// ============================================================================
/**
* 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 - sub_70C
*
* 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 - sub_7C8
*
* 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 - sub_944
*
* 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 - sub_AA0
*
* 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 - sub_CB4
*
* 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 - sub_E4C
*
* 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 - sub_FC4
*
* 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 - sub_10DC
*
* 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 - sub_12C0
*
* 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 - sub_1340
*
* 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 - sub_1388
*
* 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 - sub_13C8
*
* 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 - sub_1560
*
* 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 - sub_14F0
*
* 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 - sub_14A0
*
* 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
//