调用门-中断门拾遗

个人备忘,一些总结。中断门和调用门的细节不赘述,网上很多。

调用门,用 iretd 返回。来深度理解调用门(正常应该 retf 返回)

void __declspec(naked) Dym()
{
    __asm
    {
        int 3;
        //retf; // 正常应该用 retf 返回
        mov eax, 0x11112222;
       	mov ebx, 0x22222222;
        mov ecx, 0x33333333;
        mov edx, 0x44444444;
        iretd;
    }
}

struct CVal
{
	long val;
	short seg;
};
int _tmain(int argc, _TCHAR* argv[])
{
	printf("Dym : %p\n", Dym);

	if (1)
	{	// 调用门
		// 去GDT 表将 48 位置改成 0040ec00 00081030
		// 以实际的 NoArg 地址为准
		CVal _vv= {0x0,0x48};
		__asm
		{
			pushfd;
			call fword ptr[_vv];
			add esp, 4;
		}
		printf("调用门 iretd  顺利返回了\n");
	}

	getchar();
	return 0;
}

执行上述代码,断点设置在 if (1)。查看 Dym 函数的地址
在这里插入图片描述
Windbg 双击调试环境下(配环境不赘述)。暂停 windbg,输入(可以复制页面内容,修改)
在这里插入图片描述
其中 48 就是上面代码中写的 48.不固定。自己可以改。匹配就行了。
调用门描述符一共 8字节共64位:00f0ec01`00081000
其中 00f01000 是 Dym 的函数地址。 0008 是代码段选择子,也就是
在这里插入图片描述
在这里插入图片描述
ec01,01 是参数个数,有关参见调用门细节,简单贴图如下:
在这里插入图片描述
ec 是 P DPL 0 type 位的值:P= 1, DPL = 11, type = 1100(可参见本人的总结

代码中有 pushfd ,所以是一个参数。执行调用门之后会在windbg 断点,在如下代码出:
在这里插入图片描述
Windbg 图如下:
在这里插入图片描述
上面说过,函数地址是 f01000 这里也得到验证。在内存中查看 esp 附近的栈值
在这里插入图片描述
连续五个值,分别是 :
返回地址 EIP
调用者 CS
EFLAGS
调用者 ESP
调用者 SS
参考:长调用与端调用
正常情况下调用门在提权的情况下没有 eflags 。我们是通过 pushfd 参数,将 eflags 作为参数压入的。
这也说明调用门压参数的情况下,参数是放在中间的,如下:
返回地址 EIP
调用者 CS
参数…
调用者 ESP
调用者 SS
由于上述栈内容和中断门的内容一致(原因下面讲),所以可以直接用 iretd 返回了。但是由于保存的esp 的愿因,会导致站空间还保存了 eflags ( 3 环)不正确,毕竟我们多push 了一个 eflags。通过额外的 add esp, 4 来平衡堆栈(显然可以用 popfd 实现)。
由于有 int 3 。会在调用门退出来的时候报异常,去掉则正常了。

下面贴一个完整流程截图:
call 之前暂停,查看相关寄存器值 esp eip cs ss eflag
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
总结,可以看到进入调用门函数之后,(0环)栈里面顺序保存了 eip cs eflag esp ss。值和进入调用门之前的(3环)完全一致。其中 eip 进入之前是 F01076,调用门里面保存的是 F01079。
退出来之后,断点在 add esp, 4。这时候看栈里面还压入了 eflag 的值,这个需要退出来,所以 add esp, 4。完成堆栈平衡。当然也可以通过 popfd 实现。
进入反汇编查看:
在这里插入图片描述
发现 add esp,4 这句指令地址就是 F01079 。这也就是在 0 环里面保存的 eip。完美解释!下面通过对中断门的描述,理解这里为什么要 pushfd。

中断门中用 retf 返回(正常应该用 iretd ).
先上代码

void __declspec(naked) Zdm()
{
	__asm
	{
		int 3;
		//iretd;	//  正常就是 iretd 返回, 16  位用 iret
		mov eax,dword ptr ds:[esp+0x8]  //取ELFAGE
		push eax;
		popfd //修改ELFAGE寄存器

		//重组堆栈,按照调用门堆栈的格式,这样就可以使用RETF返回
		mov eax,dword ptr ds:[esp+0x0c] //esp
		mov edx,dword ptr ds:[esp+0x010] //ss
		mov dword ptr ds:[esp+0x8],eax
		mov dword ptr ds:[esp+0xc],edx
		RETF
	}
}

int _tmain(int argc, _TCHAR* argv[])
{
	printf("Zdm : %p\n", Zdm);
	if(1)
	{	// 中断门
		__asm
		{
			int 32;
		}
		printf("中断门用 retf  顺利返回了\n");
	}
	getchar();
	return 0;
}

先在 int32 下断点,停住查看 Zdm 函数地址(注意这个地址是错误的,因为当时代码忘记了写 int 3,后面加上 int 3 的时候,函数地址忘记截图了,真实地址是下面用到的 1051010):
在这里插入图片描述
然后 windbg 暂停。输入
在这里插入图片描述

在这里插入图片描述

这个地址刚好是 索引 32(0开始)。当然也可以选其他地址,修改代码中的索引就可以了 比如:
int 33。 地址选 …508。
修改了 idt 表后 windbg 按 f5 继续执行。
截图保存查看寄存器值:
在这里插入图片描述

单步执行,断点,停在 windbg:
在这里插入图片描述
地址正是上面的 1051010 。查看 esp 相关值:
在这里插入图片描述
罗列数据如下:

0环三环
EIP 0105108EEIP 01051090
CS 001BCS 001B
EFLAG 0202EFLAG 0202
ESP 17F93CESP 17F93C
SS 0023SS 0023

可以看到数据一致。EIP除外,三环存的EIP 是0环返回后需要执行的指令地址。
上述顺序分别是
低地址
EIP
CS
EFLAGS
ESP
SS
高地址
这比调用门在中间多了一个 EFLAGS。这也正是上面调用门通过 pushfd 将参数压到中间,最后可以通过 iretd (中断门返回指令)从调用门返回的原因。
同理,这里我们如果要用 retf (调用门返回指令)从中断门返回,也需要模拟调用门的堆栈环境:
EIP
CS
ESP
SS
也就是 将 esp ss 两个值分别覆盖到 eflags esp。再看代码:

void __declspec(naked) Zdm()
{
	__asm
	{
		int 3;
		//iretd;
		mov eax,dword ptr ds:[esp+0x8]  //取ELFAGE
		push eax;
		popfd //修改ELFAGE寄存器

		//重组堆栈,按照调用门堆栈的格式,这样就可以使用RETF返回
		mov eax,dword ptr ds:[esp+0x0c] //esp
		mov edx,dword ptr ds:[esp+0x010] //ss
		mov dword ptr ds:[esp+0x8],eax
		mov dword ptr ds:[esp+0xc],edx
		RETF
	}
}

正是做了这样的操作。其中将 eflags 存到了 eflags。
同样由于有 int 3 的存在执行会报异常。去掉就正常了。

OVER。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值