分段模式复习问答
1. 段描述符保存在什么位置? 怎么找到它?
1.1 段描述符保存在内存中, 其内存首地址记录在GDTR寄存器中, GDTR寄存器的是48位.高32位保存地址,低16保存长度.
可以通过`lgdt`指令来设置GDTR寄存器, 通过`sgdt`来获取GDTR寄存器的值.
2. 段描述符都描述了什么?
2.1 描述了段的长度,基地址,属性
2.1.1 P - 描述符是否有效
2.1.2 G - 粒度影响段限长的
2.1.3 S - 段的种类
2.1.3.1 S==1 代码段/数据段
2.1.3.1 S==0 系统段
2.1.4 Type - 段的类型, 受到S位的影响.
2.1.4.1 S==1 代码段/数据段,
2.1.4.1.1 Type最高位 == 1 - 代码段
2.1.4.1.1 Type最高位 == 0 - 数据段
2.1.4.2 S==0
2.1.4.2.1 Type的值表示门描述符的种类
2.1.5 DPL - 段描述符的特权等级
2.1.6 D/B位 - 针对不同的段
2.1.6.1 对于CS段 - 等于1表示32位寻址模式,等于0表示十六位.
2.1.6.2 对于SS段, 等于1默认寄存器是ESP,等于0默认寄存器是SP
2.1.7 AVL - 供软件使用
2.1.8 L - 等于1表示64位代码
3. 段寄存器中保存的值是什么?
3.1 保存的是段选择子, 格式为13-1-2
3.1.1 其中13位表示是段描述符表的下标
3.1.2 其中1位保存的是T1,保存是使用哪个描述符表,等于0表示使用GDT表. 后2位表示RPL(请求特权等级),如果这个段寄存器是CS的话,那么这后2位表示CPL(当前特权等级).
4. 对于下面的指令,执行之后功能是什么?
mov ax,0x30 ; ax=0x30
mov fs,ax ; fs=
0x30==>13-1-2 ==> 6-0-0
将GDT表中第6个段描述符加载到FS段寄存器的不可见部分,然后将段选择子0x30保存到FS的可见部分,
在加载时CPU会做权限检查:
P Si Gr Pr Lo
Sel Base Limit Type l ze an es ng Flags
---- -------- -------- ---------- - -- -- -- -- --------
0030 84176c00 00003748 Data RW Ac 0 Bg By P Nl 00000493
检查的方式是:
if( CPL <= DPL && RPL <= DPL ){
// 可以加载
}
else{
// 触发一个异常.
}
分页模式复习问答
1. 在非物理地址扩展模式下, 一个虚拟地址的格式是什么? 开启了物理地址扩展模式之后又是什么?
1.1 非PAE模式,虚拟地址格式是: 10-10-12
10位 - 是页目录首地址
10位 - 页表首地址
12位 - 页内偏移
1.2 PAE模式,虚拟地址格式是: 2-9-9-12
2 - 页目录指针表首地址
9 - 页目录表首地址
9 - 页表首地址
12 - 页内偏移
2. 非物理地址扩展模式下,一个进程的页目录基址保存在什么地方?
2.1 保存在CR3寄存器中.
3. 页目录,页表,页内偏移的关系是什么?
3.1 页内偏移总大小占12个比特,然后一个分页是4Kb,12个比特位最大值就是4Kb, 能够表示一个分页内的所有字节.页表保存的是内存分页的首地址, 页目录保存的是页表的首地址.
4. 虚拟地址转译成物理地址的方法是什么?
4.1 根据当前内存管理模式拆成10-10-12/2-9-9-12的格式,
4.2 再找页目录表首地址, 根据虚拟地址拆出来第一部分的值找到页目录的地址.
4.3 再根据虚拟地址拆出来第二部分的值找到页表地址
4.4 再根据虚拟地址拆出来第三部分的值定位到页内偏移.
驱动编程复习问答
1. 内核中的文件路径怎么表示?
1.1 \\??\\文件路径, 用户层的文件路径前加上"\??\"前缀即可.
2. 在内核中,有当前进程的概念, 内核和进程是什么关系, 怎么获取当前进程?
2.1 内核包含了进程 , 一个内核中可以运行多个进程,
2.2 获取当前进程: PsGetCurrentProcess()
3. 在内核中能够随意读写其它进程的内存吗? 怎么解决?
3.1 不能,因为一个虚拟地址要被读写,它先要转换成物理地址,而虚拟地址转换物理地址,需要用到本进程的DirBase(页目录基址),在内核中去读写一个用户层的虚拟地址的时候,默认是使用当前进程的页目录基址去转译虚拟地址的, 因此如果读写其它进程的虚拟地址的话,就需要用到其它进程的页目录基址来转译虚拟地址.
通过进程挂靠可以改变内核中的当前进程.
- KeStackAttachProcess
- KeUnstackDetachProcess
4. 进程内核对象和线程内核对象分别使用哪两个结构体表示?
4.1 进程内核对象 - EPROCESS(KPROCESS)
4.2 线程内核对象 - ETHREAD(KTHREAD)
5. 在内核编程中, 如果一个虚拟地址不可写入, 如何将其改为可写?
5.1 MDL ,MDL能够将一个虚拟地址重新映射成另外一个虚拟地址,然后在映射的时候还可以指定这个新的虚拟地址的属性. 映射出来的新虚拟地址和映射前的虚拟地址它们共用一个物理地址.因此,改掉其中一个,物理内存都会产生变化.
驱动通讯复习问答
1. 设备名的格式是什么?
1.1 "\\Device\\设备名"
2. 符号链接名的格式是什么?
2.1 "\\DosDevices\\符号链接名"
4. 打开设备之后, 调用ReadFile时,哪个派遣函数被调用? 怎么在派遣函数中得到ReadFile的参数?
ReadFile(hDevice , pBuff , 100, &size, NULL);
派遣函数 : IRP_MJ_READ 被调用
参数位置 :
- 输入缓冲区的保存位置:
输入缓冲区保存在IRP结构体变量中,
- UserBuffer : 是用户层直接传进来的虚拟地址
- Assoxx.SystemBuffer : 系统创建缓冲区,拷贝了用户传入进来的缓冲区的内容.也就是用户缓冲区的一个副本.
- MdlAddress : 将用户缓冲区使用MDL重新映射.
- 输入缓冲区的字节数 :
保存在IO_STACK_COMPLATE中,IO_STACK_COMPLATE.Parmaer.Read.Length.
5. DeviceIoControl的CTL_CODE有何作用?
CTL_CODE由四部分组成:
- 设备类型 : 一般和设备对象创建时的类型一致.
- 功能代码 : 自定义的数值, 0~0x800被微软使用了,只能使用0x800以上的代码.
- 传输类型 :
- METHOD_BUFFER : 对缓冲区进行拷贝
- METHOD_IN_DRIRECT : 对输出缓冲区进行缓冲,对输入的缓冲区使用MDL映射
- METHOD_OUT_DRIRECT : 对输入缓冲区进行缓冲,对输出缓冲区使用MDL映射
- METHOD_NEITHER : 两者都不缓冲.
- 访问权限
- 对设备对象的访问权限.
6. DeviceIoControl被调用后,内核的哪个派遣函数被调用?输入缓冲区的字节数在派遣上中怎么获取到,输出缓冲区的字节数在派遣上中怎么获取到?
6.1 IRP_MJ_DEVICE_CONTROL被调用
6.2 参数保存位置;
- 输入缓冲区的位置 : Irp.ASSSCXXX.SystemBuffer
- 输出缓冲区的位置 : 如果使用METHOD_OUT_DRIRECT,则保存在Irp.MdlAddress,如果没有使用, 则还是使用Irp.ASSSCXXX.SystemBuffer(也就是输入和输出缓冲区是同一个)
- 输入/输出缓冲区的字节数 : IO_STACK_COMPLATE.Parmaer.DeviceContol.InputLenght/IO_STACK_COMPLATE.Parmaer.DeviceContol.OutputLenght
# OBJECT HOOK原理
1. 找到对象原型地址表:ObTypeIndexTable
2. 在表中找到要HOOK的对象的原型,遍历表,使用类型名来找出原型.
3. 通过`OBJECT_TYPE`结构体中的`TypeInfo` 就能找到原型中保存的函数指针
4. 将自定义的函数保存到要HOOK的函数指针中.
BYTE sig[]={0x12,0x34,0x56,0x78,0x9A};
for(BYTE* addr = (BYTE*)0x80000000;
addr <= 0xFFFFFFFF;
addr += sizeof(sig))
{
if(memcmp(addr ,sig,sizeof(sig) ) == 0){
break;
}
}
1. sdk,DLL工程
1.1 文件的操作,进程/线程API
2. 线程同步/异步IO/网络套接字
3. PE文件格式
4. Windows内存管理
5. Windows的注入和HOOK
6. 异常处理/调试器原理