added basic anti tamper protection

This commit is contained in:
yck1509 2014-05-11 18:37:45 +08:00
Родитель d67c52e5b5
Коммит e4a0f790cd
12 изменённых файлов: 651 добавлений и 28 удалений

Просмотреть файл

@ -107,7 +107,12 @@ namespace Confuser.Core
return defValue;
if (!objAnno.Contains(key))
return defValue;
return (TValue)Convert.ChangeType(objAnno[key], typeof(TValue));
var valueType = typeof(TValue);
if (valueType.IsValueType)
return (TValue)Convert.ChangeType(objAnno[key], typeof(TValue));
else
return (TValue)objAnno[key];
}
/// <summary>
@ -133,7 +138,12 @@ namespace Confuser.Core
return defValueFactory(key);
if (!objAnno.Contains(key))
return defValueFactory(key);
return (TValue)Convert.ChangeType(objAnno[key], typeof(TValue));
var valueType = typeof(TValue);
if (valueType.IsValueType)
return (TValue)Convert.ChangeType(objAnno[key], typeof(TValue));
else
return (TValue)objAnno[key];
}
/// <summary>
@ -159,7 +169,13 @@ namespace Confuser.Core
objAnno = annotations[new WeakReferenceKey(obj)] = new ListDictionary();
TValue ret;
if (objAnno.Contains(key))
ret = (TValue)Convert.ChangeType(objAnno[key], typeof(TValue));
{
var valueType = typeof(TValue);
if (valueType.IsValueType)
return (TValue)Convert.ChangeType(objAnno[key], typeof(TValue));
else
return (TValue)objAnno[key];
}
else
objAnno[key] = ret = factory(key);
return ret;

Просмотреть файл

@ -0,0 +1,115 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Confuser.Core;
using Confuser.Protections.AntiTamper;
using dnlib.DotNet;
namespace Confuser.Protections
{
[BeforeProtection("Ki.ControlFlow"), AfterProtection("Ki.Constants")]
class AntiTamperProtection : Protection
{
public const string _Id = "anti tamper";
public const string _FullId = "Ki.AntiTamper";
public const string _ServiceId = "Ki.AntiTamper";
protected override void Initialize(ConfuserContext context)
{
//
}
protected override void PopulatePipeline(ProtectionPipeline pipeline)
{
pipeline.InsertPreStage(PipelineStage.OptimizeMethods, new InjectPhase(this));
pipeline.InsertPreStage(PipelineStage.EndModule, new MDPhase(this));
}
public override string Name
{
get { return "Anti Tamper Protection"; }
}
public override string Description
{
get { return "This protection ensures the integrity of application."; }
}
public override string Id
{
get { return _Id; }
}
public override string FullId
{
get { return _FullId; }
}
public override ProtectionPreset Preset
{
get { return ProtectionPreset.Maximum; }
}
public void ExcludeMethod(ConfuserContext context, MethodDef method)
{
ProtectionParameters.GetParameters(context, method).Remove(this);
}
static readonly object HandlerKey = new object();
enum Mode
{
Normal,
JIT
}
class InjectPhase : ProtectionPhase
{
public InjectPhase(AntiTamperProtection parent)
: base(parent)
{
}
public override ProtectionTargets Targets
{
get { return ProtectionTargets.Modules; }
}
protected override void Execute(ConfuserContext context, ProtectionParameters parameters)
{
Mode mode = parameters.GetParameter<Mode>(context, context.CurrentModule, "mode", Mode.Normal);
IModeHandler modeHandler;
switch (mode)
{
case Mode.Normal:
modeHandler = new NormalMode();
break;
default:
throw new UnreachableException();
}
modeHandler.HandleInject((AntiTamperProtection)Parent, context, parameters);
context.Annotations.Set(context.CurrentModule, HandlerKey, modeHandler);
}
}
class MDPhase : ProtectionPhase
{
public MDPhase(AntiTamperProtection parent)
: base(parent)
{
}
public override ProtectionTargets Targets
{
get { return ProtectionTargets.Methods; }
}
protected override void Execute(ConfuserContext context, ProtectionParameters parameters)
{
var modeHandler = context.Annotations.Get<IModeHandler>(context.CurrentModule, HandlerKey);
modeHandler.HandleMD((AntiTamperProtection)Parent, context, parameters);
}
}
}
}

Просмотреть файл

@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using dnlib.DotNet.Emit;
using dnlib.DotNet;
using Confuser.Core.Helpers;
using Confuser.Core;
using Confuser.DynCipher;
using System.Diagnostics;
using Confuser.DynCipher.AST;
using Confuser.DynCipher.Generation;
using Confuser.Core.Services;
namespace Confuser.Protections.AntiTamper
{
class DynamicDeriver : IKeyDeriver
{
class CodeGen : CILCodeGen
{
Local block;
Local key;
public CodeGen(Local block, Local key, MethodDef method, IList<Instruction> instrs)
: base(method, instrs)
{
this.block = block;
this.key = key;
}
protected override Local Var(Variable var)
{
if (var.Name == "{BUFFER}")
return block;
else if (var.Name == "{KEY}")
return key;
else
return base.Var(var);
}
}
StatementBlock derivation;
Action<uint[], uint[]> encryptFunc;
public void Init(ConfuserContext ctx, RandomGenerator random)
{
StatementBlock dummy;
ctx.Registry.GetService<IDynCipherService>().GenerateCipherPair(random, out derivation, out dummy);
var dmCodeGen = new DMCodeGen(typeof(void), new[] {
Tuple.Create("{BUFFER}", typeof(uint[])),
Tuple.Create("{KEY}", typeof(uint[]))
});
dmCodeGen.GenerateCIL(derivation);
encryptFunc = dmCodeGen.Compile<Action<uint[], uint[]>>();
}
public uint[] DeriveKey(uint[] a, uint[] b)
{
uint[] ret = new uint[0x10];
Buffer.BlockCopy(a, 0, ret, 0, a.Length * sizeof(uint));
encryptFunc(ret, b);
return ret;
}
public IEnumerable<Instruction> EmitDerivation(MethodDef method, ConfuserContext ctx, Local dst, Local src)
{
var ret = new List<Instruction>();
var codeGen = new CodeGen(dst, src, method, ret);
codeGen.GenerateCIL(derivation);
codeGen.Commit(method.Body);
return ret;
}
}
}

Просмотреть файл

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using dnlib.DotNet;
using Confuser.Core;
using dnlib.DotNet.Emit;
using Confuser.Core.Services;
namespace Confuser.Protections.AntiTamper
{
enum Mode
{
Normal,
Dynamic
}
interface IKeyDeriver
{
void Init(ConfuserContext ctx, RandomGenerator random);
uint[] DeriveKey(uint[] a, uint[] b);
IEnumerable<Instruction> EmitDerivation(MethodDef method, ConfuserContext ctx, Local dst, Local src);
}
}

Просмотреть файл

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Confuser.Core;
namespace Confuser.Protections.AntiTamper
{
interface IModeHandler
{
void HandleInject(AntiTamperProtection parent, ConfuserContext context, ProtectionParameters parameters);
void HandleMD(AntiTamperProtection parent, ConfuserContext context, ProtectionParameters parameters);
}
}

Просмотреть файл

@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using dnlib.DotNet;
using Confuser.Core;
using dnlib.DotNet.Emit;
using Confuser.Core.Services;
namespace Confuser.Protections.AntiTamper
{
class NormalDeriver : IKeyDeriver
{
public void Init(ConfuserContext ctx, RandomGenerator random)
{
//
}
public uint[] DeriveKey(uint[] a, uint[] b)
{
uint[] ret = new uint[0x10];
for (int i = 0; i < 0x10; i++)
{
switch (i % 3)
{
case 0:
ret[i] = a[i] ^ b[i];
break;
case 1:
ret[i] = a[i] * b[i];
break;
case 2:
ret[i] = a[i] + b[i];
break;
}
}
return ret;
}
public IEnumerable<Instruction> EmitDerivation(MethodDef method, ConfuserContext ctx, Local dst, Local src)
{
for (int i = 0; i < 0x10; i++)
{
yield return Instruction.Create(OpCodes.Ldloc, dst);
yield return Instruction.Create(OpCodes.Ldc_I4, i);
yield return Instruction.Create(OpCodes.Ldloc, dst);
yield return Instruction.Create(OpCodes.Ldc_I4, i);
yield return Instruction.Create(OpCodes.Ldelem_U4);
yield return Instruction.Create(OpCodes.Ldloc, src);
yield return Instruction.Create(OpCodes.Ldc_I4, i);
yield return Instruction.Create(OpCodes.Ldelem_U4);
switch (i % 3)
{
case 0:
yield return Instruction.Create(OpCodes.Xor);
break;
case 1:
yield return Instruction.Create(OpCodes.Mul);
break;
case 2:
yield return Instruction.Create(OpCodes.Add);
break;
}
yield return Instruction.Create(OpCodes.Stelem_I4);
}
}
}
}

Просмотреть файл

@ -0,0 +1,260 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Confuser.Core;
using Confuser.Core.Services;
using Confuser.Core.Helpers;
using dnlib.DotNet.Emit;
using Confuser.Renamer;
using dnlib.DotNet;
using System.Diagnostics;
using dnlib.DotNet.Writer;
using System.IO;
namespace Confuser.Protections.AntiTamper
{
class NormalMode : IModeHandler
{
RandomGenerator random;
uint z, x, c, v;
uint name1, name2;
IKeyDeriver deriver;
List<MethodDef> methods;
public void HandleInject(AntiTamperProtection parent, ConfuserContext context, ProtectionParameters parameters)
{
random = context.Registry.GetService<IRandomService>().GetRandomGenerator(parent.FullId);
z = random.NextUInt32();
x = random.NextUInt32();
c = random.NextUInt32();
v = random.NextUInt32();
name1 = random.NextUInt32() & 0x7f7f7f7f;
name2 = random.NextUInt32() & 0x7f7f7f7f;
switch (parameters.GetParameter<Mode>(context, context.CurrentModule, "key", Mode.Normal))
{
case Mode.Normal:
deriver = new NormalDeriver();
break;
case Mode.Dynamic:
deriver = new DynamicDeriver();
break;
default:
throw new UnreachableException();
}
deriver.Init(context, random);
var rt = context.Registry.GetService<IRuntimeService>();
var initMethod = rt.GetRuntimeType("Confuser.Runtime.AntiTamperNormal").FindMethod("Initialize");
initMethod = InjectHelper.Inject(initMethod, context.CurrentModule);
context.CurrentModule.GlobalType.Methods.Add(initMethod);
var instrs = initMethod.Body.Instructions.ToList();
for (int i = 0; i < instrs.Count; i++)
{
var instr = instrs[i];
if (instr.OpCode == OpCodes.Ldtoken)
{
instr.Operand = context.CurrentModule.GlobalType;
}
else if (instr.OpCode == OpCodes.Call)
{
IMethod method = (IMethod)instr.Operand;
if (method.DeclaringType.Name == "Mutation" &&
method.Name == "Crypt")
{
Instruction ldDst = instrs[i - 2];
Instruction ldSrc = instrs[i - 1];
Debug.Assert(ldDst.OpCode == OpCodes.Ldloc && ldSrc.OpCode == OpCodes.Ldloc);
instrs.RemoveAt(i);
instrs.RemoveAt(i - 1);
instrs.RemoveAt(i - 2);
instrs.InsertRange(i - 2, deriver.EmitDerivation(initMethod, context, (Local)ldDst.Operand, (Local)ldSrc.Operand));
}
}
}
initMethod.Body.Instructions.Clear();
foreach (var instr in instrs)
initMethod.Body.Instructions.Add(instr);
MutationHelper.InjectKeys(initMethod,
new int[] { 0, 1, 2, 3, 4 },
new int[] { (int)(name1 * name2), (int)z, (int)x, (int)c, (int)v });
var name = context.Registry.GetService<INameService>();
var marker = context.Registry.GetService<IMarkerService>();
name.MarkHelper(initMethod, marker);
var cctor = context.CurrentModule.GlobalType.FindStaticConstructor();
cctor.Body.Instructions.Insert(0, Instruction.Create(OpCodes.Call, initMethod));
parent.ExcludeMethod(context, cctor);
parent.ExcludeMethod(context, initMethod);
}
public void HandleMD(AntiTamperProtection parent, ConfuserContext context, ProtectionParameters parameters)
{
methods = parameters.Targets.OfType<MethodDef>().ToList();
context.CurrentModuleWriterListener.OnWriterEvent += OnWriterEvent;
}
void OnWriterEvent(object sender, ModuleWriterListenerEventArgs e)
{
var writer = (ModuleWriter)sender;
if (e.WriterEvent == ModuleWriterEvent.MDEndCreateTables)
{
CreateSections(writer);
}
else if (e.WriterEvent == ModuleWriterEvent.BeginStrongNameSign)
{
EncryptSection(writer);
}
}
void CreateSections(ModuleWriter writer)
{
byte[] nameBuffer = new byte[8];
nameBuffer[0] = (byte)(name1 >> 0);
nameBuffer[1] = (byte)(name1 >> 8);
nameBuffer[2] = (byte)(name1 >> 16);
nameBuffer[3] = (byte)(name1 >> 24);
nameBuffer[4] = (byte)(name2 >> 0);
nameBuffer[5] = (byte)(name2 >> 8);
nameBuffer[6] = (byte)(name2 >> 16);
nameBuffer[7] = (byte)(name2 >> 24);
var newSection = new PESection(Encoding.ASCII.GetString(nameBuffer), 0xE0000040);
writer.Sections.Insert(0, newSection); // insert first to ensure proper RVA
uint alignment;
alignment = writer.TextSection.Remove(writer.MetaData).Value;
writer.TextSection.Add(writer.MetaData, alignment);
alignment = writer.TextSection.Remove(writer.NetResources).Value;
writer.TextSection.Add(writer.NetResources, alignment);
alignment = writer.TextSection.Remove(writer.Constants).Value;
newSection.Add(writer.Constants, alignment);
// move some PE parts to separate section to prevent it from being hashed
var peSection = new PESection("", 0x60000020);
bool moved = false;
if (writer.StrongNameSignature != null)
{
alignment = writer.TextSection.Remove(writer.StrongNameSignature).Value;
peSection.Add(writer.StrongNameSignature, alignment);
moved = true;
}
if (writer.ImportAddressTable != null)
{
alignment = writer.TextSection.Remove(writer.ImportAddressTable).Value;
peSection.Add(writer.ImportAddressTable, alignment);
moved = true;
}
if (writer.StartupStub != null)
{
alignment = writer.TextSection.Remove(writer.StartupStub).Value;
peSection.Add(writer.StartupStub, alignment);
moved = true;
}
if (moved)
writer.Sections.Add(peSection);
// move encrypted methods
var encryptedChunk = new MethodBodyChunks(writer.TheOptions.ShareMethodBodies);
newSection.Add(encryptedChunk, 4);
foreach (var method in methods)
{
if (!method.HasBody)
continue;
var body = writer.MetaData.GetMethodBody(method);
var ok = writer.MethodBodies.Remove(body);
encryptedChunk.Add(body);
}
// padding to prevent bad size due to shift division
newSection.Add(new ByteArrayChunk(new byte[4]), 4);
}
void EncryptSection(ModuleWriter writer)
{
var stream = writer.DestinationStream;
var reader = new BinaryReader(writer.DestinationStream);
stream.Position = 0x3C;
stream.Position = reader.ReadUInt32();
stream.Position += 6;
ushort sections = reader.ReadUInt16();
stream.Position += 0xc;
ushort optSize = reader.ReadUInt16();
stream.Position += 2 + optSize;
uint encLoc = 0, encSize = 0;
for (int i = 0; i < sections; i++)
{
uint nameHash = reader.ReadUInt32() * reader.ReadUInt32();
stream.Position += 8;
if (nameHash == name1 * name2)
{
encSize = reader.ReadUInt32();
encLoc = reader.ReadUInt32();
}
else if (nameHash != 0)
{
uint sectSize = reader.ReadUInt32();
uint sectLoc = reader.ReadUInt32();
Hash(stream, reader, sectLoc, sectSize);
}
stream.Position += 16;
}
uint[] key = DeriveKey();
encSize >>= 2;
stream.Position = encLoc;
uint[] result = new uint[encSize];
for (uint i = 0; i < encSize; i++)
{
uint data = reader.ReadUInt32();
result[i] = data ^ key[i & 0xf];
key[i & 0xf] = (key[i & 0xf] ^ data) + 0x3dbb2819;
}
byte[] byteResult = new byte[encSize << 2];
Buffer.BlockCopy(result, 0, byteResult, 0, byteResult.Length);
stream.Position = encLoc;
stream.Write(byteResult, 0, byteResult.Length);
}
void Hash(Stream stream, BinaryReader reader, uint offset, uint size)
{
long original = stream.Position;
stream.Position = offset;
size >>= 2;
for (uint i = 0; i < size; i++)
{
uint data = reader.ReadUInt32();
uint tmp = (z ^ data) + x + c * v;
z = x;
x = c;
x = v;
v = tmp;
}
stream.Position = original;
}
uint[] DeriveKey()
{
uint[] dst = new uint[0x10], src = new uint[0x10];
for (int i = 0; i < 0x10; i++)
{
dst[i] = v;
src[i] = x;
z = (x >> 5) | (x << 27);
x = (c >> 3) | (c << 29);
c = (v >> 7) | (v << 25);
v = (z >> 11) | (z << 21);
}
return deriver.DeriveKey(dst, src);
}
}
}

Просмотреть файл

@ -43,6 +43,12 @@
</ItemGroup>
<ItemGroup>
<Compile Include="AntiILDasmProtection.cs" />
<Compile Include="AntiTamper\AntiTamperProtection.cs" />
<Compile Include="AntiTamper\DynamicDeriver.cs" />
<Compile Include="AntiTamper\IKeyDeriver.cs" />
<Compile Include="AntiTamper\IModeHandler.cs" />
<Compile Include="AntiTamper\NormalDeriver.cs" />
<Compile Include="AntiTamper\NormalMode.cs" />
<Compile Include="Constants\CEContext.cs" />
<Compile Include="Constants\ConstantProtection.cs">
<SubType>Code</SubType>

Просмотреть файл

@ -165,30 +165,6 @@ namespace Confuser.Protections
0x7fff7fff, (uint)ManifestResourceAttributes.Private, 0x7fff7fff, 2));
*/
}
else if (e.WriterEvent == ModuleWriterEvent.BeginCalculateRvasAndFileOffsets)
{
foreach (var section in writer.Sections)
section.Name = "";
}
else if (e.WriterEvent == ModuleWriterEvent.ChunksAddedToSections)
{
var newSection = new PESection("", 0xE0000040);
writer.Sections.Insert(0, newSection);
uint alignment;
alignment = writer.TextSection.Remove(writer.MetaData).Value;
writer.TextSection.Add(writer.MetaData, alignment);
alignment = writer.TextSection.Remove(writer.NetResources).Value;
writer.TextSection.Add(writer.NetResources, alignment);
alignment = writer.TextSection.Remove(writer.MethodBodies).Value;
newSection.Add(writer.MethodBodies, alignment);
alignment = writer.TextSection.Remove(writer.Constants).Value;
newSection.Add(writer.Constants, alignment);
}
}
}
}

Просмотреть файл

@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
namespace Confuser.Runtime
{
class AntiTamperNormal
{
unsafe static void Initialize()
{
var m = typeof(AntiTamperNormal).Module;
string n = m.FullyQualifiedName;
bool f = n.Length > 0 && n[0] == '<';
byte* b = (byte*)Marshal.GetHINSTANCE(m);
byte* p = b + *(uint*)(b + 0x3c);
ushort s = *(ushort*)(p + 0x6);
ushort o = *(ushort*)(p + 0x14);
uint* e = null;
uint l = 0;
uint* r = (uint*)(p + 0x18 + o);
uint z = (uint)Mutation.KeyI1, x = (uint)Mutation.KeyI2, c = (uint)Mutation.KeyI3, v = (uint)Mutation.KeyI4;
for (int i = 0; i < s; i++)
{
uint g = (*r++) * (*r++);
if (g == (uint)Mutation.KeyI0)
{
e = (uint*)(b + (f ? *(r + 3) : *(r + 1)));
l = (f ? *(r + 2) : *(r + 0)) >> 2;
}
else if (g != 0)
{
uint* q = (uint*)(b + (f ? *(r + 3) : *(r + 1)));
uint j = *(r + 2) >> 2;
for (uint k = 0; k < j; k++)
{
uint t = (z ^ (*q++)) + x + c * v;
z = x;
x = c;
x = v;
v = t;
}
}
r += 8;
}
uint[] y = new uint[0x10], d = new uint[0x10];
for (int i = 0; i < 0x10; i++)
{
y[i] = v;
d[i] = x;
z = (x >> 5) | (x << 27);
x = (c >> 3) | (c << 29);
c = (v >> 7) | (v << 25);
v = (z >> 11) | (z << 21);
}
Mutation.Crypt(y, d);
uint h = 0;
for (uint i = 0; i < l; i++)
{
*e ^= y[h & 0xf];
y[h & 0xf] = (y[h & 0xf] ^ (*e++)) + 0x3dbb2819;
h++;
}
}
}
}

Просмотреть файл

@ -48,6 +48,7 @@
<Compile Include="RefProxy.Strong.cs" />
<Compile Include="AntiDebug.Safe.cs" />
<Compile Include="Resource.cs" />
<Compile Include="AntiTamper.Normal.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="System" />

2
dnlib

@ -1 +1 @@
Subproject commit c0bdc93a2763f2fbc3dc46a68c40ca1c09ab5e2d
Subproject commit 9d2bb28baffc35278696a9319cdf2ec0afc11111