反编译之后可以看出,程序初始化时将自己的代码段设置为了可写。
main 函数中,程序读入一个地址 addr 和一个整数 bit,然后把 addr 地址的第 bit 位翻转,然后程序退出。
我们只能翻转一个 bit,但只翻转一个 bit 不足以让我们获得任意命令执行的能力。所以这道题的关键在于,我们要利用翻转的第一个 bit 来创造出一个循环,使得我们可以翻转更多的 bit。可以翻转更多 bit 之后,我们就可以把 shellcode 写入代码段中,然后想办法跳转过去。
至于如何创造出循环,懒人做法是在代码段的地址范围内穷举所有地址,直到找到一个翻转后程序可以第二次读取我们输入(而不是崩溃或者正常退出)的位置。
我并没有去穷举,而是直接看汇编。
0x401295 处是一个 call 指令,调用了 exit()
函数退出程序,如果让它不要退出,就可以到达输出 "Invalid input"
的地方,然后跳转到输入的地方,形成循环。
这个 call 指令调用了 0x4010c0 这个函数,字节码是 E8 26 FE FF FF
,如果翻转 26
的第 6 个 bit,就可以把字节码变成 E8 66 FE FF FF
,即跳转到 0x401100,这里的逻辑是直接返回。
然后,我们利用这个循环来不断把 shellcode 覆盖 0x4010c0 也就是原来的 exit()
函数。
最后,我们把之前翻转那个 bit 改回去,程序跳转到 0x4010c0,我们的 shellcode 会被执行,就可以拿到 shell 了。