当前位置: 首页 > article >正文

内存免杀--

通过分析Ekko项目了解内存加密过程,这对对抗内存扫描来说很重要。

概述

Edr会扫描程序的内存空间,检测是否存在恶意软件,这种检测恶意软件的方式,应该和静态检测没什么区别,只不过一个扫描的对象是硬盘,一个是内存,应该都是基于特征码来检测的,为了绕过这种扫描我们可以对内存中的beacon进行加密,但这样的话就会出现问题,因为beacon被加密后是无法正常的运行的,因为代码被加密了。
#
我们提到过自己是不可以加密自己的,但是Ekko项目实现了自己加密自己的内存,这对对抗内存扫描很有帮助。

API理解

CreateEvent()创建或打开一个事件对象

SetEvent()设置对象状态已通知状态

CreateTimerQueue()创建计时器队列

CreateTimerQueueTimer()创建计时器队列计时器


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
);

phNewTimer接受计时器队列计时器的缓冲区

TimeQueue计时器队列句柄

CallBack回调函数,计时器过期时要执行的函数

Parameter回调函数的参数

DueTime过多长时间向计时器发信号

Period计时器的周期,如果为0就向计时器发信号

Flag标志,指定函数调用相关内容。

对于CreateTimerQueueTimer函数可以这样理解,它可以定时执行一个函数,应该是异步执行的。

WaitForSingleObject()等待某个对象的状态,如果状态为已通知就往下执行,或者等待超时也会往下执行,也可以设置一直等待

SystemFunction032()通过RC4加密方式加密内存

RtlCaptureContext()得到当前线程的CONTEXT结构

对于CONTEXT结构可以理解为:用于存储线程运行时寄存器的值,RIP,RSP......

NtContinue()对于这个函数确实不太理解,这里只需要知道给它传进去一个CONTEXT结构,就可以执行CONTEXT对应的代码,也就是可以控制程序执行流程。

Ekko代码阅读

一些不太重要的代码会跳过去

image.png

  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)((DWORD64)ImageBase + ((PIMAGE_DOS_HEADER)ImageBase)->e_lfanew))->OptionalHeader.SizeOfImage;//大小

这段代码通过调用CreateTimerQueue创建了一个计时器队列接着,通过调用GetModuleHandleAGetProcAddress得到了NtContinueSystemFunction032函数的地址,接着又得到了当前程序的基地址和当前程序的大小

image.png

if (CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)RtlCaptureContext, &CtxThread, 0, 0, WT_EXECUTEINTIMERTHREAD))//调用RtlCaptureContext得到CONTEXT结构
    {
        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));

通过CreateTimerQueueTimer创建了一个计时器,在创建后会立刻调用(因为DueTimeperiod都是0)RtlCaptureContext函数得到当前线程的CONTEXT结构,接着使用得到的CONTEXT结构初始化了一些CONTEXT结构,为下面的调用做准备。

image.png

        //构造VirtualProtect函数的CONTEXT结构,此函数用于修改内存权限
        // VirtualProtect( ImageBase, ImageSize, PAGE_READWRITE, &OldProtect );
        RopProtRW.Rsp -= 8;
        RopProtRW.Rip = (DWORD64)VirtualProtect;
        RopProtRW.Rcx = (DWORD64)ImageBase;
        RopProtRW.Rdx = (DWORD64)ImageSize;
        RopProtRW.R8 = PAGE_READWRITE;
        RopProtRW.R9 = (DWORD64)&OldProtect;
        //构造SystemFunction032函数的CONTEXT结构,此函数用于加密内存
        // SystemFunction032( &Key, &Img );
        RopMemEnc.Rsp -= 8;
        RopMemEnc.Rip = (DWORD64)SysFunc032;
        RopMemEnc.Rcx = (DWORD64)&Img;
        RopMemEnc.Rdx = (DWORD64)&Key;
        //构造WaitForSingleObject函数的CONTEXT结构,此函数用于睡眠
        // WaitForSingleObject( hTargetHdl, SleepTime );
        RopDelay.Rsp -= 8;
        RopDelay.Rip = (DWORD64)WaitForSingleObject;
        RopDelay.Rcx = (DWORD64)NtCurrentProcess();
        RopDelay.Rdx = SleepTime;
        //构造SystemFunction032函数的CONTEXT结构,此函数用于解密内存
        // SystemFunction032( &Key, &Img );
        RopMemDec.Rsp -= 8;
        RopMemDec.Rip = (DWORD64)SysFunc032;
        RopMemDec.Rcx = (DWORD64)&Img;
        RopMemDec.Rdx = (DWORD64)&Key;
        //构造VirtualProtect函数的CONTEXT结构,此函数用于修改内存权限
        // VirtualProtect( ImageBase, ImageSize, PAGE_EXECUTE_READWRITE, &OldProtect );
        RopProtRX.Rsp -= 8;
        RopProtRX.Rip = (DWORD64)VirtualProtect;
        RopProtRX.Rcx = (DWORD64)ImageBase;
        RopProtRX.Rdx = (DWORD64)ImageSize;
        RopProtRX.R8 = PAGE_EXECUTE_READWRITE;
        RopProtRX.R9 = (DWORD64)&OldProtect;
        //构造SetEvent函数CONTEXT结构,设置对象为已通知状态
        // SetEvent( hEvent );
        RopSetEvt.Rsp -= 8;
        RopSetEvt.Rip = (DWORD64)SetEvent;
        RopSetEvt.Rcx = (DWORD64)hEvent;

这段代码为调用VirtualProtectSystemFunction032......做准备
主要通过给CONTEXT结构的成员赋值,来模拟对应函数的调用(当然这里不是真的调用)

image.png

    //等100毫秒向计时器发信号,通过NtContinue调用VirtualProtect取消内存可执行权限
        CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)NtContinue, &RopProtRW, 100, 0, WT_EXECUTEINTIMERTHREAD);
        //等200毫秒向计时器发信号,通过NtContinue调用SystemFunction032加密内存
        CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)NtContinue, &RopMemEnc, 200, 0, WT_EXECUTEINTIMERTHREAD);
        //等300毫秒向计时器发信号,通过NtContinue调用WaitForSingleObject进行睡眠
        CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)NtContinue, &RopDelay, 300, 0, WT_EXECUTEINTIMERTHREAD);

这段代码负责加密,主要通过CreateTimerQueueTimer函数创建计时器,计时器在等待指定的毫秒后,调用回调函数(也就是调用NtContinue)而NtContinue则会根据我们传入的CONTEXT结构去执行对应的函数。

比如第一行,传入的是VirtualProtect函数的CONTEXT结构,那么NtContinue将会调用VirtualProtect

下面是调用流程,画的有点丑,师傅们见谅^ _^......

image.png
第二行就是调用SystemFunction032来对内存进行加密
第三行就是调用WaitForSingleObject来实现睡眠,SleepTime就是睡眠的时间

image.png
下面来看一下解密内存部分

 //等400毫秒向计时器发信号,通过NtContinue调用SystemFunction032解密内存
        CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)NtContinue, &RopMemDec, 400, 0, WT_EXECUTEINTIMERTHREAD);
        //等500毫秒向计时器发信号,通过NtContinue调用VirtualProtect更改内存权限为可执行
        CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)NtContinue, &RopProtRX, 500, 0, WT_EXECUTEINTIMERTHREAD);
        //等600毫秒向计时器发信号,通过NtContinue调用SetEvent设置对象为已通知状态
        CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)NtContinue, &RopSetEvt, 600, 0, WT_EXECUTEINTIMERTHREAD);

第一行通过调用SystemFunction032来解密内存,第二行调用VirtualProtect设置内存权限为可读可写可执行,第三行调用SetEvent将事件对象设置为已通知状态。
内存加密部分执行流程

image.png

主要的代码已经介绍完了,下面来看一下效果。

效果

睡眠前

image.png
睡眠后

image.png
因为作者水平有限,文章中难免会出现错误和不足之处,如果有哪些不好的地方希望各位师傅斧正。

参考

https://github.com/Cracked5pider/Ekko Ekko项目地址


http://www.kler.cn/a/153718.html

相关文章:

  • ubuntu18编译Android8的Failed to contact Jack server问题
  • 激光SLAM:Faster-Lio 算法编译与测试
  • python+pytest接口自动化(6)-请求参数格式的确定
  • LeetCode56. 合并区间
  • FL Studio2024重磅更新 带你了解FL21.2最新版本功能
  • C语言 柔性数组
  • RK3568笔记六:基于Yolov8的训练及部署
  • LCR 047. 二叉树剪枝 和 leetCode 1110. 删点成林 + 递归 + 图解
  • CentOS 系列:CentOS 7 使用 virt-install + vnc 图形界面/非图形界面 创建虚拟机
  • clickhouse的向量化执行
  • 熬夜会秃头——Beta冲刺总结随笔
  • 39.从0到上线三天搭建个人网站(第三天)
  • Node.js入门指南(完结)
  • 【Apache Doris】Manager极致丝滑地运维管理
  • IO流--12--Java lO 设计模式
  • Kibana使用指南
  • 开源图床Qchan本地部署远程访问,轻松打造个人专属轻量级图床
  • 「Verilog学习笔记」自动贩售机1
  • MySQL索引有哪些优缺点
  • Redis Cluster