您的位置:首页 > 移动开发

CSAPP深入理解操作系统 课程实验 bomb 反向编译 汇编(3)

2015-05-20 16:34 423 查看
第四关:

首先也是栈开辟:



然后由第三关以及前面对sscanf函数的分析可知,这里应该也是要输入两个数。参数1从-0xc(%ebp)加载,参数2从-0x10(%ebp)处加载。0x804a23e应该里面的内容也是sscanf的输入格式之类的。返回值要等于2,不然会引爆炸弹。







接一下来一段说明输入的第一个参数的范围要在0-14之间



接下来,赋初始值0和14,以及之前输入的参数1这三个数作为参数传给函数func4,调用func4的返回值必须为1。通过esp的偏移看出出栈顺序,也就是传递参数的顺序,第一个参数是参数1设为x,第二个参数是0,第三个参数是14.func(x,0,14)。看到-0x10(%ebp)中是我们输入的第二个数,根据je的判断条件可知,第二个数要为1。



可以看出第四关的关键点就是func4函数,接下来就是分析一下func4这个函数的汇编代码:

首先也是栈开辟:



然后是将传入的参数存到相应的寄存器,按照存入存入的顺序。



接下来就是对传入的参数进行处理:



设ecx寄存器中存储的是k,esi寄存器存储的是t,则按照汇编语句顺序对应操作是,k=z,k=k-y,t=k,t=t>>31,k=t+k;k=k/2;k=k+y;合并在一起就是:k=(z-y)/2+y



通过上面的汇编代码可以看到,经过操作后的k要和x进行比较,根据比较的结果不同分为三种情况,首先当k-x>0即k>X时





通过上面的汇编代码看到,中间又call了func4,说明func4是一个递归函数。在jmp退出递归前,最后看到eax做了相加的操作,可以得知,这种情况下返回值是相当于做了一个乘以2的操作。还可以看出在递归调用func4之前是重新给func4函数传递参数,这个时候的第一个参数是edx中的值,第二个是eax中的值,第三个是ecx中的值,这个时候就要注意相比第一次的时候各个寄存器中的值有没有发生变化了。看到过程中edx和eax都没有变化,还是x,0,最后ecx中的k代替了原来的ebx中的z。k=(z-y)/2+y-1。此时调用func4(x,0,k)。

接下来看k<=x时的情况:





首先先给eax寄存器中的值赋值为0.此时在跳转jge退出递归前返回值可以看出如果直接是k==x(jle与jge小于等于和大于等于同时成立就是等于的时候),则返回为0。如果k<x就要在继续。接下来看k<x时:



可以看到这种小于的情况下也是要递归的,像第一种情况大于时分析一样,也是传递参数,首先是第一个参数是edx,第二个是ecx,第三个是ebx。ecx中k=(z-y)/2+y+1,func(x,k,z)。在结束递归之前,看到返回值eax=2eax+1。可以看出,phase_4中最后调用func4的返回值为1,那就是要在此种情况下产生返回值。Func4就是一个递归函数,一直到等于的时候结束递归。

首先在phase_4中第一次传参调用func4(x,0,14),首先k=(z-y)/2+y=7,那要是的k<x,则x的取值范围要是8-14。这里最简单也是最耗时的方法就是8-14输入挨个看最后func4的返回值,返回为1的为输入正确的x。这样可以得到8,9,11为返回值1。则过关的密码可以是(8,1)(9,1)(11,1)三种。



另外一种方法就是将func4写成一个对应的c函数,然后运行输出。





第5关。Phase5

首先来看汇编代码,经过前面机关很容易就可以看到熟悉的栈开辟,以及调用sscanf函数,第一个参数在-0xc(%ebp)第二个参数在-0x10(%ebp),调用sscanf后返回值表示输入的数的个数要大于1.看到有ebx和esi考虑下面的情况中有循环或者数组。



接下来就是输入的数进行操作了,





看到将输入的第一个数的第四位取出来,不能全部为1.再往下看



这里看到有一个循环,看到8048e0b中很熟悉的取数组元素的操作。但是这里有一个需要注意的地方,我们输入的第一个数eax用来被计算偏移下标了。也就是说下标不是连续的。每次取出来的元素的值用到下一次去数组元素时计算下标。可以看到ecx寄存器是一个累加器,先赋值为零,然后用来保存数组元素的和,然后edx用来循环计数,在这里,只要取到的数组中元素不为15就继续循环累加求和,可以看出数组中有一个元素是15,,然后ebx作为的是数组的首地址,是有立即数0x804a1c0中,所以可知已知数组存在这个地址中。



通过上面可以看到,edx用于循环次数的计数,那么这里必须要进行15次,另外可以看到-0x10(%ebp)中是我们输入的数,要等于ecx,也就是要等于累加之后的结果。

那么看来就要首先将数组中的首地址里面存的数值取出来。









这里要找出第一个eax的值即为我们输入的第一个数。因为一共16个数,循环必须要循环15次,而且最后取的数组元素为15,则可以倒着推测,最后一个数是15.eax=ebx的偏移/4.此时eax=24/4=6.对于前面分析的对取数组元素下标的分析可知,eax是倒数第二次取到的数组中的元素的值,然后查找上面的图,再次计算eax的值为56/4=14.就是这样倒推下去:

则倒序的取数组的值为:

15 6 14 2 1 10 0 8 4 9 13 11 7 3 12

12的偏移为20那么eax为5 所以输入的第一个数为5 ,而第二个数为上面数组元素的和115

所以第五关的通关密码就是5 115





第六关:phase_6



前面关卡已经详细分析过了,都是先开始栈的开辟,看到有edi,esi,ebx就会联想到循环和数组,另外看到这里是调用了readsixnumbers,得知应该需要输入六个数字。接着往下看,



果然看到了熟悉的数组操作,数组读取元素,在这里edi存的是数组的首地址,esi作为数组坐标的偏移计算。首先数组的第一个元素减1要小于5,也就是第一个数要小于6.



看到将esi的值加1,我们令esi中的值为i,那么i<6,就在循环中,这个时候,把数组的第二个数取出来放在ebx寄存器中,下面一步很关键,它将这个时候的i在栈中存储起来,相当于将i赋值给k,然后又将第二个数的前一个数也就是第一个数放在eax中,将第二个数与第一个数比较,结果要不相等。继续往下看。



这里很关键是将在栈中临时保存的i的值k加1,然后ebx加4相当于取第三个数,只要k比5小,就跳进与eax寄存器的比较,也就是说通过k来实现第一个数与剩下五个数的比较,要第一个数跟其余五个数都不想等。



等k=5时也就是第一个数和其余五个数都比较晚了以后,跳到8048cac。

这里可以看到就是i控制的是外层循环实现,数组元素比较的基准的递增,首先第一个数与其余五个数比较不相等,然后第二个数与其余数除了第一个数比较要不相等。一直到所有的数都比较完,都不相等,而且所有的数都要小于等于6.也就是0 -6中的不同的六个数。



比较与限制循环做完之后,就是下面的,将数组的首地址重新赋给edi寄存器。然后再次取数组中的元素,比较1的大小,然后进行相应的跳转。





首先看一下取出的数组的元素大于1的时候,跳转情况:这里是看到当数组元素比1大时,就将edx偏移0x8,再次将eax加1,将数组元素在跟eax相比较,只要不相等,就再次将edx偏移0x8直到数组元素和eax中的值相等。先不管在上面立即数0x804c0c4给了edx什么,先继续看完跳转情况,再通过gdb调试看内容。





看一下如果数组元素如果比1小于等于的情况,因为所有的数都不相等,所以这个情况有的话只会是0或者1



这里可以看到操作的目的就是最后得到一个edx。然后继续向下看:可以看到接下来每次将获得的edx重新放到了一个新的数组中进行存储起来了。这里可以看到ebx用于计数,知道我们输入的六个数对应的edx都被存储。



在看一下六个数都执行完以后的跳转,这里相当于是一个新的赋值映射。



由于我们到此还没有用gdb进行调试查看,所以我们先继续看完,这一关的汇编代码非常长,尤其是上面的重复性操作,看起来让人容易一头雾水。还是要静下心来一段段分析完,遇到分析不透的可以先放在一边,先把整体的函数大概意思搞清楚。



通过这个可以看到esi又是一个计数的,然后就是把新的映射中的内容做一个循环比较,通过比较的条件可以看出,新的序列中的内容要前面的大于等于后面的,也就是一个有序序列,也就是一个递增序列。而这个新序列经过我们分析可知是根据我们输入的六个数,按照我们输入的数的不同的顺序取出来的,最后取出来的顺序必须是增序。

全部走过一遍之后,知道我们要输入6个数,这六个数互不相同,而且都要小于等于6,然后输入的顺序影响到后续得到的要是一个递增序列。

接下来就是要来分析那个神秘的立即数中的内容,与最后的递增序列的关系了。这就需要gdb调试得到了。





通过上面可以看出来首先是查看edx的内容,然后每次都是把edx偏移0x8,通过查看内容会发现非常的惊讶。首先edx中是内容,然后edx+0x8内的内容是一个地址,然后在查看这个地址,地址内的内容是一个内容,然后在将这个地址偏移0x8得到的地址内的内容又是一个地址,可以看出来,这是一个典型的链表结构。链表就是不同的node节点,每个node节点在内存中存在不同的位置,不连续的位置中。每个node由内容和next指针构成,每个next指针内的值是一个指向下一个node节点的地址,最后一个node的next指向null。

这样结合前面的分析就可以知道,我们输入的6个数实际上就是不同的node节点数,根据我输入的节点号,把节点内的内容重新进行排序,排成递增序列。

链表:

Header(0x804c04)

0x804c0c4 (0x804c0c4+0x8) 0x804c0b8 (0x804c0b8+0x8)

Node1 node2





从上面可以看到内容按从大到小排列是:

0x3bd>0x255>0x1a7>0x187>0x155>0x6c

也就是节点号按照这个顺序就是:

5 6 1 4 3 2 这个就是我们这一关的答案

接下来再来看一下前面比较繁琐的汇编代码:



第三句可以看出来这里又构建了一个新的链表,ebx偏移0x8也就是next指针指向eax,eax偏移0x8指向edx等等。一个新的链表映射。

至此全部1-6关已经顺利通过。



你以为这就结束了么,敬请期待下一篇。。。哈哈。是不是收获很大,很有意思的实验啊。不过做出来确实要花一些时间,以及要对汇编,反向编译以及gdb调试有一定的功底。还有数据结构,等。可以看出来整个关卡层层递进,考察了循环,跳转,switchcase,链表,数组等等。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐