TA的每日心情 | 开心 2024-12-9 18:45 |
---|
签到天数: 124 天 [LV.7]常住居民III
|
欢迎您注册加入!这里有您将更精采!
您需要 登录 才可以下载或查看,没有账号?注册
x
在Windows 95下,位于TIB中的偏移0x30的是指向拥有该线程的进程的基址数据指针。在Windows NT 4.0中,这个偏移保存的是指向结构体的指针,该结构体实现于kernel32.dll。遗憾的是,到现在为止,除了几个域之外我还不清楚这个结构体的格式。除此之外,类似的,在Win 2K中EB结构体也发生了变化。
typedef struct _PROCESS_PARAMETERS {
/*000*/ ULONG AllocationSize;
/*004*/ ULONG ActualSize;
/*008*/ ULONG Flags;//PPFLAG_xxx
/*00c*/ ULONG Unknown1;
/*010*/ ULONG Unknown2;
/*014*/ ULONG Unknown3;
/*018*/ HANDLE InputHandle;
/*01c*/ HANDLE OutputHandle;
/*020*/ HANDLE ErrorHandle;
/*024*/ UNICODE_STRING CurrentDirectory;
/*028*/ HANDLE CurrentDir;
/*02c*/ UNICODE_STRING SearchPaths;
/*030*/ UNICODE_STRING ApplicationName;
/*034*/ UNICODE_STRING CommandLine;
/*038*/ PVOID EnvironmentBlock;
/*03c*/ ULONG Unknown[9];
UNICODE_STRING Unknown4;
UNICODE_STRING Unknown5;
UNICODE_STRING Unknown6;
UNICODE_STRING Unknown7;
} PROCESS_PARAMETERS, *PPROCESS_PARAMETERS;
typedef struct _PEB { // Size: 0x1D8
/*000*/ UCHAR InheritedAddressSpace;
/*001*/ UCHAR ReadImageFileExecOptions;
/*002*/ UCHAR BeingDebugged;
/*003*/ UCHAR SpareBool; // Allocation size
/*004*/ HANDLE Mutant;
/*008*/ HINSTANCE ImageBaseAddress; // Instance
/*00C*/ VOID *DllList;
/*010*/ PPROCESS_PARAMETERS *ProcessParameters;
/*014*/ ULONG SubSystemData;
/*018*/ HANDLE DefaultHeap;
/*01C*/ KSPIN_LOCK FastPebLock;
/*020*/ ULONG FastPebLockRoutine;
/*024*/ ULONG FastPebUnlockRoutine;
/*028*/ ULONG EnvironmentUpdateCount;
/*02C*/ ULONG KernelCallbackTable;
/*030*/ LARGE_INTEGER SystemReserved;
/*038*/ ULONG FreeList;
/*03C*/ ULONG TlsExpansionCounter;
/*040*/ ULONG TlsBitmap;
/*044*/ LARGE_INTEGER TlsBitmapBits;
/*04C*/ ULONG ReadOnlySharedMemoryBase;
/*050*/ ULONG ReadOnlySharedMemoryHeap;
/*054*/ ULONG ReadOnlyStaticServerData;
/*058*/ ULONG AnsiCodePageData;
/*05C*/ ULONG OemCodePageData;
/*060*/ ULONG UnicodeCaseTableData;
/*064*/ ULONG NumberOfProcessors;
/*068*/ LARGE_INTEGER NtGlobalFlag; // Address of a local copy
/*070*/ LARGE_INTEGER CriticalSectionTimeout;
/*078*/ ULONG HeapSegmentReserve;
/*07C*/ ULONG HeapSegmentCommit;
/*080*/ ULONG HeapDeCommitTotalFreeThreshold;
/*084*/ ULONG HeapDeCommitFreeBlockThreshold;
/*088*/ ULONG NumberOfHeaps;
/*08C*/ ULONG MaximumNumberOfHeaps;
/*090*/ ULONG ProcessHeaps;
/*094*/ ULONG GdiSharedHandleTable;
/*098*/ ULONG ProcessStarterHelper;
/*09C*/ ULONG GdiDCAttributeList;
/*0A0*/ KSPIN_LOCK LoaderLock;
/*0A4*/ ULONG OSMajorVersion;
/*0A8*/ ULONG OSMinorVersion;
/*0AC*/ USHORT OSBuildNumber;
/*0AE*/ USHORT OSCSDVersion;
/*0B0*/ ULONG OSPlatformId;
/*0B4*/ ULONG ImageSubsystem;
/*0B8*/ ULONG ImageSubsystemMajorVersion;
/*0BC*/ ULONG ImageSubsystemMinorVersion;
/*0C0*/ ULONG ImageProcessAffinityMask;
/*0C4*/ ULONG GdiHandleBuffer[0x22];
/*14C*/ ULONG PostProcessInitRoutine;
/*150*/ ULONG TlsExpansionBitmap;
/*154*/ UCHAR TlsExpansionBitmapBits[0x80];
/*1D4*/ ULONG SessionId;
} PEB, *PPEB;
在TEB的开头是NT_TIB结构体(TEB和TIB的结合)。这个结构体中的大部分名字都很易懂,最
有意思的是指向异常处理链表的指针peExcept(Fs:[0])。这个域经常被引用。如果在随便
某个Win32应用程序下看一下实际的情况,可以看到类似下面这样的代码:
.01B45480: 64A100000000 mov eax,fs:[000000000]
.01B45486: 55 push ebp
.01B45487: 8BEC mov ebp,esp
.01B45489: 6AFF push 0FF
.01B4548B: 68F868B401 push 001B468F8
.01B45490: 687256B401 push 001B45672
.01B45495: 50 push eax
.01B45496: 64892500000000 mov fs:[000000000],esp
.01B4549D: 83EC78 sub esp,078
这段有代表性的代码是由编译器生成的,用于在堆栈中生成_EXCEPTION_REGISTRATION_RECO
RD。这个堆栈中的结构体用于实现称作“structured exception handling”的机制,这就是
结构化异常处理。接着,我们来看Windows NT下的结构化异常处理。这个机制可真是十分著
名,而且实现在编译器的细节之中。在MSDN中可以找到Matt Petriek写得非常详细的文章,
题为“A Crash Course on the Depths of Win32 Structured Exception Handling”,
此文介绍的就是这项机制。
FS:[0]中的指针是指向_EXCEPTION_REGISTRATION_RECORD首部的指针。对应地,每个结构体
在pNext域中包含着指向下一个结构体的指针和指向回调函数pfnHandler的指针。不难猜到,
这就是异常处理的处理程序。函数的原型如下:
EXCEPTION_DISPOSITION __cdecl _except_handler(
struct _EXCEPTION_RECORD *ExceptionRecord,
void * EstablisherFrame,
struct _CONTEXT *ContextRecord,
void * DispatcherContext
);
我们来分析函数的参数。第一个参数是指向下面结构体的指针。
typedef struct _EXCEPTION_RECORD {
DWORD ExceptionCode;
DWORD ExceptionFlags;
struct _EXCEPTION_RECORD *ExceptionRecord;
PVOID ExceptionAddress;
DWORD NumberParameters;
DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
} EXCEPTION_RECORD;
ExceptionCode是Windows NT的异常代号。异常在NTSTATUS.H文件中被描述为STATUX_xxxxxx
: ExceptionAddres - 发生异常的地址。
第三个参数是指向CONTEXT结构体的指针。
typedef struct _CONTEXT
{
DWORD ContextFlags;
DWORD Dr0;
DWORD Dr1;
DWORD Dr2;
:.
DWORD Esp;
DWORD SegSs;
} CONTEXT;
这个结构体定义于WINNT.H文件。其意义是不言而喻的,这里就不全写了。函数返回下面枚举
类型值中的一个:
typedef enum _EXCEPTION_DISPOSITION {
ExceptionContinueExecution,
ExceptionContinueSearch,
ExceptionNestedException,
ExceptionCollidedUnwind
} EXCEPTION_DISPOSITION;
ExceptionFlags定义了下面的位标志:
#define EH_NONCONTINUABLE 1
#define EH_UNWINDING 2
#define EH_EXIT_UNWIND 4
#define EH_STACK_INVALID 8
#define EH_NESTED_CALL 0x10
在发生异常时,控制传递到ntoskrnl.exe中相应的处理程序。例如,如果试图下面这段代码
:
mov eax,80100000h
mov dword ptr [eax],0
这会引发异常0e,控制传递向处理程序KiTrap0E,堆栈中错误号为07(试图在用户模式下向
内核写入。该页位于内存中,因为线性地址80100000h是内核加载的起始地址)。之后,控制
传递到ntdll.dll,在这里解析线程的TEB并顺次执行链表中所有的处理程序,直到某个函数
的返回代号不是ExceptionContinueSearch。在此之后,再次调用链表中的所有处理函数,直
到找到某个函数,但这次用的是另外一个代号ExceptionCode(STATUS_UNWIND)并在Except
ionFlags设置位EH_UNWINDINGS。这个标志用于异常处理,进行堆栈的清除和其它必要的工作
。如果没有一个处理程序能处理,则就来到链表中最后一个处理程序,由Win32子系统建立的
处理程序。这个处理程序是如何被建立的以及建立在哪里在以后研究CreateProcessW函数时
会讲到。关于异常处理的完整介绍可以从MSDN获得,因为在反汇编源代码中所得到的是扩大
化了的异常处理机制(更高层次的机制)。
04. 进程控制域(PROCESSOR CONTROL REGION)
===========================================================
当线程在内核模式下执行时,在FS寄存器中加载的是选择子30,用于寻址PCR结构体(基址0
xFFDFF000,界限0x00001FFF)。在NTDDK.H中远没有描述结构体所有的成员,只是其中不多
的部分。为了说明PCR中的成分信息,这里列出用于i386的结构体(Windows NT 4.0)
typedef struct _KPCR { // Size: 0xB10
/*000*/ NT_TIB NtTib;
/*01C*/ struct _KPCR* SelfPcr;
/*020*/ struct _KPRCB* Prcb; // Current PCB
/*024*/ KIRQL Irql; // Current IRQL
/*028*/ ULONG IRR;
/*02C*/ ULONG IrrActive;
/*030*/ ULONG IDR;
/*034*/ ULONG Reserved2;
/*038*/ struct _KIDTENTRY* ULONG IDT;
/*03C*/ struct _KGDTENTRY* GDT;
/*040*/ struct _KTSS* TSS;
/*044*/ USHORT MajorVersion; // 1
/*046*/ USHORT MinorVersion; // 1
/*048*/ KAFFINITY SetMember;
/*04C*/ ULONG StallScaleFactor;
/*050*/ UCHAR DebugActive;
/*051*/ UCHAR Number;
// End of official portion of KPCR
/*052*/ BOOLEAN VdmAlert;
/*053*/ UCHAR Reserved;
/*054*/ UCHAR KernelReserved[0x90-0x54];
/*090*/ ULONG SecondLevelCacheSize;
/*094*/ UCHAR HalReserved[0xD4-0x94];
/*0D4*/ ULONG InterruptMode;
/*0D8*/ ULONG Spare1;
/*0DC*/ UCHAR KernelReserved2[0x120-0xDC];
// Note that current thread is at offset 0x124 (pointer to KTHREAD)
// This essentially means that the 1st PRCB must match the active CPU
/*120*/ UCHAR PrcbData[0xB10-0x120]; // PCBs for all CPUs supported
} KPCR, *PKPCR;
PCR包含着指向PCRB(Processor Control Region)的指针,但实际上,PCRB位于PCR的偏移
0x120处,并且所有的向PCRB域的转换都是相对于PCR的起点的。在WINNT.H中描述了PCRB结构
体。这里给出整个结构体(Windows NT 4.0):
// 0x120 from KPCR
typedef struct _KPRCB {
/*000*/ USHORT MinorVersion;
/*002*/ USHORT MajorVersion;
/*004*/ struct _KTHREAD *CurrentThread;
/*008*/ struct _KTHREAD *NextThread;
/*00c*/ struct _KTHREAD *IdleThread;
/*010*/ CCHAR Number;
/*011*/ CCHAR Reserved;
/*012*/ USHORT BuildType;
/*014*/ KAFFINITY SetMember;
/*015*/ struct _RESTART_BLOCK *RestartBlock;
/*018*/ CCHAR CpuType;
/*019*/ CCHAR CpuID;
/*01A*/ CCHAR CpuStep;
/*01C*/ KPROCESSOR_STATE ProcessorState;
/*13C*/ CCHAR KernelReserved[0x40];
/*17C*/ CCHAR HalReserved[0x40];
/*1BC*/ ULONG NpxThread;
/*1C0*/ ULONG InterruptCount;
/*1C4*/ ULONG KernelTime;
/*1C8*/ ULONG UserTime;
/*1CC*/ ULONG DpcTime;
/*1D0*/ ULONG InterruptTime;
/*1D4*/ ULONG ApcBypassCount;
/*1D8*/ ULONG DpcBypassCount;
/*1DC*/ ULONG AdjustDpcThreshold;
/*1E0*/ UCHAR Spare2[0x14];
/*1F4*/ ULONG64 ThreadStartCount;
/*1FC*/ SINGLE_LIST_ENTRY FsRtlFreeSharedLockList;
/*200*/ SINGLE_LIST_ENTRY FsRtlFreeExclusiveLockList;
/*204*/ ULONG CcFastReadNoWait;
/*208*/ ULONG CcFastReadWait;
/*20C*/ ULONG CcFastReadNotPossible;
/*210*/ ULONG CcCopyReadNoWait;
/*214*/ ULONG CcCopyReadWait;
/*218*/ ULONG CcCopyReadNoWaitMiss;
/*21C*/ ULONG KeAlignmentFixupCount;
/*220*/ ULONG KeContextSwitches;
/*224*/ ULONG KeDcacheFlushCount;
/*228*/ ULONG KeExceptionDispatchCount;
/*22C*/ ULONG KeFirstLevelTbFills;
/*230*/ ULONG KeFloatingEmulationCount;
/*234*/ ULONG KeIcacheFlushCount;
/*238*/ ULONG KeSecondLevelTbFills;
/*23C*/ ULONG KeSystemCalls;
/*240*/ SINGLE_LIST_ENTRY FsRtlFreeWaitingLockList;
/*244*/ SINGLE_LIST_ENTRY FsRtlFreeLockTreeNodeList
/*248*/ CCHAR ReservedCounter[0x18];
/*260*/ PVOID SmallIrpFreeEntry;
/*264*/ PVOID LargeIrpFreeEntry;
/*268*/ PVOID MdlFreeEntry
/*26C*/ PVOID CreateInfoFreeEntry;
/*270*/ PVOID NameBufferFreeEntry
/*274*/ PVOID SharedCacheMapEntry
/*278*/ CCHAR CachePad0[8];
/*280*/ CCHAR ReservedPad[0x200];
/*480*/ CCHAR CurrentPacket[0xc];
/*48C*/ ULONG TargetSet;
/*490*/ PVOID WorkerRoutine;
/*494*/ ULONG IpiFrozen;
/*498*/ CCHAR CachePad1[0x8];
/*4A0*/ ULONG RequestSummary;
/*4A4*/ ULONG SignalDone;
/*4A8*/ ULONG ReverseStall;
/*4AC*/ ULONG IpiFrame;
/*4B0*/ CCHAR CachePad2[0x10];
/*4C0*/ ULONG DpcInterruptRequested;
/*4C4*/ CCHAR CachePad3 [0xc];
/*4D0*/ ULONG MaximumDpcQueueDepth;
/*4D4*/ ULONG MinimumDpcRate;
/*4D8*/ CCHAR CachePad4[0x8];
/*4E0*/ LIST_ENTRY DpcListHead;
/*4E8*/ ULONG DpcQueueDepth;
/*4EC*/ ULONG DpcRoutineActive;
/*4F0*/ ULONG DpcCount;
/*4F4*/ ULONG DpcLastCount;
/*4F8*/ ULONG DpcRequestRate;
/*4FC*/ CCHAR KernelReserved2[0x2c];
/*528*/ ULONG DpcLock;
/*52C*/ CCHAR SkipTick;
/*52D*/ CCHAR VendorString[0xf];
/*53C*/ ULONG MHz;
/*540*/ ULONG64 FeatureBits;
/*548*/ ULONG64 UpdateSignature;
/*550*/ ULONG QuantumEnd;
} KPRCB, *PKPRCB, *RESTRICTED_POINTER PRKPRCB;
PCRB中最有用的就是指向当前线程的指针(KTHREAD结构体)。通常内核代码以以下代码取得
此指针:
《加密解密》中没有讲得很清楚,初学者可能有点疑问。这里写得教清楚:
FS:[0]中的指针是指向_EXCEPTION_REGISTRATION_RECORD首部的指针。对应地,每个结
构体在pNext域中包含着指向下一个结构体的指针和指向回调函数pfnHandler的指针。不
难猜到,这就是异常处理的处理程序。函数的原型如下:
EXCEPTION_DISPOSITION __cdecl _except_handler(
struct _EXCEPTION_RECORD *ExceptionRecord,
void * EstablisherFrame,
struct _CONTEXT *ContextRecord,
void * DispatcherContext
);
我们来分析函数的参数。第一个参数是指向下面结构体的指针。
typedef struct _EXCEPTION_RECORD {
DWORD ExceptionCode;
DWORD ExceptionFlags;
struct _EXCEPTION_RECORD *ExceptionRecord;
PVOID ExceptionAddress;
DWORD NumberParameters;
DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
} EXCEPTION_RECORD;
ExceptionCode是Windows NT的异常代号。异常在NTSTATUS.H文件中被描述为STATUX_xx
xxxx
: ExceptionAddres - 发生异常的地址。
第三个参数是指向CONTEXT结构体的指针。
typedef struct _CONTEXT
{
DWORD ContextFlags;
DWORD Dr0;
DWORD Dr1;
DWORD Dr2;
:.
DWORD Esp;
DWORD SegSs;
} CONTEXT;
这个结构体定义于WINNT.H文件。其意义是不言而喻的,这里就不全写了。函数返回下面
枚举类型值中的一个:
typedef enum _EXCEPTION_DISPOSITION {
ExceptionContinueExecution,
ExceptionContinueSearch,
ExceptionNestedException,
ExceptionCollidedUnwind
} EXCEPTION_DISPOSITION;
ExceptionFlags定义了下面的位标志:
#define EH_NONCONTINUABLE 1
#define EH_UNWINDING 2
#define EH_EXIT_UNWIND 4
#define EH_STACK_INVALID 8
#define EH_NESTED_CALL 0x10
在发生异常时,控制传递到ntoskrnl.exe中相应的处理程序。例如,如果试图下面这段
代码:
mov eax,80100000h
mov dword ptr [eax],0
这会引发异常0e,控制传递向处理程序KiTrap0E,堆栈中错误号为07(试图在用户模式
下向内核写入。该页位于内存中,因为线性地址80100000h是内核加载的起始地址)。之
后,控制传递到ntdll.dll,在这里解析线程的TEB并顺次执行链表中所有的处理程序,
直到某个函数的返回代号不是ExceptionContinueSearch。在此之后,再次调用链表中的
所有处理函数,直到找到某个函数,但这次用的是另外一个代号ExceptionCode(STATU
S_UNWIND)并在ExceptionFlags设置位EH_UNWINDINGS。这个标志用于异常处理,进行堆
栈的清除和其它必要的工作。如果没有一个处理程序能处理,则就来到链表中最后一个
处理程序,由Win32子系统建立的处理程序。这个处理程序是如何被建立的以及建立在哪
里在以后研究CreateProcessW函数时会讲到。关于异常处理的完整介绍可以从MSDN获得
,因为在反汇编源代码中所得到的是扩大化了的异常处理机制(更高层次的机制)。
这样初学者研究SHE反调试就有章可寻了 |
|