mirror of
https://github.com/BishopFox/sliver.git
synced 2026-11-08 08:04:14 -10:00
Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41dc54d736 | ||
|
|
8a0694cb38 | ||
|
|
52c0618bc3 | ||
|
|
e2e1912628 | ||
|
|
36794f1b07 | ||
|
|
ccd8f75b03 |
No files matched your search
@@ -42,3 +42,4 @@ See the [wiki](https://sliver.sh/docs?name=Compile+from+Source).
|
||||
### License - GPLv3
|
||||
|
||||
Sliver is licensed under [GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html), some sub-components may have separate licenses. See their respective subdirectories in this project for details.
|
||||
|
||||
+357
-28
@@ -2,7 +2,9 @@ package edit
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/alecthomas/chroma/v2"
|
||||
"github.com/alecthomas/chroma/v2/formatters"
|
||||
@@ -37,30 +39,33 @@ type editorResult struct {
|
||||
}
|
||||
|
||||
type editorModel struct {
|
||||
lines [][]rune
|
||||
row int
|
||||
col int
|
||||
top int
|
||||
left int
|
||||
width int
|
||||
height int
|
||||
mode editorMode
|
||||
command string
|
||||
pending rune
|
||||
dirty bool
|
||||
filename string
|
||||
showLineNumbers bool
|
||||
syntaxName string
|
||||
lexer chroma.Lexer
|
||||
formatter chroma.Formatter
|
||||
style *chroma.Style
|
||||
highlighted []string
|
||||
highlightOn bool
|
||||
highlightDirty bool
|
||||
action exitAction
|
||||
forceQuit bool
|
||||
message string
|
||||
clearMessage bool
|
||||
lines [][]rune
|
||||
row int
|
||||
col int
|
||||
top int
|
||||
left int
|
||||
width int
|
||||
height int
|
||||
mode editorMode
|
||||
command string
|
||||
commandPrefix rune
|
||||
pending rune
|
||||
dirty bool
|
||||
filename string
|
||||
showLineNumbers bool
|
||||
syntaxName string
|
||||
lexer chroma.Lexer
|
||||
formatter chroma.Formatter
|
||||
style *chroma.Style
|
||||
highlighted []string
|
||||
highlightOn bool
|
||||
highlightDirty bool
|
||||
lastSearch string
|
||||
lastSearchForward bool
|
||||
action exitAction
|
||||
forceQuit bool
|
||||
message string
|
||||
clearMessage bool
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -76,6 +81,7 @@ func newEditorModel(content, filename string, lexer chroma.Lexer, syntaxName str
|
||||
mode: modeNormal,
|
||||
filename: filename,
|
||||
showLineNumbers: showLineNumbers,
|
||||
commandPrefix: ':',
|
||||
}
|
||||
model.setSyntax(lexer, syntaxName)
|
||||
return model
|
||||
@@ -175,10 +181,16 @@ func (m *editorModel) handleCommand(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||
case tea.KeyEsc:
|
||||
m.mode = modeNormal
|
||||
m.command = ""
|
||||
m.commandPrefix = ':'
|
||||
case tea.KeyEnter:
|
||||
m.executeCommand(strings.TrimSpace(m.command))
|
||||
if m.commandPrefix == '/' {
|
||||
m.executeSearch(strings.TrimSpace(m.command))
|
||||
} else {
|
||||
m.executeCommand(strings.TrimSpace(m.command))
|
||||
}
|
||||
m.command = ""
|
||||
m.mode = modeNormal
|
||||
m.commandPrefix = ':'
|
||||
if m.action != actionNone {
|
||||
return m, tea.Quit
|
||||
}
|
||||
@@ -276,6 +288,14 @@ func (m *editorModel) handleNormalRune(r rune) bool {
|
||||
m.moveUp()
|
||||
case 'l':
|
||||
m.moveRight()
|
||||
case 'w':
|
||||
m.moveWordForward()
|
||||
case 'b':
|
||||
m.moveWordBackward()
|
||||
case 'e':
|
||||
m.moveWordEnd()
|
||||
case '^':
|
||||
m.moveLineFirstNonWhitespace()
|
||||
case '0':
|
||||
m.moveLineStart()
|
||||
case '$':
|
||||
@@ -303,15 +323,31 @@ func (m *editorModel) handleNormalRune(r rune) bool {
|
||||
m.pending = 'g'
|
||||
case 'G':
|
||||
m.gotoBottom()
|
||||
case 'n':
|
||||
m.searchNext(true)
|
||||
case 'N':
|
||||
m.searchNext(false)
|
||||
case ':':
|
||||
m.mode = modeCommand
|
||||
m.command = ""
|
||||
m.commandPrefix = ':'
|
||||
case '/':
|
||||
m.mode = modeCommand
|
||||
m.command = ""
|
||||
m.commandPrefix = '/'
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *editorModel) executeCommand(cmd string) {
|
||||
if isDigits(cmd) {
|
||||
target, err := strconv.Atoi(cmd)
|
||||
if err == nil {
|
||||
m.gotoLine(target)
|
||||
return
|
||||
}
|
||||
}
|
||||
switch strings.ToLower(cmd) {
|
||||
case "":
|
||||
return
|
||||
@@ -408,15 +444,19 @@ func (m *editorModel) statusLine() string {
|
||||
|
||||
func (m *editorModel) commandLine() string {
|
||||
if m.mode == modeCommand {
|
||||
return ":" + m.command
|
||||
prefix := ":"
|
||||
if m.commandPrefix == '/' {
|
||||
prefix = "/"
|
||||
}
|
||||
return prefix + m.command
|
||||
}
|
||||
if m.message != "" {
|
||||
return m.message
|
||||
}
|
||||
if m.mode == modeInsert {
|
||||
return "ESC: normal :wq save+quit :q quit :n line numbers"
|
||||
return "ESC: normal / search :wq save+quit :q quit :n line numbers :<num> goto line"
|
||||
}
|
||||
return "i: insert h/j/k/l: move :wq save+quit :q quit :n line numbers"
|
||||
return "i: insert h/j/k/l: move / search :wq save+quit :q quit :n line numbers :<num> goto line"
|
||||
}
|
||||
|
||||
func (m *editorModel) content() string {
|
||||
@@ -551,6 +591,18 @@ func (m *editorModel) moveLineStart() {
|
||||
m.col = 0
|
||||
}
|
||||
|
||||
func (m *editorModel) moveLineFirstNonWhitespace() {
|
||||
line := m.lines[m.row]
|
||||
col := 0
|
||||
for col < len(line) {
|
||||
if line[col] != ' ' && line[col] != '\t' {
|
||||
break
|
||||
}
|
||||
col++
|
||||
}
|
||||
m.col = col
|
||||
}
|
||||
|
||||
func (m *editorModel) moveLineEnd() {
|
||||
m.col = len(m.lines[m.row])
|
||||
}
|
||||
@@ -570,6 +622,115 @@ func (m *editorModel) gotoBottom() {
|
||||
m.clampCol()
|
||||
}
|
||||
|
||||
func (m *editorModel) gotoLine(lineNumber int) {
|
||||
if lineNumber < 1 {
|
||||
lineNumber = 1
|
||||
}
|
||||
if len(m.lines) == 0 {
|
||||
m.row = 0
|
||||
m.col = 0
|
||||
return
|
||||
}
|
||||
if lineNumber > len(m.lines) {
|
||||
lineNumber = len(m.lines)
|
||||
}
|
||||
m.row = lineNumber - 1
|
||||
m.clampCol()
|
||||
m.ensureCursorVisible()
|
||||
}
|
||||
|
||||
func (m *editorModel) moveWordForward() {
|
||||
row := m.row
|
||||
col := m.col
|
||||
for {
|
||||
if row >= len(m.lines) {
|
||||
return
|
||||
}
|
||||
line := m.lines[row]
|
||||
if col >= len(line) {
|
||||
row++
|
||||
col = 0
|
||||
continue
|
||||
}
|
||||
if isWordChar(line[col]) {
|
||||
for col < len(line) && isWordChar(line[col]) {
|
||||
col++
|
||||
}
|
||||
}
|
||||
for col < len(line) && !isWordChar(line[col]) {
|
||||
col++
|
||||
}
|
||||
if col < len(line) {
|
||||
m.row = row
|
||||
m.col = col
|
||||
return
|
||||
}
|
||||
row++
|
||||
col = 0
|
||||
}
|
||||
}
|
||||
|
||||
func (m *editorModel) moveWordBackward() {
|
||||
row := m.row
|
||||
col := m.col - 1
|
||||
for {
|
||||
if row < 0 {
|
||||
return
|
||||
}
|
||||
line := m.lines[row]
|
||||
if col >= len(line) {
|
||||
col = len(line) - 1
|
||||
}
|
||||
for col >= 0 && (col >= len(line) || !isWordChar(line[col])) {
|
||||
col--
|
||||
}
|
||||
for col >= 0 && isWordChar(line[col]) {
|
||||
col--
|
||||
}
|
||||
if col+1 >= 0 && col+1 < len(line) {
|
||||
m.row = row
|
||||
m.col = col + 1
|
||||
return
|
||||
}
|
||||
row--
|
||||
if row >= 0 {
|
||||
col = len(m.lines[row]) - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *editorModel) moveWordEnd() {
|
||||
row := m.row
|
||||
col := m.col
|
||||
for {
|
||||
if row >= len(m.lines) {
|
||||
return
|
||||
}
|
||||
line := m.lines[row]
|
||||
if col >= len(line) {
|
||||
row++
|
||||
col = 0
|
||||
continue
|
||||
}
|
||||
if isWordChar(line[col]) {
|
||||
for col+1 < len(line) && isWordChar(line[col+1]) {
|
||||
col++
|
||||
}
|
||||
m.row = row
|
||||
m.col = col
|
||||
return
|
||||
}
|
||||
for col < len(line) && !isWordChar(line[col]) {
|
||||
col++
|
||||
}
|
||||
if col < len(line) {
|
||||
continue
|
||||
}
|
||||
row++
|
||||
col = 0
|
||||
}
|
||||
}
|
||||
|
||||
func (m *editorModel) clampCol() {
|
||||
lineLen := len(m.lines[m.row])
|
||||
if m.col > lineLen {
|
||||
@@ -763,6 +924,104 @@ func (m *editorModel) applyCursor(rendered, highlighted string, line []rune, sta
|
||||
return left + cursorStyle.Render(string(line[cursor])) + right
|
||||
}
|
||||
|
||||
func (m *editorModel) executeSearch(pattern string) {
|
||||
if pattern == "" {
|
||||
if m.lastSearch == "" {
|
||||
return
|
||||
}
|
||||
pattern = m.lastSearch
|
||||
}
|
||||
m.lastSearch = pattern
|
||||
m.lastSearchForward = true
|
||||
if !m.searchNext(true) {
|
||||
m.message = fmt.Sprintf("Pattern not found: %s", pattern)
|
||||
m.clearMessage = true
|
||||
}
|
||||
}
|
||||
|
||||
func (m *editorModel) searchNext(forward bool) bool {
|
||||
pattern := m.lastSearch
|
||||
if pattern == "" {
|
||||
m.message = "No previous search"
|
||||
m.clearMessage = true
|
||||
return false
|
||||
}
|
||||
row, col, ok := m.findMatch(pattern, forward)
|
||||
if !ok {
|
||||
m.message = fmt.Sprintf("Pattern not found: %s", pattern)
|
||||
m.clearMessage = true
|
||||
return false
|
||||
}
|
||||
m.row = row
|
||||
m.col = col
|
||||
m.lastSearchForward = forward
|
||||
m.ensureCursorVisible()
|
||||
return true
|
||||
}
|
||||
|
||||
func (m *editorModel) findMatch(pattern string, forward bool) (int, int, bool) {
|
||||
if pattern == "" {
|
||||
return 0, 0, false
|
||||
}
|
||||
pat := []rune(pattern)
|
||||
if forward {
|
||||
return m.findForward(pat)
|
||||
}
|
||||
return m.findBackward(pat)
|
||||
}
|
||||
|
||||
func (m *editorModel) findForward(pattern []rune) (int, int, bool) {
|
||||
row := m.row
|
||||
col := m.col + 1
|
||||
for r := row; r < len(m.lines); r++ {
|
||||
line := m.lines[r]
|
||||
start := 0
|
||||
if r == row {
|
||||
start = col
|
||||
}
|
||||
if idx := indexOfRunes(line, pattern, start); idx >= 0 {
|
||||
return r, idx, true
|
||||
}
|
||||
}
|
||||
for r := 0; r <= row && r < len(m.lines); r++ {
|
||||
line := m.lines[r]
|
||||
end := len(line)
|
||||
if r == row {
|
||||
end = min(end, col)
|
||||
}
|
||||
if idx := indexOfRunes(line, pattern, 0); idx >= 0 && idx < end {
|
||||
return r, idx, true
|
||||
}
|
||||
}
|
||||
return 0, 0, false
|
||||
}
|
||||
|
||||
func (m *editorModel) findBackward(pattern []rune) (int, int, bool) {
|
||||
row := m.row
|
||||
col := m.col - 1
|
||||
for r := row; r >= 0; r-- {
|
||||
line := m.lines[r]
|
||||
end := len(line)
|
||||
if r == row {
|
||||
end = col + 1
|
||||
}
|
||||
if idx := lastIndexOfRunes(line, pattern, end); idx >= 0 {
|
||||
return r, idx, true
|
||||
}
|
||||
}
|
||||
for r := len(m.lines) - 1; r >= row && r >= 0; r-- {
|
||||
line := m.lines[r]
|
||||
end := len(line)
|
||||
if r == row {
|
||||
end = len(line)
|
||||
}
|
||||
if idx := lastIndexOfRunes(line, pattern, end); idx >= 0 {
|
||||
return r, idx, true
|
||||
}
|
||||
}
|
||||
return 0, 0, false
|
||||
}
|
||||
|
||||
func splitLines(content string) [][]rune {
|
||||
parts := strings.Split(content, "\n")
|
||||
if len(parts) == 0 {
|
||||
@@ -783,6 +1042,76 @@ func popRune(value string) string {
|
||||
return string(runes[:len(runes)-1])
|
||||
}
|
||||
|
||||
func isDigits(value string) bool {
|
||||
if value == "" {
|
||||
return false
|
||||
}
|
||||
for _, r := range value {
|
||||
if r < '0' || r > '9' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func isWordChar(r rune) bool {
|
||||
return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r)
|
||||
}
|
||||
|
||||
func indexOfRunes(haystack, needle []rune, start int) int {
|
||||
if len(needle) == 0 {
|
||||
return -1
|
||||
}
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
if start >= len(haystack) {
|
||||
return -1
|
||||
}
|
||||
for i := start; i+len(needle) <= len(haystack); i++ {
|
||||
match := true
|
||||
for j := range needle {
|
||||
if haystack[i+j] != needle[j] {
|
||||
match = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if match {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func lastIndexOfRunes(haystack, needle []rune, end int) int {
|
||||
if len(needle) == 0 {
|
||||
return -1
|
||||
}
|
||||
if end > len(haystack) {
|
||||
end = len(haystack)
|
||||
}
|
||||
for i := end - len(needle); i >= 0; i-- {
|
||||
match := true
|
||||
for j := range needle {
|
||||
if haystack[i+j] != needle[j] {
|
||||
match = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if match {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func baseName(path string) string {
|
||||
if path == "" {
|
||||
return ""
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"syscall"
|
||||
|
||||
"github.com/bishopfox/sliver/implant/sliver/extension"
|
||||
"github.com/bishopfox/sliver/implant/sliver/mount"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
pb "github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"google.golang.org/protobuf/proto"
|
||||
@@ -56,6 +57,7 @@ var (
|
||||
pb.MsgNetstatReq: netstatHandler,
|
||||
|
||||
pb.MsgSideloadReq: sideloadHandler,
|
||||
pb.MsgMountReq: mountHandler,
|
||||
|
||||
pb.MsgReconfigureReq: reconfigureHandler,
|
||||
pb.MsgSSHCommandReq: runSSHCommandHandler,
|
||||
@@ -154,6 +156,27 @@ func listExtensionsHandler(data []byte, resp RPCResponse) {
|
||||
resp(data, err)
|
||||
}
|
||||
|
||||
func mountHandler(data []byte, resp RPCResponse) {
|
||||
mountReq := &pb.MountReq{}
|
||||
err := proto.Unmarshal(data, mountReq)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
mountData, err := mount.GetMountInformation()
|
||||
mountResp := &pb.Mount{
|
||||
Info: mountData,
|
||||
Response: &commonpb.Response{},
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
mountResp.Response.Err = err.Error()
|
||||
}
|
||||
|
||||
data, err = proto.Marshal(mountResp)
|
||||
resp(data, err)
|
||||
}
|
||||
|
||||
func getUid(fileInfo os.FileInfo) string {
|
||||
uid := int32(fileInfo.Sys().(*syscall.Stat_t).Uid)
|
||||
uid_str := strconv.FormatUint(uint64(uid), 10)
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
//go:build darwin
|
||||
|
||||
package mount
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2023 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func getString(input []byte) string {
|
||||
ver := string(input)
|
||||
if i := strings.Index(ver, "\x00"); i != -1 {
|
||||
ver = ver[:i]
|
||||
}
|
||||
return ver
|
||||
}
|
||||
|
||||
func formatMountFlags(flags uint32) string {
|
||||
opts := make([]string, 0, 12)
|
||||
|
||||
if flags&unix.MNT_RDONLY != 0 {
|
||||
opts = append(opts, "ro")
|
||||
} else {
|
||||
opts = append(opts, "rw")
|
||||
}
|
||||
if flags&unix.MNT_NOSUID != 0 {
|
||||
opts = append(opts, "nosuid")
|
||||
}
|
||||
if flags&unix.MNT_NODEV != 0 {
|
||||
opts = append(opts, "nodev")
|
||||
}
|
||||
if flags&unix.MNT_NOEXEC != 0 {
|
||||
opts = append(opts, "noexec")
|
||||
}
|
||||
if flags&unix.MNT_SYNCHRONOUS != 0 {
|
||||
opts = append(opts, "sync")
|
||||
}
|
||||
if flags&unix.MNT_ASYNC != 0 {
|
||||
opts = append(opts, "async")
|
||||
}
|
||||
if flags&unix.MNT_NOATIME != 0 {
|
||||
opts = append(opts, "noatime")
|
||||
}
|
||||
if flags&unix.MNT_JOURNALED != 0 {
|
||||
opts = append(opts, "journaled")
|
||||
}
|
||||
if flags&unix.MNT_QUOTA != 0 {
|
||||
opts = append(opts, "quota")
|
||||
}
|
||||
if flags&unix.MNT_UNION != 0 {
|
||||
opts = append(opts, "union")
|
||||
}
|
||||
if flags&unix.MNT_AUTOMOUNTED != 0 {
|
||||
opts = append(opts, "automounted")
|
||||
}
|
||||
if flags&unix.MNT_REMOVABLE != 0 {
|
||||
opts = append(opts, "removable")
|
||||
}
|
||||
if flags&unix.MNT_DONTBROWSE != 0 {
|
||||
opts = append(opts, "nobrowse")
|
||||
}
|
||||
if flags&unix.MNT_IGNORE_OWNERSHIP != 0 {
|
||||
opts = append(opts, "ignore-ownership")
|
||||
}
|
||||
if flags&unix.MNT_LOCAL != 0 {
|
||||
opts = append(opts, "local")
|
||||
}
|
||||
if flags&unix.MNT_ROOTFS != 0 {
|
||||
opts = append(opts, "rootfs")
|
||||
}
|
||||
if flags&unix.MNT_SNAPSHOT != 0 {
|
||||
opts = append(opts, "snapshot")
|
||||
}
|
||||
if flags&unix.MNT_CPROTECT != 0 {
|
||||
opts = append(opts, "cprotect")
|
||||
}
|
||||
if flags&unix.MNT_NOUSERXATTR != 0 {
|
||||
opts = append(opts, "nouserxattr")
|
||||
}
|
||||
if flags&unix.MNT_QUARANTINE != 0 {
|
||||
opts = append(opts, "quarantine")
|
||||
}
|
||||
if flags&unix.MNT_UNKNOWNPERMISSIONS != 0 {
|
||||
opts = append(opts, "unknown-permissions")
|
||||
}
|
||||
if flags&unix.MNT_DEFWRITE != 0 {
|
||||
opts = append(opts, "defwrite")
|
||||
}
|
||||
if flags&unix.MNT_DOVOLFS != 0 {
|
||||
opts = append(opts, "volfs")
|
||||
}
|
||||
if flags&unix.MNT_NOBLOCK != 0 {
|
||||
opts = append(opts, "noblock")
|
||||
}
|
||||
if flags&unix.MNT_RELOAD != 0 {
|
||||
opts = append(opts, "reload")
|
||||
}
|
||||
if flags&unix.MNT_UPDATE != 0 {
|
||||
opts = append(opts, "update")
|
||||
}
|
||||
if flags&unix.MNT_EXPORTED != 0 {
|
||||
opts = append(opts, "exported")
|
||||
}
|
||||
if flags&unix.MNT_MULTILABEL != 0 {
|
||||
opts = append(opts, "multilabel")
|
||||
}
|
||||
if flags&unix.MNT_STRICTATIME != 0 {
|
||||
opts = append(opts, "strictatime")
|
||||
}
|
||||
if flags&unix.MNT_FORCE != 0 {
|
||||
opts = append(opts, "force")
|
||||
}
|
||||
|
||||
return strings.Join(opts, ",")
|
||||
}
|
||||
|
||||
func GetMountInformation() ([]*sliverpb.MountInfo, error) {
|
||||
mountInfo := make([]*sliverpb.MountInfo, 0)
|
||||
|
||||
count, err := unix.Getfsstat(nil, unix.MNT_NOWAIT)
|
||||
if err != nil || count == 0 {
|
||||
return mountInfo, err
|
||||
}
|
||||
|
||||
stats := make([]unix.Statfs_t, count)
|
||||
count, err = unix.Getfsstat(stats, unix.MNT_NOWAIT)
|
||||
if err != nil {
|
||||
return mountInfo, err
|
||||
}
|
||||
|
||||
stats = stats[:count]
|
||||
for _, stat := range stats {
|
||||
var mountData sliverpb.MountInfo
|
||||
mountData.VolumeName = getString(stat.Mntfromname[:])
|
||||
mountData.MountPoint = getString(stat.Mntonname[:])
|
||||
mountData.Label = "/"
|
||||
mountData.VolumeType = getString(stat.Fstypename[:])
|
||||
mountData.MountOptions = formatMountFlags(stat.Flags)
|
||||
mountData.TotalSpace = stat.Blocks * uint64(stat.Bsize)
|
||||
mountData.FreeSpace = stat.Bavail * uint64(stat.Bsize)
|
||||
mountData.UsedSpace = (stat.Blocks - stat.Bfree) * uint64(stat.Bsize)
|
||||
mountInfo = append(mountInfo, &mountData)
|
||||
}
|
||||
|
||||
return mountInfo, nil
|
||||
}
|
||||
@@ -1,7 +1,25 @@
|
||||
// +linux
|
||||
//go:build linux
|
||||
|
||||
package mount
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2023 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"os"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package mount
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
@@ -19,8 +20,6 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mount
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +darwin
|
||||
//go:build darwin
|
||||
|
||||
package procdump
|
||||
|
||||
|
||||
@@ -18,6 +18,129 @@ package version
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
func GetVersion() string {
|
||||
import (
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
//{{if .Config.Debug}}
|
||||
"log"
|
||||
//{{end}}
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
type plist struct {
|
||||
XMLName xml.Name `xml:"plist"`
|
||||
Dict dict `xml:"dict"`
|
||||
}
|
||||
|
||||
type dict struct {
|
||||
Key []string `xml:"key"`
|
||||
String []string `xml:"string"`
|
||||
}
|
||||
|
||||
func getString(input []byte) string {
|
||||
ver := string(input)
|
||||
if i := strings.Index(ver, "\x00"); i != -1 {
|
||||
ver = ver[:i]
|
||||
}
|
||||
return ver
|
||||
}
|
||||
|
||||
func readOSRelease() string {
|
||||
paths := []string{
|
||||
"/System/Library/CoreServices/SystemVersion.plist",
|
||||
"/System/Library/CoreServices/ServerVersion.plist",
|
||||
}
|
||||
for _, path := range paths {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
properties, err := parsePlistFile(file)
|
||||
_ = file.Close()
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("error parsing %s: %v", path, err)
|
||||
//{{end}}
|
||||
continue
|
||||
}
|
||||
if osRelease := buildOSRelease(properties); osRelease != "" {
|
||||
return osRelease
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func parsePlistFile(file *os.File) (map[string]string, error) {
|
||||
var v plist
|
||||
if err := xml.NewDecoder(file).Decode(&v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(v.Dict.Key) != len(v.Dict.String) {
|
||||
return nil, errors.New("invalid plist content")
|
||||
}
|
||||
properties := make(map[string]string, len(v.Dict.Key))
|
||||
for i, key := range v.Dict.Key {
|
||||
properties[key] = v.Dict.String[i]
|
||||
}
|
||||
return properties, nil
|
||||
}
|
||||
|
||||
func buildOSRelease(properties map[string]string) string {
|
||||
productName := properties["ProductName"]
|
||||
productVersion := properties["ProductVersion"]
|
||||
productBuildVersion := properties["ProductBuildVersion"]
|
||||
if productName == "" || productVersion == "" || productBuildVersion == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%s %s (%s)", productName, productVersion, productBuildVersion)
|
||||
}
|
||||
|
||||
func normalizeArch(arch string) string {
|
||||
arch = strings.TrimSpace(arch)
|
||||
if arch == "" {
|
||||
return ""
|
||||
}
|
||||
switch strings.ToLower(arch) {
|
||||
case "amd64":
|
||||
return "x86_64"
|
||||
case "i386", "i486", "i586", "i686", "x86":
|
||||
return "x86"
|
||||
default:
|
||||
return arch
|
||||
}
|
||||
}
|
||||
|
||||
func formatDetailedVersion(osRelease, kernel, arch string) string {
|
||||
parts := make([]string, 0, 3)
|
||||
if osRelease != "" {
|
||||
parts = append(parts, osRelease)
|
||||
}
|
||||
if kernel != "" {
|
||||
parts = append(parts, fmt.Sprintf("kernel %s", kernel))
|
||||
}
|
||||
if arch != "" {
|
||||
parts = append(parts, arch)
|
||||
}
|
||||
return strings.Join(parts, " ")
|
||||
}
|
||||
|
||||
func GetVersion() string {
|
||||
osRelease := readOSRelease()
|
||||
var uname unix.Utsname
|
||||
if err := unix.Uname(&uname); err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("error getting OS version: %v", err)
|
||||
//{{end}}
|
||||
return osRelease
|
||||
}
|
||||
if osRelease == "" {
|
||||
osRelease = getString(uname.Sysname[:])
|
||||
}
|
||||
kernel := getString(uname.Release[:])
|
||||
arch := normalizeArch(getString(uname.Machine[:]))
|
||||
return formatDetailedVersion(osRelease, kernel, arch)
|
||||
}
|
||||
@@ -22,7 +22,10 @@ package version
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
//{{if .Config.Debug}}
|
||||
"log"
|
||||
//{{end}}
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
@@ -39,11 +42,111 @@ func getString(input [65]int8) string {
|
||||
return ver
|
||||
}
|
||||
|
||||
func readOSRelease() string {
|
||||
paths := []string{"/etc/os-release", "/usr/lib/os-release"}
|
||||
for _, path := range paths {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
return buildOSRelease(parseOSRelease(string(data)))
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func parseOSRelease(content string) map[string]string {
|
||||
values := make(map[string]string)
|
||||
for _, line := range strings.Split(content, "\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
key, value, ok := strings.Cut(line, "=")
|
||||
if !ok || key == "" {
|
||||
continue
|
||||
}
|
||||
key = strings.TrimSpace(key)
|
||||
value = unescape(unquote(strings.TrimSpace(value)))
|
||||
values[key] = value
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
func unquote(s string) string {
|
||||
if len(s) < 2 {
|
||||
return s
|
||||
}
|
||||
if (s[0] == '"' || s[0] == '\'') && s[0] == s[len(s)-1] {
|
||||
return s[1 : len(s)-1]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func unescape(s string) string {
|
||||
return strings.NewReplacer(
|
||||
`\$`, `$`,
|
||||
`\"`, `"`,
|
||||
`\'`, `'`,
|
||||
`\\`, `\`,
|
||||
"\\`", "`",
|
||||
).Replace(s)
|
||||
}
|
||||
|
||||
func buildOSRelease(values map[string]string) string {
|
||||
name := values["NAME"]
|
||||
version := values["VERSION"]
|
||||
if version == "" {
|
||||
version = values["VERSION_ID"]
|
||||
}
|
||||
if name != "" && version != "" {
|
||||
return fmt.Sprintf("%s %s", name, version)
|
||||
}
|
||||
return values["PRETTY_NAME"]
|
||||
}
|
||||
|
||||
func normalizeArch(arch string) string {
|
||||
arch = strings.TrimSpace(arch)
|
||||
if arch == "" {
|
||||
return ""
|
||||
}
|
||||
switch strings.ToLower(arch) {
|
||||
case "amd64":
|
||||
return "x86_64"
|
||||
case "i386", "i486", "i586", "i686", "x86":
|
||||
return "x86"
|
||||
default:
|
||||
return arch
|
||||
}
|
||||
}
|
||||
|
||||
func formatDetailedVersion(osRelease, kernel, arch string) string {
|
||||
parts := make([]string, 0, 3)
|
||||
if osRelease != "" {
|
||||
parts = append(parts, osRelease)
|
||||
}
|
||||
if kernel != "" {
|
||||
parts = append(parts, fmt.Sprintf("kernel %s", kernel))
|
||||
}
|
||||
if arch != "" {
|
||||
parts = append(parts, arch)
|
||||
}
|
||||
return strings.Join(parts, " ")
|
||||
}
|
||||
|
||||
// GetVersion returns the os version information
|
||||
func GetVersion() string {
|
||||
osRelease := readOSRelease()
|
||||
var uname syscall.Utsname
|
||||
if err := syscall.Uname(&uname); err != nil {
|
||||
log.Fatal(err)
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("error getting OS version: %v", err)
|
||||
//{{end}}
|
||||
return osRelease
|
||||
}
|
||||
return fmt.Sprintf("%s %s %s", getString(uname.Sysname), getString(uname.Nodename), getString(uname.Release))
|
||||
if osRelease == "" {
|
||||
osRelease = getString(uname.Sysname)
|
||||
}
|
||||
kernel := getString(uname.Release)
|
||||
arch := normalizeArch(getString(uname.Machine))
|
||||
return formatDetailedVersion(osRelease, kernel, arch)
|
||||
}
|
||||
Reference in new issue
Block a user