前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何半天学会一门汇编

如何半天学会一门汇编

作者头像
血狼debugeeker
发布2021-03-02 10:50:52
8200
发布2021-03-02 10:50:52
举报
文章被收录于专栏:debugeeker的专栏

本文讲述如何用半天时间学会一门汇编的诀窍。在学习汇编过程,最好用Visual Studio调试,打开汇编模式,把栈视图和寄存器视图都打开。函数调用使用cdecl,在调试过程中使用汇编单步。

很多程序员都觉得汇编是可怕的编程语言,感觉很难学,繁多的指令,各种寄存器,寻址方式和CPU机制紧密相关,一切都让人望而却步。其实,汇编相对众多编程语言来说,是一门非常简单的语言:它没有奇技淫巧式的语法,也没有各种全家桶式的框架。它之所以显得非常难掌握的原因:

  1. 它解决的问题,离程序员平时面临的问题太远。
  2. 目前很多编程语言书籍和资料都是集中该语言本身,很少会和其它语言横向对比和建立联系。讲C语言就是讲C语言,讲C++也只是讲C++,讲汇编也是只是讲汇编。至于C/C++和汇编之间的对比和联系呢?没有。

但在现实生活中,还是有不少地方用到汇编语言,除了搞嵌入式之外,在C/C++,OC之类的语言,在定位程序崩溃,内存泄露,逆向破解,漏洞挖掘和分析,恶意软件分析,都会用到。

所以,还是需要学一下汇编的。如何学呢?重要是把它和程序员平时面临的问题和熟识的语言建立一种联系。这和学数理化差不多,数理化学得好的人,基本上都会把抽象思维和现实世界建立某种联系。

在所有的编程语言中,这三样东西基本上是不可或缺的:

  1. 函数
  2. 程序执行顺序
  3. 数据结构

所以,重要是建立这三样东西在高级编程语言C/C++和汇编的对应关系。对于什么指令,寄存器,寻址方式和CPU机制,就先不管。

函数


在高级编程语言里,函数的参数传递是通过变量或数值,返回值是通过变量或数值。那么在汇编里呢?在汇编里,参数传递和返回结果叫做调用约定

调用约定传递参数和返回值,是通过寄存器和栈来完成的。那么,就要看传递参数用到什么寄存器,返回值用到什么样的寄存器。由于寄存器数量有限,就演变成这些问题:

  1. 第一二三四五...这些参数分别用哪些寄存器来传递?有没有个数限制,超过了限制,参数又如何传递?
  2. 返回值通过哪个寄存器传递?
  3. 如果通过栈来传递,标志栈的是哪个寄存器?
  4. 在C++的情况下,成员函数的参数传递又是如何?
  5. 当前函数桢用哪个寄存器表示?
  6. 函数执行完,如何返回调用者?

x86下,在cdecl调用方式,基本上,参数都是通过栈来传递,返回值是通过eax传递,栈是由esp来控制,而this指针是通过ecx(windows下)或栈(Linux)。函数桢用ebp指向。返回地址在栈上。

mips下,参数都是通过a0-a7传递的,多余的则放在栈上,通过sp来指向,而返回值往往一般只通过v0返回。this指针一般作为第一个参数用a0传递。函数桢用fp指向。返回地址放在ra

sparc下,则会比较奇怪。传递时是通过o0-o6来传递,但在函数执行时则从i0-i6来取,当然超过是在放在栈上。而返回值则通过i0传递,调用者则从o0来取。栈是通过sp指向。函数桢用fp指向,返回地址在i7

iOS下,参数是通过x0-x3传递,返回值也是通过x0。由于没有进行调试,只是在IDA进行逆向,所以其它不清楚。

这上面是我曾经搞过的CPU平台,其中x86sparc是08-10年时,mips是11年-12年接触的。iOS是在2020年搞了一天,只是为了看看jailbreak反检测机制。

从上面来看,可以需要了解的寄存器少了很多,而且需要了解的寄存器都是有关联的。而且这些知识点可以这样掌握:

  1. 编写没有参数和返回值的函数,只是进行简单的1+1的操作。了解一下编译器会生成哪些汇编
  2. 编写没有参数有返回值的函数,return 1+1的操作,了解返回值是放在哪个寄存器的。
  3. 编写有参数有返回值的函数,了解一下参数是如何传递,并且把参数的个数不断增加,看看传递改变。
  4. 编写一个类和一个成员函数,看看this指针如何传递。

本人的coredump系列第三章就是这个思路。详情请见开发目录

程序执行顺序


无论高级语言是怎样的,它编译成二进制文件后,它的执行逻辑应该是不变的。也就是说,顺序执行,在机器码层面还是顺序执行; 条件执行在机器码层面还是条件执行;循环执行在机器码层面还是循环执行;函数调用在机器码层面还是函数调用(不优化,不内联的情况下)。程序的执行顺序就构成了程序的骨架,也就是说,由于汇编和机器码是一一对应的,在汇编中,只要找到if/else/switch/continue/break/while/do/for之类以及函数调用的对应指令或特征,就可以把汇编和高级语言对应起来。

x86下,break, while(1), for( ;; ), continue对应于jmp区别在于break跳转的地址是向后if/else/switch/for/do/while对应于一些jnz, jz, jne, je, jnb, jbe,jg之类的指令。函数调用是call,函数返回是ret

mips下,break, while(1), for( ;; ), continue对应于j,jr区别在于break跳转的地址是向后, ``if/else/switch/for/do/while对应于bne,beq,函数调用对应jal,返回对应于jr $ra`。

sparc下,break, while(1), for( ;; ), continue对应于ba区别在于break跳转的地址是向后, if/else/switch/for/do/while对应于bne,be,ble,bg,bge之类,函数调用对应call,返回对应于jmpl

对这些平台来说,只要掌握上面的指令,就可以在函数里,把几万行的汇编代码分成一小块一小块来分析,每小块的其它指令查手册就行了。

本人的coredump系列第四章也是这个思路,详情请见开发目录

剩余内容请看本人公众号debugeeker, 链接为如何半天学会一门汇编

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/02/14 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 函数
  • 程序执行顺序
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档