首页
社区
课程
招聘
[原创]猜测性说明:windows第二版的栈展开
发表于: 2025-2-10 08:52 1361

[原创]猜测性说明:windows第二版的栈展开

2025-2-10 08:52
1361

本篇文章介绍对于windows第二版的栈展开,我个人对于其中用到的数据结构和它的工作流程的推测

windows第一版的栈展开,在windows的官网有介绍:
07fK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6D9k6h3q4J5L8W2)9J5k6h3#2A6j5%4u0G2M7$3!0X3N6q4)9J5k6h3y4G2L8g2)9J5c8Y4A6Z5i4K6u0V1j5$3&6Q4x3V1k6U0M7s2m8Q4x3V1k6T1N6h3W2D9k6q4)9J5c8X3g2^5j5$3g2H3N6r3W2G2L8W2)9J5k6r3S2S2L8X3c8D9K9h3&6Y4i4K6u0V1P5o6j5@1i4K6y4r3N6X3W2W2N6#2)9K6c8r3#2K6N6X3y4Q4x3X3b7I4y4U0l9`.

关于第二版的栈展开,我找到的资料不是很多。对其做的一些研究,在此记录一下。

第二版是指,当UNWIND_INFO的version字段等于2时。

第二版相比于第一版,在展开时,需要更少的检查/判断就可以知道是否在epilog中,并且当展开一个PC处于epilog范围内的函数时,也无需再检测PC后边的汇编指令。而为了达成这些目的,第二版在数据结构上添加了更多的数据,对编译器生成的代码,也做了更严格的要求。

在数据结构上,第二版新加了一个操作码,专门用于表示epilog的大小和位置。也就是说,对于第二版而言,一个函数中,所有的epilog,都已在数据结构中清晰的标明出来了,只要简单的判断一下,当前的PC是否在其范围内就可以了。而不用像第一版那样,需要试探执行来判断。关于新加的操作码,我们接下来再说。

对编译器的要求上,有两个改变,一个是操作码的顺序,一个是对汇编代码的要求。在操作码顺序上,相比于第一版中比较自由的排列,第二版中所有的push要在表的最后(详细参见下面)。在对汇编代码的要求上,在epilog中,归还栈的操作只能出现一条(比如,add rsp,0x10)。 而pop和归还栈操作中间的位置,就是要写在数据结构中的epilog的大小。这样要求的原因,是因为,save操作即使多次进行也不影响最终结果,第二版让所有的save操作在栈归还之前来完成,栈归还只有一条。执行完栈归还呢,就帮忙把剩下的pop模拟执行了;如果没执行栈归还呢,就把所有的操作码都执行了,(这时无非是多几次save操作,这是不影响结果的),再归还栈,pop。

接下来说下具体的技术细节,新加的操作码,网上的给它起的名字是UWOP_EPILOG,值为6。当函数块(RUNTIME_FUNCTION)有epilog时,需要添加UWOP_EPILOG。第一个UWOP_EPILOG表示epilog的大小(函数中所有epilog的大小都需要相同,这是对编译器的要求),之后的UWOP_EPILOG依次表示本函数块中每个epilog的位置,此位置通过与函数块结尾的距离表示。如果设置了标志(具体参见下面),第一个UWOP_EPILOG也会参与表示epilog的位置,此时每个UWOP_EPILOG表示一个epilog。表示位置的UWOP_EPILOG是按epilog位置有序存放的。当UWOP_EPILOG的个数为奇数时,需要添加一个空的UWOP_EPILOG来进行对齐。空的UWOP_EPILOG里,除了UnwindOp字段为UWOP_EPILOG,其余字段均为0。

第二版对于操作码的顺序,如下表所示:
UWOP_EPILOG(多个或0个,在最前面)
UWOP_其他类型(多个或0个,可以为UWOP_ALLOC_SMALL)
UWOP_PUSH_NONVOL(多个或0个)
UWOP_ALLOC_SMALL(1个或0个,大小固定为8字节,用于模拟popfq)
UWOP_PUSH_MACHFRAME(1个或0个,在最后面)

第一个UWOP_EPILOG的OpInfo字段,有两个取值,1或0。如果为1,则表示第一个UWOP_EPILOG也参与epilog位置的指定,第一个epilog的位置由CodeOffset字段来指定。

第二个以及之后的UWOP_EPILOG,他们的OpInfo字段做为高4位,和CodeOffset字段共同组成一个12位的偏移。

指定epilog位置的偏移,是相对于RUNTIME_FUNCTION.EndAddress(也就是函数块结束位置)的向上偏移,指明了epilog的起始位置

接下来说一下展开过程。和第一版一样,也是先判断是否在epilog里。如果在epilog里:
遍历链,找到表中第一个push操作,然后开始往后遍历,边遍历边数PC走过了多少个字节。此字节数,是从epilog的起始位置算起的,所以可以用来查找真实PC执行到了哪里。当模拟执行的PC到达真实PC之后,开始执行模拟的pop操作。执行完所有表中的pop后,同理执行UWOP_ALLOC_SMALL和UWOP_PUSH_MACHFRAME。都执行完后,模拟ret指令。
如果不在epilog里:
遍历链,跳过每个节点中,遇到的UWOP_EPILOG,执行其他的操作码。像第一版一样,如果在prolog中,也要跳过还未执行的指令,如何跳过的算法和第一版相同。

另外,链中的每个函数块,他们的版本要一样,或者都为1,或者都为2。

参考资料:
reactos中RtlVirtualUnwind函数的实现,里面有对于第一版处理流程的代码:819K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6V1L8%4S2&6k6$3g2F1i4K6u0W2M7X3g2S2j5%4c8G2M7#2)9J5k6h3!0J5k6#2)9J5c8X3b7^5i4K6u0r3k6o6u0X3i4K6u0r3N6h3&6%4K9h3&6V1i4K6g2X3z5r3y4Q4x3X3g2Z5N6r3#2D9i4K6t1K6j5e0l9K6j5K6V1I4j5U0k6U0y4o6x3%4x3o6j5$3x3U0M7J5k6h3u0U0x3X3x3J5k6X3k6X3x3o6f1I4j5e0c8U0


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 1
支持
分享
最新回复 (1)
雪    币: 803
活跃值: (2899)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
5f6K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6V1L8%4c8F1k6i4c8Q4x3V1k6U0L8%4u0W2j5$3I4J5i4K6u0r3j5X3I4G2j5W2)9J5c8X3#2S2M7%4c8W2M7W2)9J5c8Y4y4J5j5#2)9J5c8Y4g2F1N6$3W2F1k6r3g2J5i4K6u0r3j5h3#2V1y4U0c8Q4x3V1k6#2L8Y4N6A6L8X3c8W2M7W2)9#2k6X3q4E0k6o6j5@1i4K6u0W2j5%4m8H3
直接看代码
2025-2-10 10:22
0
游客
登录 | 注册 方可回帖
返回