0. TL; DR
本文介绍常见的内存查杀工具的技术原理,分析 UDRL AceLdr[1] 躲避内存查杀的几种技术实现。
1. 内存查杀工具
以下内存查杀工具是从 AceLdr 的 README 取的,基本囊括了大部分的内存扫描工具。
1.1 Hunt-Sleeping-Beacons
Hunt-Sleeping-Beacons[2] 首先使用 NtQuerySystemInformation
枚举所有进程的所有线程,找出存在 SYSTEM_THREAD_INFORMATION::WaitReason == DelayExecution
线程的进程,因为 Sleep
的线程处于该状态。(绕过该检测可以使用 WaitForSingleObject
类似函数,等待该函数返回的线程处于 UserRequest
状态)。该库为了检测 AceLdr 使用 WaitForSingleObject
的延时,加入了新检测方法:检测由 APC 发起的 WaitForSingleObject
调用。
对上一步筛选出来的进程做以下三个检测:
一、检测函数调用栈上的模块是否有 Module Stomping 或不存在于磁盘上,源码在 checkCallstack[3]。通过 StackWalk64
枚举模块,检测该模块是否能被获取到模块信息,.text 段是否被修改)。(忽略 .NET 程序以上两种异常,忽略 NTDLL 和 KERNELBASE 的 hooks)
二、检测线程 KernelTime 和 UserTime 的比值,源码在 checkLandDiff[4]。这个检测我不太懂,不作评价,作者的解释为:
Since a beacon spends more time waiting for commands than actually executing code, it can be fingerprinted by comparing the fields KernelTime and UserTime of SYSTEM_THREAD_INFORMATION. Initially I thought that the time sleeping would count as time spent in Kernelmode, but it turned out the other way. I am not sure why :’P Additionally, both fields increase only after the operator executed some commands with the beacon. Also here, I am not sure why :’P
三、检测 KERNEL32 上的 inline hooks,源码在 checkInlineHooks[5]。目的是检测 hook KERNEL32!Sleep
的情况。
1.2 BeaconHunter
BeaconHunter[6] 和上一个类似,首先筛选存在 DelayExecution
状态线程的进程,然后通过 ETW 提供的以下行为日志进行分析,计算出可疑分值。
•HTTP/HTTPS callbacks•DNS queries•File system (cd,ls,upload,rm)•Process termination (kill)•Shell commands (run,execute)
1.3 BeaconEye
BeaconEye[7] 大家应该很熟悉,通过读取内存数据,通过 yara 检测 Beacon的特征。
1.4 patriot
patriot[8] 检测以下特征:
一、检测线程 eip 指向 VM Protect 函数,源码见 FindSuspiciousContext[9]。该检测技术是针对 Ekko[10] 的一种很巧妙的延时函数混淆技巧。该技术来自 nighthawk,借助 Timer 线程执行一系列存储于 APC 队列的 ROP 来实现混淆的延时功能。技术细节见 NightHawk’s Attempt At Obfuscate and Sleep[11]。(检测使用的手法有点太不清真了,以8字节步长暴力搜索所有的 commit 内存)
二、检测内存中的 PE / DOS 头来检测 ProcessHollowing。
三、检测 elevated 进程中的可疑内存块,源码见 EnumerateMemory[12]。检测非 Image 且可执行且已 commit 的内存块。
四、检测 stomping / overwriting 导致的模块内容修改。源码见 ValidateIntegrity[13]。
五、检测 PE 异常,如 RWX 节,PE 头或节越界。源码见 ParseHeader[14] 。
1.5 moneta
moneta[15] 是一个非常不错的内存扫描工具,技术原理可学习作者的文章 Masking Malicious Memory Artifacts – Part II: Blending in with False Positives[16]。
moneta 的检测分以下步骤:
一、扫描动态或未知代码:
1.可执行的非 Image 内存。2.映射的 Image 中被修改的代码段。3.PEB 的 Image Base 或 线程起始地址不在 Image 的内存范围内。4.未签名 Image 中未被修改的代码(this is a soft indicator for hunting not a malware IOC)。
二、映射到内存的 PE Image 本身的可疑特征:
1.PE 节在内存中和磁盘上的保护属性不同。如磁盘上为 R,内存中为 RX。2.映射到内存中的 Image 的 PE 头被更改。3.映射的 Image 无法查询到 FILE_OBJECT
,代表无法找到对应的磁盘文件。
三、进程自身的异常:
1.内存中有一个映射的 Image ,其在 PEB 中没有对应的 modules entry。2.内存中的 Image,其在 PEB entry 中记录的 name 或 path 与 FILE_OBJECT
对应的不同。
1.6 pe-sieve
pe-sieve[17] 的扫描技术非常全面,能够扫描 replaced/injected PEs, shellcodes, hooks, and other in-memory patches. Detects inline hooks, Process Hollowing, Process Doppelgänging, Reflective DLL Injection, etc.
pe-sieve 代码量较大,具体实现没有细看,简单列举检测类别:
一、scanModules[18]:
1.检查映射模块的名称与磁盘名称是否相同。2.模块在磁盘上不存在。3.扫描 Hollowing,检测模块是否存在于 PEB entry 中,文件头是否被修改。4.检测 inline hooks。
二、scanModuleIATs[19],检查模块的 IAT hooks。
三、scanThreads[20],检查线程堆栈、Context 等,源码见 thread_scanner[21]。
(该项目的作者我关注很久了,其开源了很多注入、反射加载一类的对抗技术,比如 Process Ghosting、Process Doppelgänging、pe_to_shellcode 等,还有防御类的如 hollows_hunter。所以 pe-sieve 是一个真正懂攻防的人做的左右互搏的东西 : )
1.7 MalMemDetect
MalMemDetect[22] 编译为 DLL 并注入进程进行检测。
它一方面检测 Hollowing,另一方面 hook RtlAllocateHeap
、NtWaitForSingleObject
、InternetConnect
等函数,然后回溯线程调用栈寻找可疑内存。源码见 HookedInternetConnectW[23] & DetectHollowingAndHooks[24]
2. AceLdr
先看一下 AceLdr 的 features
Dynamic Memory Encryption
Creates a new heap for any allocations from Beacon and encrypts entries before sleep.
Code Obfuscation and Encryption
Changes the memory containing CS executable code to non-executable and encrypts it (FOLIAGE).
Return Address Spoofing at Execution
Certain WinAPI calls are executed with a spoofed return address (InternetConnectA, NtWaitForSingleObject, RtlAllocateHeap).
Sleep Without Sleep
Delayed execution using WaitForSingleObjectEx.
RC4 Encryption
All encryption performed with SystemFunction032.
分析它源码中的躲避内存扫描技术只需要分析几个 hook 函数就够了。
2.1 Sleep_Hook
Sleep_Hook[25] 采用了 FOLIAGE[26] 的技术,与上文讲到的 HightHawk 实现的延时混淆技术类似。
其核心是通过向单独创建的 Wait Thread 的 APC 队列插入一系列 Thread Context 组合的 ROP,依次执行 VM protect、堆栈加密( advapi32!SystemFunction032
) 、延时(WaitForSingleObject
)和这些步骤的逆操作。然后在主线程里使用 NtSignalAndWaitForSingleObject
触发 Wait Thread 链并等待其退出。
2.2 Heap_Hook
Heap_Hook 通过 Hook GetProcessHeap
来使 Beacon 获取到 AceLdr 初始化创建的堆,以此来使 Sleep 时能加密整个 Beacon 堆数据。
BeaconHeap = Api.ntdll.RtlCreateHeap( HEAP_GROWABLE, NULL, 0, 0, NULL, NULL );
fillStub( MemoryBuffer, BeaconHeap, Reg.Full );
...
SECTION( D ) HANDLE GetProcessHeap_Hook()
{
return ( ( PSTUB )OFFSET( Stub ) )->Heap;
};
2.3 Spoof
Spoof[27] 对 RtlAllocateHeap
、HeapAlloc
、InternetConnectA
、NtWaitForSingleObject
做 hook,混淆这些函数的调用堆栈。
混淆调用堆栈的原理见 x64 return address spoofing[28]。
3. AceLdr 的实际测试
通读全文的读者应该心里对这些内存检测和绕过技术有了一定了解,其实 AceLdr 的内存绕过技术不够全面,所以我有一些怀疑它是否能绕过这些检测工具。例如针对可执行的私有内存检测,这是 shellcode 常见特征,AceLdr 有这样一段代码明显就过不了检测
Status = Api.ntdll.NtAllocateVirtualMemory( ( HANDLE )-1, &MemoryBuffer, 0, &Reg.Full, MEM_COMMIT, PAGE_READWRITE );
if( Status == STATUS_SUCCESS )
{
...
Status = Api.ntdll.NtProtectVirtualMemory( ( HANDLE )-1, &MemoryBuffer, &Reg.Exec, PAGE_EXECUTE_READ, &OldProtection );
...
}
拿 pe-sieve 和 moneta 测试了一下,都是可以检出的:
C:Usersx>\MacHomeDownloadsMoneta64.exe -m ioc -p 9272
_____ __
/ ____ ____ _____/ |______
/ / / _ / _/ __ ____
/ Y ( <_> ) | ___/| | / __ _
____|__ /____/|___| /___ >__| (____ /
/ / / /
Moneta v1.0 | Forrest Orr | 2020
beacon.exe : 9272 : x64 : C:Usersxbeacon.exe
0x0000000000100000:0x00001000 | Private
0x0000000000100000:0x00001000 | RX | 0x00000000 | Abnormal private executable memory
...
C:Usersx>\MacHomeDownloadspe-sieve64.exe /shellc /pid 9272 /quiet /jlvl 2
---
PID: 9272
---
SUMMARY:
Total scanned: 53
Skipped: 0
-
Hooked: 3
Replaced: 0
Hdrs Modified: 52
IAT Hooks: 0
Implanted: 1
Implanted PE: 0
Implanted shc: 1
Unreachable files: 0
Other: 0
-
Total suspicious: 56
---
(上面的一些误报是因为 ARM Windows 的 xtajit64。)
虽然如此,但 AceLdr 依然是一个优秀的躲避内存扫描 UDRL。通过学习本文介绍的内存检测和躲避技术,可以针对性的集成到武器库中。
References
[1]
AceLdr: https://github.com/kyleavery/AceLdr[2]
Hunt-Sleeping-Beacons: https://github.com/thefLink/Hunt-Sleeping-Beacons[3]
checkCallstack: https://github.com/thefLink/Hunt-Sleeping-Beacons/blob/main/Hunt-Sleeping-Beacons.c#L58[4]
checkLandDiff: https://github.com/thefLink/Hunt-Sleeping-Beacons/blob/main/Hunt-Sleeping-Beacons.c#L305[5]
checkInlineHooks: https://github.com/thefLink/Hunt-Sleeping-Beacons/blob/main/Hunt-Sleeping-Beacons.c#L396[6]
BeaconHunter: https://github.com/3lp4tr0n/BeaconHunter[7]
BeaconEye: https://github.com/CCob/BeaconEye[8]
patriot: https://github.com/joe-desimone/patriot[9]
FindSuspiciousContext: https://github.com/joe-desimone/patriot/blob/main/patriot.cpp#L54[10]
Ekko: https://github.com/Cracked5pider/Ekko[11]
NightHawk’s Attempt At Obfuscate and Sleep: https://suspicious.actor/2022/05/05/mdsec-nighthawk-study.html[12]
EnumerateMemory: https://github.com/joe-desimone/patriot/blob/main/patriot.cpp#L157[13]
ValidateIntegrity: https://github.com/joe-desimone/patriot/blob/main/pefile.cpp#L231[14]
ParseHeader: https://github.com/joe-desimone/patriot/blob/main/pefile.cpp#L36[15]
moneta: https://github.com/forrest-orr/moneta[16]
Masking Malicious Memory Artifacts – Part II: Blending in with False Positives: https://www.forrest-orr.net/post/masking-malicious-memory-artifacts-part-ii-insights-from-moneta[17]
pe-sieve: https://github.com/hasherezade/pe-sieve[18]
scanModules: https://github.com/hasherezade/pe-sieve/blob/master/scanners/scanner.cpp#L314[19]
scanModuleIATs: https://github.com/hasherezade/pe-sieve/blob/master/scanners/scanner.cpp#L402[20]
scanThreads: https://github.com/hasherezade/pe-sieve/blob/master/scanners/scanner.cpp#L449[21]
thread_scanner: https://github.com/hasherezade/pe-sieve/blob/master/scanners/thread_scanner.cpp[22]
MalMemDetect: https://github.com/waldo-irc/MalMemDetect/[23]
HookedInternetConnectW: https://github.com/waldo-irc/MalMemDetect/blob/main/MalMemDetect/Source.cpp#L204[24]
DetectHollowingAndHooks: https://github.com/waldo-irc/MalMemDetect/blob/main/MalMemDetect/Hollow.h#L2[25]
Sleep_Hook: https://github.com/kyleavery/AceLdr/blob/main/src/hooks/delay.c#L468[26]
FOLIAGE: https://github.com/SecIdiot/FOLIAGE[27]
Spoof: https://github.com/kyleavery/AceLdr/blob/main/src/hooks/spoof.c[28]
x64 return address spoofing: https://www.unknowncheats.me/forum/anti-cheat-bypass/268039-x64-return-address-spoofing-source-explanation.html
原文始发于微信公众号(0x4d5a):躲避内存查杀 – AceLdr 技术解析