Skip to content

Instantly share code, notes, and snippets.

@GarnetSunset
Created January 18, 2019 18:32
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save GarnetSunset/01c71be4a7047d4720f01c7ac2be3551 to your computer and use it in GitHub Desktop.
Save GarnetSunset/01c71be4a7047d4720f01c7ac2be3551 to your computer and use it in GitHub Desktop.
Powershell Deobfuscator in Python, mixing multiple different methods together.
#Created by Max 'Libra' Kersten (@LibraAnalysis)
#Additions from lasq88's research and code.
#QoL changes and combination by GarnetSunset
import re,sys,zlib,base64,argparse
obfuscation_methods = [
('replace','Replace String','replace\s?\('),
('decompress','Compress string','::decompress'),
('split','ASCII table with split','-split'),
('ascii','ASCII table','\((?:\s?[0-9]{2,3}\s?,?){5,}\)')
]
if sys.version_info < (3, 0):
sys.stdout.write("Sorry, requires Python 3.x, not Python 2.x\n")
sys.exit(1)
def FindString(str,regx):
match = re.compile(regx,flags=re.I)
search = re.findall(match,str)
return search
def StrChar(matchobj):
return "'"+chr(int(matchobj.group(1)))+"'"
def FindReplaces(text,replacements):
match = re.compile("replace\s*(\(|\(\(|)\\\\?'([a-zA-Z0-9]{2,4})\\\\?'(\)|),\\\\?'(.{1})\\\\?'(\)|)",flags=re.I)
search = re.findall(match,text)
if (search == []):
return 0
else:
for i in search:
replacements.add((i[1],i[3]))
return replacements
def DeobfReplace(text):
replacements = set()
while True:
text = re.sub("'\+'|''\+|\+''","",text)
text = re.sub('\[char\]([0-9]{2,3})',StrChar,text,flags=re.I)
text = re.sub("'\+'|''\+|\+''","",text)
text = re.sub('\[string\]','',text,flags=re.I)
if(FindReplaces(text, replacements) == 0):
return text
else:
replacements = FindReplaces(text, replacements)
for i in replacements:
text = re.sub("'\+'|''\+|\+''","",text)
text = text.replace(i[0],i[1])
def Detect(text):
for (_, method, regx) in obfuscation_methods:
match = re.compile(regx,flags=re.I)
search = re.findall(match,text)
if (search != []):
return method
if (search == []):
return -1
def Deobfuscate(text,method):
try:
if (method == "Replace String"):
return DeobfReplace(text)
elif (method == "Compress string"):
compressed = FindString(text,"frombase64string\(\s?'(\S*)'")[0]
return zlib.decompress(base64.b64decode(compressed),-zlib.MAX_WBITS)
elif (method == "ASCII table with split"):
split_by = '|'.join(FindString(text,"split\s?'(\S)'"))
ascii_table = re.split(split_by,FindString(text,"\('(\S*)'")[0])
return ''.join(chr(int(i)) for i in ascii_table)
elif (method == "ASCII table"):
ascii_table = re.split(',',FindString(text,'\(((?:\s?[0-9]{2,3}\s?,?){5,})\)')[0])
return ''.join(chr(int(i)) for i in ascii_table)
except:
print("Unfortunately deobfuscation failed. Maybe you provided wrong method or there is no method to deobfuscate this code.")
return -1
#Define information regarding the original script's location
powershellPath = input('Powershell script file with extension: ')
powershellFile = open(powershellPath,'r')
#Read all lines of the original script
powershellContent = powershellFile.readlines()
#The variable which contains all deobfuscated lines
output = ''
#The variable which keeps track of the amount of string formats that have been replaced
formatCount = 0
#The variable which keeps track of the amount of variables that have been replaced
variableCount = 0
#The variable which keeps track of the amount of removed back ticks
backtickCount = 0
#Loop through the file, line by line
for line in powershellContent:
detected = Detect(line)
if (detected != -1):
line = Deobfuscate(line,detected)
else:
backtickCount += line.count("`")
#Replace the back tick with nothing to remove the needless back ticks
line = line.replace("`", "")
#Match the string formatting
matchedLine = re.findall(""""((?:\{\d+\})+)"\s*-[fF]\s*((?:'.*?',?)+)""", line)
#If one or more matches have been found, continue. Otherwise skip the replacement part
if len(matchedLine) > 0:
#Each match in each line is broken down into two parts: the indices part ("{0}{2}{1}") and the strings ("var", "ble", "ia")
for match in matchedLine:
#Convert all indices to integers within a list
indices = list(map(int, re.findall("{(\d+)}", match[0])))
#All strings are saved in an array
strings = re.findall("'([^']+?)'", match[1])
#The result is the correctly formatted string
result = "".join([strings[i] for i in indices])
#The current line is altered based on the found match, with which it is replaced
line = line.replace(match[0], result, 1)
line = line.replace(match[1], "", 1)
#Regex the "-f" and "-F" so that "-f[something]" is not replaced
formatFlag = re.findall("""(-[fF])(?=[^\w])""", line)
if len(formatFlag) > 0:
for formatFlagMatch in formatFlag:
line = line.replace(formatFlagMatch, "")
#Find all strings between quotation marks.
varDeclaration = re.findall("""(?<=\()\"(?=[^\)]+\+[^\)]+\))(?:[^\{\}\-\)])+\"(?=\))""", line)
#The concatenated variable
variable = ''
#For each string in the list, the items are concatenated
if len(varDeclaration) > 0:
for string in varDeclaration:
variable = string.replace("\"", "")
variable = variable.replace("+", "")
variable = variable.replace(" ", "")
variable = "\"" + variable + "\""
variableCount += 1
#Replace the variable with the concatenated one
line = line.replace(varDeclaration[0], variable)
formatCount += 1
#When all matches are done, add the altered line to the output
output += line
urls = FindString(line,'https?://(?:[-\w.]|(?:%[\da-fA-F]{2}))+[/\-\w]+')
print('URLs Found:')
for url in urls:
print(url)
#When all lines are checked, write the output variable to a file
with open("deobfusc_"+powershellPath, 'w') as f:
f.write(output)
print("Amount of removed back ticks:")
print(backtickCount)
print("Amount of formatted strings that have been deobfuscated and concatenated:")
print(formatCount)
print("Amount of variables that have been concatenated:")
print(variableCount)
print("Total amount of modifications:")
print((backtickCount + formatCount + variableCount))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment