surech.ch – Homepage von Stefan Urech

Praktizierender Zyniker und Freidenker

Spass mit der (virtuellen) Slot-Maschine!

| 2 Kommentare

Auf Facebook fiel mir diese Anzeige auf:

Als regelmässiger Kunde vom Casino in Bern musste ich mir diese Angebot genauer anschauen. Es kommt schliesslich recht selten vor, dass ein Casino etwas gratis hergibt. Tatsächlich empfängt uns dort eine hübsche Slot-Maschine:

Leider gibt es (wie in den meisten Glückspielen) recht selten etwas zu Gewinnen. Aber wie schon Indiana Jones zu hören bekam:

You lost today kid. But that doesn’t mean you have to like it.

Tatsächlich verliere auch ich nicht gerne. Also schauen wir uns diese Slot-Maschine mal genauer an.

Meine erste Vermutung ist, dass bei jedem Spiel eine Kommunikation mit dem Server abläuft. Also werfe ich mal einen Blick auf den Netzwerk-Traffic nach dem Klick auf „Spielen“
Und der ist: leer. Interessant. Also muss die ganze Logik im Browser ablaufen. Dann man den Source-Code der Homepage anschauen:

Und war ich hier sehe, lässt jedem Entwickler das Herz aufgehen: Sprechende Dateinamen, formatierter Code und hilfreiche Kommentare! Nach einer Analyse stelle ich folgendes fest:

  • Die Variable canWin scheint sehr wichtig zu sein, denn sie steuern an vielen Stellen den Programmfluss. Wirklich interessant ist aber, dass diese immer false ist.
    Befüllt wird sie aus den übergebenen Argumenten:

        this.canWin = false;
        this.winSpin = 0;
        this.winSign = '';
        this.spinCount = 0;
        this.spinCountMax = 0;
        this.iSpinCountDisp = 0;
    
        if (arguments != undefined && arguments.length == 1) {
            this.canWin = arguments[0][0] == '1';
            this.winSpin = parseInt(arguments[0][1]);
            this.winSign = arguments[0][2];
            this.spinCountMax = parseInt(arguments[0][7]);
        }
    

    Die Argumente werden in der HTML-Seite übergeben. Und hier ist der erste Parameter leer, womit auch canWin immer false ist:

                //Blade variables
                SlotGame('', '3', 'bar', defaultImageHeight, marginTop, margitBottom, paddingLeft, '3');
    
  • Wenn die Variable canWin nicht gesetzt ist wird aktiv dafür gesorgt, dass nie die gleichen Symbole auf einer Zeile erscheinen:
    if (this.canWin) {
        /* ... */
    } else {
        //DAB->20161019: We ensure that the 3 signals will be different
        do {
            this.result1 = parseInt(Math.random() * this.items1.length)
            this.result2 = parseInt(Math.random() * this.items2.length)
            this.result3 = parseInt(Math.random() * this.items3.length)
        }
                //while (this.result1 == this.result2 && this.result1 == this.result3 && this.result1 != null);
                //while (this.items1[this.result1].id == this.items2[this.result2].id && this.items1[this.result1].id == this.items3[this.result3].id && this.result1 != null);
    
                //DAB->20170109: The position 1 in the array of items had the wrong item sign. So avoid that item.
        while (this.items1[this.result1].id == this.items2[this.result2].id && this.items1[this.result1].id == this.items3[this.result3].id && this.result1 != null
        || (this.result1 == 1 || this.result2 == 1 || this.result3 == 1))
    }
    

    Der Kommentar ist übrigens nichts von mir, sondern steht so im ausgelieferten JavaScript. Scheinbar hatte der Autor einige Mühe, das while-Statement richtig zu formulieren.

  • Sollte man es trotzdem schaffen zu gewinnen, dann wird das Element mit der ID winnerButton angezeigt:

                if (that.canWin) {
                    if (that.winSpin == that.spinCount) {
                        $('#results').show();
                        $('#status').text(BLURB_TBL[ec]);
    
                        that.spinCount = 0;
    
                        $('#play').attr('style', 'display: none;');
                        $('#winnerButton').attr('style', 'display: block;');
    
                        console.log("Winner... => change button");
                    }
                } 
    

    Im HTML gibt es zwar dieses Element, es beinhaltet aber nichts relevantes:

    <div id="winnerButton" style="display:none;">                                
    <input class="form-control" name="domain_name1" type="hidden" value="powerdays">
    </div>
    

An diesem Punkt war ich überzeugt, dass uns das Casino übers Ohr hauen will. Zur Verifikation rief ich meine treue Twittergemeinde an:

Schnell melde sich ein guter Kollege. Er machte mich darauf aufmerksam, dass es irgendeine Server-seitige Steuerung der Gewinner geben muss. Der Anbieter will schliesslich die Kontrolle über das Spiel behalten.
Also muss manchmal ein anderes HTML aufgeliefert werden. Das lässt sich zum Glück schnell herausfinden, indem man die Seite mehrmals mit wget herunterlädt:

#!/bin/bash

for n in {1..150}
do
    echo Loading ${n}...
    wget http://www.gcbwin.ch/powerdays -q -O powerday${n}
    grep SlotGame powerday${n}
done

Und tatsächlich, nach 90 Aufrufen erhielt ich eine geringfügig andere Seite ausgeliefert. Bei den Verlierern wird das Java-Script wie folgt aufgerufen:

SlotGame('', '3', 'bar', defaultImageHeight, marginTop, margitBottom, paddingLeft, '3');

Bei den Gewinnen aber so:

SlotGame('1', '3', 'lemon', defaultImageHeight, marginTop, margitBottom, paddingLeft, '3');

Das erste Argument steuert unseren bekannten Parameter canWin. Der zweite gibt an, in welchem Versuch wir gewinnen sollen. Und mit lemon erhält der Gewinner drei Zitronen. bar wäre übrigens das Optimum.

Jetzt erhält auch das Element winnerButton einen sinnvollen Inhalt:

<div id="winnerButton" style="display:none;">
    <input class="form-control" name="domain_name1" type="hidden" value="powerdays">
    <form method="POST" action="http://www.gcbwin.ch/gamePlay/create" accept-charset="UTF-8"><input name="_token" type="hidden" value="MIv16LI6TU1hGo96IEyByH2PV1PQjionFbgzdU1u">
        <input class="form-control" name="domain_name" type="hidden" value="powerdays">
        <input class="form-control" name="code" type="hidden" value="M7H94UHB">
        <input class="form-control" name="winSpin" type="hidden" value="3">
        <input class="form-control" name="winSign" type="hidden" value="lemon">

        <input class="button btn-play" type="submit" value="Preis holen">
    </form>
</div>

Fazit
Schon beim Ausliefern der Seite ist klar, ob man gewinnt oder nicht. Die ganze Visualisierung ist nur Spielerei. Damit ist es relativ einfach, das System zu umgehen.

Tipp an den Entwickler: Den JavaScript unbedingt minimieren und verschleiern. Das verbessert die Ladezeiten der Seiten und hätte es mir deutlich schwieriger gemacht, die Sache zu verstehen.

Noch zwei Bemerkungen:

  • Der Wettbewerb endet am 22. Januar 2017. Um obige Bespiele trotzdem nachvollziehen können habe ich den (öffentlich zugänglichen!) Source-Code auf Github abgelegt.
  • Mit den gewonnen Erkenntnissen ist es ein leichtes, einen Gewinn zu erzielen. Das habe ich natürlich nicht gemacht und mich damit in keiner Art bereichert.

2 Kommentare

Schreibe einen Kommentar

Pflichtfelder sind mit * markiert.