From 9cd92dc8f6d6dc0dc9ac701e591dcac5441fb4e8 Mon Sep 17 00:00:00 2001 From: Doug Saylor Date: Sat, 22 Jul 2023 17:43:15 -0500 Subject: [PATCH 1/7] Initial version of xorgen with base level features --- cmd/xorgen/internal/tmpl/screen_embed.go.tmpl | 43 ++++ cmd/xorgen/internal/tmpl/template.go | 198 ++++++++++++++++++ cmd/xorgen/internal/tmpl/test.txt | 1 + cmd/xorgen/internal/tmpl/test_txt.go | 30 +++ cmd/xorgen/internal/tmpl/tmpl_test.go | 13 ++ cmd/xorgen/main.go | 88 ++++++++ go.mod | 1 + go.sum | 2 + 8 files changed, 376 insertions(+) create mode 100644 cmd/xorgen/internal/tmpl/screen_embed.go.tmpl create mode 100644 cmd/xorgen/internal/tmpl/template.go create mode 100644 cmd/xorgen/internal/tmpl/test.txt create mode 100644 cmd/xorgen/internal/tmpl/test_txt.go create mode 100644 cmd/xorgen/internal/tmpl/tmpl_test.go create mode 100644 cmd/xorgen/main.go diff --git a/cmd/xorgen/internal/tmpl/screen_embed.go.tmpl b/cmd/xorgen/internal/tmpl/screen_embed.go.tmpl new file mode 100644 index 0000000..7b2d0ff --- /dev/null +++ b/cmd/xorgen/internal/tmpl/screen_embed.go.tmpl @@ -0,0 +1,43 @@ +// Code generated by xorgen, DO NOT EDIT. +package {{.Package}} + +import ( + "bytes" +{{- if .Compress }} + "compress/gzip" + "github.com/saylorsolutions/gocryptx/pkg/xor" + "io" +{{- else }} + "github.com/saylorsolutions/gocryptx/pkg/xor" +{{- end }} +) + +func {{if .Exposed}}U{{else}}u{{end}}nscreen{{.FileMethodName}}() ([]byte, error) { + key := {{ .KeyString }} + data := {{ .DataString }} + offset := {{ .Offset }} + r, err := xor.NewReader(bytes.NewReader(data), key, offset) + if err != nil { + return nil, err + } +{{- if .Compress }} + uncompress, err := gzip.NewReader(r) + if err != nil { + return nil, err + } + var out bytes.Buffer + _, err = io.Copy(&out, uncompress) + if err != nil { + return nil, err + } + uncompress.Close() + return out.Bytes(), nil +{{- else }} + out := make([]byte, len(data)) + _, err = r.Read(out) + if err != nil { + return nil, err + } + return out, nil +{{- end }} +} diff --git a/cmd/xorgen/internal/tmpl/template.go b/cmd/xorgen/internal/tmpl/template.go new file mode 100644 index 0000000..c9be50a --- /dev/null +++ b/cmd/xorgen/internal/tmpl/template.go @@ -0,0 +1,198 @@ +package tmpl + +import ( + "bytes" + "compress/gzip" + _ "embed" + "fmt" + "github.com/saylorsolutions/gocryptx/pkg/xor" + "io" + "os" + "path/filepath" + "regexp" + "text/template" + "unicode" +) + +const ( + idealMinKeyLen = 20 +) + +var ( + //go:embed screen_embed.go.tmpl + tmplText string + tmplTemplate = template.Must(template.New("template").Parse(tmplText)) +) + +type Params struct { + Package string + Exposed bool + Compress bool + FileMethodName string + KeyString string + DataString string + Offset int + keyData []byte + fileData []byte +} + +func GenerateFile(params *Params, err error) error { + if err != nil { + return err + } + out, err := os.Create(uniuncap(params.FileMethodName) + ".go") + if err != nil { + return err + } + defer func() { + _ = out.Close() + }() + + if err := tmplTemplate.Execute(out, params); err != nil { + return err + } + return nil +} + +func SetKey(file string, exposed bool, compress bool, key []byte, offset int) (*Params, error) { + params := &Params{ + Exposed: exposed, + Compress: compress, + Offset: offset, + } + if err := populateContextData(params); err != nil { + return nil, err + } + _, err := populateFileData(params, file) + if err != nil { + return nil, err + } + params.keyData = key + if err := screenData(params); err != nil { + return nil, err + } + return params, nil +} + +func RandomKeyOffset(file string, exposed bool, compress bool) (*Params, error) { + params := &Params{ + Exposed: exposed, + Compress: compress, + } + if err := populateContextData(params); err != nil { + return nil, err + } + length, err := populateFileData(params, file) + if err != nil { + return nil, err + } + var ( + key []byte + offset int + ) + switch { + case length > 3*idealMinKeyLen: + key, offset, err = xor.GenKeyAndOffset(length / 3) + case length > 2*idealMinKeyLen: + key, offset, err = xor.GenKeyAndOffset(length / 2) + default: + key, offset, err = xor.GenKeyAndOffset(length) + } + if err != nil { + return nil, err + } + params.keyData = key + params.Offset = offset + if err := screenData(params); err != nil { + return nil, err + } + return params, nil +} + +func populateContextData(params *Params) error { + cwd, err := os.Getwd() + if err != nil { + return err + } + params.Package = filepath.Base(cwd) + return nil +} + +var ( + fileCleansePattern = regexp.MustCompile(`[^a-zA-Z0-9_]`) +) + +func populateFileData(params *Params, file string) (int, error) { + f, err := os.Open(file) + if err != nil { + return 0, err + } + defer func() { + _ = f.Close() + }() + + data, err := io.ReadAll(f) + if err != nil { + return 0, err + } + + if params.Compress { + var buf bytes.Buffer + w, err := gzip.NewWriterLevel(&buf, gzip.BestCompression) + if err != nil { + return 0, err + } + _, err = w.Write(data) + if err != nil { + return 0, err + } + if err := w.Close(); err != nil { + return 0, err + } + data = buf.Bytes() + } + params.fileData = data + + _, fname := filepath.Split(file) + params.FileMethodName = fileCleansePattern.ReplaceAllString(unicap(fname), "_") + return len(data), nil +} + +func screenData(params *Params) error { + var buf bytes.Buffer + w, err := xor.NewWriter(&buf, params.keyData, params.Offset) + if err != nil { + return err + } + _, err = w.Write(params.fileData) + if err != nil { + return err + } + params.KeyString = fmt.Sprintf("%#v", params.keyData) + params.DataString = fmt.Sprintf("%#v", buf.Bytes()) + return nil +} + +func unicap(s string) string { + runes := []rune(s) + switch len(runes) { + case 0: + return "" + case 1: + return string(unicode.ToUpper(runes[0])) + default: + return string(append([]rune{unicode.ToUpper(runes[0])}, runes[1:]...)) + } +} + +func uniuncap(s string) string { + runes := []rune(s) + switch len(runes) { + case 0: + return "" + case 1: + return string(unicode.ToLower(runes[0])) + default: + return string(append([]rune{unicode.ToLower(runes[0])}, runes[1:]...)) + } +} diff --git a/cmd/xorgen/internal/tmpl/test.txt b/cmd/xorgen/internal/tmpl/test.txt new file mode 100644 index 0000000..5a891f1 --- /dev/null +++ b/cmd/xorgen/internal/tmpl/test.txt @@ -0,0 +1 @@ +A test message that should be screened \ No newline at end of file diff --git a/cmd/xorgen/internal/tmpl/test_txt.go b/cmd/xorgen/internal/tmpl/test_txt.go new file mode 100644 index 0000000..132bb57 --- /dev/null +++ b/cmd/xorgen/internal/tmpl/test_txt.go @@ -0,0 +1,30 @@ +// Code generated by xorgen, DO NOT EDIT. +package tmpl + +import ( + "bytes" + "compress/gzip" + "github.com/saylorsolutions/gocryptx/pkg/xor" + "io" +) + +func unscreenTest_txt() ([]byte, error) { + key := []byte{0xdb, 0xfd, 0x4e, 0xac, 0x38, 0xf5, 0xa, 0xef, 0xce, 0xa8, 0xf8, 0xfe, 0xbb, 0xc8, 0xc9, 0x40, 0x7, 0x5f, 0x71, 0xd8} + data := []byte{0xd7, 0x42, 0x48, 0x7, 0x5f, 0x71, 0xd8, 0xdb, 0xff, 0xb1, 0xde, 0x6c, 0xdd, 0x43, 0xc2, 0xe0, 0xf9, 0x30, 0xb3, 0x96, 0xe6, 0x87, 0xc, 0x48, 0xa, 0x59, 0x11, 0x93, 0xd1, 0x1f, 0x84, 0xf6, 0x3d, 0x25, 0x22, 0x87, 0xf9, 0xb0, 0xb4, 0xee, 0xe0, 0x87, 0x6e, 0x4d, 0x12, 0xbc, 0x93, 0x96, 0xfc, 0x4a, 0xac, 0x38, 0xa, 0xf5, 0x44, 0xdb, 0xb3, 0x72, 0xd8, 0xbb, 0xc8, 0xc9} + offset := 13 + r, err := xor.NewReader(bytes.NewReader(data), key, offset) + if err != nil { + return nil, err + } + uncompress, err := gzip.NewReader(r) + if err != nil { + return nil, err + } + var out bytes.Buffer + _, err = io.Copy(&out, uncompress) + if err != nil { + return nil, err + } + uncompress.Close() + return out.Bytes(), nil +} diff --git a/cmd/xorgen/internal/tmpl/tmpl_test.go b/cmd/xorgen/internal/tmpl/tmpl_test.go new file mode 100644 index 0000000..fed2ad4 --- /dev/null +++ b/cmd/xorgen/internal/tmpl/tmpl_test.go @@ -0,0 +1,13 @@ +//go:generate xorgen -c test.txt +package tmpl + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestUnScreenTestTxt(t *testing.T) { + data, err := unscreenTest_txt() + assert.NoError(t, err) + assert.Equal(t, "A test message that should be screened", string(data)) +} diff --git a/cmd/xorgen/main.go b/cmd/xorgen/main.go new file mode 100644 index 0000000..45d61e2 --- /dev/null +++ b/cmd/xorgen/main.go @@ -0,0 +1,88 @@ +package main + +import ( + "bytes" + "encoding/hex" + "fmt" + "github.com/saylorsolutions/gocryptx/cmd/xorgen/internal/tmpl" + flag "github.com/spf13/pflag" + "io" + "os" + "strings" +) + +func main() { + var ( + helpFlag bool + exposedFlag bool + compressFlag bool + ) + flags := flag.NewFlagSet("xorgen", flag.ContinueOnError) + flags.BoolVarP(&helpFlag, "help", "h", false, "Prints this usage information.") + flags.BoolVarP(&exposedFlag, "exposed", "E", false, "Make the unscreen method exposed from the file. It's recommended to only expose from within an internal package.") + flags.BoolVarP(&compressFlag, "compressed", "c", false, "Payload should be gzip compressed when embedded, which includes a checksum to help prevent tampering.") + flags.Usage = func() { + fmt.Printf(` +xorgen generates code to embed XOR obfuscated (and optionally compressed) data by generating a *.go file based on the input file. This pairs well with go:generate comments. +The name of the generated Go file will be based on the name of the input file, replacing characters that match the regex pattern [^a-zA-Z0-9_] with "_". +For example, given a file called super-secret.txt, a Go file will be created in the current directory called super_secret_txt.go, containing a method called unscreenSuper_secret_txt. +See the -E flag below to make it an exposed method, and make sure you review the SECURITY notes below if you're unfamiliar with XOR screening. + +USAGE: xorgen FILE [KEY] + +Note: If a key argument is given, it will be used with offset 0. + +ARGS: + FILE is the input file to be embedded. + KEY is optional and may be specified to override secure random generation behavior. + +FLAGS: +%s +SECURITY: + This is not encryption, this is obfuscation, and they are very different things! +XOR screening is intended to hide embedded data from passive binary analysis only, since XOR screening is easily reversible. +`, flags.FlagUsages()) + } + if len(os.Args) == 1 { + flags.Usage() + return + } + if err := flags.Parse(os.Args[1:]); err != nil { + flags.Usage() + fatal("Error parsing flags: %v", err) + } + if helpFlag { + flags.Usage() + return + } + + switch flags.NArg() { + case 0: + fatal("Missing required FILE argument") + case 1: + if err := tmpl.GenerateFile(tmpl.RandomKeyOffset(flags.Arg(0), exposedFlag, compressFlag)); err != nil { + fatal("Failed to generate file: %v", err) + } + default: + var key bytes.Buffer + _, err := io.Copy(&key, hex.NewDecoder(strings.NewReader(flags.Arg(1)))) + if err != nil { + fatal("Failed to decode KEY, must be a hex string with only the characters a-f, A-F, or 0-9") + } + if err := tmpl.GenerateFile(tmpl.SetKey(flags.Arg(0), exposedFlag, compressFlag, key.Bytes(), 0)); err != nil { + fatal("Failed to generate file: %v", err) + } + } +} + +func fatal(msg string, args ...any) { + echo(msg, args...) + os.Exit(1) +} + +func echo(msg string, args ...any) { + if !strings.HasSuffix(msg, "\n") { + msg += "\n" + } + fmt.Printf(msg, args...) +} diff --git a/go.mod b/go.mod index 78c2c64..ffd7ee0 100644 --- a/go.mod +++ b/go.mod @@ -7,5 +7,6 @@ require github.com/stretchr/testify v1.8.4 require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index fa4b6e6..01fa189 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= From fbcf0f9ba77cf66936e6772a78b4203b561e17e0 Mon Sep 17 00:00:00 2001 From: Doug Saylor Date: Sat, 22 Jul 2023 17:49:23 -0500 Subject: [PATCH 2/7] Adding details on xor and xorgen --- README.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b7743b3..17e7c75 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,10 @@ This repo has a few helpful crypto utilities that I've used or plan to use. --- -## Note +**Note:** + Use at your own risk! -While I've done my best to ensure that these functions are working as intended, I don't recommend using any of this for anything that could risk life, limb, property, or any serious material harm without extensive security review. +While I've done my best to ensure that this functionality is working as intended, I don't recommend using any of this for anything that could risk life, limb, property, or any serious material harm without extensive security review. If you suspect that a security issue exists, please notify me by creating an issue here on GitHub, and I will address it as soon as possible. If you are a security professional, I can always use another set of eyes to verify the security and integrity of these utilities. @@ -14,3 +15,9 @@ When the time comes where I am no longer maintaining this repository, either by If this is the case - and even before then - feel free to fork this repository and enhance as you see fit. --- + +## Packages +* **xor:** Provides some utilities for XOR screening, including an io.Reader and io.Writer implementation that screens in flight. + +## Applications +* **xorgen:** Provides a CLI that can be used with go:generate comments to easily embed XOR screened and compressed files. From 36dd84b0789cbf5ddea790e6738aa773d0ea70c8 Mon Sep 17 00:00:00 2001 From: Doug Saylor Date: Sat, 22 Jul 2023 18:14:42 -0500 Subject: [PATCH 3/7] Added stream method in case it's easier - Fixed template to use the correct indent of tab rather than spaces. - Changed Compress to Compressed in params because it looks better in the template. - Updated testing. --- cmd/xorgen/internal/tmpl/screen_embed.go.tmpl | 77 +++++++++++-------- cmd/xorgen/internal/tmpl/template.go | 14 ++-- cmd/xorgen/internal/tmpl/test_txt.go | 19 ++++- cmd/xorgen/internal/tmpl/tmpl_test.go | 17 +++- 4 files changed, 82 insertions(+), 45 deletions(-) diff --git a/cmd/xorgen/internal/tmpl/screen_embed.go.tmpl b/cmd/xorgen/internal/tmpl/screen_embed.go.tmpl index 7b2d0ff..0c30807 100644 --- a/cmd/xorgen/internal/tmpl/screen_embed.go.tmpl +++ b/cmd/xorgen/internal/tmpl/screen_embed.go.tmpl @@ -2,42 +2,57 @@ package {{.Package}} import ( - "bytes" -{{- if .Compress }} - "compress/gzip" - "github.com/saylorsolutions/gocryptx/pkg/xor" - "io" + "bytes" +{{- if .Compressed }} + "compress/gzip" + "github.com/saylorsolutions/gocryptx/pkg/xor" + "io" {{- else }} - "github.com/saylorsolutions/gocryptx/pkg/xor" + "github.com/saylorsolutions/gocryptx/pkg/xor" {{- end }} ) func {{if .Exposed}}U{{else}}u{{end}}nscreen{{.FileMethodName}}() ([]byte, error) { - key := {{ .KeyString }} - data := {{ .DataString }} - offset := {{ .Offset }} - r, err := xor.NewReader(bytes.NewReader(data), key, offset) - if err != nil { - return nil, err - } -{{- if .Compress }} - uncompress, err := gzip.NewReader(r) - if err != nil { - return nil, err - } - var out bytes.Buffer - _, err = io.Copy(&out, uncompress) - if err != nil { - return nil, err - } - uncompress.Close() - return out.Bytes(), nil + key := {{ .KeyString }} + data := {{ .DataString }} + offset := {{ .Offset }} + r, err := xor.NewReader(bytes.NewReader(data), key, offset) + if err != nil { + return nil, err + } +{{- if .Compressed }} + uncompress, err := gzip.NewReader(r) + if err != nil { + return nil, err + } + var out bytes.Buffer + _, err = io.Copy(&out, uncompress) + if err != nil { + return nil, err + } + uncompress.Close() + return out.Bytes(), nil {{- else }} - out := make([]byte, len(data)) - _, err = r.Read(out) - if err != nil { - return nil, err - } - return out, nil + out := make([]byte, len(data)) + _, err = r.Read(out) + if err != nil { + return nil, err + } + return out, nil {{- end }} } + +func {{if .Exposed}}S{{else}}s{{end}}tream{{.FileMethodName}}() (io.Reader, error) { + key := {{ .KeyString }} + data := {{ .DataString }} + offset := {{ .Offset }} +{{- if .Compressed }} + r, err := xor.NewReader(bytes.NewReader(data), key, offset) + if err != nil { + return nil, err + } + return gzip.NewReader(r) +{{- else }} + return xor.NewReader(bytes.NewReader(data), key, offset) +{{- end }} +} \ No newline at end of file diff --git a/cmd/xorgen/internal/tmpl/template.go b/cmd/xorgen/internal/tmpl/template.go index c9be50a..4b6d3c2 100644 --- a/cmd/xorgen/internal/tmpl/template.go +++ b/cmd/xorgen/internal/tmpl/template.go @@ -27,7 +27,7 @@ var ( type Params struct { Package string Exposed bool - Compress bool + Compressed bool FileMethodName string KeyString string DataString string @@ -56,9 +56,9 @@ func GenerateFile(params *Params, err error) error { func SetKey(file string, exposed bool, compress bool, key []byte, offset int) (*Params, error) { params := &Params{ - Exposed: exposed, - Compress: compress, - Offset: offset, + Exposed: exposed, + Compressed: compress, + Offset: offset, } if err := populateContextData(params); err != nil { return nil, err @@ -76,8 +76,8 @@ func SetKey(file string, exposed bool, compress bool, key []byte, offset int) (* func RandomKeyOffset(file string, exposed bool, compress bool) (*Params, error) { params := &Params{ - Exposed: exposed, - Compress: compress, + Exposed: exposed, + Compressed: compress, } if err := populateContextData(params); err != nil { return nil, err @@ -136,7 +136,7 @@ func populateFileData(params *Params, file string) (int, error) { return 0, err } - if params.Compress { + if params.Compressed { var buf bytes.Buffer w, err := gzip.NewWriterLevel(&buf, gzip.BestCompression) if err != nil { diff --git a/cmd/xorgen/internal/tmpl/test_txt.go b/cmd/xorgen/internal/tmpl/test_txt.go index 132bb57..d0309f9 100644 --- a/cmd/xorgen/internal/tmpl/test_txt.go +++ b/cmd/xorgen/internal/tmpl/test_txt.go @@ -8,10 +8,10 @@ import ( "io" ) -func unscreenTest_txt() ([]byte, error) { - key := []byte{0xdb, 0xfd, 0x4e, 0xac, 0x38, 0xf5, 0xa, 0xef, 0xce, 0xa8, 0xf8, 0xfe, 0xbb, 0xc8, 0xc9, 0x40, 0x7, 0x5f, 0x71, 0xd8} - data := []byte{0xd7, 0x42, 0x48, 0x7, 0x5f, 0x71, 0xd8, 0xdb, 0xff, 0xb1, 0xde, 0x6c, 0xdd, 0x43, 0xc2, 0xe0, 0xf9, 0x30, 0xb3, 0x96, 0xe6, 0x87, 0xc, 0x48, 0xa, 0x59, 0x11, 0x93, 0xd1, 0x1f, 0x84, 0xf6, 0x3d, 0x25, 0x22, 0x87, 0xf9, 0xb0, 0xb4, 0xee, 0xe0, 0x87, 0x6e, 0x4d, 0x12, 0xbc, 0x93, 0x96, 0xfc, 0x4a, 0xac, 0x38, 0xa, 0xf5, 0x44, 0xdb, 0xb3, 0x72, 0xd8, 0xbb, 0xc8, 0xc9} - offset := 13 +func UnscreenTest_txt() ([]byte, error) { + key := []byte{0xa8, 0x40, 0x13, 0x30, 0x25, 0x63, 0x26, 0xf6, 0xdc, 0xfc, 0x1d, 0xed, 0x44, 0x29, 0x10, 0x85, 0x2a, 0x6a, 0xfa, 0xd3} + data := []byte{0x5f, 0x98, 0x38, 0x25, 0x63, 0x26, 0xf6, 0xdc, 0xfe, 0xe2, 0x9f, 0x10, 0x1, 0x59, 0xa8, 0x4, 0x3b, 0x32, 0x9e, 0x85, 0x6e, 0x5d, 0x7c, 0x6a, 0x36, 0xe, 0x3f, 0x94, 0xd0, 0x4c, 0xc5, 0x8a, 0xe1, 0x3f, 0x48, 0x63, 0x3b, 0xb2, 0x99, 0xfd, 0x68, 0x5d, 0x1e, 0x6f, 0x2e, 0xeb, 0xbd, 0x91, 0xfd, 0x19, 0xed, 0x44, 0xd6, 0xef, 0x2e, 0x3f, 0x71, 0x70, 0xf5, 0xa8, 0x40, 0x13} + offset := 1 r, err := xor.NewReader(bytes.NewReader(data), key, offset) if err != nil { return nil, err @@ -28,3 +28,14 @@ func unscreenTest_txt() ([]byte, error) { uncompress.Close() return out.Bytes(), nil } + +func StreamTest_txt() (io.Reader, error) { + key := []byte{0xa8, 0x40, 0x13, 0x30, 0x25, 0x63, 0x26, 0xf6, 0xdc, 0xfc, 0x1d, 0xed, 0x44, 0x29, 0x10, 0x85, 0x2a, 0x6a, 0xfa, 0xd3} + data := []byte{0x5f, 0x98, 0x38, 0x25, 0x63, 0x26, 0xf6, 0xdc, 0xfe, 0xe2, 0x9f, 0x10, 0x1, 0x59, 0xa8, 0x4, 0x3b, 0x32, 0x9e, 0x85, 0x6e, 0x5d, 0x7c, 0x6a, 0x36, 0xe, 0x3f, 0x94, 0xd0, 0x4c, 0xc5, 0x8a, 0xe1, 0x3f, 0x48, 0x63, 0x3b, 0xb2, 0x99, 0xfd, 0x68, 0x5d, 0x1e, 0x6f, 0x2e, 0xeb, 0xbd, 0x91, 0xfd, 0x19, 0xed, 0x44, 0xd6, 0xef, 0x2e, 0x3f, 0x71, 0x70, 0xf5, 0xa8, 0x40, 0x13} + offset := 1 + r, err := xor.NewReader(bytes.NewReader(data), key, offset) + if err != nil { + return nil, err + } + return gzip.NewReader(r) +} diff --git a/cmd/xorgen/internal/tmpl/tmpl_test.go b/cmd/xorgen/internal/tmpl/tmpl_test.go index fed2ad4..a0e2dc5 100644 --- a/cmd/xorgen/internal/tmpl/tmpl_test.go +++ b/cmd/xorgen/internal/tmpl/tmpl_test.go @@ -1,13 +1,24 @@ -//go:generate xorgen -c test.txt +//go:generate xorgen -Ec test.txt package tmpl import ( "github.com/stretchr/testify/assert" + "io" + "strings" "testing" ) -func TestUnScreenTestTxt(t *testing.T) { - data, err := unscreenTest_txt() +func TestUnscreenTest_txt(t *testing.T) { + data, err := UnscreenTest_txt() assert.NoError(t, err) assert.Equal(t, "A test message that should be screened", string(data)) } + +func TestStreamTest_txt(t *testing.T) { + r, err := StreamTest_txt() + assert.NoError(t, err) + var buf strings.Builder + _, err = io.Copy(&buf, r) + assert.NoError(t, err) + assert.Equal(t, "A test message that should be screened", buf.String()) +} From 3f28076eb16193054f10979ffff901c277d88381 Mon Sep 17 00:00:00 2001 From: Doug Saylor Date: Sun, 23 Jul 2023 02:26:04 -0500 Subject: [PATCH 4/7] Adding newline at the end of the template --- cmd/xorgen/internal/tmpl/screen_embed.go.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/xorgen/internal/tmpl/screen_embed.go.tmpl b/cmd/xorgen/internal/tmpl/screen_embed.go.tmpl index 0c30807..381f2bb 100644 --- a/cmd/xorgen/internal/tmpl/screen_embed.go.tmpl +++ b/cmd/xorgen/internal/tmpl/screen_embed.go.tmpl @@ -55,4 +55,4 @@ func {{if .Exposed}}S{{else}}s{{end}}tream{{.FileMethodName}}() (io.Reader, erro {{- else }} return xor.NewReader(bytes.NewReader(data), key, offset) {{- end }} -} \ No newline at end of file +} From 606836dbe5f03e2635e8348ef626d60c52bebffe Mon Sep 17 00:00:00 2001 From: Doug Saylor Date: Sun, 23 Jul 2023 04:29:55 -0500 Subject: [PATCH 5/7] Moving to function options --- cmd/xorgen/internal/tmpl/template.go | 189 +++++++++++++++------------ cmd/xorgen/internal/tmpl/test_txt.go | 12 +- cmd/xorgen/main.go | 16 ++- 3 files changed, 126 insertions(+), 91 deletions(-) diff --git a/cmd/xorgen/internal/tmpl/template.go b/cmd/xorgen/internal/tmpl/template.go index 4b6d3c2..2509da8 100644 --- a/cmd/xorgen/internal/tmpl/template.go +++ b/cmd/xorgen/internal/tmpl/template.go @@ -32,81 +32,92 @@ type Params struct { KeyString string DataString string Offset int + keyData []byte fileData []byte + targetFileName string } -func GenerateFile(params *Params, err error) error { - if err != nil { - return err - } - out, err := os.Create(uniuncap(params.FileMethodName) + ".go") - if err != nil { - return err +// ParamOpt operates on Params in a standard and predictable way, and is used in GenerateFile. +// If any ParamOpt returns an error, then file generation ceases and the error is returned. +type ParamOpt = func(params *Params) error + +// CompressData indicates that data should be compressed. +func CompressData(val ...bool) ParamOpt { + return func(params *Params) error { + if len(val) > 0 { + params.Compressed = val[0] + return nil + } + params.Compressed = true + return nil } - defer func() { - _ = out.Close() - }() +} - if err := tmplTemplate.Execute(out, params); err != nil { - return err +// ExposeFunctions indicates that generated functions should be exposed. +func ExposeFunctions(val ...bool) ParamOpt { + return func(params *Params) error { + if len(val) > 0 { + params.Exposed = val[0] + return nil + } + params.Exposed = true + return nil } - return nil } -func SetKey(file string, exposed bool, compress bool, key []byte, offset int) (*Params, error) { - params := &Params{ - Exposed: exposed, - Compressed: compress, - Offset: offset, +// UseKeyOffset sets a key to be used instead of generating one randomly. +func UseKeyOffset(key []byte, offset int) ParamOpt { + return func(params *Params) error { + params.keyData = key + params.Offset = offset + return nil } +} + +// RandomKey generates a random key and offset based on the payload size. +func RandomKey() ParamOpt { + return randomKey +} + +// GenerateFile will generate a file embedding the input file with XOR screening. +// Various generation options may be passed as zero or more ParamOpt. +func GenerateFile(input string, opts ...ParamOpt) error { + params := new(Params) if err := populateContextData(params); err != nil { - return nil, err - } - _, err := populateFileData(params, file) - if err != nil { - return nil, err + return err } - params.keyData = key - if err := screenData(params); err != nil { - return nil, err + if err := populateFileData(params, input); err != nil { + return err } - return params, nil -} -func RandomKeyOffset(file string, exposed bool, compress bool) (*Params, error) { - params := &Params{ - Exposed: exposed, - Compressed: compress, - } - if err := populateContextData(params); err != nil { - return nil, err + for _, opt := range opts { + if err := opt(params); err != nil { + return err + } } - length, err := populateFileData(params, file) - if err != nil { - return nil, err + + if len(params.keyData) == 0 { + if err := randomKey(params); err != nil { + return err + } } - var ( - key []byte - offset int - ) - switch { - case length > 3*idealMinKeyLen: - key, offset, err = xor.GenKeyAndOffset(length / 3) - case length > 2*idealMinKeyLen: - key, offset, err = xor.GenKeyAndOffset(length / 2) - default: - key, offset, err = xor.GenKeyAndOffset(length) + if err := screenData(params); err != nil { + return err } + + out, err := os.Create(params.targetFileName + ".go") if err != nil { - return nil, err + return err } - params.keyData = key - params.Offset = offset - if err := screenData(params); err != nil { - return nil, err + defer func() { + _ = out.Close() + }() + + if err := tmplTemplate.Execute(out, params); err != nil { + return err } - return params, nil + return nil } func populateContextData(params *Params) error { @@ -122,10 +133,10 @@ var ( fileCleansePattern = regexp.MustCompile(`[^a-zA-Z0-9_]`) ) -func populateFileData(params *Params, file string) (int, error) { +func populateFileData(params *Params, file string) error { f, err := os.Open(file) if err != nil { - return 0, err + return err } defer func() { _ = f.Close() @@ -133,33 +144,57 @@ func populateFileData(params *Params, file string) (int, error) { data, err := io.ReadAll(f) if err != nil { - return 0, err + return err + } + params.fileData = data + _, fname := filepath.Split(file) + params.FileMethodName = fileCleansePattern.ReplaceAllString(unicap(fname), "_") + params.targetFileName = fileCleansePattern.ReplaceAllString(fname, "_") + return nil +} + +func randomKey(params *Params) error { + var ( + key []byte + offset int + err error + length = len(params.fileData) + ) + switch { + case length > 3*idealMinKeyLen: + key, offset, err = xor.GenKeyAndOffset(length / 3) + case length > 2*idealMinKeyLen: + key, offset, err = xor.GenKeyAndOffset(length / 2) + default: + key, offset, err = xor.GenKeyAndOffset(length) + } + if err != nil { + return err } + params.keyData = key + params.Offset = offset + return nil +} + +func screenData(params *Params) error { + var buf bytes.Buffer if params.Compressed { var buf bytes.Buffer w, err := gzip.NewWriterLevel(&buf, gzip.BestCompression) if err != nil { - return 0, err + return err } - _, err = w.Write(data) + _, err = w.Write(params.fileData) if err != nil { - return 0, err + return err } if err := w.Close(); err != nil { - return 0, err + return err } - data = buf.Bytes() + params.fileData = buf.Bytes() } - params.fileData = data - - _, fname := filepath.Split(file) - params.FileMethodName = fileCleansePattern.ReplaceAllString(unicap(fname), "_") - return len(data), nil -} -func screenData(params *Params) error { - var buf bytes.Buffer w, err := xor.NewWriter(&buf, params.keyData, params.Offset) if err != nil { return err @@ -184,15 +219,3 @@ func unicap(s string) string { return string(append([]rune{unicode.ToUpper(runes[0])}, runes[1:]...)) } } - -func uniuncap(s string) string { - runes := []rune(s) - switch len(runes) { - case 0: - return "" - case 1: - return string(unicode.ToLower(runes[0])) - default: - return string(append([]rune{unicode.ToLower(runes[0])}, runes[1:]...)) - } -} diff --git a/cmd/xorgen/internal/tmpl/test_txt.go b/cmd/xorgen/internal/tmpl/test_txt.go index d0309f9..c57b4ac 100644 --- a/cmd/xorgen/internal/tmpl/test_txt.go +++ b/cmd/xorgen/internal/tmpl/test_txt.go @@ -9,9 +9,9 @@ import ( ) func UnscreenTest_txt() ([]byte, error) { - key := []byte{0xa8, 0x40, 0x13, 0x30, 0x25, 0x63, 0x26, 0xf6, 0xdc, 0xfc, 0x1d, 0xed, 0x44, 0x29, 0x10, 0x85, 0x2a, 0x6a, 0xfa, 0xd3} - data := []byte{0x5f, 0x98, 0x38, 0x25, 0x63, 0x26, 0xf6, 0xdc, 0xfe, 0xe2, 0x9f, 0x10, 0x1, 0x59, 0xa8, 0x4, 0x3b, 0x32, 0x9e, 0x85, 0x6e, 0x5d, 0x7c, 0x6a, 0x36, 0xe, 0x3f, 0x94, 0xd0, 0x4c, 0xc5, 0x8a, 0xe1, 0x3f, 0x48, 0x63, 0x3b, 0xb2, 0x99, 0xfd, 0x68, 0x5d, 0x1e, 0x6f, 0x2e, 0xeb, 0xbd, 0x91, 0xfd, 0x19, 0xed, 0x44, 0xd6, 0xef, 0x2e, 0x3f, 0x71, 0x70, 0xf5, 0xa8, 0x40, 0x13} - offset := 1 + key := []byte{0xba, 0x15, 0xf1, 0x45, 0x9d, 0xa9, 0xad, 0x37, 0x22, 0x25, 0xbc, 0x37, 0x82, 0x9f, 0x42, 0xae, 0x10, 0x28, 0xfb, 0x87, 0xc7, 0x12, 0xff, 0x68, 0xba, 0x19, 0xa3, 0xff, 0x60, 0xe1, 0xf2, 0xa4, 0x37, 0xaa, 0x86, 0x38, 0x51, 0xda} + data := []byte{0xb6, 0x26, 0x3f, 0x22, 0x25, 0xbc, 0x37, 0x82, 0x9d, 0xbd, 0xdc, 0x44, 0x0, 0xb2, 0xaa, 0xe9, 0x43, 0x37, 0x25, 0x97, 0x37, 0xed, 0xb3, 0x2f, 0xb4, 0xda, 0x6d, 0x7f, 0x86, 0xd7, 0x10, 0x9f, 0x12, 0x95, 0xd8, 0xb8, 0x14, 0xd5, 0xe3, 0xf8, 0x1f, 0x6c, 0xb, 0xf6, 0x7a, 0x4f, 0xd4, 0xf, 0xaf, 0x14, 0x28, 0xfb, 0x78, 0x38, 0xb9, 0xea, 0x73, 0x30, 0x3f, 0xa3, 0xff, 0x60} + offset := 5 r, err := xor.NewReader(bytes.NewReader(data), key, offset) if err != nil { return nil, err @@ -30,9 +30,9 @@ func UnscreenTest_txt() ([]byte, error) { } func StreamTest_txt() (io.Reader, error) { - key := []byte{0xa8, 0x40, 0x13, 0x30, 0x25, 0x63, 0x26, 0xf6, 0xdc, 0xfc, 0x1d, 0xed, 0x44, 0x29, 0x10, 0x85, 0x2a, 0x6a, 0xfa, 0xd3} - data := []byte{0x5f, 0x98, 0x38, 0x25, 0x63, 0x26, 0xf6, 0xdc, 0xfe, 0xe2, 0x9f, 0x10, 0x1, 0x59, 0xa8, 0x4, 0x3b, 0x32, 0x9e, 0x85, 0x6e, 0x5d, 0x7c, 0x6a, 0x36, 0xe, 0x3f, 0x94, 0xd0, 0x4c, 0xc5, 0x8a, 0xe1, 0x3f, 0x48, 0x63, 0x3b, 0xb2, 0x99, 0xfd, 0x68, 0x5d, 0x1e, 0x6f, 0x2e, 0xeb, 0xbd, 0x91, 0xfd, 0x19, 0xed, 0x44, 0xd6, 0xef, 0x2e, 0x3f, 0x71, 0x70, 0xf5, 0xa8, 0x40, 0x13} - offset := 1 + key := []byte{0xba, 0x15, 0xf1, 0x45, 0x9d, 0xa9, 0xad, 0x37, 0x22, 0x25, 0xbc, 0x37, 0x82, 0x9f, 0x42, 0xae, 0x10, 0x28, 0xfb, 0x87, 0xc7, 0x12, 0xff, 0x68, 0xba, 0x19, 0xa3, 0xff, 0x60, 0xe1, 0xf2, 0xa4, 0x37, 0xaa, 0x86, 0x38, 0x51, 0xda} + data := []byte{0xb6, 0x26, 0x3f, 0x22, 0x25, 0xbc, 0x37, 0x82, 0x9d, 0xbd, 0xdc, 0x44, 0x0, 0xb2, 0xaa, 0xe9, 0x43, 0x37, 0x25, 0x97, 0x37, 0xed, 0xb3, 0x2f, 0xb4, 0xda, 0x6d, 0x7f, 0x86, 0xd7, 0x10, 0x9f, 0x12, 0x95, 0xd8, 0xb8, 0x14, 0xd5, 0xe3, 0xf8, 0x1f, 0x6c, 0xb, 0xf6, 0x7a, 0x4f, 0xd4, 0xf, 0xaf, 0x14, 0x28, 0xfb, 0x78, 0x38, 0xb9, 0xea, 0x73, 0x30, 0x3f, 0xa3, 0xff, 0x60} + offset := 5 r, err := xor.NewReader(bytes.NewReader(data), key, offset) if err != nil { return nil, err diff --git a/cmd/xorgen/main.go b/cmd/xorgen/main.go index 45d61e2..d4218b6 100644 --- a/cmd/xorgen/main.go +++ b/cmd/xorgen/main.go @@ -60,7 +60,13 @@ XOR screening is intended to hide embedded data from passive binary analysis onl case 0: fatal("Missing required FILE argument") case 1: - if err := tmpl.GenerateFile(tmpl.RandomKeyOffset(flags.Arg(0), exposedFlag, compressFlag)); err != nil { + err := tmpl.GenerateFile( + flags.Arg(0), + tmpl.RandomKey(), + tmpl.CompressData(compressFlag), + tmpl.ExposeFunctions(exposedFlag), + ) + if err != nil { fatal("Failed to generate file: %v", err) } default: @@ -69,7 +75,13 @@ XOR screening is intended to hide embedded data from passive binary analysis onl if err != nil { fatal("Failed to decode KEY, must be a hex string with only the characters a-f, A-F, or 0-9") } - if err := tmpl.GenerateFile(tmpl.SetKey(flags.Arg(0), exposedFlag, compressFlag, key.Bytes(), 0)); err != nil { + err = tmpl.GenerateFile( + flags.Arg(0), + tmpl.UseKeyOffset(key.Bytes(), 0), + tmpl.CompressData(compressFlag), + tmpl.ExposeFunctions(exposedFlag), + ) + if err != nil { fatal("Failed to generate file: %v", err) } } From 5960654ae4981d99bf9227e38a2a26be66689cd0 Mon Sep 17 00:00:00 2001 From: Doug Saylor Date: Sun, 23 Jul 2023 04:44:52 -0500 Subject: [PATCH 6/7] Moving key, data, and offset outside of generated functions --- cmd/xorgen/internal/tmpl/screen_embed.go.tmpl | 16 ++++++++-------- cmd/xorgen/internal/tmpl/test_txt.go | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/cmd/xorgen/internal/tmpl/screen_embed.go.tmpl b/cmd/xorgen/internal/tmpl/screen_embed.go.tmpl index 381f2bb..497aa7c 100644 --- a/cmd/xorgen/internal/tmpl/screen_embed.go.tmpl +++ b/cmd/xorgen/internal/tmpl/screen_embed.go.tmpl @@ -12,11 +12,14 @@ import ( {{- end }} ) +var ( + key{{.FileMethodName}} = {{ .KeyString }} + data{{.FileMethodName}} = {{ .DataString }} + offset{{.FileMethodName}} = {{ .Offset }} +) + func {{if .Exposed}}U{{else}}u{{end}}nscreen{{.FileMethodName}}() ([]byte, error) { - key := {{ .KeyString }} - data := {{ .DataString }} - offset := {{ .Offset }} - r, err := xor.NewReader(bytes.NewReader(data), key, offset) + r, err := xor.NewReader(bytes.NewReader(data{{.FileMethodName}}), key{{.FileMethodName}}, offset{{.FileMethodName}}) if err != nil { return nil, err } @@ -43,11 +46,8 @@ func {{if .Exposed}}U{{else}}u{{end}}nscreen{{.FileMethodName}}() ([]byte, error } func {{if .Exposed}}S{{else}}s{{end}}tream{{.FileMethodName}}() (io.Reader, error) { - key := {{ .KeyString }} - data := {{ .DataString }} - offset := {{ .Offset }} {{- if .Compressed }} - r, err := xor.NewReader(bytes.NewReader(data), key, offset) + r, err := xor.NewReader(bytes.NewReader(data{{.FileMethodName}}), key{{.FileMethodName}}, offset{{.FileMethodName}}) if err != nil { return nil, err } diff --git a/cmd/xorgen/internal/tmpl/test_txt.go b/cmd/xorgen/internal/tmpl/test_txt.go index c57b4ac..f9f6547 100644 --- a/cmd/xorgen/internal/tmpl/test_txt.go +++ b/cmd/xorgen/internal/tmpl/test_txt.go @@ -8,11 +8,14 @@ import ( "io" ) +var ( + keyTest_txt = []byte{0x9f, 0xfe, 0xfb, 0xc2, 0x8a, 0x3, 0x5c, 0x5f, 0x7d, 0x68, 0x42, 0x75, 0x50, 0x12, 0x9d, 0xec, 0xc3, 0x4f, 0xdf, 0x98, 0x75, 0x2d, 0xcc, 0xf3, 0xf3, 0xf4, 0x9b, 0xd3, 0x7, 0x92, 0xf0, 0xf1, 0x51, 0x63, 0x3a, 0xbf, 0x5f, 0x58} + dataTest_txt = []byte{0xee, 0xda, 0x6b, 0x3a, 0xbf, 0x5f, 0x58, 0x9f, 0xfc, 0x4, 0xb0, 0xde, 0x2b, 0x15, 0x72, 0x53, 0x39, 0x8a, 0x38, 0x7d, 0x3c, 0xd3, 0xa0, 0x8c, 0x1a, 0xf7, 0x51, 0x3d, 0x1, 0x9d, 0xdb, 0x3d, 0x3c, 0xb4, 0x1e, 0x4e, 0xc3, 0xb8, 0xbb, 0x4, 0x4b, 0x74, 0x91, 0x15, 0x15, 0x52, 0xb5, 0xb6, 0xc3, 0x8e, 0x3, 0x5c, 0xa0, 0x82, 0xc3, 0x57, 0x6e, 0xda, 0x34, 0x9d, 0xec, 0xc3} + offsetTest_txt = 31 +) + func UnscreenTest_txt() ([]byte, error) { - key := []byte{0xba, 0x15, 0xf1, 0x45, 0x9d, 0xa9, 0xad, 0x37, 0x22, 0x25, 0xbc, 0x37, 0x82, 0x9f, 0x42, 0xae, 0x10, 0x28, 0xfb, 0x87, 0xc7, 0x12, 0xff, 0x68, 0xba, 0x19, 0xa3, 0xff, 0x60, 0xe1, 0xf2, 0xa4, 0x37, 0xaa, 0x86, 0x38, 0x51, 0xda} - data := []byte{0xb6, 0x26, 0x3f, 0x22, 0x25, 0xbc, 0x37, 0x82, 0x9d, 0xbd, 0xdc, 0x44, 0x0, 0xb2, 0xaa, 0xe9, 0x43, 0x37, 0x25, 0x97, 0x37, 0xed, 0xb3, 0x2f, 0xb4, 0xda, 0x6d, 0x7f, 0x86, 0xd7, 0x10, 0x9f, 0x12, 0x95, 0xd8, 0xb8, 0x14, 0xd5, 0xe3, 0xf8, 0x1f, 0x6c, 0xb, 0xf6, 0x7a, 0x4f, 0xd4, 0xf, 0xaf, 0x14, 0x28, 0xfb, 0x78, 0x38, 0xb9, 0xea, 0x73, 0x30, 0x3f, 0xa3, 0xff, 0x60} - offset := 5 - r, err := xor.NewReader(bytes.NewReader(data), key, offset) + r, err := xor.NewReader(bytes.NewReader(dataTest_txt), keyTest_txt, offsetTest_txt) if err != nil { return nil, err } @@ -30,10 +33,7 @@ func UnscreenTest_txt() ([]byte, error) { } func StreamTest_txt() (io.Reader, error) { - key := []byte{0xba, 0x15, 0xf1, 0x45, 0x9d, 0xa9, 0xad, 0x37, 0x22, 0x25, 0xbc, 0x37, 0x82, 0x9f, 0x42, 0xae, 0x10, 0x28, 0xfb, 0x87, 0xc7, 0x12, 0xff, 0x68, 0xba, 0x19, 0xa3, 0xff, 0x60, 0xe1, 0xf2, 0xa4, 0x37, 0xaa, 0x86, 0x38, 0x51, 0xda} - data := []byte{0xb6, 0x26, 0x3f, 0x22, 0x25, 0xbc, 0x37, 0x82, 0x9d, 0xbd, 0xdc, 0x44, 0x0, 0xb2, 0xaa, 0xe9, 0x43, 0x37, 0x25, 0x97, 0x37, 0xed, 0xb3, 0x2f, 0xb4, 0xda, 0x6d, 0x7f, 0x86, 0xd7, 0x10, 0x9f, 0x12, 0x95, 0xd8, 0xb8, 0x14, 0xd5, 0xe3, 0xf8, 0x1f, 0x6c, 0xb, 0xf6, 0x7a, 0x4f, 0xd4, 0xf, 0xaf, 0x14, 0x28, 0xfb, 0x78, 0x38, 0xb9, 0xea, 0x73, 0x30, 0x3f, 0xa3, 0xff, 0x60} - offset := 5 - r, err := xor.NewReader(bytes.NewReader(data), key, offset) + r, err := xor.NewReader(bytes.NewReader(dataTest_txt), keyTest_txt, offsetTest_txt) if err != nil { return nil, err } From eff760b02fef93059f0756b0c51312638fdc4302 Mon Sep 17 00:00:00 2001 From: Doug Saylor Date: Sun, 23 Jul 2023 04:51:11 -0500 Subject: [PATCH 7/7] Documentation changes --- cmd/xorgen/main.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cmd/xorgen/main.go b/cmd/xorgen/main.go index d4218b6..40a9536 100644 --- a/cmd/xorgen/main.go +++ b/cmd/xorgen/main.go @@ -19,14 +19,14 @@ func main() { ) flags := flag.NewFlagSet("xorgen", flag.ContinueOnError) flags.BoolVarP(&helpFlag, "help", "h", false, "Prints this usage information.") - flags.BoolVarP(&exposedFlag, "exposed", "E", false, "Make the unscreen method exposed from the file. It's recommended to only expose from within an internal package.") + flags.BoolVarP(&exposedFlag, "exposed", "E", false, "Make the unscreen function exposed from the file. It's recommended to only expose from within an internal package.") flags.BoolVarP(&compressFlag, "compressed", "c", false, "Payload should be gzip compressed when embedded, which includes a checksum to help prevent tampering.") flags.Usage = func() { fmt.Printf(` xorgen generates code to embed XOR obfuscated (and optionally compressed) data by generating a *.go file based on the input file. This pairs well with go:generate comments. The name of the generated Go file will be based on the name of the input file, replacing characters that match the regex pattern [^a-zA-Z0-9_] with "_". -For example, given a file called super-secret.txt, a Go file will be created in the current directory called super_secret_txt.go, containing a method called unscreenSuper_secret_txt. -See the -E flag below to make it an exposed method, and make sure you review the SECURITY notes below if you're unfamiliar with XOR screening. +For example, given a file called super-secret.txt, a Go file will be created in the current directory called super_secret_txt.go, containing a function called unscreenSuper_secret_txt. +See the -E flag below to make it an exposed function, and make sure you review the SECURITY notes below if you're unfamiliar with XOR screening. USAGE: xorgen FILE [KEY] @@ -41,6 +41,8 @@ FLAGS: SECURITY: This is not encryption, this is obfuscation, and they are very different things! XOR screening is intended to hide embedded data from passive binary analysis only, since XOR screening is easily reversible. +It's noteworthy that using gzip compression could make part of the XOR key easier to recover, since the gzip header is somewhat predictable. +This isn't really important to the threat model of this obfuscation method, since the plain text key is stored right next to the screened data. `, flags.FlagUsages()) } if len(os.Args) == 1 {