Skip to content

Commit

Permalink
Major update on obfuscation engine
Browse files Browse the repository at this point in the history
  • Loading branch information
EgeBalci committed May 30, 2020
1 parent 3238dcf commit 7897e4b
Show file tree
Hide file tree
Showing 6 changed files with 258 additions and 68 deletions.
4 changes: 2 additions & 2 deletions lib/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ data:

// NewDecoderAssembly creates a unobfuscated decoder stub to the given encoded payload
// with the given architecture and seed value
func (encoder Encoder) NewDecoderAssembly(payload []byte) string {
func (encoder *Encoder) NewDecoderAssembly(payload []byte) string {

decoder := STUB[encoder.architecture]
reg := encoder.SafeRandomRegister(encoder.architecture/8, "ECX")
Expand All @@ -56,7 +56,7 @@ func (encoder Encoder) NewDecoderAssembly(payload []byte) string {

// AddSchemaDecoder creates decoder stub for binaries that are ciphered with SchemaCipher function.
// The schema array that is used on the given payload, architecture of the payload and obfuscation level is required.
func (encoder Encoder) AddSchemaDecoder(payload []byte, schema SCHEMA) ([]byte, error) {
func (encoder *Encoder) AddSchemaDecoder(payload []byte, schema SCHEMA) ([]byte, error) {

index := 0

Expand Down
12 changes: 6 additions & 6 deletions lib/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ type Encoder struct {
}

// NewEncoder for creating new encoder structures
func NewEncoder() Encoder {
func NewEncoder() *Encoder {
// Create with default settings
var encoder Encoder
// need to check architecture
Expand All @@ -42,7 +42,7 @@ func NewEncoder() Encoder {
encoder.Seed = RandomByte()
encoder.EncodingCount = 1
encoder.SaveRegisters = false
return encoder
return &encoder
}

// SetArchitecture sets the encoder architecture
Expand All @@ -59,13 +59,13 @@ func (encoder *Encoder) SetArchitecture(arch int) error {
}

// GetArchitecture returns the encoder architecture
func (encoder Encoder) GetArchitecture() int {
func (encoder *Encoder) GetArchitecture() int {
return encoder.architecture
}

// Encode function is the primary encode method for SGN
// all nessary options and parameters are contained inside the encodder struct
func (encoder Encoder) Encode(payload []byte) ([]byte, error) {
func (encoder *Encoder) Encode(payload []byte) ([]byte, error) {

if encoder.SaveRegisters {
payload = append(payload, SafeRegisterSuffix[encoder.architecture]...)
Expand Down Expand Up @@ -139,7 +139,7 @@ func CipherADFL(data []byte, seed byte) []byte {
// Encoding done without using any loop conditions based on the schema values.
// Function performs logical/arithmetic operations given in the schema array.
// If invalid operand supplied function returns nil
func (encoder Encoder) SchemaCipher(data []byte, index int, schema SCHEMA) []byte {
func (encoder *Encoder) SchemaCipher(data []byte, index int, schema SCHEMA) []byte {

for _, cursor := range schema {

Expand Down Expand Up @@ -189,7 +189,7 @@ func CoinFlip() bool {
// NewCipherSchema generates random schema for
// using int the SchemaCipher function.
// Generated schema contains random operands and keys.
func (encoder Encoder) NewCipherSchema(num int) SCHEMA {
func (encoder *Encoder) NewCipherSchema(num int) SCHEMA {
schema := make(SCHEMA, num)

for i, cursor := range schema {
Expand Down
89 changes: 84 additions & 5 deletions lib/instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ var ConditionalJumpMnemonics = []string{
"JZ",
}

// GarbageMnemonics array containing instructions
// that does not affect the overall execution of the program
// GarbageMnemonics array containing safe garbage instructions
// that does not munipulate registers or stack (do not affect the overall execution of the program)
// !!! These instructions must not clobber registers or stack flags may be affected !!!
var GarbageMnemonics = []string{
";", // no instruction (empty)
Expand Down Expand Up @@ -84,20 +84,99 @@ var GarbageMnemonics = []string{
"CMOVNBE {R},{R}",
"CMOVNLE {R},{R}",
"CMOVNGE {R},{R}",
// Recursion starts here...
"{F}", // Garbage function
"{UG}", // Unsafe garbage instructions
"JMP {L};{G};{L}:",
"NOT {R};{G};NOT {R}",
"NEG {R};{G};NEG {R}",
"INC {R};{G};DEC {R}",
"DEC {R};{G};INC {R}",
"PUSH {R};{G};POP {R}",
"BSWAP {R};{G};BSWAP {R}",
"ADD {R},{K};{G};SUB {R},{K}",
"SUB {R},{K};{G};ADD {R},{K}",
"ROR {R},{K};{G};ROL {R},{K}",
"ROL {R},{K};{G};ROR {R},{K}"}
//"PUSH EBP;MOV EBP,ESP;{G};MOV ESP,EBP;POP EBP"} // function prologue/apilogue, doesn't compile if arch == 64
"ROL {R},{K};{G};ROR {R},{K}",
}

// JMP 2 -> Jumps to next instruction
// "LEA",
// "NOT",
// "NEG",
// "INC",
// "DEC"

// UnsafeRmMnemonics array contains operands that manipulate the given destination register with a given register or memory location operator(r/m/32/64)
var UnsafeRmMnemonics = []string{
"OR",
"ADD",
"SUB",
"XOR",
"AND",
"MOV",
"SBB",
"BSR",
"BSF",
"ADCX",
"ADOX",
"BLSR",
"TEST",
"CMOVA",
"CMOVB",
"CMOVC",
"CMOVE",
"CMOVG",
"CMOVL",
"CMOVO",
"CMOVP",
"CMOVS",
"CMOVZ",
"BLSMSK",
"CMOVAE",
"CMOVGE",
"CMOVLE",
"CMOVNA",
"CMOVNB",
"CMOVNC",
"CMOVNE",
"CMOVNG",
"CMOVNL",
"CMOVNO",
"CMOVNP",
"CMOVNS",
"CMOVNZ",
"CMOVPE",
"CMOVPO",
"CMOVBE",
"CMOVNAE",
"CMOVNBE",
"CMOVNLE",
"CMOVNGE",
}

// UnsafeImmMnemonics array contains operands that manipulate the given destination register with a given immidiate value operator
var UnsafeImmMnemonics = []string{
"OR",
"ADC",
"ADD",
"AND",
"BT",
"BTC",
"BTR",
"BTS",
"IMUL",
"RCL",
"RCR",
"ROL",
"ROR",
"SBB",
"MOV",
"SUB",
"XOR",
"IMUL",
}

// JMP 2 -> Jumps to next instruction
func addGarbageJumpMnemonics() {
for _, i := range ConditionalJumpMnemonics {
GarbageMnemonics = append(GarbageMnemonics, i+" 2")
Expand Down
151 changes: 110 additions & 41 deletions lib/obfuscate.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,27 @@ import (
"strings"
)

// SGN ASM Label Definitions;
//-----------------------------
// {R} = RANDOM GENERAL PURPOSE REGISTER
// {K} = RANDOM BYTE OF DATA
// {L} = RANDOM ASM LABEL
// {G} = RANDOM GARBAGE ASSEMBLY
// {F} = RANDOM GARBAGE FUNCTION
// {ST} = RANDOM STACK ADDRESS
// {UG} = RANDOM UNSAFE GARBAGE ASSEMBLY

// GenerateGarbageAssembly generates random garbage instruction(s) assemblies
// with the subject encoder architecture
func (encoder Encoder) GenerateGarbageAssembly() string {
// based on the subject encoder architecture
func (encoder *Encoder) GenerateGarbageAssembly() string {

//registerSize := ((rand.Intn(encoder.Architecture/32) + 1) << 2)
randomGarbageAssembly := GarbageMnemonics[rand.Intn(len(GarbageMnemonics))]

/* !!! ORDER IS IMPORTANT !!! */

if strings.Contains(randomGarbageAssembly, "{R}") {
register := encoder.RandomRegister(encoder.architecture / 8)

randomGarbageAssembly = strings.ReplaceAll(randomGarbageAssembly, "{R}", register)
}

Expand All @@ -28,16 +39,24 @@ func (encoder Encoder) GenerateGarbageAssembly() string {
randomGarbageAssembly = strings.ReplaceAll(randomGarbageAssembly, "{L}", RandomLabel())
}

if strings.Contains(randomGarbageAssembly, "{F}") {
randomGarbageAssembly = strings.ReplaceAll(randomGarbageAssembly, "{F}", encoder.GenerateGarbageFunctionAssembly())
}

if strings.Contains(randomGarbageAssembly, "{G}") {
innerRandomGarbageAssembly := encoder.GenerateGarbageAssembly()
randomGarbageAssembly = strings.ReplaceAll(randomGarbageAssembly, "{G}", innerRandomGarbageAssembly)
randomGarbageAssembly = strings.ReplaceAll(randomGarbageAssembly, "{G}", encoder.GenerateGarbageAssembly())
}
return randomGarbageAssembly + ";\n"

if strings.Contains(randomGarbageAssembly, "{UG}") {
randomGarbageAssembly = strings.ReplaceAll(randomGarbageAssembly, "{UG}", encoder.GenerateUnsafeGarbageAssembly())
}

return randomGarbageAssembly + ";"
}

// GenerateGarbageInstructions generates random garbage instruction(s)
// with the specified architecture and returns the assembled bytes
func (encoder Encoder) GenerateGarbageInstructions() ([]byte, error) {
func (encoder *Encoder) GenerateGarbageInstructions() ([]byte, error) {

randomGarbageAssembly := encoder.GenerateGarbageAssembly()
garbage, ok := encoder.Assemble(randomGarbageAssembly)
Expand Down Expand Up @@ -65,15 +84,64 @@ func (encoder Encoder) GenerateGarbageInstructions() ([]byte, error) {
garbage = append(garbage, garbage2...)

if len(garbage) <= encoder.ObfuscationLimit {
encoder.ObfuscationLimit -= len(garbage)
return garbage, nil
}

return encoder.GenerateGarbageInstructions()
}

// CalculateAverageGarbageInstructionSize generates a JMP instruction over random bytes
func (encoder Encoder) CalculateAverageGarbageInstructionSize() (float64, error) {
// GenerateUnsafeGarbageAssembly generates random unsafe garbage instruction(s) assemblies
// based on the subject encoder architecture
func (encoder *Encoder) GenerateUnsafeGarbageAssembly() string {

destRegister := encoder.RandomRegister(encoder.architecture / 8)
unsafeGarbageAssembly := ""

unsafeGarbageAssembly += fmt.Sprintf("PUSH %s;", destRegister)
// After saving the target register to stack we can munipulate the register unlimited times
if CoinFlip() {
unsafeGarbageAssembly += encoder.GenerateGarbageAssembly()
}

// Add first unsafe garbage
switch rand.Intn(3) {
case 0:
unsafeGarbageAssembly += fmt.Sprintf("%s %s,%s;", UnsafeRmMnemonics[rand.Intn(len(UnsafeRmMnemonics))], destRegister, encoder.RandomRegister(encoder.architecture/8))
case 1:
unsafeGarbageAssembly += fmt.Sprintf("%s %s,%s;", UnsafeRmMnemonics[rand.Intn(len(UnsafeRmMnemonics))], destRegister, encoder.GetRandomStackAddress())
case 2:
unsafeGarbageAssembly += fmt.Sprintf("%s %s,0x%x;", UnsafeImmMnemonics[rand.Intn(len(UnsafeImmMnemonics))], destRegister, RandomByte()%127)
}

// Keep adding unsafe garbage by chance
for {
if CoinFlip() {
unsafeGarbageAssembly += encoder.GenerateGarbageAssembly()
}

if CoinFlip() {
switch rand.Intn(3) {
case 0:
unsafeGarbageAssembly += fmt.Sprintf("%s %s,%s;", UnsafeRmMnemonics[rand.Intn(len(UnsafeRmMnemonics))], destRegister, encoder.RandomRegister(encoder.architecture/8))
case 1:
unsafeGarbageAssembly += fmt.Sprintf("%s %s,%s;", UnsafeRmMnemonics[rand.Intn(len(UnsafeRmMnemonics))], destRegister, encoder.GetRandomStackAddress())
case 2:
unsafeGarbageAssembly += fmt.Sprintf("%s %s,0x%x;", UnsafeImmMnemonics[rand.Intn(len(UnsafeImmMnemonics))], destRegister, RandomByte()%127)
}
} else {
break
}
}

if CoinFlip() {
unsafeGarbageAssembly += encoder.GenerateGarbageAssembly()
}
unsafeGarbageAssembly += fmt.Sprintf("POP %s;", destRegister)
return unsafeGarbageAssembly + ";"
}

// CalculateAverageGarbageInstructionSize calculate the avarage size of generated random garbage instructions
func (encoder *Encoder) CalculateAverageGarbageInstructionSize() (float64, error) {

var average float64 = 0
for i := 0; i < 1000; i++ {
Expand All @@ -88,37 +156,38 @@ func (encoder Encoder) CalculateAverageGarbageInstructionSize() (float64, error)
return average, nil
}

// // GenerateGarbageFunction generates a meaningless function with garbage instructions inside
// func (encoder Encoder) GenerateGarbageFunction() ([]byte, error) {

// prologue := ""
// prologue += "PUSH EBP;"
// prologue += "MOV EBP,ESP;"
// prologue += fmt.Sprintf("SUB ESP,0x%d", int(RandomByte()))

// prologueBin, ok := encoder.Assemble(prologue)
// if !ok {
// return nil, errors.New("garbage function assembly failed")
// }

// //
// garbage, err := encoder.GenerateGarbageInstructions()
// if err != nil {
// return nil, err
// }

// epilogue := ""
// epilogue += "MOV ESP,EBP;"
// epilogue += "POP EBP;"
// epilogue += "RET;"

// epilogueBin, ok := encoder.Assemble(prologue)
// if !ok {
// return nil, errors.New("random garbage instruction assembly failed")
// }

// return append(append(prologueBin, garbage...), epilogueBin...), nil
// }
// GenerateGarbageFunctionAssembly generates a function frame assembly with garbage instructions inside
func (encoder *Encoder) GenerateGarbageFunctionAssembly() string {

bp := ""
sp := ""

switch encoder.architecture {
case 32:
bp = "EBP"
sp = "ESP"
case 64:
bp = "RBP"
sp = "RSP"
default:
panic(errors.New("invalid architecture selected"))
}

prologue := ""
prologue += fmt.Sprintf("PUSH %s;", bp)
prologue += fmt.Sprintf("MOV %s,%s;", bp, sp)
prologue += fmt.Sprintf("SUB %s,0x%d;", sp, int(RandomByte()))

// Fill the function body with garbage instructions
garbage := encoder.GenerateGarbageAssembly()

epilogue := ""
epilogue += fmt.Sprintf("MOV %s,%s;", sp, bp)
epilogue += fmt.Sprintf("POP %s;", bp)
epilogue += "RET;"

return prologue + garbage + epilogue
}

// GenerateGarbageJump generates a JMP instruction over random bytes
func (encoder Encoder) GenerateGarbageJump() ([]byte, error) {
Expand Down
Loading

0 comments on commit 7897e4b

Please sign in to comment.