【转载】睡眠混淆技术分享

admin 2026-03-09 02:03:04 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 文档分享睡眠混淆技术,通过定时内存加解密与权限切换规避检测。重点解析Ekko和Cronos利用TimerQueue、ROP链及NtContinue实现休眠加密的机制。同时探讨防御方案,涵盖Hunt-Sleeping-Beacons对可疑计时器、异常调用栈等特征的扫描,以及EtwTi-FluctuationMonitor利用ETW监控内存权限波动的原理,为攻防对抗提供技术参考。 综合评分: 85 文章分类: 免杀,红队,二进制安全,安全工具,实战经验


cover_image

【转载】睡眠混淆技术分享

Arcueid Arcueid

隐雾安全

2026年3月8日 10:06 四川

好文推荐

文章作者:先知社区(Arcueid)

文章来源:https://xz.aliyun.com/news/17512

睡眠混淆是用于对抗内存检测的一项技术 在特定的触发器触发或定时执行内存加解密 改变权限 来规避AV检测

下面通过几个开源项目学习这种技术

sleep obfuscation

EKKO

https://github.com/Cracked5pider/Ekko

VOID EkkoObf( DWORD SleepTime ) {     CONTEXT CtxThread   = { 0 };
     CONTEXT RopProtRW   = { 0 };     CONTEXT RopMemEnc   = { 0 };     CONTEXT RopDelay    = { 0 };     CONTEXT RopMemDec   = { 0 };     CONTEXT RopProtRX   = { 0 };     CONTEXT RopSetEvt   = { 0 };
     HANDLE  hTimerQueue = NULL;     HANDLE  hNewTimer   = NULL;     HANDLE  hEvent      = NULL;     PVOID   ImageBase   = NULL;     DWORD   ImageSize   = 0;     DWORD   OldProtect  = 0;
     // Can be randomly generated     CHAR    KeyBuf[ 16 ]= { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 };     USTRING Key         = { 0 };     USTRING Img         = { 0 };
     PVOID   NtContinue  = NULL;     PVOID   SysFunc032  = NULL;
     hEvent      = CreateEventW( 0, 0, 0, 0 );     hTimerQueue = CreateTimerQueue();
     NtContinue  = GetProcAddress( GetModuleHandleA( "Ntdll" ), "NtContinue" );     SysFunc032  = GetProcAddress( LoadLibraryA( "Advapi32" ),  "SystemFunction032" );
     ImageBase   = GetModuleHandleA( NULL );     ImageSize   = ( ( PIMAGE_NT_HEADERS ) ( ImageBase + ( ( PIMAGE_DOS_HEADER ) ImageBase )->e_lfanew ) )->OptionalHeader.SizeOfImage;
     Key.Buffer  = KeyBuf;     Key.Length  = Key.MaximumLength = 16;
     Img.Buffer  = ImageBase;     Img.Length  = Img.MaximumLength = ImageSize;
     if ( CreateTimerQueueTimer( &hNewTimer, hTimerQueue, RtlCaptureContext, &CtxThread, 0, 0, WT_EXECUTEINTIMERTHREAD ) )     {         WaitForSingleObject( hEvent, 0x32 );
         memcpy( &RopProtRW, &CtxThread, sizeof( CONTEXT ) );         memcpy( &RopMemEnc, &CtxThread, sizeof( CONTEXT ) );         memcpy( &RopDelay,  &CtxThread, sizeof( CONTEXT ) );         memcpy( &RopMemDec, &CtxThread, sizeof( CONTEXT ) );         memcpy( &RopProtRX, &CtxThread, sizeof( CONTEXT ) );         memcpy( &RopSetEvt, &CtxThread, sizeof( CONTEXT ) );
         // VirtualProtect( ImageBase, ImageSize, PAGE_READWRITE, &OldProtect );         RopProtRW.Rsp  -= 8;         RopProtRW.Rip   = VirtualProtect;         RopProtRW.Rcx   = ImageBase;         RopProtRW.Rdx   = ImageSize;         RopProtRW.R8    = PAGE_READWRITE;         RopProtRW.R9    = &OldProtect;
         // SystemFunction032( &Key, &Img );         RopMemEnc.Rsp  -= 8;         RopMemEnc.Rip   = SysFunc032;         RopMemEnc.Rcx   = &Img;         RopMemEnc.Rdx   = &Key;
         // WaitForSingleObject( hTargetHdl, SleepTime );         RopDelay.Rsp   -= 8;         RopDelay.Rip    = WaitForSingleObject;         RopDelay.Rcx    = NtCurrentProcess();         RopDelay.Rdx    = SleepTime;
         // SystemFunction032( &Key, &Img );         RopMemDec.Rsp  -= 8;         RopMemDec.Rip   = SysFunc032;         RopMemDec.Rcx   = &Img;         RopMemDec.Rdx   = &Key;
         // VirtualProtect( ImageBase, ImageSize, PAGE_EXECUTE_READWRITE, &OldProtect );         RopProtRX.Rsp  -= 8;         RopProtRX.Rip   = VirtualProtect;         RopProtRX.Rcx   = ImageBase;         RopProtRX.Rdx   = ImageSize;         RopProtRX.R8    = PAGE_EXECUTE_READWRITE;         RopProtRX.R9    = &OldProtect;
         // SetEvent( hEvent );         RopSetEvt.Rsp  -= 8;         RopSetEvt.Rip   = SetEvent;         RopSetEvt.Rcx   = hEvent;
         puts( "[INFO] Queue timers" );
         CreateTimerQueueTimer( &hNewTimer, hTimerQueue, NtContinue, &RopProtRW, 100, 0, WT_EXECUTEINTIMERTHREAD );         CreateTimerQueueTimer( &hNewTimer, hTimerQueue, NtContinue, &RopMemEnc, 200, 0, WT_EXECUTEINTIMERTHREAD );         CreateTimerQueueTimer( &hNewTimer, hTimerQueue, NtContinue, &RopDelay,  300, 0, WT_EXECUTEINTIMERTHREAD );         CreateTimerQueueTimer( &hNewTimer, hTimerQueue, NtContinue, &RopMemDec, 400, 0, WT_EXECUTEINTIMERTHREAD );         CreateTimerQueueTimer( &hNewTimer, hTimerQueue, NtContinue, &RopProtRX, 500, 0, WT_EXECUTEINTIMERTHREAD );         CreateTimerQueueTimer( &hNewTimer, hTimerQueue, NtContinue, &RopSetEvt, 600, 0, WT_EXECUTEINTIMERTHREAD );
         puts( "[INFO] Wait for hEvent" );
         WaitForSingleObject( hEvent, INFINITE );
         puts( "[INFO] Finished waiting for event" );     }
     DeleteTimerQueue( hTimerQueue ); }

首先创建了一个事件 一个TimerQueue

然后获取NtContinue用于切换线程上下文 SystemFunction032 用于加密

CreateTimerQueueTimer的第三个参数是个回调RtlCaptureContext 用于获取线程上下文 填充到CtxThread

在if内进行ROP的初始化 利用NtContinue切换rip 依次执行修改内存属性 加密 sleep 解密 修改内存属性

对应的函数参数 作者给出了注释

以上这些事是工作线程做的 最终SetEvent 使控制流返回主线程

CreateTimerQueueTimer 底层调用ntdll!RtlCreateTimer 调用了ntdll!TpSetTimerEx 调用了ntdll!TppSetTimer

在TppSetTimer中调用TppETWTimerSet 最终会调用NtTraceEvent记录

但是实际上在TppEtwTimerSet上下断并断不下来

SharedData是空的

Cronos

https://github.com/Idov31/Cronos

创建定时器 同样通过RtlCaptureContext 获取线程上下文

从加载的所有模块中搜特征码

同样通过Ntcontinue切换上下文

QuadSleep纯汇编实现

rcx: pop rcx; ret

rdx: pop rdx; ret

r8: add rsp,20; pop rdi; ret

r9: SleepEx

调用SleepEx从而APC

修改内存属性为RW -> 加密 -> 解密 -> 修改内存属性为RWX

检测

Hunt-Sleeping-Beacons

具体WorkFactory相关知识可以参考

https://urien.gitbook.io/diago-lima/a-deep-dive-into-exploiting-windows-thread-pools/attacking-worker-factories

直接跟进process_scanner::scan_processes

对传进来的process数组进行扫描 具体的扫描行为定义在

suspicious_timer

通过NtQuerySystemInformation 查询进程句柄信息 获取所有WorkerFactory

每个WorkerFactory都有对应的StartRoutine 由其创建的工作线程执行

通过NtQueryInformationWorkerFactory 可以查询StartRoutine

这里通过该API查询 获得StartParameter 一个指向 TP_POOL的指针

TimerQueue就处于这个结构中

typedef struct _FULL_TP_POOL {     struct _TPP_REFCOUNT Refcount;     long Padding_239;     union _TPP_POOL_QUEUE_STATE QueueState;     struct _TPP_QUEUE* TaskQueue[3];     struct _TPP_NUMA_NODE* NumaNode;     struct _GROUP_AFFINITY* ProximityInfo;     void* WorkerFactory;     void* CompletionPort;     struct _RTL_SRWLOCK Lock;     struct _LIST_ENTRY PoolObjectList;     struct _LIST_ENTRY WorkerList;     struct _TPP_TIMER_QUEUE TimerQueue;     struct _RTL_SRWLOCK ShutdownLock;     UINT8 ShutdownInitiated;     UINT8 Released;     UINT16 PoolFlags;     long Padding_240;     struct _LIST_ENTRY PoolLinks;     struct _TPP_CALLER AllocCaller;     struct _TPP_CALLER ReleaseCaller;     volatile INT32 AvailableWorkerCount;     volatile INT32 LongRunningWorkerCount;     UINT32 LastProcCount;     volatile INT32 NodeStatus;     volatile INT32 BindingCount;     UINT32 CallbackChecksDisabled : 1;     UINT32 TrimTarget : 11;     UINT32 TrimmedThrdCount : 11;     UINT32 SelectedCpuSetCount;     long Padding_241;     struct _RTL_CONDITION_VARIABLE TrimComplete;     struct _LIST_ENTRY TrimmedWorkerList; } FULL_TP_POOL, * PFULL_TP_POOL;

通过判断函数地址 以确认是否恶意

blocking_apc

遍历堆栈 检查是否调用了KiUserApcDispatcher

blocking_timer

遍历堆栈,检查是否调用了 RtlpTpTimerCallback

abnormal_intermodular_call

检测异常调用堆栈 通过判断kernelbase或kernel32 是否是由ntdll调用过来的 以决定是否恶意

因为正常情况下是不存在这种调用的

return_address_spoofing

检测返回地址是否为 jmp rbx, jmp rbp等

stomped_module

遍历调用堆栈 判断其返回地址的模块是否是原始共享模块(即未被修改) 来判断是否恶意 (一些关键dll不在此列)

hardware_breakpoints

检测DR0-3 DR7 从而判断是否启用了硬件断点 以判断是否恶意

non_executable_memory

判断地址是否没有任一 执行权限 PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY

调用堆栈中的地址没有执行权限 这是不正常的

EtwTi-FluctuationMonitor

https://github.com/jdu2600/EtwTi-FluctuationMonitor

Black hat ASIA 2023 You Can Run, but You Can’t Hide – Finding the Footprints of Hidden Shellcode 提到

一块内存反复的修改权限 是不正常的 可以设置一个阈值 当该内存修改次数达到阈值时告警

通过订阅Microsoft-Windows-Threat-Intelligence的KERNEL_THREATINT_KEYWORD_PROTECTVM_LOCAL

设置回调 判断是否是第一次发生权限变化

变化后插入g_ImmutableCodePages 即标记为不可变

如果后续发生变化 且是不可变的 则输出…is fluctuating 也就是说这里的阈值是1


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:隐雾安全 Arcueid Arcueid《【转载】睡眠混淆技术分享》

藏蓝芳华,守护清朗 网络安全文章

藏蓝芳华,守护清朗

文章总结: 本文介绍了梧州市公安局网安支队民警陈杰慧的先进事迹。她深耕网络案件打击一线,参与侦办涉网刑事案件300余起,挽回损失上千万元。她主动转型网安领域,研
评论:0   参与:  0