I wanted to take a look at dnlib trying to understand what are its possibilities. at the same point I had to deal with an ‘Agent Tesla’ sample, which used strings obfuscation with AES CBC 256 bits.
The decryption method was easy to find, as can be seen in the capture below:
A quick win consist to copy paste this method inside a new Visual Studio project, and it will do the job later.
First we have to load the assembly binary.
ModuleDefMD module = ModuleDefMD.Load(executable);
Next we parse the types and all the methods of the binary and search for a String opcode (value 114 in IL) and if the string is forwarded by a Call, that mean that the string is a method parameter.
foreach (TypeDef type in module.GetTypes())
{
countModule++;
foreach (MethodDef method in type.Methods)
{
//check if the method is not empty and if it not a constructor
if (!method.HasBody || method.IsConstructor)
continue;
countMethod++;
for (int i = 0; i < method.Body.Instructions.Count; i++)
{
if (method.Body.Instructions[i].OpCode.Value == 114) //OpCodes.Ldstr)
{
if (method.Body.Instructions[i + 1].OpCode == OpCodes.Call)
{
Now we can retreive the encrypted string, and past it to the DecryptString method. Afterwards, to get a clean output sample, we have to remove the string and the call to the real decryption method inside the loaded assembly
var cryptedstring = method.Body.Instructions[i].Operand.ToString();
string decryptedstring = DecryptString(cryptedstring);
//For exception max stack value
method.Body.KeepOldMaxStack = true;
method.Body.Instructions[i].OpCode = OpCodes.Ldstr;
method.Body.Instructions[i].Operand = decryptedstring;
method.Body.Instructions.Remove(method.Body.Instructions[i + 1]);
And finally we save our new clean assembly
String outputfilename = outputFolderName +" \\" + filename + "_uncrypt.exe";
module.Write(outputfilename);
Now opening the cleaned Assembly in Dnspy, we can see the clean strings:
Of course, if the algorithm changes, we have to fix inside the source code and rebuild the binary, which can be a bit painful. An evolution could be used a kind of reflexion to call the decryption method inside the loaded assembly (with is RVA for example). We will explore this another time.
As you can see, it's really easy to use the basic of DNlib, and we can do even more cool thinks!
The source code of the decrypter can be found on my github, in case it is useful to anyone.
If you see any bugs or have further ideas to make it great again, don't hesitate to message me.