Reverse Engineer and win always

SpinDeals is a Coupon-based game where if you win in the game’s lucky wheel, you earn gifts (discounts on local restaurants). I was inquisitive to see what’s going on inside the client. Specifically, I was very interested who decides when the user won. It’s a lucky game, fine, but who verifies if the user’s random selection is indeed a lucky number? The server? The client?

Observing the app’s source code, I was able to identify the classes that reveal the whole logic behind the application. Additionally, I intercepted server calls using Burp Proxy and Frida, yet the app has not made any requests AT ALL! – Only some statistics are sent out. That means I just had to find the code producing the coupon – it should be somewhere in the client’s code.

I have searched the databases the app created, and it seems the database ‘data.db” holds the key. This database has two attractive tables: “coupons” and “gift”.

What we need is the coupons table, because this table contains – among others – fields such as: expired, redeemed, geoLat, geoLon, code, and status. Each table’s row correlates to other tables through foreign keys (e.g. place key, company key etc). Example values of earned coupons are “le1535304779”, “i1535570948”, …

Now, let’s see how the client generates those coupons.

You may find the coupon class model found at “com.spindealsapp.entity.Coupon”.

What we see here are setters and getters. So whatever class is using the coupon model, is using it for storing the values and re-use the object later-on. So which class is creating a coupon model? Well, search for the pattern: “new Coupon (” and you can find the class that is creating Coupon model objects. The class is: “com.spindealsapp.activity.GameActivity”. Also, the function that creates coupon model objects is called “createCoupon”.

  private void createCoupon(Gift gift) {
        StringBuilder couponCodeBuilder = new StringBuilder();
        couponCodeBuilder.append(, 2).toLowerCase().replaceAll("[^a-z]", ""));
        couponCodeBuilder.append(String.valueOf(System.currentTimeMillis()).substring(0, 10));
        String couponCode = couponCodeBuilder.toString();
        int lock = 1;
        long lockTime = System.currentTimeMillis() + gift.getTimeLock();
        if (! && lockTime > System.currentTimeMillis()) {
            lock = 0;
        } = new Coupon(-1, couponCode, place.getCompanyKey(), gift.getId(), place.getId(), gift.getDescription(), CurrentUser.user.getId(), System.currentTimeMillis(), System.currentTimeMillis() + gift.getExpirationTime(), lockTime, lock, place.getSpin().getRrule());
        CouponExtension couponExtension = new CouponExtension(-1, couponCode, place.getCompanyKey(), gift.getId(), place.getId(), gift.getDescription(), CurrentUser.user.getId(), System.currentTimeMillis(), System.currentTimeMillis() + gift.getExpirationTime(), lockTime,, place.getName(),, (long) place.getType(), place.getTypeName(), place.getGeoLat(), place.getGeoLon(), lock, 0, place.getCity(), gift.getRules(), 0, place.getKeywords(), place.getSpin().getRrule());

We need to know the second argument of the Coupon’s model constructor. The 2nd argument is the coupon code. So finding what the 2nd argument is, you can trace back how the client generates the coupon code.

The client generates the coupon code using the companies first two letters and then concatenates the first 10 digits of the current timestamp (in milliseconds). I guess that is the only thing you need to know to generate a new coupon. To get a free coupon, find the first two letters of the company of the restaurant you wish to win (it’s in the database mentioned before) and concatenate them with the current timestamp. Now show it to the waitress, and you are finished. Moreover, if you wish to make it realistic, and show the code from the app, there are two ways: First one is to insert a record into the coupon table, and the second way is actually to alter the game so you can win every time (the app inserts the row in the database table by itself). But where is the algorithm which decides when the user wins?

I will give you a hint: Look at onAnimationEnd function.

Leave a Reply

Your email address will not be published. Required fields are marked *