WIP: Golang Proot code #1
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
*.o
|
*.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
|
#ifndef BUILD_H
|
||||||
#define BUILD_H
|
#define BUILD_H
|
||||||
#undef VERSION
|
#undef VERSION
|
||||||
#define VERSION "v5.4.0-60485d26"
|
#define VERSION "-46d69dae"
|
||||||
// #define HAVE_PROCESS_VM
|
#define HAVE_PROCESS_VM
|
||||||
// #define HAVE_SECCOMP_FILTER
|
#define HAVE_SECCOMP_FILTER
|
||||||
#endif /* BUILD_H */
|
#endif /* BUILD_H */
|
||||||
|
@@ -468,6 +468,11 @@ int NO_main(int argc, char *const argv[])
|
|||||||
if (tracee == NULL)
|
if (tracee == NULL)
|
||||||
goto error;
|
goto error;
|
||||||
tracee->pid = getpid();
|
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. */
|
/* Pre-configure the first tracee. */
|
||||||
status = parse_config(tracee, argc, argv);
|
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. */
|
/* Start tracing the first tracee and all its children. */
|
||||||
exit(event_loop());
|
exit(event_loop(tracee));
|
||||||
|
|
||||||
error:
|
error:
|
||||||
TALLOC_FREE(tracee);
|
TALLOC_FREE(tracee);
|
||||||
|
Binary file not shown.
Binary file not shown.
@@ -71,10 +71,14 @@ import "C"
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Tracee struct{ Pointer unsafe.Pointer }
|
type Tracee struct {
|
||||||
|
Pointer unsafe.Pointer
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
func NewCli() (*Tracee, error) {
|
func NewCli() (*Tracee, error) {
|
||||||
// Pre-create the first tracee (pid == 0).
|
// Pre-create the first tracee (pid == 0).
|
||||||
@@ -124,5 +128,5 @@ func (tracee *Tracee) SetWorkdir(workdir string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (tracee *Tracee) LoopEvent() {
|
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;
|
return -ENOSYS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Send the KILL signal to all tracees when PRoot has received a fatal
|
/* Send the KILL signal to all tracees when PRoot has received a fatal signal. */
|
||||||
* signal. */
|
// Needs fix to kill from Tracee* struct
|
||||||
static void kill_all_tracees2(int signum, siginfo_t *siginfo UNUSED, void *ucontext UNUSED)
|
static void kill_all_tracees2(int signum, siginfo_t *siginfo UNUSED, void *ucontext UNUSED)
|
||||||
{
|
{
|
||||||
note(NULL, WARNING, INTERNAL, "signal %d received from process %d",
|
note(NULL, WARNING, INTERNAL, "signal %d received from process %d", signum, siginfo->si_pid);
|
||||||
signum, siginfo->si_pid);
|
// kill_all_tracees(current_tracee);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -278,16 +271,16 @@ static void check_architecture(Tracee *tracee)
|
|||||||
* Wait then handle any event from any tracee. This function returns
|
* Wait then handle any event from any tracee. This function returns
|
||||||
* the exit status of the last terminated program.
|
* the exit status of the last terminated program.
|
||||||
*/
|
*/
|
||||||
int event_loop()
|
int event_loop(Tracee *current_tracee1)
|
||||||
{
|
{
|
||||||
struct sigaction signal_action;
|
struct sigaction signal_action;
|
||||||
long status;
|
long status;
|
||||||
int signum;
|
int signum;
|
||||||
|
|
||||||
/* Kill all tracees when exiting. */
|
/* Kill all tracees when exiting. */
|
||||||
status = atexit(kill_all_tracees);
|
// status = atexit(kill_all_tracees);
|
||||||
if (status != 0)
|
// if (status != 0)
|
||||||
note(NULL, WARNING, INTERNAL, "atexit() failed");
|
// note(NULL, WARNING, INTERNAL, "atexit() failed");
|
||||||
|
|
||||||
/* All signals are blocked when the signal handler is called.
|
/* All signals are blocked when the signal handler is called.
|
||||||
* SIGINFO is used to know which process has signaled us and
|
* SIGINFO is used to know which process has signaled us and
|
||||||
@@ -309,7 +302,7 @@ int event_loop()
|
|||||||
/* Kill all tracees on abnormal termination
|
/* Kill all tracees on abnormal termination
|
||||||
* signals. This ensures no process is left
|
* signals. This ensures no process is left
|
||||||
* untraced. */
|
* untraced. */
|
||||||
signal_action.sa_sigaction = kill_all_tracees2;
|
// signal_action.sa_sigaction = kill_all_tracees2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SIGUSR1:
|
case SIGUSR1:
|
||||||
@@ -348,7 +341,7 @@ int event_loop()
|
|||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
|
||||||
/* This is the only safe place to free tracees. */
|
/* This is the only safe place to free tracees. */
|
||||||
free_terminated_tracees();
|
free_terminated_tracees(current_tracee1);
|
||||||
|
|
||||||
/* Wait for the next tracee's stop. */
|
/* Wait for the next tracee's stop. */
|
||||||
pid = waitpid(-1, &tracee_status, __WALL);
|
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);
|
default_ptrace_options);
|
||||||
if (status < 0) {
|
if (status < 0) {
|
||||||
note(tracee, ERROR, SYSTEM, "ptrace(PTRACE_SETOPTIONS)");
|
note(tracee, ERROR, SYSTEM, "ptrace(PTRACE_SETOPTIONS)");
|
||||||
exit(EXIT_FAILUtRE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@@ -28,7 +28,7 @@
|
|||||||
#include "tracee/tracee.h"
|
#include "tracee/tracee.h"
|
||||||
|
|
||||||
extern int launch_process(Tracee *tracee, char *const argv[]);
|
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 int handle_tracee_event(Tracee *tracee, int tracee_status);
|
||||||
extern bool restart_tracee(Tracee *tracee, int signal);
|
extern bool restart_tracee(Tracee *tracee, int signal);
|
||||||
|
|
||||||
|
@@ -49,9 +49,6 @@
|
|||||||
#define __W_STOPCODE(sig) ((sig) << 8 | 0x7f)
|
#define __W_STOPCODE(sig) ((sig) << 8 | 0x7f)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef LIST_HEAD(tracees, tracee) Tracees;
|
|
||||||
static Tracees tracees;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove @zombie from its parent's list of zombies. Note: this is a
|
* Remove @zombie from its parent's list of zombies. Note: this is a
|
||||||
* talloc destructor.
|
* talloc destructor.
|
||||||
@@ -99,7 +96,7 @@ static int remove_tracee(Tracee *tracee)
|
|||||||
|
|
||||||
/* This could be optimize by using a dedicated list of
|
/* This could be optimize by using a dedicated list of
|
||||||
* children and ptracees. */
|
* children and ptracees. */
|
||||||
LIST_FOREACH(relative, &tracees, link)
|
LIST_FOREACH(relative, tracee->RootConfig->tracees, link)
|
||||||
{
|
{
|
||||||
/* Its children are now orphan. */
|
/* Its children are now orphan. */
|
||||||
if (relative->parent == tracee)
|
if (relative->parent == tracee)
|
||||||
@@ -221,8 +218,18 @@ static uint64_t next_vpid = 1;
|
|||||||
* an error occurred (ENOMEM), otherwise it returns the newly
|
* an error occurred (ENOMEM), otherwise it returns the newly
|
||||||
* allocated structure.
|
* 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 *tracee;
|
||||||
|
|
||||||
tracee = new_dummy_tracee(NULL);
|
tracee = new_dummy_tracee(NULL);
|
||||||
@@ -236,13 +243,12 @@ static Tracee *new_tracee(pid_t pid)
|
|||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
if (((tracee)->link.le_next = (&tracees)->lh_first) != ((void *)0))
|
if (((tracee)->link.le_next = (current_tracee->RootConfig->tracees)->lh_first) != ((void *)0))
|
||||||
(&tracees)->lh_first->link.le_prev = &(tracee)->link.le_next;
|
(current_tracee->RootConfig->tracees)->lh_first->link.le_prev = &(tracee)->link.le_next;
|
||||||
(&tracees)->lh_first = (tracee);
|
(current_tracee->RootConfig->tracees)->lh_first = (tracee);
|
||||||
(tracee)->link.le_prev = &(&tracees)->lh_first;
|
(tracee)->link.le_prev = &(current_tracee->RootConfig->tracees)->lh_first;
|
||||||
} while (0)
|
} while (0);
|
||||||
|
tracee->life_context = talloc_new(tracee);
|
||||||
tracee->life_context = talloc_new(tracee);
|
|
||||||
|
|
||||||
return tracee;
|
return tracee;
|
||||||
}
|
}
|
||||||
@@ -273,7 +279,7 @@ Tracee *get_ptracee(const Tracee *ptracer, pid_t pid, bool only_stopped,
|
|||||||
return ptracee;
|
return ptracee;
|
||||||
}
|
}
|
||||||
|
|
||||||
LIST_FOREACH(ptracee, &tracees, link)
|
LIST_FOREACH(ptracee, ptracer->RootConfig->tracees, link)
|
||||||
{
|
{
|
||||||
/* Discard tracees that don't have this ptracer. */
|
/* Discard tracees that don't have this ptracer. */
|
||||||
if (PTRACEE.ptracer != 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)
|
if (current_tracee != NULL && current_tracee->pid == pid)
|
||||||
return (Tracee *)current_tracee;
|
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)
|
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)
|
if (tracee->killall_on_exit)
|
||||||
{
|
{
|
||||||
VERBOSE(tracee, 1, "terminating all tracees on exit");
|
VERBOSE(tracee, 1, "terminating all tracees on exit");
|
||||||
kill_all_tracees();
|
kill_all_tracees(tracee);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Free all tracees marked as terminated.
|
* Free all tracees marked as terminated.
|
||||||
*/
|
*/
|
||||||
void free_terminated_tracees()
|
void free_terminated_tracees(Tracee *tracee)
|
||||||
{
|
{
|
||||||
Tracee *next;
|
Tracee *next;
|
||||||
|
|
||||||
/* Items can't be deleted when using LIST_FOREACH. */
|
/* Items can't be deleted when using LIST_FOREACH. */
|
||||||
next = tracees.lh_first;
|
next = tracee->RootConfig->tracees->lh_first;
|
||||||
while (next != NULL)
|
while (next != NULL)
|
||||||
{
|
{
|
||||||
Tracee *tracee = next;
|
Tracee *tracee = next;
|
||||||
@@ -630,10 +636,10 @@ int swap_config(Tracee *tracee1, Tracee *tracee2)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Send the KILL signal to all tracees. */
|
/* Send the KILL signal to all tracees. */
|
||||||
void kill_all_tracees()
|
void kill_all_tracees(Tracee *current_tracee)
|
||||||
{
|
{
|
||||||
Tracee *tracee;
|
Tracee *tracee;
|
||||||
|
|
||||||
LIST_FOREACH(tracee, &tracees, link)
|
LIST_FOREACH(tracee, current_tracee->RootConfig->tracees, link)
|
||||||
kill(tracee->pid, SIGKILL);
|
kill(tracee->pid, SIGKILL);
|
||||||
}
|
}
|
||||||
|
@@ -76,8 +76,18 @@ typedef struct {
|
|||||||
bool disabled;
|
bool disabled;
|
||||||
} Heap;
|
} 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. */
|
/* Information related to a tracee process. */
|
||||||
typedef struct tracee {
|
typedef struct tracee {
|
||||||
|
tracee_root *RootConfig;
|
||||||
|
|
||||||
/**********************************************************************
|
/**********************************************************************
|
||||||
* Private resources *
|
* 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 int new_child(Tracee *parent, word_t clone_flags);
|
||||||
extern Tracee *new_dummy_tracee(TALLOC_CTX *context);
|
extern Tracee *new_dummy_tracee(TALLOC_CTX *context);
|
||||||
extern void terminate_tracee(Tracee *tracee);
|
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 int swap_config(Tracee *tracee1, Tracee *tracee2);
|
||||||
extern void kill_all_tracees();
|
extern void kill_all_tracees(Tracee *tracee);
|
||||||
|
|
||||||
#endif /* TRACEE_H */
|
#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
|
Qemu string // qemu tool to call
|
||||||
|
|
||||||
Cmd *exec.Cmd // Golang process
|
Cmd *exec.Cmd // Golang process
|
||||||
EventLocked bool
|
|
||||||
NoSeccomp bool
|
NoSeccomp bool
|
||||||
Err error
|
Err error
|
||||||
|
|
||||||
last_exit_status int
|
|
||||||
Tracees []*Tracee
|
|
||||||
vpid int
|
|
||||||
|
|
||||||
doneEnd *exec.ExitError
|
doneEnd *exec.ExitError
|
||||||
done context.Context
|
done context.Context
|
||||||
donefFn context.CancelFunc
|
donefFn context.CancelFunc
|
||||||
|
@@ -4,18 +4,10 @@ package proot
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"reflect"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"slices"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
|
|
||||||
goexec "sirherobrine23.com.br/go-bds/exec/exec"
|
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
|
runtime.LockOSThread() // Lock thread to use PTRACE
|
||||||
proot.EventLocked = true
|
|
||||||
proot.done, proot.donefFn = context.WithCancel(context.Background())
|
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()
|
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