Core API for Time Retrieval and Conversion
Key Data Structures
; TIME_FIELDS structure definition
TIME_FIELDS STRUCT
Year WORD ? ; Year (1601-30827)
Month WORD ? ; Month (1-12)
Day WORD ? ; Day (1-31)
Hour WORD ? ; Hour (0-23)
Minute WORD ? ; Minute (0-59)
Second WORD ? ; Second (0-59)
Milliseconds WORD ? ; Milliseconds (0-999)
Weekday WORD ? ; Weekday (0-6, 0=Sunday)
TIME_FIELDS ENDS
Complete Implementation of Time Retrieval and Formatting
Function Implementation Code
; Get formatted time string
; Output: eax - points to static buffer (format: "YYYY.MM.DD.HH.MM.SS")
GetFormattedTimeString proc
local systemTime:LARGE_INTEGER
local localTime:LARGE_INTEGER
local timeFields:TIME_FIELDS
; 1. Get current system time (UTC)
invoke KeQuerySystemTime, addr systemTime
; 2. Convert to local time
invoke ExSystemTimeToLocalTime, addr systemTime, addr localTime
; 3. Convert to readable time fields
invoke RtlTimeToTimeFields, addr localTime, addr timeFields
; 4. Format string
; Use static buffer (consider thread safety in actual driver)
mov eax, offset szTimeBuffer
; Format year (4 digits)
movzx ecx, timeFields.Year
invoke RtlIntegerToUnicodeString, ecx, 10, eax, offset szTimeBuffer
; Add separator
mov byte ptr [eax+4], '.'
; Format month (2 digits)
movzx ecx, timeFields.Month
invoke FormatTwoDigits, ecx, eax+5
; Add separator
mov byte ptr [eax+7], '.'
; Format day (2 digits)
movzx ecx, timeFields.Day
invoke FormatTwoDigits, ecx, eax+8
; Add separator
mov byte ptr [eax+10], '.'
; Format hour (2 digits)
movzx ecx, timeFields.Hour
invoke FormatTwoDigits, ecx, eax+11
; Add separator
mov byte ptr [eax+13], '.'
; Format minute (2 digits)
movzx ecx, timeFields.Minute
invoke FormatTwoDigits, ecx, eax+14
; Add separator
mov byte ptr [eax+16], '.'
; Format second (2 digits)
movzx ecx, timeFields.Second
invoke FormatTwoDigits, ecx, eax+17
; String terminator
mov byte ptr [eax+19], 0
mov eax, offset szTimeBuffer
ret
GetFormattedTimeString endp
; Helper function: Format two-digit numbers
; Input: ecx - number, edi - output buffer
FormatTwoDigits proc
push edx
push ebx
mov eax, ecx
xor edx, edx
mov ebx, 10
div ebx ; eax = tens, edx = units
add al, '0' ; Convert to ASCII
mov [edi], al
add dl, '0'
mov [edi+1], dl
pop ebx
pop edx
ret
FormatTwoDigits endp
; Data segment definition
.data
szTimeBuffer db 20 dup(0) ; Sufficient to hold "YYYY.MM.DD.HH.MM.SS"
Step-by-Step Code Analysis
-
System Time Retrieval:
invoke KeQuerySystemTime, addr systemTime
- Retrieves UTC time, the number of 100-nanosecond intervals since January 1, 1601
Local Time Conversion:
invoke ExSystemTimeToLocalTime, addr systemTime, addr localTime
- Converts to local time considering timezone settings
Time Field Decomposition:
invoke RtlTimeToTimeFields, addr localTime, addr timeFields
- Decomposes the 64-bit time value into readable date-time components
String Formatting:
- Uses
<span>RtlIntegerToUnicodeString</span>to convert the year - Custom
<span>FormatTwoDigits</span>handles month, day, hour, minute, second - Adds separators to build the final string
Practical Application Examples
Driver Logging
; Log debug information with timestamp
LogWithTimestamp proc pMessage:DWORD
local szTime[20]:BYTE
; Get formatted time
call GetFormattedTimeString
; Output log
invoke DbgPrint, offset szLogFormat, eax, pMessage
ret
LogWithTimestamp endp
.data
szLogFormat db "[%s] %s", 0Ah, 0
File Timestamp
; Create filename with timestamp
GenerateTimestampFilename proc
local szTime[20]:BYTE
; Get current time
call GetFormattedTimeString
; Build filename
invoke RtlStringCbPrintf, offset szFilename,
sizeof szFilename,
offset szFileFormat,
eax
ret
GenerateTimestampFilename endp
.data
szFileFormat db "Log_%s.txt", 0
Key Considerations
-
Thread Safety:
- The above implementation uses a static buffer, which is not suitable for multi-threaded environments
- In actual drivers, thread-local storage or passing output buffers should be used
Buffer Management:
; Safe version interface
GetFormattedTimeStringEx proc pBuffer:DWORD, bufferSize:DWORD
; Add buffer length check
cmp bufferSize, 20
jb .buffer_too_small
; ...remaining implementation...
.buffer_too_small:
mov eax, STATUS_BUFFER_TOO_SMALL
ret
Performance Considerations:
- Frequent calls to time functions may impact performance
- For high-frequency logging, consider caching time values
Timezone Handling:
<span>ExSystemTimeToLocalTime</span>uses the system’s current timezone settings- The driver cannot control user-modified timezones
Extended Functionality Implementation
Adding Millisecond Display
; Add milliseconds to the time string
; ...existing code...
; Add milliseconds after seconds
mov byte ptr [eax+19], '.'
movzx ecx, timeFields.Milliseconds
invoke FormatThreeDigits, ecx, eax+20
; Update string terminator
mov byte ptr [eax+23], 0
; Buffer needs to be expanded to 24 bytes
Multi-language Weekday Display
; Add weekday display
AppendWeekday proc pBuffer:DWORD, weekday:WORD
movzx eax, weekday
mov ecx, offset WeekdayNames
mov edx, [ecx + eax*4]
invoke RtlStringCbCat, pBuffer, MAX_PATH, edx
ret
AppendWeekday endp
.data
WeekdayNames dd offset szSunday, offset szMonday, offset szTuesday
dd offset szWednesday, offset szThursday, offset szFriday
dd offset szSaturday
szSunday db "Sun",0
szMonday db "Mon",0
; ...other weekday names...
Through this time handling mechanism, kernel drivers can generate human-readable time information for logging, file naming, and various other scenarios. Understanding the processes of retrieving, converting, and formatting time values is crucial for developing reliable system-level software.