Tuesday 30 December 2014

Writing a JS sol parser to cheat idle farmer

Idle farmer was a game I discovered a few days ago which was one of the more enjoyable incremental games. Unfortunately, farming in the game is slow and tedious, and only gets worse over time. I decided to speed it up a little.

Scanning variables in cheat engine yields no results, for reasons I don’t know (and haven’t investigated), so I decided to look at the sol data. Converted to json, it looks something like this:

{
    "dataos": "gJE5706:321250LB<9Â ... "
}

Clearly, this is some form of encrypted data. I hooked the game up in the JPEXS decompiler, and after some digging, found these two classes related to save data handling:

DataSaver
DatosGuardados

DatosGuardados is the class which handles save data encryption/decryption, and DataSaver is the one which convert’s it into an actionscript format.

Something new here which isn’t usually seen in other flash games is it’s use of the haxe serialization/deserialization routines. So I took the lazy route to write a parser in js, the closest language to both haxe and actionscript which is the easiest to hack with.

There’s no existing sol parser for js, but sol is really just a thin container around amf3, and there already are some libraries for dealing with it, so this wasn’t so hard.

Next came the deserialization part, which involved copy pasting DataSaver and DatosGuardados and manually converting them to haxe (the language similarities meant that this was relatively easy work).

Then came the hard part. The decrypted data was not unserializable, nor human readable. There weren’t any avm bytecode debuggers to help either. How are we going to know what fucked up?

But luck is on our side, and the actionscript trace function comes to the rescue. We can basically print anything by inserting the following in the avm code:

findpropstrict Qname(PackageNamespace(""),"trace")
getlocal 1
callpropvoid Qname(PackageNamespace(""),"trace") 1

and enabling flash player tracing in mm.cfg.

After printing some loop variables and inspecting the code which clearly didn’t look right, the bug became apparent:

_loc14_ = 0;
while(_loc14_ < _loc6_) {
    _loc14_++;
    _loc15_ = _loc14_;
    _loc11_ = param1.charCodeAt(_loc13_ - _loc15_ - 1);
    _loc2_.b = _loc2_.b + String.fromCharCode(_loc11_ - _loc10_);
}

(param1 is the input string, looks like an off-by-one here)

The assignment translates to the following, can you spot the error?

getlocal 14
inclocal_i 14
convert_i
setlocal 15

getlocal pushes local 14 onto the stack, inclocal_i doesn’t modify the stack, so convert_i is still using the old value - _loc14_, not _loc14_ + 1.

The fix is simple:

_loc14_ = 0;
while(_loc14_ < _loc6_) {
    _loc15_ = _loc14_;
    _loc14_++;
    _loc11_ = param1.charCodeAt(_loc13_ - _loc15_ - 1);
    _loc2_.b = _loc2_.b + String.fromCharCode(_loc11_ - _loc10_);
}

Now we can finally deserialize, pretty print everything in json, make the exporter, and be done with it!

And if you feel like cheating today, git clone https://github.com/simonzack/idle_farmer_parser.

Oh, and if you’re wondering about the strange key names, they (and the game) are written in spanish.

No comments:

Post a Comment