-
-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 1af1dd8
Showing
17 changed files
with
434 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
name: release | ||
|
||
on: | ||
push: | ||
tags: | ||
- v* | ||
|
||
jobs: | ||
build: | ||
name: releasing | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- uses: actions/checkout@v3 | ||
with: | ||
fetch-depth: 0 | ||
|
||
- uses: actions/setup-go@v3 | ||
with: | ||
go-version: "1.19" | ||
- uses: goreleaser/goreleaser-action@v3 | ||
with: | ||
version: latest | ||
args: release --rm-dist | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
name: tests | ||
|
||
on: | ||
pull_request: | ||
|
||
jobs: | ||
test: | ||
name: tests | ||
runs-on: ${{ matrix.os }} | ||
strategy: | ||
matrix: | ||
os: [ ubuntu-latest ] | ||
|
||
steps: | ||
- uses: actions/checkout@v3 | ||
- uses: actions/setup-go@v3 | ||
with: | ||
go-version: '1.19' | ||
cache: true | ||
- name: Run tests | ||
run: sudo -E $(which go) test ./... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
.idea |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
builds: | ||
- id: siphon | ||
main: . | ||
binary: siphon | ||
ldflags: | ||
- "-s -w -extldflags '-fno-PIC -static'" | ||
env: | ||
- CGO_ENABLED=0 | ||
goos: | ||
- linux | ||
goarch: | ||
- "amd64" | ||
- "arm64" | ||
- "386" | ||
- "arm" | ||
changelog: | ||
sort: asc | ||
filters: | ||
exclude: | ||
- "^docs:" | ||
- "^test:" | ||
|
||
archives: | ||
- format: binary | ||
name_template: "{{ .Binary}}-{{ .Os }}-{{ .Arch }}" | ||
|
||
release: | ||
prerelease: auto | ||
github: | ||
owner: liamg | ||
name: siphon |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
This is free and unencumbered software released into the public domain. | ||
|
||
Anyone is free to copy, modify, publish, use, compile, sell, or | ||
distribute this software, either in source code form or as a compiled | ||
binary, for any purpose, commercial or non-commercial, and by any | ||
means. | ||
|
||
In jurisdictions that recognize copyright laws, the author or authors | ||
of this software dedicate any and all copyright interest in the | ||
software to the public domain. We make this dedication for the benefit | ||
of the public at large and to the detriment of our heirs and | ||
successors. We intend this dedication to be an overt act of | ||
relinquishment in perpetuity of all present and future rights to this | ||
software under copyright law. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
OTHER DEALINGS IN THE SOFTWARE. | ||
|
||
For more information, please refer to <http://unlicense.org/> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# Siphon | ||
|
||
Intercept input/output (_stdin/stdout/stderr_) for any process, even where said output is sent to _/dev/null_ or elsewhere. | ||
|
||
![demo gif](demo.gif) | ||
|
||
It can also be used to spy on another users shell: | ||
|
||
![demo gif 2](demo2.gif) | ||
|
||
Currently Siphon works on Linux, with `amd64`, `arm64`, `arm`, and `386`. Adding support for more architectures is pretty simple, feel free to raise an issue. | ||
|
||
It uses `ptrace` which means you'll likely need to run it as `root` for the ptrace privilege. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
module github.com/liamg/siphon | ||
|
||
go 1.19 | ||
|
||
require ( | ||
github.com/spf13/cobra v1.6.0 | ||
github.com/stretchr/testify v1.7.4 | ||
) | ||
|
||
require ( | ||
github.com/davecgh/go-spew v1.1.1 // indirect | ||
github.com/inconshreveable/mousetrap v1.0.1 // indirect | ||
github.com/pmezard/go-difflib v1.0.0 // indirect | ||
github.com/spf13/pflag v1.0.5 // indirect | ||
gopkg.in/yaml.v3 v3.0.1 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= | ||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= | ||
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= | ||
github.com/spf13/cobra v1.6.0 h1:42a0n6jwCot1pUmomAp4T7DeMD+20LFv4Q54pxLf2LI= | ||
github.com/spf13/cobra v1.6.0/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= | ||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= | ||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= | ||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= | ||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||
github.com/stretchr/testify v1.7.4 h1:wZRexSlwd7ZXfKINDLsO4r7WBt3gTKONc6K/VesHvHM= | ||
github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"runtime" | ||
"syscall" | ||
) | ||
|
||
func watchProcess(pid int, stdout, stderr, stdin bool) error { | ||
// ensure tracing all comes from same thread | ||
runtime.LockOSThread() | ||
|
||
if _, err := os.FindProcess(pid); err != nil { | ||
return fmt.Errorf("could not find process with pid %d: %w", pid, err) | ||
} | ||
|
||
if err := syscall.PtraceAttach(pid); err == syscall.EPERM { | ||
return fmt.Errorf("could not attach to process with pid %d: %w - check your permissions", pid, err) | ||
} else if err != nil { | ||
return err | ||
} | ||
|
||
status := syscall.WaitStatus(0) | ||
if _, err := syscall.Wait4(pid, &status, 0, nil); err != nil { | ||
return err | ||
} | ||
|
||
defer func() { | ||
_ = syscall.PtraceDetach(pid) | ||
_, _ = syscall.Wait4(pid, &status, 0, nil) | ||
}() | ||
|
||
// deliver SIGTRAP|0x80 | ||
if err := syscall.PtraceSetOptions(pid, syscall.PTRACE_O_TRACESYSGOOD); err != nil { | ||
return err | ||
} | ||
|
||
for { | ||
fd, data, err := interceptReadsAndWrites(pid) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if stdout && fd == uint64(syscall.Stdout) || stderr && fd == uint64(syscall.Stderr) || stdin && fd == uint64(syscall.Stdin) { | ||
if fd == uint64(syscall.Stdin) { | ||
fd = uint64(os.Stdin.Fd()) | ||
} | ||
_, _ = fmt.Fprintf(os.NewFile(uintptr(fd), "pipe"), "%s", string(data)) | ||
} | ||
} | ||
} | ||
|
||
func interceptReadsAndWrites(pid int) (fd uint64, data []byte, err error) { | ||
|
||
// intercept syscall | ||
err = syscall.PtraceSyscall(pid, 0) | ||
if err != nil { | ||
return 0, nil, fmt.Errorf("could not intercept syscall: %w", err) | ||
} | ||
|
||
// wait for a syscall | ||
status := syscall.WaitStatus(0) | ||
_, err = syscall.Wait4(pid, &status, 0, nil) | ||
if err != nil { | ||
return 0, nil, err | ||
} | ||
|
||
// if interrupted, stop tracing | ||
if status.StopSignal().String() == "interrupt" { | ||
_ = syscall.PtraceSyscall(pid, int(status.StopSignal())) | ||
return 0, nil, fmt.Errorf("process interrupted") | ||
} | ||
|
||
defer func() { | ||
|
||
// continue the syscall we intercepted | ||
err = syscall.PtraceSyscall(pid, 0) | ||
if err != nil { | ||
err = fmt.Errorf("could not continue process: %w", err) | ||
return | ||
} | ||
|
||
// and wait for it to finish | ||
status := syscall.WaitStatus(0) | ||
_, err = syscall.Wait4(pid, &status, 0, nil) | ||
if err != nil { | ||
err = fmt.Errorf("could not wait for process: %w", err) | ||
return | ||
} | ||
|
||
// process exited | ||
if status.Exited() { | ||
err = fmt.Errorf("process exited") | ||
return | ||
} | ||
|
||
// if interrupted, stop tracing | ||
if status.StopSignal().String() == "interrupt" { | ||
_ = syscall.PtraceSyscall(pid, int(status.StopSignal())) | ||
err = fmt.Errorf("process interrupted") | ||
return | ||
} | ||
}() | ||
|
||
// if we have a syscall, examine it... | ||
if status.TrapCause()&int(syscall.SIGTRAP|0x80) > 0 { | ||
|
||
// read registers | ||
regs := &syscall.PtraceRegs{} | ||
if err := syscall.PtraceGetRegs(pid, regs); err != nil { | ||
return 0, nil, err | ||
} | ||
|
||
// find the syscall number for the host architecture | ||
syscallNo := grabSyscallNo(regs) | ||
|
||
// if it's a read/write syscall, grab the args | ||
switch syscallNo { | ||
case syscall.SYS_READ, syscall.SYS_WRITE: | ||
|
||
// grab the args to WRITE for the host architecture | ||
// fd == file descriptor (generally 1 for stdout, 2 for stderr) | ||
// ptr == pointer to the buffer | ||
// lng == length of the buffer | ||
fd, ptr, lng := grabArgsFromRegs(regs) | ||
|
||
// if we want to see this output, read it from memory | ||
if lng > 0 { | ||
data := make([]byte, lng) | ||
if _, err := syscall.PtracePeekData(pid, uintptr(ptr), data); err != nil { | ||
return 0, nil, err | ||
} | ||
return fd, data, nil | ||
} | ||
} | ||
|
||
} | ||
|
||
return 0, nil, err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"strconv" | ||
|
||
"github.com/spf13/cobra" | ||
) | ||
|
||
var cmd = &cobra.Command{ | ||
Use: "siphon [pid]", | ||
Args: cobra.ExactArgs(1), | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
cmd.SilenceUsage = true | ||
cmd.SilenceErrors = true | ||
pid, err := strconv.ParseInt(args[0], 10, 64) | ||
if err != nil { | ||
return fmt.Errorf("invalid pid: %w", err) | ||
} | ||
return watchProcess(int(pid), flagStdOut, flagStdErr, flagStdIn) | ||
}, | ||
} | ||
|
||
var ( | ||
flagStdOut = true | ||
flagStdErr = true | ||
flagStdIn = false | ||
) | ||
|
||
func main() { | ||
|
||
cmd.Flags().BoolVarP(&flagStdOut, "stdout", "o", flagStdOut, "Show stdout") | ||
cmd.Flags().BoolVarP(&flagStdErr, "stderr", "e", flagStdErr, "Show stderr") | ||
cmd.Flags().BoolVarP(&flagStdIn, "stdin", "i", flagStdIn, "Show stdin") | ||
|
||
if err := cmd.Execute(); err != nil { | ||
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "Error: %v", err) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
//go:build 386 | ||
|
||
package main | ||
|
||
import ( | ||
"syscall" | ||
) | ||
|
||
func grabSyscallNo(regs *syscall.PtraceRegs) uint64 { | ||
return uint64(regs.Orig_eax) | ||
} | ||
|
||
func grabArgsFromRegs(regs *syscall.PtraceRegs) (fd, ptr, lng uint64) { | ||
return uint64(regs.Ebx), uint64(regs.Ecx), uint64(regs.Edx) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
//go:build amd64 | ||
|
||
package main | ||
|
||
import ( | ||
"syscall" | ||
) | ||
|
||
func grabSyscallNo(regs *syscall.PtraceRegs) uint64 { | ||
return regs.Orig_rax | ||
} | ||
|
||
func grabArgsFromRegs(regs *syscall.PtraceRegs) (fd, ptr, lng uint64) { | ||
return regs.Rdi, regs.Rsi, regs.Rdx | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
//go:build arm | ||
|
||
package main | ||
|
||
import ( | ||
"syscall" | ||
) | ||
|
||
func grabSyscallNo(regs *syscall.PtraceRegs) uint64 { | ||
return uint64(regs.Uregs[7]) | ||
} | ||
|
||
func grabArgsFromRegs(regs *syscall.PtraceRegs) (fd, ptr, lng uint64) { | ||
return uint64(regs.Uregs[0]), uint64(regs.Uregs[1]), uint64(regs.Uregs[2]) | ||
} |
Oops, something went wrong.