编写简单的防杀程序
HOHO
发现res区现在编程风行哈~ 眼看就要开学,偶再不鲜点血就说不过去了 :P
其实是比较老的东西了 不过个人感觉似乎还是有点意义的 :)
应该能过taskmgr,IceSword(IceSword的线程信息中一个一个线程的杀还是可以干掉这种的,之于如何防IceSword按线程杀以后再讲:)),DarkSpy 1.0.5.0(我没有办法拿到内部版 所以不知道内部版是什么样的情况:P),SnipeSword (截至偶发贴之前的所有版本),GMER 1.0.13.12551(我所能拿到的最新版本)
过不了Rootkit Unhooker的虚拟内存清零(RkU的以后再讲:P),也过不了Simple Taskmgr 1.0.303
事实上,在simple taskmgr 1.0.303(st)中,所有被提及的全能bypass掉(DarkSpy杀st的时候会蓝屏T_T),不过st用的是ring0 inline hook API,暂时不说。
IceSword杀进程是使用NtTerminateProcess(当然是先恢复掉hook咯 :P),DarkSpy似乎是自己实现了一个PspTerminateProcess(但是并没有实现PspTerminateThreadByPointer),SnipeSword也是使用NtTerminateProcess(调用前恢复hook)。
无论是NtTerminateProcess还是PspTerminateProcess,最终都是调用了PspTerminateThreadByPointer(可以通过windows 2k的源代码或者wrk来看到)
以下以wrk中的代码为例,windows 2k中PspTerminateThreadByPointer的参数是两个,wrk中为三个。不过这些我们不需要关心:P
NtTerminateProcess节选如下:
PspTerminateProcess(因为PspTerminateProcess相对比较简单,就全复制过来了):
可以看到这两个最后都是使用PspTerminateThreadByPointer来按照线程来终止的。
再看看PspTerminateThreadByPointer的代码(节选):
也就是说,只要是“他杀”(当前进程(PsGetCurrentThread())不等于参数Thread),那么就会判断IS_SYSTEM_THREAD这个宏 如果为true,就直接返回STATUS_ACCESS_DENIED(拒绝访问)
从PspTerminateThreadByPointer的参数表上我们可以知道,Thread是一个PETHREAD类型的数据。
而PETHREAD被定义为*ETHREAD,也就是ETHREAD结构的指针。
ETHREAD结构(来自windbg,结构为xp sp2下的——这个结构在不同系统下不一定相同,CrossThreadFlags在ETHREAD结构中的偏移需要根据系统来判断):
理论上都都讲完了,我们来回顾下:
1、不论SnipeSword,IceSword,DarkSpy,又或是GMER(如果我没记错,GMER应该是使用NtTerminateProcess的方法来结束进程的),最终杀进程都要过PspTerminateThreadByPointer
2、PspTerminateThreadByPointer会检查线程的SystemThread标志(ETHREAD结构中CrossThreadFlags的第四位),如果被置为1,就返回拒绝访问(STATUS_ACCESS_DENIED)
3、ETHREAD结构在不同系统中不同,所以我们需要判断操作系统版本(这个我不会在演示代码中出现,交给大家了:P)。
但是我们仍然有一些问题没有解决:
1、进程的ETHREAD结构在内存中的地址是多少?
2、ETHREAD结构存在于系统高2G空间,如何修改?
至于判断系统版本,相信大家都会 :)
1、ETHREAD结构的地址
ntdll.dll导出了一个未文档化的函数——NtQuerySystemInformation,它可以用来查询SystemHandleInformation,查询SystemHandleInformation的返回的数据结构如下(懒得再弄vb的了,直接把C的弄过来,这个可能跟我的代码中的定义有些不同,但是实际上被用到的数据在结构中的偏移是相同的:P):
NumberOfHandles表示返回的数组个数,SYSTEM_HANDLE_TABLE_ENTRY_INFO则是返回的数据。
SYSTEM_HANDLE_TABLE_ENTRY_INFO中的HandleValue是句柄的值,Object便是这个Handle对应的对象(Object,如果Handle类型为线程,那么这个对象就是ETHREAD结构)的地址。
嗯,我想我说的还是比较清楚的。
2、修改物理内存
ntdll.dll还导出了一个函数叫做NtSystemDebugControl,这个函数可以用来操作高2G的内存空间。
剩下的废话不多说了~ 大家看附件把 :)
完整的代码和程序:)
发现res区现在编程风行哈~ 眼看就要开学,偶再不鲜点血就说不过去了 :P
其实是比较老的东西了 不过个人感觉似乎还是有点意义的 :)
应该能过taskmgr,IceSword(IceSword的线程信息中一个一个线程的杀还是可以干掉这种的,之于如何防IceSword按线程杀以后再讲:)),DarkSpy 1.0.5.0(我没有办法拿到内部版 所以不知道内部版是什么样的情况:P),SnipeSword (截至偶发贴之前的所有版本),GMER 1.0.13.12551(我所能拿到的最新版本)
过不了Rootkit Unhooker的虚拟内存清零(RkU的以后再讲:P),也过不了Simple Taskmgr 1.0.303
事实上,在simple taskmgr 1.0.303(st)中,所有被提及的全能bypass掉(DarkSpy杀st的时候会蓝屏T_T),不过st用的是ring0 inline hook API,暂时不说。
IceSword杀进程是使用NtTerminateProcess(当然是先恢复掉hook咯 :P),DarkSpy似乎是自己实现了一个PspTerminateProcess(但是并没有实现PspTerminateThreadByPointer),SnipeSword也是使用NtTerminateProcess(调用前恢复hook)。
无论是NtTerminateProcess还是PspTerminateProcess,最终都是调用了PspTerminateThreadByPointer(可以通过windows 2k的源代码或者wrk来看到)
以下以wrk中的代码为例,windows 2k中PspTerminateThreadByPointer的参数是两个,wrk中为三个。不过这些我们不需要关心:P
NtTerminateProcess节选如下:
引用:
NTSTATUS NtTerminateProcess( __in_opt HANDLE ProcessHandle, __in NTSTATUS ExitStatus ) { //省略... st = STATUS_NOTHING_TO_TERMINATE; for (Thread = PsGetNextProcessThread (Process, NULL); Thread != NULL; Thread = PsGetNextProcessThread (Process, Thread)) { st = STATUS_SUCCESS; if (Thread != Self) { PspTerminateThreadByPointer (Thread, ExitStatus, FALSE); } } //省略... return st; } |
PspTerminateProcess(因为PspTerminateProcess相对比较简单,就全复制过来了):
引用:
NTSTATUS PspTerminateProcess( PEPROCESS Process, NTSTATUS ExitStatus ) { PETHREAD Thread; NTSTATUS st; PAGED_CODE(); if (Process->Flags & PS_PROCESS_FLAGS_BREAK_ON_TERMINATION) { PspCatchCriticalBreak("Terminating critical process 0x%p (%s)\n", Process, Process->ImageFileName); } PS_SET_BITS (&Process->Flags, PS_PROCESS_FLAGS_PROCESS_DELETE); st = STATUS_NOTHING_TO_TERMINATE; for (Thread = PsGetNextProcessThread (Process, NULL); Thread != NULL; Thread = PsGetNextProcessThread (Process, Thread)) { st = STATUS_SUCCESS; PspTerminateThreadByPointer (Thread, ExitStatus, FALSE); } if (st == STATUS_NOTHING_TO_TERMINATE || Process->DebugPort != NULL) { ObClearProcessHandleTable (Process); st = STATUS_SUCCESS; } return st; } |
可以看到这两个最后都是使用PspTerminateThreadByPointer来按照线程来终止的。
再看看PspTerminateThreadByPointer的代码(节选):
引用:
NTSTATUS PspTerminateThreadByPointer( IN PETHREAD Thread, IN NTSTATUS ExitStatus, IN BOOLEAN DirectTerminate ) { //省略... if (DirectTerminate && Thread == PsGetCurrentThread()) { ASSERT (KeGetCurrentIrql() < APC_LEVEL); PS_SET_BITS (&Thread->CrossThreadFlags, PS_CROSS_THREAD_FLAGS_TERMINATED); PspExitThread (ExitStatus); } else { if (IS_SYSTEM_THREAD (Thread)) { return STATUS_ACCESS_DENIED; } //省略... } return Status; } |
也就是说,只要是“他杀”(当前进程(PsGetCurrentThread())不等于参数Thread),那么就会判断IS_SYSTEM_THREAD这个宏 如果为true,就直接返回STATUS_ACCESS_DENIED(拒绝访问)
引用:
#define IS_SYSTEM_THREAD(Thread) (((Thread)->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_SYSTEM) != 0) |
从PspTerminateThreadByPointer的参数表上我们可以知道,Thread是一个PETHREAD类型的数据。
而PETHREAD被定义为*ETHREAD,也就是ETHREAD结构的指针。
ETHREAD结构(来自windbg,结构为xp sp2下的——这个结构在不同系统下不一定相同,CrossThreadFlags在ETHREAD结构中的偏移需要根据系统来判断):
引用:
lkd> dt _ETHREAD nt!_ETHREAD +0x000 Tcb : _KTHREAD +0x1c0 CreateTime : _LARGE_INTEGER +0x1c0 NestedFaultCount : Pos 0, 2 Bits +0x1c0 ApcNeeded : Pos 2, 1 Bit +0x1c8 ExitTime : _LARGE_INTEGER +0x1c8 LpcReplyChain : _LIST_ENTRY +0x1c8 KeyedWaitChain : _LIST_ENTRY +0x1d0 ExitStatus : Int4B +0x1d0 OfsChain : Ptr32 Void +0x1d4 PostBlockList : _LIST_ENTRY +0x1dc TerminationPort : Ptr32 _TERMINATION_PORT +0x1dc ReaperLink : Ptr32 _ETHREAD +0x1dc KeyedWaitValue : Ptr32 Void +0x1e0 ActiveTimerListLock : Uint4B +0x1e4 ActiveTimerListHead : _LIST_ENTRY +0x1ec Cid : _CLIENT_ID +0x1f4 LpcReplySemaphore : _KSEMAPHORE +0x1f4 KeyedWaitSemaphore : _KSEMAPHORE +0x208 LpcReplyMessage : Ptr32 Void +0x208 LpcWaitingOnPort : Ptr32 Void +0x20c ImpersonationInfo : Ptr32 _PS_IMPERSONATION_INFORMATION +0x210 IrpList : _LIST_ENTRY +0x218 TopLevelIrp : Uint4B +0x21c DeviceToVerify : Ptr32 _DEVICE_OBJECT +0x220 ThreadsProcess : Ptr32 _EPROCESS +0x224 StartAddress : Ptr32 Void +0x228 Win32StartAddress : Ptr32 Void +0x228 LpcReceivedMessageId : Uint4B +0x22c ThreadListEntry : _LIST_ENTRY +0x234 RundownProtect : _EX_RUNDOWN_REF +0x238 ThreadLock : _EX_PUSH_LOCK +0x23c LpcReplyMessageId : Uint4B +0x240 ReadClusterSize : Uint4B +0x244 GrantedAccess : Uint4B +0x248 CrossThreadFlags : Uint4B +0x248 Terminated : Pos 0, 1 Bit +0x248 DeadThread : Pos 1, 1 Bit +0x248 HideFromDebugger : Pos 2, 1 Bit +0x248 ActiveImpersonationInfo : Pos 3, 1 Bit +0x248 SystemThread : Pos 4, 1 Bit +0x248 HardErrorsAreDisabled : Pos 5, 1 Bit +0x248 BreakOnTermination : Pos 6, 1 Bit +0x248 SkipCreationMsg : Pos 7, 1 Bit +0x248 SkipTerminationMsg : Pos 8, 1 Bit +0x24c SameThreadPassiveFlags : Uint4B +0x24c ActiveExWorker : Pos 0, 1 Bit +0x24c ExWorkerCanWaitUser : Pos 1, 1 Bit +0x24c MemoryMaker : Pos 2, 1 Bit +0x250 SameThreadApcFlags : Uint4B +0x250 LpcReceivedMsgIdValid : Pos 0, 1 Bit +0x250 LpcExitThreadCalled : Pos 1, 1 Bit +0x250 AddressSpaceOwner : Pos 2, 1 Bit +0x254 ForwardClusterOnly : UChar +0x255 DisablePageFaultClustering : UChar |
理论上都都讲完了,我们来回顾下:
1、不论SnipeSword,IceSword,DarkSpy,又或是GMER(如果我没记错,GMER应该是使用NtTerminateProcess的方法来结束进程的),最终杀进程都要过PspTerminateThreadByPointer
2、PspTerminateThreadByPointer会检查线程的SystemThread标志(ETHREAD结构中CrossThreadFlags的第四位),如果被置为1,就返回拒绝访问(STATUS_ACCESS_DENIED)
3、ETHREAD结构在不同系统中不同,所以我们需要判断操作系统版本(这个我不会在演示代码中出现,交给大家了:P)。
但是我们仍然有一些问题没有解决:
1、进程的ETHREAD结构在内存中的地址是多少?
2、ETHREAD结构存在于系统高2G空间,如何修改?
至于判断系统版本,相信大家都会 :)
1、ETHREAD结构的地址
ntdll.dll导出了一个未文档化的函数——NtQuerySystemInformation,它可以用来查询SystemHandleInformation,查询SystemHandleInformation的返回的数据结构如下(懒得再弄vb的了,直接把C的弄过来,这个可能跟我的代码中的定义有些不同,但是实际上被用到的数据在结构中的偏移是相同的:P):
引用:
typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO { USHORT UniqueProcessId; USHORT CreatorBackTraceIndex; UCHAR ObjectTypeIndex; UCHAR HandleAttributes; USHORT HandleValue; PVOID Object; ULONG GrantedAccess; } SYSTEM_HANDLE_TABLE_ENTRY_INFO, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO; typedef struct _SYSTEM_HANDLE_INFORMATION { ULONG NumberOfHandles; SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[ 1 ]; } SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION; |
NumberOfHandles表示返回的数组个数,SYSTEM_HANDLE_TABLE_ENTRY_INFO则是返回的数据。
SYSTEM_HANDLE_TABLE_ENTRY_INFO中的HandleValue是句柄的值,Object便是这个Handle对应的对象(Object,如果Handle类型为线程,那么这个对象就是ETHREAD结构)的地址。
嗯,我想我说的还是比较清楚的。
2、修改物理内存
ntdll.dll还导出了一个函数叫做NtSystemDebugControl,这个函数可以用来操作高2G的内存空间。
剩下的废话不多说了~ 大家看附件把 :)
完整的代码和程序:)