20145310 《信息安全系统设计基础》第五周同学问题总结

学习目标

  • 理解逆向的概念
  • 掌握X86汇编基础,能够阅读(反)汇编代码
  • 了解ISA(指令集体系结构)
  • 理解函数调用栈帧的概念,并能用GDB进行调试

学习资源

  • 教材:第三章《程序的机器级表示》,详细学习指导见教材导读与每周考试重点。:重点是3.7节,3.11节
  • 实验楼课程资料:实验四,课程邀请码:W7FQKW4Y
  • 教材中代码运行、思考一下,读代码的学习方法见「代码驱动的程序设计学习」。

所遇到的问题及解决办法

本周主要进行了汇编方面内容的学习,大家遇到的问题主要集中在课后练习和对实验楼练习编译的情况,我先对几个常见问题做个总结吧。

在为code.c进行优化编译的时候出现问题

这个大家会有各种各样的编译错误,最主要的问题就是0 和 o 不能分清,应该是O1而不是01,这两个实在太容易看错了。其实呢,仔细思考了一下O1的含义,1当然是优先级,那么O想必就是output输出的意思了,这样就不容易打错了。

栈和栈针

栈和栈帧的关系
栈:
栈底指针和栈顶指针(执行时,它在动),栈底位于最下。
一种特殊的数据结构,是一种只能在一端进行插入和删除操作的特殊线性表。
存储原则是先进后出。
对栈的插入与删除操作中,不需要改变栈底指针。
栈是从高地址向低地址延伸的。
栈——相对整个系统而言,调用栈相对某个进程而言。
栈帧——则是相对某个函数而言,调用栈就是正在使用的栈空间,由多个嵌套调用函数所使用的栈帧组成。
栈帧表示程序的函数调用记录,而栈帧又是记录在栈上面。(为单个过程分配的那部分栈称为栈帧)
每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所需要的各种信息。因此栈作用就是用来保持栈帧的活动记录(即函数调用)。

之后大家的问题主要集中在课后习题上,在这里我也为大家的问题做出汇总

  • 20145201

问题:p131 3.16 为什么C语言只有一个条件语句;而汇编中有两个分支呢?
解答:第一个条件分支是&&表达式实现的一部分;如果对p非空的测试失败,代码会跳过对a的测试

  • 20145203

问题:关于mol和leal指令,课本上说leal指令是用来计算地址的,所以他不改变条件码,那么mov和leal到底区别在哪?
在 20145218的博客中也有这样的提问:既然leal是mov的变形,leal与mov有何区别,两者分别如何使用?
解答:lea eax,[eax+2*eax]的效果是eax = eax + eax * 2
mov edx [ebp+16]的效果是edx=(dword)(ebp+16).因此leal指令不改变条件码。
mov是将数据从源操作传到目的操作数中,lea是将源操作数的地址传到目的操作数中。一个是数据,一个是地址。
前者计算有效地址,后者从指定位置读入数据。

  • 20145208

问题一

教材第113页习题3.1中对操作数260(%ecx,%edx)的求值遇到了问题

按照公式算出来是(264),在这里卡住了,再看了下一个空的时候才反应过来换算成16进制计算是(0x108),得出答案0x13。

问题二

教材121页的习题3.9,和前面的习题3.5类似,都是汇编代码转成c语言代码,但是有所不同,稍稍复杂一点,涉及到了算数和逻辑操作,一开始做起来不适应。

前后对照并且动手做了两遍之后就会觉得顺手多了,上一条语句的值直接用在下面的语句中,一开始可能看不习惯,但是动手很重要,做了两次就可以将看到的汇编代码代入到C语言的相应位置了,相比较C语言翻译成汇编语言来说,汇编语言反翻译成C语言相对容易一点,可能是因为更习惯C语言的原因。

问题三

教材148页的习题3.29,因为前面对于switch语句在汇编中的示例没有仔细阅读,在做题的时候找不到对应的跳转表序号

仔细阅读之后了解C语言中case对应的序号对应着跳转表中的序号,需要注意的时候跳转表中的序号从0开始的。

  • 20145211

数据传输实例

int exchange(int *xp, int y){                                 
    int x =  *xp;  
    *xp = y;  
    return x;  
}  
  
//*********汇编代码******/  
//1 movl 8(%ebp),%eax   Get xp  
//2 movl 12(%ebp),%edx  Get y  
//3 movl (%eax),%ecx    Get x at *xp  
//4 movl %edx,(%eax)    Store y at *xp  
//5 movl %ecx,%eax      Set x as return value   
通过汇编代码可以得到两点收获:

指针其实是地址,间接引用指针就是将该指针放在一个寄存器中 ,然后在间接存储器引
用中引用这个寄存器
局部变量通常保存在寄存器中,而不是存储器(个人猜测应该是局部变量属于动态
分配,局部变量因此被动态置入寄存器,而非存储器)
  • 20145214

问题:P108学习反汇编指令时在完全按照书上的步骤操作的情况下出现无法找到code.o文件的情况

解决:既然提示无此文件,就按照GCC编译的格式产生code.o文件,于是可以顺利进行反汇编

虽然最后用最原始的方法解决,但是书上的确说会产生.o文件,但是返回文件夹查看后发现我产生的是一个名字为1的文件,发现有可能是gcc的指令中o的大小写出现问题,于是用了一个test.c测试

这一次用gcc -O1 -c 发现可以产生test.o文件,用小写的o后面的1会被认为是输出的对象文件名,所以一开始才会只产生了1文件

  • 20145215

Makefile没有问题,主第一次已经make过一遍,第二次如果没有修改过直接编译就会出现错误,可以先make clean一下,再make就没有问题了。

  • 20145216

问题:练习3.23中for循环的执行语句我做的答案是val=(2*val)|(x&ox1);x>>1,参考答案是val=(val<<1)|(x&ox1);x>>=1,结果不一样
解决:>>与>>=的差别:>>是右移运算符,只进行计算,不改变x的值;>>=是右移赋值符,进行右移运算后把计算的结果赋值给x。

  • 20145219

问题:p148 3.29通过阅读汇编代码和跳转表补填C语言代码中的缺失部分,我不明白课后答案中case 几是怎么得出的,越读越混乱,截至上交博客时还未解决。
解决:在评论区20145313同学给出了解决方法:C语言中case对应的序号对应着跳转表中的序号,需要注意的时候跳转表中的序号从0开始的。 具体可参见 20145335同学博客

  • 20145220

问题:一开始在做练习题3.15的B题,思考为什么是ox8048359-25,-25是oxe7的补码表示,但25是转换成十进制的数,为什么可以直接和十六进制的数进行运算,为什么不能直接ox8048359+oxe7计算,这样的结果是ox8048440,但按照答案的解释是 ox8048340。
解决:忽略了补码的问题

  • 20145222

问题:CMP和SUB用在什么地方?
解决:查资料得这个例子:SUB D,S 是D-S结果送回D中,即目的操作数中,而CMP D,S也是相减但结果不送回目的操作数中,一般用作比较数大小时用。

  • 20145223

问题:习题3.15 题目:C
解决:je指令的目标为0x8048391,则mov指令的地址为0x8048391-0x12,即0x804837f。又因为74、12占2字节,则je指令的地址为0x804837f-2,即0x804837d因此答案是:
804837d: 74 12 je 8048391
804837f: b8 00 00 00 mov $0x0, %eax

  • 20145227

问题:教材P131练习题3.16得C代码为:

void cond(int a,int *p)
{
    if(p&&a>0)
        *p +=a;
}

按照与汇编代码等价的C语言goto版本,写一个与之等价的C语言代码如下:

void goto_cond(int a,int *p)
{
    if(p == 0)
        goto done;
    if(a<=0)
        goto done;
    *p +=a;
    done:
        return;
}

为什么C语言只有一个if语句;而汇编中有两个分支呢?

解答:第一个条件分支是&&表达式实现的一部分;如果对p为非空的测试失败,代码会跳过对a>0的测试。

问题:教材P151练习题3.30

  call next
  next:
  popl %eax

为什么这个调用没有与之匹配的ret指令呢?这段代码功能是什么?

解答:这并不是一个真正的过程调用,因为控制是按照与指令相同的顺序进行的,而返回值是从栈中弹出的。这是IA32中将程序计数器的值放到整数寄存器中的唯一方法。

  • 20145231

问题:链接器作用(汇编与反汇编代码区别):
解答:代码的地址范围不同;链接器确定存储全局变量accum的地址(p109)

问题:局部变量保存在寄存器中,全局变量保存在存储器中吗?
解答:C语言的全局变量在这个程序运行期间一直存放在内存中的静态(全局)存储区,程序运行结束释放,另外,函数的局部变量存放在内存中的栈存储区中,函数执行完释放内存空间,但是static声明的静态局部变量存储在静态(全局)存储区,程序运行结束释放,但是作用域不变

问题:cmp和sub用在什么地方
解答:cmp和test都是不改变寄存器的值,test多用于判断操作数是否为零)
cmp:只设置条件码,不能改变寄存器的值
sub:可以改变寄存器的值

  • 20145305

问题:出现cmovl和cmovge
解决:CMOVL/CMOVNGE——小于/不大于或者等于——(SF异域OF) = 1

问题:练习题29中出现jmp .L7(,%eax,4),不理解括号前已经用标识了此处需要跳转,为什么又要写出括号中的地址
解决:L7是跳转表,*指示此处跳到跳转表,括号中的地址是具体跳转到的位置

  • 20145308

问题: 练习3.33 D, 做此题的时候,很不理解%esp和偏移量为+4、+8两个位置存储的数值是如何形成的
解决:将字符串“%x %x”存储在%esp的位置,同时通过leal命令,将x和y的位置存储在+8、+4的位置

  • 20145317

1、P113练习3.1刚开始看题就晕了,很多操作数搞混了,仔细看了表格后,明白了:$Imm是立即数,Imm是存储器位置,Ea是寄存器,(Ea)是存储器位置;此外,比例因子只和变址寄存器的值相乘,最后总结答案如下:
Ox100
OxAB
Ox108
OxFF
OxAB(和第二个等价)
Ox11
Ox13
OxFF
Ox11

2、P155练习3.32题不会做,反复阅读P153~P154后,发现漏看了返回值放在%eax寄存器中这句话,很关键,解答思路如下:

1、代码最后两行:
subl %eax,%edx
movl %edx,%eax
以及return x-c,可确定:%edx为x,%eax为c,注意到有后缀“l”,所以c和x为long int型。
2、看前两行,movsbl、movl命令说明%edx、%eax为双字,第三行代码明显是*p=d,所以,%edx为d,%eax为p,全部为long int型。
3、完整c代码如下:
long int fun(long int d,long int p)
{
*p=d;
long int c=p;
long int x=d;
return x-c
}

  • 20145320

七号同学把练习中的问题解决的很透彻,值得我们学习。
3.1练习中有一道题是260(%eax,%edx),我觉得这个好像题目并没有给出,后来对比下面发现这个260原来是十进制,然后转化为0x104就做出了答案

3.2练习中关于改变push $0xff的指令后缀,开始我考虑根据立即数$0xff应该是pushw,后来我发现对于栈操作都是双字操作,所以不管是pop还是push都应该使用pushl和popl
3.4练习是关于类型转化的练习,有几个注意的地方
有符号扩展为无符号数使用符号扩展而不是零扩展
无论是有符号数还是无符号数在往更小的类型进行缩小的时候只需要使用低八位的寄存器。

3.7练习注意有几个二元操作的指令的用法与出现操作数的数量有关。

3.8练习移位量只能存储在单字节寄存器元素%cl中。

3.10练习是一个十分有趣的练习,他告诉了我们另外一种把寄存器的值置为0的操作,对比于更直接的movl $0,%edx,xorl的优势在于只需要两个字节,而movl需要五个字节。

P122页最下面有一段movl 8(%ebp),%(edx)、sarl $31,%edx,这里做的是将16位的x进行符号扩展到32位,其中高16位放在%edx中,低16位放在%eax中。

3.12练习,刚开始做我有点懵,因为都做惯了双字的数据,没想到y竟然是四字的数据。要注意的是x*y_l乘积应该使用无符号乘法,而对y_h的低32位使用无符号和有符号都没有关系。

3.15练习我觉得很重要,做会这个基本就了解指令重定位的基本做法。

3.7节讲述的是函数在调用时,栈地址如何变化,如何使用特定的寄存器保护原来的数值。

  • 20145330

3.1
题目:将值存放在指明的存储器地址和寄存器中
9(%eax,%edx)
**解决*:根据操作数格式操作数值来计算:ox[9+100+03]=ox10C,地址ox10C对应值为ox11
3.9
题目:基于汇编代码,填写C语言代码缺失的部分
解决:熟练掌握整数算术操作,了解每条语句的意思,这样更好的助于理解
3.15
题目:mov指令的地址是多少
XXXXXXX: 72 12 je 8048391
XXXXXXX: b8 00 00 00 00 mov $0x0,%eax
解决:根据反汇编器产生的注释,跳转目标是绝对地址ox8048391,得到mov指令地址ox8048391-ox12=ox804837f
出错原因为16进制换算掌握不牢固,应多加练习

  • 20145335

习题3.1

其中有一个是260(%ecx,%edc)计算值,起初一直把260当作十进制运算,可是没有结果,后来发现260是十进制,但是参与运算时需要将其转换为16进制再次参与运算,这样260=Ox104这样答案也就得到了。

习题3.2

指令的补全,对于mov指令格式为mov s,d,将s传送给d。但是对于MOV %eax,(%eax) 为什么是MOVL而不是MOVW?后来知道%ax为寄存器16位,%eax是扩展寄存器32位,这样就需要传送双字,就需要MOVL指令。

习题3.7

对于指令imull $16,(%eax,%edx,4)其中后面部分(%eax,%edx,4)为目的操作数,计算出来为Ox10C,对于16是十进制,如习题3.1一样,在运算时候需要换成十六进制Ox10,这样对其运算就可以得到正确的结果。

习题3.29

对于switch语句的汇编代码,最初在做的时候存在序号问题,后来仔细查阅了解C语言中case对应的序号对应着跳转表中的序号,需要注意的时候跳转表中的序号从0开始的。

总结

本周主要学习的是汇编内容,虽然汇编内容已经不常用了,但对我们来说还是很有必要学的,特别是娄老师在每个博客下都建议让大家逐步调试一下代码,反汇编结合GDB动态分析收获会更大,在当时很多同学都没有做,不过之后前两周由于有3分的加分,大部分同学也都做了,相信大家都有了进一步的收货吧。这周大家的问题主要还是集中在课后习题上,其实大部分都是对汇编概念等的不理解,不过由于上学期已经学过汇编语言,这周大家的大问题也不是很多,解决起来也不是很困难,好多同学也已经自己解决了问题。本章重点是3.7,但没有3.1-3.6的基础也是不行,课后习题很重要。
posted @ 2016-12-20 23:11  20145310刘宇飞  阅读(294)  评论(0编辑  收藏  举报