UOJ Logo riteme的博客

博客

交互题中如何防止选手程序在stdin/stdout上搞事情

2017-01-11 09:28:49 By riteme

最近有人到我们学校的OJ来给我们传授人生的经验,告诉我们如何使用C那一套强大的函数和GCC的一些黑科技来实现从stdin直接读入数据。就目前为止,我所了解的Hack手段和防治措施是这样的:

  • 定义类或结构体作为Reader,在Reader的构造函数中写代码,从而在main函数前动手。

交互库也使用一个类或结构体的Reader来读入即可。GCC默认的构造顺序是编译单元的编译顺序,因此需要在编译命令里面,交互库的文件要在选手文件之前。C++11中static全局变量保证在main函数之前构造,在C++11之前并没有定义,但是一般的编译器基本都保证了这一点。

  • 利用GCC扩展init_priority来修改构造顺序,使得选手的Reader比交互库的Reader先构造。

init_priority在GCC和Clang上都有用......

init_priority的声明方法是这样的:

TYPE NAME __attribute__ ((init_priority(n)));

其中$n$必须是正整数,且在$[101, \;65535]$范围内。权值越小优先级越高,也就意味着会在优先级低的变量之前构造。

因此,我们需要将交互库的Reader的优先级设为$101$

需要注意一个事情,假如你在使用cin来读入,那么在读入之前,cin/cout是还没有被构造的,所以需要在Reader前面使用下面的语句来提前初始化cin/cout

static std::ios_base::Init iosinit __attribute__ ((init_priority(101)));

直接使用C的I/O的不会出事。另外一点就是non-POD类型也需要提前构造(如std::stringstd::map和你的其他类和结构体等),在Reader之前定义并且设置优先级。

  • 使用rewind/fseekstdin重新移至开头并读取。

对于,我们所要做的,就是在交互库读取完数据之后,stdin关掉。如果想更好玩一些,可以打开个/dev/urandom给它读:

freopen("/dev/urandom", "r", stdin);
  • 暴力试出token长度从而利用fseek来修改交互库输出。

类似的,交互库其实并不需要token,交互库的token似乎并没有任何卵用,因为利用fseek能够绕过,setbuf也可以偷到......

为此我们可以在Readerstdout也给换掉(换成NULL或者/dev/null给它玩),然后交互库输出时又换回来即可。 也可以使用Unix中的dupdup2来保存stdout。 交互库输出完毕后需要stdout干掉

  • 使用dup复制输入输出句柄,用fdopen重新打开stdin/stdout

充分利用了Unix那套文件操作的强大。对于此,我们需要自己使用dupstdin/stdout复制一份,然后使用close关闭原来的输入输出。大致的代码如下:

#include <unistd.h>  // dup, close, fdopen, STDIN_FILENO, STDOUT_FILENO

#define MAGIC 103  // 随便选个不是很大的数字

class IO {
 public:
    IO() {
        // 读取数据...

        fclose(stdin);
        close(STDIN_FILENO);

        for (int i = 0; i < MAGIC; i++) {
            dup(STDERR_FILENO);  // 防止stdout_copy变为3
        }

        stdouot_copy = dup(STDOUT_FILENO);
        fclose(stdout);
        close(STDOUT_FILENO);
    }

    ~IO() {
        stdout = fdopen(stdout_copy, "w");

        // 输出结果...

        fclose(stdout);
        close(stdout_copy);
    }

 private:
     int stdout_copy;
};

static IO io __attribute__ ((init_priority(101)));

总结一下就是,在没有加密的输入输出中,使用全局变量的构造函数来抢先获得stdin/stdout的控制权,避免选手程序搞事。

此文可能会不定期更新。

话说是不是数据加密就可以一劳永逸了?但数据加密后如何清真地支持Hack?求dalao帮助。

介绍一个速度相当快的线性时间构造后缀数组的算法

2016-06-19 21:34:01 By riteme

新博客

2016-02-07 12:32:58 By riteme
riteme Avatar