首页
论坛
课程
招聘
[原创]x86中通过 call far 来进入1环
2021-8-7 16:15 8304

[原创]x86中通过 call far 来进入1环

2021-8-7 16:15
8304

环境:

winxp
windbg
xp中安装vc6(网上找的绿色版,安装包才几十mb,适用于做实验)

基础知识

段描述符
图片描述
代码和数据段
s位为1是代码和数据段描述符
type值小于8为数据段,大于等于8为代码段
图片描述
系统段
s位为0是系统段描述符
我们需要构造一张tss,所以type的值应该为hex:b;bin:1011
图片描述
当前3张图为主要用到的东西

构造gdt

通过 call fat 来实现进入1环,主要需要在gdt中构造3个段描述符:
1环的cs,1环的ss和一块tss

步骤1:

先查看gdt,我们需要构建3个描述符,所以需要3个空白处
图片描述

步骤2

这是海哥原视频中的构建1环tss内存的代码,拿出来改改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
DWORD iTss[0x68] =
{
    0x00000000, //link
    0x00000000//esp0
    0x10000010, //ss0
    0x00000000, //esp1
    0x00000000, //ss1
    0x00000000, //esp2
    0x00000000, //ss2
    (DWORD)iCr3,    //cr3
    0x00401020, //eip
    0x00000000, //eflags
    0x00000000, //eax
    0x00000000, //ecx
    0x00000000, //edx
    0x00000000, //ebx
    (DWROD)bu,  //esp
    0x00000000, //ebp
    0x00000000, //esi
    0x00000000, //edi
    0x00000023, //es
    0x00000008, //cs
    0x00000010, //ss
    0x00000023, //ds
    0x00000030, //fs
    0x00000000, //gs
    0x00000000, //ldt
    0x20ac0000
};

我们需要替换到其中的段寄存器:
可以看看,无论cs,还是ss,都指向一个0环的段中,我们去gdt中找找
图片描述
通过cs和ss拆解得到index,找到这两个描述符,直接拖出来改改就行。

先构建tss

图片描述
我们代码中的tss内存首地址为:0x12fdcc
填入我们刚刚查找出来的三个空描述符中的随便一个
index:dec:12 段选择子selector:bin:1001 0001;hex:91
8003f090 eq 8003f090 0000e912`fdcc0068

构建cs

index:dec:19 1001 1001 selector: 99 hex
8003f098 eq 8003f098 00cfb900`0000ffff

构建ss

index:dec:21 1010 1001 selector: a9 hex
8003f0a8 eq 8003f0a8 00cfb300`0000ffff

 

图片描述
好了,段描述符已经准备就绪

步骤3

通过 !process 0 0 来获取进程信息
图片描述

 

图片描述
从windbg中找到我们已经运行的进程

cr3是用来切换进程,找到这个值,填入到VMware中的进程窗口中

然后下一步,运行

从当前cs和ss的cpl我们就可以看出,我们成功进入1环,并且执行了我们的代码。
由于我们的 call 是一个任务切换,所以并不会向堆栈中压入ss、esp、cs、返回地址,所以返回时要从 tss 中的 ptl 拿取返回值,这时我们要指定 eflags 的 nt 为1,这样 iretd 才不会从堆栈中取返回值。

代码

我们将刚刚我们自己写入gdt的段描述对应的段选择子,写入tss中,tss是一块内存,我们可以直接写入,然后会在我们call时,进行任务切换,替换我们设定好的寄存器值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include <iostream>
#include <Windows.h>
 
DWORD dwOK;
DWORD dwESP;
DWORD dwCS;
 
DWORD dwEFLG;
DWORD dwSS;
 
 
void __declspec(naked) Func()
{
    dwOK = 1;
    __asm
    {
        //int 3
 
        //实验会发现当前eip的eflgs中的nt位为1,看截图
        //返回地址从tss中的ptl中拿出
        //PTL为自动填补的上一个tss连接
        push eax
        pushfd
        pop eax
        mov dwEFLG,eax
        or eax,0x4000
        push eax
        popfd
 
        mov eax,0
        mov dwESP,esp
        mov ax,ss
        mov dwSS,eax
        mov ax,cs
        mov dwCS,eax
 
        pop eax
 
        iretd
    }
}
 
//过去当前eip,通过函数调用堆栈中的返回地址获取
void __declspec(naked) GetEIP()
{
    __asm
    {
        mov eax,dword ptr ss:[esp]
        ret
    }
 
}
 
 
int main()
{
        //查看函数地址
    printf("Function addr:[%x]\n", &Func);
 
    char bu[0x10];    //12ff70
    int iCr3;
    printf("input CR:\n");
    scanf("%x",&iCr3);    //通过windbg工具, !process 0 0 指令获取
    //写入寄存器的值,我们刚刚设定好的段选择子
    DWORD ITss[0x68] =
    {
        0x00000000,//link
        0x00000000,    //esp0
        0x00000000,    //ss0
        0x00000000,    //esp1
        0x000000a9,    //ss1
        0x00000000,    //esp2
        0x00000000,    //ss2
        (DWORD)iCr3,
        (DWORD)&Func,    //eip
        0x00000000,    //eflags
        0x00000000,    //eax
        0x00000000,    //ecx
        0x00000000,    //edx
        0x00000000,    //ebx
        (DWORD)bu,    //esp
        0x00000000,    //ebp
        0x00000000,    //esi
        0x00000000,    //edi
        0x00000023,    //es
        0x00000099,    //cs
        0x000000a9,    //ss
        0x00000023,    //ds
        0x000000a9,    //fs  这里不管改不改都能跑,问题不大
        0x00000000,    //gs
        0x00000000,    //ldt
        0x20ac0000
    };
 
    printf("tss adress:[%x]\n", &ITss);
    system("pause");
 
    char buff[6];
    *(DWORD*)&buff[4] = 0x91;
 
    __asm
    {
        call fword ptr[buff]
    }
 
        //查看当前的 eip
    DWORD eipp;
    GetEIP();
    __asm
    {
        mov eipp,eax
    }
    //打印输出值
    printf("current eip:%x\n", eipp);
    printf("TSS:[%x]\n",(DWORD)(ITss[0]));
    printf("ESP = %x    CS = %x    SS = %x    Eflgs = %x\n", dwESP,dwCS,dwSS,dwEFLG);
 
    if(dwOK == 1)
    {
        printf("Ring1 success!\n");
    }
    else
    {
        printf("Ring1 failed!\n");
    }
 
    system("pause");
    return 0;
}

总结

好了,完毕,暂时不能在1环中使用 int 3中断,后续搞明白了我再回来补上。
来自一枚菜鸡的总结


[注意] 欢迎加入看雪团队!base上海,招聘CTF安全工程师,将兴趣和工作融合在一起!看雪20年安全圈的口碑,助你快速成长!

收藏
点赞0
打赏
分享
最新回复 (5)
雪    币: 2291
活跃值: 活跃值 (1778)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Mr.hack 活跃值 2021-8-7 20:38
2
0
eflags的nt位表示是否是任务嵌套返回,任务嵌套和非任务嵌套两种状态在执行iret指令时的行为是不一样的
雪    币: 270
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
那年雨夏 活跃值 2021-8-7 21:04
3
0
Mr.hack eflags的nt位表示是否是任务嵌套返回,任务嵌套和非任务嵌套两种状态在执行iret指令时的行为是不一样的
对啊。。我用call far调用,进入1环,产生任务切换,nt为1,通过当前 tr 指向的 tss 中的 ptl 拿取返回地址,应该没啥问题啊。。。。
雪    币: 270
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
那年雨夏 活跃值 2021-8-7 21:07
4
0
Mr.hack eflags的nt位表示是否是任务嵌套返回,任务嵌套和非任务嵌套两种状态在执行iret指令时的行为是不一样的
虽然这时的 nt 本身就为1,所以不用给 nt 置1,大佬我遗漏了啥。。。
雪    币: 2291
活跃值: 活跃值 (1778)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Mr.hack 活跃值 2021-8-7 21:25
5
0
那年雨夏 虽然这时的 nt 本身就为1,所以不用给 nt 置1,大佬我遗漏了啥。。。
我写的这篇帖子应该可以帮到你https://bbs.pediy.com/thread-258279.htm
雪    币: 270
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
那年雨夏 活跃值 2021-8-7 22:01
6
0
Mr.hack 我写的这篇帖子应该可以帮到你https://bbs.pediy.com/thread-258279.htm
多谢
游客
登录 | 注册 方可回帖
返回