WIP: Golang Proot code #1

Draft
Sirherobrine23 wants to merge 5 commits from proot_binding into main
21 changed files with 1567 additions and 9 deletions
Showing only changes of commit 94176dc6bf - Show all commits

View File

@@ -37,13 +37,13 @@ typedef struct binding {
bool must_exist;
struct {
CIRCLEQ_ENTRY(binding) pending;
CIRCLEQ_ENTRY(binding) guest;
CIRCLEQ_ENTRY(binding) host;
struct { struct binding *cqe_next; struct binding *cqe_prev; } pending;
struct { struct binding *cqe_next; struct binding *cqe_prev; } guest;
struct { struct binding *cqe_next; struct binding *cqe_prev; } host;
} link;
} Binding;
typedef CIRCLEQ_HEAD(bindings, binding) Bindings;
typedef struct bindings { struct binding *cqh_first; struct binding *cqh_last; } Bindings;
extern Binding *insort_binding3(const Tracee *tracee, const TALLOC_CTX *context,
const char host_path[PATH_MAX], const char guest_path[PATH_MAX]);

View File

@@ -48,8 +48,7 @@
* Compute the offset of the register @reg_name in the USER area.
*/
#define USER_REGS_OFFSET(reg_name) \
(offsetof(struct user, regs) \
+ offsetof(struct user_regs_struct, reg_name))
(offsetof(struct user, regs) + offsetof(struct user_regs_struct, reg_name))
#define REG(tracee, version, index) \
(*(word_t*) (((uint8_t *) &tracee->_regs[version]) + reg_offset[index]))
@@ -182,8 +181,7 @@ word_t peek_reg(const Tracee *tracee, RegVersion version, Reg reg)
result = REG(tracee, version, reg);
/* Use only the 32 least significant bits (LSB) when running
* 32-bit processes on a 64-bit kernel. */
/* Use only the 32 least significant bits (LSB) when running 32-bit processes on a 64-bit kernel. */
if (is_32on64_mode(tracee))
result &= 0xFFFFFFFF;

View File

@@ -93,7 +93,7 @@ typedef struct tracee {
**********************************************************************/
/* Link for the list of all tracees. */
LIST_ENTRY(tracee) link;
struct { struct tracee *le_next; struct tracee **le_prev; } link;
/* Process identifier. */
pid_t pid;

233
proot/proot/execve/elf.go Normal file
View File

@@ -0,0 +1,233 @@
package execve
/*
#define EI_NIDENT 16
typedef struct {
unsigned char e_ident[EI_NIDENT];
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
uint32_t e_entry;
uint32_t e_phoff;
uint32_t e_shoff;
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
} ElfHeader32;
typedef struct {
unsigned char e_ident[EI_NIDENT];
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
uint64_t e_entry;
uint64_t e_phoff;
uint64_t e_shoff;
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
} ElfHeader64;
typedef union {
ElfHeader32 class32;
ElfHeader64 class64;
} ElfHeader;
typedef struct {
uint32_t p_type;
uint32_t p_offset;
uint32_t p_vaddr;
uint32_t p_paddr;
uint32_t p_filesz;
uint32_t p_memsz;
uint32_t p_flags;
uint32_t p_align;
} ProgramHeader32;
typedef struct {
uint32_t p_type;
uint32_t p_flags;
uint64_t p_offset;
uint64_t p_vaddr;
uint64_t p_paddr;
uint64_t p_filesz;
uint64_t p_memsz;
uint64_t p_align;
} ProgramHeader64;
typedef union {
ProgramHeader32 class32;
ProgramHeader64 class64;
} ProgramHeader;
// Object type:
#define ET_REL 1
#define ET_EXEC 2
#define ET_DYN 3
#define ET_CORE 4
// Segment flags:
#define PF_X 1
#define PF_W 2
#define PF_R 4
typedef enum {
PT_LOAD = 1,
PT_DYNAMIC = 2,
PT_INTERP = 3,
PT_GNU_STACK = 0x6474e551,
} SegmentType;
typedef struct {
int32_t d_tag;
uint32_t d_val;
} DynamicEntry32;
typedef struct {
int64_t d_tag;
uint64_t d_val;
} DynamicEntry64;
typedef union {
DynamicEntry32 class32;
DynamicEntry64 class64;
} DynamicEntry;
typedef enum {
DT_STRTAB = 5,
DT_RPATH = 15,
DT_RUNPATH = 29
} DynamicType;
// The following macros are also compatible with ELF 64-bit.
#define ELF_IDENT(header, index) (header).class32.e_ident[(index)]
#define ELF_CLASS(header) ELF_IDENT(header, 4)
#define IS_CLASS32(header) (ELF_CLASS(header) == 1)
#define IS_CLASS64(header) (ELF_CLASS(header) == 2)
// Helper to access a @field of the structure ElfHeaderXX.
#define ELF_FIELD(header, field) \
(IS_CLASS64(header) \
? (header).class64. e_ ## field \
: (header).class32. e_ ## field)
// Helper to access a @field of the structure ProgramHeaderXX
#define PROGRAM_FIELD(ehdr, phdr, field) \
(IS_CLASS64(ehdr) \
? (phdr).class64. p_ ## field \
: (phdr).class32. p_ ## field)
// Helper to access a @field of the structure DynamicEntryXX
#define DYNAMIC_FIELD(ehdr, dynent, field) \
(IS_CLASS64(ehdr) \
? (dynent).class64. d_ ## field \
: (dynent).class32. d_ ## field)
#define KNOWN_PHENTSIZE(header, size) \
( (IS_CLASS32(header) && (size) == sizeof(ProgramHeader32)) \
|| (IS_CLASS64(header) && (size) == sizeof(ProgramHeader64)))
#define IS_POSITION_INDENPENDANT(elf_header) \
(ELF_FIELD((elf_header), type) == ET_DYN)
*/
const EI_NIDENT = 16
type ElfHeader32 struct {
Ident [EI_NIDENT]byte
Type uint16
Machine uint16
Version uint32
Entry uint32
Phoff uint32
Shoff uint32
Flags uint32
Ehsize uint16
Phentsize uint16
Phnum uint16
Shentsize uint16
Shnum uint16
Shstrndx uint16
}
type ElfHeader64 struct {
Ident [EI_NIDENT]byte
Type uint16
Machine uint16
Version uint32
Entry uint64
Phoff uint64
Shoff uint64
Flags uint32
Ehsize uint16
Phentsize uint16
Phnum uint16
Shentsize uint16
Shnum uint16
Shstrndx uint16
}
type ElfHeader struct {
class32 ElfHeader32
class64 ElfHeader64
}
type ProgramHeader32 struct {
Type uint32
Offset uint32
Vaddr uint32
Paddr uint32
Filesz uint32
Memsz uint32
Flags uint32
Align uint32
}
type ProgramHeader64 struct {
Type uint32
Flags uint32
Offset uint64
Vaddr uint64
Paddr uint64
Filesz uint64
Memsz uint64
Align uint64
}
type ProgramHeader struct {
class32 ProgramHeader32
class64 ProgramHeader64
}
type DynamicEntry32 struct {
Tag int32
Val uint32
}
type DynamicEntry64 struct {
Tag int64
Val uint64
}
type DynamicEntry struct {
class32 DynamicEntry32
class64 DynamicEntry64
}
type DynamicType int32
const (
DT_STRTAB DynamicType = 5
DT_RPATH DynamicType = 15
DT_RUNPATH DynamicType = 29
)

View File

@@ -0,0 +1,14 @@
package execve
type Mapping struct {
Addr, Lenght, ClearLength, Prot, Flags, FD, Offset uint64
}
type LoadInfo struct {
HostPath, UserPath, RawPath string
Mappings *Mapping
ElfHeader *ElfHeader
needsExecutableStack bool
Interp *LoadInfo
}

View File

@@ -0,0 +1,31 @@
package path
type LinkBinding struct {
Next, Prev *Binding
}
type Binding struct {
Host, Guest Path
NeedSubstitution, MustExist bool
Link struct {
Pending, Guest, Host LinkBinding
}
}
// struct bindings { struct binding *cqh_first; struct binding *cqh_last; } Bindings
type Bindings struct {
First, Last *Binding
}
/*
extern Binding *insort_binding3(const Tracee *tracee, const TALLOC_CTX *context, const char host_path[PATH_MAX], const char guest_path[PATH_MAX]);
extern Binding *new_binding(Tracee *tracee, const char *host, const char *guest, bool must_exist);
extern int initialize_bindings(Tracee *tracee);
extern const char *get_path_binding(const Tracee* tracee, Side side, const char path[PATH_MAX]);
extern Binding *get_binding(const Tracee *tracee, Side side, const char path[PATH_MAX]);
extern const char *get_root(const Tracee* tracee);
extern int substitute_binding(const Tracee* tracee, Side side, char path[PATH_MAX]);
extern void remove_binding_from_all_lists(const Tracee *tracee, Binding *binding);
*/

3
proot/proot/path/glue.go Normal file
View File

@@ -0,0 +1,3 @@
package path
// extern mode_t build_glue(Tracee *tracee, const char *guest_path, char host_path[PATH_MAX], Finality finality);

76
proot/proot/path/path.go Normal file
View File

@@ -0,0 +1,76 @@
package path
import (
"syscall"
"golang.org/x/sys/unix"
)
type Side int
type Type int
type Finality int
type Comparison int
const (
Guest Side = iota
Host
// Used for bindings as specified by the user but not canonicalized yet (new_binding, initialize_binding).
Pedding
)
// Type type
const (
Regular Type = iota
Symlink
)
// Path ending type.
const (
NOT_FINAL Finality = iota
FINAL_NORMAL
FINAL_SLASH
FINAL_DOT
)
// Comparison between two paths.
const (
PATHS_ARE_EQUAL Comparison = iota
PATH1_IS_PREFIX
PATH2_IS_PREFIX
PATHS_ARE_NOT_COMPARABLE
)
type Path struct {
Path [syscall.PathMax]byte
Length int
Side Side
}
/*
extern int which(Tracee *tracee, const char *paths, char host_path[PATH_MAX], const char *command);
extern int realpath2(Tracee *tracee, char host_path[PATH_MAX], const char *path, bool deref_final);
extern int getcwd2(Tracee *tracee, char guest_path[PATH_MAX]);
extern void chop_finality(char *path);
extern int translate_path(Tracee *tracee, char host_path[PATH_MAX], int dir_fd, const char *guest_path, bool deref_final);
extern int detranslate_path(Tracee *tracee, char path[PATH_MAX], const char t_referrer[PATH_MAX]);
extern bool belongs_to_guestfs(const Tracee *tracee, const char *path);
extern int join_paths(int number_paths, char result[PATH_MAX], ...);
extern int list_open_fd(const Tracee *tracee);
extern Comparison compare_paths(const char *path1, const char *path2);
extern Comparison compare_paths2(const char *path1, size_t length1, const char *path2, size_t length2);
extern size_t substitute_path_prefix(char path[PATH_MAX], size_t old_prefix_length, const char *new_prefix, size_t new_prefix_length);
extern int readlink_proc_pid_fd(pid_t pid, int fd, char path[PATH_MAX]);
*/
// #define AT_FD(dirfd, path) ((dirfd) != AT_FDCWD && ((path) != NULL && (path)[0] != '/'))
/* Check if path interpretable relatively to dirfd, see openat(2) for details. */
func IsAtFD(dirfd int, path string) bool {
return dirfd != unix.AT_FDCWD && path[0] != '/'
}

16
proot/proot/path/proc.go Normal file
View File

@@ -0,0 +1,16 @@
package path
type Action int
// Action to do after a call to readlink_proc().
const (
DEFAULT Action = iota // Nothing special to do, treat it as a regular link.
CANONICALIZE // The symlink was dereferenced, now canonicalize it.
DONT_CANONICALIZE // The symlink shouldn't be dereferenced nor canonicalized.
)
/*
extern Action readlink_proc(const Tracee *tracee, char result[PATH_MAX], const char path[PATH_MAX], const char component[NAME_MAX], Comparison comparison);
extern ssize_t readlink_proc2(const Tracee *tracee, char result[PATH_MAX], const char path[PATH_MAX]);
*/

View File

@@ -0,0 +1,40 @@
package syscall
import (
"syscall"
"golang.org/x/sys/unix"
origin_tracee "sirherobrine23.com.br/go-bds/exec/proot/proot/tracee"
)
type Tracee origin_tracee.Tracee
func (tracee *Tracee) TranslateSyscall() {
isEnterStage := tracee.Status == 0
if tracee.Exe == "" {
panic("Empty exe")
}
if err := (*origin_tracee.Tracee)(tracee).FetchRegs(); err != nil {
return
}
var status error
if isEnterStage {
if tracee.Chain.Syscalls == nil {
(*origin_tracee.Tracee)(tracee).SaveCurrentRegs(origin_tracee.ORIGINAL)
// status = tracee.TranslateSyscallEnter()
(*origin_tracee.Tracee)(tracee).SaveCurrentRegs(origin_tracee.MODIFIED)
} else {
// notify_extensions
// status = (*origin_tracee.Tracee)(tracee).NotifyExtensions()
tracee.RestartHow = unix.PTRACE_SYSCALL
}
if status != nil {
// tracee.SetSysnum(origin_tracee.PR_void)
(*origin_tracee.Tracee)(tracee).PokeReg(origin_tracee.CURRENT, origin_tracee.SYSARG_RESULT, uint64(status.(syscall.Errno)))
tracee.Status = int(status.(syscall.Errno))
}
}
}

19
proot/proot/tracee/abi.go Normal file
View File

@@ -0,0 +1,19 @@
package tracee
import "unsafe"
type Abi uint8
const (
ABI_DEFAULT Abi = iota
ABI_2 // x86_32 on x86_64.
ABI_3 // x32 on x86_64.
NB_MAX_ABIS
)
func (tracee *Tracee) SizeofWord() int {
if tracee.Is32On64() {
return int(unsafe.Sizeof(uint64(0))) / 2
}
return int(unsafe.Sizeof(uint64(0)))
}

View File

@@ -0,0 +1,28 @@
//go:build amd64 && (linux || android)
package tracee
func (tracee *Tracee) GetAbi() Abi {
switch tracee.Regs[ORIGINAL].Cs {
case 0x23:
return ABI_2
case 0x33:
if tracee.Regs[ORIGINAL].Ds == 0x2b {
return ABI_3
}
}
return ABI_DEFAULT
}
func (tracee *Tracee) Is32On64() bool {
switch tracee.Regs[ORIGINAL].Cs {
case 0x23:
return tracee.Regs[ORIGINAL].Ds == 0x2b
case 0x33:
if tracee.Regs[ORIGINAL].Ds == 0x2b {
return true
}
}
return false
}

View File

@@ -0,0 +1,6 @@
//go:build !amd64 && (linux || android)
package tracee
func (tracee *Tracee) GetAbi() Abi { return ABI_DEFAULT }
func (tracee *Tracee) Is32On64() bool { return false }

317
proot/proot/tracee/event.go Normal file
View File

@@ -0,0 +1,317 @@
package tracee
import (
"fmt"
"os/exec"
"syscall"
"unsafe"
"golang.org/x/sys/unix"
)
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 = syscall.Errno(e1)
}
return
}
func (tracee *Tracee) RestartTracee(signal syscall.Signal) bool {
if tracee.AsPtracer.WaitPID != 0 || signal == -1 {
return false
} else if err := ptrace(tracee.RestartHow, tracee.PID, 0, uintptr(signal)); err != nil {
return false
}
tracee.RestartHow = 0
tracee.Running = true
return true
}
func (tracee *Tracee) LaunchProces(cmd *exec.Cmd) error {
if cmd.SysProcAttr == nil {
cmd.SysProcAttr = &syscall.SysProcAttr{}
}
cmd.SysProcAttr.Ptrace = true
if err := cmd.Start(); err != nil {
return err
}
if !tracee.RootConfig.NoSeccmp {
// tracee.EnableSyscallFiltering()
}
tracee.PID = cmd.Process.Pid
return nil
}
func (tracee *Tracee) EventLoop() error { return nil }
func isKernel4_8() bool {
var uts unix.Utsname
if err := unix.Uname(&uts); err != nil {
return false
}
var major, minor int
fmt.Sscanf(string(uts.Release[:]), "%d.%d", &major, &minor)
return major == 4 && minor >= 8 || major > 4
}
const PTRACE_EVENT_SECCOMP2 = unix.PTRACE_EVENT_SECCOMP + 1
const defaultPtraceOption = 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
func (tracee *Tracee) HandleTraceeEvent4_8(traceeStatus unix.WaitStatus) error {
if tracee.RestartHow == 0 {
if tracee.Seccomp == Enabled && !tracee.SysexitPending {
tracee.RestartHow = unix.PTRACE_CONT
} else {
tracee.RestartHow = unix.PTRACE_SYSCALL
}
}
var seccomp_detected, seccomp_enabled bool
var signal syscall.Errno
if traceeStatus.Exited() {
tracee.RootConfig.LastExitStatus = traceeStatus.ExitStatus()
tracee.TerminateTracee()
return nil
} else if traceeStatus.Signaled() {
// check_architecture(tracee);
tracee.TerminateTracee()
} else if traceeStatus.Stopped() {
deliverSigtrap := false
signal = syscall.Errno(traceeStatus&0xfff00) >> 8
// static bool deliver_sigtrap = false;
fall_t:
switch signal {
case syscall.Errno(unix.SIGTRAP):
if deliverSigtrap {
break
}
deliverSigtrap = true
err := ptrace(unix.PTRACE_SETOPTIONS, tracee.PID, 0, defaultPtraceOption|unix.PTRACE_O_TRACESECCOMP)
if err == nil {
if !tracee.RootConfig.NoSeccmp {
seccomp_enabled = true
}
} else {
seccomp_enabled = false
if err = ptrace(unix.PTRACE_SETOPTIONS, tracee.PID, 0, defaultPtraceOption); err != nil {
return err
}
}
goto fall_t
case syscall.Errno(unix.SIGTRAP) | PTRACE_EVENT_SECCOMP2<<8,
syscall.Errno(unix.SIGTRAP) | unix.PTRACE_EVENT_SECCOMP<<8:
if !seccomp_detected && seccomp_enabled {
tracee.Seccomp = Enabled
seccomp_detected = true
}
if signal == (syscall.Errno(unix.SIGTRAP)|PTRACE_EVENT_SECCOMP2<<8) || signal == (syscall.Errno(unix.SIGTRAP)|unix.PTRACE_EVENT_SECCOMP<<8) {
flags := 0
signal = 0
if tracee.Seccomp != Enabled {
break
}
err := ptrace(unix.PTRACE_GETEVENTMSG, tracee.PID, 0, uintptr(unsafe.Pointer(&flags)))
if err != nil {
break
} else if flags&0x1 == 0 {
tracee.RestartHow = unix.PTRACE_CONT
tracee.TranslateSyscall()
if tracee.Seccomp == Disabling {
tracee.RestartHow = unix.PTRACE_SYSCALL
}
break
}
}
goto fall_t
case syscall.Errno(unix.SIGTRAP) | 0x80:
signal = 0
if tracee.Exe == "" {
tracee.RestartHow = unix.PTRACE_CONT
return nil
}
switch tracee.Seccomp {
case Enabled:
if tracee.Status == 0 {
tracee.RestartHow = unix.PTRACE_SYSCALL
tracee.SysexitPending = true
} else {
tracee.RestartHow = unix.PTRACE_CONT
tracee.SysexitPending = false
}
fallthrough
case Disable:
// tracee.TranslateSyscall()
if tracee.Seccomp == Disabling {
tracee.RestartHow = unix.PTRACE_SYSCALL
tracee.Seccomp = Disable
}
case Disabling:
tracee.Seccomp = Disable
if tracee.Status == 0 {
tracee.Status = 1
}
}
case syscall.Errno(unix.SIGTRAP) | unix.PTRACE_EVENT_VFORK<<8:
signal = 0
tracee.NewChild(unix.PTRACE_EVENT_VFORK)
case syscall.Errno(unix.SIGTRAP) | unix.PTRACE_EVENT_FORK<<8,
syscall.Errno(unix.SIGTRAP) | unix.PTRACE_EVENT_CLONE<<8:
signal = 0
tracee.NewChild(0)
case syscall.Errno(unix.SIGTRAP) | unix.PTRACE_EVENT_VFORK_DONE<<8,
syscall.Errno(unix.SIGTRAP) | unix.PTRACE_EVENT_EXEC<<8,
syscall.Errno(unix.SIGTRAP) | unix.PTRACE_EVENT_EXIT<<8:
signal = 0
case syscall.Errno(unix.SIGSTOP):
if tracee.Exe == "" {
tracee.Sigstop = SIGSTOP_PENDING
// signal = -1
signal = 0
}
if tracee.Sigstop == SIGSTOP_IGNORED {
tracee.Sigstop = SIGSTOP_ALLOWED
signal = 0
}
}
}
tracee.AsPtracee.Event4.Proot.Pending = true
return signal
}
func (tracee *Tracee) HandleTraceeEvent(traceeStatus unix.WaitStatus) error {
if isKernel4_8() {
return tracee.HandleTraceeEvent4_8(traceeStatus)
}
if tracee.RestartHow == 0 {
if tracee.Seccomp == Enabled && !tracee.SysexitPending {
tracee.RestartHow = unix.PTRACE_CONT
} else {
tracee.RestartHow = unix.PTRACE_SYSCALL
}
}
seccomp_dected := false
var signal error
if traceeStatus.Exited() {
tracee.RootConfig.LastExitStatus = traceeStatus.ExitStatus()
tracee.TerminateTracee()
return nil
} else if traceeStatus.Signaled() {
// check_architecture(tracee);
tracee.TerminateTracee()
} else if traceeStatus.Stopped() {
deliverSigtrap := false
signal = syscall.Errno(traceeStatus&0xfff00) >> 8
godo:
switch signal {
case syscall.Errno(unix.SIGTRAP):
if deliverSigtrap {
break
}
deliverSigtrap = true
status := ptrace(unix.PTRACE_SETOPTIONS, tracee.PID, 0, defaultPtraceOption|unix.PTRACE_O_TRACESECCOMP)
if status != nil {
status = ptrace(unix.PTRACE_SETOPTIONS, tracee.PID, 0, defaultPtraceOption)
if status != nil {
return status
}
}
goto godo
case syscall.Errno(unix.SIGTRAP) | 0x80:
signal = unix.Errno(0)
if tracee.Exe == "" {
tracee.RestartHow = unix.PTRACE_CONT
return nil
}
switch tracee.Seccomp {
case Enabled:
if tracee.Status == 0 {
tracee.RestartHow = unix.PTRACE_SYSCALL
tracee.SysexitPending = true
} else {
tracee.RestartHow = unix.PTRACE_CONT
tracee.SysexitPending = false
}
fallthrough
case Disable:
tracee.TranslateSyscall()
if tracee.Seccomp == Disabling {
tracee.RestartHow = unix.PTRACE_SYSCALL
tracee.Seccomp = Disable
}
case Disabling:
tracee.Seccomp = Disable
if tracee.Status == 0 {
tracee.Status = 1
}
}
case syscall.Errno(unix.SIGTRAP) | PTRACE_EVENT_SECCOMP2<<8,
syscall.Errno(unix.SIGTRAP) | unix.PTRACE_EVENT_SECCOMP<<8:
flags := 0
signal = unix.Errno(0)
if !seccomp_dected {
tracee.Seccomp = Enabled
seccomp_dected = true
}
if tracee.Seccomp != Enabled {
break
}
err := ptrace(unix.PTRACE_GETEVENTMSG, tracee.PID, 0, uintptr(unsafe.Pointer(&flags)))
if err != nil {
break
} else if flags&0x1 != 0 {
tracee.RestartHow = unix.PTRACE_SYSCALL
break
}
tracee.RestartHow = unix.PTRACE_CONT
tracee.TranslateSyscall()
if tracee.Seccomp == Disabling {
tracee.RestartHow = unix.PTRACE_SYSCALL
}
case syscall.Errno(unix.SIGTRAP) | unix.PTRACE_EVENT_VFORK<<8:
signal = unix.Errno(0)
tracee.NewChild(unix.PTRACE_EVENT_VFORK)
case syscall.Errno(unix.SIGTRAP) | unix.PTRACE_EVENT_FORK<<8, syscall.Errno(unix.SIGTRAP) | unix.PTRACE_EVENT_CLONE<<8:
signal = unix.Errno(0)
tracee.NewChild(0)
case syscall.Errno(unix.SIGTRAP) | unix.PTRACE_EVENT_VFORK_DONE<<8, syscall.Errno(unix.SIGTRAP) | unix.PTRACE_EVENT_EXEC<<8, syscall.Errno(unix.SIGTRAP) | unix.PTRACE_EVENT_EXIT<<8:
signal = unix.Errno(0)
case syscall.Errno(unix.SIGSTOP):
if tracee.Exe == "" {
tracee.Sigstop = SIGSTOP_PENDING
signal = unix.Errno(0)
}
if tracee.Sigstop == SIGSTOP_IGNORED {
tracee.Sigstop = SIGSTOP_ALLOWED
signal = unix.Errno(0)
}
}
}
tracee.AsPtracee.Event4.Proot.Pending = false
return signal
}

View File

@@ -0,0 +1 @@
package tracee

79
proot/proot/tracee/reg.go Normal file
View File

@@ -0,0 +1,79 @@
package tracee
import "golang.org/x/sys/unix"
type RegVersion int
const (
CURRENT RegVersion = iota
ORIGINAL
MODIFIED
NB_REG_VERSION
)
type Reg int
const (
SYSARG_NUM Reg = iota
SYSARG_1
SYSARG_2
SYSARG_3
SYSARG_4
SYSARG_5
SYSARG_6
SYSARG_RESULT
STACK_POINTER
INSTR_POINTER
RTLD_FINI
STATE_FLAGS
USERARG_1
)
var __user = &unix.PtraceRegs{}
func (tracee *Tracee) PeekReg(Version RegVersion, Reg Reg) (result uint64) {
if Version < NB_REG_VERSION {
panic("invalid version")
}
// result = REG(tracee, version, reg);
result = tracee.Reg(Version, Reg)
// Use only the 32 least significant bits (LSB) when running 32-bit processes on a 64-bit kernel.
if tracee.Is32On64() {
result &= 0xFFFFFFFF
}
return
}
// Save the @tracee's current register bank into the @version register bank.
func (tracee *Tracee) SaveCurrentRegs(Ver RegVersion) {
if Ver == ORIGINAL {
tracee.RegsWereChanged = false
}
tracee.Regs[Ver] = tracee.Regs[CURRENT]
}
func (tracee *Tracee) FetchRegs() error {
return unix.PtraceGetRegs(tracee.PID, &tracee.Regs[CURRENT])
}
func (tracee *Tracee) PushRegs() error {
if tracee.RegsWereChanged {
if tracee.RestoreOrigianlRegs {
tracee.Restore(SYSARG_NUM)
tracee.Restore(SYSARG_1)
tracee.Restore(SYSARG_2)
tracee.Restore(SYSARG_3)
tracee.Restore(SYSARG_4)
tracee.Restore(SYSARG_5)
tracee.Restore(SYSARG_6)
tracee.Restore(STACK_POINTER)
}
return unix.PtraceSetRegs(tracee.PID, &tracee.Regs[CURRENT])
}
return nil
}

View File

@@ -0,0 +1,66 @@
//go:build 386 && (linux || android)
package tracee
import (
"unsafe"
)
/**
* Compute the offset of the register @reg_name in the USER area.
*/
// #define USER_REGS_OFFSET(reg_name) \
// (offsetof(struct user, regs) \
// + offsetof(struct user_regs_struct, reg_name))
// #define REG(tracee, version, index) \
// (*(word_t*) (((uint8_t *) &tracee->_regs[version]) + reg_offset[index]))
// static off_t reg_offset[] = {
// [SYSARG_NUM] = USER_REGS_OFFSET(orig_eax),
// [SYSARG_1] = USER_REGS_OFFSET(ebx),
// [SYSARG_2] = USER_REGS_OFFSET(ecx),
// [SYSARG_3] = USER_REGS_OFFSET(edx),
// [SYSARG_4] = USER_REGS_OFFSET(esi),
// [SYSARG_5] = USER_REGS_OFFSET(edi),
// [SYSARG_6] = USER_REGS_OFFSET(ebp),
// [SYSARG_RESULT] = USER_REGS_OFFSET(eax),
// [STACK_POINTER] = USER_REGS_OFFSET(esp),
// [INSTR_POINTER] = USER_REGS_OFFSET(eip),
// [RTLD_FINI] = USER_REGS_OFFSET(edx),
// [STATE_FLAGS] = USER_REGS_OFFSET(eflags),
// [USERARG_1] = USER_REGS_OFFSET(eax),
// };
var RegOffset = []uintptr{
SYSARG_NUM: unsafe.Offsetof(__user.Orig_eax),
SYSARG_1: unsafe.Offsetof(__user.Ebx),
SYSARG_2: unsafe.Offsetof(__user.Ecx),
SYSARG_3: unsafe.Offsetof(__user.Edx),
SYSARG_4: unsafe.Offsetof(__user.Esi),
SYSARG_5: unsafe.Offsetof(__user.Edi),
SYSARG_6: unsafe.Offsetof(__user.Ebp),
SYSARG_RESULT: unsafe.Offsetof(__user.Eax),
STACK_POINTER: unsafe.Offsetof(__user.Esp),
INSTR_POINTER: unsafe.Offsetof(__user.Eip),
RTLD_FINI: unsafe.Offsetof(__user.Edx),
STATE_FLAGS: unsafe.Offsetof(__user.Eflags),
USERARG_1: unsafe.Offsetof(__user.Eax),
}
func (tracee *Tracee) Reg(Version RegVersion, Reg Reg) uint64 {
return uint64(uintptr(unsafe.Pointer(&tracee.Regs[Version])) + uintptr(RegOffset[Reg]))
}
func (tracee *Tracee) PokeReg(Version RegVersion, Reg Reg, Value uint64) {
if tracee.PeekReg(CURRENT, Reg) == Value {
return
}
// REG(tracee, CURRENT, reg) = value;
*(*uint64)(unsafe.Add(unsafe.Pointer(&tracee.Regs[Version]), int(RegOffset[Reg]))) = Value
tracee.RegsWereChanged = true
}
func (tracee *Tracee) Restore(Reg Reg) {
(*(*uint64)(unsafe.Add(unsafe.Pointer(&tracee.Regs[CURRENT]), int(RegOffset[Reg])))) = tracee.Reg(ORIGINAL, Reg)
}

View File

@@ -0,0 +1,119 @@
//go:build amd64 && (linux || android)
package tracee
import (
"unsafe"
)
// /**
// * Compute the offset of the register @reg_name in the USER area.
// */
// #define USER_REGS_OFFSET(reg_name) \
// (offsetof(struct user, regs) \
// + offsetof(struct user_regs_struct, reg_name))
// static off_t reg_offset[] = {
// [SYSARG_NUM] = USER_REGS_OFFSET(orig_rax),
// [SYSARG_1] = USER_REGS_OFFSET(rdi),
// [SYSARG_2] = USER_REGS_OFFSET(rsi),
// [SYSARG_3] = USER_REGS_OFFSET(rdx),
// [SYSARG_4] = USER_REGS_OFFSET(r10),
// [SYSARG_5] = USER_REGS_OFFSET(r8),
// [SYSARG_6] = USER_REGS_OFFSET(r9),
// [SYSARG_RESULT] = USER_REGS_OFFSET(rax),
// [STACK_POINTER] = USER_REGS_OFFSET(rsp),
// [INSTR_POINTER] = USER_REGS_OFFSET(rip),
// [RTLD_FINI] = USER_REGS_OFFSET(rdx),
// [STATE_FLAGS] = USER_REGS_OFFSET(eflags),
// [USERARG_1] = USER_REGS_OFFSET(rdi),
// };
// static off_t reg_offset_x86[] = {
// [SYSARG_NUM] = USER_REGS_OFFSET(orig_rax),
// [SYSARG_1] = USER_REGS_OFFSET(rbx),
// [SYSARG_2] = USER_REGS_OFFSET(rcx),
// [SYSARG_3] = USER_REGS_OFFSET(rdx),
// [SYSARG_4] = USER_REGS_OFFSET(rsi),
// [SYSARG_5] = USER_REGS_OFFSET(rdi),
// [SYSARG_6] = USER_REGS_OFFSET(rbp),
// [SYSARG_RESULT] = USER_REGS_OFFSET(rax),
// [STACK_POINTER] = USER_REGS_OFFSET(rsp),
// [INSTR_POINTER] = USER_REGS_OFFSET(rip),
// [RTLD_FINI] = USER_REGS_OFFSET(rdx),
// [STATE_FLAGS] = USER_REGS_OFFSET(eflags),
// [USERARG_1] = USER_REGS_OFFSET(rax),
// };
// #define REG(tracee, version, index) \
// (*(word_t*) (tracee->_regs[version].cs == 0x23 \
// ? (((uint8_t *) &tracee->_regs[version]) + reg_offset_x86[index]) \
// : (((uint8_t *) &tracee->_regs[version]) + reg_offset[index])))
var (
RegOffset = []uintptr{
SYSARG_NUM: unsafe.Offsetof(__user.Orig_rax),
SYSARG_1: unsafe.Offsetof(__user.Rdi),
SYSARG_2: unsafe.Offsetof(__user.Rsi),
SYSARG_3: unsafe.Offsetof(__user.Rdx),
SYSARG_4: unsafe.Offsetof(__user.R10),
SYSARG_5: unsafe.Offsetof(__user.R8),
SYSARG_6: unsafe.Offsetof(__user.R9),
SYSARG_RESULT: unsafe.Offsetof(__user.Rax),
STACK_POINTER: unsafe.Offsetof(__user.Rsp),
INSTR_POINTER: unsafe.Offsetof(__user.Rip),
RTLD_FINI: unsafe.Offsetof(__user.Rdx),
STATE_FLAGS: unsafe.Offsetof(__user.Eflags),
USERARG_1: unsafe.Offsetof(__user.Rdi),
}
RegOffsetX86 = []uintptr{
SYSARG_NUM: unsafe.Offsetof(__user.Orig_rax),
SYSARG_1: unsafe.Offsetof(__user.Rbx),
SYSARG_2: unsafe.Offsetof(__user.Rcx),
SYSARG_3: unsafe.Offsetof(__user.Rdx),
SYSARG_4: unsafe.Offsetof(__user.Rsi),
SYSARG_5: unsafe.Offsetof(__user.Rdi),
SYSARG_6: unsafe.Offsetof(__user.Rbp),
SYSARG_RESULT: unsafe.Offsetof(__user.Rax),
STACK_POINTER: unsafe.Offsetof(__user.Rsp),
INSTR_POINTER: unsafe.Offsetof(__user.Rip),
RTLD_FINI: unsafe.Offsetof(__user.Rdx),
STATE_FLAGS: unsafe.Offsetof(__user.Eflags),
USERARG_1: unsafe.Offsetof(__user.Rax),
}
)
func (tracee *Tracee) Reg(Version RegVersion, Reg Reg) uint64 {
if Version < NB_REG_VERSION {
panic("invalid version")
}
if tracee.Regs[Version].Cs == 0x23 {
return uint64(uintptr(unsafe.Pointer(&tracee.Regs[Version])) + uintptr(RegOffsetX86[Reg]))
}
return uint64(uintptr(unsafe.Pointer(&tracee.Regs[Version])) + uintptr(RegOffset[Reg]))
}
func (tracee *Tracee) PokeReg(Version RegVersion, Reg Reg, Value uint64) {
if tracee.PeekReg(CURRENT, Reg) == Value {
return
}
// REG(tracee, CURRENT, reg) = value;
var unpoint unsafe.Pointer
if tracee.Regs[Version].Cs == 0x23 {
unpoint = unsafe.Add(unsafe.Pointer(&tracee.Regs[Version]), int(RegOffsetX86[Reg]))
} else {
unpoint = unsafe.Add(unsafe.Pointer(&tracee.Regs[Version]), int(RegOffset[Reg]))
}
*(*uint64)(unpoint) = Value
tracee.RegsWereChanged = true
}
func (tracee *Tracee) Restore(Reg Reg) {
if tracee.Regs[CURRENT].Cs == 0x23 {
(*(*uint64)(unsafe.Add(unsafe.Pointer(&tracee.Regs[CURRENT]), int(RegOffsetX86[Reg])))) = tracee.Reg(ORIGINAL, Reg)
}
(*(*uint64)(unsafe.Add(unsafe.Pointer(&tracee.Regs[CURRENT]), int(RegOffset[Reg])))) = tracee.Reg(ORIGINAL, Reg)
}

View File

@@ -0,0 +1,66 @@
//go:build arm && (linux || android)
package tracee
import (
"unsafe"
)
// /**
// * Compute the offset of the register @reg_name in the USER area.
// */
// #define USER_REGS_OFFSET(reg_name) \
// (offsetof(struct user, regs) \
// + offsetof(struct user_regs_struct, reg_name))
// #define REG(tracee, version, index) \
// (*(word_t*) (((uint8_t *) &tracee->_regs[version]) + reg_offset[index]))
// off_t reg_offset[] = {
// [SYSARG_NUM] = USER_REGS_OFFSET(uregs[7]),
// [SYSARG_1] = USER_REGS_OFFSET(uregs[0]),
// [SYSARG_2] = USER_REGS_OFFSET(uregs[1]),
// [SYSARG_3] = USER_REGS_OFFSET(uregs[2]),
// [SYSARG_4] = USER_REGS_OFFSET(uregs[3]),
// [SYSARG_5] = USER_REGS_OFFSET(uregs[4]),
// [SYSARG_6] = USER_REGS_OFFSET(uregs[5]),
// [SYSARG_RESULT] = USER_REGS_OFFSET(uregs[0]),
// [STACK_POINTER] = USER_REGS_OFFSET(uregs[13]),
// [INSTR_POINTER] = USER_REGS_OFFSET(uregs[15]),
// [USERARG_1] = USER_REGS_OFFSET(uregs[0]),
// };
var RegOffset = []uintptr{
SYSARG_NUM: unsafe.Offsetof(__user.Uregs) + uintptr(7*unsafe.Sizeof(__user.Uregs[0])),
SYSARG_1: unsafe.Offsetof(__user.Uregs) + uintptr(0*unsafe.Sizeof(__user.Uregs[0])),
SYSARG_2: unsafe.Offsetof(__user.Uregs) + uintptr(1*unsafe.Sizeof(__user.Uregs[0])),
SYSARG_3: unsafe.Offsetof(__user.Uregs) + uintptr(2*unsafe.Sizeof(__user.Uregs[0])),
SYSARG_4: unsafe.Offsetof(__user.Uregs) + uintptr(3*unsafe.Sizeof(__user.Uregs[0])),
SYSARG_5: unsafe.Offsetof(__user.Uregs) + uintptr(4*unsafe.Sizeof(__user.Uregs[0])),
SYSARG_6: unsafe.Offsetof(__user.Uregs) + uintptr(5*unsafe.Sizeof(__user.Uregs[0])),
SYSARG_RESULT: unsafe.Offsetof(__user.Uregs) + uintptr(0*unsafe.Sizeof(__user.Uregs[0])),
STACK_POINTER: unsafe.Offsetof(__user.Uregs) + uintptr(13*unsafe.Sizeof(__user.Uregs[0])),
INSTR_POINTER: unsafe.Offsetof(__user.Uregs) + uintptr(15*unsafe.Sizeof(__user.Uregs[0])),
USERARG_1: unsafe.Offsetof(__user.Uregs) + uintptr(0*unsafe.Sizeof(__user.Uregs[0])),
}
func (tracee *Tracee) Reg(Version RegVersion, Reg Reg) uint64 {
return uint64(uintptr(unsafe.Pointer(&tracee.Regs[Version])) + uintptr(RegOffset[Reg]))
}
func (tracee *Tracee) PokeReg(Version RegVersion, Reg Reg, Value uint64) {
if tracee.PeekReg(CURRENT, Reg) == Value {
return
}
// REG(tracee, CURRENT, reg) = value;
*(*uint64)(unsafe.Add(unsafe.Pointer(&tracee.Regs[Version]), int(RegOffset[Reg]))) = Value
tracee.RegsWereChanged = true
}
// #define RESTORE(sysarg) (void) (reg_offset[SYSARG_RESULT] != reg_offset[sysarg] && (REG(tracee, CURRENT, sysarg) = REG(tracee, ORIGINAL, sysarg)))
func (tracee *Tracee) Restore(Reg Reg) {
if RegOffset[SYSARG_RESULT] != RegOffset[Reg] {
(*(*uint64)(unsafe.Add(unsafe.Pointer(&tracee.Regs[CURRENT]), int(RegOffset[Reg])))) = tracee.Reg(ORIGINAL, Reg)
}
}

View File

@@ -0,0 +1,67 @@
//go:build arm64 && (linux || android)
package tracee
import (
"unsafe"
)
/**
* Compute the offset of the register @reg_name in the USER area.
*/
// #define USER_REGS_OFFSET(reg_name) \
// (offsetof(struct user, regs) \
// + offsetof(struct user_regs_struct, reg_name))
// #define REG(tracee, version, index) \
// (*(word_t*) (((uint8_t *) &tracee->_regs[version]) + reg_offset[index]))
// #undef USER_REGS_OFFSET
// #define USER_REGS_OFFSET(reg_name) offsetof(struct user_regs_struct, reg_name)
// static off_t reg_offset[] = {
// [SYSARG_NUM] = USER_REGS_OFFSET(regs[8]),
// [SYSARG_1] = USER_REGS_OFFSET(regs[0]),
// [SYSARG_2] = USER_REGS_OFFSET(regs[1]),
// [SYSARG_3] = USER_REGS_OFFSET(regs[2]),
// [SYSARG_4] = USER_REGS_OFFSET(regs[3]),
// [SYSARG_5] = USER_REGS_OFFSET(regs[4]),
// [SYSARG_6] = USER_REGS_OFFSET(regs[5]),
// [SYSARG_RESULT] = USER_REGS_OFFSET(regs[0]),
// [STACK_POINTER] = USER_REGS_OFFSET(sp),
// [INSTR_POINTER] = USER_REGS_OFFSET(pc),
// [USERARG_1] = USER_REGS_OFFSET(regs[0]),
// };
var RegOffset = []uintptr{
// + uintptr(7*unsafe.Sizeof(__user.Uregs[0]))
SYSARG_NUM: unsafe.Offsetof(__user.Regs) + uintptr(8*unsafe.Sizeof(__user.Regs[0])),
SYSARG_1: unsafe.Offsetof(__user.Regs) + uintptr(0*unsafe.Sizeof(__user.Regs[0])),
SYSARG_2: unsafe.Offsetof(__user.Regs) + uintptr(1*unsafe.Sizeof(__user.Regs[0])),
SYSARG_3: unsafe.Offsetof(__user.Regs) + uintptr(2*unsafe.Sizeof(__user.Regs[0])),
SYSARG_4: unsafe.Offsetof(__user.Regs) + uintptr(3*unsafe.Sizeof(__user.Regs[0])),
SYSARG_5: unsafe.Offsetof(__user.Regs) + uintptr(4*unsafe.Sizeof(__user.Regs[0])),
SYSARG_6: unsafe.Offsetof(__user.Regs) + uintptr(5*unsafe.Sizeof(__user.Regs[0])),
SYSARG_RESULT: unsafe.Offsetof(__user.Regs) + uintptr(0*unsafe.Sizeof(__user.Regs[0])),
STACK_POINTER: unsafe.Offsetof(__user.Sp),
INSTR_POINTER: unsafe.Offsetof(__user.Pc),
USERARG_1: unsafe.Offsetof(__user.Regs) + uintptr(0*unsafe.Sizeof(__user.Regs[0])),
}
func (tracee *Tracee) Reg(Version RegVersion, Reg Reg) uint64 {
return uint64(uintptr(unsafe.Pointer(&tracee.Regs[Version])) + uintptr(RegOffset[Reg]))
}
func (tracee *Tracee) PokeReg(Version RegVersion, Reg Reg, Value uint64) {
if tracee.PeekReg(CURRENT, Reg) == Value {
return
}
// REG(tracee, CURRENT, reg) = value;
*(*uint64)(unsafe.Add(unsafe.Pointer(&tracee.Regs[Version]), int(RegOffset[Reg]))) = Value
tracee.RegsWereChanged = true
}
func (tracee *Tracee) Restore(Reg Reg) {
if RegOffset[SYSARG_RESULT] != RegOffset[Reg] {
(*(*uint64)(unsafe.Add(unsafe.Pointer(&tracee.Regs[CURRENT]), int(RegOffset[Reg])))) = tracee.Reg(ORIGINAL, Reg)
}
}

View File

@@ -0,0 +1,379 @@
//go:build linux || android
package tracee
import (
"syscall"
"golang.org/x/sys/unix"
"sirherobrine23.com.br/go-bds/exec/proot/proot/execve"
"sirherobrine23.com.br/go-bds/exec/proot/proot/path"
)
const HostRootfs string = "/host-rootfs"
// struct { struct tracee *le_next; struct tracee **le_prev; }
type TraceeLink struct {
Next *Tracee
Prev **Tracee
}
type WaitsIn int
const (
DOESNT_WAIT = iota
WAITS_IN_KERNEL
WAITS_IN_PROOT
)
type AsPtracer struct {
NbPtracees int
Zombies struct{ lh_first *Tracee }
WaitPID int
WaitOption uint
WaitsIn WaitsIn
}
type AsPtracerEvent struct {
Value int
Pending bool
}
type AsPtracee struct {
Ptracer *Tracee
Event4 struct {
Proot AsPtracerEvent
Ptracer AsPtracerEvent
}
TracingStarted bool
IgnoreLoeaderSyscall bool
IgnoreSyscall bool
Option uint
IsZombie bool
}
type Sigstop int
const (
SIGSTOP_IGNORED Sigstop = iota
SIGSTOP_ALLOWED
SIGSTOP_PENDING
)
type Seccomp int
const (
Disable Seccomp = iota
Disabling
Enabled
)
// Information related to a file-system name-space.
type FileSystemNameSpace struct {
Cwd string // Current working directory, à la /proc/self/pwd.
Bindings struct {
// List of bindings as specified by the user but not canonicalized yet.
Pending *path.Bindings
// List of bindings canonicalized and sorted in the "guest" order.
Guest *path.Binding
// List of bindings canonicalized and sorted in the "host" order.
Host *path.Bindings
}
}
// Virtual heap, emulated with a regular memory mapping.
type Heap struct {
Base uint64
Size uint64
Disabled bool
}
type Tracees struct {
First *Tracee
}
type TraceeRoot struct {
Tracees Tracees
NextVPID uint
NoSeccmp bool
LastExitStatus int
}
// Information related to a tracee process.
type Tracee struct {
RootConfig *TraceeRoot
Link TraceeLink // Link for the list of all tracees.
PID int // Process identifier.
Vpid uint // Unique tracee identifier.
Running bool // Is it currently running or not?
Terminated bool // Is this tracee ready to be freed? TODO: move to a list dedicated to terminated tracees instead.
KillallOnExit bool // Whether termination of this tracee implies an immediate kill of all tracees.
Parent *Tracee // Parent of this tracee, NULL if none.
Clone bool // Is it a "clone", i.e has the same parent as its creator.
AsPtracer AsPtracer // Support for ptrace emulation (tracer side).
AsPtracee AsPtracee // Support for ptrace emulation (tracee side).
/* Current status:
* 0: enter syscall
* 1: exit syscall no error
* -errno: exit syscall with error.
*/
Status int
/*
#define IS_IN_SYSENTER(tracee) ((tracee)->status == 0)
#define IS_IN_SYSEXIT(tracee) (!IS_IN_SYSENTER(tracee))
#define IS_IN_SYSEXIT2(tracee, sysnum) (IS_IN_SYSEXIT(tracee) && get_sysnum((tracee), ORIGINAL) == sysnum)
*/
// PTRACE_REQUEST_TYPE
RestartHow int // How this tracee is restarted.
/* Value of the tracee's general purpose registers. */
// struct user_regs_struct _regs[NB_REG_VERSION];
// bool _regs_were_changed;
// bool restore_original_regs;
RestoreOrigianlRegs bool
RegsWereChanged bool
Regs [NB_REG_VERSION]unix.PtraceRegs
Sigstop Sigstop // State for the special handling of SIGSTOP.
GlueType uint // Specify the type of the final component during the initialization of a binding. This variable is first defined in bind_path() then used in build_glue().
// During a sub-reconfiguration, the new setup is relatively to @tracee's file-system name-space. Also, @paths holds its $PATH environment variable in order to emulate the execvp(3) behavior.
Reconf struct {
Tracee *Tracee
paths string
}
// Unrequested syscalls inserted by PRoot after an actual syscall.
Chain struct {
// struct chained_syscalls *syscalls;
Syscalls *any // Assuming chained_syscalls is a slice of some `any`
ForceFinalResult bool
FinalResult uint64
}
// Load info generated during execve sysenter and used during execve sysexit.
LoadInfo *execve.LoadInfo
MixedMode bool // Disable mixed-execution (native host) check
Verbose int // Verbosity level.
Seccomp Seccomp // State of the seccomp acceleration for this tracee.
SysexitPending bool // Ensure the sysexit stage is always hit under seccomp.
FS *FileSystemNameSpace // Information related to a file-system name-space.
Heap *Heap // Virtual heap, emulated with a regular memory mapping.
// Path to the executable, à la /proc/self/exe.
Exe, NewExe string
Qemu *string // Runner command-line.
Glue string // Path to glue between the guest rootfs and the host rootfs.
// List of extensions enabled for this tracee.
// struct extensions *extensions;
HostLDSOPaths, GuestLDSOPaths string // For the mixed-mode, the guest LD_LIBRARY_PATH is saved during the "guest -> host" transition, in order to be restored during the "host -> guest" transition (only if the host LD_LIBRARY_PATH hasn't changed).
ToolName string // For diagnostic purpose.
}
/*
extern Tracee *get_tracee(const Tracee *tracee, pid_t pid, bool create);
extern Tracee *get_ptracee(const Tracee *ptracer, pid_t pid, bool only_stopped, bool only_with_pevent, word_t wait_options);
extern Tracee *get_stopped_ptracee(const Tracee *ptracer, pid_t pid, bool only_with_pevent, word_t wait_options);
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(Tracee *tracee);
extern int swap_config(Tracee *tracee1, Tracee *tracee2);
extern void kill_all_tracees(Tracee *tracee);
*/
// #define PTRACEE (ptracee->as_ptracee)
// #define PTRACER (ptracer->as_ptracer)
func (tracee *Tracee) Ptracer() *AsPtracer { return &tracee.AsPtracer }
func (tracee *Tracee) Ptracee() *AsPtracee { return &tracee.AsPtracee }
func NewDummyTracee() (tracee *Tracee) {
tracee = new(Tracee)
tracee.Link = TraceeLink{}
tracee.FS = new(FileSystemNameSpace)
tracee.Heap = new(Heap)
return
}
func newTracee(pid int, currentTracee *Tracee) *Tracee {
if currentTracee == nil {
currentTracee = NewDummyTracee()
currentTracee.RootConfig = &TraceeRoot{Tracees: Tracees{}, NextVPID: 1}
}
tracee := NewDummyTracee()
tracee.PID = pid
currentTracee.RootConfig.NextVPID++
tracee.Vpid = currentTracee.RootConfig.NextVPID
/*
do {
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(true)
*/
if tracee.Link.Next = currentTracee.RootConfig.Tracees.First; tracee.Link.Next != nil {
tracee.Link.Next.Link.Prev = &tracee.Link.Next
}
currentTracee.RootConfig.Tracees.First = tracee
tracee.Link.Prev = &currentTracee.RootConfig.Tracees.First
return tracee
}
// Return the entry related to the tracee @pid. If no entry were found, a new one is created if @create is true, otherwise NULL is returned.
func (tracee *Tracee) GetTracee(Pid int, create bool) *Tracee {
if tracee != nil && tracee.PID == Pid {
return tracee
}
for tracee := tracee.RootConfig.Tracees.First; tracee != nil; tracee = tracee.Link.Next {
if tracee.PID == Pid {
return tracee
}
}
if create {
return newTracee(Pid, tracee)
}
return nil
}
// #define EXPECTED_WAIT_CLONE(wait_options, tracee) \
// ((((wait_options) & __WALL) != 0) || ((((wait_options) & __WCLONE) != 0) && (tracee)->clone) || ((((wait_options) & __WCLONE) == 0) && !(tracee)->clone))
func ExpectedWaitClone(wait_options uint, tracee *Tracee) bool {
return (((wait_options & syscall.WALL) != 0) ||
(((wait_options & syscall.WCLONE) != 0) && tracee.Clone) ||
(((wait_options & syscall.WCLONE) == 0) && !tracee.Clone))
}
// Return the first [stopped?] tracee with the given @pid (-1 for any) which has the given @ptracer,
// and which has a pending event for its ptracer if @only_with_pevent is true.
// See wait(2) manual for the meaning of @wait_options. This function returns NULL if there's no such ptracee.
func (tracee *Tracee) GetPtracee(Pid int, onlyStopped, onlyWithPevent bool, wait_options uint) *Tracee {
if tracee == nil {
return nil
}
// Zombies first
// for
for ptracee := tracee.AsPtracer.Zombies.lh_first; ptracee != nil; ptracee = ptracee.Link.Next {
if ptracee.PID != Pid {
continue
}
if !ExpectedWaitClone(wait_options, ptracee) {
continue
}
return ptracee
}
for ptracee := tracee.RootConfig.Tracees.First; ptracee != nil; ptracee = ptracee.Link.Next {
// if ((ptracee->as_ptracee).ptracer != ptracer)
// continue;
// if (pid != ptracee->pid && pid != -1)
// continue;
// if (!EXPECTED_WAIT_CLONE(wait_options, ptracee))
// continue;
// if (!only_stopped)
// return ptracee;
// if (ptracee->running)
// continue;
// if ((ptracee->as_ptracee).event4.ptracer.pending || !only_with_pevent)
// return ptracee;
// if (pid == ptracee->pid)
// return NULL;
if ptracee.PID != Pid {
continue
}
if !ExpectedWaitClone(wait_options, ptracee) {
continue
}
if !onlyStopped {
return ptracee
}
if ptracee.Running {
continue
}
if ptracee.AsPtracee.Event4.Ptracer.Pending || !onlyWithPevent {
return ptracee
}
if ptracee.PID == Pid {
return nil
}
}
return nil
}
// Wrapper for get_ptracee(), this ensures only a stopped tracee is returned (or NULL).
func (tracee *Tracee) GetStoppedPtracee(Pid int, onlyWithPevent bool, wait_options uint) *Tracee {
return tracee.GetPtracee(Pid, true, onlyWithPevent, wait_options)
}
// Wrapper for get_ptracee(), this ensures no running tracee is returned.
func (tracee *Tracee) HasPtracees(Pid int, wait_options uint) bool {
return tracee.GetPtracee(Pid, false, false, wait_options) != nil
}
// Mark tracee as terminated and optionally take action.
func (tracee *Tracee) TerminateTracee() {
tracee.Terminated = true
if tracee.KillallOnExit {
tracee.KillAllTracees()
}
}
// Free all tracees marked as terminated.
func (tracee *Tracee) FreeTerminatedTracees() {
next := tracee.RootConfig.Tracees.First
for next != nil {
tracee := next
next = tracee.Link.Next
if tracee.Terminated {
tracee.TerminateTracee()
}
}
}
// Send the KILL signal to all tracees.
func (tracee *Tracee) KillAllTracees() {
for tracee := tracee.RootConfig.Tracees.First; tracee != nil; tracee = tracee.Link.Next {
syscall.Kill(tracee.PID, syscall.SIGKILL)
}
}
// Make new @parent's child inherit from it. Depending on @clone_flags,
// some information are copied or shared.
// This function returns -errno if an error occured, otherwise 0.
func (tracee *Tracee) NewChild(cloneFlags uint) error {
return nil
}