首页
社区
课程
招聘
[翻译]gargoyle,一个逃过内存扫描的技术
发表于: 2017-3-16 22:44 7366

[翻译]gargoyle,一个逃过内存扫描的技术

2017-3-16 22:44
7366

gargoyle,一个逃过内存扫描的技术

201734

  gargoyle是一项将一个程序的所有可执行代码都隐藏在不可执行的内存块中的技术。在一些程序员定义区间内,gargoyle会活跃起来——结合一些ROP欺骗——把自己标记为可执行并发挥作用。

  在扫描内存来寻找异常时,通常会扫描可执行的内存块。gargoyle可以在Windows上实现将数据隐藏在不可执行的内存块中。

  1.gargoyle产生一些任意代码后,设置一些尾递归。

  2.VirtualProtectEx函数标记gargoyle为不可执行并返回到WaitForSingleObjectEx函数,该函数在等待我们发送Windows计时器给它。

  3.计时器的结束程序是一个ROP配置,pop *pop espret。它会把堆栈指针移到一个我们可以仔细地手动控制的堆栈中。

  4.我们特殊的堆栈让ret调用VirtualProtectEx函数,使我们可以读//执行。

  5.VirtualProtectEx函数返回到gargoyle,继续下一个循环。

  此处只展示了该技术在32Windows系统上的应用。下面,我们来挖掘该技术实现的所有细节。


动态内存分析

  动态的分析内存确实是一项代价很高的操作——如果你在使用Windows Defender,可能已经面临这个问题了(Google搜索 Antimalware Service Executable)(译者注:这是Windows Defender的一个进程,在后台执行扫描时会出现CPU和内存使用率居高不下的情况)。因为程序必须在可执行内存块中常驻,所以一种减少计算负担的技术就是只分析可执行代码页。在许多进程中,需要分析的内存数量会减少一个数量级。

  gargoyle显示这是一个有风险的行为。通过使用Windows异步过程调用,只读/写内存可以被调用为可执行内存来执行一些任务。一旦它完成了任务,就会恢复到读/写内存,直到计时器到期再重复这个循环。

  当然,Windows API中不存在InvokeNonExecutableMemoryOnTimerEx函数。重复这个循环需要其他的一些工作......


Windows异步过程调用(APCWindows Asynchronous Procedure

  异步编程允许一些任务推迟执行,可能是在一个单独线程的执行上下文中。每个线程都有自己的APC队列,当一个线程进入alertable状态时,Windows会从线程的APC队列调度工作到等待线程中。

有很多方法排队APC

   ·ReadFileEx

   ·SetWaitableTimer

   ·SetWaitableTimerEx

   ·WriteFileEx

  有很多方法让线程进入alertable状态:

   ·SleepEx

   ·SignalObjectAndWait

   ·MsgWaitForMultipleObjectsEx

   ·WaitForMultipleObjectsEx

   ·WaitForSingleObjectEx

  我们采用一个组合,用CreateWaitableTimer创建一个计时器,然后用SetWaitableTimer排队APC


  这些默认的安全属性就很好,我们不用手动重置,也不用命名计时器。所以CreateWaitableTimer的所有参数都默认为0nullptr。函数返回一个句柄给我们新定义的计时器。接着,我们需要排队APC

  第一个参数是我们从CreateWaitableTimer得到的句柄。pDueTime参数是一个指向LARGE_INTEGER的指针,指定第一个计时器到期的时间。例如,我们将它简单设置为0(马上到期)。lPeriod参数定义了毫秒级的到期间隔。这决定了时钟频率,即哪个gargoyle会被调用。

  下一个参数pfnCompletionRoutine是我们重点考虑的部分。这表示Windows调用等待线程的地址。当APC在被派遣时,可执行内存块中是没有gargoyle代码的。如果我们想在gargoyle中找到pfnCompletionRoutine,我们需要以一个数据执行保护(DEP)冲突来解决。

  相反,我们使用一种特殊的返回导向编程(ROP)二进制指令代码块(gadget)(译者注:gadget是从函数开始到ret返回指令之间的代码块,ROP技术按照一定顺序拼接这些代码,即通过代码复用,来执行想要的操作),通过lpArgToCompletionRoutine,将可执行线程的堆栈定位到SetWaitableTimer的第二个参数的地址指针。当ROP代码块中执行返回指令ret S,预先准备好的特殊的返回栈会调用VirtualProtectEx函数来标记gargoyle 为可执行。

  最后一个参数表示是否在计时器到期时唤醒一个休眠的计算机。我们在这里将其设置为false


Windows数据执行保护(DEP)和VirtualProtectEx函数

  最后使用的是VirtualProtectEx函数,用来标记内存中的各种安全属性:

  我们会两次调用VirtualProtectExgargoyle执行完之后(我们将线程标记为alertable状态之前)和gargoyle开始执行时(线程派遣APC完成之后)。更多细节请看信息图。

  在本文的证明中,gargoyletrampolineROP代码块和读/写内存都是同一个进程的,所以第一个参数hProcess相当于 GetCurrentProcess。参数lpAddress等于gargoyle的地址,参数dwSize等于gargoyle可执行内存块的大小。我们将想要的内存保护属性赋给参数flNewProtect。我们不用关心原先的保护属性是什么,但是lpflOldProtect不是一个可选属性,所以我们把它指向一些预先留出的空内存。

  唯一取决于上下文的参数是flNewProtect。当gargoyle转为休眠状态时,我们将其标记为PAGE_READWRITE0x04。在gargoyle转为可执行之前,我们将其标记为PAGE_EXECUTE_READ0x20


堆栈跳板


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 1
支持
分享
最新回复 (7)
雪    币: 719
活跃值: (777)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
2
MARK
2017-3-16 23:46
0
雪    币: 4751
活跃值: (1783)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
一语中的 柳暗花明
2017-3-17 15:06
0
雪    币: 878
活跃值: (737)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
4
mark
2017-3-17 16:13
0
雪    币: 3542
活跃值: (1872)
能力值: ( LV6,RANK:93 )
在线值:
发帖
回帖
粉丝
5
粗略看了下,貌似是标题党啊
2017-3-17 16:36
0
雪    币: 433
活跃值: (1910)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
6
隐藏模块的技术终于要公开了啊哈哈哈哈哈哈
2017-3-17 20:35
0
雪    币: 5828
活跃值: (3644)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
不错的分析
2017-3-17 20:51
0
雪    币: 3574
活跃值: (4719)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
lynnux 粗略看了下,貌似是标题党啊[em_16]
。。。没任何意义。。。就是完整的标题党。
2017-3-18 19:32
0
游客
登录 | 注册 方可回帖
返回
//