编写简单的防杀程序

编写简单的防杀程序

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节选如下:


引用:

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的内存空间。

剩下的废话不多说了~ 大家看附件把 :)

完整的代码和程序:)

你可能感兴趣的:(编写简单的防杀程序)