WIP: Golang Proot code #1

Draft
Sirherobrine23 wants to merge 5 commits from proot_binding into main
17 changed files with 446 additions and 918 deletions
Showing only changes of commit 31bcff2719 - Show all commits

3
.gitignore vendored
View File

@@ -1,2 +1,3 @@
*.o
*.d
*.d
*.res

31
.vscode/c_cpp_properties.json vendored Normal file
View File

@@ -0,0 +1,31 @@
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**"
],
"defines": [
"__USE_GNU"
],
"compilerPath": "/usr/bin/clang",
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "linux-clang-x64",
"compilerArgs": [
"-g",
"-Wall",
"-Wextra",
"-O2",
"-D_FILE_OFFSET_BITS=64",
"-D_GNU_SOURCE",
"-Iproot/binding_proot_linux",
"-Iproot/binding_proot_linux",
"-Iproot/binding_proot_linux/../lib/uthash/include",
"-Wl,-z,noexecstack",
"-ltalloc"
]
}
],
"version": 4
}

View File

@@ -0,0 +1,8 @@
#include <sys/uio.h>
#include <stdlib.h>
int main(void)
{
return process_vm_readv(0, NULL, 0, NULL, 0, 0)
+ process_vm_writev(0, NULL, 0, NULL, 0, 0);
}

View File

@@ -0,0 +1,31 @@
#include <sys/prctl.h> /* prctl(2), PR_* */
#include <linux/seccomp.h> /* SECCOMP_MODE_FILTER, */
#include <linux/filter.h> /* struct sock_*, */
#include <linux/audit.h> /* AUDIT_ARCH_*, */
#include <stddef.h> /* offsetof(3), */
int main(void)
{
const size_t arch_offset = offsetof(struct seccomp_data, arch);
const size_t syscall_offset = offsetof(struct seccomp_data, nr);
struct sock_fprog program;
#define ARCH_NR AUDIT_ARCH_X86_64
struct sock_filter filter[] = {
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, arch_offset),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, AUDIT_ARCH_X86_64, 0, 1),
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, syscall_offset),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_TRACE)
};
program.filter = filter;
program.len = sizeof(filter) / sizeof(struct sock_filter);
(void) prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
(void) prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &program);
return 1;
}

View File

@@ -0,0 +1,308 @@
# If you want to build outside of the source tree, use the -f option:
# make -f ${SOMEWHERE}/proot/src/GNUmakefile
# the VPATH variable must point to the actual makefile directory
VPATH := $(dir $(lastword $(MAKEFILE_LIST)))
SRC = $(dir $(firstword $(MAKEFILE_LIST)))
GIT = git
RM = rm
INSTALL = install
CC = $(CROSS_COMPILE)gcc
LD = $(CC)
STRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
PYTHON = python3
HAS_SWIG := $(shell swig -version 2>/dev/null)
PYTHON_MAJOR_VERSION = $(shell ${PYTHON} -c "import sys; print(sys.version_info.major)" 2>/dev/null)
PYTHON_EMBED = $(shell ${PYTHON} -c "import sys; print('--embed' if sys.hexversion > 0x03080000 else '')" 2>/dev/null)
HAS_PYTHON_CONFIG := $(shell ${PYTHON}-config --ldflags ${PYTHON_EMBED} 2>/dev/null)
CPPFLAGS += -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -I. -I$(VPATH) -I$(VPATH)/../lib/uthash/include
CFLAGS += -g -Wall -Wextra -O2 -Wdeprecated-declarations -Wunused-parameter
CFLAGS += $(shell pkg-config --cflags talloc)
LDFLAGS += -Wl,-z,noexecstack
LDFLAGS += $(shell pkg-config --libs talloc)
CARE_LDFLAGS = $(shell pkg-config --libs libarchive)
OBJECTS += \
cli/cli.o \
cli/proot.o \
cli/note.o \
execve/enter.o \
execve/exit.o \
execve/shebang.o \
execve/elf.o \
execve/ldso.o \
execve/auxv.o \
execve/aoxp.o \
path/binding.o \
path/glue.o \
path/canon.o \
path/path.o \
path/proc.o \
path/temp.o \
syscall/seccomp.o \
syscall/syscall.o \
syscall/chain.o \
syscall/enter.o \
syscall/exit.o \
syscall/sysnum.o \
syscall/socket.o \
syscall/heap.o \
syscall/rlimit.o \
tracee/tracee.o \
tracee/mem.o \
tracee/reg.o \
tracee/event.o \
ptrace/ptrace.o \
ptrace/user.o \
ptrace/wait.o \
extension/extension.o \
extension/kompat/kompat.o \
extension/fake_id0/fake_id0.o \
extension/link2symlink/link2symlink.o \
extension/portmap/portmap.o \
extension/portmap/map.o \
loader/loader-wrapped.o
define define_from_arch.h
$2$1 := $(shell $(CC) $1 -E -dM -DNO_LIBC_HEADER $(SRC)/arch.h | grep -w $2 | cut -f 3 -d ' ')
endef
$(eval $(call define_from_arch.h,,HAS_LOADER_32BIT))
ifdef HAS_LOADER_32BIT
OBJECTS += loader/loader-m32-wrapped.o
endif
ifneq ($(and $(HAS_SWIG),$(HAS_PYTHON_CONFIG)),)
OBJECTS += extension/python/python.o \
extension/python/proot_wrap.o \
extension/python/python_extension.o \
extension/python/proot.o
endif
CARE_OBJECTS = \
cli/care.o \
cli/care-manual.o \
extension/care/care.o \
extension/care/final.o \
extension/care/extract.o \
extension/care/archive.o
.DEFAULT_GOAL = proot
all: proot
######################################################################
# Beautified output
quiet_GEN = @echo " GEN $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $@"; $(GEN)
quiet_CC = @echo " CC $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $@"; $(CC)
quiet_LD = @echo " LD $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $@"; $(LD)
quiet_INSTALL = @echo " INSTALL $?"; $(INSTALL)
V = 0
ifeq ($(V), 0)
quiet = quiet_
Q = @
silently = >/dev/null 2>&1
else
quiet =
Q =
silently =
endif
######################################################################
# Auto-configuration
GIT_VERSION := $(shell git describe --tags `git rev-list --tags --max-count=1`)
GIT_COMMIT := $(shell git rev-list --all --max-count=1 | cut -c 1-8)
VERSION = $(GIT_VERSION)-$(GIT_COMMIT)
CHECK_VERSION = if [ ! -z "$(VERSION)" ]; \
then /bin/echo -e "\#undef VERSION\n\#define VERSION \"$(VERSION)\""; \
fi;
ifneq ($(and $(HAS_SWIG),$(HAS_PYTHON_CONFIG)),)
CHECK_PYTHON_EXTENSION = /bin/echo -e "\#define HAVE_PYTHON_EXTENSION"
endif
CHECK_FEATURES = process_vm seccomp_filter
CHECK_PROGRAMS = $(foreach feature,$(CHECK_FEATURES),.check_$(feature))
CHECK_OBJECTS = $(foreach feature,$(CHECK_FEATURES),.check_$(feature).o)
CHECK_RESULTS = $(foreach feature,$(CHECK_FEATURES),.check_$(feature).res)
.SILENT .IGNORE .INTERMEDIATE: $(CHECK_OBJECTS) $(CHECK_PROGRAMS)
.check_%.o: .check_%.c
-$(COMPILE:echo=false) $(silently)
.check_%: .check_%.o
-$(LINK:echo=false) $(silently)
.check_%.res: .check_%
$(Q)if [ -e $< ]; then echo "#define HAVE_$(shell echo $* | tr a-z A-Z)" > $@; else echo "" > $@; fi
build.h: $(CHECK_RESULTS)
$($(quiet)GEN)
$(Q)echo "/* This file is auto-generated, edit at your own risk. */" > $@
$(Q)echo "#ifndef BUILD_H" >> $@
$(Q)echo "#define BUILD_H" >> $@
$(Q)sh -c '$(CHECK_VERSION)' >> $@
$(Q)sh -c '$(CHECK_PYTHON_EXTENSION)' >> $@
$(Q)cat $^ >> $@
$(Q)echo "#endif /* BUILD_H */" >> $@
BUILD_ID_NONE := $(shell if ld --build-id=none --version >/dev/null 2>&1; then echo ',--build-id=none'; fi)
######################################################################
# Build rules
COMPILE = $($(quiet)CC) $(CPPFLAGS) $(CFLAGS) -MD -c $(SRC)$< -o $@
LINK = $($(quiet)LD) -o $@ $^ $(LDFLAGS)
OBJIFY = $($(quiet)GEN) \
$(OBJCOPY) \
--input-target binary \
--output-target `env LC_ALL=C $(OBJDUMP) -f cli/cli.o | \
grep 'file format' | awk '{print $$4}'` \
--binary-architecture `env LC_ALL=C $(OBJDUMP) -f cli/cli.o | \
grep architecture | cut -f 1 -d , | awk '{print $$2}'` \
$< $@
proot: $(OBJECTS)
$(LINK)
care: $(OBJECTS) $(CARE_OBJECTS)
$(LINK) $(CARE_LDFLAGS)
# Special case to compute which files depend on the auto-generated
# file "build.h".
USE_BUILD_H := $(patsubst $(SRC)%.c,%.o,$(shell grep -E -sl 'include[[:space:]]+"build.h"' $(patsubst %.o,$(SRC)%.c,$(OBJECTS) $(CARE_OBJECTS))))
$(USE_BUILD_H): build.h
%.o: %.c
@mkdir -p $(dir $@)
$(COMPILE)
.INTERMEDIATE: manual
manual: $(VPATH)/../doc/care/manual.rst
$(Q)cp $< $@
cli/care-manual.o: manual cli/cli.o
$(OBJIFY)
cli/%-licenses.o: licenses cli/cli.o
$(OBJIFY)
######################################################################
# Python extension
define build_python_extension
CPPFLAGS += $(shell ${PYTHON}-config --includes)
LDFLAGS += $(shell ${PYTHON}-config --ldflags ${PYTHON_EMBED})
SWIG = swig
quiet_SWIG = @echo " SWIG $$@"; swig
SWIG_OPT = -python
ifeq ($(PYTHON_MAJOR_VERSION), 3)
SWIG_OPT += -py3
endif
.INTERMEDIATE:python_extension.py
python_extension.py: extension/python/python_extension.py
$$(Q)cp $$< $$@
extension/python/python_extension.o: python_extension.py cli/cli.o
$$(OBJIFY)
.SECONDARY: proot_wrap.c proot.py
proot_wrap.c proot.py: extension/python/proot.i
$$($$(quiet)SWIG) $$(SWIG_OPT) -outcurrentdir -I$$(VPATH) $$(VPATH)/extension/python/proot.i
extension/python/proot.o: proot.py cli/cli.o
$$(OBJIFY)
extension/python/proot_wrap.o: proot_wrap.c
$$($$(quiet)CC) $$(CPPFLAGS) $$(CFLAGS) -MD -c $$< -o $$@
endef
ifneq ($(and $(HAS_SWIG),$(HAS_PYTHON_CONFIG)),)
$(eval $(build_python_extension))
endif
######################################################################
# Build rules for the loader
define build_loader
LOADER$1_OBJECTS = loader/loader$1.o loader/assembly$1.o
$(eval $(call define_from_arch.h,$1,LOADER_ARCH_CFLAGS))
$(eval $(call define_from_arch.h,$1,LOADER_ADDRESS))
LOADER_CFLAGS$1 += -fPIC -ffreestanding $(LOADER_ARCH_CFLAGS$1)
LOADER_LDFLAGS$1 += -static -nostdlib -Wl$(BUILD_ID_NONE),-Ttext=$(LOADER_ADDRESS$1),-z,noexecstack
loader/loader$1.o: loader/loader.c
@mkdir -p $$(dir $$@)
$$(COMPILE) $1 $$(LOADER_CFLAGS$1)
loader/assembly$1.o: loader/assembly.S
@mkdir -p $$(dir $$@)
$$(COMPILE) $1 $$(LOADER_CFLAGS$1)
loader/loader$1: $$(LOADER$1_OBJECTS)
$$($$(quiet)LD) $1 -o $$@ $$^ $$(LOADER_LDFLAGS$1)
.INTERMEDIATE: loader$1.elf
loader$1.elf: loader/loader$1
$$(Q)cp $$< $$@
$$(Q)$(STRIP) $$@
loader/loader$1-wrapped.o: loader$1.elf cli/cli.o
$$(OBJIFY)
endef
$(eval $(build_loader))
ifdef HAS_LOADER_32BIT
$(eval $(call build_loader,-m32))
endif
######################################################################
# Dependencies
.DELETE_ON_ERROR:
$(OBJECTS) $(CARE_OBJECTS) $(LOADER_OBJECTS) $(LOADER-m32_OBJECTS): $(firstword $(MAKEFILE_LIST))
DEPS = $(OBJECTS:.o=.d) $(CARE_OBJECTS:.o=.d) $(LOADER_OBJECTS:.o=.d) $(LOADER-m32_OBJECTS:.o=.d) $(CHECK_OBJECTS:.o=.d)
-include $(DEPS)
######################################################################
# PHONY targets
PREFIX ?= /usr/local
BINDIR ?= $(PREFIX)/bin
.PHONY: clean distclean install install-care uninstall
clean distclean:
-$(RM) -f $(CHECK_OBJECTS) $(CHECK_PROGRAMS) $(CHECK_RESULTS) $(OBJECTS) $(CARE_OBJECTS) $(LOADER_OBJECTS) $(LOADER-m32_OBJECTS) proot care loader/loader loader/loader-m32 cli/care-manual.o $(DEPS) build.h licenses proot.py proot_wrap.c
install: proot
$($(quiet)INSTALL) -D $< $(DESTDIR)$(BINDIR)/$<
install-care: care
$($(quiet)INSTALL) -D $< $(DESTDIR)$(BINDIR)/$<
uninstall:
-$(RM) -f $(DESTDIR)$(BINDIR)/proot
uninstall-care:
-$(RM) -f $(DESTDIR)$(BINDIR)/care

View File

@@ -2,7 +2,7 @@
#ifndef BUILD_H
#define BUILD_H
#undef VERSION
#define VERSION "v5.4.0-60485d26"
// #define HAVE_PROCESS_VM
// #define HAVE_SECCOMP_FILTER
#define VERSION "-46d69dae"
#define HAVE_PROCESS_VM
#define HAVE_SECCOMP_FILTER
#endif /* BUILD_H */

View File

@@ -468,6 +468,11 @@ int NO_main(int argc, char *const argv[])
if (tracee == NULL)
goto error;
tracee->pid = getpid();
tracee->RootConfig = talloc_zero(tracee, tracee_root);
if (tracee->RootConfig == NULL) {
note(tracee, ERROR, INTERNAL, "talloc_zero() failed: %s", strerror(errno));
goto error;
}
/* Pre-configure the first tracee. */
status = parse_config(tracee, argc, argv);
@@ -482,7 +487,7 @@ int NO_main(int argc, char *const argv[])
}
/* Start tracing the first tracee and all its children. */
exit(event_loop());
exit(event_loop(tracee));
error:
TALLOC_FREE(tracee);

View File

Binary file not shown.

View File

Binary file not shown.

View File

@@ -71,10 +71,14 @@ import "C"
import (
"fmt"
"syscall"
"unsafe"
)
type Tracee struct{ Pointer unsafe.Pointer }
type Tracee struct {
Pointer unsafe.Pointer
Err error
}
func NewCli() (*Tracee, error) {
// Pre-create the first tracee (pid == 0).
@@ -124,5 +128,5 @@ func (tracee *Tracee) SetWorkdir(workdir string) bool {
}
func (tracee *Tracee) LoopEvent() {
C.event_loop()
tracee.Err = syscall.Errno(C.event_loop((*C.Tracee)(tracee.Pointer)))
}

View File

@@ -109,19 +109,12 @@ int launch_process(Tracee *tracee, char *const argv[])
return -ENOSYS;
}
/* Send the KILL signal to all tracees when PRoot has received a fatal
* signal. */
/* Send the KILL signal to all tracees when PRoot has received a fatal signal. */
// Needs fix to kill from Tracee* struct
static void kill_all_tracees2(int signum, siginfo_t *siginfo UNUSED, void *ucontext UNUSED)
{
note(NULL, WARNING, INTERNAL, "signal %d received from process %d",
signum, siginfo->si_pid);
kill_all_tracees();
/* Exit immediately for system signals (segmentation fault,
* illegal instruction, ...), otherwise exit cleanly through
* the event loop. */
if (signum != SIGQUIT)
_exit(EXIT_FAILURE);
note(NULL, WARNING, INTERNAL, "signal %d received from process %d", signum, siginfo->si_pid);
// kill_all_tracees(current_tracee);
}
/**
@@ -278,16 +271,16 @@ static void check_architecture(Tracee *tracee)
* Wait then handle any event from any tracee. This function returns
* the exit status of the last terminated program.
*/
int event_loop()
int event_loop(Tracee *current_tracee1)
{
struct sigaction signal_action;
long status;
int signum;
/* Kill all tracees when exiting. */
status = atexit(kill_all_tracees);
if (status != 0)
note(NULL, WARNING, INTERNAL, "atexit() failed");
// status = atexit(kill_all_tracees);
// if (status != 0)
// note(NULL, WARNING, INTERNAL, "atexit() failed");
/* All signals are blocked when the signal handler is called.
* SIGINFO is used to know which process has signaled us and
@@ -309,7 +302,7 @@ int event_loop()
/* Kill all tracees on abnormal termination
* signals. This ensures no process is left
* untraced. */
signal_action.sa_sigaction = kill_all_tracees2;
// signal_action.sa_sigaction = kill_all_tracees2;
break;
case SIGUSR1:
@@ -348,7 +341,7 @@ int event_loop()
pid_t pid;
/* This is the only safe place to free tracees. */
free_terminated_tracees();
free_terminated_tracees(current_tracee1);
/* Wait for the next tracee's stop. */
pid = waitpid(-1, &tracee_status, __WALL);
@@ -474,7 +467,7 @@ static int handle_tracee_event_kernel_4_8(Tracee *tracee, int tracee_status)
default_ptrace_options);
if (status < 0) {
note(tracee, ERROR, SYSTEM, "ptrace(PTRACE_SETOPTIONS)");
exit(EXIT_FAILUtRE);
exit(EXIT_FAILURE);
}
}
else {

View File

@@ -28,7 +28,7 @@
#include "tracee/tracee.h"
extern int launch_process(Tracee *tracee, char *const argv[]);
extern int event_loop();
extern int event_loop(Tracee *current_tracee);
extern int handle_tracee_event(Tracee *tracee, int tracee_status);
extern bool restart_tracee(Tracee *tracee, int signal);

View File

@@ -49,9 +49,6 @@
#define __W_STOPCODE(sig) ((sig) << 8 | 0x7f)
#endif
typedef LIST_HEAD(tracees, tracee) Tracees;
static Tracees tracees;
/**
* Remove @zombie from its parent's list of zombies. Note: this is a
* talloc destructor.
@@ -99,7 +96,7 @@ static int remove_tracee(Tracee *tracee)
/* This could be optimize by using a dedicated list of
* children and ptracees. */
LIST_FOREACH(relative, &tracees, link)
LIST_FOREACH(relative, tracee->RootConfig->tracees, link)
{
/* Its children are now orphan. */
if (relative->parent == tracee)
@@ -221,8 +218,18 @@ static uint64_t next_vpid = 1;
* an error occurred (ENOMEM), otherwise it returns the newly
* allocated structure.
*/
static Tracee *new_tracee(pid_t pid)
static Tracee *new_tracee(pid_t pid, Tracee *current_tracee)
{
if (current_tracee == NULL)
{
current_tracee = new_dummy_tracee(NULL);
if (current_tracee == NULL)
return NULL;
talloc_set_destructor(current_tracee, remove_tracee);
current_tracee->RootConfig = (tracee_root *)talloc_zero(current_tracee, tracee_root);
current_tracee->RootConfig->next_vpid = 1;
}
Tracee *tracee;
tracee = new_dummy_tracee(NULL);
@@ -236,13 +243,12 @@ static Tracee *new_tracee(pid_t pid)
do
{
if (((tracee)->link.le_next = (&tracees)->lh_first) != ((void *)0))
(&tracees)->lh_first->link.le_prev = &(tracee)->link.le_next;
(&tracees)->lh_first = (tracee);
(tracee)->link.le_prev = &(&tracees)->lh_first;
} while (0)
tracee->life_context = talloc_new(tracee);
if (((tracee)->link.le_next = (current_tracee->RootConfig->tracees)->lh_first) != ((void *)0))
(current_tracee->RootConfig->tracees)->lh_first->link.le_prev = &(tracee)->link.le_next;
(current_tracee->RootConfig->tracees)->lh_first = (tracee);
(tracee)->link.le_prev = &(current_tracee->RootConfig->tracees)->lh_first;
} while (0);
tracee->life_context = talloc_new(tracee);
return tracee;
}
@@ -273,7 +279,7 @@ Tracee *get_ptracee(const Tracee *ptracer, pid_t pid, bool only_stopped,
return ptracee;
}
LIST_FOREACH(ptracee, &tracees, link)
LIST_FOREACH(ptracee, ptracer->RootConfig->tracees, link)
{
/* Discard tracees that don't have this ptracer. */
if (PTRACEE.ptracer != ptracer)
@@ -344,7 +350,7 @@ Tracee *get_tracee(const Tracee *current_tracee, pid_t pid, bool create)
if (current_tracee != NULL && current_tracee->pid == pid)
return (Tracee *)current_tracee;
for ((tracee) = ((&tracees)->lh_first); (tracee); (tracee) = ((tracee)->link.le_next))
for ((tracee) = ((current_tracee->RootConfig->tracees)->lh_first); (tracee); (tracee) = ((tracee)->link.le_next))
{
if (tracee->pid == pid)
{
@@ -356,7 +362,7 @@ Tracee *get_tracee(const Tracee *current_tracee, pid_t pid, bool create)
}
}
return (create ? new_tracee(pid) : NULL);
return (create ? new_tracee(pid, current_tracee) : NULL);
}
/**
@@ -372,19 +378,19 @@ void terminate_tracee(Tracee *tracee)
if (tracee->killall_on_exit)
{
VERBOSE(tracee, 1, "terminating all tracees on exit");
kill_all_tracees();
kill_all_tracees(tracee);
}
}
/**
* Free all tracees marked as terminated.
*/
void free_terminated_tracees()
void free_terminated_tracees(Tracee *tracee)
{
Tracee *next;
/* Items can't be deleted when using LIST_FOREACH. */
next = tracees.lh_first;
next = tracee->RootConfig->tracees->lh_first;
while (next != NULL)
{
Tracee *tracee = next;
@@ -630,10 +636,10 @@ int swap_config(Tracee *tracee1, Tracee *tracee2)
}
/* Send the KILL signal to all tracees. */
void kill_all_tracees()
void kill_all_tracees(Tracee *current_tracee)
{
Tracee *tracee;
LIST_FOREACH(tracee, &tracees, link)
LIST_FOREACH(tracee, current_tracee->RootConfig->tracees, link)
kill(tracee->pid, SIGKILL);
}

View File

@@ -76,8 +76,18 @@ typedef struct {
bool disabled;
} Heap;
typedef LIST_HEAD(tracees, tracee) Tracees;
typedef struct tracee_root
{
Tracees *tracees;
uint64_t next_vpid;
} tracee_root;
/* Information related to a tracee process. */
typedef struct tracee {
tracee_root *RootConfig;
/**********************************************************************
* Private resources *
**********************************************************************/
@@ -284,8 +294,8 @@ extern bool has_ptracees(const Tracee *ptracer, pid_t pid, word_t wait_options);
extern int new_child(Tracee *parent, word_t clone_flags);
extern Tracee *new_dummy_tracee(TALLOC_CTX *context);
extern void terminate_tracee(Tracee *tracee);
extern void free_terminated_tracees();
extern void free_terminated_tracees(Tracee *tracee);
extern int swap_config(Tracee *tracee1, Tracee *tracee2);
extern void kill_all_tracees();
extern void kill_all_tracees(Tracee *tracee);
#endif /* TRACEE_H */

View File

@@ -1,32 +0,0 @@
package main
import (
"fmt"
"os"
"sirherobrine23.com.br/go-bds/exec/exec"
"sirherobrine23.com.br/go-bds/exec/proot"
)
func main() {
cmd, _ := proot.NewProc()
err := cmd.Start(&exec.Exec{
Arguments: os.Args[min(1, len(os.Args)):],
Stdout: os.Stdout,
Stderr: os.Stderr,
Stdin: os.Stdin,
})
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(-1)
return
}
if err = cmd.Wait(); err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(-1)
return
}
}

View File

@@ -14,14 +14,9 @@ type Proot struct {
Qemu string // qemu tool to call
Cmd *exec.Cmd // Golang process
EventLocked bool
NoSeccomp bool
Err error
last_exit_status int
Tracees []*Tracee
vpid int
doneEnd *exec.ExitError
done context.Context
donefFn context.CancelFunc

View File

@@ -4,18 +4,10 @@ package proot
import (
"context"
"fmt"
"log"
"os"
"os/exec"
"reflect"
"runtime"
"slices"
"syscall"
"time"
"unsafe"
"golang.org/x/sys/unix"
goexec "sirherobrine23.com.br/go-bds/exec/exec"
)
@@ -90,832 +82,8 @@ func (proot *Proot) Start(options *goexec.Exec) error {
}
runtime.LockOSThread() // Lock thread to use PTRACE
proot.EventLocked = true
proot.done, proot.donefFn = context.WithCancel(context.Background())
go proot.Event() // Start background loop event
// go proot.Event() // Start background loop event
return proot.Cmd.Start()
}
func mountProcessState(pid int, status unix.WaitStatus, rusage *unix.Rusage) *os.ProcessState {
newState := &os.ProcessState{}
ptr := reflect.ValueOf(newState).Elem()
ptrType := ptr.Type()
for index := range ptr.Type().NumField() {
ptr, ptrType := ptr.Field(index), ptrType.Field(index)
switch ptrType.Name {
case "pid":
ptr.SetInt(int64(pid))
case "status":
ptr.Set(reflect.ValueOf(syscall.WaitStatus(status)))
case "rusage":
ptr.Set(reflect.ValueOf(any(rusage).(*syscall.Rusage)))
}
}
return newState
}
func (proot *Proot) Event() {
var wstatus unix.WaitStatus
var rusage unix.Rusage
for {
if proot.Cmd == nil || proot.Cmd.Process == nil {
<-time.After(time.Microsecond) // wait 1ms to check
continue
}
wpid, err := unix.Wait4(proot.Cmd.Process.Pid, &wstatus, unix.WALL, &rusage)
fmt.Println(err)
switch err {
case nil:
case syscall.Errno(10):
proot.doneEnd = &exec.ExitError{ProcessState: mountProcessState(proot.Cmd.Process.Pid, wstatus, &rusage)}
proot.donefFn()
return
case syscall.Errno(3):
unix.Kill(wpid, unix.PTRACE_CONT)
continue
default:
panic(err)
}
fmt.Printf("wpid: %d, status %08d, Exited: %5t, Signaled: %t, Stopped: %t, Continued: %t\n",
wpid,
wstatus,
wstatus.Exited(),
wstatus.Signaled(),
wstatus.Stopped(),
wstatus.Continued(),
)
log.Printf("Getting tracee to wpid %d\n", wpid)
tracee := proot.GetTracee(nil, wpid, true)
tracee.Running = false
if tracee.AsPtracee != nil {
log.Printf("handle_ptracee_event to %d\n", wpid)
keep_stopped := proot.handle_ptracee_event(tracee, wstatus)
if keep_stopped {
continue
}
}
log.Printf("handle_tracee_event to %d\n", wpid)
signal := proot.handle_tracee_event(tracee, wstatus)
log.Printf("restarting tracee to %d with %s\n", wpid, unix.Signal(signal))
if signal == -255 && proot.Err != nil {
fmt.Println(proot.Err)
signal = 0
}
proot.restart_tracee(tracee, unix.Signal(signal))
}
}
func (proot *Proot) GetTracee(current_tracee *Tracee, wpid int, create bool) *Tracee {
if current_tracee != nil && current_tracee.Pid == wpid {
return current_tracee
}
for _, tracee := range proot.Tracees {
if tracee.Pid == wpid {
return tracee
}
}
if create {
tracee := new(Tracee)
tracee.Pid = wpid
proot.vpid++
tracee.Vpid = uint64(proot.vpid)
proot.Tracees = append(proot.Tracees, tracee)
return tracee
}
return nil
}
func (proot *Proot) handle_ptracee_event(ptracee *Tracee, event unix.WaitStatus) (keep_stopped bool) {
ptracer := ptracee.AsPtracee.Ptracer
var handled_by_proot_first bool
ptracee.AsPtracee.Event4.Proot.Value = int(event)
ptracee.AsPtracee.Event4.Proot.Pending = true
keep_stopped = true
if event.Stopped() {
switch unix.Signal((event & 0xfff00) >> 8) {
case unix.SIGTRAP | 0x80:
if ptracee.AsPtracee.IgnoreSyscalls || ptracee.AsPtracee.IgnoreLoaderSyscalls {
return false
} else if ptracee.AsPtracee.Options&unix.PTRACE_O_TRACESYSGOOD == 0 {
// event &= ~(0x80 << 8);
// event &= ^(0x80 << 8)
handled_by_proot_first = ptracee.Status == 0
}
// FORK
case unix.SIGTRAP | unix.PTRACE_EVENT_FORK<<8:
if (ptracer.AsPtracee.Options & unix.PTRACE_O_TRACEFORK) == 0 {
return false
}
ptracer.AsPtracee.TracingStarted = true
handled_by_proot_first = true
// VFORK
case unix.SIGTRAP | unix.PTRACE_EVENT_VFORK<<8:
if (ptracer.AsPtracee.Options & unix.PTRACE_O_TRACEVFORK) == 0 {
return false
}
ptracer.AsPtracee.TracingStarted = true
handled_by_proot_first = true
// VFORKDONE
case unix.SIGTRAP | unix.PTRACE_EVENT_VFORK_DONE<<8:
if (ptracer.AsPtracee.Options & unix.PTRACE_O_TRACEVFORKDONE) == 0 {
return false
}
ptracer.AsPtracee.TracingStarted = true
handled_by_proot_first = true
// CLONE
case unix.SIGTRAP | unix.PTRACE_EVENT_CLONE<<8:
if (ptracer.AsPtracee.Options & unix.PTRACE_O_TRACECLONE) == 0 {
return false
}
ptracer.AsPtracee.TracingStarted = true
handled_by_proot_first = true
// EXIT
case unix.SIGTRAP | unix.PTRACE_EVENT_EXIT<<8:
if (ptracer.AsPtracee.Options & unix.PTRACE_O_TRACEEXIT) == 0 {
return false
}
ptracer.AsPtracee.TracingStarted = true
handled_by_proot_first = true
// EXEC
case unix.SIGTRAP | unix.PTRACE_EVENT_EXEC<<8:
if (ptracer.AsPtracee.Options & unix.PTRACE_O_TRACEEXEC) == 0 {
return false
}
ptracer.AsPtracee.TracingStarted = true
handled_by_proot_first = true
case unix.SIGTRAP | unix.PTRACE_EVENT_SECCOMP<<8:
return false
default:
ptracee.AsPtracee.TracingStarted = true
}
} else if event.Exited() || event.Signaled() {
ptracee.AsPtracee.TracingStarted = true
keep_stopped = false
}
if !ptracee.AsPtracee.TracingStarted {
return false
}
if handled_by_proot_first {
ptracee.AsPtracee.Event4.Proot.Value = proot.handle_tracee_event(ptracee, unix.WaitStatus(ptracee.AsPtracee.Event4.Proot.Value))
}
ptracee.AsPtracee.Event4.Ptracer.Value = int(event)
ptracee.AsPtracee.Event4.Ptracer.Pending = true
unix.Kill(ptracee.Pid, unix.SIGCHLD)
if ptracer.AsPtracer.WaitPid == -1 || ptracer.AsPtracer.WaitPid == ptracee.Pid &&
(ptracer.AsPtracer.WaitOptions&unix.WALL != 0 || (ptracer.AsPtracer.WaitOptions&unix.WCLONE != 0 && ptracee.Clone) || (ptracer.AsPtracer.WaitOptions&unix.WCLONE == 0 && !ptracee.Clone)) {
status := proot.update_wait_status(ptracer, ptracee)
if status != 0 {
// proot.poke_reg(ptracer, SYSARG_RESULT, (word_t) status);
}
/* Write ptracer's register cache back. */
// proot.push_regs(ptracer);
ptracer.AsPtracer.WaitPid = 0
restarted := proot.restart_tracee(ptracer, 0)
if !restarted {
keep_stopped = false
}
}
return
}
func (proot *Proot) update_wait_status(ptracer, ptracee *Tracee) (result int) {
/* Special case: the Linux kernel reports the terminating
* event issued by a process to both its parent and its
* tracer, except when they are the same. In this case the
* Linux kernel reports the terminating event only once to the
* tracing parent ... */
if ptracee.AsPtracee.Ptracer == ptracee.Parent &&
(unix.WaitStatus(ptracee.AsPtracee.Event4.Ptracer.Value).Exited() ||
unix.WaitStatus(ptracee.AsPtracee.Event4.Ptracer.Value).Signaled()) {
/* ... So hide this terminating event (toward its
* tracer, ie. PRoot) and make the second one appear
* (towards its parent, ie. the ptracer). This will
* ensure its exit status is collected from a kernel
* point-of-view (ie. it doesn't stay a zombie
* forever). */
// restart_original_syscall(ptracer);
/* Detach this ptracee from its ptracer, PRoot doesn't
* have anything else to emulate. */
// detach_from_ptracer(ptracee);
/* Zombies can rest in peace once the ptracer is notified. */
// if (PTRACEE.is_zombie)
// TALLOC_FREE(ptracee);
return 0
}
// address = peek_reg(ptracer, ORIGINAL, SYSARG_2);
// if (address != 0) {
// poke_int32(ptracer, address, PTRACEE.event4.ptracer.value);
// if (errno != 0)
// return -errno;
// }
// PTRACEE.event4.ptracer.pending = false;
/* Be careful; ptracee might get freed before its pid is returned. */
// result = ptracee->pid;
// /* Zombies can rest in peace once the ptracer is notified. */
// if (PTRACEE.is_zombie) {
// detach_from_ptracer(ptracee);
// TALLOC_FREE(ptracee);
// }
// return result;
return 0
}
func (proot *Proot) restart_tracee(tracee *Tracee, signal unix.Signal) bool {
// tracee->as_ptracer.wait_pid != 0 || signal == -1
if tracee.AsPtracer.WaitPid != 0 || signal == -1 {
return false
}
err := ptrace(tracee.RestartHow, tracee.Pid, 0, uintptr(signal))
if err != nil {
return false
}
tracee.RestartHow = 0
tracee.Running = true
return true
}
func is_kernel_4_8() bool {
var ust unix.Utsname
unix.Uname(&ust)
var major, minor int
fmt.Sscanf(string(ust.Release[:]), "%d.%d", &major, &minor)
return (major == 4 && minor >= 8) || major > 4
}
func (proot *Proot) terminate_tracee(tracee *Tracee) {
tracee.Terminated = true
if tracee.KillallOnExit {
for _, tracee := range slices.Backward(proot.Tracees) {
unix.Kill(tracee.Pid, unix.SIGKILL)
}
}
}
func (proot *Proot) handle_tracee_event_kernel_4_8(tracee *Tracee, tracee_status unix.WaitStatus) int {
/* Don't overwrite restart_how if it is explicitly set
* elsewhere, i.e in the ptrace emulation when single
* stepping. */
if tracee.RestartHow == 0 {
/* When seccomp is enabled, all events are restarted in
* non-stop mode, but this default choice could be overwritten
* later if necessary. The check against "sysexit_pending"
* ensures PTRACE_SYSCALL (used to hit the exit stage under
* seccomp) is not cleared due to an event that would happen
* before the exit stage, eg. PTRACE_EVENT_EXEC for the exit
* stage of execve(2). */
if tracee.Seccomp == 2 && !tracee.SysexitPending {
tracee.RestartHow = unix.PTRACE_CONT
} else {
tracee.RestartHow = unix.PTRACE_SYSCALL
}
}
var signal int
var seccomp_detected, seccomp_enabled bool
/* Not a signal-stop by default. */
signal = 0
if tracee_status.Exited() {
proot.last_exit_status = tracee_status.ExitStatus()
proot.terminate_tracee(tracee)
} else if tracee_status.Signaled() {
// check_architecture(tracee);
proot.terminate_tracee(tracee)
} else if tracee_status.Stopped() {
/* Don't use WSTOPSIG() to extract the signal since it clears the PTRACE_EVENT_* bits. */
signal = (int(tracee_status) & 0xfff00) >> 8
var deliver_sigtrap bool
switch signal {
case int(unix.SIGTRAP):
default_ptrace_options :=
unix.PTRACE_O_TRACESYSGOOD |
unix.PTRACE_O_TRACEFORK |
unix.PTRACE_O_TRACEVFORK |
unix.PTRACE_O_TRACEVFORKDONE |
unix.PTRACE_O_TRACEEXEC |
unix.PTRACE_O_TRACECLONE |
unix.PTRACE_O_TRACEEXIT
if deliver_sigtrap {
break
}
deliver_sigtrap = true
/* Try to enable seccomp mode 2... */
err := ptrace(unix.PTRACE_SETOPTIONS, tracee.Pid, 0, uintptr(default_ptrace_options|unix.PTRACE_O_TRACESECCOMP))
if err != nil {
seccomp_enabled = false
/* ... otherwise use default options only. */
err = ptrace(unix.PTRACE_SETOPTIONS, tracee.Pid, 0, uintptr(default_ptrace_options))
if err != nil {
proot.Err = fmt.Errorf("ptrace(PTRACE_SETOPTIONS): %s", err)
return -255
}
} else {
if proot.NoSeccomp {
seccomp_enabled = true
}
}
fallthrough
case int(unix.SIGTRAP | unix.PTRACE_EVENT_SECCOMP<<8):
if !seccomp_detected && seccomp_enabled {
// VERBOSE(tracee, 1, "ptrace acceleration (seccomp mode 2) enabled");
tracee.Seccomp = ENABLED
seccomp_detected = true
}
if signal == int(unix.SIGTRAP|unix.PTRACE_EVENT_SECCOMP<<8) {
signal = 0
flags := 0
/* Use the common ptrace flow if seccomp was
* explicitly disabled for this tracee. */
if tracee.Seccomp != ENABLED {
break
}
err := ptrace(unix.PTRACE_GETEVENTMSG, tracee.Pid, 0, uintptr(unsafe.Pointer(&flags)))
if err != nil {
break
}
if (flags & FILTER_SYSEXIT) == 0 {
tracee.RestartHow = unix.PTRACE_CONT
// translate_syscall(tracee);
if tracee.Seccomp == DISABLING {
tracee.RestartHow = unix.PTRACE_SYSCALL
}
break
}
}
fallthrough
case int(unix.SIGTRAP | 0x80):
signal = 0
/* This tracee got signaled then freed during the
sysenter stage but the kernel reports the sysexit
stage; just discard this spurious tracee/event. */
if tracee.Exe == "" {
tracee.RestartHow = unix.PTRACE_CONT /* SYSCALL OR CONT */
return 0
}
switch tracee.Seccomp {
case ENABLED:
if tracee.AsPtracee.Ptracer.Status == 0 {
/* sysenter: ensure the sysexit
* stage will be hit under seccomp. */
tracee.RestartHow = unix.PTRACE_SYSCALL
tracee.SysexitPending = true
} else {
/* sysexit: the next sysenter
* will be notified by seccomp. */
tracee.RestartHow = unix.PTRACE_CONT
tracee.SysexitPending = false
}
fallthrough
case DISABLED:
// translate_syscall(tracee);
/* This syscall has disabled seccomp. */
if tracee.Seccomp == DISABLING {
tracee.RestartHow = unix.PTRACE_SYSCALL
tracee.Seccomp = DISABLED
}
case DISABLING:
/* Seccomp was disabled by the
* previous syscall, but its sysenter
* stage was already handled. */
tracee.Seccomp = DISABLED
if tracee.AsPtracee.Ptracer.Status == 0 {
tracee.Status = 1
}
}
case int(unix.SIGTRAP | unix.PTRACE_EVENT_VFORK<<8):
signal = 0
// proot.new_child(tracee, CLONE_VFORK);
case int(unix.SIGTRAP | unix.PTRACE_EVENT_FORK<<8), int(unix.SIGTRAP | unix.PTRACE_EVENT_CLONE<<8):
signal = 0
// proot.new_child(tracee, 0);
case int(unix.SIGTRAP | unix.PTRACE_EVENT_VFORK_DONE<<8), int(unix.SIGTRAP | unix.PTRACE_EVENT_EXEC<<8),
int(unix.SIGTRAP | unix.PTRACE_EVENT_EXIT<<8):
signal = 0
case int(unix.SIGSTOP):
/* Stop this tracee until PRoot has received
* the EVENT_*FORK|CLONE notification. */
if tracee.Exe == "" {
tracee.Sigstop = SIGSTOP_PENDING
signal = -1
}
/* For each tracee, the first SIGSTOP
* is only used to notify the tracer. */
if tracee.Sigstop == SIGSTOP_IGNORED {
tracee.Sigstop = SIGSTOP_ALLOWED
signal = 0
}
}
}
// /* Clear the pending event, if any. */
tracee.AsPtracee.Event4.Proot.Pending = false
return signal
}
func (proot *Proot) handle_tracee_event(tracee *Tracee, tracee_status unix.WaitStatus) int {
if is_kernel_4_8() {
return proot.handle_tracee_event_kernel_4_8(tracee, tracee_status)
}
// static bool seccomp_detected = false;
// long status;
// int signal;
// /* Don't overwrite restart_how if it is explicitly set
// * elsewhere, i.e in the ptrace emulation when single
// * stepping. */
// if (tracee->restart_how == 0) {
// /* When seccomp is enabled, all events are restarted in
// * non-stop mode, but this default choice could be overwritten
// * later if necessary. The check against "sysexit_pending"
// * ensures PTRACE_SYSCALL (used to hit the exit stage under
// * seccomp) is not cleared due to an event that would happen
// * before the exit stage, eg. PTRACE_EVENT_EXEC for the exit
// * stage of execve(2). */
// if (tracee->seccomp == ENABLED && !tracee->sysexit_pending)
// tracee->restart_how = PTRACE_CONT;
// else
// tracee->restart_how = PTRACE_SYSCALL;
// }
// /* Not a signal-stop by default. */
// signal = 0;
// if (WIFEXITED(tracee_status)) {
// last_exit_status = WEXITSTATUS(tracee_status);
// VERBOSE(tracee, 1,
// "vpid %" PRIu64 ": exited with status %d",
// tracee->vpid, last_exit_status);
// terminate_tracee(tracee);
// }
// else if (WIFSIGNALED(tracee_status)) {
// check_architecture(tracee);
// VERBOSE(tracee, 1,
// "vpid %" PRIu64 ": terminated with signal %d",
// tracee->vpid, WTERMSIG(tracee_status));
// terminate_tracee(tracee);
// }
// else if (WIFSTOPPED(tracee_status)) {
// /* Don't use WSTOPSIG() to extract the signal
// * since it clears the PTRACE_EVENT_* bits. */
// signal = (tracee_status & 0xfff00) >> 8;
// switch (signal) {
// static bool deliver_sigtrap = false;
// case SIGTRAP: {
// const unsigned long default_ptrace_options = (
// PTRACE_O_TRACESYSGOOD |
// PTRACE_O_TRACEFORK |
// PTRACE_O_TRACEVFORK |
// PTRACE_O_TRACEVFORKDONE |
// PTRACE_O_TRACEEXEC |
// PTRACE_O_TRACECLONE |
// PTRACE_O_TRACEEXIT);
// /* Distinguish some events from others and
// * automatically trace each new process with
// * the same options.
// *
// * Note that only the first bare SIGTRAP is
// * related to the tracing loop, others SIGTRAP
// * carry tracing information because of
// * TRACE*FORK/CLONE/EXEC. */
// if (deliver_sigtrap)
// break; /* Deliver this signal as-is. */
// deliver_sigtrap = true;
// /* Try to enable seccomp mode 2... */
// status = ptrace(PTRACE_SETOPTIONS, tracee->pid, NULL,
// default_ptrace_options | PTRACE_O_TRACESECCOMP);
// if (status < 0) {
// /* ... otherwise use default options only. */
// status = ptrace(PTRACE_SETOPTIONS, tracee->pid, NULL,
// default_ptrace_options);
// if (status < 0) {
// note(tracee, ERROR, SYSTEM, "ptrace(PTRACE_SETOPTIONS)");
// exit(EXIT_FAILURE);
// }
// }
// }
// /* Fall through. */
// case SIGTRAP | 0x80:
// signal = 0;
// /* This tracee got signaled then freed during the
// sysenter stage but the kernel reports the sysexit
// stage; just discard this spurious tracee/event. */
// if (tracee->exe == NULL) {
// tracee->restart_how = PTRACE_CONT; /* SYSCALL OR CONT */
// return 0;
// }
// switch (tracee->seccomp) {
// case ENABLED:
// if (IS_IN_SYSENTER(tracee)) {
// /* sysenter: ensure the sysexit
// * stage will be hit under seccomp. */
// tracee->restart_how = PTRACE_SYSCALL;
// tracee->sysexit_pending = true;
// }
// else {
// /* sysexit: the next sysenter
// * will be notified by seccomp. */
// tracee->restart_how = PTRACE_CONT;
// tracee->sysexit_pending = false;
// }
// /* Fall through. */
// case DISABLED:
// translate_syscall(tracee);
// /* This syscall has disabled seccomp. */
// if (tracee->seccomp == DISABLING) {
// tracee->restart_how = PTRACE_SYSCALL;
// tracee->seccomp = DISABLED;
// }
// break;
// case DISABLING:
// /* Seccomp was disabled by the
// * previous syscall, but its sysenter
// * stage was already handled. */
// tracee->seccomp = DISABLED;
// if (IS_IN_SYSENTER(tracee))
// tracee->status = 1;
// break;
// }
// break;
// case SIGTRAP | PTRACE_EVENT_SECCOMP2 << 8:
// case SIGTRAP | PTRACE_EVENT_SECCOMP << 8: {
// unsigned long flags = 0;
// signal = 0;
// if (!seccomp_detected) {
// VERBOSE(tracee, 1, "ptrace acceleration (seccomp mode 2) enabled");
// tracee->seccomp = ENABLED;
// seccomp_detected = true;
// }
// /* Use the common ptrace flow if seccomp was
// * explicitely disabled for this tracee. */
// if (tracee->seccomp != ENABLED)
// break;
// status = ptrace(PTRACE_GETEVENTMSG, tracee->pid, NULL, &flags);
// if (status < 0)
// break;
// /* Use the common ptrace flow when
// * sysexit has to be handled. */
// if ((flags & FILTER_SYSEXIT) != 0) {
// tracee->restart_how = PTRACE_SYSCALL;
// break;
// }
// /* Otherwise, handle the sysenter
// * stage right now. */
// tracee->restart_how = PTRACE_CONT;
// translate_syscall(tracee);
// /* This syscall has disabled seccomp, so move
// * the ptrace flow back to the common path to
// * ensure its sysexit will be handled. */
// if (tracee->seccomp == DISABLING)
// tracee->restart_how = PTRACE_SYSCALL;
// break;
// }
// case SIGTRAP | PTRACE_EVENT_VFORK << 8:
// signal = 0;
// (void) new_child(tracee, CLONE_VFORK);
// break;
// case SIGTRAP | PTRACE_EVENT_FORK << 8:
// case SIGTRAP | PTRACE_EVENT_CLONE << 8:
// signal = 0;
// (void) new_child(tracee, 0);
// break;
// case SIGTRAP | PTRACE_EVENT_VFORK_DONE << 8:
// case SIGTRAP | PTRACE_EVENT_EXEC << 8:
// case SIGTRAP | PTRACE_EVENT_EXIT << 8:
// signal = 0;
// break;
// case SIGSTOP:
// /* Stop this tracee until PRoot has received
// * the EVENT_*FORK|CLONE notification. */
// if (tracee->exe == NULL) {
// tracee->sigstop = SIGSTOP_PENDING;
// signal = -1;
// }
// /* For each tracee, the first SIGSTOP
// * is only used to notify the tracer. */
// if (tracee->sigstop == SIGSTOP_IGNORED) {
// tracee->sigstop = SIGSTOP_ALLOWED;
// signal = 0;
// }
// break;
// default:
// /* Deliver this signal as-is. */
// break;
// }
// }
// /* Clear the pending event, if any. */
// tracee->as_ptracee.event4.proot.pending = false;
// return signal;
return 0
}
func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) {
_, _, e1 := unix.Syscall6(unix.SYS_PTRACE, uintptr(request), uintptr(pid), uintptr(addr), uintptr(data), 0, 0)
if e1 != 0 {
err = e1
}
return
}
func (proot *Proot) getPtraceRegs(pid int) (*unix.PtraceRegs, error) {
var regs unix.PtraceRegs
if err := unix.PtraceGetRegs(pid, &regs); err != nil {
return nil, err
}
return &regs, nil
}
type Tracee struct {
// Private
Pid int
Vpid uint64
Running bool
Terminated bool
KillallOnExit bool
Parent *Tracee
Clone bool
AsPtracer struct {
NbPtracees int
WaitPid int
WaitOptions uint64
WaitsIn int // enum: 0 = DOESNT_WAIT, 1 = WAITS_IN_KERNEL, 2 = WAITS_IN_PROOT
}
AsPtracee *struct {
Ptracer *Tracee
Event4 struct {
Proot struct {
Value int
Pending bool
}
Ptracer struct {
Value int
Pending bool
}
}
TracingStarted bool
IgnoreLoaderSyscalls bool
IgnoreSyscalls bool
Options uint64
IsZombie bool
}
Status int
RestartHow int // syscall.PTRACE_CONT etc.
Regs [NB_REG_VERSION]syscall.PtraceRegs
RegsWereChanged bool
RestoreOriginalRegs bool
Sigstop int // enum: 0 = IGNORED, 1 = ALLOWED, 2 = PENDING
GlueType uint32 // mode_t is often uint32
Reconf struct {
Tracee *Tracee
Paths string
}
Chain struct {
Syscalls *ChainedSyscalls
ForceFinalResult bool
FinalResult uint64
}
LoadInfo *LoadInfo
MixedMode bool
// Inherited
Verbose int
Seccomp int
SysexitPending bool
// Shared or private
FS *FileSystemNameSpace
Heap *Heap
// Shared until execve
Exe string
NewExe string
// (Re)configuration
Qemu []string
Glue string
Extensions *Extensions
// Read-only
HostLdsoPaths string
GuestLdsoPaths string
ToolName string
}
type Heap struct {
Base uint64 // assuming word_t = uint64
Size uint64 // size_t
Disabled bool
}
type FileSystemNameSpace struct {
Bindings struct {
Pending *Bindings
Guest *Bindings
Host *Bindings
}
Cwd string
}
type RegVersion int
const (
CURRENT RegVersion = iota
ORIGINAL
MODIFIED
NB_REG_VERSION
)
type Mapping struct {
Addr uint64
Length uint64
ClearLength uint64
Prot uint64
Flags uint64
Fd uint64
Offset uint64
}
type LoadInfo struct {
HostPath string
UserPath string
RawPath string
Mappings []*Mapping
ElfHeader ElfHeader
NeedsExecutableStack bool
Interp *LoadInfo
}
type Bindings struct{}
type Extensions struct{}
type ChainedSyscalls struct{}