Windows Defender内存扫描功能分析

大家好!这里是219攻防实验室!

219攻防实验室专注于前沿攻防研究、武器平台化、红队评估、攻防演练,虽然我们是新实验室,但我们的成员有深入操作系统多年的老牌程序员、有专注攻防领域10余年的老手、亦有攻防演练中崭露头角的新锐,我们热爱技术热爱分享,就像我们的名字一样,219 爱依旧,对技术的爱永远依旧。
      Defender是Windows系统自带的反病毒软件,从Vista开始直接内置在Windows系统中,默认处于开启状态,对大部分普通用户来说是其系统安全基础保障,因此也是安全研究者一个重要的研究目标。

0x00 关于内存扫描

    内存扫描也称运行时分析(Run-time Analysis)与传统的文件静态扫描类似,一般依赖一组已知的特征码对恶意代码进行识别,与静态扫描不同的是内存扫描分析的对象是内存(RAM),而不是磁盘上的文件。因为依赖特征码,内存扫描一般无法检测未知的恶意代码,但是误报率接近于零并且可以识别出恶意代码家族。
     相较于静态扫描,内存扫描对攻击者更具挑战,因为在内存中混淆代码的难度更大。攻击者可以通过各类无文件攻击(Fileless Attack)、代码加密混淆、加壳等技术避免静态扫描(包括基于文件的机器学习检测)或者修改文件特征,但运行时内存特征一般不变。如各种Loader免杀方案一般是通过使用不同编程语言与编码或者加密算法组合修改恶意代码的文件特征,但执行后始终会将原始镜像在内存中解密执行。

0x01 分析目标及思路

      将基于静态分析与动态调试相结合的方式对Defender的内存扫描功能进行简单分析,主要目标如下:
  • 确定内存扫描功能具体由哪个模块实现以及实现方式;
  • 定位内存中具体哪段内存数据是恶意代码特征码;
  • 内存扫描如何触发的,流程是怎样的。
分析思路:
  • 先静态看模块描述,根据模块描述对功能进行猜测;
  • 对模块功能进行求证,微软的软件一般都有pdb,静态反编译后看下函数名,找下是否有内存扫描相关函数;
  • 动态分析,对相关函数下断点,根据函数调用堆栈进行分析,分析具体的流程及实现。

0x02 分析过程

1. 模块分析

      根据资料已知MsMpEng.exe是Defender反病毒软件主服务进程,分析版本为4.18.2209.7-0,查看该进程加载的模块如下:

Windows Defender内存扫描功能分析

根据已有资料及模块描述猜测模块功能:
  • MpRtp.dll: 运行时监控,一般通过和驱动通信获取监控信息,触发其他扫描,内存扫描如何触发应该和这个模块相关。
  • MpClient.dll/MpSvc.dll: 信息较少,应该是给其他组件提供的接口,可能有内存扫描相关接口和实现。
  • mpengine.dll: Defender反病毒核心引擎,特征库匹配、恶意代码判定相关,之前的研究基本都是针对这个模块进行的分析的,还包含本地脱壳、本地沙箱模拟等功能,内存扫描特征码匹配应该在这。
      然后开始静态反编译分析这几个模块,与预想的不一致,这几个模块微软都没有提供pdb(后面微软又上传了mpengine.dll的pdb),暂时使用之前有pdb版本文件进行分析,通过内存扫描相关关键字及模块导入表大致罗列下这几个模块关键函数及功能猜测,方便后续通过动态分析验证。
  • MpRtp.dll
//通过微文件端口与WdFilter.sys进行通信获取驱动回调监控信息,可能与触发内存扫描相关RealtimeProtection::CThreadPoolIoFilterRequest::DoOverlappedOperationRealtimeProtection::CFilterCommunicatorBase::CommunicatorMainFunctionRealtimeProtection::CFilterCommunicatorBase::ParkFilterRequestToFilterFilterGetMessage
      对MpRtp.dll的静态分析主要通过微文件端口通信相关的API展开的,微文件端口是实现驱动层与应用层双向实时通信的一种方式,后续动态调试重点是什么行为触发了内存扫描,具体触发流程。
  • MpClient.dll
//通过内存扫描关键字可以找到一些内存扫描相关函数MpFastMemoryScanOpen MpClient::CMpMemoryScan::HandleScanEvents
      MpClient.dll中存在一些内存扫描相关函数,不过从函数逻辑来看并未发现实现代码,只是一些调用接口和导出函数,MpClient::CMpMemoryScan::HandleScanEvents从函数名称来看是处理内存扫描事件的,该函数中又通过NdrClientCall3函数通过RPC调用到其他模块,可以通过静态分析获取到该RPC接口的UUID c503f532-443a-4c69-8300-ccd1fbdb3839,调用的ProcNum 0x5f。

Windows Defender内存扫描功能分析

  • MpSvc.dll
OnDemandStartScanMpService::NewScanContextMpService::CMpSvcScanWorkItem::OnAction(void)MpService::CMpSvcScanWorkItem::Run(void)MpService::CGlobalEventsJob::OnActionMpService::CMpMemScanEngineVfz::vfz_Read
      静态分析MpSvc.dll时发现很多关于内存扫描相关的函数名,还在导入表中发现ReadProcessMemory函数,ReadProcessMemory功能为读取其他进程内存数据,该函数可能和内存扫描相关。除此之外MpSvc.dll实现了c503f532-443a-4c69-8300-ccd1fbdb3839 服务端接口,发现ProcNum为0x5f的函数为ServerMpRpcMemoryScanQueryNotification,函数表中还包含其他可能与动态扫描相关的函数。

Windows Defender内存扫描功能分析

  • mpengine.dll

CResmgrems::ScanCEMSContext::EmsScanCSMSProcess::ScanCompletedProcessMemoryScanCache::ProcessMemoryScanCacheSMSMaps::ShouldSendMemoryScanReport
      mpengine.dll中存在很多与内存扫描相关函数,同时也导入了ReadProcessMemory,调用ReadProcessMemory的函数很多,从命名来看很多也与内存扫描相关。

Windows Defender内存扫描功能分析

2. 动态分析环境准备

动态分析前需要先去除Defender的自保护,详细如下:

  • ObProcess(PreCall): 进程对象保护回调,原理是通过注册进程对象回调对OpenProcess这类API的打开进程获取进程句柄行为进行过滤,在WdFilter.sys驱动中实现。可以使用Windows-Kernel-Explorer移除掉WdFilter.sys注册的ObProcess(PreCall)回调保护。
  • PPL(Protected Process Light):在PPL的保护下未经合法签名的程序不能对PPL保护的进程进行任意访问,只有非常受限的权限,可以使用PPLKiller或者mimikatz去除MsMpEng.exe进程的PPL保护。

Windows Defender内存扫描功能分析

      去除Defender的自保护后即可正常附加调试,不过单机调试有时候会整个系统卡死,猜测是断点时候正好是驱动与应用层通信时候,导致System线程挂起,系统就卡死了,可以使用Windbg双机调试解决这个问题,设置好双机调试环境后使用Windbg附加调试MsMpeng.exe进程。
0: kd> !process 0 0 MsMpEng.exe
PROCESS ffff8009e37ca080
SessionId: 0 Cid: 107c Peb: e79fb62000 ParentCid: 029c
DirBase: 12dc55000 ObjectTable: ffffcc06b8867700 HandleCount: 749.
Image: MsMpEng.exe
0: kd> .process /i /p ffff8009e37ca080
You need to continue execution (press 'g' <enter>) for the context
to be switched. When the debugger breaks in again, you will be in
the new process context.
0: kd> g
Break instruction exception - code 80000003 (first chance)
nt!DbgBreakPointWithStatus:
fffff805`05400fc0 cc int 3
0: kd> .reload /f /user
Loading User Symbols
........
      因为需要分析Defender内存扫描逻辑,还需要一个样本来触发内存扫描,这里使用一个经过静态免杀CobaltStrike beacon作为样本,样本文件名为payload.exe,在Defender环境中静态免杀,但是启动后会被Defender拦截,显示 Behavior:Win32/CobaltStrike.E!sms。

3. 内存扫描实现模块定位及内存扫描功能实现分析

      通过对静态分析列出的函数下断点反复分析,发现对ReadProcessMemory函数下断点可以获取到一个内存扫描时候的栈回溯,通过对ReadProcessMemory第一个参数检查确认该栈回溯为对payload.exe进行内存扫描的过程。
1: kd> bu KernelBase!ReadProcessMemory
1: kd> k
# Child-SP RetAddr Call Site
00 000000e7`a017d988 00007fff`64e7da0f KERNELBASE!ReadProcessMemory
01 000000e7`a017d990 00007fff`64e7d613 mpengine!CSMSProcess::InvokeScanner+0xb7
02 000000e7`a017da00 00007fff`64e7d384 mpengine!CSMSProcess::ScanRange+0xa7
03 000000e7`a017daa0 00007fff`64e7cb9a mpengine!CSMSProcess::Scan1Worker+0x244
04 000000e7`a017db40 00007fff`64cecca7 mpengine!CSMSProcess::Scan+0x45e
05 000000e7`a017dc30 00007fff`654f19c0 mpengine!CEMSContext::EmsScan+0x34b
06 000000e7`a017dd00 00007fff`654f0130 mpengine!EmsEnumProcesses+0x140
07 000000e7`a017ddd0 00007fff`654f0725 mpengine!RunEMS+0x2dc
08 000000e7`a017df10 00007fff`654f01c5 mpengine!CResmgrems::ScanImpl+0x21d
...
16 000000e7`a017f9b0 00007fff`7dcd2260 mpclient!MpGetASRPerRuleExclusions+0x5f043
17 000000e7`a017fa00 00007fff`7dcc31aa ntdll!TppWorkpExecuteCallback+0x130
18 000000e7`a017fa50 00007fff`7da67034 ntdll!TppWorkerThread+0x68a
19 000000e7`a017fd50 00007fff`7dcc26a1 KERNEL32!BaseThreadInitThunk+0x14
1a 000000e7`a017fd80 00000000`00000000 ntdll!RtlUserThreadStart+0x21
1: kd> !handle @rcx
PROCESS ffff8009e37ca080
SessionId: 0 Cid: 107c Peb: e79fb62000 ParentCid: 029c
DirBase: 12dc55000 ObjectTable: ffffcc06b8867700 HandleCount: 967.
Image: MsMpEng.exe
Handle table at ffffcc06b8867700 with 967 entries in use
0a14: Object: ffff8009e6914340 GrantedAccess: 00001410 (Protected) Entry: ffffcc06aeaf8850
Object: ffff8009e6914340 Type: (ffff8009dcccaf00) Process
ObjectHeader: ffff8009e6914310 (new version)
HandleCount: 10 PointerCount: 294928
1: kd> !process ffff8009e6914340 0
PROCESS ffff8009e6914340
SessionId: 1 Cid: 10ec Peb: f18504000 ParentCid: 1aa8
DirBase: 45344000 ObjectTable: ffffcc06b1110880 HandleCount: 331.
Image: payload.exe
      进一步分析函数mpengine!CSMSProcess::InvokeScanner,在调用ReadProcessMemory读取内存数据后会进入hstr_internal_search_worker函数,经分析该函数就是特征码匹配函数。hstr_internal_search_worker会调用BMMatchEx2匹配读取的内存数据中是否有病毒库中的特征码,BMMatchEx2按照函数名及函数逻辑分析应该是通过实现BM字符串匹配算法来匹配病毒库中的特征码。
# Child-SP          RetAddr               Call Site00 000000e7`a017d548 00007fff`64b4ed9b     mpengine!BMMatchEx201 000000e7`a017d550 00007fff`64b4e022     mpengine!hstr_internal_search_worker+0x35b02 000000e7`a017d880 00007fff`64eaf6e1     mpengine!hstr_internal_search+0x7e03 000000e7`a017d910 00007fff`64eaf64e     mpengine!CSMSScanner::ScanWorker+0x5904 000000e7`a017d960 00007fff`64e7dae1     mpengine!CSMSScanner::Scan+0x3e05 000000e7`a017d990 00007fff`64e7d613     mpengine!CSMSProcess::InvokeScanner+0x18906 000000e7`a017da00 00007fff`64e7d384     mpengine!CSMSProcess::ScanRange+0xa707 000000e7`a017daa0 00007fff`64e7cb9a     mpengine!CSMSProcess::Scan1Worker+0x24408 000000e7`a017db40 00007fff`64cecca7     mpengine!CSMSProcess::Scan+0x45e09 000000e7`a017dc30 00007fff`654f19c0     mpengine!CEMSContext::EmsScan+0x34b...

4. 侧信道定位内存扫描特征码

      在上一步中我们已经定位到具体的内存扫描逻辑,理论上可以通过分析hstr_internal_search_worker函数逻辑分析出内存扫描匹配的具体逻辑,进而分析出哪段内存特征码匹配成功了,但hstr_internal_search_worker函数逻辑稍微复杂了点。
      猜测Defender内存扫描匹配成功和匹配失败ReadProcessMemory读取内存大小应该会不一样,可以根据这种现象判断是否匹配成功,提取特征码,类似爆破密码时可以根据爆破成功和失败返回的内容不同判断是否爆破成功。与预想一致Defender内存匹配真有这个问题,大部分情况下ReadProcessMemory读取的内存大小为0x1000,一旦ReadProcessMemory读取的内存大小是一个不常见的小数字时候就会弹出拦截信息,根据这种现象可以设置Windbg条件断点,断下后打印的栈回溯如下:
00 000000e7`a00fed80 00007fff`65592d69     mpengine!CEMSTele::Matched+0x31801 000000e7`a00fee50 00007fff`653cf858     mpengine!CSMSScanner::EnumHSTR+0x1f902 000000e7`a00feee0 00007fff`653cfbb4     mpengine!CSMSProcess::Report+0x17803 000000e7`a00ff010 00007fff`64e7ccfc     mpengine!CSMSProcess::ScanCompleted+0xd004 000000e7`a00ff040 00007fff`65595667     mpengine!CSMSProcess::Scan+0x5c005 000000e7`a00ff130 00007fff`65594a58     mpengine!CSMSContext::ScanProcess+0xc3...
      从CEMSTele::Matched函数命名及栈回溯逻辑分析此时的栈回溯应该是内存扫描完成,然后对检测到的特征码进行上报的过程,此时会在CEMSTele::Matched中通过ReadProcessMemory重新读取目标内存中匹配成功的内存特征码。这样就可以在ReadProcessMemory返回后的第一个指令下断点并打印函数参数即可获取匹配成功的特征码信息。
1: kd> bp mpengine!CEMSTele::Matched+0x318 ".printf "Matched:n"; db rdi+1ch L@r15"
1: kd> g
Matched: 00000284`ea182166 4c 63 c2 4d 03 c0 42 0f-10 04 c0 48 8b c1 f3 0f Lc.M..B....H....
00000284`ea182176 7f 01 c3

Windows Defender内存扫描功能分析

      其中rdi+1ch为读取的buffer地址,r15为读取的大小,这样就可以直接打印内存扫描匹配成功的特征码。

5. 曲折的分析内存扫描触发逻辑

      现在还剩下内存扫描如何触发的没分析了,由于Defender内存扫描通过线程池异步操作实现并且除mpengine.dll外其他模块只有老版本pdb,导致分析过程比较曲折,不过最后还是从线程池实现逻辑分析出了内存扫描触发逻辑。Defender线程池消费者函数为CommonUtil::CMpSimpleThreadPool::AsyncDequeue,对应还有一个生产者函数CommonUtil::CMpSimpleThreadPool::Submit,可以对这个函数下断查看函数调用栈分析内存触发逻辑,需要注意mpclient.dll与mpengine.dll都使用的这套线程池,需要同时对这两个模块中的CommonUtil::CMpSimpleThreadPool::Submit函数下断点。
//mpclient.dll 无pdb符号文件,需要通过类似bindiff这样的工具找到CommonUtil::CMpSimpleThreadPool::Submit函数在当前版本的偏移
0: kd> bp mpclient.dll+a7420 ".echo "mpclient::CommonUtil::CMpSimpleThreadPool::Submit";k;g"
0: kd> bp mpengine!CommonUtil::CMpSimpleThreadPool::Submit ".echo "mpengine!CommonUtil::CMpSimpleThreadPool::Submit";k;g"
      通过查看内存扫描过程中Windbg打印的函数调用栈,可以整理出内存扫描的触发过程:
  • 触发源头是WdFilter.sys驱动捕获到模块加载事件后通过微文件端口将信息发送给mprtp.dll,然后mprtp.dll向线程池中提交了一个ModuleLoad事件待处理。
//由于没有pdb,这里调用栈中的函数名是通过bindiff对比出来的# Child-SP          RetAddr               Call Site00 000000e7`9fcff718 00007fff`73db3ec8     mpclient!CommonUtil::CMpSimpleThreadPool::Submit                                          mpclient!MpGetASRPerRuleExclusions+0x5e9d0 01 000000e7`9fcff720 00007fff`73e1adb0     mprtp!RealtimeProtection::CMpPluginWorkItemBase::PrioritizedDispatchJob_75E833E40         mprtp!MpPluginDeviceControlValidateDataDuplicationRemoteLocationConfiguration+0x1d2b802 000000e7`9fcff750 00007fff`73e0cde2     mprtp!RealtimeProtection::CProcessAgent::HandleModuleLoad_75E89ACF0                       mprtp!MpPluginDeviceControlValidateDataDuplicationRemoteLocationConfiguration+0x841a003 000000e7`9fcff7c0 00007fff`73e2465e     mprtp!RealtimeProtection::CProcessWatcher::HandleAsynchronousRequest_75E88CCD0            mprtp!MpPluginDeviceControlValidateDataDuplicationRemoteLocationConfiguration+0x761d204 000000e7`9fcff810 00000000`00000000     mprtp!RealtimeProtection::CAsynchronousWatcherBase::HandleRequest_75E8A45B0bl             mprtp!MpPluginDeviceControlValidateDataDuplicationRemoteLocationConfiguration+0x8da4e05 000000e7`a097f188 00007fff`73e08b3c     mprtp!RealtimeProtection::CFileSystemWatcher::HandleRequest_75E8886C0                     mprtp!MpPluginDeviceControlValidateDataDuplicationRemoteLocationConfiguration+0x71f2c06 000000e7`a097f768 00007fff`73e52233     mprtp!RealtimeProtection::CFilterCommunicatorBase::CommunicatorMainFunction_75E8D1884     mprtp!MpPluginDeviceControlValidateDataDuplicationRemoteLocationConfiguration+0xbb623
  • 然后会对加载的Module进行一次文件检测,检测文件是否可信,不可信的模块会被添加到MOAC中。
00 00000047`a287e338 00007ffa`1e8d2717     mpengine!MOACManager::AddUntrustedToMoac01 00000047`a287e340 00007ffa`1e3a49d9     mpengine!CacheMgr::AddUntrustedToMoac+0x3702 00000047`a287e370 00007ffa`1e95041d     mpengine!IsFriendlyFile+0x3fd03 00000047`a287e4a0 00007ffa`1e6dd5bc     mpengine!VerifyIsFriendlyFile+0xe104 00000047`a287e580 00007ffa`1e724a81     mpengine!ProcessContext::IsFriendlyImageFile+0x13c05 00000047`a287e640 00007ffa`1e71dec1     mpengine!SignatureHandler::ReportDetection+0x5f106 00000047`a287ec50 00007ffa`1e725a37     mpengine!SignatureHandler::HandleDetection+0x42107 00000047`a287ee30 00007ffa`1e725c43     mpengine!SignatureHandler::TestForDetection+0x2a308 00000047`a287f2a0 00007ffa`1e7261f8     mpengine!SignatureHandler::TestForDetectionWithTokenizedPath+0x1db09 00000047`a287f380 00007ffa`1e721185     mpengine!SignatureHandler::TestForModuleLoad+0x7c0a 00000047`a287f3f0 00007ffa`1e70dede     mpengine!SignatureHandler::HandleNotification+0xa95...10 00000047`a287f9d0 00007ffa`1e82b982     mpengine!NotificationItem::OnAction+0x4211 00000047`a287fa20 00007ffa`3c132260     mpengine!CommonUtil::CMpSimpleThreadPool::AsyncDequeue+0xde12 00000047`a287fa60 00007ffa`3c1231aa     ntdll!TppWorkpExecuteCallback+0x13013 00000047`a287fab0 00007ffa`3a277034     ntdll!TppWorkerThread+0x68a14 00000047`a287fdb0 00007ffa`3c1226a1     KERNEL32!BaseThreadInitThunk+0x1415 00000047`a287fde0 00000000`00000000     ntdll!RtlUserThreadStart+0x21
  • 因为Module非可信,同时还会被提交到一个事件到检测队列QueueDetection。
00 00000047`a287e468 00007ffa`1e72c191     mpengine!CommonUtil::CMpSimpleThreadPool::Submit01 00000047`a287e470 00007ffa`1e6ea00d     mpengine!DetectionQueue::QueueDetection+0x36902 00000047`a287e5c0 00007ffa`1e731116     mpengine!DetectionController::QueueDetection+0x4d03 00000047`a287e5f0 00007ffa`1e72529d     mpengine!ScanHandlerBase::ReportDetection+0xda04 00000047`a287e640 00007ffa`1e71dec1     mpengine!SignatureHandler::ReportDetection+0xe0d05 00000047`a287ec50 00007ffa`1e725a37     mpengine!SignatureHandler::HandleDetection+0x42106 00000047`a287ee30 00007ffa`1e725c43     mpengine!SignatureHandler::TestForDetection+0x2a307 00000047`a287f2a0 00007ffa`1e7261f8     mpengine!SignatureHandler::TestForDetectionWithTokenizedPath+0x1db08 00000047`a287f380 00007ffa`1e721185     mpengine!SignatureHandler::TestForModuleLoad+0x7c...
  • DetectionQueue::OnAction消费检测队列中的事件,然后TriggerEmsScan会提交一个内存扫描任务到线程池。
00 00000047`a287f118 00007ffa`1e70a05e     mpengine!CommonUtil::CMpSimpleThreadPool::Submit01 00000047`a287f120 00007ffa`1e709b8f     mpengine!TriggerScan+0x6a02 00000047`a287f170 00007ffa`1e6c6b09     mpengine!TriggerEmsScan+0x23b03 00000047`a287f2a0 00007ffa`1e6cb266     mpengine!DoTriggeredActions+0x2e904 00000047`a287f350 00007ffa`1e6cd156     mpengine!PerformDetectionActions+0x2d605 00000047`a287f4d0 00007ffa`1e6cb643     mpengine!DetectionItem::UpdateCharacteristics+0x8c606 00000047`a287f6b0 00007ffa`1e72ba54     mpengine!DetectionItem::Send+0xf707 00000047`a287f820 00007ffa`1e72bd53     mpengine!DetectionQueue::DispatchDetections+0x3f408 00000047`a287f9a0 00007ffa`1e82b982     mpengine!DetectionQueue::OnAction+0xa309 00000047`a287fa20 00007ffa`3c132260     mpengine!CommonUtil::CMpSimpleThreadPool::AsyncDequeue+0xde0a 00000047`a287fa60 00007ffa`3c1231aa     ntdll!TppWorkpExecuteCallback+0x1300b 00000047`a287fab0 00007ffa`3a277034     ntdll!TppWorkerThread+0x68a0c 00000047`a287fdb0 00007ffa`3c1226a1     KERNEL32!BaseThreadInitThunk+0x140d 00000047`a287fde0 00000000`00000000     ntdll!RtlUserThreadStart+0x21
  • 线程池会执行内存扫描的任务,执行内存扫描的栈回溯。
//bp mpengine!CEMSContext::EmsScan+0x34 ".printf "CEMSContext::EmsScan:%mu", rax;g"CEMSContext::EmsScan:DeviceHarddiskVolume3payload.exe  # Child-SP          RetAddr               Call Site00 00000047`a337d6b0 00007ffa`1ebc19c0     mpengine!CEMSContext::EmsScan+0x3401 00000047`a337d780 00007ffa`1ebc0130     mpengine!EmsEnumProcesses+0x14002 00000047`a337d850 00007ffa`1ebc0725     mpengine!RunEMS+0x2dc03 00000047`a337d990 00007ffa`1ebc01c5     mpengine!CResmgrems::ScanImpl+0x21d04 00000047`a337da50 00007ffa`1e366a1f     mpengine!CResmgrems::Scan+0x1505 00000047`a337db80 00007ffa`1e791082     mpengine!ResmgrProcessResource+0x1c706 00000047`a337ddb0 00007ffa`1e77656d     mpengine!ResScan+0xa3607 00000047`a337e1f0 00007ffa`1e7797d8     mpengine!ScanOpenWithContext+0x191108 00000047`a337eaf0 00007ffa`1e759248     mpengine!UberScanOpen+0xa6409 00000047`a337ec10 00007ffa`1e519c13     mpengine!ksignal+0x6a80a 00000047`a337ed90 00007ffa`1e518fcb     mpengine!DispatchSignalHelper+0x6f0b 00000047`a337edf0 00007ffa`2a8070f3     mpengine!DispatchSignalOnHandle+0x9b0c 00000047`a337f260 00000000`00000000     mpsvc!ServiceCrtMain+0x1f6e3
      因为执行内存扫描流程太长且均为异步任务,因此中间流程未具体分析,只是分析了本次扫描是由WdFilter.sys驱动监控到模块加载触发的,那如何验证分析结果是否正确呢?
      这里可以采用排除法验证结果是否正确,流程如下:
  • 使用Windows-Kernel-Explorer移除WdFilter.sys除模块加载回调以外的其他监控回调(进程/线程/注册表),检查是否触发最终的内存扫描。这里测试结果是能触发内存扫描
  • 只移除WdFilter.sys模块加载回调,检查是否触发最终内存扫描。这里测试结果是不能。
     使用排除法验证了本次分析结果的正确性,模块加载事件会触发内存扫描。在分析过程中同时也发现Defender中也包含其他事件的处理逻辑,mpengine!SignatureHandler::HandleNotification函数包含了不同事件的重载实现,不同的事件如下:
  • ArNotification

  • BootChangeNotification

  • DesktopNotification

  • EtwNotification

  • FileNotification

  • InternalNotification

  • NetworkNotification2

  • ProcessNotification

  • RegistryNotification

  • RemoteThreadCreateNotification

  • VolumeMountNotification

      限于时间和篇幅这里未对其他事件进行分析,不排除上面的其他事件也可能会导致触发内存扫描逻辑。

0x03 总结 

      本文对Defender的内存扫描功能进行了一个简单分析,最后总结回答下分析目标中提出的三个问题:

  • 确定内存扫描功能具体由哪个模块实现以及实现方式
    内存扫描功能具体实现在mpengine.dll中实现,实现方式是通过ReadProcessMemory读取目标进程内存数据,然后通过BM字符串匹配算法对目标内存数据与特征码数据进行匹配,与文件静态检测类似,只是数据源不同。

  • 定位内存中具体哪段内存数据是恶意代码特征码
    可以通过侧信道分析方法,利用ReadProcessMemory读取数据大小不同定位到特征码,对mpengine!CEMSTele::Matched+0x318下断点可直接打印当次检测出的恶意内存特征码。

  • 内存扫描如何触发的,流程是怎样的
    本次分析的样本是模块加载事件触发的内存扫描,不排除其他事件也会触发内存扫描。流程比较复杂,中间会经过不同的异步检测过程最终才到内存扫描逻辑。

0x04  参考链接

  • https://labs.withsecure.com/publications/bypassing-windows-defender-runtime-scanning
  • https://learn.microsoft.com/zh-cn/microsoft-365/security/intelligence/fileless-threats
  • https://github.com/AxtMueller/Windows-Kernel-Explorer

Windows Defender内存扫描功能分析

原文始发于微信公众号(219攻防实验室):Windows Defender内存扫描功能分析

版权声明:admin 发表于 2022年11月24日 下午3:39。
转载请注明:Windows Defender内存扫描功能分析 | CTF导航

相关文章

暂无评论

暂无评论...