Last active
March 2, 2025 11:32
-
-
Save struppigel/7f24f8730f3f41605f7a64cc39835cee to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Uses PowerShell and dnlib to decrypt strings dynamically | |
# requires pythonnet: pip install pythonnet | |
# requires dnlib.dll: adjust path below if necessary, remove ZoneIdentifier if you downloaded it | |
# | |
# This is applicable for samples that have a non-generic static decrypt method with an integer | |
# Small changes to the PS script will make it also work for non-static or generic methods | |
# Caution: This script runs the sample's code! | |
# author: Karsten Hahn @ G DATA CyberDefense | |
import clr | |
import subprocess | |
import os | |
import sys | |
clr.AddReference(r'dnlib.dll') | |
import dnlib | |
from dnlib.DotNet import * | |
from dnlib.DotNet.Emit import OpCodes | |
ps_script = r""" | |
$nums = @(extractedargs) | |
$assembly = [Reflection.Assembly]::LoadFile("filename") | |
$method = $assembly.ManifestModule.ResolveMethod(decrypttoken) | |
$nums | ForEach-Object { | |
try { | |
"$($_):" + $method.Invoke($null, @($_.PSObject.BaseObject)) | |
} catch {} | |
} | Out-File -FilePath tempfile -Encoding UTF8 | |
""" | |
def extract_values_from_method(method): | |
if not method.HasBody: return [] | |
values = [] | |
instr = [x for x in method.Body.Instructions] | |
while len(instr) >= 2: | |
ldc_i4 = instr[0] | |
call_instr = instr[1] | |
if ldc_i4.OpCode.Code == OpCodes.Ldc_I4.Code and call_instr.OpCode.Code == OpCodes.Call.Code: | |
i4_val = ldc_i4.GetLdcI4Value() | |
values.append(i4_val) | |
instr = instr[1:] | |
return values | |
def extract_values_from_module(module): | |
values =[] | |
for t in module.GetTypes(): | |
for m in t.Methods: | |
values.extend(extract_values_from_method(m)) | |
return values | |
def decrypt_strings_from_method(method, strings_dict): | |
if not method.HasBody: return [] | |
instr = [x for x in method.Body.Instructions] | |
while len(instr) >= 2: | |
ldc_i4 = instr[0] | |
call_instr = instr[1] | |
if ldc_i4.OpCode.Code == OpCodes.Ldc_I4.Code and call_instr.OpCode.Code == OpCodes.Call.Code: | |
i4_val = ldc_i4.GetLdcI4Value() | |
if i4_val in strings_dict: | |
dec_str = strings_dict.get(i4_val) | |
call_instr.OpCode = OpCodes.Ldstr | |
call_instr.Operand = dec_str | |
ldc_i4.OpCode = OpCodes.Nop | |
print('decoded', i4_val, ':', dec_str) | |
instr = instr[1:] | |
def decrypt_strings_from_module(module, strings_dict): | |
values =[] | |
for t in module.GetTypes(): | |
for m in t.Methods: | |
decrypt_strings_from_method(m, strings_dict) | |
def dynamic_string_decrypt(values, token, filename): | |
tempfile = "result.tmp" | |
global ps_script | |
ps_script = ps_script.replace('decrypttoken',token) | |
ps_script = ps_script.replace('extractedargs', str(values)[1:-1]) | |
ps_script = ps_script.replace('filename', filename) | |
ps_script = ps_script.replace('tempfile', tempfile) | |
print("executing decrypt method...") | |
# Run the PowerShell script and capture its output. | |
result = subprocess.run( | |
["powershell.exe", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", ps_script], | |
capture_output=True, | |
text=True, | |
check=True | |
) | |
with open(tempfile, "rb") as f: | |
# utf-8-sig removes the BOM | |
output = f.read().decode(encoding="utf-8-sig").rstrip() | |
output_dict = {} | |
for line in output.splitlines(): | |
key, sep, value = line.partition(':') | |
key = int(key) | |
if len(value.strip()) > 0: | |
output_dict[key] = value.strip() | |
os.remove(tempfile) | |
return output_dict | |
def main(afile, decrypt_token): | |
module = ModuleDefMD.Load(afile) | |
values = extract_values_from_module(module) | |
print("extracted decrypt indices", len(values)) | |
strings_dict = dynamic_string_decrypt(values, decrypt_token, afile) | |
decrypt_strings_from_module(module, strings_dict) | |
print("decrypted strings", len(strings_dict)) | |
outfile = afile + '.deobfus' | |
module.Write(outfile) | |
print() | |
print('Done decrypting, output written to', outfile) | |
if __name__ == '__main__': | |
if len(sys.argv) < 3: | |
print('usage: python string_decrypter.py <inputfile> <decrypt_method_token>') | |
else: | |
afile = sys.argv[1] | |
token = sys.argv[2] | |
main(afile, token) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment