├── .gitignore ├── COPYING ├── FileCompare.cpp ├── Makefile ├── README ├── caretaker.c ├── exit_code.h ├── syscall_listener.c └── syscall_listener.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | test* 3 | .* 4 | *~ 5 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (C) 2012, Hexcles Ma 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /FileCompare.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * FileCompare.cpp 3 | * 4 | * This file is part of Eevee. 5 | * 6 | * Copyright (C) 2012 Hexcles Ma 7 | * 8 | * Eevee is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Eevee is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Eevee. If not, see . 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #define EX_SAME 1 30 | #define EX_DIFF 0 31 | #define STRICT_MODE 1 32 | #define NORMAL_MODE 0 33 | #define BUFF_SIZE 65536 34 | using namespace std; 35 | 36 | int MODE = NORMAL_MODE; 37 | char *filename1 = NULL, *filename2 = NULL; 38 | char buf1[BUFF_SIZE], buf2[BUFF_SIZE]; 39 | string str1, str2; 40 | 41 | void printusage() { 42 | puts("FileCompare v0.2"); 43 | puts("Usage:\tFileCompare StdFile UsrFile [--mode {strict|normal}]"); 44 | puts("若两文件一致返回值为1,否则返回为0,发生错误返回BSD标准错误代码。"); 45 | puts(""); 46 | puts("--mode\t默认为normal"); 47 | puts("\tnormal\t正常模式,忽略行末空格和文末回车。"); 48 | puts("\tstrict\t严格模式,判断是否两文件严格一致。"); 49 | puts("--help\t显示此帮助"); 50 | } 51 | 52 | inline void trim(string &str) { 53 | while (str.length() > 0 && *(str.end() - 1) == ' ') str.erase(str.end() - 1); 54 | } 55 | 56 | int normal_compare() { 57 | //compare file size 58 | struct stat s1, s2; 59 | stat(filename1, &s1); 60 | stat(filename2, &s2); 61 | if (s2.st_size < s1.st_size / 2 || s2.st_size > s1.st_size * 2) return EX_DIFF; 62 | //compare file content 63 | ifstream fin1(filename1, ifstream::in), fin2(filename2, ifstream::in); 64 | fin1.sync_with_stdio(false); 65 | fin2.sync_with_stdio(false); 66 | str1.reserve(BUFF_SIZE); 67 | str2.reserve(BUFF_SIZE); 68 | while (fin1.good() || fin2.good()) { 69 | if (fin1.good()) { 70 | getline(fin1, str1); 71 | trim(str1); 72 | } else str1 = ""; 73 | if (fin2.good()) { 74 | getline(fin2, str2); 75 | trim(str2); 76 | } else str2 = ""; 77 | if (str1 != str2){ 78 | cout << str1 << endl; 79 | cout << str2 << endl; 80 | return EX_DIFF; 81 | } 82 | } 83 | return EX_SAME; 84 | } 85 | 86 | int strict_compare() { 87 | //compare file size 88 | size_t len; 89 | struct stat s1, s2; 90 | stat(filename1, &s1); 91 | stat(filename2, &s2); 92 | if (s1.st_size != s2.st_size) return EX_DIFF; 93 | //compare file content 94 | FILE *fp1, *fp2; 95 | fp1 = fopen(filename1, "rb"); 96 | fp2 = fopen(filename2, "rb"); 97 | while (!feof(fp1)) { 98 | len = fread(buf1, 1, sizeof(buf1), fp1); 99 | fread(buf2, 1, sizeof(buf2), fp2); 100 | if (memcmp(buf1, buf2, len)) return EX_DIFF; 101 | } 102 | return EX_SAME; 103 | } 104 | 105 | int main(int argc, char *argv[]) { 106 | //Process the argument 107 | if (argc != 2 && argc != 3 && argc != 5) { 108 | printusage(); 109 | return EX_USAGE; 110 | } 111 | for (int i = 1; i < argc; ++ i) { 112 | if (!strcmp(argv[i], "--mode")) { 113 | if (i == argc - 1) { 114 | printusage(); 115 | return EX_USAGE; 116 | } 117 | ++ i; 118 | if (!strcmp(argv[i], "strict")) MODE = STRICT_MODE; 119 | else if(!strcmp(argv[i], "normal")) MODE = NORMAL_MODE; 120 | else { 121 | printusage(); 122 | return EX_USAGE; 123 | } 124 | } else if (!strcmp(argv[i], "--help")){ 125 | printusage(); 126 | return EX_USAGE; 127 | } else { 128 | if (filename1) filename2 = argv[i]; 129 | else filename1 = argv[i]; 130 | } 131 | } 132 | if (!filename1 || !filename2) { 133 | printusage(); 134 | return EX_USAGE; 135 | } 136 | if (access(filename1, R_OK) || access(filename2, R_OK)) { 137 | fprintf(stderr, "Error: can not open file.\n"); 138 | return EX_NOINPUT; 139 | } 140 | //Do compare 141 | switch (MODE) { 142 | case NORMAL_MODE: return normal_compare(); 143 | case STRICT_MODE: return strict_compare(); 144 | } 145 | return EX_USAGE; 146 | } 147 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS=-O2 -Wall 3 | 4 | .PHONY: all 5 | all: caretaker 6 | 7 | caretaker: caretaker.o syscall_listener.o 8 | $(CC) $(CFLAGS) caretaker.o syscall_listener.o -o caretaker 9 | 10 | caretaker.o: caretaker.c exit_code.h syscall_listener.h 11 | $(CC) $(CFLAGS) -c caretaker.c 12 | 13 | syscall_listener.o: syscall_listener.c exit_code.h syscall_listener.h 14 | $(CC) $(CFLAGS) -c syscall_listener.c 15 | 16 | .PHONY: clean 17 | clean: 18 | rm *.o caretaker 19 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Eevee is a local OI(and perhaps ACM in the future) judger for Linux. 2 | 3 | It is orignially designed to be used in Vulpix - the Online Judge, but 4 | tend to be standalone. 5 | 6 | Eevee contains two main parts: 7 | 1. caretaker - the program runner. A simple sandbox using setuid, 8 | chroot, setrlimit, etc. 9 | - module for limiting system call added by LX(http://lix.li). 10 | 2. comparer - the result judger. A simple file comparer with OI style 11 | (ignoring blanks on the line end and file end). 12 | 13 | (More things are in plan.) 14 | 15 | Eevee is free software, written by Hexcles Ma (http://robotshell.org). 16 | -------------------------------------------------------------------------------- /caretaker.c: -------------------------------------------------------------------------------- 1 | /* 2 | * caretaker.c 3 | * 4 | * This file is part of Eevee. 5 | * 6 | * Copyright (C) 2012, Hexcles Ma 7 | * All rights reserved. 8 | * 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted provided that the following conditions are 11 | * met: 12 | * 13 | * * Redistributions of source code must retain the above copyright 14 | * notice, this list of conditions and the following disclaimer. 15 | * * Redistributions in binary form must reproduce the above 16 | * copyright notice, this list of conditions and the following disclaimer 17 | * in the documentation and/or other materials provided with the 18 | * distribution. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | 50 | #include "exit_code.h" 51 | #include "syscall_listener.h" 52 | 53 | /* option list */ 54 | static const char *optString = "t:m:i:o:h"; 55 | static const struct option longOpts[] = { 56 | {"time", required_argument, NULL, 't'}, 57 | {"memory", required_argument, NULL, 'm'}, 58 | {"input", required_argument, NULL, 'i'}, 59 | {"output", required_argument, NULL, 'o'}, 60 | {"help", no_argument, NULL, 'h'}, 61 | { NULL, no_argument, NULL, 0 } 62 | }; 63 | 64 | /* global options and default values */ 65 | char *program_invocation_name; 66 | char *infileName = NULL, *outfileName = NULL, *prgfileName = NULL; 67 | int timeLimit = 1000, memoryLimit = 131072, result = EX_SUCCESS, timeout_killed; 68 | 69 | uid_t parent_uid, child_uid; 70 | gid_t parent_gid, child_gid; 71 | pid_t child_pid; 72 | char tmpdirTemplate[] = "/tmp/EeveeTMP.XXXXXX", *tmpdirName; 73 | 74 | char *path_cat(const char *path, char *file); 75 | inline int tv2ms(struct timeval tv); 76 | void timeout(); 77 | void print_usage(); 78 | void apply_rlimit(int resource, int limit); 79 | void parse_opt(int argc, char * const argv[]); 80 | void init_env(); 81 | int watch_prg(); 82 | void clean_env(); 83 | 84 | inline int tv2ms(struct timeval tv) { 85 | return (int) (tv.tv_usec / 1000) + tv.tv_sec * 1000; 86 | } 87 | 88 | void timeout() { 89 | if (child_pid > 0) kill(child_pid, SIGKILL); 90 | timeout_killed = 1; 91 | alarm(0); 92 | } 93 | 94 | char *path_cat(const char *path, char *file) { 95 | size_t path_len = strlen(path), file_len = strlen(file); 96 | char *result; 97 | result = malloc((path_len + file_len + 2) * sizeof(*path)); 98 | strcpy(result, path); 99 | result[path_len] = '/'; 100 | strcpy(result + path_len + 1, file); 101 | //result[path_len + 1 + file_len] = '\0'; 102 | return result; 103 | } 104 | 105 | void print_usage() { 106 | printf("Usage: %s [OPTION] PROGRAM \n", program_invocation_name); 107 | printf("Run and watch the contestant's PROGRAM. (Part of the Eeevee)\n"); 108 | printf( 109 | "Options:\n" 110 | " -t, --time=TIME_LIMIT in ms, positive int only (default is 1000)\n" 111 | " -m, --memory=MEMORY_LIMIT in KB, positive int only (default is 131072)\n" 112 | " -i, --input=INPUT_FILE must in the same directory as PROGRAM\n" 113 | " (file name must be identical with the problem description)\n" 114 | " -o, --output=OUTPUT_FILENAME the NAME of output file (should NOT exist!)\n" 115 | " -h, --help print this help\n\n" 116 | "Output:\n" 117 | " 1.exited: WEXITSTATUS TIME(ms) MEMORY(KB)\n" 118 | " 2.killed: message\n" 119 | "Notes: PROGRAM must be compiled statically!\n"); 120 | exit(EX_SUCCESS); 121 | } 122 | 123 | void apply_rlimit(int resource, int limit) { 124 | struct rlimit lim; 125 | lim.rlim_cur = lim.rlim_max = limit; 126 | if (setrlimit(resource, &lim) != 0) 127 | error(EX_INTER, 0, "Error setting rlimit."); 128 | } 129 | 130 | void parse_opt(int argc, char * const argv[]) { 131 | int longIndex = 0, opt = 0; 132 | opt = getopt_long(argc, argv, optString, longOpts, &longIndex); 133 | while (opt != -1) { 134 | switch (opt) { 135 | case 't': 136 | timeLimit = atoi(optarg); 137 | if (timeLimit <= 0) error(EX_ERROR, 0, 138 | "TIME_LIMIT must be a positive integer."); 139 | break; 140 | 141 | case 'm': 142 | memoryLimit = atoi(optarg); 143 | if (memoryLimit <= 0) error(EX_ERROR, 0, 144 | "MEMORY_LIMIT must be a positive integer."); 145 | break; 146 | 147 | case 'i': 148 | if (optarg == 0) error(EX_ERROR, 0, "INPUT_FILE missing."); 149 | infileName = optarg; 150 | break; 151 | 152 | case 'o': 153 | if (optarg == 0) error(EX_ERROR, 0, "OUTPUT_FILE missing."); 154 | outfileName = optarg; 155 | break; 156 | 157 | case 'h': 158 | print_usage(); 159 | break; 160 | 161 | default: 162 | error(EX_ERROR, 0, 163 | "Please run 'caretaker --help' for more information."); 164 | break; 165 | } 166 | opt = getopt_long(argc, argv, optString, longOpts, &longIndex); 167 | } 168 | if (optind == argc) error(EX_ERROR, 0, "PROGRAM not specified."); 169 | else prgfileName = argv[optind]; 170 | //printf("%d\n%d\n%s\n%s\n%s\n", timeLimit, memoryLimit, infileName, outfileName, prgfileName); 171 | } 172 | 173 | void init_env(){ 174 | parent_uid = geteuid(); 175 | parent_gid = getegid(); 176 | if (parent_uid != 0) 177 | error(EX_FATAL, 0, "Must be run as root."); 178 | struct passwd *nobody = getpwnam("nobody"); 179 | if (nobody == NULL) 180 | error(EX_FATAL, 0, "Cannot find user 'nobody'."); 181 | child_uid = nobody->pw_uid; 182 | child_gid = nobody->pw_gid; 183 | umask(0); 184 | tmpdirName = mkdtemp(tmpdirTemplate); 185 | if (tmpdirName == NULL) 186 | error(EX_FATAL, 0, "Error create temp directory."); 187 | 188 | char *buffer = malloc(strlen(tmpdirName) + strlen(prgfileName) + 10); 189 | sprintf(buffer, "cp %s %s", prgfileName, tmpdirName); 190 | if (system(buffer) != 0) { 191 | clean_env(); 192 | error(EX_ERROR, 0, "PROGRAM not exist."); 193 | } 194 | if (infileName != NULL) { 195 | buffer = realloc(buffer, strlen(tmpdirName) + strlen(infileName) + 10); 196 | sprintf(buffer, "cp %s %s", infileName, tmpdirName); 197 | if (system(buffer) != 0) { 198 | clean_env(); 199 | error(EX_ERROR, 0, "INPUT_FILE not exist.\n"); 200 | } 201 | } 202 | free(buffer); 203 | 204 | //chown(tmpdirName, parent_uid, parent_gid); 205 | chmod(tmpdirName, 00711); 206 | char *tmppath; 207 | 208 | tmppath = path_cat(tmpdirName, prgfileName); 209 | chown(tmppath, child_uid, child_gid); 210 | chmod(tmppath, 00555); 211 | free(tmppath); 212 | 213 | if (infileName != NULL) { 214 | tmppath = path_cat(tmpdirName, infileName); 215 | chown(infileName, parent_uid, parent_gid); 216 | chmod(infileName, 00644); 217 | free(tmppath); 218 | } 219 | 220 | if (outfileName != NULL) { 221 | tmppath = path_cat(tmpdirName, outfileName); 222 | int tmpof = open(tmppath, O_WRONLY | O_CREAT | O_TRUNC, 00666); 223 | close(tmpof); 224 | free(tmppath); 225 | } 226 | } 227 | 228 | int watch_prg(){ 229 | int status; 230 | struct rusage usage; 231 | char *envs[] = { NULL }, *args[] = { prgfileName, NULL }; 232 | child_pid = fork(); 233 | if (child_pid == -1) { 234 | fprintf(stderr, "Error forking.\n"); 235 | return EX_FATAL; 236 | } 237 | if (child_pid == 0) { 238 | //child process 239 | if (chroot(tmpdirName) != 0) 240 | error(EX_INTER, 0, "Error chroot."); 241 | chdir("/"); 242 | int olderr = dup(STDERR_FILENO); 243 | int null = open("/dev/null", O_WRONLY), zero = open("/dev/zero", O_RDONLY); 244 | dup2(zero, STDIN_FILENO); 245 | dup2(null, STDOUT_FILENO); 246 | dup2(null, STDERR_FILENO); 247 | apply_rlimit(RLIMIT_CPU, (int) (timeLimit + 1000) / 1000); //in seconds 248 | apply_rlimit(RLIMIT_AS, (memoryLimit + 10240) * 1024); //in bytes 249 | apply_rlimit(RLIMIT_NOFILE, 10); //one greater than max file number permitted 250 | setgid(child_gid); 251 | setuid(child_uid); 252 | if ((geteuid() != child_uid) || (getegid() != child_gid)) 253 | error(EX_INTER, 0, "Error setting uid/gid."); 254 | listen_me(); //init ptrace_me 255 | execve(prgfileName, args, envs); 256 | dup2(olderr, STDERR_FILENO); 257 | error(EX_INTER, 0, "Error executing(forgot to link statically?)."); 258 | } else { 259 | //parent process 260 | signal(SIGALRM, timeout); 261 | alarm((int) (timeLimit + 2000) / 1000); 262 | long memory_max = 0; 263 | while(1) { //listening 264 | wait3(&status, WUNTRACED, &usage); 265 | int st = parse_status(status); 266 | int time = tv2ms(usage.ru_utime) + tv2ms(usage.ru_stime); 267 | long memory_now = usage.ru_minflt * (getpagesize() >> 10); 268 | if (memory_now > memory_max) 269 | memory_max = memory_now; 270 | if ((time > timeLimit) || timeout_killed) { 271 | printf("Time Limit Exceeded\n"); 272 | ptrace(PTRACE_KILL, child_pid, NULL, NULL); 273 | return EX_TLE; 274 | } 275 | if (memory_max > memoryLimit) { 276 | printf("Memory Limit Exceeded\n"); 277 | ptrace(PTRACE_KILL, child_pid, NULL, NULL); 278 | return EX_MLE; 279 | } 280 | if (st >= 0) { //exited 281 | printf("%d %dms %ldKiB\n", WEXITSTATUS(status), time, memory_max); 282 | return st; 283 | } 284 | if (st == EX_YOYOCHECKNOW) { 285 | check_call(child_pid); 286 | } 287 | listen_again(child_pid); 288 | } 289 | } 290 | return EX_SUCCESS; 291 | } 292 | 293 | void clean_env(){ 294 | char *buffer; 295 | if (outfileName != NULL) { 296 | char *tmppath = path_cat(tmpdirName, outfileName); 297 | buffer = malloc(strlen(tmppath) + 10); 298 | sprintf(buffer, "cp -p %s .", tmppath); 299 | system(buffer); 300 | free(tmppath); 301 | } else buffer = malloc(strlen(tmpdirName) + 10); 302 | sprintf(buffer, "rm -rf %s", tmpdirName); 303 | system(buffer); 304 | free(buffer); 305 | } 306 | 307 | int main(int argc, char *argv[]){ 308 | program_invocation_name = argv[0]; 309 | parse_opt(argc, argv); 310 | init_env(); 311 | result = watch_prg(); 312 | clean_env(); 313 | return result; 314 | } 315 | -------------------------------------------------------------------------------- /exit_code.h: -------------------------------------------------------------------------------- 1 | /* 2 | * exit code: 3 | * 0: success (both the runner and program) 4 | * ---------------runner--------------- 5 | * 1: invalid options/command line format error or file not exists 6 | * 2: internal fatal (chroot, setuid, etc.) 7 | * (details in stderr) 8 | * --------------program--------------- 9 | * 251: Time Limit Exceeded 10 | * 252: Memory Limit Exceeded 11 | * 253: Runtime Error (with return code in stdout, if any) 12 | */ 13 | #ifndef __EXIT_CODE_H 14 | #define __EXIT_CODE_H 15 | 16 | #define EX_SUCCESS 0 17 | #define EX_ERROR 1 18 | #define EX_FATAL 2 19 | #define EX_INTER 250 20 | #define EX_TLE 251 21 | #define EX_MLE 252 22 | #define EX_RE 253 23 | 24 | /* 25 | * Internal exit code 26 | * Passed to main program(caretaker) by syscall_listener 27 | */ 28 | #define EX_NOTEND (-255) 29 | #define EX_YOYOCHECKNOW (-127) 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /syscall_listener.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "exit_code.h" 15 | #include "syscall_listener.h" 16 | 17 | int call_count[512]={0}; 18 | 19 | void listen_me(); 20 | void listen_again(); 21 | int parse_status(int); 22 | void check_call(pid_t); 23 | //definitions begin at line 300 24 | 25 | const int syscall_limit[]= 26 | { 27 | 0, 0, -1, //fork 28 | 0, 0, -1, 29 | -1, -1, -1, //creat 30 | -1, -1, 1, //exec 31 | -1, 0, -1, //mknot 32 | 33 | -1, -1, -1, //break 34 | -1, 0, -1, 35 | -1, -1, -1, 36 | -1, -1, -1, 37 | -1, -1, -1, 38 | 39 | -1, -1, -1, //gtty 40 | -1, -1, -1, 41 | -1, -1, -1, 42 | -1, -1, -1, 43 | -1, -1, -1, 44 | 45 | 0, -1, -1, //getgid 46 | -1, -1, -1, 47 | -1, -1, -1, 48 | 0, -1, -1, 49 | -1, -1, 0, 50 | 51 | -1, -1, -1, //ustat 52 | -1, -1, -1, 53 | -1, -1, -1, 54 | -1, -1, -1, 55 | -1, -1, -1, 56 | 57 | -1, -1, -1, //getrusage 58 | 0, -1, -1, 59 | -1, -1, -1, 60 | -1, 0, 0, 61 | -1, -1, -1, 62 | 63 | 0, 0, -1, // truncate 64 | -1, -1, -1, 65 | -1, -1, -1, 66 | -1, -1, -1, 67 | -1, -1, -1, 68 | 69 | -1, -1, -1, //lstat 70 | 0, 0, -1, 71 | -1, -1, -1, 72 | -1, -1, -1, 73 | -1, -1, -1, 74 | 75 | -1, -1, 0, //uname 76 | -1, -1, -1, 77 | -1, -1, -1, 78 | -1, -1, -1, 79 | -1, -1, -1, 80 | 81 | -1, -1, -1, //afs_syscall 82 | -1, -1, -1, 83 | -1, -1, -1, 84 | -1, 0, 0, 85 | -1, -1, -1, 86 | 87 | -1, -1, -1, //mlockall 88 | -1, -1, -1, 89 | -1, -1, -1, 90 | -1, -1, -1, 91 | -1, 0, -1, 92 | 93 | -1, -1, -1, //query_module 94 | -1, -1, -1, 95 | -1, -1, -1, 96 | 0, -1, -1, 97 | -1, -1, -1, 98 | 99 | 0, 0, -1, //chown 100 | -1, -1, -1, 101 | -1, -1, -1, 102 | -1, -1, 0, 103 | 0, -1, -1, 104 | 105 | -1, -1, 0, //fstat64 106 | -1, -1, -1, 107 | -1, -1, -1, 108 | -1, -1, -1, 109 | -1, -1, -1, 110 | 111 | -1, -1, -1, //chwon32 112 | -1, -1, -1, 113 | -1, -1, -1, 114 | -1, -1, -1, 115 | 0, 0, -1, 116 | 117 | -1, -1, -1, //lsetxattr 118 | -1, -1, -1, 119 | -1, -1, -1, 120 | -1, -1, -1, 121 | -1, -1, -1, 122 | 123 | -1, -1, -1, //sched_getaffinity 124 | 0, 0, -1, 125 | -1, -1, -1, 126 | -1, -1, 0, 127 | 0, -1, -1, 128 | 129 | -1, -1, -1, //remap_file_pages 130 | -1, -1, -1, 131 | -1, -1, -1, 132 | -1, -1, -1, 133 | -1, -1, -1, 134 | 135 | -1, -1, -1, //fadvise64_64 136 | -1, -1, -1, 137 | -1, -1, -1, 138 | -1, -1, -1, 139 | -1, -1, -1, 140 | 141 | 0, -1, -1, // request_key 142 | -1, -1, -1, 143 | -1, -1, -1, 144 | -1, -1, -1, 145 | -1, -1, -1, 146 | 147 | -1, -1, -1, //renameat 148 | -1, -1, -1, 149 | -1, -1, -1, 150 | -1, -1, -1, 151 | -1, -1, -1, 152 | 153 | -1, -1, -1, //move_pages 154 | -1, -1, -1, 155 | -1, -1, -1, 156 | -1, -1, -1, 157 | -1, -1, -1, 158 | 159 | -1, -1, -1, //inotify_init1 160 | 0, 0, -1, 161 | -1, -1, -1, 162 | -1, -1, -1, 163 | -1, -1, -1, 164 | 165 | -1, -1 166 | }; 167 | 168 | const char* const syscall_list[]= 169 | { 170 | "SYS_restart_syscall" ,"SYS_exit" ,"SYS_fork" , 171 | "SYS_read" ,"SYS_write" ,"SYS_open" , 172 | "SYS_close" ,"SYS_waitpid" ,"SYS_creat" , 173 | "SYS_link" ,"SYS_unlink" ,"SYS_execve" , 174 | "SYS_chdir" ,"SYS_time" ,"SYS_mknod" , 175 | 176 | "SYS_chmod" ,"SYS_lchown" ,"SYS_break" , 177 | "SYS_oldstat" ,"SYS_lseek" ,"SYS_getpid" , 178 | "SYS_mount" ,"SYS_umount" ,"SYS_setuid" , 179 | "SYS_getuid" ,"SYS_stime" ,"SYS_ptrace" , 180 | "SYS_alarm" ,"SYS_oldfstat" ,"SYS_pause" , 181 | 182 | "SYS_utime" ,"SYS_stty" ,"SYS_gtty" , 183 | "SYS_access" ,"SYS_nice" ,"SYS_ftime" , 184 | "SYS_sync" ,"SYS_kill" ,"SYS_rename" , 185 | "SYS_mkdir" ,"SYS_rmdir" ,"SYS_dup" , 186 | "SYS_pipe" ,"SYS_times" ,"SYS_prof" , 187 | 188 | "SYS_brk" ,"SYS_setgid" ,"SYS_getgid" , 189 | "SYS_signal" ,"SYS_geteuid" ,"SYS_getegid" , 190 | "SYS_acct" ,"SYS_umount2" ,"SYS_lock" , 191 | "SYS_ioctl" ,"SYS_fcntl" ,"SYS_mpx" , 192 | "SYS_setpgid" ,"SYS_ulimit" ,"SYS_oldolduname" , 193 | 194 | 195 | "SYS_umask" ,"SYS_chroot" ,"SYS_ustat" , 196 | "SYS_dup2" ,"SYS_getppid" ,"SYS_getpgrp" , 197 | "SYS_setsid" ,"SYS_sigaction" ,"SYS_sgetmask" , 198 | "SYS_ssetmask" ,"SYS_setreuid" ,"SYS_setregid" , 199 | "SYS_sigsuspend" ,"SYS_sigpending" ,"SYS_sethostname" , 200 | 201 | "SYS_setrlimit" ,"SYS_getrlimit" ,"SYS_getrusage" , 202 | "SYS_gettimeofday" ,"SYS_settimeofday" ,"SYS_getgroups" , 203 | "SYS_setgroups" ,"SYS_select" ,"SYS_symlink" , 204 | "SYS_oldlstat" ,"SYS_readlink" ,"SYS_uselib" , 205 | "SYS_swapon" ,"SYS_reboot" ,"SYS_readdir" , 206 | 207 | "SYS_mmap" ,"SYS_munmap" ,"SYS_truncate" , 208 | "SYS_ftruncate" ,"SYS_fchmod" ,"SYS_fchown" , 209 | "SYS_getpriority" ,"SYS_setpriority" ,"SYS_profil" , 210 | "SYS_statfs" ,"SYS_fstatfs" ,"SYS_ioperm" , 211 | "SYS_socketcall" ,"SYS_syslog" ,"SYS_setitimer" , 212 | 213 | "SYS_getitimer" ,"SYS_stat" ,"SYS_lstat" , 214 | "SYS_fstat" ,"SYS_olduname" ,"SYS_iopl" , 215 | "SYS_vhangup" ,"SYS_idle" ,"SYS_vm86old" , 216 | "SYS_wait4" ,"SYS_swapoff" ,"SYS_sysinfo" , 217 | "SYS_ipc" ,"SYS_fsync" ,"SYS_sigreturn" , 218 | 219 | "SYS_clone" ,"SYS_setdomainname" ,"SYS_uname" , 220 | "SYS_modify_ldt" ,"SYS_adjtimex" ,"SYS_mprotect" , 221 | "SYS_sigprocmask" ,"SYS_create_module" ,"SYS_init_module" , 222 | "SYS_delete_module" ,"SYS_get_kernel_syms" ,"SYS_quotactl" , 223 | "SYS_getpgid" ,"SYS_fchdir" ,"SYS_bdflush" , 224 | 225 | "SYS_sysfs" ,"SYS_personality" ,"SYS_afs_syscall" , 226 | "SYS_setfsuid" ,"SYS_setfsgid" ,"SYS__llseek" , 227 | "SYS_getdents" ,"SYS__newselect" ,"SYS_flock" , 228 | "SYS_msync" ,"SYS_readv" ,"SYS_writev" , 229 | "SYS_getsid" ,"SYS_fdatasync" ,"SYS__sysctl" , 230 | 231 | "SYS_mlock" ,"SYS_munlock" ,"SYS_mlockall" , 232 | "SYS_munlockall" ,"SYS_sched_setparam" ,"SYS_sched_getparam" , 233 | "SYS_sched_setscheduler" ,"SYS_sched_getscheduler" ,"SYS_sched_yield" , 234 | "SYS_sched_get_priority_max" ,"SYS_sched_get_priority_min" ,"SYS_sched_rr_get_interval" , 235 | "SYS_nanosleep" ,"SYS_mremap" ,"SYS_setresuid" , 236 | 237 | "SYS_getresuid" ,"SYS_vm86" ,"SYS_query_module" , 238 | "SYS_poll" ,"SYS_nfsservctl" ,"SYS_setresgid" , 239 | "SYS_getresgid" ,"SYS_prctl" ,"SYS_rt_sigreturn" , 240 | "SYS_rt_sigaction" ,"SYS_rt_sigprocmask" ,"SYS_rt_sigpending" , 241 | "SYS_rt_sigtimedwait" ,"SYS_rt_sigqueueinfo" ,"SYS_rt_sigsuspend" , 242 | 243 | "SYS_pread64" ,"SYS_pwrite64" ,"SYS_chown" , 244 | "SYS_getcwd" ,"SYS_capget" ,"SYS_capset" , 245 | "SYS_sigaltstack" ,"SYS_sendfile" ,"SYS_getpmsg" , 246 | "SYS_putpmsg" ,"SYS_vfork" ,"SYS_ugetrlimit" , 247 | "SYS_mmap2" ,"SYS_truncate64" ,"SYS_ftruncate64" , 248 | 249 | "SYS_stat64" ,"SYS_lstat64" ,"SYS_fstat64" , 250 | "SYS_lchown32" ,"SYS_getuid32" ,"SYS_getgid32" , 251 | "SYS_geteuid32" ,"SYS_getegid32" ,"SYS_setreuid32" , 252 | "SYS_setregid32" ,"SYS_getgroups32" ,"SYS_setgroups32" , 253 | "SYS_fchown32" ,"SYS_setresuid32" ,"SYS_getresuid32" , 254 | 255 | "SYS_setresgid32" ,"SYS_getresgid32" ,"SYS_chown32" , 256 | "SYS_setuid32" ,"SYS_setgid32" ,"SYS_setfsuid32" , 257 | "SYS_setfsgid32" ,"SYS_pivot_root" ,"SYS_mincore" , 258 | "SYS_madvise1" ,"SYS_getdents64" ,"SYS_fcntl64" , 259 | "" ,"" ,"SYS_gettid" , 260 | 261 | "SYS_readahead" ,"SYS_setxattr" ,"SYS_lsetxattr" , 262 | "SYS_fsetxattr" ,"SYS_getxattr" ,"SYS_lgetxattr" , 263 | "SYS_fgetxattr" ,"SYS_listxattr" ,"SYS_llistxattr" , 264 | "SYS_flistxattr" ,"SYS_removexattr" ,"SYS_lremovexattr" , 265 | "SYS_fremovexattr" ,"SYS_tkill" ,"SYS_sendfile64" , 266 | 267 | "SYS_futex" ,"SYS_sched_setaffinity" ,"SYS_sched_getaffinity" , 268 | "SYS_set_thread_area" ,"SYS_get_thread_area" ,"SYS_io_setup" , 269 | "SYS_io_destroy" ,"SYS_io_getevents" ,"SYS_io_submit" , 270 | "SYS_io_cancel" ,"SYS_fadvise64" ,"" , 271 | "SYS_exit_group" ,"SYS_lookup_dcookie" ,"SYS_epoll_create" , 272 | 273 | "SYS_epoll_ctl" ,"SYS_epoll_wait" ,"SYS_remap_file_pages" , 274 | "SYS_set_tid_address" ,"SYS_timer_create" ,"SYS_timer_settime" , 275 | "SYS_timer_gettime" ,"SYS_timer_getoverrun" ,"SYS_timer_delete" , 276 | "SYS_clock_settime" ,"SYS_clock_gettime" ,"SYS_clock_getres" , 277 | "SYS_clock_nanosleep" ,"SYS_statfs64" ,"SYS_fstatfs64" , 278 | 279 | "SYS_tgkill" ,"SYS_utimes" ,"SYS_fadvise64_64" , 280 | "SYS_vserver" ,"SYS_mbind" ,"SYS_get_mempolicy" , 281 | "SYS_set_mempolicy" ,"SYS_mq_open" ,"SYS_mq_unlink" , 282 | "SYS_mq_timedsend" ,"SYS_mq_timedreceive" ,"SYS_mq_notify" , 283 | "SYS_mq_getsetattr" ,"SYS_kexec_load" ,"SYS_waitid" , 284 | 285 | "" ,"SYS_add_key" ,"SYS_request_key" , 286 | "SYS_keyctl" ,"SYS_ioprio_set" ,"SYS_ioprio_get" , 287 | "SYS_inotify_init" ,"SYS_inotify_add_watch" ,"SYS_inotify_rm_watch" , 288 | "SYS_migrate_pages" ,"SYS_openat" ,"SYS_mkdirat" , 289 | "SYS_mknodat" ,"SYS_fchownat" ,"SYS_futimesat" , 290 | 291 | "SYS_fstatat64" ,"SYS_unlinkat" ,"SYS_renameat" , 292 | "SYS_linkat" ,"SYS_symlinkat" ,"SYS_readlinkat" , 293 | "SYS_fchmodat" ,"SYS_faccessat" ,"SYS_pselect6" , 294 | "SYS_ppoll" ,"SYS_unshare" ,"SYS_set_robust_list" , 295 | "SYS_get_robust_list" ,"SYS_splice" ,"SYS_sync_file_range" , 296 | 297 | "SYS_tee" ,"SYS_vmsplice" ,"SYS_move_pages" , 298 | "SYS_getcpu" ,"SYS_epoll_pwait" ,"SYS_utimensat" , 299 | "SYS_signalfd" ,"SYS_timerfd_create" ,"SYS_eventfd" , 300 | "SYS_fallocate" ,"SYS_timerfd_settime" ,"SYS_timerfd_gettime" , 301 | "SYS_signalfd4" ,"SYS_eventfd2" ,"SYS_epoll_create1" , 302 | 303 | "SYS_dup3" ,"SYS_pipe2" ,"SYS_inotify_init1" , 304 | "SYS_preadv" ,"SYS_pwritev" ,"SYS_rt_tgsigqueueinfo" , 305 | "SYS_perf_event_open" ,"SYS_recvmmsg" ,"SYS_fanotify_init" , 306 | "SYS_fanotify_mark" ,"SYS_prlimit64" ,"SYS_name_to_handle_at" , 307 | "SYS_open_by_handle_at" ,"SYS_clock_adjtime" ,"SYS_syncfs" , 308 | 309 | "SYS_sendmmsg" ,"SYS_setns" 310 | }; 311 | 312 | void listen_me() 313 | { 314 | if (ptrace(PTRACE_TRACEME, 0, NULL, NULL)==-1) 315 | error(EX_INTER, 0, "Error initiating ptrace"); 316 | } 317 | 318 | void listen_again(pid_t child_pid) 319 | { 320 | if (ptrace(PTRACE_SYSCALL, child_pid, NULL, NULL)==-1) 321 | error(EX_INTER, 0, "Error ptracsing child"); 322 | } 323 | 324 | 325 | void check_call(pid_t child_pid) 326 | { 327 | #ifdef __x86_64__ 328 | int id = ptrace(PTRACE_PEEKUSER, child_pid, (void*)(8*ORIG_RAX), NULL); 329 | #else 330 | int id = ptrace(PTRACE_PEEKUSER, child_pid, (void*)(4*ORIG_EAX), NULL); 331 | #endif 332 | if (id==-1) 333 | error(EX_INTER, 0, "Error peeking user"); 334 | call_count[id]++; 335 | if ((call_count[id]&1) == 0) 336 | return; 337 | 338 | printf("Calling %-16s\t(id:%-4d)\t\n", syscall_list[id], id); 339 | if (syscall_limit[id] == 0) //no limits 340 | return; 341 | if (call_count[id] > syscall_limit[id]) 342 | { 343 | ptrace(PTRACE_KILL, child_pid, NULL, NULL); 344 | error(EX_INTER, 0, "forbidden operation"); 345 | } 346 | } 347 | 348 | int parse_status(int status) 349 | { 350 | if (WIFEXITED(status)) //exited 351 | { 352 | if (WEXITSTATUS(status) == 0) 353 | return EX_SUCCESS; 354 | else 355 | return EX_RE; 356 | } 357 | 358 | if (WIFSIGNALED(status)) 359 | return EX_FATAL; 360 | 361 | if (WIFSTOPPED(status)) 362 | { 363 | int sig = WSTOPSIG(status); 364 | switch (sig) 365 | { 366 | case SIGUSR1: //regular check 367 | break; //just a place holder 368 | case SIGTRAP: //ptrace got an syscall (to call or has returned) 369 | return EX_YOYOCHECKNOW; 370 | case SIGXFSZ: //write too much to file 371 | return EX_RE;//actually, it should be EX_OLE 372 | default: 373 | return EX_RE; 374 | } 375 | } 376 | return EX_NOTEND; 377 | } 378 | 379 | -------------------------------------------------------------------------------- /syscall_listener.h: -------------------------------------------------------------------------------- 1 | extern void listen_me(); 2 | extern void listen_again(pid_t child_pid); 3 | extern void check_call(pid_t child_pid); 4 | extern int parse_status(int status); 5 | --------------------------------------------------------------------------------