1、 */int lsbZero(int x) /x右移一位再左移一位实现把最低有效位置0 x = x1; x = x return x; * byteNot - bit-inversion to byte n from word x * Bytes numbered from 0 (LSB) to 3 (MSB) * Examples: getByteNot(0x12345678,1) = 0x1234A978 6 2int byteNot(int x, int n) /x第n个字节每位都和1异或实现取反 int y = 0xff; n = n3; y = y x = x&(0xff); y =
2、 y& return !(xy); * logicalAnd - x & y 3 int logicalAnd(int x, int y) /把x和y分别转化为逻辑的0和1,再相与 x = (!(!x)&y); * logicalOr - x | yint logicalOr(int x, int y) /把x和y分别转化为逻辑的0和1,再相或x)|(! * rotateLeft - Rotate x to the left by n * Can assume that 0 = n = 31 rotateLeft(0x87654321,4) = 0x76543218 25int rotateL
3、eft(int x, int n) /先构造低n位为1,高(32-n)位为0的数z,x左移n位后的数加上x右移(32-n)位的数&z即可 int z; z = (131)(32+(n+1)&z)+(x/* * parityCheck - returns 1 if x contains an odd number of 1s parityCheck(5) = 0, parityCheck(7) = 1 4int parityCheck(int x) /每次将数的低半数位与高半数位比较,再把y右移31位,最后把y转化为逻辑的0和1 int y; y = x16; y = yx; y = y(y31
4、)&0x1)(x30)&0x1); return m0x1; * mult3div2 - multiplies by 3/2 rounding toward 0, * Should exactly duplicate effect of C expression (x*3/2), * including overflow behavior. mult3div2(11) = 16 * mult3div2(-9) = -13 * mult3div2(1073741824) = -536870912(overflow) 12int mult3div2(int x) /左移一位再+x即x*3,右移一位
5、的时候,当y的最高位和最低位都为0时还要+1 int y = (x1)+(y1)&(y int n = (y x = (mn)&(m(x+(y+1) return (!x); * absVal - absolute value of x absVal(-1) = 1. * You may assume -TMax = x x = (y&(x+1)+(y)& * float_abs - Return bit-level equivalent of absolute value of f for * floating point argument f. * Both the argument an
6、d result are passed as unsigned ints, but * they are to be interpreted as the bit-level representations of * single-precision floating point values. * When argument is NaN, return argument. Any integer/unsigned operations incl. |, &. also if, whileunsigned float_abs(unsigned uf) int x=uf&(10x7f80000
7、0) return uf; else return x; * float_f2i - Return bit-level equivalent of expression (int) f * for floating point argument f. * Argument is passed as unsigned int, but * it is to be interpreted as the bit-level representation of a * single-precision floating point value. * Anything out of range (inc
8、luding NaN and infinity) should return * 0x80000000u. 30int float_f2i(unsigned uf) unsigned num=0x80000000; int x=(uf&0x007fffff)0x00800000; int order=0; order=(uf&0x7f800000)23; if(order158) return num; if(order1)=1)150) return (x(150-order)+1; else150) return x(150-order);1.4 实验过程编写源码,运行btest,得出实验
9、结果。1.5实验结果可见13个函数全部正确。1.6实验小结 此次实验主要考查的是对数据的处理,对此需要掌握数据在机器中的表示,运用合理的位运算来实现相应的功能。实验2: Binary Bombs 2.1 实验概述本实验中,你要使用课程所学知识拆除一个“binary bombs”来增强对程序的机器级表示、汇编语言、调试器和逆向工程等方面原理与技能的掌握。一个“binary bombs”(二进制炸弹,下文将简称为炸弹)是一个Linux可执行C程序,包含了6个阶段(phase1phase6)。炸弹运行的每个阶段要求你输入一个特定的字符串,若你的输入符合程序预期的输入,该阶段的炸弹就被“拆除”,否则炸
10、弹“爆炸”并打印输出 BOOM!字样。实验的目标是拆除尽可能多的炸弹层次。每个炸弹阶段考察了机器级语言程序的一个不同方面,难度逐级递增:* 阶段1:字符串比较* 阶段2:循环* 阶段3:条件/分支* 阶段4:递归调用和栈* 阶段5:指针* 阶段6:链表/指针/结构另外还有一个隐藏阶段,但只有当你在第4阶段的解之后附加一特定字符串后才会出现。为了完成二进制炸弹拆除任务,你需要使用gdb调试器和objdump来反汇编炸弹的可执行文件,并单步跟踪调试每一阶段的机器代码,从中理解每一汇编语言代码的行为或作用,进而设法“推断”出拆除炸弹所需的目标字符串。这可能需要你在每一阶段的开始代码前和引爆炸弹的函数
11、前设置断点,以便于调试。实验语言:C语言实验环境:linux2.2 实验内容反汇编bomb,得到汇编代码,根据汇编代码完成拆炸弹任务。2.2.1 阶段1 字符串比较1.任务描述:找到与输入的字符串进行比较的存储的字符串的首地址,进而得到存储的字符串,得到结果。2.实验设计:根据反汇编代码一步一步分析,具体见实验过程。3.实验过程:将bomb反汇编输出到asm.txt文件中,在反汇编代码中查找phase_1的位置:从上面的语句可以看出所需要的两个变量是存在于%ebp所指的堆栈存储单元里,在main函数中:得知%eax里存储的是调用read_line()函数后返回的结果,就是输入的字符串,所以得知
12、和用户输入字符串比较的字符串的存储地址为0x804a204,可用gdb查看这个地址存储的数据内容:翻译过后的结果为The future will be better tomorrow.4.实验结果:可见结果正确。2.2.2 阶段2 循环完成炸弹2的拆除 观察分析phase_2代码,使用gdb调试分析结果找到phase_2代码:由read_six_numbers知是要输入6个数字,观察:可知输入的第一个和第二个必须依次为0,1观察这两个循环可知只有当输入的数为前两个数之和时才不会bomb,故得到序列0,1,1,2,3,5输入上述序列后得:可知结果正确。2.2.3 阶段3 条件/分支完成炸弹3的拆
13、除 观察分析phase_3代码,使用gdb调试分析结果找到phase_3代码如下:08048c0a : 8048c0a: 83 ec 3c sub $0x3c,%esp 8048c0d: 8d 44 24 2c lea 0x2c(%esp),%eax 8048c11: 89 44 24 10 mov %eax,0x10(%esp) 8048c15: 8d 44 24 27 lea 0x27(%esp),%eax 8048c19: 89 44 24 0c mov %eax,0xc(%esp) 8048c1d: 8d 44 24 28 lea 0x28(%esp),%eax 8048c21: 89
14、 44 24 08 mov %eax,0x8(%esp) 8048c25: c7 44 24 04 4e a2 04 movl $0x804a24e,0x4(%esp)由此行代码查看输入内容:可知输入的依次是数字、字符、数字 8048c43: 83 7c 24 28 07 cmpl $0x7,0x28(%esp) 8048c48: 0f 87 f5 00 00 00 ja 8048d43 8048d43: e8 8d 04 00 00 call 80491d5 可见输入的第一个数一定小于7 8048c4e: 8b 44 24 28 mov 0x28(%esp),%eax 8048c52: ff
15、 24 85 60 a2 04 08 jmp *0x804a260(,%eax,4)假设输入的第一个数为0,即(%eax)=0,所以: 8048c59: b8 76 00 00 00 mov $0x76,%eax 8048c5e: 81 7c 24 2c 04 01 00 cmpl $0x104,0x2c(%esp)所以第二个字符ascll码为0x76,即字符v而第三个数为0x104,即260从实验结果来看结果正确,拆弹成功。2.2.4 阶段4 递归调用和栈拆除炸弹4 观察分析phase_4代码,使用gdb调试分析结果用x/sb 0x804a3cf 来查询有几个输入以及输入的类型,如下所示:由
16、此可见输入是两个整数。再由phase_4中:知道func4第二个参数值为1f,即37再仔细研究func4函数,发现其实现了递归调用:08048d5c 8048d5c: 56 push %esi 8048d5d: 53 push %ebx 8048d5e: 83 ec 14 sub $0x14,%esp 8048d61: 8b 54 24 20 mov 0x20(%esp),%edx /ebx是传递的参数/ 8048d65: 8b 44 24 24 mov 0x24(%esp),%eax 8048d69: 8b 74 24 28 mov 0x28(%esp),%esi 8048d6d: 89 f
17、1 mov %esi,%ecx 8048d6f: 29 c1 sub %eax,%ecx 8048d71: 89 cb mov %ecx,%ebx 8048d73: c1 eb 1f shr $0x1f,%ebx / ebx 右移31位 / 8048d76: 01 d9 add %ebx,%ecx 8048d78: d1 f9 sar %ecx 8048d7a: 8d 1c 01 lea (%ecx,%eax,1),%ebx 8048d7d: 39 d3 cmp %edx,%ebx 8048d7f: 7e 17 jle 8048d98 8048d81: 8d 4b ff lea -0x1(%e
18、bx),%ecx 8048d84: 89 4c 24 08 mov %ecx,0x8(%esp) 8048d88: 89 44 24 04 mov %eax,0x4(%esp) 8048d8c: 89 14 24 mov %edx,(%esp) 8048d8f: e8 c8 ff ff ff call 8048d5c 8048d94: 01 d8 add %ebx,%eax 8048d96: eb 1b jmp 8048db3 8048d98: 89 d8 mov %ebx,%eax 8048d9a: 8048d9c: 7d 15 jge 8048db3 8048d9e: 89 74 24 0
19、8 mov %esi,0x8(%esp) 8048da2: 8d 43 01 lea 0x1(%ebx),%eax 8048da5: 8048da9: 8048dac: e8 ab ff ff ff call 8048d5c 15时,bomb;x=1时,取x低4位;使用gdb调试发现,要输入的是两个%d数。由后面的步骤知输入第一个数为初始数组下标,第二个数为循环15次累加求的和。再接着: 8048e70: 8b 04 85 80 a2 04 08 mov 0x804a280(,%eax,4),%eax,这句就是从(0x804a280+eax*4)里面拿数据出来,加到eax上。因为eax只能是0F的数,所以0x804a260 这个地址里面存的应该是一个数据大小为16的数组,用gdb看,得到:观察到果然是一个数组,然后下面就是把5个输入对应ascll码的低4位转换的十进制数对应的数值一个一个的转化为这个数组,得到累加值ecx。观察循环部分:由此知当退出循环的条件是取出的数ea