mirror of
https://gitlab.com/niansa/execontrol.git
synced 2025-03-06 20:48:26 +01:00
Initial commit
This commit is contained in:
commit
037d77caff
3 changed files with 156 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
CMakeLists.txt.user*
|
5
CMakeLists.txt
Normal file
5
CMakeLists.txt
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
|
project(execontrol LANGUAGES C)
|
||||||
|
|
||||||
|
add_executable(execontrol main.c)
|
150
main.c
Normal file
150
main.c
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sys/ptrace.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/user.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
|
||||||
|
#define run_checked(name, ...) check_ret(#name "(" #__VA_ARGS__ ")", name(__VA_ARGS__))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
long check_ret(const char *fnc, long value) {
|
||||||
|
if (value < 0) {
|
||||||
|
perror(fnc);
|
||||||
|
exit(-errno);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t read_string(char *buffer, size_t buffer_len, int pid, unsigned long long addr) {
|
||||||
|
// Read string
|
||||||
|
size_t string_len;
|
||||||
|
for (string_len = 0; string_len != buffer_len-1; string_len++) {
|
||||||
|
buffer[string_len] = ptrace(PTRACE_PEEKDATA, pid, addr+string_len);
|
||||||
|
if (buffer[string_len] == '\0') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (buffer[string_len] < 0) {
|
||||||
|
buffer[string_len] = '\0';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer[buffer_len-1] = '\0';
|
||||||
|
return string_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void run(int pid, int *sync) {
|
||||||
|
// Start ptrace
|
||||||
|
run_checked(ptrace, PTRACE_SEIZE, pid, 0, 0);
|
||||||
|
run_checked(ptrace, PTRACE_INTERRUPT, pid, 0, 0);
|
||||||
|
run_checked(waitpid, pid, 0, 0);
|
||||||
|
run_checked(ptrace, PTRACE_SETOPTIONS, pid, 0, PTRACE_O_EXITKILL);
|
||||||
|
// Run
|
||||||
|
int lastsig = 0;
|
||||||
|
char syscall_result = 0;
|
||||||
|
for (;;) {
|
||||||
|
int status;
|
||||||
|
// Wait for next event
|
||||||
|
run_checked(ptrace, PTRACE_SYSCALL, pid, 0, lastsig);
|
||||||
|
run_checked(waitpid, pid, &status, 0);
|
||||||
|
// Handle exit
|
||||||
|
if (WIFEXITED(status)) {
|
||||||
|
exit(WEXITSTATUS(status));
|
||||||
|
}
|
||||||
|
// Handle signals
|
||||||
|
if (WIFSTOPPED(status)) {
|
||||||
|
lastsig = WSTOPSIG(status);
|
||||||
|
// Handle trap
|
||||||
|
if (lastsig == SIGTRAP) {
|
||||||
|
lastsig = 0;
|
||||||
|
// Must be a syscall; handle it
|
||||||
|
struct user_regs_struct regs;
|
||||||
|
run_checked(ptrace, PTRACE_GETREGS, pid, 0, ®s);
|
||||||
|
switch (regs.orig_rax) {
|
||||||
|
case SYS_execve: {
|
||||||
|
if (!syscall_result) {
|
||||||
|
// Read filename
|
||||||
|
char filename[1024];
|
||||||
|
read_string(filename, sizeof(filename), pid, regs.rdi);
|
||||||
|
// Check filename
|
||||||
|
if (strstr(filename, "python")) {
|
||||||
|
regs.orig_rax = -1;
|
||||||
|
regs.rax = -ECANCELED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case SYS_clone: {
|
||||||
|
if (syscall_result) {
|
||||||
|
// Attach to it
|
||||||
|
int npid = regs.rax;
|
||||||
|
run_checked(write, sync[1], &npid, sizeof(npid));
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case SYS_execveat: {
|
||||||
|
regs.orig_rax = -1;
|
||||||
|
regs.rax = -ENOTSUP;
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
// Toggle syscall_result flag
|
||||||
|
syscall_result = !syscall_result;
|
||||||
|
run_checked(ptrace, PTRACE_SETREGS, pid, 0, ®s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void kill_parent() {
|
||||||
|
kill(getppid(), SIGINT);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
// Check arguments
|
||||||
|
if (argc < 2) {
|
||||||
|
fprintf(stderr, "Usage: %s <pipe> <executable> [args...]\n", argv[0]);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
// Get arguments
|
||||||
|
char *control_pipe_path= argv[1],
|
||||||
|
*executable = argv[2],
|
||||||
|
**executable_argv = argv+2;
|
||||||
|
// Create control pipe
|
||||||
|
mkfifo(control_pipe_path, 0600);
|
||||||
|
int control_pipe = run_checked(open, control_pipe_path, O_RDWR);
|
||||||
|
// Create synchronization buffer
|
||||||
|
int sync[2];
|
||||||
|
run_checked(pipe, sync);
|
||||||
|
char syncbuf[1];
|
||||||
|
// Start process
|
||||||
|
pid_t pid = fork();
|
||||||
|
if (pid == 0) {
|
||||||
|
run_checked(write, sync[1], syncbuf, sizeof(syncbuf));
|
||||||
|
// Run given program if not init, else run init
|
||||||
|
execvp(executable, executable_argv);
|
||||||
|
perror(executable);
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
run_checked(read, sync[0], syncbuf, sizeof(syncbuf));
|
||||||
|
// Run
|
||||||
|
if (fork() == 0) {
|
||||||
|
atexit(kill_parent);
|
||||||
|
run(pid, sync);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
// Attach to all further processes
|
||||||
|
for(;;) {
|
||||||
|
int npid;
|
||||||
|
run_checked(read, sync[0], &npid, sizeof(npid));
|
||||||
|
if (fork() == 0) {
|
||||||
|
run(npid, sync);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue