├── go.mod ├── go.sum ├── .gitignore ├── LICENSE ├── README.md └── main.go /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/Binject/exec2shell 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/Binject/debug v0.0.0-20210312092933-6277045c2fdf 7 | github.com/akamensky/argparse v1.4.0 8 | ) 9 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/Binject/debug v0.0.0-20210312092933-6277045c2fdf h1:Cx4YJvjPZD91xiffqJOq8l3j1YKcvx3+8duqq7DX9gY= 2 | github.com/Binject/debug v0.0.0-20210312092933-6277045c2fdf/go.mod h1:QzgxDLY/qdKlvnbnb65eqTedhvQPbaSP2NqIbcuKvsQ= 3 | github.com/akamensky/argparse v1.4.0 h1:YGzvsTqCvbEZhL8zZu2AiA5nq805NZh75JNj4ajn1xc= 4 | github.com/akamensky/argparse v1.4.0/go.mod h1:S5kwC7IuDcEr5VeXtGPRVZ5o/FdhcMlQz4IZQuw64xA= 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # If you prefer the allow list template instead of the deny list, see community template: 2 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore 3 | # 4 | # Binaries for programs and plugins 5 | *.exe 6 | *.exe~ 7 | *.dll 8 | *.so 9 | *.dylib 10 | 11 | # Test binary, built with `go test -c` 12 | *.test 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | 17 | # Dependency directories (remove the comment below to include it) 18 | # vendor/ 19 | 20 | # Go workspace file 21 | go.work 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Binject 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # exec2shell 2 | Extracts TEXT section of a PE, ELF, or Mach-O executable to shellcode 3 | 4 | 5 | ## Installation 6 | 7 | ## To install to GOPATH/bin: 8 | 9 | ```go install github.com/Binject/exec2shell@latest``` 10 | 11 | ## Build from source: 12 | 13 | ``` 14 | git clone https://github.com/Binject/exec2shell.git 15 | 16 | cd exec2shell 17 | 18 | go build . 19 | ``` 20 | 21 | # Usage 22 | 23 | exec2shell [-h|--help] -i|--in "" [-o|--out ""] 24 | [-c|--c-outfile ""] [-n|--c-var ""] 25 | [-g|--go-outfile ""] [-p|--go-pkg ""] 26 | [-v|--go-var ""] 27 | 28 | Arguments: 29 | | Short Form | Long Form | Description | Default | 30 | | --- | --- | --- | --- | 31 | | **-h** | --help | Print help information | | 32 | | **-i** | --in | Input PE, ELF, or Mach-o binary | | 33 | | **-o** | --out | Output file - Shellcode as Binary | shellcode.bin | 34 | | **-c** | --c-outfile | Output file - Shellcode as C Array | 35 | | **-n** | --c-var | Sets variable name for C Array output. | SHELLCODE | 36 | | **-g** | --go-outfile | Output file - Shellcode as Go Array | 37 | | **-p** | --go-pkg | Sets package string for Go Array output. | shellcode | 38 | | **-v** | --go-var | Sets variable name for Go Array output. | shellcode | 39 | 40 | 41 | # Examples 42 | 43 | ### Make shellcode from a binary: 44 | 45 | ```exec2shell -i someprog.exe -o shellcode.bin``` 46 | 47 | ### Make a C-Array style header file shellcode: 48 | 49 | ```exec2shell -i someprog.exe -c shellcode.h -n SHELLCODE_VARNAME``` 50 | 51 | ### Make a Go-Array style shellcode 52 | 53 | ```exec2shell -i someprog.exe -g shellcode.go -p mypackage -v GO_VARNAME``` 54 | 55 | ### Make All the Things at Once: 56 | 57 | ```exec2shell -i someprog.exe -o shellcode.bin -c shellcode.h -g shellcode.go -p mypackage -n C_VARNAME -v GO_VARNAME``` 58 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "os" 9 | 10 | "github.com/Binject/debug/elf" 11 | "github.com/Binject/debug/macho" 12 | "github.com/Binject/debug/pe" 13 | 14 | "github.com/akamensky/argparse" 15 | ) 16 | 17 | const ( 18 | // ERROR - constant for an error 19 | ERROR = iota 20 | // ELF - constant for ELF binary format 21 | ELF = iota 22 | // MACHO - constant for Mach-O binary format 23 | MACHO = iota 24 | // PE - constant for PE binary format 25 | PE = iota 26 | ) 27 | 28 | func main() { 29 | 30 | parser := argparse.NewParser("exec2shell", "Extracts TEXT section of a PE, ELF, or Mach-O executable to shellcode") 31 | srcFile := parser.String("i", "in", &argparse.Options{Required: true, Help: "Input PE, ELF, or Mach-o binary"}) 32 | dstFile := parser.String("o", "out", &argparse.Options{Required: false, 33 | Default: "shellcode.bin", Help: "Output file - Shellcode as Binary"}) 34 | cFile := parser.String("c", "c-outfile", &argparse.Options{Required: false, 35 | Help: "Output file - Shellcode as C Array"}) 36 | cVar := parser.String("n", "c-var", &argparse.Options{Required: false, 37 | Default: "SHELLCODE", Help: "Sets variable name for C Array output"}) 38 | goFile := parser.String("g", "go-outfile", &argparse.Options{Required: false, 39 | Help: "Output file - Shellcode as Go Array"}) 40 | goPkg := parser.String("p", "go-pkg", &argparse.Options{Required: false, 41 | Default: "shellcode", Help: "Sets package string for Go Array output"}) 42 | goVar := parser.String("v", "go-var", &argparse.Options{Required: false, 43 | Default: "shellcode", Help: "Sets variable name for Go Array output"}) 44 | 45 | if err := parser.Parse(os.Args); err != nil { 46 | log.Println(parser.Usage(err)) 47 | return 48 | } 49 | 50 | btype := ERROR 51 | buf, err := ioutil.ReadFile(*srcFile) 52 | if err != nil { 53 | log.Println(err) 54 | return 55 | } 56 | 57 | if bytes.Equal(buf[:4], []byte{0x7F, 'E', 'L', 'F'}) { 58 | btype = ELF 59 | } 60 | if bytes.Equal(buf[:3], []byte{0xfe, 0xed, 0xfa}) { 61 | if buf[3] == 0xce || buf[3] == 0xcf { 62 | // FE ED FA CE - Mach-O binary (32-bit) 63 | // FE ED FA CF - Mach-O binary (64-bit) 64 | btype = MACHO 65 | } 66 | } 67 | if bytes.Equal(buf[1:4], []byte{0xfa, 0xed, 0xfe}) { 68 | if buf[0] == 0xce || buf[0] == 0xcf { 69 | // CE FA ED FE - Mach-O binary (reverse byte ordering scheme, 32-bit) 70 | // CF FA ED FE - Mach-O binary (reverse byte ordering scheme, 64-bit) 71 | btype = MACHO 72 | } 73 | } 74 | if bytes.Equal(buf[:2], []byte{0x4d, 0x5a}) { 75 | btype = PE 76 | } 77 | 78 | if btype == ERROR { 79 | log.Println("Unknown Binary Format") 80 | return 81 | } 82 | 83 | var data []byte 84 | 85 | switch btype { 86 | case ELF: 87 | elfFile, err := elf.NewFile(bytes.NewReader(buf)) 88 | if err != nil { 89 | log.Println(err) 90 | return 91 | } 92 | for _, p := range elfFile.Progs { 93 | if p.Type == elf.PT_LOAD && p.Flags == (elf.PF_R|elf.PF_X) { 94 | data = make([]byte, p.Filesz) 95 | n, err := p.ReaderAt.ReadAt(data, 0) 96 | if n != int(p.Filesz) || err != nil { 97 | log.Println(n, err) 98 | return 99 | } 100 | err = os.WriteFile(*dstFile, data, 0660) 101 | if err != nil { 102 | log.Println(err) 103 | return 104 | } 105 | break 106 | } 107 | } 108 | case MACHO: 109 | machoFile, err := macho.NewFile(bytes.NewReader(buf)) 110 | if err != nil { 111 | log.Println(err) 112 | return 113 | } 114 | for _, section := range machoFile.Sections { 115 | if section.SectionHeader.Seg == "__TEXT" && section.Name == "__text" { 116 | data, err = section.Data() 117 | if err != nil { 118 | log.Println(err) 119 | return 120 | } 121 | err = os.WriteFile(*dstFile, data, 0660) 122 | if err != nil { 123 | log.Println(err) 124 | return 125 | } 126 | break 127 | } 128 | } 129 | case PE: 130 | peFile, err := pe.NewFile(bytes.NewReader(buf)) 131 | if err != nil { 132 | log.Println(err) 133 | return 134 | } 135 | for _, section := range peFile.Sections { 136 | flags := section.Characteristics 137 | if flags&pe.IMAGE_SCN_MEM_EXECUTE != 0 { // this section is executable 138 | 139 | data, err = section.Data() 140 | if err != nil { 141 | log.Println(err) 142 | return 143 | } 144 | err = os.WriteFile(*dstFile, data, 0660) 145 | if err != nil { 146 | log.Println(err) 147 | return 148 | } 149 | break 150 | } 151 | } 152 | } 153 | 154 | if len(*cFile) > 0 { 155 | b := bytes.Buffer{} 156 | b.WriteString(fmt.Sprintf("\nunsigned char %s[] = {", *cVar)) 157 | for i := 0; i < len(data); i++ { 158 | if i%12 == 0 { 159 | b.WriteString("\n ") 160 | } 161 | b.WriteString(fmt.Sprintf("0x%02x", data[i])) 162 | if i+1 != len(data) { 163 | b.WriteString(", ") 164 | } 165 | } 166 | b.WriteString("};\n\n") 167 | err = os.WriteFile(*cFile, b.Bytes(), 0660) 168 | if err != nil { 169 | log.Println(err) 170 | return 171 | } 172 | } 173 | 174 | if len(*goFile) > 0 { 175 | b := bytes.Buffer{} 176 | b.WriteString(fmt.Sprintf("package %s\n\nvar %s = []byte{\n", *goPkg, *goVar)) 177 | for i := 0; i < len(data); i++ { 178 | if i%12 == 0 { 179 | b.WriteString("\n ") 180 | } 181 | b.WriteString(fmt.Sprintf("0x%02x", data[i])) 182 | if i+1 != len(data) { 183 | b.WriteString(", ") 184 | } 185 | } 186 | b.WriteString("};\n\n") 187 | err = os.WriteFile(*goFile, b.Bytes(), 0660) 188 | if err != nil { 189 | log.Println(err) 190 | return 191 | } 192 | } 193 | } 194 | --------------------------------------------------------------------------------