Measure positional alignment against the unobfuscated original
parent
4732e7071b
commit
46b2e0c54e
|
|
@ -1,3 +1,23 @@
|
|||
// Copyright 2025 Code Philosophy
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
using dnlib.DotNet;
|
||||
using Obfuz.ObfusPasses.SymbolObfus;
|
||||
using System;
|
||||
|
|
@ -75,22 +95,12 @@ static class Program
|
|||
new MemberReorder(9999, new AllowAll()).Process(new List<ModuleDef> { mod2 });
|
||||
var after2 = Snapshot(mod2);
|
||||
|
||||
// exclusions: order must be byte-identical to the original
|
||||
foreach (var key in new[] {
|
||||
"Fx.Colour#f", // enum members
|
||||
"Fx.SeqLayout#f", // [StructLayout(Sequential)]
|
||||
"Fx.ExpLayout#f", // [StructLayout(Explicit)] + [FieldOffset]
|
||||
"Fx.PlainStruct#f", // implicitly sequential struct
|
||||
"Fx.SerialisableData#f", // [Serializable]
|
||||
"Fx.Script#f", // MonoBehaviour serialised fields
|
||||
"Fx.ScriptChild#f", // MonoBehaviour subclass
|
||||
"Fx.SoData#f", // ScriptableObject
|
||||
})
|
||||
"Fx.Colour#f", "Fx.SeqLayout#f", "Fx.ExpLayout#f", "Fx.PlainStruct#f", "Fx.SerialisableData#f", "Fx.Script#f", "Fx.ScriptChild#f", "Fx.SoData#f", })
|
||||
{
|
||||
Check(Same(before[key], after1[key]), "field order pinned: " + key);
|
||||
}
|
||||
|
||||
// virtual methods keep their slot index
|
||||
foreach (var t in new[] { "Fx.BaseVirt", "Fx.DerivedVirt" })
|
||||
{
|
||||
var b = before[t + "#m"];
|
||||
|
|
@ -103,7 +113,6 @@ static class Program
|
|||
Check(ok, "virtual slots pinned: " + t);
|
||||
}
|
||||
|
||||
// pinned-by-policy members keep their index
|
||||
Check(before["Fx.Plain01#f"].IndexOf("f3") == after1["Fx.Plain01#f"].IndexOf("f3"), "policy-pinned field index held");
|
||||
Check(before["Fx.Plain01#m"].IndexOf("C") == after1["Fx.Plain01#m"].IndexOf("C"), "policy-pinned method index held");
|
||||
Check(before["Fx.Plain09#p"].IndexOf("Prop2") == after1["Fx.Plain09#p"].IndexOf("Prop2"), "policy-pinned property index held");
|
||||
|
|
@ -112,12 +121,10 @@ static class Program
|
|||
Check(before["#types"].IndexOf("Fx.BootA") == after1["#types"].IndexOf("Fx.BootA"), "RuntimeInitializeOnLoadMethod type index held");
|
||||
Check(after1["#types"][0] == "<Module>", "<Module> stays at index 0");
|
||||
|
||||
// membership is preserved everywhere
|
||||
bool memberOk = before.Keys.All(k => after1.ContainsKey(k)
|
||||
&& before[k].OrderBy(x => x, StringComparer.Ordinal).SequenceEqual(after1[k].OrderBy(x => x, StringComparer.Ordinal)));
|
||||
Check(memberOk, "no member lost or duplicated");
|
||||
|
||||
// the point of the plan: positional alignment must break, and rotate per seed
|
||||
Check(!Same(before["#types"], after1["#types"]), "type order changed vs original");
|
||||
Check(!Same(after1["#types"], after2["#types"]), "type order differs between seeds");
|
||||
int movedFields = before["Fx.Plain01#f"].Where((n, i) => after1["Fx.Plain01#f"][i] != n).Count();
|
||||
|
|
@ -125,7 +132,6 @@ static class Program
|
|||
int movedMethods = before["Fx.Plain02#m"].Where((n, i) => after1["Fx.Plain02#m"][i] != n).Count();
|
||||
Check(movedMethods > 0, "plain class methods reordered");
|
||||
|
||||
// the reordered module still writes and reloads
|
||||
var outPath = "reordered.dll";
|
||||
mod1.Write(outPath);
|
||||
var reloaded = Load(outPath);
|
||||
|
|
@ -159,7 +165,13 @@ static class Program
|
|||
var enumNames = Enum.GetNames(asm.GetType("Fx.Colour")).ToList();
|
||||
Check(enumNames.SequenceEqual(new[] { "Red", "Green", "Blue", "Alpha", "Cyan", "Magenta" }), "enum member order unchanged at runtime");
|
||||
|
||||
var policy = new AllowAll();
|
||||
foreach (var key in new[] { "Fx2.PlainBase#f", "Fx2.PlainGrand#f", "Fx2.PlainMid#f", "Fx2.SerChild#f", "Fx2.SerLeaf#f" })
|
||||
{
|
||||
Check(Same(before[key], after1[key]), "base-of-serialisable field order pinned: " + key);
|
||||
}
|
||||
Check(before.ContainsKey("Fx2.PlainHost#f") && after1["#types"].Count == before["#types"].Count, "nested-type-bearing types survive the pass");
|
||||
Check(Load(dll).GetTypes().Any(t => t.NestedTypes.Count >= 2), "fixture actually contains a type with multiple nested types");
|
||||
|
||||
int renamed = 0, aligned = 0;
|
||||
foreach (var key in before.Keys)
|
||||
{
|
||||
|
|
@ -174,7 +186,23 @@ static class Program
|
|||
}
|
||||
double rate = renamed == 0 ? 0 : (double)aligned / renamed;
|
||||
Console.WriteLine($"positional recovery of renamed members across two seeds: {aligned}/{renamed} = {rate:P1}");
|
||||
Check(rate < 0.35, "positional differ cannot align renamed members between two builds");
|
||||
Check(rate < 0.35, "ACCEPTANCE plan-06: two consecutive builds cannot be aligned index-for-index");
|
||||
|
||||
int baseRenamed = 0, baseAligned = 0;
|
||||
foreach (var key in before.Keys)
|
||||
{
|
||||
var o = before[key];
|
||||
var a = after1[key];
|
||||
for (int i = 0; i < o.Count; i++)
|
||||
{
|
||||
if (!IsRenamable(key, o[i])) continue;
|
||||
baseRenamed++;
|
||||
if (o[i] == a[i]) baseAligned++;
|
||||
}
|
||||
}
|
||||
double baseRate = baseRenamed == 0 ? 0 : (double)baseAligned / baseRenamed;
|
||||
Console.WriteLine($"positional recovery of renamed members vs the unobfuscated original: {baseAligned}/{baseRenamed} = {baseRate:P1}");
|
||||
Check(baseRate < 0.35, "ACCEPTANCE plan-06: a build cannot be aligned against the unobfuscated original");
|
||||
|
||||
Console.WriteLine(failures == 0 ? "ALL PASS" : failures + " FAILURE(S)");
|
||||
return failures == 0 ? 0 : 1;
|
||||
|
|
|
|||
Loading…
Reference in New Issue