If you execute ./syscallprint ls -l it will print syscall ID's being called by ls -l command.
syscallprint.go
package main
import (
"fmt"
"os"
"os/exec"
"syscall"
)
func main() {
var regs syscall.PtraceRegs
cmd := exec.Command(os.Args[1], os.Args[2:]...)
cmd.Stderr = os.Stderr
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.SysProcAttr = &syscall.SysProcAttr{
Ptrace: true,
}
cmd.Start()
err := cmd.Wait()
if err != nil {
fmt.Printf("Wait returned: %v\n", err)
}
pid := cmd.Process.Pid
exit := true
for {
if exit {
err = syscall.PtraceGetRegs(pid, ®s)
if err != nil {
break
}
fmt.Printf("SystemId: (%x)\n", regs.Orig_rax)
// TODO: print syscall parameters
}
err = syscall.PtraceSyscall(pid, 0)
if err != nil {
panic(err)
}
_, err = syscall.Wait4(pid, nil, 0, nil)
if err != nil {
panic(err)
}
exit = !exit
}
}
Is it possible to get parameters of syscall being made by ls -l?
It seems to be possible with c from this question. Is it possible with go-lang?
Related
I got a binary that works like the below:
> ./my_bin raw.avi output_file.avi
output_file.avi is what I want, some verbose information will print in the terminal when the job is succeeded, like:
Copyright 2022 Company Inc... Success.
I want to run this command inside my code and redirect the output_file.avi into some byte array so that I don't have to read it from disk and delete it. My approach looks like the below Golang snippet:
func wrongOne(stdin []byte) ([]byte, error) {
inBuf := bytes.NewBuffer(stdin)
outBuf := bytes.NewBuffer(nil)
cmd := exec.Command("./my_bin", "/dev/stdin", "/dev/stdout")
cmd.Stdin = inBuf
cmd.Stdout = outBuf
err := cmd.Run()
if err != nil {
return nil, err
}
return outBuf.Bytes(), nil // wrong
}
However, the return byte array is longer than the below approach, which leads to failure on the MD5 check.
func correctOne(stdin []byte) ([]byte, error) {
inBuf := bytes.NewBuffer(stdin)
cmd := exec.Command("./my_bin", "/dev/stdin", "output_file")
cmd.Stdin = inBuf
err := cmd.Run()
if err != nil {
return nil, err
}
return os.ReadFile("output_file")
}
the wrongOne function can be modified to following code to be correct:
func modifiedWrongOne(stdin []byte) ([]byte, error) {
inBuf := bytes.NewBuffer(stdin)
outBuf := bytes.NewBuffer(nil)
cmd := exec.Command("./my_bin", "/dev/stdin", "/dev/stdout")
cmd.Stdin = inBuf
cmd.Stdout = outBuf
err := cmd.Run()
if err != nil {
return nil, err
}
correct, _ := correctOne(stdin)
return outBuf.Bytes()[:len(correct)], nil // diff
}
I presume that the output verbose information is included in the /dev/stdout so that the wrongOne function doesn't works. i.e.,
the output of wrongOne = the output of correctOne + []byte{"Copyright 2022 Company Inc... Success."}
Is there any solution that I can get the output_file.avi in the pipe without save it as file and read it from disk? Thanks!
The command writes the copyright notice to stdout. To avoid commingling the copyright notice with the output file, use a file other than /dev/stdout as the output file.
The function below uses Cmd.ExtraFiles to connect a pipe to fd 3 in the child process. The function copies data from the pipe to a byte buffer and returns those bytes to the caller.
func otherOne(stdin []byte) ([]byte, error) {
r, w, err := os.Pipe()
if err != nil {
return nil, err
}
defer r.Close()
defer w.Close()
cmd := exec.Command("./my_bin", "/dev/stdin", "/dev/fd/3")
cmd.Stdin = bytes.NewReader(stdin)
cmd.ExtraFiles = []*os.File{w} // The first file is fd 3.
if err := cmd.Start(); err != nil {
return nil, err
}
w.Close()
var outbuf bytes.Buffer
if _, err := io.Copy(&outbuf, r); err != nil {
return nil, err
}
if err := cmd.Wait(); err != nil {
return nil, err
}
return outbuf.Bytes(), nil
}
After months, I figure out another way to solve this problem. The basic idea is similar with Cerise, i.e., using /dev/fd/3 to redirect the output file. After that, we redirect /dev/fd/3 to /dev/stdout, verbose log to /dev/stderr
by 3>&1, 1>&2, respectively. An additional gen.sh is added. Here's the solution:
#gen.sh
./mybin /dev/stdin /dev/fd/3 3>&1 1>&2
// gen.go
func gen(stdin []byte) ([]byte, error) {
inBuf := bytes.NewBuffer(stdin)
outBuf := bytes.NewBuffer(nil)
cmd := exec.Command("./gen.sh")
cmd.Stdin = inBuf
cmd.Stdout = outBuf
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
return nil, err
}
return outBuf.Bytes(), nil
}
I have been using exec.CommandContext to run sudo commands in my Go packages, and it works. Nowadays, I am trying to add a timeout so that the command just terminates if it does not finish after the timeout ends. However, I realized the RunAsSudo function would never return. I read Golang context.WithTimeout doesn't work with exec.CommandContext "su -c" command and thus modified RunAsSudo accordingly, but the function still does not return.
Below is the original RunAsSudo, Version 1:
myCtx, cancel := context.WithTimeout(context.Background(), 8*time.Second)
defer cancel()
err := e.RunAsSudo(myCtx, f.commandContext, cmd)
func RunAsSudo(ctx context.Context, exc exec.CommandContext, command string) (err error) {
cmd := exc(ctx, "/bin/bash", "-c", fmt.Sprintf("sudo %s", command))
err = cmd.Start()
if err != nil {
return
}
err = cmd.Wait()
return
}
modified RunAsSudo version 2, which still does not work:
func RunAsSudoWithTimeout(ctx context.Context, exc exec.CommandContext, command string) (error) {
cmd := exc(rpiCtx, "/bin/bash", "-c", fmt.Sprintf("sudo %s", command))
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
go func() {
<-ctx.Done()
if ctx.Err() == context.DeadlineExceeded {
syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
}
}()
err := cmd.Start()
if err != nil {
return err
}
err = cmd.Wait()
return err
}
However, I noticed that if I replaced cmd with something that does not require sudo permission eg cmd := exc(ctx, "/bin/bash", "-c", "sleep 1h"), RunAsSudo (both versions 1 and 2) returns after the timeout.
Version 3 of RunAsSudo, which returns after the timeout:
func RunAsSudo(ctx context.Context, exc exec.CommandContext, command string) (err error) {
errs := make(chan error, 1)
cmd := exc(ctx, "/bin/bash", "-c", fmt.Sprintf("sudo %s", command))
if stdout != nil {
cmd.Stdout = stdout
}
if stderr != nil {
cmd.Stderr = stderr
}
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
go func() {
defer close(errs)
err := cmd.Start()
if err != nil {
errs <- err
return
}
err = cmd.Wait()
errs <- err
}()
select {
case <-ctx.Done():
if ctx.Err() == context.DeadlineExceeded {
syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
fmt.Println("syskill!")
}
return ctx.Err()
case err := <-errs:
return err
}
}
How come version 3 returns after the timeout but not version 2 or 1? Is version 3 the right approach to terminating processes started by exec.CommandContext after a timeout? Thanks a lot~~
In Go, I am trying to create a function that reads and processes the next line of input:
// Read a string of hex from stdin and parse to an array of bytes
func ReadHex() []byte {
r := bufio.NewReader(os.Stdin)
t, _ := r.ReadString('\n')
data, _ := hex.DecodeString(strings.TrimSpace(t))
return data
}
Unfortunately, this only works the first time it is called. It captures the first line but is unable to capture subsequent lines piped via standard input.
I suspect, if the same persistent bufio.Reader() object was used on each subsequent call, it would work but I haven't been able to achieve this without passing it manually on each function call.
Yes, try this:
package main
import (
"bufio"
"encoding/hex"
"fmt"
"log"
"os"
"strings"
)
func ReadFunc() func() []byte {
r := bufio.NewReader(os.Stdin)
return func() []byte {
t, err := r.ReadString('\n')
if err != nil {
log.Fatal(err)
}
data, err := hex.DecodeString(strings.TrimSpace(t))
if err != nil {
log.Fatal(err)
}
return data
}
}
func main() {
r, w, err := os.Pipe()
if err != nil {
log.Fatal(err)
}
os.Stdin = r
w.Write([]byte(`ffff
cafebabe
ff
`))
w.Close()
ReadHex := ReadFunc()
fmt.Println(ReadHex())
fmt.Println(ReadHex())
fmt.Println(ReadHex())
}
Output:
[255 255]
[202 254 186 190]
[255]
Using a struct, try this:
package main
import (
"bufio"
"encoding/hex"
"fmt"
"io"
"log"
"os"
"strings"
)
// InputReader struct
type InputReader struct {
bufio.Reader
}
// New creates an InputReader
func New(rd io.Reader) *InputReader {
return &InputReader{Reader: *bufio.NewReader(rd)}
}
// ReadHex returns a string of hex from stdin and parse to an array of bytes
func (r *InputReader) ReadHex() []byte {
t, err := r.ReadString('\n')
if err != nil {
log.Fatal(err)
}
data, err := hex.DecodeString(strings.TrimSpace(t))
if err != nil {
log.Fatal(err)
}
return data
}
func main() {
r, w, err := os.Pipe()
if err != nil {
log.Fatal(err)
}
os.Stdin = r
w.Write([]byte(`ffff
cafebabe
ff
`))
w.Close()
rdr := New(os.Stdin)
fmt.Println(rdr.ReadHex())
fmt.Println(rdr.ReadHex())
fmt.Println(rdr.ReadHex())
}
I am trying to run nikto -h {url} on kali. My nikto -h command works fine, but when I add an URL, nothing is output.
I am not sure if it is the process or something else.
How can I see the output directly rather than buffering and displaying it?
package main
import (
"bytes"
"fmt"
"log"
"os/exec"
// "strings"
)
func main() {
cmd := exec.Command("nikto","-h","google.com")
// cmd.Stdin = strings.NewReader("some input")
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
}
As #Adrian suggested in the comments, using os.Stdout works
package main
import (
"log"
"os"
"os/exec"
)
func main() {
cmd := exec.Command("ls", "-l")
cmd.Stdout = os.Stdout
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
}
I have the following Go code which will eventually fill the disk and fail with ENOSPC (just a proof of concept). How can I determine from the err returned by os.Write that it indeed failed because of ENOSPC (so I need a way to grab errno after the write operation) ?
package main
import (
"log"
"os"
)
func main() {
fd, _ := os.Create("dump.txt")
defer fd.Close()
for {
buf := make([]byte, 1024)
_, err := fd.Write(buf)
if err != nil {
log.Fatalf("%T %v", err, err)
}
}
}
EDIT: Updated the program as #FUZxxl suggested:
package main
import (
"log"
"os"
"syscall"
)
func main() {
fd, _ := os.Create("dump.txt")
defer fd.Close()
for {
buf := make([]byte, 1024)
_, err := fd.Write(buf)
if err != nil {
log.Printf("%T %v\n", err, err)
errno, ok := err.(syscall.Errno)
if ok {
log.Println("type assert ok")
if errno == syscall.ENOSPC {
log.Println("got ENOSPC")
}
} else {
log.Println("type assert not ok")
}
break
}
}
}
However, I'm not getting the expected result. Here is the output:
2015/02/15 10:13:27 *os.PathError write dump.txt: no space left on device
2015/02/15 10:13:27 type assert not ok
File operations generally return an *os.PathError; cast err to os.PathError and use the Err field to examine the underlying cause, like this:
patherr, ok := err.(*os.PathError)
if ok && patherr.Err == syscall.ENOSPC {
log.Println("Out of disk space!")
}