第七章——Windows内核基础-内核数据结构(内核对象,SSDT,TEB,PEB)

一,内核对象
内核对象的结构图
一个内核对象分为对象头和对象体两个部分,在对象头中至少有一个object_header和其他信息。对象体紧挨着对象头中的object_header。
这里注意对象指针指向的并不是对象头,如果要访问对象头,需要拿对象体减去一个特定的偏移值,获取到对象头,在通过对象头获取其他字段
Windows内核对象可以分为三类:
⑴.Dispatcher对象
        之所以叫Dispatcher对象,是因为这种对象在对象体的开始位置存放了一个特定的公共数据结构DISPATCHER_HEADER。
        包含DISPATCHER_HEADER结构的内核对象的名字都是以字母K开头,表示这是一个内核对象,例如:KPROCESS,KTHREAD,KEVENT等等,但是反过来说,以K开头的内        核对象不一定就是Dispatcher对象。 是不是Dispatcher对象还是取决于是否有DISPATCHER_HEADER结构体。
        此对象都是可以等待的内核对象,这些对象可以作为参数传给内核KeWaitForSingleObject函数,以及常见的应用层函数WaitForSingleObject
 
⑵.I/O对象
        I/O对象在对象体开始位置并没有放置DISPATCHER_HEADER结构体,但通常会放置一个type和size有关的整形成员,其中的type表示该内核对象的类型(例如文件的内核对象类型为26),size则表示为大小。常见的I/O对象有:DEVICE_OBJECT,FILE_OBJECT,IRP,DRIVER_OBJECT
 
⑶.其他对象
        除了以上两类对象之外的都为其他内核对象,这里只说进程对象(EPROCESS)和线程对象(ETHREAD)
        进程对象EPROCESS:每个进程都有一个EPROCESS结构,记录了各种数据,所有的EPROCESS都被存放在一个双向链表中,我们在枚举进程的时候,通过遍历链表就可以查看到所有的进程。当然自己也可以将自己的进程从这个双向链表中摘除,从而隐藏自身。
        有两个内核函数可以虎丘到EPROCESS结构
        ①PsLookupProcessByProcessID(
                IN         HANDLE processID,
                OUT     PEPROCESS * process );
        ②PsGetCurrentProcess();直接获取当前进程的EPOCESS
 
EPROCESS结构的常见数据
    
    ETHREAD结构是管理线程的内核对象,每个线程都有对象的EHREAD结构,第一个成员就是线程对象KTHREAD成员,所有的ETHREAD结构也被放在一个双向链表中
    ETHREAD重要成员:KTHREAD Tcb       线程内核对象。
                                    CLIENT_ID Cid       进程ID    
 
    KPROCESS,EPROCESS,ETHREAD,KTHREAD之间的关系图:
可以看出,EPROCESS和ETHREAD都是双向链表组织管理,一个EPROCESS中包含一个KPROCESS,这个KPROCESS中指向一个ETHREAD结构,在ETHREAD中又包含一个KTHREAD结构
 
 
二,SSDT
SSDT全称系统服务描述符表,通过ntoskrnl.exe导出。一个函数的调用,在应用层会先在kernel32.dll中调用,在通过ntdll中检查参数,之后通过调用中断进入0环,将调用函数的服务号存放到eax中,之后根据eax的值(索引)在SSDT中找到指定的函数
结构如下:
typedef struct ServiceDescriptorEntry {
     unsigned int *ServiceTableBase;                   //表的基地址
     unsigned int *ServiceCounterTableBase;                        
     unsigned int NumberOfServices;                //表中服务函数的个数       
     unsigned char *ParamTableBase;
} ServiceDescriptorTableEntry_t, *PServiceDescriptorTableEntry_t;
 
SSDT表最重要的两个成员一个为表的基地址,一个为函数个数
SSDT其实就是一个存放函数指针的数值,既然是函数指针,那寻址查找函数的方式就是:ServiceTableBase+index*4 (32位机器)
在64位上的寻址方式是基地址+索引*16。也可以用左移4位表示= [基地址+index*4]>>4+基地址
 
Shadow SSDT:它与SSDT类似,只是它包含的事ntoskrnel.exe和win32k.sys服务函数,主要处理user32.dll和GDI32.dll的系统调用
截屏保护,按键保护,搜索窗口保护,防止窗口关闭保护都可以从Shadow SSDT中的函数HOOK而实现
 
 
三,TEB(线程环境块)
    一个进程的所有的TEB都存放在从0X7FFDE000开始的线性内存中,每4KB为一个完整的TEB
    在fs:[0]的地址执行TEB结构,这个结构开头是一个NT_TIB结构
 TEB结构:   
TEB at fs:7FFDF000
+0x000 NtTib // _NT_TIB
+0x01c EnvironmentPointer // Ptr32 Void
+0x020 ClientId // _CLIENT_ID
+0x028 ActiveRpcHandle // Ptr32 Void
+0x02c ThreadLocalStoragePointer // Ptr32 Void
+0x030 ProcessEnvironmentBlock // Ptr32 _PEB 这里指向 PEB 表,即进程环境块
 
NT_TIB结构:
typedef struct _NT_TIB //sizeof 1ch
{
00h struct _EXCEPTION_REGISTRATION *ExceptionList; //SEH链入口
04h PVOID StackBase; //堆栈基址
08h PVOID StackLimit; //堆栈大小
0ch PVOID SubSystemTib;
union {
PVOID FiberData;
10h DWORD Version;
};
14h PVOID ArbitraryUserPointer;
18h struct _NT_TIB *Self; //本NT_TIB结构自身的线性地址
}NT_TIB;
 
如何访问TEB? 可以通过NtCurrentTeb函数和FS段寄存器访问这两种方法访问TEB
FS段寄存器访问:mov eax,dword ptr fs:[0x18];   //指向自身的TEB结构
这里如果是FS:[0]则表示为NT_TIB结构体
 
 
四,PEB
PEB在TEB的0x30偏移处查找,获取PEB的方法:
mov eax,dword ptr fs:[0x18];        //查找TEB
mov eax,dword ptr [eax+0x30];       //查找PEB
PEB+0X2地方为BeingDebugged.如果被调试该字段为1,反之为0
PEB结构的字段非常多,这里不一一赘述
总结下PEB,TEB和EPROCESS,ETHREAD关系
posted @ 2018-12-27 16:11  峰中追风  阅读(1747)  评论(0编辑  收藏  举报

___________________________________________________________________________________________没有白跑的路