内存休眠时混淆技术二:相知

admin 2024年2月4日10:26:53评论20 views字数 3775阅读12分35秒阅读模式

内存休眠时混淆技术相知

前言

又是许久未更的一篇文章,不出意外的话,这应该是我农历年之前的最后一更了,感谢默默关注我的小伙伴们,在这里提前祝大家新年快乐,希望在新的一年里带给大家更多更好的文章,话不多说开始今天的内容!

上篇文章中我们了解了基本的内存休眠时混淆技术,通过利用shellcode实现每隔固定时间对模块各个节加解密操作。本节将继续深入这一话题,让我们看看“现代”技术对此进行了哪些升级。

【友情提示】 :代码可在此获得 https://github.com/jseclab/wechat_public/blob/main/sleepcrypt/p2/main.c

正文

概述

所谓更为现代的内存休眠加密技术大概是由从 foliage [1]开始的,而foliage则是对石像鬼技术的升级完善,在之后,foliage作者对号称下一代恶意软件的nighthawk中使用的内存休眠加密技术进行了分析(通过VT上的样本,Nighthawk是闭源软件)后整理成文并发表,随后5pider针对文中所述对此技术进行了复现,也就是可能很多人正在用的ekko(ex),同时 Bokuloader[2] 也集成了此技术。

石像鬼回顾

在开始之前先让我们简单回顾一下 来自2017年的石像鬼技术 ,给没有看过的朋友简单提一下,这项技术在当时主要是为了规避杀毒引擎扫描器扫描可执行内存,石像鬼(gargoyle) 技术则利用一段巧妙构思的shellcode实现定时变换内存节属性来规避扫描,下图基本展示了这一技术的全过程。

  • • 主程序申请内存存放shellcode

  • • shellcode初始化计时器对象和构造堆栈,最终调用VirtualProtec修改内存属性,随后等待计时器对象触发(红色

  • • 等待期间shellcode内存变为只读状态(灰色

  • • 计时器触发并执行回调,通过ROP返回到VirtualProtect修改内存属性为可读可执行

  • • 最后再次跳转到shellcode执行,周而复始(当然非首次执行将不再对计时器对象进行初始化操作)。

  • 内存休眠时混淆技术二:相知

关于石像鬼我们只需要了解两点

1:其shellcode实现了内存属性自修改(我改我自己)

2:没有实现内存加解密

”传统“技术缺陷

1:需要用到shellcode,就得为其分配一段可执行内存(当然也可以用RWX自注入等,非重点不讨论)

2:整个过程shellcode内存本身没有得到加密

3:自己实现加密算法,简单的异或甚至可能会被解密

4:随着依据调用栈检测逐渐流行,shellcode调用来自一段非可执行的内存区域可能会变得可疑

内存休眠时混淆技术二:相知

”现代“技术的诞生

前面提到,石像鬼的巧妙构思居然可以让一段shellcode实现定时修改自身的内存保护属性,而且不会导致崩溃,但是不方便的点在于,如果我们想添加要保护的代码到其中,就必须在shellcode中编写,这对开发者来讲及其不友好,当然他只是一个POC,并不需要过多的苛责。

试想一下,如果我们能将其改写为模块本身的代码,而非一段独立运行的代码,同时想想办法在加上加解密的特性,那么我们就可以几乎解决传统内存休眠加密技术中的所有缺点,而这就是”现代“内存休眠加密技术,不使用shellcode,而是利用原生API巧妙实现休眠,不使用自定义的算法,而使用windows自带的加解密接口实现加解密,怎么越听越像LOLBin(Living off the land Binaries)了呢 内存休眠时混淆技术二:相知

Ekko(ex)的QueueUserAPC实现

实际上ekko对此技术的实现代码量并不是非常多,但是初看会感到难以理解,不过没关系,我们先从其目的入手,其根本还是在于实现自身内存的加解密,只不过在当中要休眠一段时间在解密然后执行,如上一节所述,我们依然需要一个外协小帮手(其他线程),而自身是实现不了的,不同于使用shellcode,这次我们要用到线程回调

但是实现这一过程,我们需要调用好几个API,比如修改属性的,加密内存的,还有休眠的,我们的外协小帮手自然是不知道的,那我们主线程如何告知小帮手怎么做呢?答案是通过线程队列,主线程在进入等待状态之前就需要把这些事情给安排好。

关于线程队列,每个线程都有一个自身APC队列,一般线程处于警戒态时候才会执行队列中的回调(Win 11才支持的QueueUserAPC2则可以让线程在非警戒态下直接调用)

内存休眠时混淆技术二:相知

既然如此,ekko通过调用CreateTimerQueueTimer告诉小助手要干什么,如下所示,第三个参数callback代表小助手要执行的函数,第四个参数Parameter是回调函数的参数,那么问题就来了,如果我要调用的函数参数有好几个怎么办 :?

BOOL CreateTimerQueueTimer(
  [out]          PHANDLE             phNewTimer,
  [in, optional] HANDLE              TimerQueue,
  [in]           WAITORTIMERCALLBACK Callback,
  [in, optional] PVOID               Parameter,
  [in]           DWORD               DueTime,
  [in]           DWORD               Period,
  [in]           ULONG               Flags
);

这~,就得感谢NtContinue了,在64位下我们通过寄存器传递前四个参数,而这都可以通过修改NtContinue的参数一的Context中的寄存器来实现。

NTSYSAPI 
NTSTATUS
NTAPI
NtContinue(
  IN PCONTEXT             ThreadContext,
  IN BOOLEAN              RaiseAlert );

而这其中比较难的是Context.Rsp该如何设置?

通过调试Ekko可以发现通过NtContinue调用的函数最终总是会返回到ntdll.RtlpTpTimerCallback某个偏移处。

内存休眠时混淆技术二:相知

那么EKKO是如何设置ESP的值,同时这个ESP中存储的刚好是返回地址呢?那下面就需要用到 RtlCaptureContext了,我们先向小助手请求调用RtlCaptureContext,随后我们就能从ContexRecord中获取其Rsp的值,而*(Rsp-0x8)存放的就是返回地址。

NTSYSAPI
VOID
NTAPI
RtlCaptureContext(
    _Out_ PCONTEXT ContextRecord
    );

内存休眠时混淆技术二:相知

如果理解了这些点,那再看EKKO的代码就能理解个七七八八了。

既然如此,那我是否可以利用我们更为常见的QueueUserAPC API来达到同样目的?

当然可以!

  • • 创建线程,使其处于警戒状态

DWORD WINAPI QueueApcThread( LPVOID EvtHandle )
{
    WaitForSingleObjectEx(GetCurrentProcess(), INFINITE, TRUE);
    return 0;
}
  • • 通过RtlCaptureContext先行获取RSP

QueueUserAPC((PAPCFUNC)RtlCaptureContext, ThreadHd, (ULONG_PTR)&Ctx);
QueueUserAPC(WaitEventApc, ThreadHd, (ULONG_PTR)StartEvtHd); //防止竞争
  • • 依次将其他调用放入队列

QueueUserAPC((PAPCFUNC)NtContinue, ThreadHd, (ULONG_PTR)&Vp);
QueueUserAPC((PAPCFUNC)NtContinue, ThreadHd, (ULONG_PTR)&Enc);
QueueUserAPC((PAPCFUNC)NtContinue, ThreadHd, (ULONG_PTR)&Slp);
QueueUserAPC((PAPCFUNC)NtContinue, ThreadHd, (ULONG_PTR)&Dec);
QueueUserAPC((PAPCFUNC)NtContinue, ThreadHd, (ULONG_PTR)&Vp1);
QueueUserAPC((PAPCFUNC)NtContinue, ThreadHd, (ULONG_PTR)&EndEvt);
  • • 激活对象通知小助手开始干活,并等待其结束

SignalObjectAndWait(StartEvtHd, EndEvtHd, INFINITE, FALSE);

成果展示

引用

[1]:https://github.com/realoriginal/foliage

[2]:https://github.com/boku7/BokuLoader

ekko:https://github.com/Cracked5pider/Ekko/tree/main

原文始发于微信公众号(无名之):内存休眠时混淆技术二:相知

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年2月4日10:26:53
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   内存休眠时混淆技术二:相知http://cn-sec.com/archives/2466366.html

发表评论

匿名网友 填写信息