Fixes several bugs in encoding and obfuscation and adds a new payload execution stress test.

- fix(encode): Remove redundant modulo operations from ADD/SUB ciphers
- fix(obfuscate): Remove "JMP 2" from the list of safe garbage instructions
- fix(obfuscate): Use correct slice length when selecting random garbage instruction
- fix(sgn): Correct register exclusion logic in GetSafeRandomRegister
- test: Add integration stress test for payload execution on Windows
This commit is contained in:
Drew Bonasera
2026-04-29 02:45:39 -04:00
committed by Drew
parent f54fa65b57
commit 6203484fcd
5 changed files with 82 additions and 6 deletions
+2 -2
View File
@@ -154,9 +154,9 @@ func (encoder *Encoder) SchemaCipher(data []byte, index int, schema SCHEMA) []by
case "XOR":
binary.BigEndian.PutUint32(data[index:index+4], (binary.BigEndian.Uint32(data[index:index+4]) ^ binary.LittleEndian.Uint32(cursor.Key)))
case "ADD":
binary.LittleEndian.PutUint32(data[index:index+4], (binary.LittleEndian.Uint32(data[index:index+4])-binary.BigEndian.Uint32(cursor.Key))%0xFFFFFFFF)
binary.LittleEndian.PutUint32(data[index:index+4], binary.LittleEndian.Uint32(data[index:index+4])-binary.BigEndian.Uint32(cursor.Key))
case "SUB":
binary.LittleEndian.PutUint32(data[index:index+4], (binary.LittleEndian.Uint32(data[index:index+4])+binary.BigEndian.Uint32(cursor.Key))%0xFFFFFFFF)
binary.LittleEndian.PutUint32(data[index:index+4], binary.LittleEndian.Uint32(data[index:index+4])+binary.BigEndian.Uint32(cursor.Key))
case "ROL":
binary.LittleEndian.PutUint32(data[index:index+4], bits.RotateLeft32(binary.LittleEndian.Uint32(data[index:index+4]), -int(binary.BigEndian.Uint32(cursor.Key))))
case "ROR":
-1
View File
@@ -47,7 +47,6 @@ var SafeGarbageInstructions = []string{
"FNOP",
"FXAM",
"FTST",
"JMP 2",
"ROL {R},0",
"ROR {R},0",
"SHL {R},0",
+1 -1
View File
@@ -71,7 +71,7 @@ func GetRandomSafeAssembly() string {
newSafeGarbageInstructions = append(newSafeGarbageInstructions, jmp+" {L};{G};{L}:")
//newSafeGarbageInstructions = append(newSafeGarbageInstructions, jmp+" 2")
}
return newSafeGarbageInstructions[rand.Intn(len(SafeGarbageInstructions))]
return newSafeGarbageInstructions[rand.Intn(len(newSafeGarbageInstructions))]
}
// GetRandomUnsafeAssembly return a safe garbage instruction assembly
+7 -2
View File
@@ -162,11 +162,16 @@ func (encoder Encoder) GetBasePointer() string {
func (encoder Encoder) GetSafeRandomRegister(size int, excludes ...string) (string, error) {
regs := []REG{}
for _, r := range REGS[encoder.architecture] {
safe := true
for _, x := range excludes {
if r.Extended != x && r.Full != x && r.High != x && r.Low != x {
regs = append(regs, r)
if r.Extended == x || r.Full == x || r.High == x || r.Low == x {
safe = false
break
}
}
if safe {
regs = append(regs, r)
}
}
r := regs[rand.Intn(len(regs))]
+72
View File
@@ -0,0 +1,72 @@
//go:build windows
// +build windows
package test
import (
"runtime"
"syscall"
"testing"
"unsafe"
"github.com/EgeBalci/sgn/pkg"
)
var (
kernel32 = syscall.NewLazyDLL("kernel32.dll")
virtualAlloc = kernel32.NewProc("VirtualAlloc")
virtualFree = kernel32.NewProc("VirtualFree")
// NOP NOP NOP RET works on both x86 and x64
dummyPayload = []byte{0x90, 0x90, 0x90, 0xC3}
)
func TestIntegrationExecution(t *testing.T) {
var arch int
if runtime.GOARCH == "386" {
arch = 32
} else if runtime.GOARCH == "amd64" {
arch = 64
} else {
t.Skipf("Unsupported architecture: %s", runtime.GOARCH)
}
t.Logf("[*] Starting SGN execution stress test (%d-bit)...\n", arch)
iterations := 1000
for i := 0; i < iterations; i++ {
enc, err := sgn.NewEncoder(arch)
if err != nil {
t.Fatalf("Failed to init encoder: %v", err)
}
enc.ObfuscationLimit = 50
encoded, err := enc.Encode(dummyPayload)
if err != nil {
t.Fatalf("Failed to encode: %v", err)
}
addr, _, errInfo := virtualAlloc.Call(
0,
uintptr(len(encoded)),
0x1000|0x2000, // MEM_COMMIT | MEM_RESERVE
0x40, // PAGE_EXECUTE_READWRITE
)
if addr == 0 {
t.Fatalf("VirtualAlloc failed: %v", errInfo)
}
ptr := (*[1 << 30]byte)(unsafe.Pointer(addr))
copy(ptr[:], encoded)
// Execute payload
syscall.Syscall(addr, 0, 0, 0, 0)
// Free memory
virtualFree.Call(addr, 0, 0x8000)
}
t.Logf("[+] Stress test complete: %d executions with no crashes!", iterations)
}