TARDIS
TARDIS copied to clipboard
Design decision for multiple ABI support
Here's a function I was messing around with that would make supporting multiple ABIs easier.
int read_syacall_arg(pid_t pid, void * dst, size_t len, struct user_regs_struct * uregs, int arg_idx) {
// basically the core definition of Linux's ABI
// https://blog.packagecloud.io/eng/2016/04/05/the-definitive-guide-to-linux-system-calls/
#ifdef __x86_64__
switch (arg_idx) {
case 0: read_block(pid, dst, uregs->rdi, len); break;
case 1: read_block(pid, dst, uregs->rsi, len); break;
case 2: read_block(pid, dst, uregs->rdx, len); break;
case 3: read_block(pid, dst, uregs->rcx, len); break;
case 4: read_block(pid, dst, uregs->r8, len); break;
case 5: read_block(pid, dst, uregs->r9, len); break;
default: return 1; // 6 arguments maximum per syscall
}
return 0;
#else
#ifdef __i386__
switch (arg_idx) {
case 0: read_block(pid, dst, uregs->ebx, len); break;
case 1: read_block(pid, dst, uregs->ecx, len); break;
case 2: read_block(pid, dst, uregs->edx, len); break;
case 3: read_block(pid, dst, uregs->esi, len); break;
case 4: read_block(pid, dst, uregs->edi, len); break;
case 5: read_block(pid, dst, uregs->ebp, len); break;
default: return 1; // 6 arguments maximum per syscall
}
return 0;
//add more architectures here
#endif
#endif
return 2; //Unsupported architecture
}
Then, I would replace all other instances of read_block
in tardis.c with this new read_syscall_arg
(and similar for write_block
). For example, before_nanosleep
would be rewritten as
void before_nanosleep(pid_t pid, struct user_regs_struct * uregs) {
struct timespec ts;
read_syscall_arg(pid, &ts, sizeof(struct timespec), uregs, 0);
scale_timespec(&ts, 1.0/delayfactor, 0);
write_syscall_arg(pid, &ts, sizeof(struct timespec), uregs, 0);
}
P.S. I think I would need to use runtime ABI detection, not macros, because x86_64 CPUs can run 32-bit x86 binaries.
I'd definitely be open to adding multi-arch support, although I'll need to have a sit down and think about the design first (It's been a while since I've looked at this project)
I think you're right about the runtime arch detection, and if I remember correctly there's already some code present that checks if the target process is 64-bit.
if I remember correctly there's already some code present that checks if the target process is 64-bit.
Yeah, I'm not sure that is64bit
works as intended. When I tested it, it report 64-bit for 32-bit processes on my 64-bit machine. Here's another way to test, which works as expected for me. I wrote a modified is64bit
function using my new method which you might see in an upcoming PR.
After experimenting with my prototype a bit longer, it has become clear to me that TARDIS needs to be recompiled as 32 bit to work with 32 bit binaries (there are ways around doing so, but they aren't worth it). Syscalls have different numbers on the different platforms (including x86 vs x86_64)
I'm not sure that is64bit works as intended.
Just to get the record straight, the original version of is64bit worked as intended. The reason I thought otherwise is when running tardis compiled as 64-bit on a 32-bit program, I called is64bit
when the first execve
was interrupted by ptrace. The execve hadn't completed, so is64bit was always returning true (more accurately matching how tardis was compiled).