数据段代码段权限规则

首先认识一下裸函数,可以看见,当函数中什么也没写编译器也仍然会进行栈的初始化操作,而如果是裸函数,是不会有这些初始化操作的。

image

 

image

 

不过裸函数中需要至少一个ret返回,不然当程序进行到这里会直接异常结束,因为这里并没有指令。

1.段权限规则 代码段与数据段 堆栈段

image

 

段是硬件资源的划分

如何判断运行在3环下还是0环下呢?

要看cs段与ss段,只有两个同时处在0环下才是内核环境,但由于cs段与ss段的权限总是一致的,所以一般看cs就可以

image

 

DPL:descriptor privilege level

CPL:current......(cs段描述符的dpl)

RPL:request......(cs ds ss后面两位)

image

 

举例:当ds=23  rpl=3,那就是一个三环的请求权限

如果是20,就是一个0环的请求来访问,但接不接受不一定。

普通的数据段下,是可以随意给的

我们来进行实验:

image

 

image

 

可以发现这种情况下(rpl为0环,而dpl为3环)是可以正常运行的

那如果rpl与dpl都是0环呢?

image

image

 

可以发现这里直接报错了,因为本来cs是三环的,而ds想提权到0环,能否提权要看cs,如果在三环下跑代码是不能提权到0环下的

因此普通数据段下rpl是没有意义的,那么ss段呢?我们继续进行试验

image

 

将dpl改回三环

image

 

image

 

这里可以看到若ss的rpl是0环,但dpl与cpl是3环的,这里出错了。而当ss的rpl与dpl都是3环的,仍然可以正常运行。因此cs的rpl是不能随意赋值的,是有意义的。

堆栈段rpl==cpl==dpl才能更改

那么在代码段下呢?

image

 

这里发现48被修改成4b为三环,因此,在代码段上rpl也是没有用的,cpl==dpl

2.跨段跳转不提权 jmp和call

image

 

这种写法是不支持的,如果要实现对于cs的修改要间接jmp如:

image

 

但由于我们是jmp到test函数的,那该怎样回来呢?如果要用ret的话需要我们push构建

我们可以选择标签:

编译器在编译时,记下标签这个位置的内存地址,名字就叫heihei,等到链接完成,它就变成一个实实在在的地址值。采用这种方式就可以成功ret回来,但这种方式会有一种瑕疵,因为段选择子并没有被改回来

image

 

image

 

image

 

可以看到以call far的形式调用esp减少了8字节,但一般的call一般只会减少4字节的地址大小。我们查看一下堆栈中的信息

image

 

可以看到这里除了返回地址还压栈了cs原本的值1b

这种情况下ret是否能返回呢?

Ret的原理是从原有的堆栈里取出eip,并且esp-4,所以在这种情况下是可以ret的,但堆栈会不平衡,因为还有4字节的1b在堆栈中。单纯的ret4虽然可以正常运行,但修改的cs也并没有被改回去,这里就要用到retf

image

 

Retf专门应对这种call far,并且cs的值也会被还原

image

 

作业:在jmp far bufcode的情况下从test回到main且将cs改回去。

image

 

这里主要的修改是在push heihei前push了一个cs,这样就可以直接在堆栈构造出一个类似于bufcode的间接跳转,这样直接在test中jmp far [esp]即可,由于vs8并不支持这种写法所以采用了机器码的方式。下图为堆栈和结果

image

 

image

 

代码:

void __declspec(naked) test(){

    __asm{

        //jmp far [esp]

        _emit 0FFh

        _emit 02Ch

        _emit 024h

    }

}

 

int main(){

    char bufcode[6]={0, 0, 0, 0, 0x48, 0};

    *(int*)&bufcode[0]=(int)test;

    __asm{

        push cs

        push heihei

        jmp far bufcode

heihei:

        add esp,0x6

    }

}

 

 

posted @ 2026-11-03 18:11  面面mianmian  阅读(2)  评论(0)    收藏  举报
Live2D