调用门就是提权的通道
低32位0-15是门的低两字节的偏移,高32位15-31是门高两字节的偏移。
低32位16-31是段选择子。只能是代码段。
S位为0,调用门一定为代码段。
高32位的0-4位 表示操作数,这个位置有多大表示会往堆栈里压多少个参数。
执行流程
指令格式:CALL CS:EIP
执行步骤:1.根据CS的值 查GDT表 找到对应的段描述符 S位必须为0 Type位为1100
2.根据段描述符中的 段大小和基地址找到执行的代码
构造过程
简单写出调用的函数
#include "stdafx.h"
#include <Windows.h>
void __declspec(naked) test(){
__asm{
//int 3;
retf;
}
}
int main(int argc, char* argv[])
{
char buf[6] ={0,0,0,0,0x4b,0};//构造段选择符 EIP可以置0 因为等下会直接跳到那个段里面去执行
printf("test:%x\n",test);
__asm{
call fword ptr buf;
}
system("pause");
return 0;
}
构造段选择符 0040ec00`00081000
然后int3 断下来后 观察栈,发现栈里面依次压入了返回地址,CS的值,之前堆栈的地址和SS的值。此时观察上面的CS的值已经变成了8。
可以试着调用一下R0的函数来测试下
#include "stdafx.h"
#include <Windows.h>
typedef int (_cdecl *DbgPrintProc)(char const * const, ...);
DbgPrintProc DbgPrint = (DbgPrintProc)0x83e6241f;//在windbg中用uf nt!DbgPrint获取到函数的地址
char* strs = {"------\n"};
void __declspec(naked) test(){
__asm{
//int 3;
pushad;
pushfd;
push fs;//保存KPCR的地址,不保存的话会蓝屏
mov ax,0x30;
mov fs,ax;
mov eax,[strs];
push eax;//把要输的东西传入函数
call DbgPrint;
add esp,4;
pop fs;
popfd;
popad;//还原现场
retf;
}
}
int main(int argc, char* argv[])
{
char buf[6] ={0,0,0,0,0x4b,0};
printf("test:%x\n",test);
__asm{
//int 3;
call fword ptr buf;
}
system("pause");
return 0;
}
构造一个带有参数的调用门 0040ec01`00081000
#include "stdafx.h"
#include <Windows.h>
void __declspec(naked) test(){
__asm{
//int 3;
iretd;//要用iretd,用retf会蓝屏
}
}
int main(int argc, char* argv[])
{
char buf[6] ={0,0,0,0,0x4b,0};
printf("test:%x\n",test);
__asm{
//int 3;
push 0x1234;
call fword ptr buf;
add esp,4;
}
system("pause");
return 0;
}
TIPS
1.当通过门 权限不变时 只会PUSH两个值:CS 返回地址 新的CS由调用门决定
2.当通过门,权限改变的时候,会PUSH四个值:SS ESP CS 返回地址,新的CS由调用门决定,新的SS和ESP由TSS提供。
3.通过门调用是要执行哪行代码由调用门决定,但使用RETF返回时,由堆栈中压入的值决定,进入门时只能按照指定按照调用门的值跳转,出门是则可以通过修改堆栈中的值实现随意跳转。
4.retf 来返回 返回地址、cs、esp、ss,iretd来返回 返回地址、cs、eflag、esp、ss,iretd 会先检测有无任务嵌套,用TSS 段中的pBlink替代寄存器,然后确认NT位为0.,再查堆栈返回。