1
0
out-of-tree/cmd/debug.go

288 lines
6.3 KiB
Go
Raw Normal View History

2018-11-25 14:46:29 +00:00
// Copyright 2018 Mikhail Klementev. All rights reserved.
// Use of this source code is governed by a AGPLv3 license
// (or later) that can be found in the LICENSE file.
2024-02-17 22:38:43 +00:00
package cmd
2018-11-25 14:46:29 +00:00
import (
"errors"
"fmt"
"os"
"strings"
"time"
2023-03-18 21:30:07 +00:00
"github.com/rs/zerolog/log"
"gopkg.in/logrusorgru/aurora.v2"
2019-02-02 21:24:29 +00:00
2024-02-20 13:25:31 +00:00
"code.dumpstack.io/tools/out-of-tree/artifact"
2019-02-02 21:24:29 +00:00
"code.dumpstack.io/tools/out-of-tree/config"
"code.dumpstack.io/tools/out-of-tree/distro"
"code.dumpstack.io/tools/out-of-tree/fs"
2019-02-02 21:24:29 +00:00
"code.dumpstack.io/tools/out-of-tree/qemu"
2018-11-25 14:46:29 +00:00
)
2023-01-31 07:13:33 +00:00
type DebugCmd struct {
Kernel string `help:"regexp (first match)" required:""`
Gdb string `help:"gdb listen address" default:"tcp::1234"`
SshAddr string `help:"ssh address to listen" default:"127.0.0.1"`
SshPort int `help:"ssh port to listen" default:"50022"`
ArtifactConfig string `help:"path to artifact config" type:"path"`
2023-01-31 07:13:33 +00:00
Kaslr bool `help:"Enable KASLR"`
Smep bool `help:"Enable SMEP"`
Smap bool `help:"Enable SMAP"`
Kpti bool `help:"Enable KPTI"`
NoKaslr bool `help:"Disable KASLR"`
NoSmep bool `help:"Disable SMEP"`
NoSmap bool `help:"Disable SMAP"`
NoKpti bool `help:"Disable KPTI"`
}
// TODO: merge with pew.go
2023-01-31 07:13:33 +00:00
func (cmd *DebugCmd) Run(g *Globals) (err error) {
kcfg, err := config.ReadKernelConfig(g.Config.Kernels)
if err != nil {
2024-05-23 10:19:46 +00:00
log.Error().Err(err).Msg("read kernel config")
2023-01-31 07:13:33 +00:00
}
var configPath string
if cmd.ArtifactConfig == "" {
configPath = g.WorkDir + "/.out-of-tree.toml"
} else {
configPath = cmd.ArtifactConfig
}
2024-02-20 13:25:31 +00:00
ka, err := artifact.Artifact{}.Read(configPath)
2018-11-25 14:46:29 +00:00
if err != nil {
return
}
if ka.SourcePath == "" {
2023-02-23 08:25:19 +00:00
ka.SourcePath = g.WorkDir
2018-11-25 14:46:29 +00:00
}
2023-02-23 08:25:19 +00:00
ki, err := firstSupported(kcfg, ka, cmd.Kernel)
2018-11-25 14:46:29 +00:00
if err != nil {
return
}
kernel := qemu.Kernel{KernelPath: ki.KernelPath, InitrdPath: ki.InitrdPath}
2019-08-17 09:05:06 +00:00
q, err := qemu.NewSystem(qemu.X86x64, kernel, ki.RootFS)
2018-11-25 14:46:29 +00:00
if err != nil {
return
}
err = q.SetSSHAddrPort(cmd.SshAddr, cmd.SshPort)
if err != nil {
return
}
if ka.Qemu.Cpus != 0 {
q.Cpus = ka.Qemu.Cpus
}
if ka.Qemu.Memory != 0 {
q.Memory = ka.Qemu.Memory
}
if ka.Docker.Timeout.Duration != 0 {
2023-02-23 08:25:19 +00:00
g.Config.Docker.Timeout.Duration = ka.Docker.Timeout.Duration
}
q.SetKASLR(false) // set KASLR to false by default because of gdb
q.SetSMEP(!ka.Mitigations.DisableSmep)
q.SetSMAP(!ka.Mitigations.DisableSmap)
2019-08-20 00:05:19 +00:00
q.SetKPTI(!ka.Mitigations.DisableKpti)
2023-02-23 08:25:19 +00:00
if cmd.Kaslr {
q.SetKASLR(true)
2023-02-23 08:25:19 +00:00
} else if cmd.NoKaslr {
q.SetKASLR(false)
}
2023-02-23 08:25:19 +00:00
if cmd.Smep {
q.SetSMEP(true)
2023-02-23 08:25:19 +00:00
} else if cmd.NoSmep {
q.SetSMEP(false)
}
2023-02-23 08:25:19 +00:00
if cmd.Smap {
q.SetSMAP(true)
2023-02-23 08:25:19 +00:00
} else if cmd.NoSmap {
q.SetSMAP(false)
}
2023-02-23 08:25:19 +00:00
if cmd.Kpti {
2019-08-20 00:05:19 +00:00
q.SetKPTI(true)
2023-02-23 08:25:19 +00:00
} else if cmd.NoKpti {
2019-08-20 00:05:19 +00:00
q.SetKPTI(false)
}
redgreen := func(name string, enabled bool) aurora.Value {
if enabled {
return aurora.BgGreen(aurora.Black(name))
}
return aurora.BgRed(aurora.White(name))
}
2019-08-20 00:05:19 +00:00
fmt.Printf("[*] %s %s %s %s\n",
redgreen("KASLR", q.GetKASLR()),
redgreen("SMEP", q.GetSMEP()),
2019-08-20 00:05:19 +00:00
redgreen("SMAP", q.GetSMAP()),
redgreen("KPTI", q.GetKPTI()))
fmt.Printf("[*] SMP: %d CPUs\n", q.Cpus)
fmt.Printf("[*] Memory: %d MB\n", q.Memory)
2023-02-23 08:25:19 +00:00
q.Debug(cmd.Gdb)
coloredGdbAddress := aurora.BgGreen(aurora.Black(cmd.Gdb))
2019-08-21 06:16:25 +00:00
fmt.Printf("[*] gdb is listening on %s\n", coloredGdbAddress)
2018-11-25 14:46:29 +00:00
err = q.Start()
if err != nil {
return
}
defer q.Stop()
tmp, err := fs.TempDir()
2018-11-25 14:46:29 +00:00
if err != nil {
return
}
defer os.RemoveAll(tmp)
2023-02-16 06:27:17 +00:00
err = q.WaitForSSH(time.Minute)
if err != nil {
return
}
2023-02-15 11:48:25 +00:00
if ka.StandardModules {
// Module depends on one of the standard modules
2024-02-20 13:25:31 +00:00
err = artifact.CopyStandardModules(q, ki)
2023-02-15 11:48:25 +00:00
if err != nil {
2024-05-23 10:19:46 +00:00
log.Error().Err(err).Msg("copy standard modules")
2023-02-15 11:48:25 +00:00
return
}
}
2024-02-20 13:25:31 +00:00
err = artifact.PreloadModules(q, ka, ki, g.Config.Docker.Timeout.Duration)
2020-06-14 20:14:59 +00:00
if err != nil {
2024-05-23 10:19:46 +00:00
log.Error().Err(err).Msg("preload modules")
2020-06-14 20:14:59 +00:00
return
}
2023-03-22 20:56:44 +00:00
var buildDir, outFile, output, remoteFile string
2018-11-25 14:46:29 +00:00
2024-02-20 13:25:31 +00:00
if ka.Type == artifact.Script {
2023-03-22 20:56:44 +00:00
err = q.CopyFile("root", ka.Script, ka.Script)
if err != nil {
return
}
} else {
2024-02-20 13:25:31 +00:00
buildDir, outFile, output, err = artifact.Build(log.Logger, tmp, ka, ki, g.Config.Docker.Timeout.Duration)
2023-03-22 20:56:44 +00:00
if err != nil {
2024-05-23 10:19:46 +00:00
log.Error().Err(err).Msg(output)
2023-03-22 20:56:44 +00:00
return
}
2018-11-25 14:46:29 +00:00
remoteFile = "/tmp/" + strings.Replace(ka.Name, " ", "_", -1)
2024-02-20 13:25:31 +00:00
if ka.Type == artifact.KernelModule {
remoteFile += ".ko"
2023-03-22 20:56:44 +00:00
}
err = q.CopyFile("user", outFile, remoteFile)
if err != nil {
return
}
2018-11-25 14:46:29 +00:00
}
// Copy all test files to the remote machine
2023-01-31 07:13:33 +00:00
for _, f := range ka.TestFiles {
if f.Local[0] != '/' {
2023-05-01 14:57:08 +00:00
if buildDir != "" {
2023-05-01 14:53:52 +00:00
f.Local = buildDir + "/" + f.Local
}
}
2023-01-31 07:13:33 +00:00
err = q.CopyFile(f.User, f.Local, f.Remote)
if err != nil {
2024-05-23 10:19:46 +00:00
log.Error().Err(err).Msgf("copy %s -> %s", f.Local, f.Remote)
return
}
}
2018-11-25 14:46:29 +00:00
coloredRemoteFile := aurora.BgGreen(aurora.Black(remoteFile))
fmt.Printf("[*] build result copied to %s\n", coloredRemoteFile)
2019-08-17 09:05:06 +00:00
fmt.Printf("\n%s\n", q.GetSSHCommand())
2023-02-23 08:25:19 +00:00
fmt.Printf("gdb %s -ex 'target remote %s'\n\n", ki.VmlinuxPath, cmd.Gdb)
2019-08-16 06:32:26 +00:00
// TODO set substitute-path /build/.../linux-... /path/to/linux-source
2018-11-25 14:46:29 +00:00
err = interactive(q)
return
}
2023-02-23 08:25:19 +00:00
2024-02-20 13:25:31 +00:00
func firstSupported(kcfg config.KernelConfig, ka artifact.Artifact,
kernel string) (ki distro.KernelInfo, err error) {
2023-02-23 08:25:19 +00:00
km, err := kernelMask(kernel)
if err != nil {
return
}
2024-02-20 13:25:31 +00:00
ka.Targets = []artifact.Target{km}
2023-02-23 08:25:19 +00:00
for _, ki = range kcfg.Kernels {
var supported bool
supported, err = ka.Supported(ki)
if err != nil || supported {
return
}
}
2024-02-20 11:37:19 +00:00
err = errors.New("no supported kernel found")
2023-02-23 08:25:19 +00:00
return
}
func handleLine(q *qemu.System) (err error) {
fmt.Print("out-of-tree> ")
rawLine := "help"
fmt.Scanf("%s", &rawLine)
params := strings.Fields(rawLine)
cmd := params[0]
switch cmd {
case "h", "help":
fmt.Printf("help\t: print this help message\n")
fmt.Printf("log\t: print qemu log\n")
fmt.Printf("clog\t: print qemu log and cleanup buffer\n")
fmt.Printf("cleanup\t: cleanup qemu log buffer\n")
fmt.Printf("ssh\t: print arguments to ssh command\n")
fmt.Printf("quit\t: quit\n")
case "l", "log":
2023-04-06 19:50:57 +00:00
fmt.Println(q.Stdout)
2023-02-23 08:25:19 +00:00
case "cl", "clog":
2023-04-06 19:50:57 +00:00
fmt.Println(q.Stdout)
q.Stdout = ""
2023-02-23 08:25:19 +00:00
case "c", "cleanup":
2023-04-06 19:50:57 +00:00
q.Stdout = ""
2023-02-23 08:25:19 +00:00
case "s", "ssh":
fmt.Println(q.GetSSHCommand())
case "q", "quit":
return errors.New("end of session")
default:
fmt.Println("No such command")
}
return
}
func interactive(q *qemu.System) (err error) {
for {
err = handleLine(q)
if err != nil {
return
}
}
}