From: sgf Date: Fri, 24 Nov 2023 16:30:56 +0000 (+0300) Subject: new(ptrace): Proof of concept of failed GETREGS. X-Git-Url: https://gitweb.sgf-dma.tk/?a=commitdiff_plain;h=223b9b35eeab2189501d121515f2da3e3e5b7298;p=go.git new(ptrace): Proof of concept of failed GETREGS. --- diff --git a/containers_from_scratch/guide_to_syscalls/traceme.c b/containers_from_scratch/guide_to_syscalls/traceme.c new file mode 100644 index 0000000..a407abf --- /dev/null +++ b/containers_from_scratch/guide_to_syscalls/traceme.c @@ -0,0 +1,122 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv){ + setvbuf(stdout, NULL, _IONBF, 0); + if (argc < 2) { + printf("Too few arguments\n"); + abort(); + } + + pid_t mypid = getpid(); + printf("pidA = %d\n", mypid); + + char *cmd = argv[1]; + char **newargv = malloc(sizeof(char*) * argc); + for (int i = 1; i < argc; i++) { + newargv[i-1] = argv[i]; + } + newargv[argc-1] = NULL; + + pid_t child_pid = fork(); + if (child_pid) { + printf("[%d]: pidB = %d\n", mypid, child_pid); + + if (kill(child_pid, 0) == -1) { + perror("signal right after start: "); + } else { + printf("[%d]: Successfully sent a signal right after start\n", mypid); + } + + struct user_regs_struct regs; + printf("[%d]: Get child registers\n", mypid); + // Following register quire will fail in any case, because child is + // not stopped! + if (ptrace(PTRACE_GETREGS, child_pid, NULL, ®s) == -1) { + perror("PTRACE_GETREGS:"); + } + printf("[%d]: orig_rax = %d\n", mypid, regs.orig_rax); + + printf("[%d]: waiting for child to stop\n", mypid); + int status; + while (waitpid(child_pid, &status, 0)) { + if (WIFEXITED(status)) { + printf("[%d]: Child exited with status %d\n", mypid, WEXITSTATUS(status)); + return 0; + + } else if (WIFSIGNALED(status)) { + int termsig = WTERMSIG(status); + printf("[%d]: Child killed by signal %d\n", mypid, termsig); + return 0; + + } else if (WIFSTOPPED(status)) { + int csig = WSTOPSIG(status); + printf("[%d]: Child is traced and and is about to receive signal %d\n", mypid, csig); + + if (kill(child_pid, 0) == -1) { + perror("signal when stopped: "); + } else { + printf("[%d]: Successfully sent a signal when stopped\n", mypid); + } + + sleep(2); + struct user_regs_struct regs; + printf("[%d]: Get child registers\n", mypid); + // But this register will succeed, because now child is + // stopped. + if (ptrace(PTRACE_GETREGS, child_pid, NULL, ®s) == -1) { + perror("PTRACE_GETREGS:"); + } + printf("[%d]: orig_rax = %d\n", mypid, regs.orig_rax); + + sleep(1); + // Suppress signal. + //if (ptrace(PTRACE_CONT, child_pid, 0, 0) == 1) { + // perror("PTRACE_CONT:"); + //} + if (ptrace(PTRACE_SYSCALL, child_pid, 0, 0) == 1) { + perror("PTRACE_SYSCALL:"); + } + + if (kill(child_pid, 0) == -1) { + perror("signal when running: "); + } else { + printf("[%d]: Successfully sent a signal when running\n", mypid); + } + + + } else if (WIFCONTINUED(status)) { + printf("[%d]: Child continued\n", mypid); + + } else { + printf("[%d]: Child is elsewhere\n", mypid); + } + } + + } else { + mypid = getpid(); + if ((ptrace(PTRACE_TRACEME, 0, NULL, NULL) == -1) == -1) { + perror("PTRACE_TRACEME:"); + } + // Child should normally raise SIGSTOP, so parent has a chance to + // inspect its state through ptrace. + // But go runtime does not raise such signal. + //raise(SIGSTOP); + + printf("[%d]: Sleep after traceme and before execve..\n", mypid); + sleep(6); + printf("[%d]: execve '%s'\n", mypid, cmd); + execve(cmd, newargv, NULL); + perror("execve"); + } + return 0; +} diff --git a/containers_from_scratch/guide_to_syscalls/traceme.go b/containers_from_scratch/guide_to_syscalls/traceme.go new file mode 100644 index 0000000..f252bc1 --- /dev/null +++ b/containers_from_scratch/guide_to_syscalls/traceme.go @@ -0,0 +1,122 @@ + +package main + +import ( + "fmt" + "os" + "os/exec" + "syscall" + "time" +) + +func main() { + mypid := os.Getpid() + fmt.Printf("pidA = %v\n", mypid); + + fmt.Printf("[%v]: Run %v\n", mypid, os.Args[1:]) + + cmd := exec.Command(os.Args[1], os.Args[2:]...) + cmd.SysProcAttr = &syscall.SysProcAttr { + Ptrace: true, + } + cmd.Stderr = os.Stderr + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + + cmd.Start() + child := cmd.Process + fmt.Printf("[%d]: pidB = %v\n", mypid, child.Pid) + + sigZero := syscall.Signal(0) + if err := child.Signal(sigZero); err != nil { + fmt.Printf("[%d]: Error: signal right after start (go): %v\n", mypid, err) + } else { + fmt.Printf("[%d]: Successfully sent a signal right after start (go)\n", mypid) + } + + if err := cmd.Wait(); err != nil { + fmt.Printf("[%v]: Wait returned: %v\n", mypid, err) + } + + for { + time.Sleep(time.Second * 2) + + var regs syscall.PtraceRegs + fmt.Printf("[%v]: Get child registers, when stopped\n", mypid) + if err := syscall.PtraceGetRegs(child.Pid, ®s); err != nil { + panic(err) + } + fmt.Printf("[%v]: Orig_rax = %v\n", mypid, regs.Orig_rax) + + if err := child.Signal(syscall.Signal(0)); err != nil { + fmt.Printf("Error: signal when stopped (go): %v\n", err) + if err := syscall.Kill(child.Pid, sigZero); err != nil { + fmt.Printf("Error: signal when stopped (syscall): %v\n", err) + } else { + fmt.Printf("[%d]: Successfully sent a signal when stopped (syscall)\n", mypid) + } + } else { + fmt.Printf("[%d]: Successfully sent a signal when stopped (go)\n", mypid) + } + + time.Sleep(time.Second) + //if err := syscall.PtraceCont(child.Pid, 0); err != nil { + if err := syscall.PtraceSyscall(child.Pid, 0); err != nil { + // Here the go program will frequently fail with 'no such + // process', though the child process is still alive (which is + // checked by sending sigZero below). This is quite reliable + // reproducible with sleep for at least 1 second (above). Without + // sleep all work fine. The reason is unknown. + if err := syscall.Kill(child.Pid, sigZero); err != nil { + fmt.Printf("Error: signal at failed ptrace_restart (syscall): %v\n", err) + } else { + fmt.Printf("[%d]: Successfully sent a signal at failed ptrace_restart (syscall)\n", mypid) + } + panic(err) + } + + //time.Sleep(time.Second) + fmt.Printf("[%v]: Get child registers, when running\n", mypid) + // Following register quire will fail in any case, because child is + // not stopped! + if err := syscall.PtraceGetRegs(child.Pid, ®s); err != nil { + fmt.Printf("[%d]: Error: PTRACE_GETREGS: %v\n", mypid, err) + } + fmt.Printf("[%v]: Orig_rax = %v\n", mypid, regs.Orig_rax) + + if err := child.Signal(syscall.Signal(0)); err != nil { + fmt.Printf("Error: signal when running (go): %v\n", err) + if err := syscall.Kill(child.Pid, sigZero); err != nil { + fmt.Printf("Error: signal when running (syscall): %v\n", err) + } else { + fmt.Printf("[%d]: Successfully sent a signal when running (syscall)\n", mypid) + } + } else { + fmt.Printf("[%d]: Successfully sent a signal when running (go)\n", mypid) + } + + + var ws syscall.WaitStatus + if _, err := syscall.Wait4(child.Pid, &ws, 0, nil); err != nil { + panic(err) + } + + if ws.Exited() { + fmt.Printf("[%v]: Child exited with status %v\n", mypid, ws.ExitStatus()) + break + + } else if ws.Signaled() { + fmt.Printf("[%v]: Child killed by signal %v\n", mypid, ws.Signal().String()) + break + + } else if ws.Stopped() { + fmt.Printf("[%v]: Child is traced and and is about to receive %v\n", mypid, ws.StopSignal().String()) + + } else if ws.Continued() { + fmt.Printf("[%v]: Child continued\n", mypid) + + } else { + fmt.Printf("[%d]: Child is elsewhere\n", mypid); + } + } +}