标题:闲谈 Restricted Function #2 出处:Felix021 时间:Tue, 02 Nov 2010 13:36:22 +0000 作者:felix021 地址:https://www.felix021.com/blog/read.php?1950 内容: 上一篇谈到:“典型的情况是某些非法内存访问,Glibc会open("/dev/tty",...),write()一些错误信息,然后open("/proc/self/maps", ...)把进程的内存映射表输出。还有一个更常见的情况,那就是用qsort。GCC的qsort实现,会主动open(/proc/meminfo),获取一些信息,通过这些信息来最优化排序时的内存管理。” 通过执行strace ./a.out 2>&1 | grep open 可以看到进程执行的open系统调用。 对于这类情况,ptrace监控进程open系统调用,并获取文件名,检查文件名是否合法,如果合法,予以放行,这样就可以避免上述RE被误判为RF的情况了。 说起来很简单,但是具体做起来就有点麻烦。不过可以从我写woj-land的时候参考的ptrace教程《Playing with ptrace, 玩转ptrace》入手。这篇非常不错,强烈推荐有兴趣的同学学习学习: Playing with ptrace, Part I — 玩转ptrace(一) http://www.kgdb.info/gdb/playing_with_ptrace_part_i/ Playing with ptrace, Part II — 玩转ptrace(二) http://www.kgdb.info/gdb/playing_with_ptrace_part_ii/ 上篇以write系统调用为例,介绍了write系统调用的等价汇编码(当然是早期的了,现在的pc上linux貌似不用0x80了)、截获write系统调用,获取write系统调用的参数和返回值、甚至反转write系统调用的输出。具体的看原文了。 在这里,我需要peek的是read系统调用的参数。以x86为例,一个最简单的read系统调用:read("/proc/meminfo", O_RDONLY) ,大致等同于movl $5, %eax ;open的系统调用编号 movl $filename, %ebx ;文件名地址(这里不一定对) movl $0, %ecx ;open的flags: O_RDONLY int $0x80 也就是说,可以通过获取x86 CPU寄存器的值来取得系统调用的参数,具体代码如下:user_regs_struct regs; ptrace(PTRACE_GETREGS, child, NULL, ®s); syscall_id = regs.orig_eax; //因为eax也是用来填写函数返回值的,所以该struct额外增加了一个orig_eax params[0] = regs.ebx; params[1] = regs.ecx; union u{ unsigned long val; char chars[sizeof(long)]; }data; data.val = ptrace(PTRACE_PEEKDATA, child, params[0], NULL); 注意:虽然ebx中存放的是地址,但是不是用户程序可以直接读取的,需要通过ptrace的PTRACE_PEEKDATA命令来获取。该命令一次可以获取sizeof(long)个字节的内容,如果要获取字符串的所有内容,就得通过循环来完成。 此外,为了保证代码能够在x86_64架构下通过编译,需要把上述代码做个小修改:#if __WORDSIZE == 32 syscall_id = regs.orig_eax; params[0] = regs.ebx; params[1] = regs.ecx; #else syscall_id = regs.orig_rax; params[0] = regs.rdi; params[1] = regs.rsi; #endif 通过这种方式,能够识别出open系统调用打开的文件,在保证系统安全的情况下,改善了Judge的识别能力。 更具体的代码可以参见: http://code.google.com/p/woj-land/source/browse/trunk/code/judge/judge.h?spec=svn150&r=150#671 Generated by Bo-blog 2.1.0