Letzte Epoche ändern - Von dnSpy zu Ghidra

Last Epoch ist ein Einzelspieler-ARPG, das auf Unity und C # basiert. Das Spiel hat ein Handwerkssystem - der Spieler findet Modifikatoren, die er dann auf die Ausrßstung anwendet. Mit jedem Modifikator sammelt sich "Instabilität" an, was die Wahrscheinlichkeit erhÜht, dass der Gegenstand zerbrochen wird



Ich habe zwei Ziele verfolgt:



  • Entfernen Sie "Bruch" eines Elements durch Anwenden von Modifikatoren
  • Verwenden Sie beim Basteln keine Modifikatoren


Schnitt



So sieht das Handwerksfenster im Spiel aus:



Handwerksfenster der letzten Epoche



Teil eins, in dem wir .NET-Code ohne Registrierung und SMS bearbeiten



Zunächst werde ich den Prozess der Änderung der alten Version des Spiels (0.7.8) beschreiben.



C# IL (Intermediate Language) . IL- . Unity IL- <GameFolder>/Managed/Assembly-CSharp.dll



IL- dnSpy — .NET, . dnSpy .NET , IDE.





, dnSpy Assembly-CSharp.dll



dnSpy mit geĂśffneter Assembly-CSharp.dll



. , — , Craft .



— CraftingSlotManager:



dnSpy x CraftingSlotManager



Forge() :



ndSpy x CraftingSlotManager.Forge ()



// CraftingSlotManager
// Token: 0x06002552 RID: 9554 RVA: 0x0015E958 File Offset: 0x0015CB58
public void Forge()
{
    if (!this.forging)
    {
        this.forging = true;
        base.StartCoroutine(this.ForgeBlocker(10));
        bool flag = false;
        int num = -1;
        if (this.main.HasContent())
        {
            int num2 = 0;
            int num3 = 0;
            if (this.debugNoFracture)
            {
                num3 = -10;
            }
            float num4 = 1f;
            int num5 = -1;
            bool flag2 = false;
            ItemData data = this.main.GetContent()[0].data;
            ItemData itemData = null;
            if (this.support.HasContent())
            {
                itemData = this.support.GetContent()[0].data;
                num5 = (int)itemData.subType;
                if (itemData.subType == 0)
                {
                    num3--;
                    flag2 = true;
                }
                else if (itemData.subType == 1)
                {
                    num4 = UnityEngine.Random.Range(0.4f, 1f);
                    flag2 = true;
                }
            }
            if (this.appliedAffixID >= 0)
            {
                Debug.Log("applied ID: " + this.appliedAffixID.ToString());
                if (this.forgeButtonText.text == "Forge")
                {
                    if (data.AddAffixTier(this.appliedAffixID, Mathf.RoundToInt((float)(5 + num2) * num4), num3))
                    {
                        num = this.appliedAffixID;
                        flag = true;
                    }
                    GlobalDataTracker.instance.CheckForShard(this.appliedAffixID);
                    if (flag2)
                    {
                        this.support.Clear();
                    }
                    if (!GlobalDataTracker.instance.CheckForShard(this.appliedAffixID))
                    {
                        this.DeselectAffixID();
                    }
                }
            }
            else if (this.modifier.HasContent())
            {
                Debug.Log("modifier lets go");
                ItemData data2 = this.modifier.GetContent()[0].data;
                if (data2.itemType == 102)
                {
                    if (data2.subType == 0)
                    {
                        Debug.Log("shatter it");
                        Notifications.CraftingOutcome(data.Shatter());
                        if (num5 == 0)
                        {
                            flag2 = false;
                        }
                        this.main.Clear();
                        flag = true;
                        this.ResetAffixList();
                    }
                    else if (data2.subType == 1)
                    {
                        Debug.Log("refine it");
                        if (data.AddInstability(Mathf.RoundToInt((float)(2 + num2) * num4), num3, 0))
                        {
                            data.ReRollAffixRolls();
                        }
                        flag = true;
                    }
                    else if (data2.subType == 2 && data.affixes.Count > 0)
                    {
                        Debug.Log("remove it");
                        if (data.AddInstability(Mathf.RoundToInt((float)(2 + num2) * num4), num3, 0))
                        {
                            ItemAffix affixToRemove = data.affixes[UnityEngine.Random.Range(0, data.affixes.Count)];
                            data.RemoveAffix(affixToRemove);
                        }
                        flag = true;
                    }
                    else if (data2.subType == 3 && data.affixes.Count > 0)
                    {
                        Debug.Log("cleanse it");
                        List<ItemAffix> list = new List<ItemAffix>();
                        foreach (ItemAffix item in data.affixes)
                        {
                            list.Add(item);
                        }
                        foreach (ItemAffix affixToRemove2 in list)
                        {
                            data.RemoveAffix(affixToRemove2);
                        }
                        if (num5 == 0)
                        {
                            flag2 = false;
                        }
                        data.SetInstability((int)((Mathf.Clamp(UnityEngine.Random.Range(5f, 15f), 0f, (float)data.instability) + (float)num2) * num4));
                        flag = true;
                    }
                    else if (data2.subType == 4 && data.sockets == 0)
                    {
                        Debug.Log("socket it");
                        data.AddSocket(1);
                        data.SetInstability((int)((Mathf.Clamp(UnityEngine.Random.Range(5f, 15f), 0f, (float)data.instability) + (float)num2) * num4));
                        flag = true;
                    }
                }
            }
            if (flag)
            {
                UISounds.playSound(UISounds.UISoundLabel.CraftingSuccess);
                if (this.modifier.HasContent())
                {
                    ItemData data3 = this.modifier.GetContent()[0].data;
                    this.modifier.Clear();
                    if (num >= 0 && GlobalDataTracker.instance.CheckForShard(num))
                    {
                        this.PopShardToModifierSlot(num);
                    }
                    else if (data3.itemType == 102)
                    {
                        foreach (SingleSubTypeContainer singleSubTypeContainer in ItemContainersManager.instance.materials.Containers)
                        {
                            if (singleSubTypeContainer.CanAddItemType((int)data3.itemType) && singleSubTypeContainer.allowedSubID == (int)data3.subType && singleSubTypeContainer.HasContent())
                            {
                                singleSubTypeContainer.MoveItemTo(singleSubTypeContainer.GetContent()[0], 1, this.modifier, new IntVector2?(IntVector2.Zero), Context.SILENT);
                                break;
                            }
                        }
                    }
                    if (num >= 0 && this.prefixTierVFXObjects.Length != 0 && this.suffixTierVFXObjects.Length != 0)
                    {
                        ItemData itemData2 = null;
                        if (this.main.HasContent())
                        {
                            itemData2 = this.main.GetContent()[0].data;
                        }
                        if (itemData2 != null && this.main.HasContent())
                        {
                            List<ItemAffix> list2 = new List<ItemAffix>();
                            List<ItemAffix> list3 = new List<ItemAffix>();
                            foreach (ItemAffix itemAffix in itemData2.affixes)
                            {
                                if (itemAffix.affixType == AffixList.AffixType.PREFIX)
                                {
                                    list2.Add(itemAffix);
                                }
                                else
                                {
                                    list3.Add(itemAffix);
                                }
                            }
                            for (int i = 0; i < list2.Count; i++)
                            {
                                if ((int)list2[i].affixId == num && this.prefixTierVFXObjects[i])
                                {
                                    this.prefixTierVFXObjects[i].SetActive(true);
                                }
                            }
                            for (int j = 0; j < list3.Count; j++)
                            {
                                if ((int)list3[j].affixId == num && this.suffixTierVFXObjects[j])
                                {
                                    this.suffixTierVFXObjects[j].SetActive(true);
                                }
                            }
                        }
                    }
                }
                if (!flag2)
                {
                    goto IL_6B3;
                }
                this.support.Clear();
                using (List<SingleSubTypeContainer>.Enumerator enumerator2 = ItemContainersManager.instance.materials.Containers.GetEnumerator())
                {
                    while (enumerator2.MoveNext())
                    {
                        SingleSubTypeContainer singleSubTypeContainer2 = enumerator2.Current;
                        if (singleSubTypeContainer2.CanAddItemType((int)itemData.itemType) && singleSubTypeContainer2.allowedSubID == (int)itemData.subType && singleSubTypeContainer2.HasContent())
                        {
                            singleSubTypeContainer2.MoveItemTo(singleSubTypeContainer2.GetContent()[0], 1, this.support, new IntVector2?(IntVector2.Zero), Context.SILENT);
                            break;
                        }
                    }
                    goto IL_6B3;
                }
            }
            this.modifier.Clear();
            this.support.Clear();
        }
        IL_6B3:
        if (!flag)
        {
            UISounds.playSound(UISounds.UISoundLabel.CraftingFailure);
        }
        this.slamVFX.SetActive(true);
        this.UpdateItemInfo();
        this.UpdateFractureChanceDisplay();
        this.UpdateForgeButton();
        ShardCountText.UpdateAll();
    }
}


. , ( num1, num2...). , .



— CraftingSlot', . CraftingSlotManager .





: this.modifier this.support



, .



:



this.modifier.Clear();
this.support.Clear();


, ( , , ) — .



this.modifier.Clear(); this.support.Clear();



dnSpy — .dll — :



dnSpy-Bearbeitungsmethode





Fracture, :



dnSpy x CraftingSlotManager.Forge ()



— int num3 = -10; — .



,



0.7.9 IL2CPP , . IL-, … ?



-, No-CD, OllyDbg 10 . , -





, .dll- GameAssembly.dll 55 . , .



dll- Ghidra' , ( Analyze Address Table)



Ghidra



, , , — .



IL2CPP Il2CppDumper, - ( <GameFolder>/il2cpp_data/Metadata/global-metadata.dat). , .



dll :



Il2CppDumper-Ausgabe



DummyDll dll- IL-. Assembly-CSharp.dll dnSpy CraftingSlotManager:



dnSpy (wiederhergestellt) x CraftingSlotManager



, , !



Address(RVA = "0x5B9FC0", Offset = "0x5B89C0", VA = "0x1805B9FC0")


VA — ce, :



Ghidra Schmiedeversatz



, .



? , Il2CppDumper , — , ghidra.py script.json, . , , .





, this.modifier.Clear(); this.support.Clear();. . .



Ghidra



— . , CALL NOP



( C, Clear Code Bytes), 90 . !



Ghidra



OneSlotItemContainer$$Clear() Forge() ( , this.main.Clear(); , ).





int num3 = -10; . — , ~60 , , . 15 , .



Ghidra



, ( 4 MOVZX AND), . , .



( ) dnSpy , "" AddInstability



public bool AddInstability(int addedInstability, int fractureTierModifier = 0, int affixTier = 0)
    {
        int num = this.RollFractureTier(fractureTierModifier, affixTier);
        if (num > 0)
        {
            this.Fracture(num); // <-----   
            return false;
        }
        this.instability = ((int)this.instability + addedInstability).clampToByte();
        this.RebuildID();
        return true;
    }


:



Ghidra



, CALL ItemData$$RollFractureTier, TEST EAX :



Ghidra



, uVar3 < 1. — ( ) JG(Jump short if greater) JLE(Jump short if less or equal).



— . CALL XOR EAX, EAX ( ), NOP'.



Ghidra



! , GameAssembly.dll (- .bin ) .





" ", , .



In der Realität werden viele populäre Sprachen zu Zwischencode kompiliert, der von Profildekompilierern hervorragend interpretiert und modifiziert wird. Fßr solche Modifikationen reichen oft die ßblichen Programmierkenntnisse aus.



Während native Binärdateien für Ihre Augen und Ihr Gehirn gefährlich sein können, reicht ein oberflächliches Wissen darüber, wie Programme auf einer Hardware-Ebene funktionieren, in Verbindung mit modernen Open-Source-Tools häufig für geringfügige Änderungen aus.




All Articles