断链实现:
在内核中很多数据使用_LIST_ENTRY,双向循环链表作为数据结构
typedef struct _LIST_ENTRY
{
struct _LIST_ENTRY *Flink;
struct _LIST_ENTRY *Blink;
} LIST_ENTRY, *PLIST_ENTRY;
首先要先了解EPROCESS,和KPROCESS两个重要的结构体; 每个windows进程在0环都有这样一个对应的结构体:
nt!_EPROCESS
+0x000 Pcb : _KPROCESS
+0x098 ProcessLock : _EX_PUSH_LOCK 进程锁
+0x0a0 CreateTime : _LARGE_INTEGER 进程创建时间
+0x0a8 ExitTime : _LARGE_INTEGER 进程结束时间
+0x0b0 RundownProtect : _EX_RUNDOWN_REF
+0x0b4 UniqueProcessId : Ptr32 Void PID进程id
+0x0b8 ActiveProcessLinks : _LIST_ENTRY 活跃进程链表 双向循环链表 可断链
+0x0c0 ProcessQuotaUsage : [2] Uint4B 物理页相关统计
+0x0c8 ProcessQuotaPeak : [2] Uint4B
+0x0d0 CommitCharge : Uint4B cpu占用信息
+0x0d4 QuotaBlock : Ptr32 _EPROCESS_QUOTA_BLOCK
+0x0d8 CpuQuotaBlock : Ptr32 _PS_CPU_QUOTA_BLOCK cpu占用信息
+0x0dc PeakVirtualSize : Uint4B 虚拟内存相关统计
+0x0e0 VirtualSize : Uint4B
+0x0e4 SessionProcessLinks : _LIST_ENTRY
+0x0ec DebugPort : Ptr32 Void 调试相关≠0则被调试
+0x0f0 ExceptionPortData : Ptr32 Void
+0x0f0 ExceptionPortValue : Uint4B
+0x0f0 ExceptionPortState : Pos 0, 3 Bits
+0x0f4 ObjectTable : Ptr32 _HANDLE_TABLE 句柄表 内核对象
+0x0f8 Token : _EX_FAST_REF
+0x0fc WorkingSetPage : Uint4B
+0x100 AddressCreationLock : _EX_PUSH_LOCK
+0x104 RotateInProgress : Ptr32 _ETHREAD
+0x108 ForkInProgress : Ptr32 _ETHREAD
+0x10c HardwareTrigger : Uint4B
+0x110 PhysicalVadRoot : Ptr32 _MM_AVL_TABLE
+0x114 CloneRoot : Ptr32 Void
+0x118 NumberOfPrivatePages : Uint4B
+0x11c NumberOfLockedPages : Uint4B
+0x120 Win32Process : Ptr32 Void
+0x124 Job : Ptr32 _EJOB
+0x128 SectionObject : Ptr32 Void
+0x12c SectionBaseAddress : Ptr32 Void
+0x130 Cookie : Uint4B
+0x134 Spare8 : Uint4B
+0x138 WorkingSetWatch : Ptr32 _PAGEFAULT_HISTORY
+0x13c Win32WindowStation : Ptr32 Void
+0x140 InheritedFromUniqueProcessId : Ptr32 Void 父进程id
+0x144 LdtInformation : Ptr32 Void
+0x148 VdmObjects : Ptr32 Void
+0x14c ConsoleHostProcess : Uint4B
+0x150 DeviceMap : Ptr32 Void
+0x154 EtwDataSource : Ptr32 Void
+0x158 FreeTebHint : Ptr32 Void
+0x160 PageDirectoryPte : Uint8B
+0x168 Session : Ptr32 Void
+0x16c ImageFileName : [15] UChar 进程名最多存16个字符,截断
+0x17b PriorityClass : UChar
+0x17c JobLinks : _LIST_ENTRY
+0x184 LockedPagesList : Ptr32 Void
+0x188 ThreadListHead : _LIST_ENTRY 双向循环链表
+0x190 SecurityPort : Ptr32 Void
+0x194 PaeTop : Ptr32 Void
+0x198 ActiveThreads : Uint4B 活跃线程数量
+0x19c ImagePathHash : Uint4B
+0x1a0 DefaultHardErrorProcessing : Uint4B
+0x1a4 LastThreadExitStatus : Int4B
+0x1a8 Peb : Ptr32 _PEB PEB(process ENVIRONMENT BLOCK 进程环境块)
+0x1ac PrefetchTrace : _EX_FAST_REF
+0x1b0 ReadOperationCount : _LARGE_INTEGER
+0x1b8 WriteOperationCount : _LARGE_INTEGER
+0x1c0 OtherOperationCount : _LARGE_INTEGER
+0x1c8 ReadTransferCount : _LARGE_INTEGER
+0x1d0 WriteTransferCount : _LARGE_INTEGER
+0x1d8 OtherTransferCount : _LARGE_INTEGER
+0x1e0 CommitChargeLimit : Uint4B
+0x1e4 CommitChargePeak : Uint4B
+0x1e8 AweInfo : Ptr32 Void
+0x1ec SeAuditProcessCreationInfo : 进程路径_SE_AUDIT_PROCESS_CREATION_INFO
+0x1f0 Vm : _MMSUPPORT
+0x25c MmProcessLinks : _LIST_ENTRY
+0x264 HighestUserAddress : Ptr32 Void
+0x268 ModifiedPageCount : Uint4B
+0x26c Flags2 : Uint4B
+0x26c JobNotReallyActive : Pos 0, 1 Bit
+0x26c AccountingFolded : Pos 1, 1 Bit
+0x26c NewProcessReported : Pos 2, 1 Bit
+0x26c ExitProcessReported : Pos 3, 1 Bit
+0x26c ReportCommitChanges : Pos 4, 1 Bit
+0x26c LastReportMemory : Pos 5, 1 Bit
+0x26c ReportPhysicalPageChanges : Pos 6, 1 Bit
+0x26c HandleTableRundown : Pos 7, 1 Bit
+0x26c NeedsHandleRundown : Pos 8, 1 Bit
+0x26c RefTraceEnabled : Pos 9, 1 Bit
+0x26c NumaAware : Pos 10, 1 Bit
+0x26c ProtectedProcess : Pos 11, 1 Bit 保护进程
+0x26c DefaultPagePriority : Pos 12, 3 Bits
+0x26c PrimaryTokenFrozen : Pos 15, 1 Bit
+0x26c ProcessVerifierTarget : Pos 16, 1 Bit
+0x26c StackRandomizationDisabled : Pos 17, 1 Bit
+0x26c AffinityPermanent : Pos 18, 1 Bit
+0x26c AffinityUpdateEnable : Pos 19, 1 Bit
+0x26c PropagateNode : Pos 20, 1 Bit
+0x26c ExplicitAffinity : Pos 21, 1 Bit
+0x26c Spare1 : Pos 22, 1 Bit
+0x26c ForceRelocateImages : Pos 23, 1 Bit
+0x26c DisallowStrippedImages : Pos 24, 1 Bit
+0x26c LowVaAccessible : Pos 25, 1 Bit
+0x26c RestrictIndirectBranchPrediction : Pos 26, 1 Bit
+0x26c AddressPolicyFrozen : Pos 27, 1 Bit
+0x26c SpeculativeStoreBypassDisable : Pos 28, 1 Bit
+0x270 Flags : Uint4B
+0x270 CreateReported : Pos 0, 1 Bit
+0x270 NoDebugInherit : Pos 1, 1 Bit
+0x270 ProcessExiting : Pos 2, 1 Bit
+0x270 ProcessDelete : Pos 3, 1 Bit
+0x270 Wow64SplitPages : Pos 4, 1 Bit
+0x270 VmDeleted : Pos 5, 1 Bit
+0x270 OutswapEnabled : Pos 6, 1 Bit
+0x270 Outswapped : Pos 7, 1 Bit
+0x270 ForkFailed : Pos 8, 1 Bit
+0x270 Wow64VaSpace4Gb : Pos 9, 1 Bit
+0x270 AddressSpaceInitialized : Pos 10, 2 Bits
+0x270 SetTimerResolution : Pos 12, 1 Bit
+0x270 BreakOnTermination : Pos 13, 1 Bit
+0x270 DeprioritizeViews : Pos 14, 1 Bit
+0x270 WriteWatch : Pos 15, 1 Bit
+0x270 ProcessInSession : Pos 16, 1 Bit
+0x270 OverrideAddressSpace : Pos 17, 1 Bit
+0x270 HasAddressSpace : Pos 18, 1 Bit
+0x270 LaunchPrefetched : Pos 19, 1 Bit
+0x270 InjectInpageErrors : Pos 20, 1 Bit
+0x270 VmTopDown : Pos 21, 1 Bit
+0x270 ImageNotifyDone : Pos 22, 1 Bit
+0x270 PdeUpdateNeeded : Pos 23, 1 Bit
+0x270 VdmAllowed : Pos 24, 1 Bit
+0x270 CrossSessionCreate : Pos 25, 1 Bit
+0x270 ProcessInserted : Pos 26, 1 Bit
+0x270 DefaultIoPriority : Pos 27, 3 Bits
+0x270 ProcessSelfDelete : Pos 30, 1 Bit
+0x270 SetTimerResolutionLink : Pos 31, 1 Bit
+0x274 ExitStatus : Int4B
+0x278 VadRoot : _MM_AVL_TABLE
+0x298 AlpcContext : _ALPC_PROCESS_CONTEXT
+0x2a8 TimerResolutionLink : _LIST_ENTRY
+0x2b0 RequestedTimerResolution : Uint4B
+0x2b4 ActiveThreadsHighWatermark : Uint4B
+0x2b8 SmallestTimerResolution : Uint4B
+0x2bc TimerResolutionStackRecord : Ptr32 _PO_DIAG_STACK_RECORD
+0x2c0 SequenceNumber : Uint8B
+0x2c8 CreateInterruptTime : Uint8B
+0x2d0 CreateUnbiasedInterruptTime : Uint8B
+0x2d8 SecurityDomain : Uint8B
_EPROCESS的第一个成员是KPROCESS
nt!_KPROCESS
+0x000 Header : _DISPATCHER_HEADER 可等待内核对象
+0x010 ProfileListHead : _LIST_ENTRY
+0x018 DirectoryTableBase : Uint4B CR3 通过线性地址以及分页模式定位物理基地址
+0x01c LdtDescriptor : _KGDTENTRY
+0x024 Int21Descriptor : _KIDTENTRY 历史遗留
+0x02c ThreadListHead : _LIST_ENTRY 双向循环链表
+0x034 ProcessLock : Uint4B 内核进程锁
+0x038 Affinity : _KAFFINITY_EX cpu亲核性
+0x044 ReadyListHead : _LIST_ENTRY 进程就绪列表
+0x04c SwapListEntry : _SINGLE_LIST_ENTRY交互磁盘
+0x050 ActiveProcessors : _KAFFINITY_EX 活跃核心
+0x05c AutoAlignment : Pos 0, 1 Bit 对齐
+0x05c DisableBoost : Pos 1, 1 Bit
+0x05c DisableQuantum : Pos 2, 1 Bit 关闭时间碎片
+0x05c ActiveGroupsMask : Pos 3, 1 Bit
+0x05c ReservedFlags : Pos 4, 28 Bits
+0x05c ProcessFlags : Int4B
+0x060 BasePriority : Char 基础优先级8,进程下所有线程最低的优先级
+0x061 QuantumReset : Char时间碎片
+0x062 Visited : UChar
+0x063 Unused3 : UChar
+0x064 ThreadSeed : [1] Uint4B
+0x068 IdealNode : [1] Uint2B
+0x06a IdealGlobalNode : Uint2B
+0x06c Flags : _KEXECUTE_OPTIONS
+0x06d AddressPolicy : UChar
+0x06e IopmOffset : Uint2B
+0x070 Unused4 : Uint4B
+0x074 StackCount : _KSTACK_COUNT
+0x078 ProcessListEntry : _LIST_ENTRY
+0x080 CycleTime : Uint8B
+0x088 KernelTime : Uint4B
+0x08c UserTime : Uint4B
+0x090 VdmTrapcHandler : Ptr32 Void //虚拟8086模式下使用
其中在_EPROCESS我们需要关注的是
ActiveProcessLinks 进程活跃链表
SessionProcessLinks 会话链表
ObjectTable 私有句柄表
其中在_KPROCESS我们需要关注的是
ProcessListEntry 进程链表
想要隐藏进程把所在数据的链表进程断链即可
//断链关键代码
FORCEINLINE
BOOLEAN
MyRemoveEntryList(
_In_ PLIST_ENTRY Entry
)
{
PLIST_ENTRY PrevEntry;
PLIST_ENTRY NextEntry;
NextEntry = Entry->Flink;
PrevEntry = Entry->Blink;
if ((NextEntry->Blink != Entry) || (PrevEntry->Flink != Entry)) {
return FALSE;
}
PrevEntry->Flink = NextEntry;
NextEntry->Blink = PrevEntry;
return (BOOLEAN)(PrevEntry == NextEntry);
}
//
//断链 ProcessListEntry
PLIST_ENTRY ProfileListHead = (PLIST_ENTRY)((PUCHAR)mypEProcess + 0x18);
MyRemoveEntryList(ProfileListHead);
//断链 struct _LIST_ENTRY ThreadListHead; //0x30
PLIST_ENTRY ThreadListHead = (PLIST_ENTRY)((PUCHAR)mypEProcess + 0x30);
MyRemoveEntryList(ProfileListHead);
//断链struct_LIST_ENTRYSessionProcessLinks
PLIST_ENTRY essionProcessLinks = (PLIST_ENTRY)((PUCHAR)mypEProcess + get_eprocess_session_offset());
MyRemoveEntryList(essionProcessLinks);
//断链 private ObjectTable
_HANDLE_TABLE* table= (_HANDLE_TABLE*)((PUCHAR)mypEProcess + get_eprocess_objecttable_offset());
MyRemoveEntryList(table->HandleTableList);
当然仅仅断链是不够的,因为操作系统还有一张全局句柄表PspCidTable,其中存储了线程和进程,我们还需要把我们的进程从全局句柄表中抹除,可以使用ExDestroyHandle
_int64 __fastcall ExDestroyHandle(__int64 a1, __int64 a2, __int64 a3)
{
unsigned int v6; // ebx
if ( *(_QWORD *)(a1 + 96) )
ExpUpdateDebugInfo(a1, KeGetCurrentThread(), a2, 2i64);
v6 = ExSweepSingleHandle(a1, a3);
ExpFreeHandleTableEntry(a1, a2, a3);
return v6;
}
if (instance->fn_get_os_build_number() == 7600 || instance->fn_get_os_build_number() == 7601) table_code = ((PHANDLE_TABLE_W7)table)->TableCode;
else table_code = table->TableCode;
auto level = table_code & 3;
if (level == 1) {
return (PHANDLE_TABLE_ENTRY)(*(uint64_t*)(table_code - 1 + 8 * (u_handle >> 10)) + 4 * (u_handle & 0X3FF));
}
else if (level == 2) {
return (PHANDLE_TABLE_ENTRY)(*(uint64_t*)(*(uint64_t*)(table_code - 2 + 8 * (u_handle >> 19)) + 8 * (u_handle >> 10 & 0X1FF)) + 4 * (u_handle & 0x3ff));
}
else {
return (PHANDLE_TABLE_ENTRY)(table_code + 4 * (u_handle & 0x3ff));
}
注:高版本对全局句柄表有加密
实验:
隐藏前DebugView 8404
隐藏后
需要注意,关闭进程前需要还原全局句柄表,不然会蓝屏;
其次在高版本系统上PG增加了对隐藏进程的检测,有概率触发蓝屏,不过没那么快,运气好能挺好几个小时;
具体的隐藏进程完整代码可以参考一份开源的:https://github.com/Oxygen1a1/HideProcess
那么问题来了,如果恶意软件隐藏自己的进程我们如何查询呢?
思考:进程是给用户看的,CPU不认识进程,代码也是跑在线程上面的 ,最重要的是cpu线程的调度是基于某些链表进行调度的,如果把cpu调度链表也断了,进程也就跑不起来了,可谓是真正实现了无痕进程隐藏;其次内核中有很多链表都能回溯到进程信息。
本人采用的方法是通过遍历枚举线程去回溯到进程,在 _ETHREAD中_CLIENT_ID储存了当前线程所属的进程id
//0x10 bytes (sizeof)
struct _CLIENT_ID
{
VOID* UniqueProcess; //0x0
VOID* UniqueThread; //0x8
};
查询恶意隐藏进程思路:正常调用API遍历进程并存储表A,暴力枚举线程回溯进程并存储表B,如果B中的进程在A中找不到基本可以判断进程进行了隐藏。 完善隐藏进程代码,方便学习