Skip to content

Commit

Permalink
Merge branch 'df_cflow'
Browse files Browse the repository at this point in the history
  • Loading branch information
mobile46 committed Feb 16, 2022
2 parents a4b899d + 492f5d4 commit 3624ff8
Show file tree
Hide file tree
Showing 2 changed files with 221 additions and 2 deletions.
45 changes: 43 additions & 2 deletions de4dot.code/deobfuscators/Dotfuscator/Deobfuscator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,39 +21,70 @@ You should have received a copy of the GNU General Public License
using System.Collections.Generic;
using dnlib.DotNet;
using de4dot.blocks;
using de4dot.blocks.cflow;

namespace de4dot.code.deobfuscators.Dotfuscator {
public class DeobfuscatorInfo : DeobfuscatorInfoBase {
public const string THE_NAME = "Dotfuscator";
public const string THE_TYPE = "df";
const string DEFAULT_REGEX = @"!^(?:eval_)?[a-z][a-z0-9]{0,2}$&!^A_[0-9]+$&" + @"^[\u2E80-\u8FFFa-zA-Z_<{$][\u2E80-\u8FFFa-zA-Z_0-9<>{}$.`-]*$";

BoolOption inlineMethods;
BoolOption removeInlinedMethods;

public DeobfuscatorInfo()
: base(DEFAULT_REGEX) {
inlineMethods = new BoolOption(null, MakeArgName("inline"), "Inline short methods", true);
removeInlinedMethods = new BoolOption(null, MakeArgName("remove-inlined"), "Remove inlined methods", true);
}

public override string Name => THE_NAME;
public override string Type => THE_TYPE;

public override IDeobfuscator CreateDeobfuscator() =>
new Deobfuscator(new Deobfuscator.Options {
RenameResourcesInCode = false,
ValidNameRegex = validNameRegex.Get(),
InlineMethods = inlineMethods.Get(),
RemoveInlinedMethods = removeInlinedMethods.Get(),
});

protected override IEnumerable<Option> GetOptionsInternal() =>
new List<Option>() {
inlineMethods,
removeInlinedMethods,
};
}

class Deobfuscator : DeobfuscatorBase {
Options options;

string obfuscatorName = "Dotfuscator";

StringDecrypter stringDecrypter;
bool foundDotfuscatorAttribute = false;
bool startedDeobfuscating = false;

internal class Options : OptionsBase {
public bool InlineMethods { get; set; }
public bool RemoveInlinedMethods { get; set; }
}

public override string Type => DeobfuscatorInfo.THE_TYPE;
public override string TypeLong => DeobfuscatorInfo.THE_NAME;
public override string Name => obfuscatorName;
public Deobfuscator(Options options) : base(options) { }
protected override bool CanInlineMethods => startedDeobfuscating ? options.InlineMethods : true;

public override IEnumerable<IBlocksDeobfuscator> BlocksDeobfuscators {
get {
var list = new List<IBlocksDeobfuscator>();
if (CanInlineMethods)
list.Add(new DfMethodCallInliner());
return list;
}
}

public Deobfuscator(Options options) : base(options) => this.options = options;

protected override int DetectInternal() {
int val = 0;
Expand Down Expand Up @@ -94,16 +125,26 @@ void InitializeVersion(TypeDef attr) {
obfuscatorName = "Dotfuscator " + val.Groups[1].ToString();
}

void RemoveInlinedMethods() {
if (!options.InlineMethods || !options.RemoveInlinedMethods)
return;
RemoveInlinedMethods(DfMethodCallInliner.Find(module, staticStringInliner.Methods));
}

public override void DeobfuscateBegin() {
base.DeobfuscateBegin();
DoCflowClean();
DoStringBuilderClean();
foreach (var info in stringDecrypter.StringDecrypterInfos)
staticStringInliner.Add(info.method, (method, gim, args) => stringDecrypter.Decrypt(method, (string)args[0], (int)args[1]));
DeobfuscatedFile.StringDecryptersAdded();

startedDeobfuscating = true;
}

public override void DeobfuscateEnd() {
RemoveInlinedMethods();

if (CanRemoveStringDecrypterType)
AddMethodsToBeRemoved(stringDecrypter.StringDecrypters, "String decrypter method");

Expand Down
178 changes: 178 additions & 0 deletions de4dot.code/deobfuscators/Dotfuscator/DfMethodCallInliner.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
using System.Collections.Generic;
using de4dot.blocks;
using de4dot.blocks.cflow;
using dnlib.DotNet;
using dnlib.DotNet.Emit;

namespace de4dot.code.deobfuscators.Dotfuscator
{
class DfMethodCallInliner : MethodCallInlinerBase, IBranchHandler {
InstructionEmulator emulator;
BranchEmulator branchEmulator;
int emulateIndex;
IList<Instruction> instructions;

public DfMethodCallInliner() {
emulator = new InstructionEmulator();
branchEmulator = new BranchEmulator(emulator, this);
}

public static List<MethodDef> Find(ModuleDefMD module, IEnumerable<MethodDef> notInlinedMethods) {
var notInlinedMethodsDict = new Dictionary<MethodDef, bool>();
foreach (var method in notInlinedMethods)
notInlinedMethodsDict[method] = true;

var inlinedMethods = new List<MethodDef>();

foreach (var type in module.GetTypes()) {
foreach (var method in type.Methods) {
if (!notInlinedMethodsDict.ContainsKey(method) && CanInline(method))
inlinedMethods.Add(method);
}
}

return inlinedMethods;
}

void IBranchHandler.HandleNormal(int stackArgs, bool isTaken) {
if (!isTaken)
emulateIndex++;
else
emulateIndex = instructions.IndexOf((Instruction)instructions[emulateIndex].Operand);
}

bool IBranchHandler.HandleSwitch(Int32Value switchIndex) {
if (!switchIndex.AllBitsValid())
return false;
var instr = instructions[emulateIndex];
var targets = (Instruction[])instr.Operand;
if (switchIndex.Value >= 0 && switchIndex.Value < targets.Length)
emulateIndex = instructions.IndexOf(targets[switchIndex.Value]);
else
emulateIndex++;
return true;
}

protected override bool DeobfuscateInternal() {
bool modified = false;
var instrs = block.Instructions;
for (int i = 0; i < instrs.Count; i++) {
var instr = instrs[i].Instruction;
if (instr.OpCode.Code == Code.Call)
modified |= InlineMethod(instr, i);
}

return modified;
}

static bool CanInline(MethodDef method) {
if (!DotNetUtils.IsMethod(method, "System.Int32", "(System.Int32)"))
return false;
if (!method.IsAssembly)
return false;
if (method.MethodSig.GetGenParamCount() > 0)
return false;

return method.IsStatic;
}

bool CanInline2(MethodDef method) => CanInline(method) && method != blocks.Method;

bool InlineMethod(Instruction callInstr, int instrIndex) {
var methodToInline = callInstr.Operand as MethodDef;
if (methodToInline == null)
return false;

if (!CanInline2(methodToInline))
return false;
var body = methodToInline.Body;
if (body == null)
return false;

if (instrIndex == 0)
return false;

var ldci4 = block.Instructions[instrIndex - 1];
if (!ldci4.IsLdcI4())
return false;
if (!GetNewValue(methodToInline, ldci4.GetLdcI4Value(), out int newValue))
return false;

block.Instructions[instrIndex - 1] = new Instr(OpCodes.Nop.ToInstruction());
block.Instructions[instrIndex] = new Instr(Instruction.CreateLdcI4(newValue));
return true;
}

bool GetNewValue(MethodDef method, int arg, out int newValue) {
newValue = 0;
emulator.Initialize(method);
emulator.SetArg(method.Parameters[0], new Int32Value(arg));

emulateIndex = 0;
instructions = method.Body.Instructions;
int counter = 0;
while (true) {
if (counter++ >= 50)
return false;
if (emulateIndex < 0 || emulateIndex >= instructions.Count)
return false;
var instr = instructions[emulateIndex];
switch (instr.OpCode.Code) {
case Code.Br:
case Code.Br_S:
case Code.Beq:
case Code.Beq_S:
case Code.Bge:
case Code.Bge_S:
case Code.Bge_Un:
case Code.Bge_Un_S:
case Code.Bgt:
case Code.Bgt_S:
case Code.Bgt_Un:
case Code.Bgt_Un_S:
case Code.Ble:
case Code.Ble_S:
case Code.Ble_Un:
case Code.Ble_Un_S:
case Code.Blt:
case Code.Blt_S:
case Code.Blt_Un:
case Code.Blt_Un_S:
case Code.Bne_Un:
case Code.Bne_Un_S:
case Code.Brfalse:
case Code.Brfalse_S:
case Code.Brtrue:
case Code.Brtrue_S:
case Code.Switch:
if (!branchEmulator.Emulate(instr))
return false;
break;

case Code.Ret:
var retValue = emulator.Pop();
if (!retValue.IsInt32())
return false;
var retValue2 = (Int32Value)retValue;
if (!retValue2.AllBitsValid())
return false;
newValue = retValue2.Value;
return true;

default:
emulator.Emulate(instr);
emulateIndex++;
break;
}
}
}

protected override bool IsCompatibleType(int paramIndex, IType origType, IType newType) {
if (new SigComparer(SigComparerOptions.IgnoreModifiers).Equals(origType, newType))
return true;
if (IsValueType(newType) || IsValueType(origType))
return false;
return newType.FullName == "System.Object";
}
}
}

0 comments on commit 3624ff8

Please sign in to comment.