进入保护模式后,可以利iret 设置eflags寄存器中VM位 从而进入V86 mode。返回保护模式是用sysenter指令返回。
注意事项:
1、V86 mode是运行在CPL 3特权下的,操作代码是16位;
2、分页机制有保护标志;
3、需先初始化 GDT、IDT;
4、使用iret指令前要将eglags中的IOPL设置为3,这样执行iret、sysenter指令不会报错;
5、寻方式为 段:偏移,1M范围内;
6、IOPL值需手动恢复
7、待续
void bios_intx(BYTE number,PCONTEXT cont)
{
DWORD stackAddr;
WORD seg=*(WORD*)(number*4);
WORD ip=*(WORD*)(number*4+2);
*(DWORD*)(0x7C00-4)=0x0202; //中断返回iret用到的eflags
*(WORD*)(0x7C00-6)=0x0000; //cs
*(WORD*)(0x7C00-8)=0x7C05; //eip
*(BYTE*)(0x7C00)=0xEA; //根据中断号跳转中断入口地址
*(WORD*)(0x7C01)=*(WORD*)(number*4); //偏移
*(WORD*)(0x7C03)=*(WORD*)(number*4+2);//段
*(WORD*)(0x7C05)=0x340f;//sysenter 0x340f
*(WORD*)(0x7C09)=0x90;//
asm("mov %%esp,%0 \n"
:"=m"(stackAddr)
:);
//根据后面保存数据使用栈的数量计算
stackAddr-=24;
//sysenter用到的数据
wrmsr(IA32_SYSENTER_CS,(0x10),0); //
wrmsr(IA32_SYSENTER_ESP,stackAddr,0);
wrmsr(IA32_SYSENTER_EIP,&&_THIS_IP_,0);
__asm__(
"pushl %%ebp\n" // |—————|
"pushfl \n" // | 合计需保存
"pushl %%gs \n" // | 6个寄存器
"pushl %%fs\n" // | 此处保存的数量
"pushl %%es \n" // | 影响stackAddr的值
"pushl %%ds\n" // |—————|
"pushl $0x0 \n" //gs |—————|
"pushl $0x0 \n" //fss |
"pushl $0x0 \n" //es | 6个段寄存器
"pushl $0x0 \n" //ds | +
"pushl $0x0 \n" //ss | esp+eip+eflags
"pushl $(0x7C00-8) \n" //esp | 都是用于iret 进入v86 mode 的
"pushl $0x23202 \n" //eflags | 标志,第17位vm ;第9位中断 ;第13~14位 iopl=3
"pushl $0 \n" //cs |
"pushl $0x7C00 \n" //压入eip |—————|
"movl %0,%%eax\n" // |—————|
"movl 0x9C(%%eax),%%edi\n" // |为中断传递
"movl 0xA0(%%eax),%%esi\n" // |的数据
"movl 0xA4(%%eax),%%ebx\n" // |寄存器
"movl 0xA8(%%eax),%%edx\n" // |
"movl 0xAC(%%eax),%%ecx\n" // |
"movl 0xB0(%%eax),%%eax\n" // |—————|
"iret \n"
:
:"m"(cont)
);
//:"m"(cont->Eax),"m"(cont->Ebx),"m"(cont->Ecx),"m"(cont->Edx),"m"(cont->Edi),"m"(cont->Esi)
_THIS_IP_:
asm(
"pushf\n" // |---------------
"pushal\n" // |eflags
"pushl %%ds \n" // |8个通用寄存器
"pushl %%es\n" // |4个段寄存器
"pushl %%fs\n" // |共13个全部保存
"pushl %%gs \n" // |-------------
"add $52,%%esp\n" // 13*4
"popl %%ds \n" //ds |------------
"popl %%es\n" //es |
"popl %%fs\n" //fs |共六个寄器
"popl %%gs \n" //gs |
"popf \n" //eflags |
"popl %%ebp\n"// |-------------
:
:);
memcpy(&cont->SegGs,(void*)(stackAddr-52),52);
cont->EFlag=*(DWORD*)(stackAddr-4);
//memcpy(&cont->Edi,(stackAddr+),4);
//stackAddr
return;
}
void bios_int_10h()
{
CONTEXT cont;
memset(&cont,0,sizeof(CONTEXT));
cont.Eax=0x4f0a;
cont.Ebx=0;
bios_intx(0x10,&cont);
//es:di为返回数据
print_farmat_msg("int 10 %x,%x \n",cont.SegEs<<4,cont.Edi);
}