Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
197 lines (139 sloc) 4.41 KB
'''
KeyBoy Config file decoder
From: It's Parliamentary: KeyBoy and the targeting of the Tibetan Community
Permalink: https://citizenlab.org/2016/11/parliament-keyboy/
Adam Hulcoop / September 19, 2016
adam.hulcoop[@]citizenlab.ca
'''
import sys
import argparse
import logging
LookupTable = {}
# Function to compute 9-bit sliding window values and return list of converted indices
def byteDecoder(rawBytes):
#rawBytes should be bytearray of values
k=0
ba = []
bytex=0
while( bytex < len(rawBytes) -1):
index = 0
ix = bytex
t=0
while ( index < 9):
while (k<8):
if( ( ord(rawBytes[ix]) & (1 << (7-k))) > 0 ):
t = 2*t +1
else:
t = 2*t
index+=1
k+=1
if( index > 8):
break
if ( k==8 ):
ix+=1
k=0
if (index<=8):
k=0
elif ( (index == 9) and (k==8)):
k=0
bytex+=1
#t should hold the resultant value
ba.append(t)
bytex+=1
return ba
def CreateLookupTable():
for x in xrange(0,256):
LookupTable[x] = "{:x}".format(x)
# 0x101 is used as the exit value
LookupTable[257] = "reserved"
return
def BuildExtraLookupTable(curIndex, prevIndex):
#curIndex and prevIndex are indices from the decoded input file
startFrom = len(LookupTable)
if( curIndex > startFrom) :
# if we are looking up an index beyond the existing table size
vPrev = LookupTable.get(prevIndex)
vCur = LookupTable.get(prevIndex)
else:
vPrev = LookupTable.get(prevIndex)
vCur = LookupTable.get(curIndex)
newVal = vPrev
if( len(vCur.split(',')) > 1 ):
augItem = vCur.split(',')[0]
else:
augItem = vCur
newVal = ','.join([vPrev,augItem])
LookupTable[startFrom+1] = newVal
logging.debug("0x%x => %s" % ((startFrom+1),newVal ))
return
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Decode KeyBoy backdoor configuration files')
parser.add_argument('FILE', help='KeyBoy encoded config file')
parser.add_argument('--verbose','-v',action='store_true',help='Enable verbose output')
parser.add_argument('--skip','-s',type=int,help='Skip over <SKIP> bytes at beginning of file')
args = parser.parse_args()
if args.verbose:
logging.basicConfig(format='%(message)s',level=logging.DEBUG)
else:
logging.basicConfig(format='%(message)s',level=logging.WARNING)
print "============================"
print "{KeyBoy Config file Decoder}"
print "============================"
print ""
with open(args.FILE, "rb") as f:
if args.skip is None:
f.seek(0,0)
else:
logging.warn("Skipping %i bytes.." % args.skip)
f.seek(args.skip,0)
szfile = f.read(8)
szf_dat = int(szfile,16)
logging.debug("Size of decoded config file:\t%i bytes" % szf_dat)
szconf = f.read(8)
szc_dat = int(szconf,16)
logging.debug("Size of encoded config data:\t%i bytes" % szc_dat)
logging.debug("")
bytesRaw = f.read(szc_dat)
ba = byteDecoder(bytesRaw)
#create the initial Lookup table
CreateLookupTable()
ResultsList = []
#Build the extra lookup table values
logging.debug("Dynamic lookup table entries:")
for val in range(len(ba)):
#for each entry in the bytes array,
# we need to look up the value in the table.
# As we go, we need to build new entries on the table
# corresponding to the value from this lookup, concatenated with the first character from the
# the value printed in the previous lookup. BuildExtraLookupTable function does that step.
if( val == 0):
continue
elif (val == 1):
ResultsList.append(LookupTable.get(ba[val]))
prevValue = ba[val]
else:
curValue = ba[val]
if (curValue != 257):
BuildExtraLookupTable(curValue, prevValue)
ResultsList.append(LookupTable.get(ba[val]))
prevValue= curValue
logging.debug("")
logging.debug("Decoded index list:")
logging.debug(['0x%x' % j for j in ba])
logging.debug("")
resultData = ",".join(ResultsList)
hChars = resultData.split(',')
Output = ""
for h in hChars:
Output += chr(int(h,16))
logging.debug("Retrieved config character array:")
logging.debug(['0x%x' % int(m,16) for m in hChars])
logging.debug("")
TokenizedOutput = Output.split('\r\n')
ConfigSections = ['Identity Code:','C2 Host/IP #1:','C2 Host/IP #2:','C2 Host/IP #3:','C2 Port #1:','C2 Port #2:','C2 Port #3:','Password:','Campaign ID:','']
logging.warn("Configuration Data:")
headr = "=" * 30
logging.warn(headr)
for x in range(len(TokenizedOutput)-1):
logging.warn("%s\t%s" % (ConfigSections[x],TokenizedOutput[x]))
logging.warn("")