Code Snippets For Dotnet Configuration Extractors
Useful code snippets for developing dotnet configuration extractors.
Useful code snippets for developing dotnet configuration extractors.
This is a series of code snippets that I have found useful when developing configuration extractors for dotnet-based malware.
Here are some examples where I have applied these snippets.
This page would not exist without the work of these folk. Much of this work is based on their blogs and scripts.
Enumerate DotNet File For Call Instruction
This snippet allows you to enumerate a dotnet exe/dll and locate all call instructions. You can replace the OpCodes.Call with any instruction of your choosing.
Here is a list of all available instructions.
import clr
clr.AddReference("dnlib.dll")
import dnlib
from dnlib.DotNet import *
from dnlib.DotNet.Emit import OpCodes
filename = "yourfile.exe"
module = ModuleDefMD.Load(filename)
for types in module.GetTypes():
for method in types.Methods:
if method.HasBody:
for instr in method.Body.Instructions:
if instr.OpCode == OpCodes.Call:
result = "Something interesting" This snippet allows you to hone in on specific functions/methods within a dotnet binary.
This particular snippet locates the first method/function that takes an integer value as an argument, and then returns a string.
Note that this uses the System.Reflection library and not the dnlib library as in other examples.
import clr
clr.AddReference("System")
module = System.Reflection.Assembly.LoadFile(filename)
for types in module.GetTypes():
for method in types.GetMethods():
params = method.GetParameters()
if params[0].ParameterType.FullName == "System.Int32":
if method.ReturnType.FullName == "System.String":
return method
This snippet enumerates a method for a known IL instruction pattern.
def has_config_sequence(method):
target_sequence = [OpCodes.Stsfld,OpCodes.Ldc_I4,OpCodes.Br_S,OpCodes.Call,OpCodes.Stsfld,OpCodes.Ret]
target_len = len(target_sequence)
for i in range(target_len):
target_sequence[i] = target_sequence[i].Name
if method.HasBody:
current_sequence = [instr.OpCode.Name for instr in method.Body.Instructions]
for i in range(1, target_len-1):
if target_sequence[-i] != current_sequence[-i]:
return False
return TrueThis snippet invokes a static method with a single integer argument. This assumes that you have already located a method using something similar to the previous snippet.
import clr
clr.AddReference("System")
python_int = 55
dotnet_int = System.Int32(python_int)
module = <module found with previous snippet>
result = module.Invoke(None, (dotnet_int,))A few notes
Invoke directly on the returned value. static, then the first argument to Invoke can be None. (dotnet_int,) rather than just dotnet_intimport clr
clr.AddReference("System")
python_int = 55
dotnet_int = System.Int32(python_int)
token = 0x06000005
method = module.ManifestModule.ResolveMethod(token)
result = method.Invoke(None, (dotnet_int,))Invokes a static generic method that takes a single integer value as an argument.
Staticimport clr
clr.AddReference("System")
method = <some method you have found using previous snippets>
concrete_method_str = method.MakeGenericMethod(clr.GetClrType(str))
python_int = 55
dotnet_int = System.Int32(python_int)
result = concrete_method_str.Invoke(None, (dotnet_int,))This snippet is a bit messy but possibly useful for someone.
It was used to patch all calls to GetExecutingAssembly and GetCallingAssembly which were followed by a Brfalse or Callvirt instruction.
This was a specific pattern used in a protector (I believe ConfuserEx) to prevent Invoke calls. By patching the calls and surrounding instructions, the decryption methods could be invoked without failing the GetExecutingAssembly == GetCallingAssembly anti-debug check.
import clr
clr.AddReference("dnlib.dll")
import dnlib
from dnlib.DotNet import *
from dnlib.DotNet.Emit import OpCodes
filename = "yourfile.exe"
module = ModuleDefMD.Load(filename)
def nop_executingAssembly(module):
nop_methods = ["GetExecutingAssembly","GetCallingAssembly"]
for types in module.GetTypes():
for method in types.Methods:
if method.HasBody:
for i in range(len(method.Body.Instructions)):
if method.Body.Instructions[i].OpCode == OpCodes.Call:
for nop_method in nop_methods:
if nop_method in str(method.Body.Instructions[i]):
method.Body.Instructions[i].OpCode = OpCodes.Nop
for j in range(1,4):
if method.Body.Instructions[i+j].OpCode == OpCodes.Callvirt:
method.Body.Instructions[i+j].OpCode = OpCodes.Nop
if method.Body.Instructions[i+j].OpCode == OpCodes.Brfalse:
method.Body.Instructions[i+j].OpCode = OpCodes.Nop
return dn_module
Obtain arguments from command line using flags/markers rather than the specific order.
This allows you to use args.file instead of sys.argv[1] and so on.
import argparse
parser = argparse.ArgumentParser(description="Extract config from AgentTesla Payloads")
parser.add_argument('-f', '--file', required=False, help="Path to agentTesla file")
parser.add_argument('-d', '--dnlib', required=False, default="dnlib.dll",help="Path to dnlib file")
parser.add_argument('--allstrings', required=False, default=False, help="print all strings for files, not just c2")
args = parser.parse_args()