returnx;
}
/*
*parityCheck-returns1ifxcontainsanoddnumberof1's
*Examples:
parityCheck(5)=0,parityCheck(7)=1
*Legalops:
!
~&^|+<<>>
*Maxops:
20
*Rating:
4
*/
intparityCheck(intx){
//每次将数的低半数位与高半数位比较,再把y右移31位,最后把y转化为逻辑的0和1
inty;
y=x<<16;
y=y^x;
y=y^(y<<8);
y=y^(y<<4);
y=y^(y<<2);
y=y^(y<<1);
y=y>>31;
return!
(!
y);
}
/*
*mul2OK-Determineifcancompute2*xwithoutoverflow
*Examples:
mul2OK(0x30000000)=1
*mul2OK(0x40000000)=0
*
*Legalops:
~&^|+<<>>
*Maxops:
20
*Rating:
2
*/
intmul2OK(intx){
//把x第31位和30位分别和1做按位与,再异或,再和1异或
intm;
m=((x>>31)&0x1)^((x>>30)&0x1);
returnm^0x1;
}
/*
*mult3div2-multipliesby3/2roundingtoward0,
*ShouldexactlyduplicateeffectofCexpression(x*3/2),
*includingoverflowbehavior.
*Examples:
mult3div2(11)=16
*mult3div2(-9)=-13
*mult3div2(1073741824)=-536870912(overflow)
*Legalops:
!
~&^|+<<>>
*Maxops:
12
*Rating:
2
*/
intmult3div2(intx){
//左移一位再+x即x*3,右移一位的时候,当y的最高位和最低位都为0时还要+1
inty=(x<<1)+x;
y=(y>>1)+(((y>>31)&1)&(((y<<31)>>31)&1));
returny;
}
/*
*subOK-Determineifcancomputex-ywithoutoverflow
*Example:
subOK(0x80000000,0x80000000)=1,
*subOK(0x80000000,0x70000000)=0,
*Legalops:
!
~&^|+<<>>
*Maxops:
20
*Rating:
3
*/
intsubOK(intx,inty){
//x的最高有效位和y的最高有效位不同且x和(x-y)的最高位不同才能判断溢出
intm=(x>>31)&1;
intn=(y>>31)&1;
x=(m^n)&(m^(((x+(~y+1))>>31)&1));
return(!
x);
}
/*
*absVal-absolutevalueofx
*Example:
absVal(-1)=1.
*Youmayassume-TMax<=x<=TMax
*Legalops:
!
~&^|+<<>>
*Maxops:
10
*Rating:
4
*/
intabsVal(intx){
//x最高位为0时就是x,最高位为1时是~x+1
inty=x>>31;
x=(y&(~x+1))+((~y)&x);
returnx;
}
/*
*float_abs-Returnbit-levelequivalentofabsolutevalueofffor
*floatingpointargumentf.
*Boththeargumentandresultarepassedasunsignedint's,but
*theyaretobeinterpretedasthebit-levelrepresentationsof
*single-precisionfloatingpointvalues.
*WhenargumentisNaN,returnargument..
*Legalops:
Anyinteger/unsignedoperationsincl.||,&&.alsoif,while
*Maxops:
10
*Rating:
2
*/
unsignedfloat_abs(unsigneduf){
intx=uf&(~(1<<31));
if(x>0x7f800000)
{
returnuf;
}
elsereturnx;
}
/*
*float_f2i-Returnbit-levelequivalentofexpression(int)f
*forfloatingpointargumentf.
*Argumentispassedasunsignedint,but
*itistobeinterpretedasthebit-levelrepresentationofa
*single-precisionfloatingpointvalue.
*Anythingoutofrange(includingNaNandinfinity)shouldreturn
*0x80000000u.
*Legalops:
Anyinteger/unsignedoperationsincl.||,&&.alsoif,while
*Maxops:
30
*Rating:
4
*/
intfloat_f2i(unsigneduf){
unsignednum=0x80000000;
intx=(uf&0x007fffff)^0x00800000;
intorder=0;
order=(uf&0x7f800000)>>23;
if(order>158){
returnnum;
}
if(order<127)return0;
elseif(((uf>>31)&1)==1){
if(order>150){
return~(x<<(order-150))+1;
}
elsereturn~(x>>(150-order))+1;
}
else{
if(order>150)returnx<<(order-150);
elsereturnx>>(150-order);
}
}
1.4实验过程
编写源码,运行btest,得出实验结果。
1.5实验结果
可见13个函数全部正确。
1.6实验小结
此次实验主要考查的是对数据的处理,对此需要掌握数据在机器中的表示,运用合理的位运算来实现相应的功能。
实验2:
BinaryBombs
2.1实验概述
本实验中,你要使用课程所学知识拆除一个“binarybombs”来增强对程序的机器级表示、汇编语言、调试器和逆向工程等方面原理与技能的掌握。
一个“binarybombs”(二进制炸弹,下文将简称为炸弹)是一个Linux可执行C程序,包含了6个阶段(phase1~phase6)。
炸弹运行的每个阶段要求你输入一个特定的字符串,若你的输入符合程序预期的输入,该阶段的炸弹就被“拆除”,否则炸弹“爆炸”并打印输出"BOOM!
!
!
"字样。
实验的目标是拆除尽可能多的炸弹层次。
每个炸弹阶段考察了机器级语言程序的一个不同方面,难度逐级递增:
*阶段1:
字符串比较
*阶段2:
循环
*阶段3:
条件/分支
*阶段4:
递归调用和栈
*阶段5:
指针
*阶段6:
链表/指针/结构
另外还有一个隐藏阶段,但只有当你在第4阶段的解之后附加一特定字符串后才会出现。
为了完成二进制炸弹拆除任务,你需要使用gdb调试器和objdump来反汇编炸弹的可执行文件,并单步跟踪调试每一阶段的机器代码,从中理解每一汇编语言代码的行为或作用,进而设法“推断”出拆除炸弹所需的目标字符串。
这可能需要你在每一阶段的开始代码前和引爆炸弹的函数前设置断点,以便于调试。
实验语言:
C语言
实验环境:
linux
2.2实验内容
反汇编bomb,得到汇编代码,根据汇编代码完成拆炸弹任务。
2.2.1阶段1字符串比较
1.任务描述:
找到与输入的字符串进行比较的存储的字符串的首地址,进而得到存储的字符串,得到结果。
2.实验设计:
根据反汇编代码一步一步分析,具体见实验过程。
3.实验过程:
将bomb反汇编输出到asm.txt文件中,在反汇编代码中查找phase_1的位置:
从上面的语句可以看出所需要的两个变量是存在于%ebp所指的堆栈存储单元里,在main函数中:
得知%eax里存储的是调用read_line()函数后返回的结果,就是输入的字符串,所以得知和用户输入字符串比较的字符串的存储地址为0x804a204,可用gdb查看这个地址存储的数据内容:
翻译过后的结果为Thefuturewillbebettertomorrow.
4.实验结果:
可见结果正确。
2.2.2阶段2循环
1.任务描述:
完成炸弹2的拆除
2.实验设计:
观察分析phase_2代码,使用gdb调试分析结果
3.实验过程:
找到phase_2代码:
由read_six_numbers知是要输入6个数字,观察:
可知输入的第一个和第二个必须依次为0,1
观察这两个循环可知只有当输入的数为前两个数之和时才不会bomb,故得到序列0,1,1,2,3,5
4.实验结果:
输入上述序列后得:
可知结果正确。
2.2.3阶段3条件/分支
1.任务描述:
完成炸弹3的拆除
2.实验设计:
观察分析phase_3代码,使用gdb调试分析结果
3.实验过程:
找到phase_3代码如下:
08048c0a:
8048c0a:
83ec3csub$0x3c,%esp
8048c0d:
8d44242clea0x2c(%esp),%eax
8048c11:
89442410mov%eax,0x10(%esp)
8048c15:
8d442427lea0x27(%esp),%eax
8048c19:
8944240cmov%eax,0xc(%esp)
8048c1d:
8d442428lea0x28(%esp),%eax
8048c21:
89442408mov%eax,0x8(%esp)
8048c25:
c74424044ea204movl$0x804a24e,0x4(%esp)
由此行代码查看输入内容:
可知输入的依次是数字、字符、数字
8048c43:
837c242807cmpl$0x7,0x28(%esp)
8048c48:
0f87f5000000ja8048d43
…
8048d43:
e88d040000call80491d5
可见输入的第一个数一定小于7
8048c4e:
8b442428mov0x28(%esp),%eax
8048c52:
ff248560a20408jmp*0x804a260(,%eax,4)
假设输入的第一个数为0,即(%eax)=0,所以:
8048c59:
b876000000mov$0x76,%eax
8048c5e:
817c242c040100cmpl$0x104,0x2c(%esp)
所以第二个字符ascll码为0x76,即字符'v'
而第三个数为0x104,即260
4.实验结果:
从实验结果来看结果正确,拆弹成功。
2.2.4阶段4递归调用和栈
1.任务描述:
拆除炸弹4
2.实验设计:
观察分析phase_4代码,使用gdb调试分析结果
3.实验过程:
用x/sb0x804a3cf来查询有几个输入以及输入的类型,如下所示:
由此可见输入是两个整数。
再由phase_4中:
知道func4第二个参数值为1f,即37
再仔细研究func4函数,发现其实现了递归调用:
08048d5c:
8048d5c:
56push%esi
8048d5d:
53push%ebx
8048d5e:
83ec14sub$0x14,%esp
8048d61:
8b542420mov0x20(%esp),%edx/ebx是传递的参数/
8048d65:
8b442424mov0x24(%esp),%eax
8048d69:
8b742428mov0x28(%esp),%esi
8048d6d:
89f1mov%esi,%ecx
8048d6f:
29c1sub%eax,%ecx
8048d71:
89cbmov%ecx,%ebx
8048d73:
c1eb1fshr$0x1f,%ebx/ebx右移31位/
8048d76:
01d9add%ebx,%ecx
8048d78:
d1f9sar%ecx
8048d7a:
8d1c01lea(%ecx,%eax,1),%ebx
8048d7d:
39d3cmp%edx,%ebx
8048d7f:
7e17jle8048d98
8048d81:
8d4bfflea-0x1(%ebx),%ecx
8048d84:
894c2408mov%ecx,0x8(%esp)
8048d88:
89442404mov%eax,0x4(%esp)
8048d8c:
891424mov%edx,(%esp)
8048d8f:
e8c8ffffffcall8048d5c
8048d94:
01d8add%ebx,%eax
8048d96:
eb1bjmp8048db3
8048d98:
89d8mov%ebx,%eax
8048d9a:
39d3cmp%edx,%ebx
8048d9c:
7d15jge8048db3
8048d9e:
89742408mov%esi,0x8(%esp)
8048da2:
8d4301lea0x1(%ebx),%eax
8048da5:
89442404mov%eax,0x4(%esp)
8048da9:
891424mov%edx,(%esp)
8048dac:
e8abffffffcall8048d5c
8048db1:
01d8add%ebx,%eax
8048db3:
83c414add$0x14,%esp
8048db6:
5bpop%ebx
8048db7:
5epop%esi
8048db8:
c3ret
下面就来剖析func4,这个函数在确定栈之后,首先取出来传递给它的参数,依次放在eax,edx,esi.中,从一个jle和一个jge可以看出,这个递归函数跳出的条件根据func4的第二个参数和第二个参数进过种种运算的结果等于第一个参数即可。
注意在递归过程中第一个参数是不变的,最后返回值是经过运算后的ebx加上第一个参数。
当时做实验时推出了具体的表达式,未记录下来,只记录了最后得出fun(11)=31。
运行结果如下:
由此可见,phase_4拆除成功!
4.实验结果:
给出阶段x的实验结果和必要的结果分析
2.2.5阶段5phase_5
1.任务描述:
拆除一个关于指针的炸弹。
2.实验设计:
此阶段实验与指针相关,又根据静态调试跟踪可知,需借助gdb的动态调试跟踪来查找相关地址中存放的数据的值,进而分析出最终的拆弹密码。
3.实验过程:
首先观察代码,分析代码时发现有多个跳转指令,具体为x>15时,bomb;x>=1时,取x低4位;
使用gdb调试发现,要输入的是两个%d数。
由后面的步骤知输入第一个数为初始数组下标,第二个数为循环15次累加求的和。
再接着:
8048e70:
8b048580a20408mov0x804a280(,%eax,4),%eax,这句就是从(0x804a280+eax*4)里面拿数据出来,加到eax上。
因为eax只能是0~F的数,所以0x804a260这个地址里面存的应该是一个数据大小为16的数组,用gdb看,得到:
观察到果然是一个数组,然后下面就是把5个输入对应ascll码的低4位转换的十进制数对应的数值一个一个的转化为这个数组,得到累加值ecx。
观察循环部分:
由此知当退出循环的条件是取出的数eax为15,而且循环