二进制炸弹(ICS作业)
**
**
密码:All your base are belong to us.
通过观察的汇编代码可以发现,函数应该为比较输入密码和标准密码的。所以前一个操作应该为将标准密码取出放到寄存器中。这样大致思路就为在中的位置设置一个断点,然后用操作单步执行到操作时,将寄存器取出即为正确密码。取出值得过程可以先用查看寄存器的地址和存储的值,再用 地址将寄存器中的值以字符串的形式输出出来。
通过已经基本熟悉了逆向的过程。
**
**
密码:0 1 1 2 3 5
行的汇编语言中,可以看出读入了一个输入的位密码,接下来就应当是比较的过程。
行中首先将与栈顶指针所存的数进行比较,如果不一样则炸弹爆炸,可以判断出第一位密码应当是。
接着比较了与栈顶的第二个元素,相同跳转,不同就会直接爆炸,所以可以推测是第二位的密码。
接着程序跳转到了,将栈顶指针保存到了4%rbx+50$。
的操作是将当前栈顶的第二个元素也就是1赋给eax。
是将当前栈顶元素也就是加到上,随后再将与标准密码进行比较,之后再不断重复这个循环。
可以找到规律,初始两位密码为 ,随后的第i位密码就是和位的和,所以算出密码为:0 1 1 2 3 5
**
**
密码:
1 471
2 735
3 349
4 469
5 942
6 192
7 551
其中的任意一组
那一行可以看出访问了一个函数,通过第三张截图中访问的第一个而第二个寄存器可以看出,第一个参数保存的是读入的一串字符串,第二个参数是 可见程序希望读入两个整数,也就是第三个密码应该为两个整数。然后 两个寄存器的值就是存这两个整数的。
的比较函数要求读入的返回值大于一,也就是要读入至少两个数,与上文的意思相符。
比较了7和读入的第一个数的大小,和下面的超过跳转,可以看出是要求第一位密码的范围应当是小于等于的整数。
再向下一直到之前一直是处理中的值,然后就是的条件跳转。可以发现这个条件用到了中的值,调用了的偏移量,可以发现是根据读入的第一个数来跳转的。
接着下面有7组差不多的命令,都是先赋值以后跳到,可见这些加上的间接跳转应该是一个语句。用第一个密码来选择。
比较了中的值与第二位密码是否相等,而中的值从中被赋过值了,所以语句中也就蕴含着与第一位密码相应的答案。共有组。
**
**
密码:3 10
根据的做法,先输出的,发现的密码也是要输入两个整数。接下来就分别用表示两位密码。
和可以看出x应该是一个小于等于的整数。
行中代码进入到了了个函数中,同时分别将作为参数传入到了函数当中。
第二个截图为函数的指令。接下来用分别表示传入的三个参数。
和将保存到了中。将的数存到了中。
这个操作将中的数逻辑右移了位。这个操作在中的数位正数的情况下,就等价与清零操作,可以先将其看作清零操作。
接下来就是将的值赋给,然后将中的值除以,再加上。
接下来将中的值与进行比较,如果大于,就将赋成中的数,继续执行函数进行递归。如果小于就将赋成中的数继续进行函数进行递归。递归结束后将的数加上递归的返回值然后作为这一层的返回值。如果等于中的值,就直接返回中的值。
递归完之后回到中去,要求返回值为,要求与返回值相等,所以可以得到第二个密码值为,而第一个密码为递归函数中的第一个参数,根据描述,写出相应的递归程序后如下:
通过试验测得,当的第一个参数为时,返回值为,所以密码为 .
**
**
密码:
这一行可以看出读入的面貌是一个位字符串。将清零。
然后通过观察整段程序,发现程序可以分成两个部分,第一部分是到之间,是一个循环。第二段是到是判断部分。
由于第一部分比较复杂,所以先看第二部分,第二部分是将地址为开始的字符串与中的进行比较。可以看到中的字符串为如下的位字符。鱼就是目标字符串为
接下来看循环中的部分。
首先将一个常数赋给了,通过和面的地址可以看到中存的:
到可以看出来这个循环是变量从到的一个循环。
和这两行进行的操作为将的数加上的和作为地址,读取其中的数赋给。而是我们读入的字符串,中存的地址就是字符串开头字母的地址,又因为是从到的,所以在每次循环中分别取出了输入字符串的第到位赋给了。然后取出了的后四位。
行访问了和保存的地址的和作为地址的地址。因为中存储的也为字符串,所以可以看成是以存储的字符串的首字符作为基地址,存的量为偏移量来访问后面的第个字符。
行将中的值存储到对应的行要比较的字符串的第位。
通过上面的分析可以看出,第i次循环要得到中的第个字符,而这个字符是通过访问字符串中的字符找到的,可以计算出,每次循环行访问时的偏移量分别为这样分别代表了后面的第个字符,第个以此类推。
而第个偏移量的获得是通过度日的字符串的第位对应的码取后四位得到的。所以可以得到输入的位字符串的后四位分别为。然后在补上高位,即获得了正确密码。
密码:5 6 2 1 4 3
到是读入部分,通过输出中存的数可以发现,中保存的是读入的第一个数的地址。输入的六位密码就用a[i]来表示。
接下来程序可以分为以下几个部分:到;到; 到最后。
首先到是一个单层循环,中使循环变量,从到遍历了读入的六个元素,判断每个元素的是是否都小于等于且互不相等。这也就要求了这个六位密码是的全排列中的一个。
到是一个两层循环,中存的是一个外层循环的循环变量,这一层循环从到遍历了一遍读入的六个数据。中保存了第重循环的循环变量。可以发现第二层循环的次数位第i位密码的值。而第二陈循环中主要涉及了和这两个寄存器。首先来看一下。
这个是的初始化,是从内存中读取了一个常数。接下来对于外层的第i次循环,内层的循环进行了¥a[i]%rdx%rdxa[i]$次,也就是说程序中保存了个常数,对于第i次外层循环,内层循环取出了第个常数。
这条语句依次将取出的元素保存在了栈中。
然后是最后一个部分,首先进行了一个这样的操作:
这段代码将栈上一部分保存在栈中的元素建立了一个链表,顺序就是,第一个放入的元素指向下一个放入的元素(这一段开始比较难以理解)。
这是程序的最后阶段了,这一段在刚刚建好的链表中从前到后的比较了量表中相邻两个元素的大小。也就是第一个元素小于第二个元素想,第二个小于第三个,以此类推,也就是这六个元素要是从小到大排列好的。
现在这个程序的作用也就比较清晰了,程序按照我们输入的六个数的顺序依次取出了系统中保存的六个常数,要求取出的这六个常数要按照从小到大的顺序排好,也就是说我们输入的六位密码的第i位代表的应该是,六个常数中排名第小的元素是第几个。那只需要看一下这六个元素依次是什么,这个问题就解决了。
输出程序中的六个常数的大小,即可得到密码:5 6 2 1 4 3
**
**
密码:22
程序中只有phase_defused这个函数还没有看过内容,所以隐藏关可能就宝运载整函数中。函数中开始就有一个比较,通过在程序中跟踪比较的那个地址发现,那个地址就是**的炸弹的个数,也就是说,只有**了六个炸弹后才能进入下面的隐藏部分。
通过这个值发现,要输入两个整数和一个字符串来进入隐藏关,而中首先传入了一个参数,通过输出发现正好是第四个炸弹的密码,所以字符串应该是跟在第四个炸弹后面的。
中调用了字符串是否相等的函数,那么根据传入的那个字符串的地址,就可以知道进入隐藏关的密码了。
进入到隐藏关后,发现隐藏关的密码是通过read_line读入的,也就是一个字符串。中调用了一个系统函数,通过查询,发现这个代码的功能是将字符串转换成相应的数字。所以的总就保存的将读入字符串传化为数字后的值。接下来的两行限制了这个数字的大小。到这段代码限制了隐藏关的密码要小于。
再向下看发现中调用了函数,假设传入的两个变量为,就是我们读入的隐藏关的密码,是一个指针,通过访问内存地址,发现的值为:
通过的代码,发现是一个递归函数。
首先当的时候,函数返回。然后递归函数先在分成了两个部分,第一个部分是的情况,第二个是的情况。
当时,跳转到了,将下一次递归的指针x指向的地址加了后进入到了下一层递归,然后将返回值乘作为这一层递归的返回值。
当的时候,又分成了两个部分,和。
当的时候,程序不再进行下一层递归,直接返回。
当的时候,跳转到了,将下一次递归的指针指向的地址增加了后进入到下一层递归,然后将返回值乘加作为这一层的返回值。
观察secre_phase的和发现,隐藏关要求函数的返回值为,所以回想整个递归过程,最深层的递归返回值一定为,如果想得到,需要将先乘加变成,再乘变成,也就是说最开始应该先执行,在下一层执行,最深的一层为,这样就可以让的返回值为。那么我们输入的密码就应该和第二层的值相等,通过访问内存地址,就可以得到隐藏关的密码:
也就是。