第五章:
level2: 用file查看文件类型,解压后得到的二进制文件能够直接执行,输出一个数字,每次运行都不同,且如果两次命令间隔时间很短,输出内容一样,猜测用时间来随机化种子。(time(NULL)的参数空指针传递时,发现用了xor %edx,%edx,这是一种很常见的设置0方法)我们进一步在时间间隔很短时用带参数/不带参数执行,结果一样,因此大概率参数并不影响命令执行结果(不过也有可能是在刚开始判断,过滤掉了)
进一步反汇编,发现并没有明显提示flag的地方。那么最可能的情况是flag隐藏在栈中。而我们分析核心代码可知,在向puts传递参数之前,有mov 0x601060(,%rax,8),%rdx,这很可疑,因为这里直接调用了栈中的值,我们用gdb运行,打印这里的值,就是flag.
level3: 使用readelf之后提示检查文件头超过了文件结尾,并且发现文件头的起始偏移位置是一个非常大的数字,不符合实际,改成64
反汇编之后,很奇怪的一点:没有text段。根据ltrace追踪可以知道文件调用了md5sum来分析自身,很可能对文件进行正确的修改之后输出的md5值就是flag.
我们进一步寻找可疑的点,Machine类型是Motorola,查阅资料可知这是大段法的机器,将其改回来。这样以后,能够正常运行,但是flag不正确。
下一步尝试修改OS/ABI,它在e_ident成员中,但是结果仍然不对。
最后一个我真的没想到,查阅了其他人写的博客,发现需要修改text段的标志。经过objdump之后,text段的Type为NOBITS,需要修改。通过节头表的起始位置、text段的标号、每个节头元素的大小,我们可以确定text段的位置是4480+14*64,但是我们要修改的是第二个成员sh_type,所以再加上4,就是我们最终要修改的位置,将其修改成1即可。
期间对readelf掌握更加熟练,-l输出程序头,-S输出节头 (-s输出符号表,不要弄混);对于程序如何通过elf头部一步步寻找到节头表,进一步定位节头有了更深刻的认识。
另外,积累的新方法:通过比较笨拙的方法修改二进制文件的某个字节,编写一个c语言程序,用wb+方式打开,再用fseek定位,fwrite写,要写的内容放在一个字节数组buffer中传给fwrite即可。
level4:直接ltrace追踪发现其设置了环境变量flag,直接就能得到结果
level5:
程序开始并不一定是从text的起始。需要看elf头中的Enter Point,可能先到某一个地方,在调用main_start()
熟悉一种常见的跳转方式: jmp *0x200b64(%rip),相对下一条指令0x200b64处跳转.
自己在使用strings检查的时候,发现了key=.....和decryptedd flag=....之类的话,经过寻找,发现他们位于根本不会执行的地方。我们如果不能够很方便地跳转到这些地方,考虑暴力改变寄存器的位置。在gdb中,p $rdi可以打印出%rdi的值,但这个值是十进制的,使用p/x来用16进制输出。另外,set var $rdi=0x400620可以改变寄存器的值。我们不应该直接改变elf中的程序入口点,因为程序主体执行之前需要一些初始化操作。我们应该在lib_start之类的函数前改变其参数,这个函数是用来跳转到%rdi寄存器所指向的地方来正式开始程序,我们修改%rdi即可。但是这时输出有一些问题,经过跟踪发现有些位置并不是打印字符,因此printf("%s")并没有打印出来。而考虑到前几题的flag都是16个16进制数,而这一次使用%s打印的一共有32字节,所以很可能正确结果就是%s的输出结果,只是我们的解密过程错了(这一点我没想到)。看了别人写的博客,还需要将一个key=0x400520(原来的入口点)转化为新的入口点0x400620。启示:对加密解密过程的算法,即编程者的意图要进行更加深入的分析。
另外,本题中key=%08x和decrypted flag=等字符串都放在.rodata段,距离代码段很近。而在运行过程中的%rsp指向的值则可以达到0x7fffffffdd60,相差还是很远的。我们可以用gdb中的x打印栈和rodata段中的值