本章学到了什么

  1. 调试技巧:在VS中断点调试,查看反汇编代码,step into进行步进调试,运行过程中查看寄存器、内存地址、变量值变化等。
  2. 机器码构造能力:使用C/C++中的直接在C代码里写汇编语言的功能(_asm)。学会了常见的汇编指令,接触了几个带有循环、跳转的汇编语言代码。
  3. 指针机制:对C/C++中的指针机制有了更深的了解。
  4. 函数调用机制:函数调用过程中栈的变化。函数调用约定的大致了解。
  5. 数组模型
  6. 结构体模型
  7. 对齐:搞清楚了为什么对齐这么重要。
  8. switch分析:switch的汇编实现原理。
  9. 加载期重定位:二进制编译和加载过程中的重定位机制究竟是怎么回事。

学习感想

  1. “猜测 - 实证 - 构建”的学习方法。

在学习底层相关的知识时,总会遇到很多问题。那么遇到问题,一定要追根究底,不能马马虎虎就过去了。不能“大概明白了”,而是要真的搞明白。这个具体的过程就是,在学习的过程中,提出问题,猜想这个问题的答案可能是什么,根据自己的猜想去写代码或者调试求证。证实了之后,提炼出这个问题包含的知识点,再构建例子与验证它。这个完整的过程搞明白了,对于一个知识点才算是真正地理解了。

反观我自己之前的学习方法,看资料的时候,往往一带而过,缺乏追根问底的精神,所以感觉一个东西好像搞明白了,其实根本就没有。在平日学习中遇到的问题,很多时候也是上网搜一下知道怎么回事就算了,却没有自己动手做一做,下一次一遇到的时候,还是不会。

  1. 体系化的学习可遇而不可求,要学会零散式地学习。

学习底层知识时,大部分知识点都是零散的,所以我们不应该好高骛远,追求一蹴而就。在学习的过程中,我们会遇到很多实际问题,然后发现自己这也不会那也不会,感觉好像无从下手。我认为,应该就事论事,遇到问题,就去搜索对应的解决方案,发现了一个问题解决一个问题,每次解决一个小的知识点,整个知识网络就在解决这些小知识点的过程中慢慢构建起来了。打个比喻,我们的学习一开始全是漏洞,补都不补过来,但是不要失去信心,每次解决一个小问题,慢慢地零散的知识就结成网了,慢慢变得滴水不漏。

学习笔记

  1. 猜测 - 实证 - 构建
  2. 使用VS2008的反汇编、监视窗口、内存窗口、单步、断点、全局变量赋值的反汇编。
  3. C语言中的指针大小为4,只存放了地址,那么类型信息有什么用呢?类型信息决定了在该地址处理数据的大小,即赋值/读取时写/读多少字节。例如int *p,那么对应汇编指令会使用dword,就是4字节。
  4. 指针强制转换的影响不是发生在转换时(因为地址都是4字节),而是在转换后赋值的时候,访问内存的字节大小。要保证指针强制转换是安全的,必须保证转换后的指针指向的数据类型大小小于原数据类型大小。
  5. 对于一个补码形式的负数求其正数值,就是求反加1。
  6. x86系列CPU的call指令寻址方式为:用与call指令相关的偏移量定位到跳转的地址。
    偏移量计算:偏移量 = 跳转到的地址 - call指令后一条指令的起始地址。
  7. call指令将返回地址保存在内存中,而且ESP寄存器指向了该内存。实际上这块内存就是栈——ESP指向栈的栈顶。每次压栈,栈顶地址变小,即ESP的值变小。
    实际上call指令相当于两条指令的组合:
    push 返回地址
    jmp 函数入口地址
  8. C语言的参数传递是从右往左压栈传递参数。
  9. ESP的存在是为了指明栈顶的位置,那么EBP存在是为了什么呢?为了每一次调用函数时,能够顺利找到压到栈中的参数位置。怎么找?利用一个确定的基址加上偏移。这个基址就是EBP。为了确保EBP的正确,每个函数调用时都要有保存和恢复EBP的过程。
    push ebp

    pop ebp
    在被调用的函数里执行时,EBP用来作为基址获取参数的值。
    在函数内分配局部变量的时候,要使用栈更低地址的空间,也就是继续压栈。
    所以,在使用EBP寻址的函数中,EBP+偏移量就是参数的地址(要回溯寻找),EBP-偏移量就是局部变量的地址。
  10. ret指令将栈顶保存的地址值弹入寄存器EIP,即pop eip。
    编译器必须保证执行ret时,ESP正好指向call指令压栈保存返回地址的那段内存。
  11. 编译器习惯上使用eax作为存储返回值的寄存器,被调用方在ret前设置eax,返回后,调用方从eax获取到该值。
  12. lea eax, [ebp + 10h] 即eax = ebp + 10h
  13. 平衡栈/清栈的两种方式:①调用方清栈,call返回后,执行add esp, x指令。②被调用方清栈:执行ret x指令。前者用空间代价换取了变参功能。
  14. 调用惯例calling convention
    (1)是寄存器还是栈传递参数
    (2)栈传递时,参数是从右往左还是从左往右压栈
    (3)谁来清栈,是调用方还是被调用方
    例如,C语言的调用方式是栈传参,参数从右往左压栈,调用方清栈。
    _stdcall是微软系统调用采用的惯例,除清栈是被调用方外,其他同C语言方式。
    _fastcall是寄存器传递。
  15. 函数指针
    函数指针包括入口地址和函数原型(函数参数表、返回类型、调用惯例)两方面的信息。
    函数指针赋值的原则是:只能将与指针原型匹配的函数的入口地址赋值给它。
    大多数函数指针强制类型转换都会出错,所以不要进行函数指针的强制类型转换。
    函数指针可用于虚函数调用。
  16. 基本数据(如单字节、双字节、四字节整数)存放处的地址必须能被自己数据类型的大小整除。
    对齐的规律:首先选定一个盒子,然后依序将字段往盒子中放,当盒子放不下后,又用下一个盒子存放,直至所有字段都存放完毕。
    盒子长度 = min{max{sizeof(成员变量)}, 对齐长度}
    字段放入盒子的可放置位置如下:
    离盒子头部偏移字节数 = n * sizeof(成员变量)(n=0, 1, 2, …)
    在编程的时候,遇到结构体,要注意是否有对齐问题。
  17. switch不能处理浮点数的原因是,它会将该数映射为数组的索引。
    实际上是一种跳转地址表的方法,计算复杂度不因分支的增加而增加,在大部分情况下比if-else要快。
  18. 在CPU保护模式下,每个执行进程(程序的一个实例)都拥有自己独立的线性地址空间,这种机制叫虚存系统。用户态程序无法直接访问物理内存。
    每个进程都有自己独立的0~4GB的线性地址空间。
    编译的时候就知道全局变量地址。这是因为编译器就能确定所有全局变量相对头部的偏移量,只要程序加载到编译器希望加载的地址,则所有全局变量地址在编译器都可以计算出。
    全局变量地址 = 程序头部加载地址b + 全局变量相对程序头部的偏移量a
    程序中要存储这个希望加载的地址,称为image base(基址)。
    如果不得不加载到一个不是希望加载的地址,那么就要进行重定位(显然这种情况经常出现)。如何确保修改成功呢?只要所有变量与基址的相对位置的偏移量是确定的,那么就没问题了。这样不需要去理解指令类型。
    relocAddr = actualBase + a
    *relocAddr = *relocAddr(重定位前) + actualBase - expectedAddr
    整个程序中有好多偏移量,所以程序中有一个表来存储这些偏移量,称为重定位表。
    Windows中的真实重定位是这样的:为了节省重定位项的空间,不是用4字节表示到程序头部的偏移量,而是将需要重定位的部分划分成一个个区(section)
    总偏移 = 区起始的偏移 + 2字节表示的区中的偏移
  19. 动态链接库中的重定位
    3个重要的API:LoadLibrary,将DLL从硬盘加载到内存;GetProcAddress,接收函数名作为输入,返回该函数入口地址。FreeLibrary,当获得函数地址后,调用此API卸载已加载库。
    在Win7之前,为了达到让所有程序共享同一DLL代码的目的,系统会将所有DLL加载到同一地址,就可以共享代码段进而节省空间。但是系统DLL固定加载基址这个特性,会被Windows下的溢出攻击利用。该个性如果不存在就几乎可以消除Windows下的溢出攻击利用。Windows 7中引入了dll随机加载的选项。(其实就是ASLR)
  20. 利用RTL学习汇编:
    (1)首先用汇编实现开发环境所带的运行时库(Run Time Library, RTL)中的函数,如C语言的RTL包括strlen、strcpy等。
    (2)然后分析系统库实现的这些函数,因为它们调用频繁,所以要求有很高的效率,基本都用汇编撰写。
    (3)再做性能实验,测试自己版本与系统版本的差异,并分析修改(可利用指令级分析工具Vtune)。
    (4)最后分析不同库实现的异同和好坏,如VC、C++ Builder、Delphi、GCC。
  21. 还有一种融汇底层知识的方法:通过编写攻防软件,将操作系统、汇编、编译原理、网络等知识在极为幽微处(如字节层次)贯通起来。

《老码识途:从机器码到框架的系统观逆向修炼之路》- 第1章 - 总结相关推荐

  1. 老码识途:从机器码到框架的系统观逆向修炼之路 pdf电子书

    重要提示尊敬的用户您好,由于老码识途:从机器码到框架的系统观逆向修炼之路pdf书受百度网盘影响无法做公共分享,只能私密分享,有不到之处请多多谅解! 百度网盘链接: http://pan.baidu.c ...

  2. 《老码识途》读书笔记:第一章(上)

    <老码识途>读书笔记:第一章--欲向码途问大道,锵锵bit是吾刀(上)   1.赋值语句 对于全局变量赋值语句,例如下面这句: 1 int gi; 2 void main(int argc ...

  3. 老码识途学习笔记(一)

    第一章 全局变量引发的故事 1 程序存储区 程序存储区一般有下列几段: 程序代码区(SECTION.txt ): 用来存放可执行文件的操作指令(二进制),也就是说是它是可执行程序在内存中的镜像.代码段 ...

  4. 老码识途读书笔记 1

    知识点记录: 1.int 或指针类型的全局变量默认初始化为0,局部变量则为0xcccccccc.(win7 + vs2008 ) 2.内存溢出攻击即使用6个字节空间改变程序执行流程达到某种目的.话说当 ...

  5. 老码识途之对象函数调用

    上一期,我们讨论了普通函数的调用过程,如果没弄明白,看这里 今天所要讲的将是对象调用函数. class C{public:int a;int b;int c;void f(int t){a = t;} ...

  6. 老码识途——在堆中构建mov和jmp指令

    // asmjmp.cpp : 定义控制台应用程序的入口点. // #include <stdio.h> #include <malloc.h>int gi; void * a ...

  7. 老码识途之构造函数和析构函数

    对象初始化过程就是先父类构造函数,再子类构造函数.,那么我们从汇编角度去探索这个过程是怎么样的 class P{public:int a ;P(){a = 1;}~P(){a = 4;} };clas ...

  8. 读书 --- 老码识途

    上周在图书馆借了这本书,这个周末细看了下,是本好书.作者应该是个大学教授叫韩宏.书中讲的很底层,一开始就告诉大家如何debug一段程序,在VS2008里面查看内存.寄存器.反汇编.通过这些来认识汇编. ...

  9. 老码识途1之函数调用和局部变量

    无论在编程中,还是在面试中,都会遇见调用函数这个东东,但是,要是让你说函数是怎么调用的,你能回答上来吗,接下来就让我们一起探索函数如何在汇编层次上实现调用的 在接下来,我们将有几个问题要去解决 函数调 ...

最新文章

  1. MySQL复习资料——用于突击考试总结
  2. mysql+create+table+index_mysql------基础及常见SQL技巧
  3. 参加kaggle比赛
  4. Android ViewPager + Fragment的布局
  5. 遇到IIS configuration error错误的可以看看,不一定是权限问题
  6. 你真的了解 MySQL 数据库的运行状况吗?
  7. OpenJDK8 272在MIPS上的编译修改记录
  8. Android 布局优化之include与merge
  9. RS-274drillIPC-2581三种PCB Gerber文件的观察软件
  10. 用iPhone打造个人的GTD(Get Things Done)实践
  11. 【示波器专题】示波器探头的原理和分类
  12. Linux技术简历项目经验示例(二)
  13. svn图标不显示的解决方案
  14. 计算机单位厘米 像素,像素厘米转换器
  15. HDFS文件访问权限
  16. 中e管家如何投资理财收益最大化
  17. vue 调起浏览器打印
  18. iOS和Android和H5交互WebViewJavascriptBridge
  19. java计算机毕业设计钢材出入库管理系统(附源码、数据库)
  20. 客服回复话术100句

热门文章

  1. 电芯和模组专用高温老化试验箱的特点
  2. 怎样才可以通过Java培训拿到高薪?-粤嵌教育
  3. 将数字类型字符转为中文类型字符
  4. Latex设计表格字体大小
  5. 工业类计算机主板维修,工控机电脑主板坏了如何维修 工业主板故障解决方法...
  6. 计算机技术在建筑的应用浅论,浅析虚拟现实技术在建筑设计领域中的应用
  7. 使用FFMPEG实现音频播放器
  8. 高性能验证码图片生成
  9. 谷歌一雪前耻!全新PaLM 2反超GPT-4,办公全家桶炸裂升级,Bard史诗进化
  10. 【华为机试真题 JAVA】字符串简单数据解压缩-100