0
0
mirror of https://github.com/sp-tarkov/assembly-tool.git synced 2025-02-13 09:30:46 -05:00
Archangel 77a24319b1 Update de4dot to use dnlib 4.4.0 and x64
Adds the source code used for this modification, this de4dot source code has been cleaned of any things not needed for deobfuscating the tarkov assembly

Co-authored-by: 静穏靄 <170472707+seionmoya@users.noreply.github.com>
2024-12-30 16:01:39 +01:00

497 lines
16 KiB
C#

/*
Copyright (C) 2011-2015 de4dot@gmail.com
This file is part of de4dot.
de4dot is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
de4dot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
*/
using System.Collections.Generic;
using dnlib.DotNet;
using dnlib.DotNet.Emit;
using de4dot.blocks;
namespace de4dot.code.deobfuscators {
public abstract class ProxyCallFixerBase {
protected ModuleDefMD module;
protected List<MethodDef> delegateCreatorMethods = new List<MethodDef>();
protected Dictionary<TypeDef, bool> delegateTypesDict = new Dictionary<TypeDef, bool>();
protected int errors = 0;
public int Errors => errors;
protected class DelegateInfo {
public IMethod methodRef; // Method we should call
public FieldDef field; // Field holding the Delegate instance
public OpCode callOpcode;
public DelegateInfo(FieldDef field, IMethod methodRef, OpCode callOpcode) {
this.field = field;
this.methodRef = methodRef;
this.callOpcode = callOpcode;
}
}
public int RemovedDelegateCreatorCalls { get; set; }
public IEnumerable<TypeDef> DelegateTypes => delegateTypesDict.Keys;
public IEnumerable<TypeDef> DelegateCreatorTypes {
get {
foreach (var method in delegateCreatorMethods)
yield return method.DeclaringType;
}
}
public virtual IEnumerable<Tuple<MethodDef, string>> OtherMethods => new List<Tuple<MethodDef, string>>();
public bool Detected => delegateCreatorMethods.Count != 0;
protected ProxyCallFixerBase(ModuleDefMD module) => this.module = module;
protected ProxyCallFixerBase(ModuleDefMD module, ProxyCallFixerBase oldOne) {
this.module = module;
foreach (var method in oldOne.delegateCreatorMethods)
delegateCreatorMethods.Add(Lookup(method, "Could not find delegate creator method"));
foreach (var kv in oldOne.delegateTypesDict)
delegateTypesDict[Lookup(kv.Key, "Could not find delegate type")] = kv.Value;
}
protected DelegateInfo Copy(DelegateInfo di) {
var method = Lookup(di.methodRef, "Could not find method ref");
var field = Lookup(di.field, "Could not find delegate field");
return new DelegateInfo(field, method, di.callOpcode);
}
protected T Lookup<T>(T def, string errorMessage) where T : class, ICodedToken =>
DeobUtils.Lookup(module, def, errorMessage);
protected void SetDelegateCreatorMethod(MethodDef delegateCreatorMethod) {
if (delegateCreatorMethod == null)
return;
delegateCreatorMethods.Add(delegateCreatorMethod);
}
protected bool IsDelegateCreatorMethod(MethodDef method) {
foreach (var m in delegateCreatorMethods) {
if (m == method)
return true;
}
return false;
}
protected virtual IEnumerable<TypeDef> GetDelegateTypes() {
foreach (var type in module.Types) {
if (type.BaseType == null || type.BaseType.FullName != "System.MulticastDelegate")
continue;
yield return type;
}
}
protected class BlockInstr {
public Block Block { get; set; }
public int Index { get; set; }
}
protected class RemoveInfo {
public int Index { get; set; }
public DelegateInfo DelegateInfo { get; set; }
public bool IsCall => DelegateInfo != null;
}
protected virtual bool ProxyCallIsObfuscated => false;
public void Deobfuscate(Blocks blocks) {
if (blocks.Method.DeclaringType != null && delegateTypesDict.ContainsKey(blocks.Method.DeclaringType))
return;
var allBlocks = blocks.MethodBlocks.GetAllBlocks();
int loops = ProxyCallIsObfuscated ? 50 : 1;
for (int i = 0; i < loops; i++) {
if (!Deobfuscate(blocks, allBlocks))
break;
}
DeobfuscateEnd(blocks, allBlocks);
}
protected abstract bool Deobfuscate(Blocks blocks, IList<Block> allBlocks);
protected virtual void DeobfuscateEnd(Blocks blocks, IList<Block> allBlocks) {
}
protected static void Add(Dictionary<Block, List<RemoveInfo>> removeInfos, Block block, int index, DelegateInfo di) {
if (!removeInfos.TryGetValue(block, out var list))
removeInfos[block] = list = new List<RemoveInfo>();
list.Add(new RemoveInfo {
Index = index,
DelegateInfo = di,
});
}
protected bool FixProxyCalls(MethodDef method, Dictionary<Block, List<RemoveInfo>> removeInfos) {
var gpContext = GenericParamContext.Create(method);
foreach (var block in removeInfos.Keys) {
var list = removeInfos[block];
var removeIndexes = new List<int>(list.Count);
foreach (var info in list) {
if (info.IsCall) {
var opcode = info.DelegateInfo.callOpcode;
var newInstr = Instruction.Create(opcode, ReResolve(info.DelegateInfo.methodRef, gpContext));
block.Replace(info.Index, 1, newInstr);
}
else
removeIndexes.Add(info.Index);
}
if (removeIndexes.Count > 0)
block.Remove(removeIndexes);
}
return removeInfos.Count > 0;
}
IMethod ReResolve(IMethod method, GenericParamContext gpContext) {
if (method.IsMethodSpec || method.IsMemberRef)
method = module.ResolveToken(method.MDToken.Raw, gpContext) as IMethod ?? method;
return method;
}
}
// Fixes proxy calls that call the delegate inline in the code, eg.:
// ldsfld delegate_instance
// ...push args...
// call Invoke
public abstract class ProxyCallFixer1 : ProxyCallFixerBase {
FieldDefAndDeclaringTypeDict<DelegateInfo> fieldToDelegateInfo = new FieldDefAndDeclaringTypeDict<DelegateInfo>();
protected ProxyCallFixer1(ModuleDefMD module)
: base(module) {
}
protected ProxyCallFixer1(ModuleDefMD module, ProxyCallFixer1 oldOne)
: base(module, oldOne) {
foreach (var key in oldOne.fieldToDelegateInfo.GetKeys())
fieldToDelegateInfo.Add(Lookup(key, "Could not find field"), Copy(oldOne.fieldToDelegateInfo.Find(key)));
}
protected void AddDelegateInfo(DelegateInfo di) => fieldToDelegateInfo.Add(di.field, di);
protected DelegateInfo GetDelegateInfo(IField field) {
if (field == null)
return null;
return fieldToDelegateInfo.Find(field);
}
public void Find() {
if (delegateCreatorMethods.Count == 0)
return;
Logger.v("Finding all proxy delegates");
foreach (var tmp in GetDelegateTypes()) {
var type = tmp;
var cctor = type.FindStaticConstructor();
if (cctor == null || !cctor.HasBody)
continue;
if (!type.HasFields)
continue;
object context = CheckCctor(ref type, cctor);
if (context == null)
continue;
Logger.v("Found proxy delegate: {0} ({1:X8})", Utils.RemoveNewlines(type), type.MDToken.ToUInt32());
RemovedDelegateCreatorCalls++;
Logger.Instance.Indent();
foreach (var field in type.Fields) {
if (!field.IsStatic)
continue;
GetCallInfo(context, field, out var calledMethod, out var callOpcode);
if (calledMethod == null)
continue;
AddDelegateInfo(new DelegateInfo(field, calledMethod, callOpcode));
Logger.v("Field: {0}, Opcode: {1}, Method: {2} ({3:X8})",
Utils.RemoveNewlines(field.Name),
callOpcode,
Utils.RemoveNewlines(calledMethod),
calledMethod.MDToken.Raw);
}
Logger.Instance.DeIndent();
delegateTypesDict[type] = true;
}
}
protected abstract object CheckCctor(ref TypeDef type, MethodDef cctor);
protected abstract void GetCallInfo(object context, FieldDef field, out IMethod calledMethod, out OpCode callOpcode);
protected override bool Deobfuscate(Blocks blocks, IList<Block> allBlocks) {
var removeInfos = new Dictionary<Block, List<RemoveInfo>>();
foreach (var block in allBlocks) {
var instrs = block.Instructions;
for (int i = 0; i < instrs.Count; i++) {
var instr = instrs[i];
if (instr.OpCode != OpCodes.Ldsfld)
continue;
var di = GetDelegateInfo(instr.Operand as IField);
if (di == null)
continue;
var callInfo = FindProxyCall(di, block, i);
if (callInfo != null) {
Add(removeInfos, block, i, null);
Add(removeInfos, callInfo.Block, callInfo.Index, di);
}
else {
errors++;
Logger.w("Could not fix proxy call. Method: {0} ({1:X8}), Proxy type: {2} ({3:X8})",
Utils.RemoveNewlines(blocks.Method),
blocks.Method.MDToken.ToInt32(),
Utils.RemoveNewlines(di.field.DeclaringType),
di.field.DeclaringType.MDToken.ToInt32());
}
}
}
return FixProxyCalls(blocks.Method, removeInfos);
}
protected virtual BlockInstr FindProxyCall(DelegateInfo di, Block block, int index) =>
FindProxyCall(di, block, index, new Dictionary<Block, bool>(), 1);
BlockInstr FindProxyCall(DelegateInfo di, Block block, int index, Dictionary<Block, bool> visited, int stack) {
if (visited.ContainsKey(block))
return null;
if (index <= 0)
visited[block] = true;
var instrs = block.Instructions;
for (int i = index + 1; i < instrs.Count; i++) {
if (stack <= 0)
return null;
var instr = instrs[i];
instr.Instruction.UpdateStack(ref stack, false);
if (stack < 0)
return null;
if (instr.OpCode != OpCodes.Call && instr.OpCode != OpCodes.Callvirt) {
if (stack <= 0)
return null;
continue;
}
var calledMethod = instr.Operand as IMethod;
if (calledMethod == null)
return null;
if (stack != (DotNetUtils.HasReturnValue(calledMethod) ? 1 : 0))
continue;
if (calledMethod.Name != "Invoke")
return null;
return new BlockInstr {
Block = block,
Index = i,
};
}
if (stack <= 0)
return null;
foreach (var target in block.GetTargets()) {
var info = FindProxyCall(di, target, -1, visited, stack);
if (info != null)
return info;
}
return null;
}
protected override void DeobfuscateEnd(Blocks blocks, IList<Block> allBlocks) =>
FixBrokenCalls(blocks.Method, allBlocks);
// The obfuscator could be buggy and call a proxy delegate without pushing the
// instance field. SA has done it, so let's fix it.
void FixBrokenCalls(MethodDef obfuscatedMethod, IList<Block> allBlocks) {
foreach (var block in allBlocks) {
var instrs = block.Instructions;
for (int i = 0; i < instrs.Count; i++) {
var call = instrs[i];
if (call.OpCode != OpCodes.Call && call.OpCode != OpCodes.Callvirt)
continue;
var methodRef = call.Operand as IMethod;
if (methodRef == null || methodRef.Name != "Invoke")
continue;
var method = DotNetUtils.GetMethod2(module, methodRef);
if (method == null || method.DeclaringType == null)
continue;
if (!delegateTypesDict.ContainsKey(method.DeclaringType))
continue;
// Oooops!!! The obfuscator is buggy. Well, let's hope it is, or it's my code. ;)
Logger.w("Holy obfuscator bugs, Batman! Found a proxy delegate call with no instance push in {0:X8}. Replacing it with a throw...", obfuscatedMethod.MDToken.ToInt32());
block.Insert(i, OpCodes.Ldnull.ToInstruction());
block.Replace(i + 1, 1, OpCodes.Throw.ToInstruction());
i++;
}
}
}
}
// Fixes proxy calls that call a static method which then calls
// Invoke() on a delegate instance, eg.:
// ...push args...
// call static method
public abstract class ProxyCallFixer2 : ProxyCallFixerBase {
MethodDefAndDeclaringTypeDict<DelegateInfo> proxyMethodToDelegateInfo = new MethodDefAndDeclaringTypeDict<DelegateInfo>();
protected ProxyCallFixer2(ModuleDefMD module)
: base(module) {
}
protected ProxyCallFixer2(ModuleDefMD module, ProxyCallFixer2 oldOne)
: base(module, oldOne) {
foreach (var oldMethod in oldOne.proxyMethodToDelegateInfo.GetKeys()) {
var oldDi = oldOne.proxyMethodToDelegateInfo.Find(oldMethod);
var method = Lookup(oldMethod, "Could not find proxy method");
proxyMethodToDelegateInfo.Add(method, Copy(oldDi));
}
}
public void Find() {
if (delegateCreatorMethods.Count == 0)
return;
Logger.v("Finding all proxy delegates");
Find2();
}
protected void Find2() {
foreach (var type in GetDelegateTypes()) {
var cctor = type.FindStaticConstructor();
if (cctor == null || !cctor.HasBody)
continue;
if (!type.HasFields)
continue;
object context = CheckCctor(type, cctor);
if (context == null)
continue;
Logger.v("Found proxy delegate: {0} ({1:X8})", Utils.RemoveNewlines(type), type.MDToken.ToUInt32());
RemovedDelegateCreatorCalls++;
var fieldToMethod = GetFieldToMethodDictionary(type);
Logger.Instance.Indent();
foreach (var field in type.Fields) {
if (!fieldToMethod.TryGetValue(field, out var proxyMethod))
continue;
GetCallInfo(context, field, out var calledMethod, out var callOpcode);
if (calledMethod == null)
continue;
Add(proxyMethod, new DelegateInfo(field, calledMethod, callOpcode));
Logger.v("Field: {0}, Opcode: {1}, Method: {2} ({3:X8})",
Utils.RemoveNewlines(field.Name),
callOpcode,
Utils.RemoveNewlines(calledMethod),
calledMethod.MDToken.ToUInt32());
}
Logger.Instance.DeIndent();
delegateTypesDict[type] = true;
}
}
protected void Add(MethodDef method, DelegateInfo di) => proxyMethodToDelegateInfo.Add(method, di);
protected abstract object CheckCctor(TypeDef type, MethodDef cctor);
protected abstract void GetCallInfo(object context, FieldDef field, out IMethod calledMethod, out OpCode callOpcode);
Dictionary<FieldDef, MethodDef> GetFieldToMethodDictionary(TypeDef type) {
var dict = new Dictionary<FieldDef, MethodDef>();
foreach (var method in type.Methods) {
if (!method.IsStatic || !method.HasBody || method.Name == ".cctor")
continue;
var instructions = method.Body.Instructions;
for (int i = 0; i < instructions.Count; i++) {
var instr = instructions[i];
if (instr.OpCode.Code != Code.Ldsfld)
continue;
var field = instr.Operand as FieldDef;
if (field == null)
continue;
dict[field] = method;
break;
}
}
return dict;
}
protected override bool Deobfuscate(Blocks blocks, IList<Block> allBlocks) {
var removeInfos = new Dictionary<Block, List<RemoveInfo>>();
foreach (var block in allBlocks) {
var instrs = block.Instructions;
for (int i = 0; i < instrs.Count; i++) {
var instr = instrs[i];
if (instr.OpCode != OpCodes.Call)
continue;
var method = instr.Operand as IMethod;
if (method == null)
continue;
var di = proxyMethodToDelegateInfo.Find(method);
if (di == null)
continue;
Add(removeInfos, block, i, di);
}
}
return FixProxyCalls(blocks.Method, removeInfos);
}
}
// Fixes proxy calls that call a static method with the instance of
// a delegate as the last arg, which then calls the Invoke method.
// ...push args...
// ldsfld delegate instance
// call static method
public abstract class ProxyCallFixer3 : ProxyCallFixer1 {
protected ProxyCallFixer3(ModuleDefMD module)
: base(module) {
}
protected ProxyCallFixer3(ModuleDefMD module, ProxyCallFixer3 oldOne)
: base(module, oldOne) {
}
protected override BlockInstr FindProxyCall(DelegateInfo di, Block block, int index) {
index++;
if (index >= block.Instructions.Count)
return null;
var calledMethod = GetCalledMethod(block.Instructions[index]);
if (calledMethod == null)
return null;
return new BlockInstr {
Block = block,
Index = index,
};
}
static IMethod GetCalledMethod(Instr instr) {
if (instr.OpCode.Code != Code.Call)
return null;
return instr.Operand as IMethod;
}
}
}