Skip to content

Commit b8a1854

Browse files
committed
Add examples
1 parent d899ca9 commit b8a1854

File tree

11 files changed

+382
-0
lines changed

11 files changed

+382
-0
lines changed

.github/workflows/build.yml

+6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ name: Build main
33
on:
44
push:
55
branches: [main]
6+
pull_request:
7+
branches: [main]
68
jobs:
79
build:
810
runs-on: windows-latest
@@ -34,3 +36,7 @@ jobs:
3436
name: Shelidate
3537
path: shelidate.exe
3638
retention-days: 7
39+
- name: Run example/simple tests
40+
run: go test -timeout 30s -v ./examples/simple/...
41+
- name: Run example/options tests
42+
run: go test -timeout 30s -v ./examples/options/...

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.exe

.vscode/c_cpp_properties.json

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"configurations": [
3+
{
4+
"name": "Win32",
5+
"includePath": [
6+
"${workspaceFolder}/**"
7+
],
8+
"defines": [
9+
"_DEBUG",
10+
"UNICODE",
11+
"_UNICODE"
12+
],
13+
"windowsSdkVersion": "10.0.22621.0",
14+
"cStandard": "c17",
15+
"cppStandard": "c++17",
16+
"intelliSenseMode": "windows-gcc-x64",
17+
"compilerPath": "C:\\msys64\\ucrt64\\bin\\gcc.exe"
18+
}
19+
],
20+
"version": 4
21+
}

.vscode/settings.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"C_Cpp.default.compilerPath": ""
3+
}

examples/options/main.c.tmpl

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#include <windows.h>
2+
#include <stdio.h>
3+
4+
DWORD WINAPI run() {
5+
unsigned char shellcode[] = {{ byteArray .Shellcode }};
6+
{{ if .Prepend }}
7+
SIZE_T shellcode_len = {{ len .Shellcode | add 1024 }};
8+
{{ else }}
9+
SIZE_T shellcode_len = {{ len .Shellcode }};
10+
{{ end }}
11+
12+
{{ if .RWX }}
13+
LPVOID dest = VirtualAlloc(NULL, shellcode_len, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE);
14+
{{ else }}
15+
LPVOID dest = VirtualAlloc(NULL, shellcode_len, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
16+
{{ end }}
17+
if (dest == NULL)
18+
{
19+
return 0;
20+
}
21+
22+
{{ if .Prepend }}
23+
memcpy(dest + 1024, shellcode, shellcode_len - 1024);
24+
{{ else }}
25+
memcpy(dest, shellcode, shellcode_len);
26+
{{ end }}
27+
28+
{{ if not .RWX }}
29+
DWORD oldProtect = 0;
30+
VirtualProtect(dest, shellcode_len, PAGE_EXECUTE_READ, &oldProtect);
31+
{{ end }}
32+
33+
void (*function)();
34+
{{ if .Prepend }}
35+
function = (void (*)())(dest + 1024);
36+
{{ else }}
37+
function = (void (*)())dest;
38+
{{ end }}
39+
function();
40+
41+
return 0;
42+
}
43+
44+
int main() {
45+
run();
46+
return 0;
47+
}

examples/options/main.go

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package main
2+
3+
import (
4+
_ "embed"
5+
"fmt"
6+
"html/template"
7+
"os"
8+
"os/exec"
9+
"strings"
10+
)
11+
12+
//go:embed main.c.tmpl
13+
var tmplS string
14+
15+
// byteArray returns a string representation of a C byte array
16+
func byteArray(b []byte) string {
17+
var s strings.Builder
18+
fmt.Fprint(&s, "{ ")
19+
full:
20+
for i := 0; i < len(b); i += 16 {
21+
for j := 0; j < 16; j++ {
22+
if i+j >= len(b)-1 {
23+
fmt.Fprintf(&s, "0x%x", b[i+j])
24+
break full
25+
}
26+
fmt.Fprintf(&s, "0x%x, ", b[i+j])
27+
}
28+
fmt.Fprint(&s, "\n")
29+
}
30+
fmt.Fprint(&s, " }")
31+
return s.String()
32+
}
33+
34+
func add(a int, b int) int {
35+
return a + b
36+
}
37+
38+
type State struct {
39+
Shellcode []byte
40+
RWX bool
41+
Prepend bool
42+
}
43+
44+
func generate(shellcode string, rwx bool, prepend bool, out string) error {
45+
tmpl := template.Must(template.New("output").Funcs(template.FuncMap{
46+
"add": add,
47+
"byteArray": byteArray,
48+
}).Parse(tmplS))
49+
50+
shellcodeB, err := os.ReadFile(shellcode)
51+
if err != nil {
52+
return fmt.Errorf("unable to open shellcode: %v", err)
53+
}
54+
55+
state := State{
56+
Shellcode: shellcodeB,
57+
RWX: rwx,
58+
Prepend: prepend,
59+
}
60+
61+
output, err := os.CreateTemp("", "example-options-*.c")
62+
if err != nil {
63+
return fmt.Errorf("unable to open output file: %v", err)
64+
}
65+
defer os.Remove(output.Name())
66+
67+
if err = tmpl.Execute(output, &state); err != nil {
68+
return fmt.Errorf("unable to template output file: %v", err)
69+
}
70+
71+
if out, err := exec.Command("gcc", output.Name(), "-o", out).CombinedOutput(); err != nil {
72+
return fmt.Errorf("unable to compile (%s): %v", out, err)
73+
}
74+
75+
return nil
76+
}
77+
78+
func main() {
79+
if len(os.Args) != 3 {
80+
fmt.Printf("usage: %v <shellcode_file> <output_file>", os.Args[0])
81+
return
82+
}
83+
84+
if err := generate(os.Args[1], true, true, os.Args[2]); err != nil {
85+
fmt.Printf("failed to generate: %v", err)
86+
return
87+
}
88+
}

examples/options/main_test.go

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"math/rand/v2"
6+
"os"
7+
"os/exec"
8+
"path/filepath"
9+
"sync"
10+
"testing"
11+
12+
"github.com/mandiant/shelidate"
13+
)
14+
15+
var s shelidate.Server
16+
17+
func TestGenerate(t *testing.T) {
18+
tmpdir := t.TempDir()
19+
sh, err := s.Generate(tmpdir, rand.Uint32())
20+
if err != nil {
21+
t.Fatalf("failed to generate test shellcode: %v", err)
22+
}
23+
24+
tests := []struct {
25+
RWX bool
26+
Prepend bool
27+
}{
28+
{RWX: false, Prepend: false},
29+
{RWX: true, Prepend: false},
30+
{RWX: false, Prepend: true},
31+
{RWX: true, Prepend: true},
32+
}
33+
for _, args := range tests {
34+
t.Run(fmt.Sprintf("RXW:%v;Prepend:%v", args.RWX, args.Prepend), func(t *testing.T) {
35+
out := filepath.Join(tmpdir, fmt.Sprintf("main-%v-%v.exe", args.RWX, args.Prepend))
36+
37+
if err := generate(sh.Path, args.RWX, args.Prepend, out); err != nil {
38+
t.Fatalf("failed to generate payload: %v", err)
39+
}
40+
41+
var wg sync.WaitGroup
42+
wg.Add(1)
43+
go func() {
44+
defer wg.Done()
45+
<-sh.Recv
46+
}()
47+
if _, err := exec.Command(out).CombinedOutput(); err != nil {
48+
t.Fatalf("failed to exec payload: %v", err)
49+
}
50+
wg.Wait()
51+
})
52+
}
53+
}
54+
55+
func TestMain(m *testing.M) {
56+
if err := s.Listen("127.0.0.1:13373"); err != nil {
57+
os.Exit(1)
58+
}
59+
defer s.Close()
60+
61+
code := m.Run()
62+
os.Exit(code)
63+
}

examples/simple/main.c.tmpl

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#include <windows.h>
2+
3+
DWORD WINAPI run() {
4+
unsigned char shellcode[] = {{ byteArray .Shellcode }};
5+
SIZE_T shellcode_len = {{ len .Shellcode }};
6+
7+
LPVOID dest = VirtualAlloc(NULL, shellcode_len, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE);
8+
if (dest == NULL)
9+
{
10+
return 0;
11+
}
12+
memcpy(dest, shellcode, shellcode_len);
13+
14+
15+
void (*function)();
16+
function = (void (*)())dest;
17+
function();
18+
19+
return 0;
20+
}
21+
22+
int main() {
23+
run();
24+
return 0;
25+
}

examples/simple/main.go

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package main
2+
3+
import (
4+
_ "embed"
5+
"fmt"
6+
"html/template"
7+
"os"
8+
"os/exec"
9+
"strings"
10+
)
11+
12+
//go:embed main.c.tmpl
13+
var tmplS string
14+
15+
// byteArray returns a string representation of a C byte array
16+
func byteArray(b []byte) string {
17+
var s strings.Builder
18+
fmt.Fprint(&s, "{ ")
19+
full:
20+
for i := 0; i < len(b); i += 16 {
21+
for j := 0; j < 16; j++ {
22+
if i+j >= len(b)-1 {
23+
fmt.Fprintf(&s, "0x%x", b[i+j])
24+
break full
25+
}
26+
fmt.Fprintf(&s, "0x%x, ", b[i+j])
27+
}
28+
fmt.Fprint(&s, "\n")
29+
}
30+
fmt.Fprint(&s, " }")
31+
return s.String()
32+
}
33+
34+
type State struct {
35+
Shellcode []byte
36+
}
37+
38+
func generate(shellcode string, out string) error {
39+
tmpl := template.Must(template.New("output").Funcs(template.FuncMap{
40+
"byteArray": byteArray,
41+
}).Parse(tmplS))
42+
43+
shellcodeB, err := os.ReadFile(shellcode)
44+
if err != nil {
45+
return fmt.Errorf("unable to open shellcode: %v", err)
46+
}
47+
48+
state := State{
49+
Shellcode: shellcodeB,
50+
}
51+
52+
output, err := os.CreateTemp("", "example-simple-*.c")
53+
if err != nil {
54+
return fmt.Errorf("unable to open output file: %v", err)
55+
}
56+
defer os.Remove(output.Name())
57+
58+
if err = tmpl.Execute(output, &state); err != nil {
59+
return fmt.Errorf("unable to template output file: %v", err)
60+
}
61+
62+
if out, err := exec.Command("gcc", output.Name(), "-o", out).CombinedOutput(); err != nil {
63+
return fmt.Errorf("unable to compile (%s): %v", out, err)
64+
}
65+
66+
return nil
67+
}
68+
69+
func main() {
70+
if len(os.Args) != 3 {
71+
fmt.Printf("usage: %v <shellcode_file> <output_file>", os.Args[0])
72+
return
73+
}
74+
75+
if err := generate(os.Args[1], os.Args[2]); err != nil {
76+
fmt.Printf("failed to generate: %v", err)
77+
return
78+
}
79+
}

0 commit comments

Comments
 (0)