Over the past few weeks I have been working on a custom packer in my spare time. In doing so, I needed to create a method of delaying execution within the unpacker stub that didn’t use any pre-defined functions. This post documents what I discovered during this project as well as some future plans I have for this method.

Contents

  1. What is SharedUserData And Why Does it Exist?
  2. How Can This Be Abused?

What is SharedUserData And Why Does it Exist?

The main purpose of SharedUserData is to provide all windows processes (Windows NT+) with a global and consistent method of obtaining frequently accessed information such as current system time, or interrupt ticks. This is faster than having to incur the performance deficit of making a syscall or calling a function such as RtlTimeToSecondsSince1980.

_KUSER_SHARED_DATA Structure

The KUSER_SHARED_DATA structure defines a data region that the kernel places at a static, pre-defined address for access within user-mode. It is important to note that this region of memory, when accessed via user-mode, is read only, writing is not possible. What’s more interesting is that even though this concept could be optimized, the kernel’s execution still begins with a page defined for this data. However, for backwards compatibility reasons, this has not been done and is very unlikely to change in the future.

The pre-defined address for acess via kernel-mode is defined in wdm.h as KI_USER_SHARED_DATA. When debugging, this is 0xFFDF0000 / 0x00000000FFFFF780 respectively on 32-bit and 64-bit systems. The read-only user-mode address for SharedUserData is 0x7FFE0000, this is static and constant for both 32-bit, and 64-bit Windows systems.

It is important to note that this structure has had attributes added over the years, below is an example from Windows XP SP2 (credit), and following is from my personal machine which is running Windows 10 Build 19043.

_KUSER_SHARED_DATA Structure Windows XP SP2

0:000> dt _KUSER_SHARED_DATA
   +0x000 TickCountLow     : Uint4B
   +0x004 TickCountMultiplier : Uint4B
   +0x008 InterruptTime    : _KSYSTEM_TIME
   +0x014 SystemTime       : _KSYSTEM_TIME
   +0x020 TimeZoneBias     : _KSYSTEM_TIME
   +0x02c ImageNumberLow   : Uint2B
   +0x02e ImageNumberHigh  : Uint2B
   +0x030 NtSystemRoot     : [260] Uint2B
   +0x238 MaxStackTraceDepth : Uint4B
   +0x23c CryptoExponent   : Uint4B
   +0x240 TimeZoneId       : Uint4B
   +0x244 Reserved2        : [8] Uint4B
   +0x264 NtProductType    : _NT_PRODUCT_TYPE
   +0x268 ProductTypeIsValid : UChar
   +0x26c NtMajorVersion   : Uint4B
   +0x270 NtMinorVersion   : Uint4B
   +0x274 ProcessorFeatures : [64] UChar
   +0x2b4 Reserved1        : Uint4B
   +0x2b8 Reserved3        : Uint4B
   +0x2bc TimeSlip         : Uint4B
   +0x2c0 AlternativeArchitecture : _ALTERNATIVE_ARCHITECTURE_TYPE
   +0x2c8 SystemExpirationDate : _LARGE_INTEGER
   +0x2d0 SuiteMask        : Uint4B
   +0x2d4 KdDebuggerEnabled : UChar
   +0x2d5 NXSupportPolicy  : UChar
   +0x2d8 ActiveConsoleId  : Uint4B
   +0x2dc DismountCount    : Uint4B
   +0x2e0 ComPlusPackage   : Uint4B
   +0x2e4 LastSystemRITEventTickCount : Uint4B
   +0x2e8 NumberOfPhysicalPages : Uint4B
   +0x2ec SafeBootMode     : UChar
   +0x2f0 TraceLogging     : Uint4B
   +0x2f8 TestRetInstruction : Uint8B
   +0x300 SystemCall       : Uint4B
   +0x304 SystemCallReturn : Uint4B
   +0x308 SystemCallPad    : [3] Uint8B
   +0x320 TickCount        : _KSYSTEM_TIME
   +0x320 TickCountQuad    : Uint8B
   +0x330 Cookie           : Uint4B

_KUSER_SHARED_DATA Structure Windows 10 Build 19043

As you can see, backwards compatibility has been taken into consideration here. This is due to the attributes ranging from 0x000 -> 0x330 are exactly the same, the only difference is that Windows 10 seems to have extra attributes appended to the end of the structure.

0:000> dt _KUSER_SHARED_DATA
ntdll!_KUSER_SHARED_DATA
   +0x000 TickCountLowDeprecated : Uint4B
   +0x004 TickCountMultiplier : Uint4B
   +0x008 InterruptTime    : _KSYSTEM_TIME
   +0x014 SystemTime       : _KSYSTEM_TIME
   +0x020 TimeZoneBias     : _KSYSTEM_TIME
   +0x02c ImageNumberLow   : Uint2B
   +0x02e ImageNumberHigh  : Uint2B
   +0x030 NtSystemRoot     : [260] Wchar
   +0x238 MaxStackTraceDepth : Uint4B
   +0x23c CryptoExponent   : Uint4B
   +0x240 TimeZoneId       : Uint4B
   +0x244 LargePageMinimum : Uint4B
   +0x248 AitSamplingValue : Uint4B
   +0x24c AppCompatFlag    : Uint4B
   +0x250 RNGSeedVersion   : Uint8B
   +0x258 GlobalValidationRunlevel : Uint4B
   +0x25c TimeZoneBiasStamp : Int4B
   +0x260 NtBuildNumber    : Uint4B
   +0x264 NtProductType    : _NT_PRODUCT_TYPE
   +0x268 ProductTypeIsValid : UChar
   +0x269 Reserved0        : [1] UChar
   +0x26a NativeProcessorArchitecture : Uint2B
   +0x26c NtMajorVersion   : Uint4B
   +0x270 NtMinorVersion   : Uint4B
   +0x274 ProcessorFeatures : [64] UChar
   +0x2b4 Reserved1        : Uint4B
   +0x2b8 Reserved3        : Uint4B
   +0x2bc TimeSlip         : Uint4B
   +0x2c0 AlternativeArchitecture : _ALTERNATIVE_ARCHITECTURE_TYPE
   +0x2c4 BootId           : Uint4B
   +0x2c8 SystemExpirationDate : _LARGE_INTEGER
   +0x2d0 SuiteMask        : Uint4B
   +0x2d4 KdDebuggerEnabled : UChar
   +0x2d5 MitigationPolicies : UChar
   +0x2d5 NXSupportPolicy  : Pos 0, 2 Bits
   +0x2d5 SEHValidationPolicy : Pos 2, 2 Bits
   +0x2d5 CurDirDevicesSkippedForDlls : Pos 4, 2 Bits
   +0x2d5 Reserved         : Pos 6, 2 Bits
   +0x2d6 CyclesPerYield   : Uint2B
   +0x2d8 ActiveConsoleId  : Uint4B
   +0x2dc DismountCount    : Uint4B
   +0x2e0 ComPlusPackage   : Uint4B
   +0x2e4 LastSystemRITEventTickCount : Uint4B
   +0x2e8 NumberOfPhysicalPages : Uint4B
   +0x2ec SafeBootMode     : UChar
   +0x2ed VirtualizationFlags : UChar
   +0x2ee Reserved12       : [2] UChar
   +0x2f0 SharedDataFlags  : Uint4B
   +0x2f0 DbgErrorPortPresent : Pos 0, 1 Bit
   +0x2f0 DbgElevationEnabled : Pos 1, 1 Bit
   +0x2f0 DbgVirtEnabled   : Pos 2, 1 Bit
   +0x2f0 DbgInstallerDetectEnabled : Pos 3, 1 Bit
   +0x2f0 DbgLkgEnabled    : Pos 4, 1 Bit
   +0x2f0 DbgDynProcessorEnabled : Pos 5, 1 Bit
   +0x2f0 DbgConsoleBrokerEnabled : Pos 6, 1 Bit
   +0x2f0 DbgSecureBootEnabled : Pos 7, 1 Bit
   +0x2f0 DbgMultiSessionSku : Pos 8, 1 Bit
   +0x2f0 DbgMultiUsersInSessionSku : Pos 9, 1 Bit
   +0x2f0 DbgStateSeparationEnabled : Pos 10, 1 Bit
   +0x2f0 SpareBits        : Pos 11, 21 Bits
   +0x2f4 DataFlagsPad     : [1] Uint4B
   +0x2f8 TestRetInstruction : Uint8B
   +0x300 QpcFrequency     : Int8B
   +0x308 SystemCall       : Uint4B
   +0x30c Reserved2        : Uint4B
   +0x310 SystemCallPad    : [2] Uint8B
   +0x320 TickCount        : _KSYSTEM_TIME
   +0x320 TickCountQuad    : Uint8B
   +0x320 ReservedTickCountOverlay : [3] Uint4B
   +0x32c TickCountPad     : [1] Uint4B
   +0x330 Cookie           : Uint4B
   +0x334 CookiePad        : [1] Uint4B
   +0x338 ConsoleSessionForegroundProcessId : Int8B
   +0x340 TimeUpdateLock   : Uint8B
   +0x348 BaselineSystemTimeQpc : Uint8B
   +0x350 BaselineInterruptTimeQpc : Uint8B
   +0x358 QpcSystemTimeIncrement : Uint8B
   +0x360 QpcInterruptTimeIncrement : Uint8B
   +0x368 QpcSystemTimeIncrementShift : UChar
   +0x369 QpcInterruptTimeIncrementShift : UChar
   +0x36a UnparkedProcessorCount : Uint2B
   +0x36c EnclaveFeatureMask : [4] Uint4B
   +0x37c TelemetryCoverageRound : Uint4B
   +0x380 UserModeGlobalLogger : [16] Uint2B
   +0x3a0 ImageFileExecutionOptions : Uint4B
   +0x3a4 LangGenerationCount : Uint4B
   +0x3a8 Reserved4        : Uint8B
   +0x3b0 InterruptTimeBias : Uint8B
   +0x3b8 QpcBias          : Uint8B
   +0x3c0 ActiveProcessorCount : Uint4B
   +0x3c4 ActiveGroupCount : UChar
   +0x3c5 Reserved9        : UChar
   +0x3c6 QpcData          : Uint2B
   +0x3c6 QpcBypassEnabled : UChar
   +0x3c7 QpcShift         : UChar
   +0x3c8 TimeZoneBiasEffectiveStart : _LARGE_INTEGER
   +0x3d0 TimeZoneBiasEffectiveEnd : _LARGE_INTEGER
   +0x3d8 XState           : _XSTATE_CONFIGURATION
   +0x710 FeatureConfigurationChangeStamp : _KSYSTEM_TIME
   +0x71c Spare            : Uint4B

_KSYSTEM_TIME Structure

The _KSYSTEM_TIME structure is relatively very simple, however I figured it would still be useful to give the definition here.

The structure (credit) is defined as follows:

typedef struct _KSYSTEM_TIME
{
     unsigned long LowPart;
     long High1Time;
     long High2Time;
} KSYSTEM_TIME, *PKSYSTEM_TIME;

SystemTime Attribute

The SystemTime attribute is in my personal opinion one of the most, if not the most useful attribute stored within the _KUSER_SHARED_DATA structure. SystemTime is a 100 nanosecond timer that is measured from Janurary 1st, 1601 of which is stored as a _KSYSTEM_TIME structure (as shown in the above definitions). It is important to note that this timer is affected by the timezone that the machine is using. Something I found interesting is that whilst SYSTEMTIME is 12 bytes in size, only the first 8 matter as High1Time and High2Time are both equal.

On all version of Windows NT+, the SystemTime attribute is located at an offset of 0x14 from the beginning (0x7FFE0014).

How Can This Be Abused?

This can be abused in a variety of ways, one of which is to use it as an alternative method of sleeping without use of any pre-defined functions. This is beneficial when looking to evade automated defense solutions such as Anti-Virus and EDR’s (Endpoint Detection and Response), reason being modern detection solutions will attempt to skip over, or patch out known sleep or pause methods in order to stop attackers from delaying execution of malicious code within a process. If an attacker is able to pull this off, they can evade most behavioural analysis measures via simply waiting so long that the sandbox closes and assumes the sample is benign.

Get Epoch Time Without Function Calls in C

In order to get epoch time (relative to the user’s timezone at least), we first need to get the start of Unix epoch in ticks. I’ve done this for you, it’s 0x019DB1DED53E8000. There really isn’t much to explain here, simply read the values from the SYSTEMTIME attribute of SharedUserData, subtract the unix time start in ticks, and divide to get the level of accuracy you wish for. In this case, I used 100000 as I wanted epoch in seconds.

unsigned long long __get_timestamp()
{
	const size_t UNIX_TIME_START = 0x019DB1DED53E8000; // Start of Unix epoch in ticks.
	const size_t TICKS_PER_SECOND = 10000000; // A tick is 100ns.
	LARGE_INTEGER time;
	time.LowPart = *(DWORD*)(0x7FFE0000 + 0x14); // Read LowPart as unsigned long.
	time.HighPart = *(long*)(0x7FFE0000 + 0x1c); // Read High1Part as long.
	return (unsigned long long)((time.QuadPart - UNIX_TIME_START) / TICKS_PER_MILLISECOND);
}

This was thrown into a simple PoC, and you can see it working as follows:

PS C:\Users\jorda\Desktop\tmp> .\poc.exe
__get_timestamp() -> 1656935008

Time-Dependent Exploit Development

Another way this can be abused is when developing exploits. The folk at Uninformed are able to explain this far better than myself, and thus if you want more than a general overview, click here for their post on this.

Conclusion

Whilst this is a fairly simple concept, I figured it would be cool to share as I quite enjoyed the use cases for defense evasion and exploitation purposes. Thanks for reading and hopefully it wasn’t too boring! :)