Kartones Blog

Be the change you wanna see in this world

Recommended Articles - 2017/03/05

Had a bunch of links pending but past weeks have been quite busy. It's so sad that unethical and directly wrong company behaviours have been dominating the news ecosystem lately...

Hacking Flash Games Example: Clicker Heroes

Note: I reported this technique to the company behind the game back at august 2015. Never got a reply.


I like videogames, so when I read about some "clickers" genre, I wanted to check how they played. Setting aside the mechanics themselves and my personal feelings regarding this kind of games, one title that caught my attention for being better than the average was Clicker Heroes. After playing a while it looked to me as if the difficulty curve was quite exponential, requiring either lots of patience or spending money at in-app purchases, so I wanted to confirm my suspicions.

The process

Checking the game binaries I saw that there was a HeroClicker.swf, so it was a Flash game. I've already peeked inside and even dissasembled SWFs with Sothink SWF Decompiler, so was my chosen tool.

I started peeking at the insides, and by mere luck I ended peeking the ImportScreen class. It had a constant called SALT just below the variable _userData, so it caught my eye. I ran the game and saw that in the options you can export your data to an apparently encrypted TXT file, and then import it back... hacking my data was way more appealing, and by chance I had a possible attack vector with the import logic.

There was another constant with a maybe too descriptive name, TEXT_SPLITTER, and scrolling down I found toAntiCheatFormat and fromAntiCheatFormat methods, performing MD5 hashes with the salt of contents retrieved from sprinkle and unSprinkle methods.

The sprinkling or scattering algorithm was not hard to read:

  1. Get user data json and base64 encode it
  2. Prepare a new array twice the original size of the base64 encoded string
  3. Place one by one all base64 encoded characters at even positions
  4. Randomly put an alphanumeric character at odd positions

And then when writing the content of the "encrypted" data to the file:

  1. Write the new sprinkled array
  2. Write the TEXT_SPLITTER constant as it is
  3. Generate an MD5 of the original JSON data with the SALT constant and write it

And of course, the inverse process to import the data.

I built a small tool to apply this algorithm using Ruby, and after saving/exporting my character, it did work and I had access to shiny data like the following:

"soulsSpent": "0",
"primalSouls": "34152",
"mercenaryCount": 5,
"gold": "1.244277239155236e135",
"baseClickDamage": 15,
"transcendent": false,
"epicHeroReceivedUpTo": 1660,
"rubies": 7165,

Writing the inverse was easy, and confirmed me that everything worked fine.

The results

This is how the end of an original encrypted file looks: Source encrypted file

And this is how a (valid) file encrypted with my tool/script looks. As you can see instead of randoms I just enter blank spaces at odd positions: Tool encrypted file

The truth is that even cheating, the game gets to some insane levels that you have to either wait a lot or do level grinding (by restarting via "trascending"), so I got bored too quickly. As often happens, tweaking or hacking a game becomes more fun than playing the game itself.

You can get the tool (needs Ruby) from my GitHub and easily see a simplified version of the algorithm.

Final note

Even if the code were didn't had so obvious names, as the text splitter fragment can be easily spotted at the end of the file (Look for Fe12NAfA3R6z4k0z at both screenshots), just doing a classic saved game deltas diff would have raised awareness (whole content of the file would have changed except for that fragment) and made me search the SWF for that splitter string.

Recommended Articles - 2017/01/29

I wasn't planning to post again so quickly, but I've been reading quite a few interesting things and wanted to share the links before the list gets too big.

Customizing Pelican archives page

I wanted to personalize the blog archives page, as it was serving its purpose but was a bit messy:

Old archives page

What I wanted was to visually differentiate posts grouping by year.

Checking the theme's article.html code, it was a simple loop without any customization option:

{% for article in dates %}
 <li><a href="/{{ article.url }}">{{ article.title }}</a> {{ article.locale_date }}</li>
{% endfor %}

As the template system uses Jinja, I tried using set to create variables on template-render time:

{% set year = '1970' %}
{% for article in dates %}
 {% if not article.locale_date.startswith(year) %}
 {% set year = article.locale_date[:4] %}
 <li>{{ article.locale_date[:4] }}</li>
 {% endif %}
 <li><a href="/{{ article.url }}">{{ article.title }}</a> {{ article.locale_date }}</li>
{% endfor %}

But it backfired on me as it was not working as expected: each article had the date repeated. Why? Because I later found that Jinja variables are only available at the scope they are created, not at inner ones, so I was actually creating two year variables, one outside of the loop (always with value 1970) and another inside always set to article.locale_date[:4].

How then to proceed then to detect different years? The answer was simple: use an old but always resourceful index-based for loop:

{% for index in range(dates|length) %}
 {% set article = dates[index] %}
 {% if index == 0 %}
 <li>{{ article.locale_date[:4] }}</li>
 {% else %}
 {% set previous_article = dates[index-1] %}
 {% if not article.locale_date.startswith(previous_article.locale_date[:4]) %}
 <li>{{ article.locale_date[:4] }}</li>
 {% endif %}
 {% endif %}
 <li><a href="/{{ article.url }}">{{ article.title }}</a> {{ article.locale_date }}</li>
{% endfor %}

Now I finally can play with the articles and check their dates and detect when the year changes. And while I could avoid the article variables, being used at build time I prefer legible code. Here are the final results:

New format

5 Advices for job seekers

Recently, between interviewing people and talking with some friends about the interview process and what do we seek, I was thinking about quickly writing a list of the five things I think most people should do when searching for a new job. The following advices assume you're searching for a technical position (in software development, systems or the like), living in Spain, with not much but some professional experience.

  1. Learn how to sell yourself: Avoid extremes, either chatting too much about irrelevant stuff (dodging the questions asked) or being too shy and not giving enough detail or good examples (when you actually knew about the topic).

  2. Aim for tech-driven companies: I still have to find a single company where sales people don't become headaches for tech. I try to learn what, how and why they do what they do, but still outcomes vary from "I sincerely don't know what I'm selling" to "I don't give a fuck as long as I get paid my variable". Best scenario: no sales people and a CEO with tech background; Nice scenario: tech-driven roadmap driving the company; Everything else tends to eventually cause crunches, planification issues and chaos.

  3. Seek for a constant learning place: Companies mutate ("pivot"), roles change, code evolves (and gets replaced), colleages come and go, but what you learn is what will really help you in the future. You're the sum of your experiences, try to amass good ones by working on interesting projects with colleages smarter and different from you.

  4. But never forget the money: If you wanted to work just for the shake of what you do you'd be at an NGO or at home doing only opensource, so don't lower your requirements just because you're passionate about what you do. Ask for the amount you think is fair, or at least the market average, instead of selling your valuable time for peanuts.

  5. Equity and bonus are secondary at startups: It's so nice to speak about how Google, Twitter or Microsoft employees got very rich (including chefs and graffiti artists), but that's not the common scenario and outside of USA happens even less often. Most of the time your grants will mean nothing and your company won't be a huge success. Or if you're lucky and it gets bought, your common stock will be like a big bonus, maybe enough buy a new car, but not enough to buy a house. Unless you're entering a well stablished and usually mid to large company, negotiate first a decent base pay, then agree about the equity and lastly arrange the variable pay. Also ask for very specific terms/rules for evaluations, you'll avoid too subjective judgements.


  • While colleages come and go, it is very important to work at a nice environment with good teammates. Ninjas and rockstars might generate more problems than those they resolve, and a toxic environment will only drain your energy and patience (whenever it is a bad coworker, a bad manager, or something else).
  • English is critical: Not a perfect level but enough so you are able to understand conversations and communicate.
  • Who you hang out with is who you are: If you work at startups, you'll probably move between startups and get job offers mostly from startups. If you do consultancy or work at big companies, your contacts will be from those ecosystems, although it is more common for startups to peek for candidates also from big companies (sometimes people wants to change).

Previous entries