Oxfuscator

Or introduce the @:

var a := “unencrypted string”;
var b := @“encrypted string”

This give the possibility to encrypt only sensitive strings.

Thanks, logged as bugs://80510 (string obfuscation)

1 Like

I would suggest both of the above:
[Assembly:EncryptStrings] to encrypt all strings (marked for encryption or not)
[EncryptStrings] to do this on class or method level
@ to encrypt one specific string

and one more, only method level: [DoNotEncrypt]
for methods that have heavy fixed string manipulation.

EncryptStrings would probably best be a property of the Obfuscate attribute.

I’m not sure I like @. For one, it already has a meaning in Pascal (address of), for another, we cant use it in C# because there @ already is a valid string prefix to disable escape chars (plus, it would be confusing for the same prefix to have different meanings in Pascal vs C#, for those of us who use both ;). But if we can diff a different prefix char, I’m all for the general syntax.

You are correct.
@ was just an idea - any prefix (or whatever to encrypt one string) will do.
We could also use the ` character (instead of ") for an encrypted string.

Or even an attribute ON the string const

writeLn([Obfuscate]"secret string");

Very nice!

1 Like

Carlo, I was thinking that encryption is indeed too much overhead.
But the goal is to make it unreadable - we could use a byte array for this.

  1. Generate a method that converts a bytearray to a string using UTF32 encoding
  2. Convert all strings to a byte array using UTF32 encoding

So in the code a string will get the form of:
var myString := obfuscatedfunction([77, 0, 0, 0, 121, 0, 0, 0, 83, 0, 0, 0, 116, 0, 0, 0, 114, 0, 0, 0, 105, 0, 0, 0, 110, 0, 0, 0, 103, 0, 0, 0]);
instead of
var myString := “MyString”;

This will make it 100% unreadable.

Edit: Timing on this call: only 2 ms (on an 8 year old 2 Ghz AMD Opteron machine with 1333 Mhz DDR3 memory)

Yeah. something like that.

Just tested: it works.

2 Likes

bugs://80509 got closed with status fixed.

For string obfuscation:

namespace RemObjects.Elements.Obfuscation;

uses
  RemObjects.Elements.Cirrus.*;

type 
  [MethodAspectAttribute(typeOf(RemObjects.Elements.Obfuscation.__Global), true, 'ObfuscateString', 0, [typeOf(String)])]
  StringObfuscationAspect = public class(IMethodCallDecorator, IAspectFinishCallback)
  private 
    class var fRandom: Random := new Random();
    class var fObfuscatedString: IFieldDefinition;
    class var fDemangleString: IMethodDefinition;
    class var fCtor: IMethodDefinition;
    class var fValue: String;

    class method MangleString(s: String; aKey: Integer): String;
    begin 
      var c := new Char[s.Length + 2];
      var lLen := s.Length xor aKey;
      c[0] := Char(lLen);
      c[1] := Char(lLen shr 16);
      aKey := ((aKey shl 3) or (aKey shr 29)) + s.Length;
      for i: Integer := 0 to s.Length -1 do begin 
        c[i + 2] := Char(Word(s[i]) xor aKey);
        aKey := ((aKey shl 3) or (aKey shr 29)) + ord(s[i]);
      end;
      exit new String(c);
    end;
  public 
    method Finish(services: IServices);
    begin 
      if fObfuscatedString <> nil then 
        fCtor.ReplaceMethodBody(new BeginStatement(
          new PlaceHolderStatement,
          new AssignmentStatement(new FieldValue(new TypeValue(fObfuscatedString.Owner), fObfuscatedString), fValue)));
      fObfuscatedString := nil;
    end;

    method ProcessMethodCall(aContext: IContext; aMethod: IMethod; aValue: ParameterizedValue): Value;
    begin
      if aValue.Parameters.Count <> 1 then begin 
        aContext.Services.EmitError('Single string parameter expected!');
        exit 
     end;
      var lVal := aValue.Parameters[0];
      if lVal.Kind <> ValueKind.Data then begin 
        aContext.Services.EmitError('Single string parameter expected!');
        exit 
     end;
      var lStringValue := String(lVal);

      if fObfuscatedString = nil then begin 
        var lTD := coalesce(
          ITypeDefinition(aContext.Services.FindType('<PrivateImplementationDetails>')),
          aContext.Services.CreateTypeDefinition('', '<PrivateImplementationDetails>', TypeDefKind.Class));
        fCtor := coalesce(lTD.GetClassConstructor as IMethodDefinition, lTD.AddConstructor(true));
        fObfuscatedString := lTD.AddField('data', aContext.Services.GetBaseType(13), true);
        // TODO: Java, Toffee implementation.
        fDemangleString := lTD.AddMethod('a', aContext.Services.GetBaseType(13), true);

        //fDemangleString.AddParameter('a', ParameterModifier.In, aContext.Services.GetBaseType(13));
        fDemangleString.AddParameter('b', ParameterModifier.In, aContext.Services.GetBaseType(7));
        fDemangleString.AddParameter('d', ParameterModifier.In, aContext.Services.GetBaseType(7));
        var parameter := new FieldValue(new TypeValue(fObfuscatedString.Owner), fObfuscatedString);
        var parameter2 := fDemangleString.GetParameter('b');
        var parameter3 := fDemangleString.GetParameter('d');
        /*

    class method DemangleString(aIn: String; aOffset, aKey: Integer): String;
    begin
      var lLen := Word(aIn[aOffset + 0]) or (Integer(aIn[aOffset + 1]) shl 16);
      lLen := lLen xor aKey;
      aKey := ((aKey shl 3) or (aKey shr 29)) + lLen;
      var c := new Char[lLen];
      for i: Integer := 0 to lLen -1 do begin 
        c[i] := Char(Word(aIn[i + aOffset + 2]) xor aKey);
        aKey := ((aKey shl 3) or (aKey shr 29)) + ord(c[i]);
      end;
      exit new String(c);
    end;

*/


        fDemangleString.ReplaceMethodBody(new BeginStatement([
            new LocalVariable("lLen", fDemangleString.GetContextType("System.Int32")),
            new LocalVariable("c", fDemangleString.GetContextType("array of System.Char"))
          ], 
          new AssignmentStatement(new NamedLocalValue("lLen"), new BinaryValue(new UnaryValue(new ProcValue(parameter, "get_Chars", new IType[0], false, new BinaryValue(parameter2, new DataValue(fDemangleString.GetContextType("System.Int32"), 0), BinaryOperator.Add, fDemangleString.GetContextType("System.Int32"))), UnaryOperator.ImpCast, fDemangleString.GetContextType("System.UInt16")), new BinaryValue(new UnaryValue(new ProcValue(parameter, "get_Chars", new IType[0], false, new BinaryValue(parameter2, new DataValue(fDemangleString.GetContextType("System.Int32"), 1), BinaryOperator.Add, fDemangleString.GetContextType("System.Int32"))), UnaryOperator.ImpCast, fDemangleString.GetContextType("System.Int32")), new DataValue(fDemangleString.GetContextType("System.Int32"), 16), BinaryOperator.Shl, fDemangleString.GetContextType("System.Int32")), BinaryOperator.Or, fDemangleString.GetContextType("System.Int32"))), 
          new AssignmentStatement(new IdentifierValue("lLen"), new BinaryValue(new IdentifierValue("lLen"), parameter3, BinaryOperator.Xor, fDemangleString.GetContextType("System.Int32"))), 
          new AssignmentStatement(parameter3, new BinaryValue(new BinaryValue(new BinaryValue(parameter3, new DataValue(fDemangleString.GetContextType("System.Int32"), 3), BinaryOperator.Shl, fDemangleString.GetContextType("System.Int32")), new BinaryValue(parameter3, new DataValue(fDemangleString.GetContextType("System.Int32"), 29), BinaryOperator.Shr, fDemangleString.GetContextType("System.Int32")), BinaryOperator.Or, fDemangleString.GetContextType("System.Int32")), new IdentifierValue("lLen"), BinaryOperator.Add, fDemangleString.GetContextType("System.Int32"))), 
          new AssignmentStatement(new NamedLocalValue("c"), new ArrayCtorCallValue(IArrayType(fDemangleString.GetContextType("array of System.Char")), new IdentifierValue("lLen"))), 
          new ForStatement("i", fDemangleString.GetContextType("System.Int32"), new DataValue(fDemangleString.GetContextType("System.Int32"), 0), new BinaryValue(new IdentifierValue("lLen"), new DataValue(fDemangleString.GetContextType("System.Int32"), 1), BinaryOperator.Sub, fDemangleString.GetContextType("System.Int32")), nil, new BeginStatement(new AssignmentStatement(new SubArrayValue(new IdentifierValue("c"), new IdentifierValue("i")), new UnaryValue(new BinaryValue(new UnaryValue(new ProcValue(parameter, "get_Chars", new IType[0], false, new BinaryValue(new BinaryValue(new IdentifierValue("i"), parameter2, BinaryOperator.Add, fDemangleString.GetContextType("System.Int32")), new DataValue(fDemangleString.GetContextType("System.Int32"), 2), BinaryOperator.Add, fDemangleString.GetContextType("System.Int32"))), UnaryOperator.ImpCast, fDemangleString.GetContextType("System.UInt16")), parameter3, BinaryOperator.Xor, fDemangleString.GetContextType("System.Int32")), UnaryOperator.ImpCast, fDemangleString.GetContextType("System.Char"))), new AssignmentStatement(parameter3, new BinaryValue(new BinaryValue(new BinaryValue(parameter3, new DataValue(fDemangleString.GetContextType("System.Int32"), 3), BinaryOperator.Shl, fDemangleString.GetContextType("System.Int32")), new BinaryValue(parameter3, new DataValue(fDemangleString.GetContextType("System.Int32"), 29), BinaryOperator.Shr, fDemangleString.GetContextType("System.Int32")), BinaryOperator.Or, fDemangleString.GetContextType("System.Int32")), new UnaryValue(new SubArrayValue(new IdentifierValue("c"), new IdentifierValue("i")), UnaryOperator.ImpCast, fDemangleString.GetContextType("System.UInt16")), BinaryOperator.Add, fDemangleString.GetContextType("System.Int32")))), false, false), 
          new ExitStatement(new NewValue(fDemangleString.GetContextType("System.String"), [new IdentifierValue("c")], new System.Collections.Generic.KeyValuePair<String, Value>[0]))));

      end;
      var lOffset: Integer;
      var lKey := fRandom.Next;
      if fValue = nil then begin
        fValue := MangleString(lStringValue, lKey);
        lOffset := 0;
      end else begin 
        var lOld := fValue;
        lOffset := lOld.Length;
        fValue := lOld + MangleString(lStringValue, lKey);
      end;

      exit new ProcValue(new TypeValue(fDemangleString.Owner), fDemangleString, [lOffset, lKey]);
    end;
  end;

method ObfuscateString(s: String): String;
begin 
  exit s;
end;

end.

(.NET only at the moment, but that’s just a question of porting the code)

Usage: put it in a dll, use it like:


type
  Program = class
  public

    class method Main(args: array of String): Int32;
    begin
      writeLn(ObfuscateString('HAHA'));
      writeLn(ObfuscateString('HIHI'));
      // add your own code here
      writeLn('The magic happens here.');
    end;

  end;

end result looks like:

        public static int Main(string[] args)
	{
		Console.Out.WriteLine(<PrivateImplementationDetails>.a(0, 1325031605));
		Console.Out.WriteLine(<PrivateImplementationDetails>.a(6, 1888006488));
		Console.Out.WriteLine("The magic happens here.");
		int result = default(int);
		return result;
	}

a single string is stored as "悱仺צⷺvɹ녜炈誏\rȡᏙ"

this needs todays build to compile

2 Likes

This works on all 4 platforms now, and is integrated in cirrus.

2 Likes

Almost forgot to test this one …

I have to add RemObjects.Elements.Obfuscation the uses.
And it works!

2 Likes

@ck,
The obfuscation call fails during compile after I added it to all strings in a unit .
buildLog:
buildLog.txt (9.4 KB)

Extra info:
I have this string:

var ConStr := "Persist Security Info=true;server=" + Server + 
              ";Initial Catalog=" + Db + 
              ";Application Name=" + system.Reflection.Assembly.GetExecutingAssembly().FullName + 
              ";MultipleActiveResultSets=True;Pooling=false;" as SafeString; //safestring = not nullable string

Add the first:

var ConStr := "Persist Security Info=true;server=" + Server + 
              ObfuscateString(";Initial Catalog=") + Db + 
              ";Application Name=" + system.Reflection.Assembly.GetExecutingAssembly().FullName + 
              ";MultipleActiveResultSets=True;Pooling=false;" as SafeString;

And it compiles.

add the second:

var ConStr := ObfuscateString("Persist Security Info=true;server=") + Server + 
              ObfuscateString(";Initial Catalog=") + Db + 
              ";Application Name=" + system.Reflection.Assembly.GetExecutingAssembly().FullName + 
              ";MultipleActiveResultSets=True;Pooling=false;" as SafeString;

And it compiles.

Add the third:

var ConStr := ObfuscateString("Persist Security Info=true;server=") + Server + 
              ObfuscateString(";Initial Catalog=") + Db + 
              ObfuscateString(";Application Name=") + system.Reflection.Assembly.GetExecutingAssembly().FullName + 
              ";MultipleActiveResultSets=True;Pooling=false;" as SafeString;

Does not compile anymore

And it won’t compile until I remove all Obfuscate string calls.

Thanks, logged as bugs://80808

this works fine:

Got it.

bugs://80808 got closed with status fixed.

Fix confirmed.

1 Like

bugs://80510 got closed with status fixed.