Skip to content

Lunar Obfuscation

Decencies edited this page Jan 4, 2023 · 2 revisions

Obfuscation

Lunar uses Name Obfuscation on both their internal Client classes, and Minecraft classes.

This section will cover both sides of the obfuscation:

  • Minecraft classes
  • Lunar classes

Minecraft classes

Minecraft's obfuscation dictionary is: [abehps]{25}

All patched minecraft classes loose their original package, and are placed in one single package, prepended with net/minecraft/, and the corresponding version e.g. v1_7, v1_8 etc.

Luckily, as mentioned in the Patching section, we can retrieve the ProGuard name of a name obfuscated Minecraft class.

Fields

Fields are the simplest to re-map, since they're not rearranged, we can just iterate through the original class, and get the obfuscated field at the same index.

Example:

for (int index = 0; index < originalNode.fields.size() - 1; index++) {
    FieldNode minecraftField = originalNode.fields.get(index);
    FieldNode patchedField = patchedNode.fields.get(index);
    fields.put(patchedField.name, minecraftField);
}

Methods

More than 90% of Minecraft classes can be easily re-mapped by getting the original class (or OptiFine patched class if OptiFine is being used), and running through it method by method.

These Minecraft classes retain their method structure, with the exception of @Overwrite annotated methods, which are moved to the bottom of the class, along with any other Mixin additions.

For that we have a simple remedy.

We can use the @MixinMerged annotation to find which Mixin class this Overwrite belongs to. Once we have the Mixin class, we can simply iterate through every method within the class where the Overwrite lies, if we reach a method that has a @MixinMerged annotation, we check which Mixin that method belongs to. If the method belongs to the same Mixin class as the @Overwrite we can store the index of this method somewhere.

Next we need to offset this index, since the @Overwrite method has been moved to the bottom of the class. We simply subtract the index of the first method from the same mixin in the patched class, from the index of the @Overwrite method we're using.

In cases where the Overwrite method was the first method from the same mixin in the patched class, this will work. However, we must account for when this is not the case.

To make this work, we will now need to iterate through each of the methods in the Mixin class where the @Overwrite belongs, and increment the offset by 1 if we reach a constructor, or if we reach an abstract method (@Shadow) if we reach a method that isn't a constructor or shadow method, we terminate the iteration.

Finally we can add this offset to the index we got from subtracting both indexes.

Here's a Java implementation of this algorithm:

// index of the overwrite within the patched class.
int patchedOverwriteIndex = patched.methods.indexOf(patchedOverwrite);

// the index of the first method from the Mixin class within this patched class.
int firstMemberIndex = 0;

// iterate through all of the methods in the patched class.
for (MethodNode method : patched.methods) {
    // get the Mixin name from the @MixinMerged annotation, if applicable.
    final String mixinName = getMixin(method);
    if (mixinName != null) {
        // if the mixin name of "method" is the same as the Mixin name of the overwrite method.
        if (mixinName.equals(mixin.name)) {
            break;
        }
    }
    firstMemberIndex++;
}

// overwrite method index, pushed back by @Shadow methods.
int mixinMemberIndex = patchedOverwriteIndex - firstMemberIndex;

// skip constructor
int offset = 1;

for (int i = 0; i < mixin.methods.size(); i++) {
    final MethodNode methodNode = mixin.methods.get(i);
    // is the method a @Shadow method?
    if ((methodNode.access & Opcodes.ACC_ABSTRACT) != 0) {
        offset++;
    } 
    // is the method not a constructor?
    else if (!methodNode.name.endsWith("init>")) {
        break;
    }
}

// final index of the overwrite method within the Mixin class.
mixinMemberIndex += offset;

Now that we have the index of where the @Overwrite is in the Mixin class, we can simply reference that method's name and we'll find the original SRG name of the method.

If the method within the Mixin still has a name that matches the Obfuscation dictionary, we can lookup the super-class / interfaces of the Mixin, and search for a method with the same name & find the mapping for that with the same method mentioned above.

Once we have the SRG / ProGuard method name, we can simply iterate through the original class, find the index of the matching method, and place the @Overwrite in the patched class where the method would have been.

Finally, with that out of the way, we can simply iterate through every method and store the Minecraft name in a map.

for (int index = 0; index < originalNode.methods.size() - 1; index++) {
    MethodNode minecraftMethod = originalNode.methods.get(index);
    MethodNode patchedMethod = patchedNode.methods.get(index);
    methods.put(patchedMethod.name + patchedMethod.desc, minecraftMethod);
}

Notes:

We are using the originalNode's count for fields and methods, this is done to ensure we don't get an IndexOutOfBoundsException since Mixins may have added more fields / methods to the patched class, which the original node does not contain.

Lunar classes

Lunar's obfuscation dictionary is very simple: [Il]{25}

Package obfuscation for Bridges is consistent between the same git commit id.

Name Leaks:

Some compiler synthetics expose original Lunar package names / class names.

public class lIllIIllllIIlIIIIllIIlIll$1 {
    public static final int[] $SwitchMap$com$moonsworth$lunar$bridge$util$ClickEventAction;
    public static final int[] $SwitchMap$com$moonsworth$lunar$bridge$util$HoverEventAction;
    ...
}
public class lIIlIlllIllIIIIIIIIIlIllI$1 {
    public static final int[] $SwitchMap$com$moonsworth$lunar$client$event$EventWithResult$EventStateResult;
    ...
}
public class llIIllIllIlIIlIIllIllllll$1 {
    public static final int[] $SwitchMap$com$moonsworth$lunar$v1_8$mods$skyblockaddons$utils$EnumUtils$AnchorPoint;
    ...
}

And, as pointed out by ronan36880, Lunar also left their original class names in their ReplayMod patches on this GitHub repository.

Here's a list of all of the unique resolved names (81)
com.moonsworth.lunar.bridge.BridgeManager
com.moonsworth.lunar.bridge.client.gui.GuiScreenBridge
com.moonsworth.lunar.bridge.client.settings.KeyBindingBridge
com.moonsworth.lunar.bridge.network.PacketBufferBridge
com.moonsworth.lunar.bridge.util.ChatColor
com.moonsworth.lunar.bridge.util.ResourceLocationBridge
com.moonsworth.lunar.client.Lunar
com.moonsworth.lunar.client.assets.packet.asset.AssetPacket
com.moonsworth.lunar.client.debug.DebugType
com.moonsworth.lunar.client.event.Event
com.moonsworth.lunar.client.event.EventBus
com.moonsworth.lunar.client.event.EventCancellable
com.moonsworth.lunar.client.event.EventWithResult
com.moonsworth.lunar.client.event.Listener
com.moonsworth.lunar.client.event.annotation.Listen
com.moonsworth.lunar.client.event.impl.EventState
com.moonsworth.lunar.client.event.impl.gui.EventGuiActionPerformed
com.moonsworth.lunar.client.event.impl.gui.EventGuiScreen
com.moonsworth.lunar.client.event.impl.gui.EventInitGui
com.moonsworth.lunar.client.event.impl.gui.EventPostGuiScreen
com.moonsworth.lunar.client.event.impl.hud.Event2D
com.moonsworth.lunar.client.event.impl.hud.Event3D
com.moonsworth.lunar.client.event.impl.input.EventKeyAction
com.moonsworth.lunar.client.event.impl.input.EventMouseAction
com.moonsworth.lunar.client.event.impl.render.EventCrosshairCheck
com.moonsworth.lunar.client.event.impl.render.EventHotbarCheck
com.moonsworth.lunar.client.event.impl.render.EventRenderHand
com.moonsworth.lunar.client.event.impl.render.EventTextCheck
com.moonsworth.lunar.client.event.impl.world.EventRenderTick
com.moonsworth.lunar.client.event.impl.world.EventTick
com.moonsworth.lunar.client.event.impl.world.entity.EventEntityViewRender
com.moonsworth.lunar.client.event.impl.world.entity.player.EventPlayer
com.moonsworth.lunar.client.feature.mod.replaymod.ReplayMod
com.moonsworth.lunar.client.feature.thirdparty.ThirdPartyMod
com.moonsworth.lunar.client.feature.thirdparty.ThirdPartyMod.EventHandler
com.moonsworth.lunar.client.feature.thirdparty.ThirdPartyMod.Instance
com.moonsworth.lunar.client.feature.thirdparty.events.ModEnableEvent
com.moonsworth.lunar.client.feature.thirdparty.replaymod.ReplayModBridge
com.moonsworth.lunar.client.gui.framework.LCUI
com.moonsworth.lunar.client.gui.framework.components.TextOption
com.moonsworth.lunar.client.gui.framework.font.CFontRenderer
com.moonsworth.lunar.client.gui.framework.mainmenu.MainMenuBaseLCUI
com.moonsworth.lunar.client.gui.framework.mainmenu.MainMenuLCUI
com.moonsworth.lunar.client.gui.notification.Anchor
com.moonsworth.lunar.client.gui.notification.Popup
com.moonsworth.lunar.client.gui.notification.PopupType
com.moonsworth.lunar.client.lang.Translatable
com.moonsworth.lunar.client.logger.Logger
com.moonsworth.lunar.client.management.managers.Fonts
com.moonsworth.lunar.client.options.BooleanOption
com.moonsworth.lunar.client.options.KeyBindOption
com.moonsworth.lunar.client.options.NumberOption
com.moonsworth.lunar.client.options.Option
com.moonsworth.lunar.client.scheduler.LunarScheduler
com.moonsworth.lunar.client.util.BrowserUtil
com.moonsworth.lunar.client.util.ComparableVersion
com.moonsworth.lunar.client.util.MathUtil
com.moonsworth.lunar.client.util.Ref
com.moonsworth.lunar.legacy.input.LegacyKeyboard
com.moonsworth.lunar.todo.Cancelable
com.moonsworth.lunar.todo.ClientRegistry
com.moonsworth.lunar.todo.ComparableVersion
com.moonsworth.lunar.todo.Configuration
com.moonsworth.lunar.todo.CoreModManager
com.moonsworth.lunar.todo.EntityViewRenderEvent
com.moonsworth.lunar.todo.EventBus
com.moonsworth.lunar.todo.EventPriority
com.moonsworth.lunar.todo.FMLClientHandler
com.moonsworth.lunar.todo.FMLCommonHandler
com.moonsworth.lunar.todo.FMLEmbeddedChannel
com.moonsworth.lunar.todo.FMLNetworkEvent
com.moonsworth.lunar.todo.FMLOutboundHandler
com.moonsworth.lunar.todo.FMLProxyPacket
com.moonsworth.lunar.todo.ForgeHooksClient
com.moonsworth.lunar.todo.IFMLLoadingPlugin
com.moonsworth.lunar.todo.InputEvent
com.moonsworth.lunar.todo.LightUtil
com.moonsworth.lunar.todo.NetworkRegistry
com.moonsworth.lunar.todo.RenderGameOverlayEvent
com.moonsworth.lunar.todo.RenderLivingEvent
com.moonsworth.lunar.todo.Side
Clone this wiki locally