快速入门Linux下GDB和汇编开发工具

文章也同时在个人博客 http://kimihe.com/更新

引言

本文亦是《读笔 汇编语言-基于Linux环境(第7章-跟踪指令:与机器指令亲密接触I)》。

本文将会以一个简单的.ASM程序,step by step地帮助大家快速入门GDB,并通过GDB调试,深入底层阐述高级语言(如C语言)中循环结构和指针的由来。

通过阅读本文,你将知道:

  • 如何快速在Linux下进行汇编开发。
  • 如何快速入门GDB。
  • 高级语言循环结构的原理。
  • 指针到底是什么。

构建汇编程序

原料

  • Linux环境,笔者是Ubuntu 12.04 LTS。
  • 安装NASM: 新立得软件包管理器。
  • 安装Kate编辑器和KWrite编辑器: 新立得软件包管理器。
  • 安装konsole:> sudo apt-get install konsole

第一个汇编程序

切到你喜欢的工作目录下,执行> kate以启动Kate编辑器,启动后界面类似于这样:

Kate

左侧是导航栏,右侧是代码编辑区,下方是终端控制区(若要启用此特性请务必先安装konsle)。

新建一个文件,命名为sandbox.asm,在其中输入如下内容:

section .data
    Snippet db "KANGAROO"
section .text
    global  _start
_start:
    nop
; Put your experiments between the two nops...
    mov ebx, Snippet
    mov eax, 8
DoMore: add byte [ebx], 32
    inc ebx
    dec eax
    jnz DoMore
; Put your experiments between the two nops...
    nop

这段代码是我们第一个汇编小例子,用于阐明循环结构的原理,请确保文章例子和你的完全一致。

循环结构的原理

如果是首次接触汇编,你可能会一头雾水,在这里你不必在意汇编的语法,只需要理解我对代码的说明即可。

此处请先注意语句Snippet db "KANGAROO",其中Snippet代表一个字符串,内容为KANGAROO。然后注意语句mov ebx, Snippet,这一步相当于获取字符串的首地址。紧接着的mov eax, 8用于获知字符串的长度。这两步很平常,高级语言的字符串处理也需要获知字符串地址以及相应的长度。

然后请关注如下四条语句:

DoMore: add byte [ebx], 32
        inc ebx
        dec eax
        jnz DoMore

此处的jnz DoMore语句便是循环结构的核心。其含义是:jnz(Jump if Not Z-Flag)进行判断,如果零标志位ZF不为0,就跳转到DoMore语句处。

于是你可以想到,只要这个ZF标志位不是0,程序就会不停地循环跳转(loop),循环结构由此而来。

你可能会想问:什么时候ZF会变成0?这个问题很好,试想一下高级语言的while(n)循环,我们必然需要一个操作步骤来改变n的值,使其在某一时刻变成0,从而跳出while。

此处,眼尖的读者可能发现了dec指令,还记得一开始的获取字符串长度为8吗?我们把8存在了eax寄存器中(如果你不清楚寄存器是什么,也没有关系,把它想象成一个可以存放数值容器即可)。通过dec eax指令,我们会不断地对eax中的8进行递减,类似于int eax = 8; eax--;总有一天,eax中的值会从8减到0,此时我们的x86 Intel CPU就会执行一项既定的操作,把ZF标志设为1,以代表此标志位处于激活状态。于是,jnz在判断的时候就发现ZF已经被激活为1了,不需要再跳转,循环结果宣告结束。

此外,不知道你有没有对于jnz跳转指令产生一些联想:它是不是很像函数指针?(jnz到一个地方,那个地方叫做DoMore,然后执行一段过程。)当然关于函数指针详细的说明,本文篇幅可就不够了,笔者会考虑以后单独写一篇文章详细说明,敬请期待~

什么是指针

有读者可能会问,还有两行代码没有解释呢。不要着急,这两行代码蕴含着指针的奥秘。听起来可能有点令人惊奇,但实际情况确实如此。让我们来看一下这两行代码:

DoMore: add byte [ebx], 32
        inc ebx

注意add byte [ebx], 32这句话,它的专业术语叫做寄存器间接寻址。它是如此神奇,毫不夸张地说,如果没有它,我们日常所见的绝大部分程序将难以构建。

这句话解释一下就是这样:有一个内存单元,它有一个byte大小的空间,里面存有一个数值n(具体是多少,现在不用关心)。把数值32 Add到这个n上,就是相当于n+=32。然后关键点来了,为了加上32,我们需要知道这个内存区域在哪儿。在哪儿呢?在ebx里存着呢!

内存就像一个个信箱,每个信箱都有自己的编号,当我们寻找自家的信箱时,会根据信箱的编号去寻找它。这里ebx就存着我们要的内存区域的编号,这个编号叫做地址根据这个地址,我们找到了那个内存单元的具体位置,然后知道了其中存了一个数n,最后把32给加到了n上。

这里,你应该可以看到,我们并不是直接去访问那个数值n的,而是先去找存放它的内存单元。这里面存在一层间接。正是有了这层间接,我们才能在高级语言中构筑起各种华丽的调用操作。

于是指针的原理也显而易见了,对于

char arr[4] = "abcd";
char *p = arr;
p+=3;
printf("*p: %c\\n", *p);

我们char *p = arr;操作定位的arr数组的首地址。arr信箱有四个格子,我们定位到第一个,然后p+=3;并不是直接给信箱什么的加3,这明显不符合逻辑,而是操作信箱的编号(地址)。加3意味着往后数三个,定位到第四个格子,最后打印里面的东西,就是字符d。

内存中的数据有两种,分别是数据地址,数据就是普通的变量,地址就是指针。希望你不要混淆。

使用GDB

下面进入最后一个知识点,快速入门GDB。在此之前,我们需要把编写的.ASM程序编译链接运行起来。你可能听说过Linux下的make工具,说白了就是个配置文件,告诉NASM,gcc等编译器怎么有效地编译我们的源码,避免重复劳动。make配合makefile文件工作,如果你不知道这到底是什么,也完全没有关系,毕竟这不是本文的重点,只是顺带提一下。

你可以在Kate编辑器中再新建一个文本,名为makefile,请确保它和我们的sandbox.asm在同一个目录下。向其中输入如下内容:

sandbox: sandbox.o
    ld -o sandbox sandbox.o
sandbox.o: sandbox.asm
    nasm -f elf64 -g -F stabs sandbox.asm -l sandbox.lst

你可以完全不必理会这四句话到底代表了什么,只需要明白它们会让NASM正确地生成我们的.ASM程序。

有了这个makefile,接下来可以在Kate编辑器下方的terminal中输入> make -k,或者你自己启动shell,切到你的工作目录,执行上述命令。如果正确编译完成,那么看起来就像这样子:

GDB_terminal

接下来,我们要使用GDB了,在Terminal中键入:> gdb sandbox以启用gdb调试。

调试,我们一般都会需要设置断点,来看看各变量的情况。这里我们已经更加深入到底层,不在内存中操作了,直接来到了CPU内部的寄存器中。键入:> b 10即在DoMore: add byte [ebx], 32语句处加入断点。

然后,键入:> r然程序开始运行。程序会停在DoMore语句那里,看起来就像这样:

step1

接着,键入:i r查看个寄存器状态,就像这样:

step2

你可以看到高亮的绿色部分,rax中存有字符串长度8,rbx中存有字符串地址。

啥?为什么不是eax和ebx?嗯,很有价值的问题,eax和ebx是32位CPU架构下的寄存器,而如今64位已经普及,我们的寄存器也随之升级了。

然后按一下Enter键,或者输入return,可以看到下一页未显示完全的一些寄存器:

step3

注意到绿色高亮部分的eflags标志位,我们发现其中除了IF什么都没有,这表明我们上文提到的ZF标志还没有被激活。

接下来,键入:s,它代表单步执行一行语句,请先执行一次,然后再键入:i r看一下结果寄存器状态:

step4

可以看到rax寄存器内部的值从8减到7,表明执行了一次循环中的dec指令。接下来你可以继续单步执行7次,即键入7次s。每一次都查看一下寄存器的状态,你会发现rax不断递减,直到0。7次单步之后,再次键入i r进行查看:

step5

你会发现rax变成0了,此时Enter到下一页,我们发现:

step6

没错!eflags中出现了ZF标志,表明其被激活,这样jnz就不会再跳到DoMore,循环终于结束了。

最后请键入q,然后y退出GDB。我们的GDB快速入门到此告一段落。

留一个小问题

看到这儿,相信你已经大概理解了循环结构和指针的原理,对汇编工具以及GDB的使用也略知一二。那么我在这里提一个小问题:这段汇编代码到底是做什么的?请你积极思考哦~

答案我会在留言中说明。

总结

本篇文章通过Linux下的一个最简易的汇编开发流程,带领大家熟悉了开发工具的使用,并入门了GDB这一神器。同时通过阅读汇编代码,从底层理解了循环结构和指针的原理。希望对大家有所启迪,感谢阅读!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容