Newer
Older
AMI-Aptio-BIOS-Reversed / PasswordCheck / PasswordCheck.md
@Ajax Dong Ajax Dong 2 days ago 6 KB Init

PasswordCheck

Function Table

Address Name Description
StringLengthInChars
ModuleEntryPoint
DateTimeToEpochSeconds
GetMinPasswordLength
CheckTimeLockout
CheckPasswordHistory
SavePasswordWithHistory
GetRemainingLockoutDays
GetRemainingLockoutMinutes
GetRemainingVerifyCount
ManageVerifyCounters
DebugPrint
DebugAssertPrint
ReadUnaligned64
CompareGuid
CheckCmosReset
Global variables (from .data section at 0x1B60-0x1C40)
EFI_HANDLE gImageHandle = NULL; // 0x1BF8
0x1BE8 EFI_BOOT_SERVICES *gBS = NULL; // 0x1BF0
0x1C00 EFI_BOOT_SERVICES *BootServices_0 = NULL; // 0x1C18
0x1C28 EFI_RUNTIME_SERVICES *RuntimeServices_0 = NULL; // 0x1C20
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
GUID used for password-related UEFI variables (unk_1B80)
GUID for the protocol installed by this module (unk_1B90)
GUID for HOB list lookup (unk_1B70)
GUID for debug protocol lookup (unk_1B60)
Days in each month (index 1-12)
static const UINT8 gDaysInMonth[13] = {
Forward declarations of internal helpers
Module entry point
Store global service table pointers
gImageHandle = (UINT64)ImageHandle;
Locate the HOB list
Cache local copies of the service tables
ProtocolHandle = 0;
Install the password check protocol
Status = BootServices->InstallProtocolInterface(
Check if the target year is a leap year
if (((Year & 3) == 0) &&
Accumulate seconds per full year from 2000 up to (but not including) Year
for (Y = BASE_YEAR; Y < Year; Y++) {
31536000 } else {
31622400 }
Accumulate seconds per completed month
for (M = 1; M < Month; M++) {
Add the day/hour/minute/second within the current month.
The formula matches the decompiled binary exactly:
TotalSeconds += (UINT64)(SECONDS_PER_HOUR * Hour - SECONDS_PER_HOUR)
Password policy enforcement functions
GUID components for the "Setup" variable (from decompilation)
1270213540 AttributeGuid[2] = 0x3E414D61; // 1044374945
return -((UINT64)(StringLengthInChars(Password) < MinLength) & PASSWORD_STATUS_NOT_FOUND_RET);
Read the Setup variable to get max lockout days
DataSize814 = VAR_DATA_SETUP_SIZE;
Determine max lockout days based on password type
MaxLockoutDays = (PasswordType == PASSWORD_TYPE_PAP)
Read the timestamp variable
DataSize8 = VAR_DATA_TIMESTAMP_SIZE;
Get current time via GetTime (RuntimeServices+24 = offset 0x18 = GetTime)
Status = gRT->GetTime(&CurrentTime, NULL);
If (now - saved) / 3600 < MaxLockoutDays, we are still in lockout
return -((UINT64)((EpochNow - EpochSaved) / SECONDS_PER_HOUR < MaxLockoutDays)
Read the Setup variable to get the history count
Read the password history variable
DataSize200 = VAR_DATA_HISTORY_SIZE;
Walk each 40-byte history entry, comparing the password
Compare the password string against this entry
PwScan = Password;
match found
If we broke early (EntryIndex < HistoryCount), password was reused.
return -((UINT64)(EntryIndex < HistoryCount) & PASSWORD_STATUS_NOT_FOUND_RET);
Empty password => delete the timestamp variable
if (*Password == 0) {
Read existing history
HistoryVarName = L"PapSaveHistory";
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) {
Calculate password length (in characters)
Zero out the first entry slot, then copy the password into it
Write updated history back (SET_VARIABLE, Attributes=3 = NV+BS)
Result = RuntimeServices_0->RT->SetVariable(
Get current time and save as timestamp
EFI_TIME SaveTime;
Read save timestamp
Read Setup variable for max lockout days
Get current time
Read HaltStamp
Read Setup variable
Get lockout minutes from offset 0x1A in Setup variable
MaxLockoutMinutes = (UINT16 )&SetupBuf[0x1A];
Cooldown has elapsed delete HaltStamp
Read current verify count
Get max verify count
Reset mode: delete the verify counter variable
VerifyCntVarName = L"PapVerifyCnt";
Increment mode: read Setup variable
Read current count
Check against max
Under limit: save incremented count
Exceeded limit: record HaltStamp
Always reset counter to 0 after processing
Internal helper / library functions
probe before calling LocateProtocol. BootServices+0x18 = AllocatePages
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.
Preserve bit 7 of CMOS address port, then select register 0x4B
CurrentAddr = IoRead8(CMOS_ADDRESS_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) {
Valid range check
if ((UINT8)(RegValue - 1) > 0xFD) {
Additional static data referenced by the module
The following data items reside in the binary at the specified offsets:

Generated by HR650X BIOS Decompilation Project