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