Fix #24
This commit is contained in:
yck1509 2014-12-09 16:54:17 +08:00
Родитель c0f7141e04
Коммит 6ce1d2dcff
7 изменённых файлов: 1814 добавлений и 27 удалений

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

@ -58,6 +58,9 @@
<Compile Include="..\GlobalAssemblyInfo.cs">
<Link>Properties\GlobalAssemblyInfo.cs</Link>
</Compile>
<Compile Include="ObfAttrMarker.cs" />
<Compile Include="ObfAttrParser.cs" />
<Compile Include="Options.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
@ -82,6 +85,10 @@
<Project>{A45C184F-F98F-4258-A928-BFF437034791}</Project>
<Name>Confuser.Runtime</Name>
</ProjectReference>
<ProjectReference Include="..\dnlib\src\dnlib.csproj">
<Project>{FDFC1237-143F-4919-8318-4926901F4639}</Project>
<Name>dnlib</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="..\ConfuserEx.snk">

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

@ -0,0 +1,323 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Confuser.Core;
using Confuser.Core.Project;
using dnlib.DotNet;
namespace Confuser.CLI {
internal class ObfAttrMarker : Marker {
protected override void MarkMember(IDnlibDef member, ConfuserContext context) {
ModuleDef module = ((IMemberRef)member).Module;
ProtectionParameters.SetParameters(context, member, ProtectionParameters.GetParameters(context, module));
}
protected override MarkerResult MarkProject(ConfuserProject proj, ConfuserContext context) {
var modules = new List<Tuple<ProjectModule, ModuleDefMD>>();
foreach (ProjectModule module in proj) {
ModuleDefMD modDef = module.Resolve(proj.BaseDirectory, context.Resolver.DefaultModuleContext);
context.CheckCancellation();
context.Resolver.AddToCache(modDef);
modules.Add(Tuple.Create(module, modDef));
}
Tuple<Packer, Dictionary<string, string>> packerInfo = null;
foreach (var module in modules) {
context.Logger.InfoFormat("Loading '{0}'...", module.Item1.Path);
MarkModule(proj, context, module.Item2, module == modules[0], ref packerInfo);
// Packer parameters are stored in modules
if (packerInfo != null)
ProtectionParameters.GetParameters(context, module.Item2)[packerInfo.Item1] = packerInfo.Item2;
}
return new MarkerResult(modules.Select(module => module.Item2).ToList(), packerInfo == null ? null : packerInfo.Item1);
}
private struct ObfuscationAttributeInfo {
public bool? ApplyToMembers;
public bool? Exclude;
public string FeatureName;
public string FeatureValue;
}
private struct ProtectionSettingsInfo {
public bool ApplyToMember;
public bool Exclude;
public string Settings;
}
private class ProtectionSettingsStack {
private Stack<ProtectionSettingsInfo[]> stack;
public ProtectionSettingsStack() {
stack = new Stack<ProtectionSettingsInfo[]>();
}
public ProtectionSettingsStack(ProtectionSettingsStack copy) {
stack = new Stack<ProtectionSettingsInfo[]>(copy.stack);
}
public void Push(IEnumerable<ProtectionSettingsInfo> infos) {
stack.Push(infos.ToArray());
}
public void Pop() {
stack.Pop();
}
public IEnumerable<ProtectionSettingsInfo> GetInfos() {
return stack.Reverse().SelectMany(infos => infos);
}
}
private static IEnumerable<ObfuscationAttributeInfo> ReadObfuscationAttributes(IHasCustomAttribute item) {
var ret = new List<ObfuscationAttributeInfo>();
for (int i = item.CustomAttributes.Count - 1; i >= 0; i--) {
var ca = item.CustomAttributes[i];
if (ca.TypeFullName != "System.Reflection.ObfuscationAttribute")
continue;
var info = new ObfuscationAttributeInfo();
bool strip = true;
foreach (var prop in ca.Properties) {
switch (prop.Name) {
case "ApplyToMembers":
Debug.Assert(prop.Type.ElementType == ElementType.Boolean);
info.ApplyToMembers = (bool)prop.Value;
break;
case "Exclude":
Debug.Assert(prop.Type.ElementType == ElementType.Boolean);
info.Exclude = (bool)prop.Value;
break;
case "StripAfterObfuscation":
Debug.Assert(prop.Type.ElementType == ElementType.Boolean);
strip = (bool)prop.Value;
break;
case "Feature":
Debug.Assert(prop.Type.ElementType == ElementType.String);
string feature = (UTF8String)prop.Value;
int sepIndex = feature.IndexOf(':');
if (sepIndex == -1) {
info.FeatureName = "";
info.FeatureValue = feature;
}
else {
info.FeatureName = feature.Substring(0, sepIndex);
info.FeatureValue = feature.Substring(sepIndex + 1);
}
break;
default:
throw new NotSupportedException("Unsupported property: " + prop.Name);
}
}
if (strip)
item.CustomAttributes.RemoveAt(i);
ret.Add(info);
}
ret.Reverse();
return ret;
}
private static IEnumerable<ProtectionSettingsInfo> ProcessAttributes(IEnumerable<ObfuscationAttributeInfo> attrs) {
foreach (var attr in attrs) {
var info = new ProtectionSettingsInfo();
info.Exclude = (attr.Exclude ?? true);
info.ApplyToMember = (attr.ApplyToMembers ?? true);
info.Settings = attr.FeatureValue;
if (!string.IsNullOrEmpty(attr.FeatureName))
throw new ArgumentException("Feature name must not be set.");
if (info.Exclude && (!string.IsNullOrEmpty(attr.FeatureName) || !string.IsNullOrEmpty(attr.FeatureValue))) {
throw new ArgumentException("Feature property cannot be set when Exclude is true.");
}
yield return info;
}
}
private void ApplySettings(ConfuserContext context, IDnlibDef def, IEnumerable<ProtectionSettingsInfo> infos) {
var settings = new ProtectionSettings();
ProtectionSettingsInfo? last = null;
var parser = new ObfAttrParser(protections);
foreach (var info in infos) {
if (info.Exclude) {
if (info.ApplyToMember)
settings.Clear();
continue;
}
last = info;
if (info.ApplyToMember) {
parser.ParseProtectionString(settings, info.Settings);
}
}
if (last != null && !last.Value.ApplyToMember) {
parser.ParseProtectionString(settings, last.Value.Settings);
}
ProtectionParameters.SetParameters(context, def, settings);
}
private static readonly Regex NSPattern = new Regex("namespace '([^']*)'");
private void MarkModule(ConfuserProject proj, ConfuserContext context, ModuleDefMD module, bool isMain,
ref Tuple<Packer, Dictionary<string, string>> packerInfo) {
var settingAttrs = new List<ObfuscationAttributeInfo>();
string snKeyPath = null, snKeyPass = null;
var namespaceAttrs = new Dictionary<string, List<ObfuscationAttributeInfo>>();
foreach (var attr in ReadObfuscationAttributes(module.Assembly)) {
if (attr.FeatureName.Equals("generate debug symbol", StringComparison.OrdinalIgnoreCase)) {
if (!isMain)
throw new ArgumentException("Only main module can set 'generate debug symbol'.");
proj.Debug = bool.Parse(attr.FeatureValue);
}
if (proj.Debug) {
module.LoadPdb();
}
if (attr.FeatureName.Equals("random seed", StringComparison.OrdinalIgnoreCase)) {
if (!isMain)
throw new ArgumentException("Only main module can set 'random seed'.");
proj.Seed = attr.FeatureValue;
}
else if (attr.FeatureName.Equals("strong name key", StringComparison.OrdinalIgnoreCase)) {
snKeyPath = Path.Combine(proj.BaseDirectory, attr.FeatureValue);
}
else if (attr.FeatureName.Equals("strong name key password", StringComparison.OrdinalIgnoreCase)) {
snKeyPass = attr.FeatureValue;
}
else if (attr.FeatureName.Equals("packer", StringComparison.OrdinalIgnoreCase)) {
if (!isMain)
throw new ArgumentException("Only main module can set 'packer'.");
Packer packer;
Dictionary<string, string> packerParams;
new ObfAttrParser(packers).ParsePackerString(attr.FeatureValue, out packer, out packerParams);
packerInfo = Tuple.Create(packer, packerParams);
}
else if (attr.FeatureName == "") {
settingAttrs.Add(attr);
}
else {
var match = NSPattern.Match(attr.FeatureName);
if (match.Success) {
string ns = match.Groups[1].Value;
var x = attr;
x.FeatureName = "";
namespaceAttrs.AddListEntry(ns, x);
}
}
}
ProcessModule(module, context, snKeyPath, snKeyPass, settingAttrs, namespaceAttrs);
}
private void ProcessModule(ModuleDefMD module, ConfuserContext context, string snKeyPath, string snKeyPass,
List<ObfuscationAttributeInfo> settingAttrs,
Dictionary<string, List<ObfuscationAttributeInfo>> namespaceAttrs) {
context.Annotations.Set(module, SNKey, LoadSNKey(context, snKeyPath, snKeyPass));
var moduleStack = new ProtectionSettingsStack();
moduleStack.Push(ProcessAttributes(settingAttrs));
ApplySettings(context, module, moduleStack.GetInfos());
var nsSettings = namespaceAttrs.ToDictionary(kvp => kvp.Key, kvp => {
var nsStack = new ProtectionSettingsStack(moduleStack);
nsStack.Push(ProcessAttributes(kvp.Value));
return nsStack;
});
foreach (var type in module.Types) {
var typeStack = nsSettings.GetValueOrDefault(type.Namespace, moduleStack);
typeStack.Push(ProcessAttributes(ReadObfuscationAttributes(type)));
ApplySettings(context, type, typeStack.GetInfos());
ProcessTypeMembers(type, context, typeStack);
typeStack.Pop();
}
}
private void ProcessTypeMembers(TypeDef type, ConfuserContext context, ProtectionSettingsStack stack) {
foreach (var nestedType in type.NestedTypes) {
stack.Push(ProcessAttributes(ReadObfuscationAttributes(nestedType)));
ApplySettings(context, nestedType, stack.GetInfos());
ProcessTypeMembers(nestedType, context, stack);
stack.Pop();
}
foreach (var prop in type.Properties) {
stack.Push(ProcessAttributes(ReadObfuscationAttributes(prop)));
ApplySettings(context, prop, stack.GetInfos());
if (prop.GetMethod != null) {
ProcessMember(prop.GetMethod, context, stack);
}
if (prop.SetMethod != null) {
ProcessMember(prop.GetMethod, context, stack);
}
foreach (var m in prop.OtherMethods)
ProcessMember(m, context, stack);
stack.Pop();
}
foreach (var evt in type.Events) {
stack.Push(ProcessAttributes(ReadObfuscationAttributes(evt)));
ApplySettings(context, evt, stack.GetInfos());
if (evt.AddMethod != null) {
ProcessMember(evt.AddMethod, context, stack);
}
if (evt.RemoveMethod != null) {
ProcessMember(evt.RemoveMethod, context, stack);
}
if (evt.InvokeMethod != null) {
ProcessMember(evt.InvokeMethod, context, stack);
}
foreach (var m in evt.OtherMethods)
ProcessMember(m, context, stack);
stack.Pop();
}
foreach (var method in type.Methods) {
if (method.SemanticsAttributes == 0)
ProcessMember(method, context, stack);
}
foreach (var field in type.Fields) {
ProcessMember(field, context, stack);
}
}
private void ProcessMember(IDnlibDef member, ConfuserContext context, ProtectionSettingsStack stack) {
stack.Push(ProcessAttributes(ReadObfuscationAttributes(member)));
ApplySettings(context, member, stack.GetInfos());
stack.Pop();
}
}
}

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

@ -0,0 +1,275 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Confuser.Core;
namespace Confuser.CLI {
internal struct ObfAttrParser {
private System.Collections.IDictionary items;
private string str;
private int index;
public ObfAttrParser(System.Collections.IDictionary items) {
this.items = items;
this.str = null;
this.index = -1;
}
private enum ParseState {
Init,
ReadPreset,
ReadItemName,
ProcessItemName,
ReadParam,
EndItem,
End
}
private bool ReadId(StringBuilder sb) {
while (index < str.Length) {
switch (str[index]) {
case '(':
case ')':
case '+':
case '-':
case '=':
case ';':
case ',':
return true;
default:
sb.Append(str[index++]);
break;
}
}
return false;
}
private void Expect(char chr) {
if (str[index] != chr)
throw new ArgumentException("Expect '" + chr + "' at position " + (index + 1) + ".");
index++;
}
private char Peek() {
return str[index];
}
private void Next() {
index++;
}
private bool IsEnd() {
return index == str.Length;
}
public void ParseProtectionString(ProtectionSettings settings, string str) {
this.str = str;
this.index = 0;
var state = ParseState.Init;
var buffer = new StringBuilder();
bool protAct = true;
string protId = null;
var protParams = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
while (state != ParseState.End) {
switch (state) {
case ParseState.Init:
ReadId(buffer);
if (buffer.ToString().Equals("preset", StringComparison.OrdinalIgnoreCase)) {
if (IsEnd())
throw new ArgumentException("Unexpected end of string in Init state.");
Expect('(');
buffer.Length = 0;
state = ParseState.ReadPreset;
}
else if (buffer.Length == 0) {
if (IsEnd())
throw new ArgumentException("Unexpected end of string in Init state.");
state = ParseState.ReadItemName;
}
else {
protAct = true;
state = ParseState.ProcessItemName;
}
break;
case ParseState.ReadPreset:
if (!ReadId(buffer))
throw new ArgumentException("Unexpected end of string in ReadPreset state.");
Expect(')');
var preset = (ProtectionPreset)Enum.Parse(typeof(ProtectionPreset), buffer.ToString(), true);
foreach (var item in items.Values.OfType<Protection>().Where(prot => prot.Preset <= preset)) {
if (!settings.ContainsKey(item))
settings.Add(item, new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase));
}
buffer.Length = 0;
if (IsEnd())
state = ParseState.End;
else {
Expect(';');
if (IsEnd())
state = ParseState.End;
else
state = ParseState.ReadItemName;
}
break;
case ParseState.ReadItemName:
protAct = true;
if (Peek() == '+') {
protAct = true;
Next();
}
else if (Peek() == '-') {
protAct = false;
Next();
}
ReadId(buffer);
state = ParseState.ProcessItemName;
break;
case ParseState.ProcessItemName:
protId = buffer.ToString();
buffer.Length = 0;
if (IsEnd() || Peek() == ';')
state = ParseState.EndItem;
else if (Peek() == '(') {
if (!protAct)
throw new ArgumentException("No parameters is allowed when removing protection.");
Next();
state = ParseState.ReadParam;
}
else
throw new ArgumentException("Unexpected character in ProcessItemName state at " + index + ".");
break;
case ParseState.ReadParam:
string paramName, paramValue;
if (!ReadId(buffer))
throw new ArgumentException("Unexpected end of string in ReadParam state.");
paramName = buffer.ToString();
buffer.Length = 0;
Expect('=');
if (!ReadId(buffer))
throw new ArgumentException("Unexpected end of string in ReadParam state.");
paramValue = buffer.ToString();
buffer.Length = 0;
protParams.Add(paramName, paramValue);
if (Peek() == ',') {
Next();
state = ParseState.ReadParam;
}
else if (Peek() == ')') {
Next();
state = ParseState.EndItem;
}
else
throw new ArgumentException("Unexpected character in ReadParam state at " + index + ".");
break;
case ParseState.EndItem:
if (protAct) {
settings[(Protection)items[protId]] = protParams;
protParams = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
else
settings.Remove((Protection)items[protId]);
if (IsEnd())
state = ParseState.End;
else {
Expect(';');
if (IsEnd())
state = ParseState.End;
else
state = ParseState.ReadItemName;
}
break;
}
}
}
public void ParsePackerString(string str, out Packer packer, out Dictionary<string, string> packerParams) {
this.str = str;
this.index = 0;
var state = ParseState.ReadItemName;
var buffer = new StringBuilder();
var ret = new ProtectionSettings();
packer = null;
packerParams = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
while (state != ParseState.End) {
switch (state) {
case ParseState.ReadItemName:
if (!ReadId(buffer))
throw new ArgumentException("Unexpected end of string in ReadItemName state.");
packer = (Packer)items[buffer.ToString()];
buffer.Length = 0;
if (IsEnd() || Peek() == ';')
state = ParseState.EndItem;
else if (Peek() == '(') {
Next();
state = ParseState.ReadParam;
}
else
throw new ArgumentException("Unexpected character in ReadItemName state at " + index + ".");
break;
case ParseState.ReadParam:
string paramName, paramValue;
if (!ReadId(buffer))
throw new ArgumentException("Unexpected end of string in ReadParam state.");
paramName = buffer.ToString();
buffer.Length = 0;
Expect('=');
if (!ReadId(buffer))
throw new ArgumentException("Unexpected end of string in ReadParam state.");
paramValue = buffer.ToString();
buffer.Length = 0;
packerParams.Add(paramName, paramValue);
if (Peek() == ',') {
Next();
state = ParseState.ReadParam;
}
else if (Peek() == ')') {
Next();
state = ParseState.EndItem;
}
else
throw new ArgumentException("Unexpected character in ReadParam state at " + index + ".");
break;
case ParseState.EndItem:
if (IsEnd())
state = ParseState.End;
else {
Expect(';');
if (!IsEnd())
throw new ArgumentException("Unexpected character in EndItem state at " + index + ".");
state = ParseState.End;
}
break;
}
}
}
}
}

1103
Confuser.CLI/Options.cs Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -1,9 +1,11 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Xml;
using Confuser.Core;
using Confuser.Core.Project;
using NDesk.Options;
namespace Confuser.CLI {
internal class Program {
@ -14,40 +16,67 @@ namespace Confuser.CLI {
string originalTitle = Console.Title;
Console.Title = "ConfuserEx";
bool noPause = false;
string outDir = null;
var p = new OptionSet() {
{ "n|nopause", "no pause after finishing protection.",
value => { noPause = (value != null); } },
{ "o|out=", "specifies output directory.",
value => { outDir = value; } },
};
List<string> files;
try {
if (args.Length < 1) {
PrintUsage();
return 0;
}
var proj = new ConfuserProject();
try {
var xmlDoc = new XmlDocument();
xmlDoc.Load(args[0]);
proj.Load(xmlDoc);
proj.BaseDirectory = Path.Combine(Path.GetDirectoryName(args[0]), proj.BaseDirectory);
}
catch (Exception ex) {
WriteLineWithColor(ConsoleColor.Red, "Failed to load project:");
WriteLineWithColor(ConsoleColor.Red, ex.ToString());
return -1;
}
files = p.Parse(args);
if (files.Count == 0)
throw new ArgumentException("No input files specified.");
}
catch (Exception ex) {
Console.Write("ConfuserEx.CLI: ");
Console.WriteLine(ex.Message);
PrintUsage();
return -1;
}
try {
var parameters = new ConfuserParameters();
parameters.Project = proj;
var logger = new ConsoleLogger();
parameters.Logger = new ConsoleLogger();
Console.Title = "ConfuserEx - Running...";
ConfuserEngine.Run(parameters).Wait();
if (files.Count == 1 && Path.GetExtension(files[0]) == "crproj") {
var proj = new ConfuserProject();
try {
var xmlDoc = new XmlDocument();
xmlDoc.Load(args[0]);
proj.Load(xmlDoc);
proj.BaseDirectory = Path.Combine(Path.GetDirectoryName(args[0]), proj.BaseDirectory);
}
catch (Exception ex) {
WriteLineWithColor(ConsoleColor.Red, "Failed to load project:");
WriteLineWithColor(ConsoleColor.Red, ex.ToString());
return -1;
}
parameters.Project = proj;
}
else {
// Generate a ConfuserProject for input modules
// Assuming first file = main module
var proj = new ConfuserProject();
foreach (var input in files)
proj.Add(new ProjectModule() { Path = input });
proj.BaseDirectory = Path.GetDirectoryName(files[0]);
proj.OutputDirectory = outDir;
parameters.Project = proj;
parameters.Marker = new ObfAttrMarker();
}
int retVal = RunProject(parameters);
bool noPause = args.Length > 1 && args[1].ToUpperInvariant() == "NOPAUSE";
if (NeedPause() && !noPause) {
Console.WriteLine("Press any key to continue...");
Console.ReadKey(true);
}
return logger.ReturnValue;
return retVal;
}
finally {
Console.ForegroundColor = original;
@ -55,13 +84,26 @@ namespace Confuser.CLI {
}
}
private static int RunProject(ConfuserParameters parameters) {
var logger = new ConsoleLogger();
parameters.Logger = new ConsoleLogger();
Console.Title = "ConfuserEx - Running...";
ConfuserEngine.Run(parameters).Wait();
return logger.ReturnValue;
}
private static bool NeedPause() {
return Debugger.IsAttached || string.IsNullOrEmpty(Environment.GetEnvironmentVariable("PROMPT"));
}
private static void PrintUsage() {
WriteLine("Usage:");
WriteLine("Confuser.CLI.exe <project configuration>");
WriteLine("Confuser.CLI -n|noPause <project configuration>");
WriteLine("Confuser.CLI -n|noPause -o|out=<output directory> <modules>");
WriteLine(" -n|noPause : no pause after finishing protection.");
WriteLine(" -o|out : specifies output directory.");
}
private static void WriteLineWithColor(ConsoleColor color, string txt) {

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

@ -26,8 +26,14 @@ namespace Confuser.Core {
/// </summary>
public static readonly object RulesKey = new object();
private Dictionary<string, Packer> packers;
private Dictionary<string, Protection> protections;
/// <summary>
/// The packers available to use.
/// </summary>
protected Dictionary<string, Packer> packers;
/// <summary>
/// The protections available to use.
/// </summary>
protected Dictionary<string, Protection> protections;
/// <summary>
/// Initalizes the Marker with specified protections and packers.

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

@ -0,0 +1,31 @@
ConfuserEx declarative obfuscation:
Attribute semantics:
ApplyToMembers: The children uses this protection settings as base.
Exclude: No protection will be applied to this item.
!ApplyToMembers + !Exclude: The protection settings just apply to this item.
ApplyToMembers + Exclude: This item and its chilren will have no protection.
Pattern examples:
generate debug symbol:true
random seed:ABCDEFG
strong name key:C:\key.snk
strong name key password:hunter2
packer:compressor(mode=dynamic)
namespace 'ConfuserEx.CLI':preset(normal);+rename;anti tamper(mode=jit,key=dynamic);-anti debug
preset(none);+rename;
Usage examples:
[assembly: Obfuscation(Exclude = false, Feature = "preset(minimum);+ctrl flow;-anti debug;+rename(mode=letters,flatten=false);")]
[assembly: Obfuscation(Exclude = false, Feature = "random seed: Hello!")]
[assembly: Obfuscation(Exclude = false, Feature = "namespace 'Test':-rename")]
namespace Test {
[Obfuscation(Exclude = false, Feature = "constants")]
class Program {
public static void Main() {
Console.WriteLine("Hi");
}
}
}