lesson006 汇编 可执行代码
汇编
汇编 约等于 机器码
源代码 进化 到 机器码 引申出编译器
什么是编译器?
编译器也是一个软件,以后就不强调汇编和机器码的区别的,以后统一用汇编代替机器码
编译器具有翻译的功能
举例 printf(); 它要与显卡交互
上节课我们有讲,操作系统已经帮我们省略了与显卡的交互,那么,操作系统与显卡之间的交互我们是不知道的。硬件部分的代码我们是没有的。这部分是操作系统提供的。每一个操作系统是不一样的,如果想要操作系统支持,那你就必须link,首先第一步产生一个OBJ文件,obj汇编是没有任何意义的,实际上是程序产生的汇编,但不能执行,因为不符合操作系统的要求,那么我们就使其符合操作系统的要求,这个步骤就叫link
Link
源码-->编译(生成obj)-->链接(根据不同的平台)——>可执行!
windows下面会加上PE结构 ,加载一些dll,加载函数等……程序执行!
编译的时候,有可能会生成不一样的代码?
因为编译器会进行一些优化
汇编代码由几大部分组成
1 赋值语句
2 跳转语句
3 计算语句
编译器就是将复杂逻辑编译成简单逻辑
这两个版本汇编代码不一样,说明了什么呢?说明编译器会进行一些优化。
所以,大家在作调试的时候,都用debug
.
临时的变量是否占用内存空间呢?肯定的,会占用内存空间的。程序里面的任何东西都要占用内存。他所占用的内存我们叫他栈
为了更好的来组织生成程序,生成的空间,我们将整体的将内存分为几块,一块“栈”一块“堆”一块“代码”一块“常量”
这样子的好处,在于更加安全
在CPU和内存的角度来看,所有的东西都是0和1,如果我是一款能操作内存的语言,就可能不小心修改了不允许改的内存,就出现比较严重的错误
那么,我们可以划分安全的保护区,只读不写的部分内存,我们可以把代码区和常量的区域内存设为保护区,不可写。
栈区
一开始开辟出1024KB大小
栈区是可以增长的。但也不是无限的:举例如下图:
栈用来做什么呢,用来存储临时变量
为什么存储临时变量,因为临时内存要占用空间,而且只是临时的,要准备释放它,如果每一次都准备了空间,还要释放,比较浪费内存
那么,栈的作用就突显了
栈有一个游标:ebp来记录开始 用另外一个游标 esp来记录这段空间的最高点。
如果我们用完了临时变量,就可以把esp和ebp重合,把这段空间作废了。就相当于我们把临时变量的空间释放掉了。
栈不仅仅存储临时变量,它还会存储什么东西呢?
跳转后返回地址
什么是跳转后返回地址呢?
程序每次调用了函数后,如API
按F10 下一步 按F11 继续看 (中间mark操作有误,出来了下,重编译了程序,内存地址变下图的041316h了。)
这个地址变了
跳转到另外一条地址里面去了。再来 F11 它又跳转了
栈是可回溯的,回溯能力非常的强
栈还能做什么呢?我们还可以使用栈传递参数
为什么ebp要上移呢?
我们把ebp到esp之间叫做一个函数的栈,ebp不上移可不可以呢,可以不上移,但不可以不知道栈底,因为每个函数里面都有临时变量,不知道栈底,那么这个函数的临时变量怎么清理呢?
再回来 vs2015 我们来验证函数与栈
什么是压栈?
出栈的动作又是什么呢?
push 压栈 pop 出栈 重要的考点
题外话题
破解玩的是 标志寄存器
标志寄存器 加上 程序计数寄存器,来决定程序的进行流程。
根据里面的结果来控制,结果大于0执行哪,小于0又执行哪,等于0又执行哪里。
我们的程序如果要验证,多是if……else来进行,所以把标志寄存器学好了,破解就so easy了。
我们来跟踪这个简单程序内存地址与寄存器之间的流程
ESP 往上走了一位,减去了一个4,往里面放了一个值:306B0400 反过来就是00046B30
push 进来一个值,就是已经压了一个栈进来。接着会发生一个 call
来看什么时间出栈
每一次pop相当于我们取东西 EDI ESI 被改变
一步步的退回去,至此全部还原
堆就放在指针里面讲。
作业:
1 写一个 i love mark 学教程里面的例子,一步步记录内存,寄存器变化,最后得出一个结论。