WIP: Golang Proot code #1
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
*.o
|
||||
*.d
|
||||
*.d
|
||||
*.res
|
31
.vscode/c_cpp_properties.json
vendored
Normal file
31
.vscode/c_cpp_properties.json
vendored
Normal 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
|
||||
}
|
8
proot/binding_proot_linux/.check_process_vm.c
Normal file
8
proot/binding_proot_linux/.check_process_vm.c
Normal 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);
|
||||
}
|
31
proot/binding_proot_linux/.check_seccomp_filter.c
Normal file
31
proot/binding_proot_linux/.check_seccomp_filter.c
Normal 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;
|
||||
}
|
||||
|
308
proot/binding_proot_linux/GNUmakefile
Normal file
308
proot/binding_proot_linux/GNUmakefile
Normal 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
|
@@ -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 */
|
||||
|
@@ -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);
|
||||
|
Binary file not shown.
Binary file not shown.
@@ -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)))
|
||||
}
|
||||
|
@@ -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 {
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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 */
|
||||
|
@@ -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
|
||||
}
|
||||
}
|
@@ -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
|
||||
|
@@ -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, ®s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ®s, 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{}
|
||||
|
Reference in New Issue
Block a user