本帖最后由 icecut 于 2014-9-29 09:47 编辑
好久没来单片机板块了。最近在深入研究单片机。所以给大家解惑之一就有了。
c语言pc版本程序是可以正常退出的。为什么单片机就需要加一个死循环在程序结束呢?
如果不加又是什么表现呢?
如果加死循环,程序结束就停住了。不加,常规认为会跑飞。但事实上用keil的人都会发现是复位,为什么是复位后续解答。
测试程序如下:
#include <reg51.h>
int main()
{
int x = 5;
int y = 6;
while(x+y != 11);
return 0;
}
这事一个测试单片机是否能正常执行加法的程序。使用keil来编译。debug的时候输出汇编
图片是汇编和c的混合输出。
这里我没有添加startup.s但是还是有一部分代码生成。在程序的下方。然后启动后首先跳转过去执行内存初始化。
这样粉色框显示内存已经全部初始化。并且sp=7,预留了7个0.
此时,执行绿线跳转到main函数开始执行用户代码。最后ret,这时候ret会将sp最初预留的0弹入pc中。于是重新从0开始执行代码
复位就这么产生了。所以很多人遇到没有死循环陷阱的程序会无限复位。
无限复位是编译器加进来的。不同编译器可能不一样。
你可以考虑在用户程序中改写这7个预留的字节,然后debug一下,就能发现crack的魅力。
上面说的有错误,不再改动,大家知道就好。省得大家说我犯错就销毁证据!!!!
经过后面和指点,这个ret的确不需要我说的crack这么麻烦。
[我写程序喜欢return 0,所以占便宜了。没遇到跑飞。]
原因继续解释。
单片机的寄存器 r0-r7是ram统一编址。这里我没有在中断中使用第二组寄存器,所以sp预留了8个地址。原因是sp++,然后存放内容,对应r0-r7.
所以错误ret的时候,正好弹出了返回值的2个寄存器。我返回0就幸运的复位了。如果你返回值随机的话,恭喜你,中招了。如果你返回了一个函数地址,却不需要参数的,恭喜你,给动态**的人留下一个坑。
继续讨论补充:
sp预留的地址为 已使用的统一编址的寄存器+全局变量。
// 本文是看我的一个徒弟在用虚拟机技术去找程序漏洞后,才考虑放出来的。从简单开始搞。给那些没学汇编的人一个汇编不难的印象。
// 当然,对于return 0 :希望大家能多获得规范编程带来的潜意识的好效果。当然用全局变量后,就没有这种效果了。如果全局变量最后2个字节是0,也会有此效果。
最简单的解法还是while(1);
|