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

So sieht das Handwerksfenster im Spiel aus:

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

. , â , Craft .
â 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 â :

Fracture, :

â int num3 = -10; â .
,
0.7.9 IL2CPP , . IL-, ⌠?
-, No-CD, OllyDbg 10 . , -
, .dll- GameAssembly.dll 55 . , .
dll- Ghidra' , ( Analyze Address Table)

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

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

, , !
Address(RVA = "0x5B9FC0", Offset = "0x5B89C0", VA = "0x1805B9FC0")
VA â ce, :

, .
? , Il2CppDumper , â , ghidra.py script.json, . , , .
, this.modifier.Clear(); this.support.Clear();. . .

â . , CALL NOP
( C, Clear Code Bytes), 90 . !

OneSlotItemContainer$$Clear() Forge() ( , this.main.Clear(); , ).
int num3 = -10; . â , ~60 , , . 15 , .

, ( 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;
}
:

, CALL ItemData$$RollFractureTier, TEST EAX :

, uVar3 < 1. â ( ) JG(Jump short if greater) JLE(Jump short if less or equal).
â . CALL XOR EAX, EAX ( ), NOP'.

! , 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.