mirror of
https://github.com/BishopFox/sliver
synced 2026-06-08 10:29:05 +00:00
167 lines
4.5 KiB
Go
167 lines
4.5 KiB
Go
package shellcodeencoders
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/bishopfox/sliver/client/console"
|
|
consts "github.com/bishopfox/sliver/client/constants"
|
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
// ShellcodeEncodersEncodeCmd - Encode shellcode files with server encoders.
|
|
func ShellcodeEncodersEncodeCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
|
encoderFlag, _ := cmd.Flags().GetString("encoder")
|
|
if strings.TrimSpace(encoderFlag) == "" {
|
|
con.PrintErrorf("Encoder is required (use --encoder). See %s for options.\n", consts.ShellcodeEncodersStr)
|
|
return
|
|
}
|
|
|
|
encoderName := normalizeShellcodeEncoderName(encoderFlag)
|
|
encoderEnum, ok := shellcodeEncoderEnum(encoderName)
|
|
if !ok {
|
|
con.PrintErrorf("Unknown encoder %q (see %s for supported encoders)\n", encoderFlag, consts.ShellcodeEncodersStr)
|
|
return
|
|
}
|
|
|
|
archFlag, _ := cmd.Flags().GetString("arch")
|
|
arch := normalizeShellcodeArch(archFlag)
|
|
if arch == "" {
|
|
arch = "amd64"
|
|
}
|
|
|
|
iterations, _ := cmd.Flags().GetInt("iterations")
|
|
if iterations <= 0 {
|
|
iterations = 1
|
|
}
|
|
|
|
badCharsRaw, _ := cmd.Flags().GetString("bad-chars")
|
|
badChars, err := decodeHexBytes("bad-chars", badCharsRaw)
|
|
if err != nil {
|
|
con.PrintErrorf("%s\n", err)
|
|
return
|
|
}
|
|
|
|
output, _ := cmd.Flags().GetString("output")
|
|
outputInfo, err := outputInfoForArgs(output, len(args))
|
|
if err != nil {
|
|
con.PrintErrorf("%s\n", err)
|
|
return
|
|
}
|
|
|
|
grpcCtx, cancel := con.GrpcContext(cmd)
|
|
defer cancel()
|
|
|
|
hadError := false
|
|
for _, path := range args {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
con.PrintErrorf("Failed to read %s: %s\n", path, err)
|
|
hadError = true
|
|
continue
|
|
}
|
|
|
|
resp, err := con.Rpc.ShellcodeEncoder(grpcCtx, &clientpb.ShellcodeEncodeReq{
|
|
Encoder: encoderEnum,
|
|
Architecture: arch,
|
|
Iterations: uint32(iterations),
|
|
BadChars: badChars,
|
|
Data: data,
|
|
Request: con.ActiveTarget.Request(cmd),
|
|
})
|
|
if err != nil {
|
|
con.PrintErrorf("Failed to encode %s: %s\n", path, err)
|
|
hadError = true
|
|
continue
|
|
}
|
|
if resp.GetResponse() != nil && resp.GetResponse().GetErr() != "" {
|
|
con.PrintErrorf("Failed to encode %s: %s\n", path, resp.GetResponse().GetErr())
|
|
hadError = true
|
|
continue
|
|
}
|
|
|
|
outputPath, err := resolveOutputPath(path, output, outputInfo, encoderName, len(args) > 1)
|
|
if err != nil {
|
|
con.PrintErrorf("Failed to write %s: %s\n", path, err)
|
|
hadError = true
|
|
continue
|
|
}
|
|
|
|
if err := os.WriteFile(outputPath, resp.Data, 0o644); err != nil {
|
|
con.PrintErrorf("Failed to write %s: %s\n", outputPath, err)
|
|
hadError = true
|
|
continue
|
|
}
|
|
|
|
con.PrintInfof("Shellcode written to %s (%d bytes)\n", outputPath, len(resp.Data))
|
|
}
|
|
|
|
if hadError {
|
|
con.PrintWarnf("One or more files failed to encode\n")
|
|
}
|
|
}
|
|
|
|
func decodeHexBytes(label string, raw string) ([]byte, error) {
|
|
trimmed := strings.TrimSpace(raw)
|
|
trimmed = strings.TrimPrefix(trimmed, "0x")
|
|
trimmed = strings.TrimPrefix(trimmed, "0X")
|
|
trimmed = strings.ReplaceAll(trimmed, " ", "")
|
|
if trimmed == "" {
|
|
return nil, nil
|
|
}
|
|
data, err := hex.DecodeString(trimmed)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%s must be hex encoded: %w", label, err)
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
func outputInfoForArgs(output string, argCount int) (os.FileInfo, error) {
|
|
if output == "" {
|
|
return nil, nil
|
|
}
|
|
info, err := os.Stat(output)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
if argCount > 1 {
|
|
if mkErr := os.MkdirAll(output, 0o755); mkErr != nil {
|
|
return nil, fmt.Errorf("failed to create output directory %s: %w", output, mkErr)
|
|
}
|
|
info, err = os.Stat(output)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to stat output directory %s: %w", output, err)
|
|
}
|
|
return info, nil
|
|
}
|
|
return nil, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
if argCount > 1 && !info.IsDir() {
|
|
return nil, fmt.Errorf("output must be a directory when encoding multiple files")
|
|
}
|
|
return info, nil
|
|
}
|
|
|
|
func resolveOutputPath(inputPath string, output string, outputInfo os.FileInfo, encoderName string, multiple bool) (string, error) {
|
|
suffix := "." + strings.ReplaceAll(encoderName, "_", "-")
|
|
base := filepath.Base(inputPath)
|
|
if output == "" {
|
|
return filepath.Join(filepath.Dir(inputPath), base+suffix), nil
|
|
}
|
|
if multiple {
|
|
if outputInfo == nil || !outputInfo.IsDir() {
|
|
return "", fmt.Errorf("output must be a directory when encoding multiple files")
|
|
}
|
|
return filepath.Join(output, base+suffix), nil
|
|
}
|
|
if outputInfo != nil && outputInfo.IsDir() {
|
|
return filepath.Join(output, base+suffix), nil
|
|
}
|
|
return output, nil
|
|
}
|