checksum einer save-funktion knacken (warcraft 3)

Dieses Thema im Forum "Sicherheit & Datenschutz" wurde erstellt von lachvogel, 20. Oktober 2007 .

Status des Themas:
Es sind keine weiteren Antworten möglich.
  1. 20. Oktober 2007
    Zuletzt von einem Moderator bearbeitet: 14. April 2017
    hi leute..

    bin nicht sicher obs hier richtig ist, hoffs mal schon..

    es geht darum die save funktion einer map zu knacken.. (Final Fantasy - Epic RPG v0.7.2 ) (es gibt ne neue version, aber die save-funktion ist dieselbe)

    naja es funktioniert so:

    man gibt ein "-save"

    es wird ein string generiert aus 30 zahlen

    der sieht zb so aus:

    1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-0-0-0-0-0-0-0-24-8-4028

    also habe ich mal die map mit einem deprotector aufgemacht um herauszufinden wie dieser save skript funktioniert. folgendes hab ich dabei rausbekommen:

    • die esten 20 zahlen speichern den lvl/stufe für jeden der 20 berufe (wie genau das aufgebaut ist weiss ich noch nicht)
    • #21 zeigt dir an wie viel gold du momentan besitzt (holz hat es in dieser map nicht, nur diamanten, und die werden in gold umgewandelt)
    • #22-27 speichern je einen gegenstand in deinem inventar (max 6 gegenstände)
    • #28 ist eine zufallszahl zwischen 20 und 40
    • #29 ist eine zufallszahl zwischen 3 und 8
    • #30 ist eine art checksum, die will ich herausfinden wie die berechnet wird

    wie man sieht, hat mein charakter kein gold und keine gegenstände. zudem hab ich noch keinen beruf ausgeübt. das wären also alles startwerte.

    nun meine frage: wie soll ich weiter vorgehen? muss ich jetzt JASS lernen? (eine programiersprache womit man glaubs sachen im warcraft 3 map editor programieren (^^) kann)

    hab schon versucht eine tabelle zu erstellen wo dann je eine zufallszahl eine achse bildete, um zu schauen obs da irgendeinen zusammenhang gibt.. man merkt schon dass sie zusammenhängen, aber eher generell.. also einfach je grösser die zufallszahlen, desto grösser die checksum.. also es ist noch nicht vollständig aber falls das jemandem helfen würde könnte ich das kurz hochladen..

    was meint ihr leute?

    edit hier noch die (unvollständige ) tabelle:

    also das ist alles mit nullwerten, und ich nehme stark an, dass der spielername auch irgendwie im speicehrcode eingebaut worden ist, damit man das nicht untereinander austauschen kann..

    habe auch mit dem namen Lachvogel gespielt, also falls das jemandem was nützt..

    und es kamen teilweise auch die selben zahlen heraus (farbig)


    Bild

    ich danke euch jetzt schon für eure hilfe, und falls was unklar ist, einfach fragen

    thx! mfg lachvogel


    edit:// habs selber hinbekommen! ^^ naja danke an die leute dies wenigstens mal angeschaut haben..

    falls es wen interessiert, pn..

    **closed**
     
  2. 27. März 2010
    AW: checksum einer save-funktion knacken (warcraft 3)

    Habe eine Anfrage bekommen wegen dem Thread (ist ne Ewigkeit her, trotzdem habe ich mir die Mühe gemacht zu erläutern, und diese Mühe will ich euch nicht vorenthalten )

    So, es ging um eine Analyse des Scripts, dass den -save code generiert.

    Dazu hab ich das Programm "Deprotect" benützt mit dem beinhalteten MPQ Editor. Damit kann man die .map Datei durchstöbern, meistens ist eine "deprotect.txt" schon vorhanden, worin die innere Struktur der .map Datei schon angegeben ist. Im Fall dieser map war das so, ansonsten ermöglicht dir das Programm Deprotect die Aufhebung von möglichen Versteck-mechanismen, die es schwieriger machen, den Code der map zu klauen/analysieren und so was.. (glaubs...)


    Naaaja auf jeden Fall suchen wir eine script-artige Datei, die die Funktion beinhaltet, unseren -save code zu generieren. Den -load kann man analysieren und herauszufinden welche Zahlen zu welchen Gegenständen usw gehören.

    Ich werde anschliessend das ganze auch anhand eines Tests mit einer Check-Summe aus meiner Tabelle demonstireren.


    Im Fall der FF Epic 0.7.2 map fand ich in "/scripts" die "war3map.j" Datei.

    Die Erläuterung gewisser Funktionen die ich angetroffen habe fand ich hier:
    LINK: Warcraft 3 JASS Wiki

    Nachdem man die .j Datei extrahiert (R-Click -> Extract) Findet man die im Work ordner zum durchsuchen.

    Darin such ich mal nach "save" und finde heraus, was alles in dem Zusammenhang gescriptet ist.

    Sachen wie: ... könnten später mal nützlich sein, von dem her zeige ich ein Beispiel für interessante Info:

    Code:
    integer udg_Total_Items_to_Save=0

    Weiterhin ist es sicher auch vorteilhaft nach sachen wie "code" "password" o.ä. zu suchen. So kam ich schliesslich auf folgende Zeilen:


    Code:
    function Trig_Save_Actions takes nothing returns nothing
    set udg_FinalPassword=""
    set udg_i=(1+GetPlayerId(GetTriggerPlayer()))
    set udg_temp_Integer8=0
    set udg_PasswordParts[1]=I2S(GetHeroLevel(udg_Squire[udg_i]))
    set udg_PasswordParts[2]=I2S(GetHeroLevel(udg_Knight[udg_i]))
    set udg_PasswordParts[3]=I2S(GetHeroLevel(udg_Archer[udg_i]))
    set udg_PasswordParts[4]=I2S(GetHeroLevel(udg_Monk[udg_i]))
    set udg_PasswordParts[5]=I2S(GetHeroLevel(udg_Thief[udg_i]))
    set udg_PasswordParts[6]=I2S(GetHeroLevel(udg_Lancer[udg_i]))
    set udg_PasswordParts[7]=I2S(GetHeroLevel(udg_Geomancer[udg_i]))
    set udg_PasswordParts[8]=I2S(GetHeroLevel(udg_Samurai[udg_i]))
    set udg_PasswordParts[9]=I2S(GetHeroLevel(udg_Ninja[udg_i]))
    set udg_PasswordParts[10]=I2S(GetHeroLevel(udg_HolySwordsman[udg_i]))
    set udg_PasswordParts[11]=I2S(GetHeroLevel(udg_Chemist[udg_i]))
    set udg_PasswordParts[12]=I2S(GetHeroLevel(udg_Wizard[udg_i]))
    set udg_PasswordParts[13]=I2S(GetHeroLevel(udg_Priest[udg_i]))
    set udg_PasswordParts[14]=I2S(GetHeroLevel(udg_Summoner[udg_i]))
    set udg_PasswordParts[15]=I2S(GetHeroLevel(udg_TimeMage[udg_i]))
    set udg_PasswordParts[16]=I2S(GetHeroLevel(udg_Mediator[udg_i]))
    set udg_PasswordParts[17]=I2S(GetHeroLevel(udg_Oracle[udg_i]))
    set udg_PasswordParts[18]=I2S(GetHeroLevel(udg_Calculator[udg_i]))
    set udg_PasswordParts[19]=I2S(GetHeroLevel(udg_Bard[udg_i]))
    set udg_PasswordParts[20]=I2S(GetHeroLevel(udg_Sorcerer[udg_i]))


    Dazu eine knappe Erklärung: I2S steht für Integer to String (Zahl->Text), denn sowas muss immer definiert werden, damit wc3 das auch versteht.
    S2I, was du beim -load code finden würdest steht also für String to Integer (Text->Zahl)


    Weiterhin erkennen wir, dass es sowas die ein "FinalPassword" gibt (wohl unser code, dass uns angezeigt wird), und, dass unser code aus mehreren "PasswordParts" besteht, die oben definiert sind. Diese interessieren uns momentan nicht, denn wir können die Funktionsweise erraten, anhand von der Tatsache, dass die "Zahl im -save code" = "gespeicherte Level des Berufs" + "Nummerierung des PasswordParts"


    (Bsp: Wollte ich Summoner Level 13 sein, würde in meinem -save code an der 14 Stelle: x = 13 + 14 -> 27 eingeben.)


    Naja nachdem wir diese ersten 20 Teile ausfindig machen konnten, machen wir uns auf der Suche nach dem Rest, indem wir nach "PasswordParts" suchen, denn so heissen offenbar unsere gesuchten Teile.


    Als nächstes erscheint die PasswordPart[21]
    (Gold + Wertsachen die in Gold umtauschbar sind + Diamanten(PLAYER_STATE_RESOURCE_LUMBER) und so)


    Code:
    set udg_temp_Integer8=(udg_temp_Integer8+(100*GetPlayerTechCountSimple('Resi',GetTriggerPlayer())))
    set udg_temp_Integer8=(udg_temp_Integer8+(200*GetPlayerTechCountSimple('R00F',GetTriggerPlayer())))
    set udg_temp_Integer8=(udg_temp_Integer8+(300*GetPlayerTechCountSimple('R00G',GetTriggerPlayer())))
    set udg_temp_Integer8=(udg_temp_Integer8+(500*GetPlayerTechCountSimple('R00H',GetTriggerPlayer())))
    set udg_temp_Integer8=(udg_temp_Integer8+(4500*GetUnitAbilityLevelSwapped('A058',udg_SpiritOfGaya[(1+GetPlayerId(GetTriggerPlayer()))])))
    set udg_temp_Integer8=(udg_temp_Integer8+(4500*GetUnitAbilityLevelSwapped('S004',udg_SpiritOfGaya[(1+GetPlayerId(GetTriggerPlayer()))])))
    set udg_temp_Integer8=(udg_temp_Integer8+(4500*GetUnitAbilityLevelSwapped('A07E',udg_SpiritOfGaya[(1+GetPlayerId(GetTriggerPlayer()))])))
    set udg_temp_Integer8=(udg_temp_Integer8+(1500*GetPlayerState(GetTriggerPlayer(),PLAYER_STATE_RESOURCE_LUMBER)))
    set udg_PasswordParts[21]=I2S(udg_temp_Integer8)


    Weiter unten ist der Code der die Items speichert. Dabei werden weitere Funktionen gerufen und ich erspare mir jetzt genauer darauf einzugehen.


    Code:
    loop
    exitwhen bj_forLoopAIndex>bj_forLoopAIndexEnd
    set udg_temp_Integer6=0
    if (Trig_Save_Func054Func002001()) then
    set udg_temp_Integer6=0
    endif
    set bj_forLoopBIndex=1
    set bj_forLoopBIndexEnd=udg_Total_Items_to_Save
    loop
    exitwhen bj_forLoopBIndex>bj_forLoopBIndexEnd
    if (Trig_Save_Func054Func003Func001001()) then
    set udg_temp_Integer6=bj_forLoopBIndex
    endif
    set bj_forLoopBIndex=bj_forLoopBIndex+1
    endloop
    set udg_PasswordParts[(21+bj_forLoopAIndex)]=I2S(udg_temp_Integer6)
    set bj_forLoopAIndex=bj_forLoopAIndex+1
    endloop


    Darauf hin unsere 2 Random Zahlen:


    Code:
    set udg_PasswordParts[28]=I2S(GetRandomInt(20,40))
    set udg_PasswordParts[29]=I2S(GetRandomInt(3,8))
    Und ENDLICH haben wir was von einer Checksum entdeckt:


    Code:
    set udg_chksm_curr_value=0

    und weiter unten steht dann sowas wie:


    Code:
    set udg_PasswordParts[30]=I2S(udg_chksm_curr_value)


    UND JETZT... fängt es an. Wir schauen mal was passiert mit der udg_chksm_curr_value ( = momentaner Wert der Checksum )


    Code:
    set udg_chksm_curr_value=0
    set bj_forLoopAIndex=1
    set bj_forLoopAIndexEnd=29
    loop
    exitwhen bj_forLoopAIndex>bj_forLoopAIndexEnd
    set udg_chksm_curr_value=(udg_chksm_curr_value+S2I(udg_PasswordParts[bj_forLoopAIndex]))
    set bj_forLoopAIndex=bj_forLoopAIndex+1
    endloop


    Zuerst werden wohl alle Zahlen im PasswordParts zusammen addiert, und temporär gespeichert. Dafür ist dieser erste Loop zuständig.

    Nächste Zeilen:


    Code:
    set bj_forLoopAIndex=1
    set bj_forLoopAIndexEnd=29
    loop
    exitwhen bj_forLoopAIndex>bj_forLoopAIndexEnd
    set udg_chksm_curr_value=(udg_chksm_curr_value+(S2I(udg_PasswordParts[bj_forLoopAIndex])*bj_forLoopAIndex))
    set bj_forLoopAIndex=bj_forLoopAIndex+1
    endloop

    In einem nächsten Schritt werden die Werte im PasswordParts mit i multipliziert, und zu der CheckSum addiert.


    Nächste Zeilen:


    Code:
    set bj_forLoopAIndex=1
    set bj_forLoopAIndexEnd=StringLength(I2S(udg_chksm_curr_value))
    loop
    exitwhen bj_forLoopAIndex>bj_forLoopAIndexEnd
    set udg_chksm_curr_value=(udg_chksm_curr_value+S2I(SubStringBJ(I2S(udg_chksm_curr_value),bj_forLoopAIndex,bj_forLoopAIndex)))
    set bj_forLoopAIndex=bj_forLoopAIndex+1
    endloop


    Diesmal haben wir einen Loop, dass aufhört, nachdem es soviel mal geloffen ist, wie Anzahl Ziffern der String unserer chksm enthält.


    Dabei fällt die Funktion "SubStringBJ" auf, diese war mir unbekannt, aber ich konnte sie nach kurzem googlen ausfindig machen:
    SubString spuckt nur einen Teil einer String aus, diesen Teil wird definiert mit einer "von dieser Stelle aus" (obligatorische Angabe) und einer "bis hier" (freiwillige Angabe).


    Hier die Beschreibung der JASS wikia Seite:


    -----
    SubString( string source, integer start, integer end )
    returns String
    -
    Gets part of a string. The first character of a string's position value is an integer of 1.

    -----


    Das heisst für unsere Funktion, dass wir zu der CheckSum jeweils die Ziffern der Checksum zwischen bj_forLoopAIndex & bj_forLoopAIndex (= Ziffer an der Stelle i) zu der CheckSum addieren.




    Endlich die letzte Zeilen:



    Code:
    set udg_chksm_curr_value=(udg_chksm_curr_value-ModuloInteger(S2I(udg_PasswordParts[28]),S2I(udg_PasswordParts[29])))
    set udg_PasswordParts[30]=I2S(udg_chksm_curr_value)


    Die Modulo Funktion ist etwas komisch zu erklären. Bei einer Division zweier ganzen Zahlen, geht es entweder gerade auf, oder es bleibt ein Rest übrig. Zum Beispiel:



    24 : 3 = 8 (rest 0)

    24 : 4 = 6 (rest 0)

    24 : 5 = 4 (rest 4)

    24 : 6 = 4 (rest 0)

    24 : 7 = 3 (rest 3)

    24 : 8 = 3 (rest 0)

    24 : 9 = 2 (rest 6)

    24 : 10 = 2 (rest 4)



    Die Modulo Funktion verlangt ein Dividend und einen Divisor und spuckt dir den Rest aus. Es kann also auch 0 ausgespuckt werden usw..

    (Dividend = Zähler, Divisor = Nenner) Anschliessend subtrahiert man dieser Rest von der CheckSum.



    Also macht unsere Funktion folgendes: CheckSum = CheckSum - Modulo[ Zufallszahl(20bis40), Zufallszahl(3bis8 ) ]


    Fertig! Unsere Checksum wurde vollständig analysiert.



    Ich werde jetzt noch probieren ein Beispiel aus dem Forum zu übernehmen und zu überprüfen.



    Hier ein Beispiel -save code (Note, habe das Spiel nicht installiert, kann das also nicht überprüfen, sollte aber funktionieren.)

    (Habe den Code aus meiner Tabelle genommen)



    1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-0-0-0-0-0-0-0-27-6-4049



    Checksumme am Anfang: 0


    1 Schritt:

    "Zuerst werden wohl alle Zahlen im PasswordParts zusammen addiert, und temporär gespeichert."


    1+2+3+...+27+6 = 243

    Checksumme momentan: 243


    2 Schritt:

    "In einem nächsten Schritt werden die Werte im PasswordParts mit i multipliziert, und zu der CheckSum addiert."

    Also nihmst du 1*[1] + 2*[2] + 3*[3] + ... + 27*[28] + 6*[29] und addierst das zu der CheckSum. (Am besten mit einer Excel Tabelle oder sowas, um das rechnen zu übernehmen ^^) Ich bin hier auf 3800 gekommen.

    Checksumme momentan: 243 + 3800 = 4043


    3 Schritt:

    "Das heisst für unsere Funktion, dass wir zu der CheckSum jeweils die Ziffern der Checksum an der Stelle i zu der CheckSum addieren."


    Also nehmen wir die erste Stelle der Zahl (grösste Stelle) und addieren diese, dann die zweite Stelle usw.

    -> 4043 + 4 = 4047

    -> 4047 + 0 = 4047

    -> 4047 + 4 = 4051

    -> 4051 + 1 = 4052

    Checksumme momentan = 4052


    4 Schritt:

    "Die Modulo Funktion verlangt ein Dividend und einen Divisor und spuckt dir den Rest aus. Anschliessend subtrahiert man dieser Rest von der CheckSum."

    Teilt man 27 durch 6 erhält man 4.5. - 6 geht also 4 ganze mal in 27, und uns bleibt ein Rest von 3.

    6*4 = 24 ; 27-24 = 3


    Checksumme FINAL = 4052 - 3 = 4049



    Werfen wir einen Blick auf unsere Tabelle können wir bestätigen, dass wir richtig gerechnet haben.

    Sooo, werde nun wieder schliessen, allerdings bin ich weiterhin gerne bereit Auskunft zu geben sollten noch Fragen bestehen.


    Euch allen noch ein schönes Weekend!


    Freundliche Grüsse

    Lachvogel
     
  3. Video Script

    Videos zum Themenbereich

    * gefundene Videos auf YouTube, anhand der Überschrift.