learn-tech/专栏/计算机基础实战课/国庆策划03揭秘代码优化操作和栈保护机制.md
2024-10-16 10:18:29 +08:00

57 lines
5.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

因收到Google相关通知网站将会择期关闭。相关通知内容
国庆策划03 揭秘代码优化操作和栈保护机制
你好我是LMOS。
今天是国庆假期策划的第三期。我们来公布第一期主观题的答案。希望你先尝试自己梳理思路,尝试回答问题以后,再来查看参考答案。
第一题
在前面课程里我们一起揭秘了C语言编译器的“搬砖”日常搞清楚了C语言会如何处理各种类型变量、各种运算符、流程控制以及由它们组成的函数并把这些内容加以转换对应到机器指令。你知道在这个转换过程中C编译器为了提高程序的执行性能会有哪些额外的操作吗试试概括一下这些操作
第一题参考答案
存在额外的操作,概括来说是对代码进行优化操作。
为了提高程序的执行性能C语言编译器在经过语义分析的阶段之后会生成平台无关的中间代码然后经历三次不同级别的代码优化。
这里首先要经历中间代码级的代码优化;而后,编译器把中间代码优化的结果作为输入,生成机器相关的目标代码;之后还会再经过一次目标代码级别的代码优化,这个优化策略和具体机器的硬件结构高度相关,且不通用。
完成了整个优化过程后,就会产生最终运行机器平台上的目标代码了。一般通用的优化代码操作具体包括四个方面,我们挨个来看看。
第一类操作是删除多余运算。编译器分析中间代码的时候,可能会发现一些计算操作属于重复计算。因为有些计算并没有让结果发生变化,它们是多余的,完全可以删除。
第二类是代码外提操作,一般用在优化循环代码,可以减少循环中代码的总数。它的原理是这样的:如果循环中的计算结果不改变某个代码段,我们就把这段代码外提,放在循环外。这种变换把计算结果不受循环执行次数影响的表达式,提到了循环的前面,使之只在循环外计算一次。
第三类是强度削弱操作。强度削弱的本质是把强度大的运算换算成强度小的运算。举例来说把加法换成乘法运算强度会更小。比如循环过程每循环一次变量的值增加1又不与循环相关每次总是增加相同的数据。因此可以把循环中计该值的加法运算变换成在循环前进行一次乘法运算。
最后一类操作是合并已知量和复写传播。有时很多运算结果都是编码时已知的,所以在代码编译时就可以计算出它们的值,我们把这种变换称为合并已知量。
还有多个变量之间的互相引用比如变量A被变量B引用而变量B又被变量C引用如果A与C之间没有能够改变B的代码就直接让C引用A这种变换称为复写传播。
第二题
在[堆与栈的区别和应用]这节课中我们知道了堆与栈区别。同时我们也清楚了C语言的函数的局部变量和返回地址都保存在栈中如果有人对这栈中数据破坏就会导致安全隐患例如改写返回地址使之指向别的恶意程序。那问题来了请问我们有什么栈保护机制么可以用你的语言描述一下么
第二题参考答案
栈保护机制有很多,我给你分享比较典型的几种。
首先是由编译器在编译程序时稍微做个检查看看是否存在栈内缓冲区溢出的错误。程序代码中采用大量的字符串或者内存操作的函数比较适合做这样的检查。通过给gcc加上 -D_FORTIFY_SOURCE=1或者2时在编译或者代码运行时通过判断数组大小来替换strcpy、memcpy、memset等函数名将它们替换成编译器中带有检查代码的函数从而防止缓冲区溢出。
通过操作系统对页表的NX位进行设置这种方法也很常见。NX即No-eXecute意思是不可执行。带NX位的页表所指向的内存中的数据是不可执行的当程序溢出成功转入恶意代码时程序会尝试在数据页面上执行指令此时CPU就会抛出异常不去执行恶意代码主要防止恶意代码在数据区溢出。
还有一种简称为ASLR的方法即地址空间分布随机化。内存空间地址随机化机制可以将进程的mmap基地址、heap基地址、栈基地址、共享库基地址随机化。这样能有效阻止攻击者在堆、栈上运行恶意代码。
最后还有栈溢出保护canary这是一种由编译器支持的技术。在Linux中将cookie信息称为canary。攻击者在覆盖返回地址的时候往往也会将cookie信息给覆盖掉导致栈保护检查失败。
而canary技术的大致思路是这样的当启用栈溢出保护后编译器会插入相关代码在函数开始执行的时候就会向栈里写入cookie信息。当函数真正返回的时候就会通过编译器插入的代码来验证cookie信息是否合法。如果不合法程序就会停止运行这样就能阻止恶意攻击代码的执行。
通过这两道题目,我们又补充了代码优化和栈保护机制的知识。接下来,我们继续回到课程主线的学习,期待你把精神状态拉满,之后学有所成!