内核学习之调用门试验

  • 调用门实验

目标: 使得一个3环程序能够读取出0环的数据

步骤分析:

  1. 3环程序的cs段寄存器保存的是3环的CPL

  1. 3环程序的cs段寄存器不可见部分保存的段描述符,是一个DPL为3的段描述符

  1. 需要将3环的cs段寄存器的CPL改成0 , 将一个0环的代码段描述符加载到cs的不可见部分. 最后3环程序才有0环的特权等级去访问0环的数据.

  • 实现步骤:

  1. 在GDT表中手工构造一个调用门

  1. 在3环程序中加载这个调用门.(加载调用门是为了将0环的段描述符加载到3环的cs段寄存器.)

  2.1 要在环将一个描述符加载cs中必须满足以下要求:

  2.1.1 需要用到间接修改cs段寄存器的指令

  2.1.2 CPL<=DPL && RPL <= DPL,此处的DPL指的是调用门保存的DPL

  因此, 调用门的DPL必须是3

  2.1.3 由于门描述符在运行后, 会直接将门描述符中的段选择子加载cs中.

  因此, 可以将一个0环的代码段的段选择子保存调用门的段选择子部分

  2.1.4 加载之后, 需要转移到一个地址上执行代码,这个地址是在调用门的段内偏移部分来指定的.

因此, 可以将一个函数的地址存放到这个位置.

// 实现的主要代码
int g_num;
short g_ss;
int g_esp;

//通过调用门调用的函数
void _declspec(naked) GateFun()
{
    g_num = 100;
    _asm mov [ g_esp ] , esp;
    _asm mov ax , ss;
    _asm mov word ptr [g_ss],ax
    _asm retf;
}

int main()
{
    printf( "调用门函数地址:%08X\n" , GateFun );
    printf( "切换的段选择子:%04X\n" , 8 );/*8是内核中的代码段选择子*/

    unsigned long long descript =
        createCallGateDescript(8/*8是内核中的代码段选择子*/ , ( unsigned int )GateFun , 0 );


    printf("请将这个段描述符写入到GDT[9]中: ");
    std::cout << std::hex <<std::uppercase<< std::setfill('0')<<std::setw(8)<< descript<<'\n';
    system( "pause" );


    // 获取当前寄存器的值.
    _asm mov[ g_esp ] , esp;
    _asm mov ax , ss;
    _asm mov word ptr[ g_ss ] , ax

    printf( "调用前  esp=%08X, ss=%04X\n" , g_esp , g_ss );

    // 前4字节是EIP,后2字节是CS(0x004b)
    char buff[ ] = { 0,0,0,0,0x4b,00 };

    _asm call fword ptr ds:[buff];

    printf( "调用后  esp=%08X, ss=%04X\n" , g_esp , g_ss );
    printf( "g_num=%d\n" , g_num );
    system( "pause" );
}

 

  1. 使用调用门之前,需要将调用门的描述符写进系统的GDT表中,使用WinDbg双机调试,获取GDT表的地址

  kd> rgdtr

  gdtr=80b95000

 

  1. 使用dq命令查看GDT表中哪些是空闲的(值为0是空闲的)

    kd> dq 80b95000
    ​
    80b95000  00000000`00000000 00cf9b00`0000ffff
    ​
    80b95010  00cf9300`0000ffff 00cffb00`0000ffff
    ​
    80b95020  00cff300`0000ffff 80008b1e`500020ab
    ​
    80b95030  84409313`ac003748 0040f300`00000fff
    ​
    80b95040  0000f200`0400ffff 00000000`00000000
    ​
    80b95050  84008913`80000068 84008913`80680068
    ​
    80b95060  00000000`00000000 00000000`00000000
    ​
    80b95070  800092b9`500003ff 00000000`00000000

     

     

   2. 构造一个可以在3环代码中使用的段选择子,构造规则:

  按照段选择子格式: | 描述符在表中的下标:13 | T1:0 | RPL:2 |

  由上面可以知道在GDT表中,第9项是空闲的,门描述符将保存在这个地方,则对应的段选择子:

13位描述符表索引 1位:T1 2位:RPL
十进制 9 0 3
二进制 1001 0 0x11

  合并为: 0100 1011 十六进制为 4B

  前4字节是EIP,后2字节是CS,后两个字节是0x004B,此数值就是在第二步中构造出来的

char buff[] = {0,0,0,0,0x4b,00};
​
__asm call fword ptr ds:[buff]
3. 把生成的程序放入虚拟机中运行 0119EC00~000811CC

 
4. 根据步骤2中的信息可以得到,GDT[9]的地址为80b95000 + 6 * 8或者80b95040 + 8开始写入
kd> eq 80b95048 0119EC00`000811CC

5. 写入之后查看发现已经写入成功

kd> dq 80b95000

80b95000 0000000000000000 00cf9b000000ffff

80b95010 00cf93000000ffff 00cffb000000ffff

80b95020 00cff3000000ffff 80008b1e500020ab

80b95030 84409313ac003748 0040f30000000fff

80b95040 0000f2000400ffff 0119ec00000811cc

80b95050 8400891380000068 8400891380680068

80b95060 0000000000000000 0000000000000000

80b95070 800092b9500003ff 0000000000000000

 6. 继续g回到虚拟机中往下查看,写入成功实验完成。

 

 

 

 

 

posted on 2019-08-28 22:13  #搬砖仔  阅读(484)  评论(0编辑  收藏  举报

导航