From f4a8e4aefd3035517fb130ed942e41295fe5874a Mon Sep 17 00:00:00 2001 From: rafael-santiago Date: Sun, 22 Aug 2021 19:54:27 -0300 Subject: [PATCH] Speed up strace detection on Linux This commit also includes gopkg/v2 --- README.md | 21 +- RELNOTES.txt | 11 + doc/todo.txt | 1 + gopkg/v2/aegis.go | 93 +++++ gopkg/v2/aegis_test.go | 652 ++++++++++++++++++++++++++++++++ gopkg/v2/go.mod | 3 + gopkg/v2/test_utils_unix.go | 47 +++ gopkg/v2/test_utils_windows.go | 36 ++ src/Forgefile.hsl | 6 +- src/Toolsets.hsl | 2 +- src/aegis.h | 2 +- src/native/linux/aegis_native.c | 20 +- src/samples/setgorgon.c | 1 - 13 files changed, 870 insertions(+), 25 deletions(-) create mode 100644 gopkg/v2/aegis.go create mode 100644 gopkg/v2/aegis_test.go create mode 100644 gopkg/v2/go.mod create mode 100644 gopkg/v2/test_utils_unix.go create mode 100644 gopkg/v2/test_utils_windows.go diff --git a/README.md b/README.md index 2d1e1f5..7583308 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,6 @@ as an ``anti-debugging`` stuff. - [``Aegis`` from ``Go``](#aegis-from-go) - [``wait4debug`` on ``Go``](#wait4debug-on-go) - [What about a ``Gopher Gorgon``?](#what-about-a-gopher-gorgon) -- [Contributors](#contributors) ## How can I build it? @@ -76,6 +75,14 @@ black-beard@QueensAnneRevenge:~/src/aegis/src# _ If all has occurred fine during your build, ``aegis`` library was built at ``../lib`` sub-directory. Additionaly, test has ran and all samples was built at ``../samples`` sub-directory. +In order to skip tests you must invoke ``Hefesto`` with the option ``--no-tests``: + +``` +black-beard@QueensAnneRevenge:~/src/aegis/src# hefesto --no-tests +(...) +black-beard@QueensAnneRevenge:~/src/aegis/src# _ +``` + [``Back``](#contents) ### Poor man's build by using ``make`` @@ -406,7 +413,7 @@ After you will define in your ``go.mod`` the following: ``` (...) -replace github.com/rafael-santiago/aegis/gopkg => github.com/rafael-santiago/aegis/gopkg/v1 +replace github.com/rafael-santiago/aegis/gopkg => github.com/rafael-santiago/aegis/gopkg/v2 (...) ``` @@ -535,13 +542,3 @@ func main() { The program will run until detecting a debugger be attached or being asked for gracefully exiting through a ``ctrl + C``. [``Back``](#contents) - -## Contributors - -The following table lists all project's contributors until now. - -| **GitHub profile** | **Who** | **Contact** | **Contributions** | -|:------------------------------:|:-----------------------|:---------------------------:|:-----------------------------------------------------------------------:| -|[](https://github.com/rafael-santiago)|Rafael Santiago|``/dev/null``|Initial idea, ``C library``, initial ``cgo-bind``, current maintainer.| - -[``Back``](#contents) diff --git a/RELNOTES.txt b/RELNOTES.txt index ba7b237..08478d6 100644 --- a/RELNOTES.txt +++ b/RELNOTES.txt @@ -3,6 +3,17 @@ Rafael -- +v2 [git-tag: 'v2'] + + Features: + + - Improvement for Linux on detecting strace. + - Adding --no-tests build option. + + Bugfixes: + + - None! + v1 [git-tag: 'v1'] Features: diff --git a/doc/todo.txt b/doc/todo.txt index 60cdf75..3746a2c 100644 --- a/doc/todo.txt +++ b/doc/todo.txt @@ -1,3 +1,4 @@ +x (A) Speed up strace detection on Linux. +Core,+Improvement x (B) Run go fmt over all go sources. +Core,+Housekeeping x (B) Document go sources. +Core,+Documentation x (A) Implement tests for gopkg. +Core,+Test diff --git a/gopkg/v2/aegis.go b/gopkg/v2/aegis.go new file mode 100644 index 0000000..8445368 --- /dev/null +++ b/gopkg/v2/aegis.go @@ -0,0 +1,93 @@ +// package aegis gathers all constants, types and functions related to libaegis cgo bind. +// -- +// Copyright (c) 2020, Rafael Santiago +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. +// +package aegis + +/* +#cgo CFLAGS: -I../../src -DCGO=1 +#include +#include +#if defined(__linux__) +# include +#elif defined(__FreeBSD__) +# include +#elif defined(__NetBSD__) +# include +#elif defined(__OpenBSD__) +# include +#elif defined(_WIN32) +# include +#endif +*/ +import "C" +import ( + "os" + "time" +) + +// AegisGorgonExitFunc defines the type of exit oracle function called by Aegis' anti-debugging gorgon. +type AegisGorgonExitFunc func(args interface{}) bool + +// AegisGorgonOnDebuggerFunc defines the type of OnDebugger functions that will be triggered by Aegis' during a debugging +// attempting. +type AegisGorgonOnDebuggerFunc func(args interface{}) + +// HasDebugger is a Go wrapper for aegis_has_debugger() from libaegis. HasDebugger returns true is a debugger is +// detected otherwise (guess what?) false. +func HasDebugger() bool { + return (C.aegis_has_debugger() == 1) +} + +// SetGorgon is a Go native implementation of aegis_set_gorgon(). This function installs a goroutine responsible for watching +// out a debugging attempt. The argument exitFunc is a function that verifies if it is time to gracefully exiting. Its +// arguments is the 'generic' argument exitFuncArgs. The argument onDebuggerFunc is a function that takes some action when a +// debugger is detected. Its arguments is the 'generic' argument onDebuggerFuncArgs. When onDebuggerFunc is nil Aegis will +// use its internal default onDebuggerFunc (defaultOnDebugger). +func SetGorgon(exitFunc AegisGorgonExitFunc, exitFuncArgs interface{}, + onDebuggerFunc AegisGorgonOnDebuggerFunc, onDebuggerFuncArgs interface{}) { + var onDebugger AegisGorgonOnDebuggerFunc + + if onDebuggerFunc != nil { + onDebugger = onDebuggerFunc + } else { + onDebugger = defaultOnDebugger + } + + gorgonRoutine := func(exitFunc AegisGorgonExitFunc, + exitFuncArgs interface{}, + onDebuggerFunc AegisGorgonOnDebuggerFunc, + onDebuggerFuncArgs interface{}, done chan bool) { + var stop bool = false + for !stop { + if C.aegis_has_debugger() == 1 { + // INFO(Rafael): There is no way to know what user is intending on doing on OnDebugger. + // Anyway, on sane anti-debugging mitigations we need to exit process. + // Since we are probably exiting here (in a panic situation), there is no + // problem on leaking this go routine, but for conscience's sake let's try + // to exit more gracefully as possible. + defer onDebugger(onDebuggerFuncArgs) + stop = true + } + if !stop && exitFunc != nil { + stop = exitFunc(exitFuncArgs) + } + time.Sleep(1 * time.Nanosecond) + } + done <- true + } + + done := make(chan bool, 1) + + go gorgonRoutine(exitFunc, exitFuncArgs, onDebugger, onDebuggerFuncArgs, done) + <-done +} + +// defaultOnDebugger is the internal Aegis onDebuggerFunc. It is rather gross, being only about an os.Exit(1) and period. +func defaultOnDebugger(args interface{}) { + os.Exit(1) +} diff --git a/gopkg/v2/aegis_test.go b/gopkg/v2/aegis_test.go new file mode 100644 index 0000000..371ad66 --- /dev/null +++ b/gopkg/v2/aegis_test.go @@ -0,0 +1,652 @@ +// +// Copyright (c) 2020, Rafael Santiago +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. +// +package aegis + +import ( + "fmt" + "io" + "os" + "os/exec" + "runtime" + "testing" + "time" +) + +const kTestSleepInSecs = 1 + +const kNextAttemptsNr = 65535 + +func hasGDB() bool { + cmd := exec.Command("gdb", "--version") + _, err := cmd.CombinedOutput() + return (err == nil) +} + +func hasLLDB() bool { + cmd := exec.Command("lldb", "--version") + _, err := cmd.CombinedOutput() + return (err == nil) +} + +func gdb() (io.WriteCloser, error) { + cmd := exec.Command("gdb") + go cmd.CombinedOutput() + return cmd.StdinPipe() +} + +func gdbAttach(gdb io.WriteCloser, pid int) error { + _, err := io.WriteString(gdb, fmt.Sprintf("attach %d\n", pid)) + return err +} + +func gdbContinue(gdb io.WriteCloser) error { + _, err := io.WriteString(gdb, fmt.Sprintf("continue\n")) + return err +} + +func gdbNext(gdb io.WriteCloser) error { + _, err := io.WriteString(gdb, fmt.Sprintf("next\n")) + return err +} + +func lldb() (io.WriteCloser, error) { + cmd := exec.Command("lldb") + go cmd.CombinedOutput() + return cmd.StdinPipe() +} + +func lldbAttach(lldb io.WriteCloser, pid int) error { + _, err := io.WriteString(lldb, fmt.Sprintf("attach --pid %d\n", pid)) + return err +} + +func lldbContinue(lldb io.WriteCloser) error { + return gdbContinue(lldb) +} + +func lldbNext(lldb io.WriteCloser) error { + return gdbNext(lldb) +} + +func isProcessRunning(pid int) bool { + return isProcessRunningNative(pid) +} + +func TestHasDebugger(t *testing.T) { + runGDBTests := hasGDB() + if runtime.GOOS == "openbsd" { + if runGDBTests == false { + t.Error(`runGDBTests == false`) + } + if hasDebuggerOpenBSD() == false { + t.Error(`hasDebuggerOpenBSD() == false`) + } + return + } + runLLDBTests := hasLLDB() + if !runGDBTests && !runLLDBTests { + t.Error(`!runGDBTests && !runLLDBTests: You need at least GDB or LLDB installed to execute tests.`) + } + + if runGDBTests { + wait4debugPath := "../../samples/golang-wait4debug" + if runtime.GOOS == "windows" { + wait4debugPath += ".exe" + } + + // INFO(Rafael): Let's just attach and continue + var pid int + + pid = runTestApp(wait4debugPath) + if pid == 0 { + t.Error(`pid == 0`) + } + defer func(pid int) { + proc, err := os.FindProcess(pid) + if err == nil { + proc.Kill() + } + }(pid) + + time.Sleep(kTestSleepInSecs * time.Second) + + if !isProcessRunning(pid) { + t.Error(`isProcessRunning(pid) == false`) + } + + var gdbProc io.WriteCloser + var err error + + gdbProc, err = gdb() + if err != nil { + t.Error(`err != nil`) + } + defer gdbProc.Close() + + time.Sleep(kTestSleepInSecs * time.Nanosecond) + + err = gdbAttach(gdbProc, pid) + if err != nil { + t.Error(`err != nil`) + } + + time.Sleep((5 * kTestSleepInSecs) * time.Nanosecond) + + err = gdbContinue(gdbProc) + if err != nil { + t.Error(`err != nil`) + } + + time.Sleep(kTestSleepInSecs * time.Second) + + if isProcessRunning(pid) { + t.Error(`isProcessRunning(pid) == true`) + } + + _, err = io.WriteString(gdbProc, "quit") + if err != nil { + t.Error(`err != nil`) + } + + // INFO(Rafael): Let's attach and try some next commands. + + pid = runTestApp(wait4debugPath) + if pid == 0 { + t.Error(`pid == 0`) + } + defer func(pid int) { + proc, err := os.FindProcess(pid) + if err == nil { + proc.Kill() + } + }(pid) + + time.Sleep(kTestSleepInSecs * time.Nanosecond) + + if !isProcessRunning(pid) { + t.Error(`isProcessRunning(pid) == false`) + } + + gdbProc, err = gdb() + if err != nil { + t.Error(`err != nil`) + } + defer gdbProc.Close() + + time.Sleep(kTestSleepInSecs * time.Nanosecond) + + err = gdbAttach(gdbProc, pid) + if err != nil { + t.Error(`err != nil`) + } + + time.Sleep((5 * kTestSleepInSecs) * time.Nanosecond) + + ntry := kNextAttemptsNr + var isRunning bool = true + for ntry > 0 && isRunning { + gdbNext(gdbProc) + time.Sleep(1 * time.Nanosecond) + ntry-- + isRunning = isProcessRunning(pid) + } + + if isRunning { + t.Error(`isProcessRunning(pid) == true`) + } + + _, err = io.WriteString(gdbProc, "quit") + if err != nil { + t.Error(`err != nil`) + } + } + + if runLLDBTests { + wait4debugPath := "../../samples/golang-wait4debug" + if runtime.GOOS == "windows" { + wait4debugPath += ".exe" + } + + // INFO(Rafael): Let's just attach and continue + var pid int + + pid = runTestApp(wait4debugPath) + if pid == 0 { + t.Error(`pid == 0`) + } + defer func(pid int) { + proc, err := os.FindProcess(pid) + if err == nil { + proc.Kill() + } + }(pid) + + time.Sleep(kTestSleepInSecs * time.Second) + + if !isProcessRunning(pid) { + t.Error(`isProcessRunning(pid) == false`) + } + + var lldbProc io.WriteCloser + var err error + + lldbProc, err = lldb() + if err != nil { + t.Error(`err != nil`) + } + defer lldbProc.Close() + + time.Sleep(kTestSleepInSecs * time.Nanosecond) + + err = lldbAttach(lldbProc, pid) + if err != nil { + t.Error(`err != nil`) + } + + time.Sleep((5 * kTestSleepInSecs) * time.Nanosecond) + + err = lldbContinue(lldbProc) + if err != nil { + t.Error(`err != nil`) + } + + time.Sleep(kTestSleepInSecs * time.Second) + + if isProcessRunning(pid) { + t.Error(`isProcessRunning(pid) == true`) + } + + _, err = io.WriteString(lldbProc, "quit") + if err != nil { + t.Error(`err != nil`) + } + + // INFO(Rafael): Let's attach and try some next commands. + + pid = runTestApp(wait4debugPath) + if pid == 0 { + t.Error(`pid == 0`) + } + defer func(pid int) { + proc, err := os.FindProcess(pid) + if err == nil { + proc.Kill() + } + }(pid) + + time.Sleep(kTestSleepInSecs * time.Nanosecond) + + if !isProcessRunning(pid) { + t.Error(`isProcessRunning(pid) == false`) + } + + lldbProc, err = lldb() + if err != nil { + t.Error(`err != nil`) + } + defer lldbProc.Close() + + time.Sleep(kTestSleepInSecs * time.Nanosecond) + + err = lldbAttach(lldbProc, pid) + if err != nil { + t.Error(`err != nil`) + } + + time.Sleep((5 * kTestSleepInSecs) * time.Nanosecond) + + ntry := kNextAttemptsNr + var isRunning bool = true + for ntry > 0 && isRunning { + lldbNext(lldbProc) + time.Sleep(1 * time.Nanosecond) + ntry-- + isRunning = isProcessRunning(pid) + } + + if isRunning { + t.Error(`isProcessRunning(pid) == true`) + } + + _, err = io.WriteString(lldbProc, "quit") + if err != nil { + t.Error(`err != nil`) + } + } +} + +func TestSetGorgon(t *testing.T) { + runGDBTests := hasGDB() + if runtime.GOOS == "openbsd" { + if runGDBTests == false { + t.Error(`runGDBTests == false`) + } + setGorgonOpenBSD() + return + } + runLLDBTests := hasLLDB() + if !runGDBTests && !runLLDBTests { + t.Error(`!runGDBTests && !runLLDBTests: You need at least GDB or LLDB installed to execute tests.`) + } + + if runGDBTests { + setgorgonPath := "../../samples/golang-setgorgon" + if runtime.GOOS == "windows" { + setgorgonPath += ".exe" + } + + // INFO(Rafael): Let's just attach and continue + var pid int + + pid = runTestApp(setgorgonPath) + if pid == 0 { + t.Error(`pid == 0`) + } + defer func(pid int) { + proc, err := os.FindProcess(pid) + if err == nil { + proc.Kill() + } + }(pid) + + time.Sleep(kTestSleepInSecs * time.Second) + + if !isProcessRunning(pid) { + t.Error(`isProcessRunning(pid) == false`) + } + + var gdbProc io.WriteCloser + var err error + + gdbProc, err = gdb() + if err != nil { + t.Error(`err != nil`) + } + defer gdbProc.Close() + + time.Sleep(kTestSleepInSecs * time.Nanosecond) + + err = gdbAttach(gdbProc, pid) + if err != nil { + t.Error(`err != nil`) + } + + time.Sleep((5 * kTestSleepInSecs) * time.Nanosecond) + + err = gdbContinue(gdbProc) + if err != nil { + t.Error(`err != nil`) + } + + time.Sleep(kTestSleepInSecs * time.Second) + + if isProcessRunning(pid) { + t.Error(`isProcessRunning(pid) == true`) + } + + _, err = io.WriteString(gdbProc, "quit") + if err != nil { + t.Error(`err != nil`) + } + + // INFO(Rafael): Let's attach and try some next commands. + + pid = runTestApp(setgorgonPath) + if pid == 0 { + t.Error(`pid == 0`) + } + defer func(pid int) { + proc, err := os.FindProcess(pid) + if err == nil { + proc.Kill() + } + }(pid) + + time.Sleep(kTestSleepInSecs * time.Nanosecond) + + if !isProcessRunning(pid) { + t.Error(`isProcessRunning(pid) == false`) + } + + gdbProc, err = gdb() + if err != nil { + t.Error(`err != nil`) + } + defer gdbProc.Close() + + time.Sleep(kTestSleepInSecs * time.Nanosecond) + + err = gdbAttach(gdbProc, pid) + if err != nil { + t.Error(`err != nil`) + } + + time.Sleep((5 * kTestSleepInSecs) * time.Nanosecond) + + ntry := kNextAttemptsNr + var isRunning bool = true + for ntry > 0 && isRunning { + gdbNext(gdbProc) + time.Sleep(1 * time.Nanosecond) + ntry-- + isRunning = isProcessRunning(pid) + } + + if isRunning { + t.Error(`isProcessRunning(pid) == true`) + } + + _, err = io.WriteString(gdbProc, "quit") + if err != nil { + t.Error(`err != nil`) + } + } + + if runLLDBTests { + setgorgonPath := "../../samples/golang-setgorgon" + if runtime.GOOS == "windows" { + setgorgonPath += ".exe" + } + + // INFO(Rafael): Let's just attach and continue + var pid int + + pid = runTestApp(setgorgonPath) + if pid == 0 { + t.Error(`pid == 0`) + } + defer func(pid int) { + proc, err := os.FindProcess(pid) + if err == nil { + proc.Kill() + } + }(pid) + + time.Sleep(kTestSleepInSecs * time.Second) + + if !isProcessRunning(pid) { + t.Error(`isProcessRunning(pid) == false`) + } + + var lldbProc io.WriteCloser + var err error + + lldbProc, err = lldb() + if err != nil { + t.Error(`err != nil`) + } + defer lldbProc.Close() + + time.Sleep(kTestSleepInSecs * time.Nanosecond) + + err = lldbAttach(lldbProc, pid) + if err != nil { + t.Error(`err != nil`) + } + + time.Sleep((5 * kTestSleepInSecs) * time.Nanosecond) + + err = lldbContinue(lldbProc) + if err != nil { + t.Error(`err != nil`) + } + + time.Sleep(kTestSleepInSecs * time.Second) + + if isProcessRunning(pid) { + t.Error(`isProcessRunning(pid) == true`) + } + + _, err = io.WriteString(lldbProc, "quit") + if err != nil { + t.Error(`err != nil`) + } + + // INFO(Rafael): Let's attach and try some next commands. + + pid = runTestApp(setgorgonPath) + if pid == 0 { + t.Error(`pid == 0`) + } + defer func(pid int) { + proc, err := os.FindProcess(pid) + if err == nil { + proc.Kill() + } + }(pid) + + time.Sleep(kTestSleepInSecs * time.Nanosecond) + + if !isProcessRunning(pid) { + t.Error(`isProcessRunning(pid) == false`) + } + + lldbProc, err = lldb() + if err != nil { + t.Error(`err != nil`) + } + defer lldbProc.Close() + + time.Sleep(kTestSleepInSecs * time.Nanosecond) + + err = lldbAttach(lldbProc, pid) + if err != nil { + t.Error(`err != nil`) + } + + time.Sleep((5 * kTestSleepInSecs) * time.Nanosecond) + + ntry := kNextAttemptsNr + var isRunning bool = true + for ntry > 0 && isRunning { + lldbNext(lldbProc) + time.Sleep(1 * time.Nanosecond) + ntry-- + isRunning = isProcessRunning(pid) + } + + if isRunning { + t.Error(`isProcessRunning(pid) == true`) + } + + _, err = io.WriteString(lldbProc, "quit") + if err != nil { + t.Error(`err != nil`) + } + } +} + +func hasDebuggerOpenBSD() bool { + const kBacalhuffy = `rm out.txt > /dev/null 2>&1 +../../samples/golang-wait4debug > out.txt & +sleep 5 +pid=$(cat out.txt | tail -n 1 | cut -d '=' -f 2,7 | cut -d ')' -f 1) +echo "attach ${pid}" > $HOME/.gdbinit +echo "continue" >> $HOME/.gdbinit +echo "quit" >> $HOME/.gdbinit +gdb > /dev/null 2>&1 +rm ${HOME}/.gdbinit out.txt +ps -p ${pid} > /dev/null 2>&1 +if [ $? -ne 0 ] ; then + echo "OpenBSD Bacalhuffy info: program has exited." + exit 0 +else + kill -9 ${pid} + echo "OpenBSD Bacalhuffy error: program is still running." + exit 1 +fi +` + fp, err := os.Create(".bacalhuffy.sh") + if err != nil { + return false + } + defer fp.Close() + defer os.Remove(".bacalhuffy.sh") + fp.WriteString(kBacalhuffy) + + cmd := exec.Command("chmod", "+x", ".bacalhuffy.sh") + cmd.Start() + + cmd = exec.Command("./.bacalhuffy.sh") + cmd.Start() + + if errCmd := cmd.Wait(); errCmd != nil { + if _, ok := errCmd.(*exec.ExitError); ok { + return false + } else { + return true + } + } + + return false +} + +func setGorgonOpenBSD() bool { + const kBacalhuffy = `rm out.txt > /dev/null 2>&1 +../../samples/golang-setgorgon > out.txt & +sleep 5 +pid=$(cat out.txt | tail -n 1 | cut -d '=' -f 2,7 | cut -d ')' -f 1) +echo "attach ${pid}" > $HOME/.gdbinit +echo "continue" >> $HOME/.gdbinit +echo "quit" >> $HOME/.gdbinit +gdb > /dev/null 2>&1 +rm ${HOME}/.gdbinit out.txt +ps -p ${pid} > /dev/null 2>&1 +if [ $? -ne 0 ] ; then + echo "OpenBSD Bacalhuffy info: program has exited." + exit 0 +else + kill -9 ${pid} + echo "OpenBSD Bacalhuffy error: program is still running." + exit 1 +fi +` + fp, err := os.Create(".bacalhuffy.sh") + if err != nil { + return false + } + defer fp.Close() + defer os.Remove(".bacalhuffy.sh") + fp.WriteString(kBacalhuffy) + + cmd := exec.Command("chmod", "+x", ".bacalhuffy.sh") + cmd.Start() + + cmd = exec.Command("./.bacalhuffy.sh") + cmd.Start() + + if errCmd := cmd.Wait(); errCmd != nil { + if _, ok := errCmd.(*exec.ExitError); ok { + return false + } else { + return true + } + } + + return false +} diff --git a/gopkg/v2/go.mod b/gopkg/v2/go.mod new file mode 100644 index 0000000..c515011 --- /dev/null +++ b/gopkg/v2/go.mod @@ -0,0 +1,3 @@ +module github.com/rafael-santiago/aegis/gopkg + +go 1.13 diff --git a/gopkg/v2/test_utils_unix.go b/gopkg/v2/test_utils_unix.go new file mode 100644 index 0000000..8811aed --- /dev/null +++ b/gopkg/v2/test_utils_unix.go @@ -0,0 +1,47 @@ +// +// Copyright (c) 2020, Rafael Santiago +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. +// +// +build !windows + +package aegis + +import ( + "fmt" + "os" + "os/exec" + "syscall" + "time" +) + +func runTestApp(appPath string) int { + appProcAttr := syscall.ProcAttr{ + "", + []string{}, + []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd()}, + nil, + } + pid, err := syscall.ForkExec(appPath, []string{""}, &appProcAttr) + if err != nil { + return 0 + } + return pid +} + +func isProcessRunningNative(pid int) bool { + cmd := exec.Command("ps", "-o", "stat=", "-p", fmt.Sprintf("%d", pid)) + data, err := cmd.CombinedOutput() + if len(data) > 0 && data[0] == 't' { + ntry := 20 + for ntry > 0 && len(data) > 0 && data[0] == 't' { + time.Sleep(1 * time.Second) + data, err = cmd.CombinedOutput() + ntry-- + } + } + // INFO(Rafael): In doubt when not possible running ps let's assume the process as running. + return ((err != nil && len(data) > 0) || (len(data) > 0 && data[0] != 'Z' && data[0] != 'X')) +} diff --git a/gopkg/v2/test_utils_windows.go b/gopkg/v2/test_utils_windows.go new file mode 100644 index 0000000..77d348a --- /dev/null +++ b/gopkg/v2/test_utils_windows.go @@ -0,0 +1,36 @@ +// +// Copyright (c) 2020, Rafael Santiago +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. +// +package aegis + +import ( + "syscall" +) + +func runTestApp(appPath string) int { + var startInfo syscall.StartupInfo + var procInfo syscall.ProcessInformation + argv := syscall.StringToUTF16Ptr(appPath) + err := syscall.CreateProcess(nil, argv, nil, nil, true, 0, nil, nil, &startInfo, &procInfo) + if err != nil { + return 0 + } + return int(procInfo.ProcessId) +} + +func isProcessRunningNative(pid int) bool { + handle, err := syscall.OpenProcess(syscall.STANDARD_RIGHTS_REQUIRED|syscall.SYNCHRONIZE|0xfff, false, uint32(pid)) + is := (err == nil) + if is { + var exitCode uint32 + err = syscall.GetExitCodeProcess(handle, &exitCode) + syscall.CloseHandle(handle) + is = (err == nil && exitCode == 259) + + } + return is +} diff --git a/src/Forgefile.hsl b/src/Forgefile.hsl index d3e3a8d..c3aeab6 100644 --- a/src/Forgefile.hsl +++ b/src/Forgefile.hsl @@ -74,7 +74,11 @@ libaegis.prologue() { libaegis.epilogue() { if (hefesto.sys.last_forge_result() == 0) { - build("test"); + var option type list; + $option = hefesto.sys.get_option("no-tests"); + if ($option.count() == 0) { + build("test"); + } hefesto.sys.echo("_____________\nBUILD SUCCESS\n"); } } diff --git a/src/Toolsets.hsl b/src/Toolsets.hsl index b2d3c9b..d7f3a5c 100644 --- a/src/Toolsets.hsl +++ b/src/Toolsets.hsl @@ -214,7 +214,7 @@ local function get_tag_version(default type string) : result type string { var exit_code type int; $exit_code = hefesto.sys.run("git describe --tags --abbrev=0 > .curr-tag 2>&1"); var tag type string; - if ($exit_code != 0) { + if ($exit_code == 0) { var curr_tag type list; $curr_tag = hefesto.sys.lines_from_file(".curr-tag", ".*"); $tag = $curr_tag.item(0); diff --git a/src/aegis.h b/src/aegis.h index 3dd224a..4b03975 100644 --- a/src/aegis.h +++ b/src/aegis.h @@ -8,7 +8,7 @@ #ifndef AEGIS_H #define AEGIS_H 1 -#define AEGIS_VERSION "v1" +#define AEGIS_VERSION "v2" #if !defined(CGO) typedef int (*aegis_gorgon_exit_test_func)(void *args); diff --git a/src/native/linux/aegis_native.c b/src/native/linux/aegis_native.c index 7a693f3..0199b37 100644 --- a/src/native/linux/aegis_native.c +++ b/src/native/linux/aegis_native.c @@ -9,12 +9,13 @@ #include #include #include +#include #include #include int aegis_has_debugger(void) { int has = 0; - FILE *fp = NULL; + int fd = -1; char proc_filepath[1024], proc_buf[1024]; char *bp = NULL, *bp_end = NULL; ssize_t proc_buf_size = 0; @@ -22,21 +23,22 @@ int aegis_has_debugger(void) { if (fork() == 0) { snprintf(proc_filepath, sizeof(proc_filepath) - 2, "/proc/%d/stat", pid); - if ((fp = fopen(proc_filepath, "r")) != NULL) { + if ((fd = open(proc_filepath, O_RDONLY)) != -1) { memset(proc_buf, 0, sizeof(proc_buf)); - proc_buf_size = fread(proc_buf, 1, sizeof(proc_buf) - 2, fp); - fclose(fp); - if ((bp = strstr(proc_buf, ")")) != NULL) { + proc_buf_size = read(fd, proc_buf, sizeof(proc_buf) - 2); + close(fd); + if (proc_buf_size > -1 && (bp = strstr(proc_buf, ")")) != NULL) { has = ((bp + 2) < bp_end && bp[2] == 't'); } } if (!has) { snprintf(proc_filepath, sizeof(proc_filepath) - 2, "/proc/%d/stack", pid); - if ((fp = fopen(proc_filepath, "r")) != NULL) { + if ((fd = open(proc_filepath, O_RDONLY)) != -1) { memset(proc_buf, 0, sizeof(proc_buf)); - proc_buf_size = fread(proc_buf, 1, sizeof(proc_buf) - 2, fp); - fclose(fp); - has = (strstr(proc_buf, "ptrace_stop") != NULL); + proc_buf_size = read(fd, proc_buf, sizeof(proc_buf) - 2); + close(fd); + has = (proc_buf_size >= 11 && strstr(proc_buf, "ptrace_stop") != NULL) || + (proc_buf_size >= 15 && strstr(proc_buf, "tracesys_phase2") != NULL); } } exit(has); diff --git a/src/samples/setgorgon.c b/src/samples/setgorgon.c index 4365184..051257f 100644 --- a/src/samples/setgorgon.c +++ b/src/samples/setgorgon.c @@ -33,7 +33,6 @@ int main(int argc, char **argv) { fprintf(stderr, "error: unable to set gorgon.\n"); exit(1); } - fprintf(stdout, "info: process started (pid=%d)...\n", getpid()); while (!bye) { usleep(2);