XenocodeRCE / neo-ConfuserEx

Updated ConfuserEX, an open-source, free obfuscator for .NET applications
http://yck1509.github.io/ConfuserEx/
Other
756 stars 89 forks source link

Databinding to public members with forceren enabled - Allow renaming of XAML/BAML references #48

Open rollsch opened 5 years ago

rollsch commented 5 years ago

Is your feature request related to a problem? Please describe. If you have XAML Databinding to public members and use ForceRen (because lets assume your project is not a public library so renaming public methods is acceptable) then this will obviously break databinding.

Describe the solution you'd like I've hacked a solution which extracts all databinding out of the XAML and makes a list of member names. It then adds these names to the list of items to not rename. My solution is very ugly but it works,

This would be great to see added as it allows a high level of obfuscation such as:

      <protection id="rename">
        <argument name="mode" value="decodable" />
        <argument name="renPublic" value="true" />
        <argument name="flatten" value="true" />
      </protection>

But it also doesn't break XAML databinding. I have a very large project and this approach hasn't failed so far.

To do this properly you would rename the XAML however where I hacked my solution in it seemed like quite a bit of work to make it also rename the XAML, instead I just disabled renaming of these members instead.

here is my function which extracts databinding from .cs files

`private void ExtractBinding(MethodDef methodDef, List<IBaseRef> dynBindings)
{
    if (methodDef == null) return;

    if (!methodDef.HasBody) return;
    if (methodDef.Body?.Instructions == null) return;

    Instruction inst0, inst1, inst2, inst3, inst4, inst5;
    for (var index = 3; index < methodDef.Body.Instructions.Count; index++)
    {
        inst0 = methodDef.Body.Instructions[index - 3];
        inst1 = methodDef.Body.Instructions[index - 2];
        inst2 = methodDef.Body.Instructions[index - 1];
        inst3 = methodDef.Body.Instructions[index];

        //Check if we are using reflection to make a dynamic binding -
        if (inst0.ToString().Contains("ldstr") && inst1.ToString().Contains("ldnull") &&
            inst2.ToString().Contains("ldtoken") && inst3.ToString().Contains("GetTypeFromHandle"))
        {
            //var fullName = inst2.Operand.ToString() + inst0.Operand.ToString();
            var dyn = new MethodRef
            {
                Name = inst0.Operand.ToString(),
                ClassFullName = inst2.Operand.ToString(),
            };
            lock(_lock) dynBindings.Add(dyn);
        }

        //Check if we are using reflection to make a dynamic binding
        if (inst0.ToString().Contains("ldstr") && inst1.ToString().Contains("ldc.i4.1") &&
            inst2.ToString().Contains("newarr") && inst2.ToString().Contains("newarr System.Type") &&
            inst3.ToString().Contains("dup"))
        {
            //var fullName = inst2.Operand.ToString() + inst0.Operand.ToString();
            var dyn = new MethodRef
            {
                Name = inst0.Operand.ToString(),
                ClassFullName = inst2.Operand.ToString(),
            };
            lock(_lock) dynBindings.Add(dyn);
        }
    }
}`

here is my code which extracts a list of databinding elements from BAML

`        public List<IBaseRef> Analyze(ModuleDefMD module, string CurrentBAMLName, byte[] data)
        {
            var dynBindings = new List<IBaseRef>();
            Debug.Assert(BitConverter.ToInt32(data, 0) == data.Length - 4);

            BamlDocument document = BamlReader.ReadDocument(new MemoryStream(data, 4, data.Length - 4));

            foreach (BamlRecord item in document)
            {
                var txt = item as StringInfoRecord;
                if (txt != null)
                {
                    dynBindings.Add(new GlobalRef{Name = txt.Value} );
                    continue;
                }

                var txt2 = item as TextRecord;
                if (txt2 != null)
                {
                    dynBindings.Add(new GlobalRef { Name = txt2.Value });
                    dynBindings.Add(new GlobalRef { Name = txt2.Value.Replace("Command", "") });
                    continue;
                }

                var txt3 = item as PropertyWithConverterRecord;
                if (txt3 != null)
                {
                    dynBindings.Add(new GlobalRef { Name = txt3.Value });
                    continue;
                }

            }
            return dynBindings;
        }
XenocodeRCE commented 5 years ago

Hello, nice find, thank you for the code. I think from here you can make a PR. ConfuserEx WPF analyzer is pretty self explanatory, however if you got stuck just let me know :))

rollsch commented 5 years ago

Can you give some pointers of where the analysers should be put?

Basically the process is as follows: Analyse all WPF to build a list of databindings - Analyser1 Analyse all C# to build a list of dynamic bindings (eg using TypeOf and things like that) - Analyser2

Remove all items found in analyser1,analyser2 from the renaming (can I simply do this from within the analyser itself?)

Where would I inject these new analysers?

If I wanted to go a step further and rename within the BAML and C#, how would this work? The analyser isn't allowed to modify anything yet so it would need to store the results and then intercept during the rewrite process?

Can you explain the class layout to assist? Writing the code is easy but understanding the code structure is where I am falling over. I just hacked everything in to make it work as I couldn't figure out where to put the analysers or the program flow.

XenocodeRCE commented 5 years ago

right there : https://github.com/XenocodeRCE/neo-ConfuserEx/tree/master/Confuser.Renamer/BAML https://github.com/XenocodeRCE/neo-ConfuserEx/tree/master/Confuser.Renamer/Analyzers

basically debug the project while obfuscating your app, and keep your eyes on the variable values. When it is about to make a mistake, just fix the code. I'm sure it's the matter of less then 2 lines of code.

rollsch commented 5 years ago

So you are happy for me to modify the existing analysers and not make new ones?