I recently heard about a really old game from a relative of mine, Taipan!.
The premise is basically the following: You are a merchant with a ship in the Pacific Ocean and try to make a few bucks sailing from coast to coast around the cities Shanghai, Nagasaki, Singapore and a few others, buying and selling silk, arms, general cargo, and also opium, if you want to risk it.
Your hometown is Hong Kong, where you have a warehouse and can store goods you bought somewhere else when they were cheap. There's pirates waiting for you to start traveling, canons you can install on your ship to defend yourself against them, an incredibly shady moneylender in Hong Kong that causes you to start with a debt that, if not paid back directly, is the reason you are going to go bankrupt and worse.
I was hooked. I mean, seriously, what's not to love about this premise?
So anyway, the game was apparently released in 1982 on the Apple II and TRS-80, both of which are not quite the systems you are used to play games on (or do anything else, for that matter) in this day and age. Luckily, I found an iOS port that looked well made and was even free!
I downloaded it from the App Store and sure enough I was soon sailing around the world, making a lot of money, paying my debts, living the dream! Until this popped up after literally 10 minutes of playing, that is:
What a bummer! It might have been mentioned somewhere in the description, but I didn't think that 12 months game time would be over in 10 real life minutes. This felt wrong. I felt betrayed.
I had about 3 options in that moment:
1) Purchase. I didn't feel like purchasing in that very moment, to be honest. I still felt like I didn't see any important part of the game.
2) Use one of the thousand universal in app purchase tools floating around the internet. These usually work by hooking the IAP functions Apple provides and make the app think that the purchase was made, when in fact it wasn't. I don't particularly like these and also, they are boring. I wanted a challenge, even if it was going to be a small one.
3) Analyze the executable to find out how to keep playing without doing the in app purchase! This sounded like a fun thing to do (no, it really did!) so I decided to go this route.
Static analysis
I am a huge fan of static analysis of binaries. It is incredible how much information about the inner workings of a program you can gather just by skimming through disassembled code in IDA or Hopper, two very popular disassemblers very suited for this task. The trial version of Hopper allows the analysis of ARM executables so I decided to go with Hopper. I launched Hopper and looked at the main window. So far, so good.
But where the hell do I get the game executable from?
Decrypting an iOS App Store binary
Every binary shipped inside an .ipa file on the App Store is encrypted. You can find details about it everywhere on the Internet so I am going to skip that part here. What's important here is, that Hopper obviously can't disassemble an encrypted executable and I needed to decrypt Taipan! somehow.
I do have a jailbroken iPhone on iOS 9.3, but both methods of easy decryption I already knew of did not work for me. Clutch is apparently not able to decrypt Taipan! and a few other apps in specific for unknown reasons, whereas Stefan Esser's dumpdecrypted quit itself when injected. I wasn't really interested in this part of the challenge, so I did not look further into the bugs preventing me from decrypting the game.
I decided to just dump the memory while the game was running, which, in theory, is the most time-consuming way of decrypting applications, but in fact turned out to be the easiest one because of the previous problems. I found this tutorial to be really on point, it worked perfectly fine for me so I can only recommend it to other people having the same problems with automated decryption tools.
Finding the check
Finally, I loaded the binary into Hopper, selected the arch I decrypted before (armv7s) and let it do its magic. I could see the light at the end of the tunnel!
I searched for the string displayed in the popup above and found the code referencing it:
Looking at this might scare you at first, but it really shouldn't. We don't even really need to know assembly for this task. Hopper already shows us that the code that displays the popup (starting at 0x18552) is referenced a few lines above. This should be our check, right?
Sure enough it is. The cmp r0, 0x0
at the end is our check. If the condition is true, the CPU jumps (or rather, branches) to the popup code as you can see in the last line. One of the good things about Hopper is its decompiler. It cuts out most of the objc runtime crap and shows you what the author probably wanted to do (most of the time, anyway):
And here we can finally see the check that decides the fate of our Taipan journey. The first condition makes sure that we are able to play the game for 12 ingame months, albeit Hopper makes it seem more complicated than it is; that's just the nature of disassembling. The second condition is more important. It shows us that as long as we do have a key enableAllYears
in the default application preference file, it is not going to show the IAP popup.
That very key is probably created after doing the IAP. We can just create that key! We don't even need to patch the binary (although we could have, but this looks superabundant here).
And indeed, after adding the key with the value 1
to Taipan's preference .plist with an editor of your choice, you are finally greeted in the new year 1861 (okay, I might have skipped a year; don't mind that debt, it's all going to be fine)!
I hope you had as much fun reading as I had writing this. Happy sailing!
P.S.: In case editing the .plist does not seem to work for you, apparently there have been some changes to how preferences are loaded in iOS 8. They are now handled by a daemon that needs to be restarted to recognize the changes made to the .plist, otherwise the application won't see them. Took me an embarrassingly long time to notice my mistake.