## 1 Screenshot

• Tool to reverse engineer Sacred 2 Loka-IDs from Sacred 2 global.res hashes.

It's pretty much just brute-force method, construct all possible hashes from all possible Loka-IDs until we've got something that fits. I see no other possible method at the moment.

Comes with multiple tools to save time depending on your needs. Comes with detailed usage instructions.

EDIT: I have once more forgotten to include lua54.dll which is mandatory, sry for that. You can get this file by downloading any of my other tools though.

Edited by Lindor

• 1
• 1

## User Feedback

Nice that there is such a tool now.  I lack the time to go test it, but I think people who will use it will know what they do. Legally (at lest here) it is not reverse engineering but finding the addresses of items.

Yes, sadly hashes need brute force and intelligence, human or artificial, to 'decipher'. Czevak and me had a discussion 11!!! years back.

Quote

czevak 56

Started conversation: February 10, 2011 · IP

Hi Chattius,

soso die Patentante war mal Serienschauspielerin? Lindenstraße?

Der eigentliche Grund meiner PN ist ein kleines (oder großes) Mathe Problem. Wir haben die global.res von Sacred 2 ja geknackt und können neue texte einfügen. Soweit so gut. Jede Textzeile in der global wird über eine eindeutige Nummer aufgerufen (hash). Diese Nummer berechnet sich aus der entsprechenden LokaID. Wir wissen wie man aus der LokaID den Hash berechnet. Umgekehrt ist das leider schwierig. Wenn ich also wissen will wie z.B. die Bonusbezeichnungen auf die Waffen kommen, muss ich zurückrechnen können wie die LokaID ungefähr aussieht.

Bei vielen LokaIDs wie z.B. Waffen- oder Setnamen ist das einfach wie BLUEPRINT_%s oder SET_%s (%s steht für irgendeine Zahl). Bei den boni ist das nicht so einfach zu erraten.

zur eigentlichen Formel wie sich die Hashes berechnen:

Die Buchstaben der LokaID gehen mit ihren ascii Werten ein (egal ob groß oder kleinbuchstaben). Daraus ergibt sich z.B. für a der Wert 65, b=66 ... z=90. Die Zahlen 0-9 haben die Werte 48-57. Das Spiel berechnet den Hash folgendermaßen:

(Wert 1. Zeichen) * 113 + (Wert 2. Zeichen)

Kommen danach noch weitere Zeichen wird das Endergebnis immer zuerst mit 113 multipliziert und dann der Wert des kommenden Zeichens addiert.

Jetzt kommt das fatale daran: Die speichervariable hat nur 32Bit. Wann immer das Ergebnis 4294967296 übersteigt, wird nur noch mit dem Rest der darüberliegt weitergerechnet.

Ich hab schon probiert durch beispielreihen wieder auf die unglaublich großen ursprünglichen Hashes (Realhash) ohne wegfallen der vielfachen von 4294967296 zurückzurechnen, damit ich dann einfach die rechenoperationen umdrehen kann, aber das ist immer eine Formel mit zwei variablen:

Realhash = X * 4294967296 + Sichtbarer Hash.

Ich kenne weder den Realhash, noch X genau. X ist sicher irgendwie von der Zeichenfolge der LokaID abhängig, aber wie genau verschließt sich mir.

Bei LokaIDs mit 5 Zeichen kann X schon 2 oder 3 sein. Bei 6 Zeichen schon alles zwischen ca. 281 und 389. Darüber wird's schnell unübersichtlich.

Ein paar Hashes zum üben und nachdenken:

aa: 7410

ab: 7411

abcde: 10694172943

UI_BONUS_123: 2435574601

BLUEPRINT_3458: 1245813544 (Realhash=32659581183857795897455909672!)

Vllt. findest du ja irgendeine Möglichkeit. Ich bin mit meinem Latein jdflls. am Ende.

Liebe Grüße,

Mario.

----

Quote

chattius 1,887

Replied: February 14, 2011 · IP

Die Schwierigkeit ist dass Hash-Funktionen verschiedene ursprüngliche Strings auf die gleiche Zahl abbilden können. So dass du nicht explizit zurückrechnen kannst. Vielleicht kann die NSA oder der BND helfen, aber ich sehe nur eine Chance wenn du den Definitionsbereich stark einschränken kannst und dann eine Brute-Force-Attacke fährst.

Wo taucht denn das Problem auf? Das Spiel verwendet den Hash-Wert um in einer Tabelle nachzuschlagen welcher Bonus verwendet wird? Und ihr kennt nur den Hashwert? Kommt der 'gehashte' Text in der Tabelle vor?

Wenn du zum Beispiel die Vermutung hast, dass der Text BLUEPRINT_xxx ist, dann kann man das schnell verifizieren. Richtig vermuteter Textanfang und bis zu 4 folgende Zufallszeichen traue ich mir zu zu knacken.

Wenn x1 bis xn die Zeichen sind, dann wird der Hashcode rekursive berechnet:

H1 = x1

H2 = (H1*113 +x2)mod216

H3 = (H2*113 +x3)mod216

=(x1*1132 + x2*1131+x3*1130)mod216

Du kannst also die Modul-Rechnung ganz am Ende machen. Wenn du jetzt den Anfang richtig rätst machst du eine Fallunterscheidung, dass nur noch ein, zwei, drei oder vier Zeichen folgen.

Annahme es wären 3 Zeichen, du setzt in einer ersten Hash-Berechnung für diese drei Zeichen den Wert 0 ein(nicht das Zeichen 0!). Damit hast du eine Konstante für den geratenen Text.

Nun nimmst du den angezeigten Hashwert und ziehst die Konstante davon ab. Was übrig bleibt ist xn+xn-1*113+xn-2*1132 und sollte eindeutig sein wenn keine ASCII-Zeichen grösser 113 vorkommen.

113<27 und ein Zeichen höchsten 8 Bit lang, sodass das Ergebnis bei 4 Zeichen maximal 3*7 +8 Bits lang sein sollt. 29 bits sind kleiner 32 . Bei 5 Zeichen wären es schon mehr wie 32Bit.

Ich sehe deshalb für mich nur Chancen wenn man schon ziemlich genau weis was der Ursprung war.

P.S.: Wenn meine Kleine in 5 Wochen aus dem Krankenhaus kommt - habt ihr schon an den Pferdenkampfkünsten gebastelt?

...

• 1
21 hours ago, chattius said:

Nice that there is such a tool now.  I lack the time to go test it, but I think people who will use it will know what they do. Legally (at lest here) it is not reverse engineering but finding the addresses of items.

Der reverse engineering part war, sich anzusehen, wie hashes aus den Loka-IDs konstruiert werden. Bequemerweise kann man mit s2rw seine eigenen loka-ID - hash - paare erzeugen, im Grunde hab ich also rekonstruiert, wie s2rw arbeitet. Aber du hast natürlich recht, die Hauptarbeit, die hier geleistet wird, ist Brute-Force und nicht reverse-engineering Es gibt noch viel Optimierungsbedarf, an Boni hab' ich z.B. überhaupt nicht gedacht, wer also Boni-hashes rekonstruieren will, muss die langsame Methode wählen.

Was das eigentliche Problem angeht: Im Grunde ist das ein zahlentheoretisches Problem. Der Realhash ist eine Zahl in Basis 113 mit den ASCII-codes als Ziffern. Zusätzlich zu dieser Formel:

Realhash = X * 4294967296 + Sichtbarer Hash

Muss man dann noch folgendes berücksichtigen:

Realhash = sum zI * 113 ^ I from I = 0 to n, wobei n die Anzahl der Ziffern ist minus 1 und zI dann der ASCII-wert.

Wenn man so eine Zahl dann in basis 2 umrechnet, kann man zurückrechnen, was die einzelnen Ziffern zum sichtbaren hash beitragen. Dabei kommt man, abhängig von der Anzahl der Ziffern, auf ein Ergebnis von wenigen hundert Möglichkeiten für die Ziffernverteilung. Das ganze ist extrem kompliziert und ein Teilgebiet der Mathematik, mit dem ich mich quasi noch nicht auseinandergesetzt habe, theoretisch aber machbar.

Ich gebe mal ein Beispiel an: Angenommen, die hashbasis wäre nicht 113 sondern 10, und wir hätten einen Realhash von 13, wobei das maximum 16 wäre, also
Realhash = X * 16 + Sichtbarer Hash
Wenn wir zwei Ziffern annehmen, 10a+b in basis 10, dann ist das binär:
1010 * a + b = X * 10000 + 1101  =>  1010 * a + b - 1101 = x * 10000

Jetzt geht man Ziffernweise von rechts nach links vor für alle Ziffern, die auf der rechten Seite bekannt  (also null) sind. Wir wissen, dass -1<a, b<10, d.h. in binär haben a und b maximal vier Ziffern:

```1010 * (a1a2a3a4) + (b1b2b3b4) - 1101 =  x * 10000
=> 1000000 * (a1) + 100000 * (a2) + 10000 * (a1 + a3) + 1000 * (a2 + a4 + b1 - 1) + 100 * (a3 + b2 - 1) + 10 * (a4 + b3) + 1 * (b4 - 1) =  x * 10000
=> 1000 * (a2 + a4 + b1 - 1) + 100 * (a3 + b2 - 1) + 10 * (a4 + b3) + 1 * (b4 - 1) =  0

=> b4 = 1
=> 1000 * (a2 + a4 + b1 - 1) + 100 * (a3 + b2 - 1) + 10 * (a4 + b3) =  0

=> a4 = b3 = 0
=> 1000 * (a2 + b1 - 1) + 100 * (a3 + b2 - 1) =  0

=> a3 = 1 - b2 = 0
=> 1000 * (a2 + b1 - 1) =  0

=> a2 = 1 - b1 = 0
=> a = x000, b = 1101
=> a = 0 or 8, b = 13 TOO BIG

=> a2 = 1 - b1 = 1
=> a = x100, b = 0101
=> a = 4, b = 5

=> a3 = 1 - b2 = 1
=> 1000 * (a2 + b1 - 1) =  0

=>a2 = 1 - b1 = 0
=>a = x010, b = 1001
=>a = 2, b = 9

=>a2 = 1 - b1 = 1
=>a = x110, b = 0001
=>a = 6, b = 1

=> a4 = b3 = 1
=> 1000 * (a2 + b1) + 100 * (a3 + b2) =  0

=> a3 = b2 = 0
=> 1000 * (a2 + b1)

=> a2 = b1 = 0
=> a=x001, b=0011
=> a=1 or 9, b=3

=> a2 = b1 = 1
=> a=x101, b=1011
=> a=5, b=11 TOO BIG

=> a3 = b2 = 1
=> 1000 * (a2 + b1 + 1)

=> a2 = 1 - b1 = 0
=> a=x011, b=1111
=> a=3, b=15 TOO BIG

=> a2 = 1 - b1 = 1
=> a=x111, b=0111
=> a=7, b=7```

Wir haben also 8 a-b-paare, wobei die erste Ziffer von a meistens unbekannt ist. Manchmal können wir sie aber trotzdem erschließen, weil a ja nur 0-9 sein kann. Es bleiben die Möglichkeiten

45, 29, 61, 13, 93 und 77

übrig.

Für basis 113 und mit mehr Stellen wird das ganze natürlich VIEEEL massiver und es bleiben einige 100 wenn nicht 1000 Optionen, aber generell würde der Algorithmus so funktionieren.

Edited by Lindor

A couple of notes:

• I forgot to include lua54.dll in the upload. Updated descriptions with instructions how to get it.
• Sorry for replying in german yesterday. Mathematics is a universal language though, hope that's enough for to be clear what I mean
• 17 hours ago, Lindor said:

Was das eigentliche Problem angeht: Im Grunde ist das ein zahlentheoretisches Problem

While I would totally be able to construct such an algorithm and compile it in an exe and do actual reverse engineering of Loka-IDs, the practicality of it would not be there because this right here:

17 hours ago, Lindor said:

einige 100 wenn nicht 1000 Optionen

was a huge understatement. I did the math on this once:

On 6/30/2022 at 11:11 PM, Lindor said:

but already a 7-letter-word needs to hold at least 3.736.842.880 duplicates. And that doesn't even count numbers and all the other symbols.

Now imagine the range of duplicates on an actual Loka-ID (~40-60 characters) with numbers and "_" sybols included. I can't even estimate a number, but I bet it would be more than the number of atoms in the visible universe.