二进制炸弹(ICS作业)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/FZHvampire/article/details/83961565

**

p h a s e 1 phase_1

**
密码:All your base are belong to us.
在这里插入图片描述
通过观察 p h a s e 1 phase_1 的汇编代码可以发现, s t r i n g s n o t e q u a l strings_not_equal 函数应该为比较输入密码和标准密码的。所以前一个 l e a lea 操作应该为将标准密码取出放到寄存器 %rsi 中。这样大致思路就为在 g d b gdb 中的 p h a s e 1 phase_1 位置设置一个断点,然后用 s i si 操作单步执行到 c a l l q callq 操作时,将寄存器 %rsi 取出即为正确密码。取出值得过程可以先用 i n f o r e g i s t e r r s i info register rsi 查看寄存器的地址和存储的值,再用 x / s x/s 地址将寄存器中的值以字符串的形式输出出来。
在这里插入图片描述
通过 p h a s e 1 phase_1 已经基本熟悉了逆向的过程。
**

p h a s e 2 phase_2

**
密码:0 1 1 2 3 5
在这里插入图片描述
+ 9 +9 行的汇编语言中,可以看出读入了一个输入的 6 6 位密码,接下来就应当是比较的过程。
+ 14 +14 行中首先将 0 0 与栈顶指针所存的数进行比较,如果不一样则炸弹爆炸,可以判断出第一位密码应当是 0 0
+ 20 +20 接着比较了 1 1 与栈顶的第二个元素,相同跳转,不同就会直接爆炸,所以可以推测 1 1 是第二位的密码。
接着程序跳转到了 + 32 +32 ,将栈顶指针保存到了4%rbx 中,又操作了一步之后跳到了 +50$。
+ 50 +50 的操作是将当前栈顶的第二个元素也就是1赋给eax。
+ 53 +53 是将当前栈顶元素也就是 0 0 加到 e a x eax 上,随后再将 e a x eax 与标准密码进行比较,之后再不断重复这个循环。
可以找到规律,初始两位密码为 0 0 1 1 ,随后的第i位密码就是 i 1 i-1 i 2 i-2 位的和,所以算出密码为:0 1 1 2 3 5
**

p h a s e 3 phase_3

**
密码:
1 471
2 735
3 349
4 469
5 942
6 192
7 551
其中的任意一组
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
+ 26 +26 那一行可以看出访问了一个 s s c a n f sscanf 函数,通过第三张截图中访问的第一个而第二个寄存器可以看出,第一个参数保存的是读入的一串字符串,第二个参数是 %d %d 可见程序希望读入两个整数,也就是第三个密码应该为两个整数。然后 %rdx %rcx 两个寄存器的值就是存这两个整数的。
+ 31 +31 的比较函数要求读入的返回值大于一,也就是要读入至少两个数,与上文的意思相符。
+ 36 +36 比较了7和读入的第一个数的大小,和下面的超过跳转,可以看出是要求第一位密码的范围应当是小于等于 7 7 的整数。
再向下一直到 + 61 +61 之前一直是处理 %rax 中的值,然后就是 + 61 +61 的条件跳转。可以发现这个条件用到了 %rax 中的值,调用了 %rax*4 的偏移量,可以发现是根据读入的第一个数来跳转的。
接着下面有7组差不多的命令,都是先赋值以后跳到 + 136 +136 ,可见这些加上 + 61 +61 的间接跳转应该是一个 s w i t c h switch 语句。用第一个密码来选择。
+ 136 +136 比较了 %eax 中的值与第二位密码是否相等,而 %eax 中的值从 s w i t c h switch 中被赋过值了,所以 s w i t c h switch 语句中也就蕴含着与第一位密码相应的答案。共有 7 7 组。
**

p h a s e 4 phase_4

**
密码:3 10
在这里插入图片描述
在这里插入图片描述
根据 p h a s e 3 phase_3 的做法,先输出 s s c a n f sscanf %rsi ,发现 p h a s e 4 phase_4 的密码也是要输入两个整数。接下来就分别用 x y x,y 表示两位密码。
+ 34 +34 + 41 +41 可以看出x应该是一个小于等于 14 14 的整数。
+ 62 +62 行中代码进入到了了个函数中,同时分别将 ( x , 0 , 14 ) (x,0,14) 作为参数传入到了函数当中。
第二个截图为 f u n c 4 func4 函数的指令。接下来用 ( a , b , c ) (a,b,c) 分别表示传入的三个参数。
+ 1 +1 + 3 +3 c b c-b 保存到了 %eax 中。 + 5 +5 %eax 的数存到了 %ebx 中。
+ 7 +7 这个操作将 %ebx 中的数逻辑右移了 31 31 位。这个操作在 %ebx 中的数位正数的情况下,就等价与清零操作,可以先将其看作清零操作。
接下来就是将 %eax 的值赋给 %ebx ,然后将 %ebx 中的值除以 2 2 ,再加上 b b
接下来将 %ebx 中的值与 a a 进行比较,如果大于 a a ,就将 c c 赋成 %ebx 中的数 1 -1 ,继续执行 f u n c 4 func4 函数进行递归。如果小于 a a 就将 b b 赋成 %ebx 中的数 + 1 +1 继续进行 f u n c 4 func4 函数进行递归。递归结束后将 %ebx 的数加上递归的返回值然后作为这一层的返回值。如果 a a 等于 %ebx 中的值,就直接返回 %ebx 中的值。
递归完之后回到 p h a s e 4 phase_4 中去, + 67 +67 要求返回值为 10 10 + 72 +72 要求 y y 与返回值 10 10 相等,所以可以得到第二个密码值为 10 10 ,而第一个密码为递归函数中的第一个参数,根据描述,写出相应的递归程序后如下:
在这里插入图片描述
在这里插入图片描述
通过试验测得,当 d f s dfs 的第一个参数为 3 3 时,返回值为 10 10 ,所以密码为 3 3 10 10 .
**

p h a s e 5 phase_5

**
密码: j d o e f g jdoefg
在这里插入图片描述
+ 13 +13 这一行可以看出读入的面貌是一个 6 6 位字符串。 + 18 +18 %eax 清零。
然后通过观察整段程序,发现程序可以分成两个部分,第一部分是 + 30 +30 + 53 +53 之间,是一个循环。第二段是 + 55 +55 + 72 +72 是判断部分。
由于第一部分比较复杂,所以先看第二部分,第二部分是将地址为 0 x 9 ( 0x9(%rsp) 开始的字符串与 %rsi 中的进行比较。可以看到 %rsi 中的字符串为如下的 6 6 位字符。鱼就是目标字符串为 o i l e r s oilers
在这里插入图片描述
接下来看循环中的部分。
+ 23 +23 首先将一个常数赋给了 %rcx ,通过和面的地址可以看到 %rcx 中存的:
在这里插入图片描述
+ 45 +45 + 53 +53 可以看出来这个循环是变量 %rax 0 0 5 5 的一个循环。
+ 30 +30 + 34 +34 这两行进行的操作为将 %rbx 的数加上 %rax 的和作为地址,读取其中的数赋给 %edx 。而 %rbx 是我们读入的字符串, %rbx 中存的地址就是字符串开头字母的地址,又因为 %rax 是从 0 0 5 5 的,所以在每次循环中分别取出了输入字符串的第 0 0 5 5 位赋给了 %eax 。然后 + 34 +34 取出了 %eax 的后四位。
+ 37 +37 行访问了 %rcx %rdx 保存的地址的和作为地址的地址。因为 %rcx 中存储的也为字符串,所以可以看成是以 %rcx 存储的字符串的首字符作为基地址, %rdx 存的量为偏移量来访问后面的第 %rdx 个字符。
+ 41 +41 行将 %dl 中的值存储到对应的 + 60 +60 行要比较的字符串的第 %rax 位。
通过上面的分析可以看出,第i次循环要得到 o i l e r s oilers 中的第 i i 个字符,而这个字符是通过访问 %rcx 字符串中的字符找到的,可以计算出,每次循环 + 37 +37 行访问时的偏移量分别为 a , 4 , f , 5 , 6 , 7 a,4,f,5,6,7 这样分别代表了 m m 后面的第 a a 个字符 o o ,第 4 4 i i 以此类推。
在这里插入图片描述

而第 i i 个偏移量的获得是通过度日的字符串的第 i i 位对应的 A S C I I ASCII 码取后四位得到的。所以可以得到输入的 6 6 位字符串的后四位分别为 a , 4 , f , 5 , 6 , 7 a,4,f,5,6,7 。然后在补上高位 6 6 ,即获得了正确密码。
p h a s e 6 phase_6
密码:5 6 2 1 4 3
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
+ 0 +0 + 18 +18 是读入部分,通过输出 %r12 中存的数可以发现, 0 x 30 ( 0x30(%rsp) 中保存的是读入的第一个数的地址。输入的六位密码就用a[i]来表示。
接下来程序可以分为以下几个部分: + 29 +29 + 82 +82 + 84 +84 + 152 +152 + 154 +154 到最后。
首先 + 29 +29 + 82 +82 是一个单层循环, %r13d 中使循环变量,从 1 1 6 6 遍历了读入的六个元素,判断每个元素的是是否都小于等于 6 6 且互不相等。这也就要求了这个六位密码是 1 6 1-6 的全排列中的一个。
+ 84 +84 + 152 +152 是一个两层循环, %rsi 中存的是一个外层循环的循环变量,这一层循环从 1 1 6 6 遍历了一遍读入的六个数据。 %eax 中保存了第 2 2 重循环的循环变量。可以发现第二层循环的次数位第i位密码的值。而第二陈循环中主要涉及了 %rdx %rsp 这两个寄存器。首先来看一下 %rdx
在这里插入图片描述
这个是 %rdx 的初始化,是从内存中读取了一个常数。接下来对于外层的第i次循环,内层的循环进行了¥a[i] ! [ ] ( h t t p s : / / i m g b l o g . c s d n i m g . c n / 20181111201712424. p n g ) 次如下操作: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20181111201712424.png) 因为 %rdx 中存的是一个地址,所以可以看到 %rdx 向后偏移了 a[i]$次,也就是说程序中保存了 6 6 个常数,对于第i次外层循环,内层循环取出了第 a [ i ] a[i] 个常数。
在这里插入图片描述
这条语句依次将取出的元素保存在了栈中。
然后是最后一个部分,首先进行了一个这样的操作:
在这里插入图片描述
这段代码将栈上一部分保存在栈中的元素建立了一个链表,顺序就是,第一个放入的元素指向下一个放入的元素(这一段开始比较难以理解)。
在这里插入图片描述
这是程序的最后阶段了,这一段在刚刚建好的链表中从前到后的比较了量表中相邻两个元素的大小。也就是第一个元素小于第二个元素想,第二个小于第三个,以此类推,也就是这六个元素要是从小到大排列好的。

现在这个程序的作用也就比较清晰了,程序按照我们输入的六个数的顺序依次取出了系统中保存的六个常数,要求取出的这六个常数要按照从小到大的顺序排好,也就是说我们输入的六位密码的第i位代表的应该是,六个常数中排名第 i i 小的元素是第几个。那只需要看一下这六个元素依次是什么,这个问题就解决了。
在这里插入图片描述
在这里插入图片描述
输出程序中的六个常数的大小,即可得到密码:5 6 2 1 4 3
**

s e c r e t p h a s e secret-phase

**
密码:22
在这里插入图片描述
程序中只有phase_defused这个函数还没有看过内容,所以隐藏关可能就宝运载整函数中。函数中开始就有一个比较,通过在程序中跟踪比较的那个地址发现,那个地址就是破解的炸弹的个数,也就是说,只有破解了六个炸弹后才能进入下面的隐藏部分。
在这里插入图片描述
通过这个值发现,要输入两个整数和一个字符串来进入隐藏关,而 + 37 +37 中首先传入了一个参数,通过输出发现正好是第四个炸弹的密码,所以字符串应该是跟在第四个炸弹后面的。
+ 88 +88 中调用了字符串是否相等的函数,那么根据传入的那个字符串的地址,就可以知道进入隐藏关的密码了。
在这里插入图片描述
在这里插入图片描述

进入到隐藏关后,发现隐藏关的密码是通过read_line读入的,也就是一个字符串。 + 19 +19 中调用了一个系统函数 s t r t o l strtol ,通过查询,发现这个代码的功能是将字符串转换成相应的数字。所以 + 24 +24 %rax 总就保存的将读入字符串传化为数字后的值。接下来的两行限制了这个数字的大小。 + 24 +24 + 35 +35 这段代码限制了隐藏关的密码要小于 0 x 3 e 8 0x3e8
再向下看发现 + 46 +46 中调用了函数 f u n 7 fun7 ,假设传入的两个变量为 ( x , y ) (x,y) y y 就是我们读入的隐藏关的密码, x x 是一个指针,通过访问内存地址,发现 x x 的值为:在这里插入图片描述
在这里插入图片描述
通过 f u n 7 fun7 的代码,发现 f u n 7 fun7 是一个递归函数。
首先当 x = 0 x=0 的时候,函数返回 1 -1 。然后递归函数先在 + 11 +11 分成了两个部分,第一个部分是 x > y x>y 的情况,第二个是 x < = y x<=y 的情况。
x > y x>y 时,跳转到了 + 29 +29 ,将下一次递归的指针x指向的地址加了 0 x 8 0x8 后进入到了下一层递归,然后将返回值乘 2 2 作为这一层递归的返回值。
x < = y x<=y 的时候,又分成了两个部分, x = y x=y x < y x<y
x = y x=y 的时候,程序不再进行下一层递归,直接返回 0 0
x < y x<y 的时候,跳转到了 + 42 +42 ,将下一次递归的指针 x x 指向的地址增加了 0 x 10 0x10 后进入到下一层递归,然后将返回值乘 2 2 1 1 作为这一层的返回值。
观察secre_phase的 + 51 +51 + 54 +54 发现,隐藏关要求函数 f u n 7 fun7 的返回值为 2 2 ,所以回想整个递归过程,最深层的递归返回值一定为 0 0 ,如果想得到 2 2 ,需要将 0 0 先乘 2 2 1 1 变成 1 1 ,再乘 2 2 变成 2 2 ,也就是说最开始应该先执行 x > y x>y ,在下一层执行 x < y x<y ,最深的一层为 x = y x=y ,这样就可以让 f u n 7 fun7 的返回值为 2 2 。那么我们输入的密码就应该和第二层的 x x 值相等,通过访问内存地址,就可以得到隐藏关的密码:在这里插入图片描述
也就是 22 22

猜你喜欢

转载自blog.csdn.net/FZHvampire/article/details/83961565