Repository: DavidBuchanan314/TARDIS
Branch: master
Commit: 5560d6497e2e
Files: 6
Total size: 10.2 KB
Directory structure:
gitextract_75bsxhu7/
├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── novdso.c
└── tardis.c
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
*.so
tardis
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2017 David Buchanan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: Makefile
================================================
all: tardis novdso.so
tardis: tardis.c
gcc tardis.c -o tardis -lm -Wall -Ofast -std=c99 -DPID_MAX=$(shell cat /proc/sys/kernel/pid_max)
novdso.so: novdso.c
gcc -std=c99 -Wall -fPIC -shared -o novdso.so novdso.c
clean:
rm -f tardis novdso.so
================================================
FILE: README.md
================================================
# TARDIS
Trace And Rewrite Delays In Syscalls: Hooking time-related Linux syscalls to warp a process's perspective of time.
This code is rather buggy, mainly due to my lack of understanding of the ptrace API.
You probably shouldn't use it for anything serious, although it could be useful for
testing/debugging certain applications.
## Things to try:
```
$ ./tardis 10000 10000 xclock
$ ./tardis 1 3 glxgears
$ ./tardis 1 -1 glxgears
$ ./tardis 10 10 firefox
$ ./tardis 10 10 /bin/sh
```

## Notes:
- Currently only x86_64 Linux is supported. It should be possible to port to i386 with fairly minimal effort.
- I used `PTRACE_SEIZE`, which only exists since kernel version 3.4.
- `novdso.so` is preloaded to prevent libc from using vDSO - otherwise `ptrace(PTRACE_SYSCALL, ...)`
wouldn't work for those syscalls (Take a look at `man vdso` for more information). You might need to
modify the `LD_PRELOAD` value to be an absolute path for some programs/environments, I only made it
relative for simplicity.
- Certain simple programs, like `glxgears`, don't mind being run with time flowing in reverse! Most programs don't however, and of course there's no way to have a negative delay.
- There are many more syscalls that I still need to handle.
Currently handled syscalls:
- `nanosleep`
- `clock_nanosleep`
- `select`
- `poll`
- `gettimeofday`
- `clock_gettime`
- `time`
================================================
FILE: novdso.c
================================================
#define _GNU_SOURCE
#include <unistd.h>
#include <sys/syscall.h>
// this is a simple shim to disable vDSO use in libc
int clock_gettime(void * clk_id, void * tp) {
return syscall(SYS_clock_gettime, clk_id, tp);
}
int gettimeofday(void * tv, void * tz) {
return syscall(SYS_gettimeofday, tv, tz);
}
int time(void * tloc) {
return syscall(SYS_time, tloc);
}
int nanosleep(const struct timespec *req, struct timespec *rem) {
return syscall(SYS_nanosleep,req,rem);
}
================================================
FILE: tardis.c
================================================
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <wait.h>
#include <time.h>
#include <sys/ptrace.h>
#include <elf.h>
#include <sys/user.h>
#include <sys/uio.h>
#include <sys/syscall.h>
#include <math.h>
#include <stdbool.h>
#define MICROSECONDS 1000000
#define NANOSECONDS (MICROSECONDS*1000)
#define NUM_SYSCALLS 512 // this is higher than the real number, but shouldn't matter
#ifndef PID_MAX
#define PID_MAX 32768 // XXX: assumption
#endif
#define NUM_CLKIDS 16 // XXX: incorrect, but "works" anyway
double starttimes[NUM_CLKIDS], delayfactor, timefactor;
bool leavesys[PID_MAX];
void (*before_handlers[NUM_SYSCALLS])(pid_t, struct user_regs_struct *);
void (*after_handlers[NUM_SYSCALLS])(pid_t, struct user_regs_struct *);
int is64bit(pid_t pid) {
struct user_regs_struct x64regs;
struct iovec iov = {
.iov_base = &x64regs,
.iov_len = sizeof(x64regs)
};
ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov);
return iov.iov_len == sizeof(x64regs);
}
void read_block(pid_t pid, void * dst, void * src, size_t len) {
for (size_t i = 0; i < len; i += sizeof(void *)) {
*(void **)(dst + i) = (void *)ptrace(PTRACE_PEEKDATA, pid, src + i, NULL); // XXX
}
}
void write_block(pid_t pid, void * src, void * dst, size_t len) {
for (size_t i = 0; i < len; i += sizeof(void *)) {
ptrace(PTRACE_POKEDATA, pid, dst + i, *(void **)(src + i)); // XXX
}
}
void scale_timespec(struct timespec * ts, double factor, double starttime) {
double time = ts->tv_sec + (double)ts->tv_nsec / NANOSECONDS;
if (starttime != 0) {
time = starttime + (time - starttime) * factor;
} else {
time *= factor;
}
ts->tv_sec = time;
ts->tv_nsec = fmod(time, 1) * NANOSECONDS;
}
void scale_timeval(struct timeval * tv, double factor, double starttime) {
double time = tv->tv_sec + (double)tv->tv_usec / MICROSECONDS;
if (starttime != 0) {
time = starttime + (time - starttime) * factor;
} else {
time *= factor;
}
tv->tv_sec = time;
tv->tv_usec = fmod(time, 1) * MICROSECONDS;
}
/* pre-syscall handlers */
void before_nanosleep(pid_t pid, struct user_regs_struct * uregs) {
struct timespec ts;
read_block(pid, &ts, (void *)uregs->rdi, sizeof(struct timespec));
scale_timespec(&ts, 1.0/delayfactor, 0);
write_block(pid, &ts, (void *)uregs->rdi, sizeof(struct timespec));
}
void before_poll(pid_t pid, struct user_regs_struct * uregs) {
int timeout = uregs->rdx & 0xFFFFFFFF; // isolate edx
if (timeout > 0) {
uregs->rdx = timeout / delayfactor; // not sure if this behaves the way I want
}
ptrace(PTRACE_SETREGS, pid, 0, uregs);
}
void before_select(pid_t pid, struct user_regs_struct * uregs) {
if (uregs->r8 != 0) {
struct timeval tv;
read_block(pid, &tv, (void *)uregs->r8, sizeof(struct timeval));
scale_timeval(&tv, 1.0/delayfactor, 0);
write_block(pid, &tv, (void *)uregs->r8, sizeof(struct timeval));
}
}
void before_clock_nanosleep(pid_t pid, struct user_regs_struct * uregs) {
struct timespec rqtp;
read_block(pid, &rqtp, (void *)uregs->rdx, sizeof(struct timespec));
scale_timespec(&rqtp, 1.0/delayfactor, 0);
write_block(pid, &rqtp, (void *)uregs->rdx, sizeof(struct timespec));
}
/* post-syscall handlers */
void after_gettimeofday(pid_t pid, struct user_regs_struct * uregs) {
struct timeval tv;
read_block(pid, &tv, (void *)uregs->rdi, sizeof(struct timeval));
scale_timeval(&tv, timefactor, starttimes[CLOCK_REALTIME]);
write_block(pid, &tv, (void *)uregs->rdi, sizeof(struct timeval));
}
void after_clock_gettime(pid_t pid, struct user_regs_struct * uregs) {
struct timespec ts;
read_block(pid, &ts, (void *)uregs->rsi, sizeof(struct timespec));
scale_timespec(&ts, timefactor, starttimes[uregs->rdi]); // FIXME check bounds
write_block(pid, &ts, (void *)uregs->rsi, sizeof(struct timespec));
}
void after_time(pid_t pid, struct user_regs_struct * uregs) {
uregs->rdi = starttimes[CLOCK_REALTIME] + (uregs->rdi - starttimes[CLOCK_REALTIME]) * timefactor;
ptrace(PTRACE_SETREGS, pid, 0, uregs);
}
void after_clock_nanosleep(pid_t pid, struct user_regs_struct * uregs) {
struct timespec rmtp;
read_block(pid, &rmtp, (void *)uregs->rcx, sizeof(struct timespec));
scale_timespec(&rmtp, 1.0/delayfactor, 0);
write_block(pid, &rmtp, (void *)uregs->rcx, sizeof(struct timespec));
}
int main(int argc, char *argv[], char *envp[]) {
if (argc < 3) {
printf("USAGE: %s DELAY_FACTOR TIME_FACTOR COMMAND [ARGS]...\n", argv[0]);
exit(EXIT_FAILURE);
}
delayfactor = strtod(argv[1], NULL);
timefactor = strtod(argv[2], NULL);
before_handlers[SYS_nanosleep] = before_nanosleep;
before_handlers[SYS_poll] = before_poll;
before_handlers[SYS_select] = before_select;
before_handlers[SYS_clock_nanosleep] = before_clock_nanosleep;
after_handlers[SYS_gettimeofday] = after_gettimeofday;
after_handlers[SYS_clock_gettime] = after_clock_gettime;
after_handlers[SYS_time] = after_time;
after_handlers[SYS_clock_nanosleep] = after_clock_nanosleep;
struct timespec sts;
for (clockid_t id = 0; id < NUM_CLKIDS; id++) {
clock_gettime(id, &sts); // sometimes id will be invalid, but it shouldn't matter
starttimes[id] = sts.tv_sec + (double)sts.tv_nsec / NANOSECONDS;
}
pid_t child = fork();
if(child == 0) {
/* child */
envp[0] = "LD_PRELOAD=./novdso.so"; // FIXME: Do something more sensible
kill(getpid(), SIGSTOP);
execvpe(argv[3], &argv[3], envp);
perror("execvpe"); // execvpe only returns on error
exit(-1);
}
#ifdef DEBUG
fprintf(stderr, "Child spawned with PID %d\n", child);
#endif
ptrace(PTRACE_SEIZE, child, 0, PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXEC | PTRACE_O_EXITKILL | PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE);
wait(NULL); // wait for SIGSTOP to happen
ptrace(PTRACE_SYSCALL, child, 0, 0); // continue execution
if (is64bit(child)) {
#ifdef DEBUG
fprintf(stderr, "Child is 64-bit\n");
#endif
} else {
fprintf(stderr, "ERROR: 32-bit processes are currently unsupported\n");
exit(-1);
}
for (;;) { // TODO: Understand ptrace, simplify code structure
struct user_regs_struct uregs;
int status;
pid_t pid = waitpid(-1, &status, 0);
if (WIFEXITED(status)) {
if (pid == child) {
exit(WEXITSTATUS(status));
} else {
continue;
}
}
if (WIFSTOPPED(status) && WSTOPSIG(status) != SIGTRAP && WSTOPSIG(status) != SIGSTOP) {
if (WSTOPSIG(status) & 0x80) {
// handle syscall
} else {
ptrace(PTRACE_SYSCALL, pid, 0, WSTOPSIG(status));
continue;
}
} else {
ptrace(PTRACE_SYSCALL, pid, 0, 0);
continue;
}
ptrace(PTRACE_GETREGS, pid, 0, &uregs);
if (!leavesys[pid]) {
#ifdef DEBUG
fprintf(stderr, "[pid %d] syscall(%llu)\t0x%016llX 0x%016llX 0x%016llX = ...\n", pid, uregs.orig_rax, uregs.rdi, uregs.rsi, uregs.rdx);
#endif
if (uregs.orig_rax < NUM_SYSCALLS && before_handlers[uregs.orig_rax] != NULL) {
before_handlers[uregs.orig_rax](pid, &uregs);
}
} else {
#ifdef DEBUG
fprintf(stderr, "... 0x%llX\n", uregs.rax);
#endif
if (uregs.orig_rax < NUM_SYSCALLS && after_handlers[uregs.orig_rax] != NULL) {
after_handlers[uregs.orig_rax](pid, &uregs);
}
}
leavesys[pid] ^= true;
ptrace(PTRACE_SYSCALL, pid, 0, 0);
}
}
gitextract_75bsxhu7/ ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── novdso.c └── tardis.c
SYMBOL INDEX (20 symbols across 2 files)
FILE: novdso.c
function clock_gettime (line 7) | int clock_gettime(void * clk_id, void * tp) {
function gettimeofday (line 11) | int gettimeofday(void * tv, void * tz) {
function time (line 15) | int time(void * tloc) {
function nanosleep (line 19) | int nanosleep(const struct timespec *req, struct timespec *rem) {
FILE: tardis.c
type user_regs_struct (line 27) | struct user_regs_struct
type user_regs_struct (line 28) | struct user_regs_struct
function is64bit (line 30) | int is64bit(pid_t pid) {
function read_block (line 41) | void read_block(pid_t pid, void * dst, void * src, size_t len) {
function write_block (line 47) | void write_block(pid_t pid, void * src, void * dst, size_t len) {
function scale_timespec (line 53) | void scale_timespec(struct timespec * ts, double factor, double starttim...
function scale_timeval (line 64) | void scale_timeval(struct timeval * tv, double factor, double starttime) {
function before_nanosleep (line 77) | void before_nanosleep(pid_t pid, struct user_regs_struct * uregs) {
function before_poll (line 84) | void before_poll(pid_t pid, struct user_regs_struct * uregs) {
function before_select (line 92) | void before_select(pid_t pid, struct user_regs_struct * uregs) {
function before_clock_nanosleep (line 101) | void before_clock_nanosleep(pid_t pid, struct user_regs_struct * uregs) {
function after_gettimeofday (line 110) | void after_gettimeofday(pid_t pid, struct user_regs_struct * uregs) {
function after_clock_gettime (line 117) | void after_clock_gettime(pid_t pid, struct user_regs_struct * uregs) {
function after_time (line 124) | void after_time(pid_t pid, struct user_regs_struct * uregs) {
function after_clock_nanosleep (line 129) | void after_clock_nanosleep(pid_t pid, struct user_regs_struct * uregs) {
function main (line 136) | int main(int argc, char *argv[], char *envp[]) {
Condensed preview — 6 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (11K chars).
[
{
"path": ".gitignore",
"chars": 12,
"preview": "*.so\ntardis\n"
},
{
"path": "LICENSE",
"chars": 1071,
"preview": "MIT License\n\nCopyright (c) 2017 David Buchanan\n\nPermission is hereby granted, free of charge, to any person obtaining a "
},
{
"path": "Makefile",
"chars": 247,
"preview": "all: tardis novdso.so\n\ntardis: tardis.c\n\tgcc tardis.c -o tardis -lm -Wall -Ofast -std=c99 -DPID_MAX=$(shell cat /proc/sy"
},
{
"path": "README.md",
"chars": 1430,
"preview": "# TARDIS\n\nTrace And Rewrite Delays In Syscalls: Hooking time-related Linux syscalls to warp a process's perspective of t"
},
{
"path": "novdso.c",
"chars": 479,
"preview": "#define _GNU_SOURCE\n#include <unistd.h>\n#include <sys/syscall.h>\n\n// this is a simple shim to disable vDSO use in libc\n\n"
},
{
"path": "tardis.c",
"chars": 7241,
"preview": "#define _GNU_SOURCE\n\n#include <stdio.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <signal.h>\n#include <wait.h>\n#i"
}
]
About this extraction
This page contains the full source code of the DavidBuchanan314/TARDIS GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 6 files (10.2 KB), approximately 3.3k tokens, and a symbol index with 20 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.