Someone asked me how I got all of the Recipes for Alchemy on the IPhone. The answer – I cheated. I hacked the system.
Let me describe how I did this. There is some simple perl scripting involved, which might be useful to people.
Step 1 – Jailbreak the iPhone
I’m a hacker. What can I say.
Step 2 – Getting the Alchemy Files
Find where the Alchemy application is stored.
You can open a terminal window, run find, and look for some file that has Alchemy in the name.
find / -name Alchemy*
Instead of a terminal window, you might find it easier to ssh into the iPhone. You do have to use Cydia to install the various extra programs. I had the following installed
- Bourne-Again shell
- Openssh
Once OpenSSH is installed, you have to use your iPhone, check the settings, and find your IP address. Say it’s 192.168.1.222. The iPhone has a habit of shutting down its IP address if not in use. So refresh the IP address using the DHCP refresh, and then type
ssh -l mobile 192.168.1.222
The password for the user “mobile” is “alpine”
Then use ‘find’ to locate the file. Once you have found this, grab all of the Alchemy files by typing the following on your Linux (ofr Windows with Cygwin)
Copying the Alchemy files onto your Linux machine
scp -r mobile@192.168.1.181:/var/mobile/Applications/9E1E4FC0-B6CE-4036-8DFB-BCB5666D4741/Alchemy.app .
This will copy all of the files onto your computer.
The string above is on my machine. Your mileage may vary.
You can examine the files to see what they contain using “string” “od”, “emacs”, or whatever. It turns out that the file you want is Sparing.plist.
Next you want to decode the resource file that contains the recipes. I use perl. Looking at the various packages, the one that makes the most sense is the Mac::PropertyList module. Looking at the Dependency, it requires the XML::Entities module. Therefore, after you install perl, download and install the two Perl modules.
Step 3 – Getting Ready to use perl
Making it easier to install perl modules
I prefer to make sure /usr/local belongs to a certain UNIX group(5), such as adm, and that all of the subdirectories have group write permission.
In other words, type the following commands as root
chgrp -R adm /usr/local chmod -r g+w /usr/local
Installing the two perl Modules
This way you can install modules without requiring root access. To make the modules, after you download them, type
tar xfz XML-Entities* cd XML-Entities perl Makefile.pl make make install cd .. # And now do the next one tar xfz Mac-Properties* cd Mac-Properties* perl Makefile.pl make make install cd ..
Step 4-Extracting the Alchemy data from the Resource file
The Mac::PropertyList module creates a complex data structure with all of the information. The elegant thing to do is to write one perl program that reads the data and prints the results you want to get. But that takes more work. I prefer the “get the job done as easy as possible” school of programming. Rather than try to figure out the format of the complex data, the simplest thing to to is to let perl decode the binary information for you. The program to use is Data::Dumper which outputs the structure of complex data. This is essential for the Perl programmer. You just give it a pointer to a complex piece of data, and Data::Dumper will describe it. The code to read the file and dump the results is below:
#!/usr/bin/perl my $filename="./Alchemy.app/Sparing.plist"; use Mac::PropertyList qw( :all ); use Data::Dumper; my $data = parse_plist_file( $filename ); my $text = plist_as_string( $data ); print Dumper($data);
Call this program List, add the +x attribute with chmod, and they type
./List >List.out
If you look at the output of this file, you will see something that looks like this
VAR1 = bless( { 'Metal+Electricity' => bless( do{(my $o = 'Aluminium')}, 'Mac::PropertyList::string' ), 'Oil+Tool' => bless( do{(my $o = 'Petrol')}, 'Mac::PropertyList::string' ),
etc.
Aha! Clearly, Metal+Electricity creates Aluminum. Let’s write a perl script that reads this file, and outputs the recipe.
But some of the lines, like the first one, is a different format. Well, perl can handle this very easily.
Parsing ASCII in perl
I usually use the following template to do string parsing
#!/usr/bin/perl -w use strict; #my filename="List.out"; my $line; while (defined($line=<>)) { # 'Metal+Electricity' => bless( do{(my $o = 'Aluminium')}, 'Mac::PropertyList::string' ), if ($line =~ /complexstring/) { } elsif ($line =~ /string/){ printf("You didn't match this line: $linen"); } else { printf("Can't parse line: $linen"); } }
I include the string I am trying to parse as a comment, to help me get the regular expression correct.
The second string is how I debug the first string
First attempt at matching an ASCII line in perl. I replace the strings I am tring to match ‘Metal+Electricity’ and ‘Aluminum’ with “.*”
if ($line =~ /'.*' => bless( do{(my $o = '.*')}, 'Mac::PropertyList::string'/) { } elsif ($line =~ /bless/){
But I want to remember the strings found within the ‘…’, so I need to add parenthesis around them, so perl will remember them. This would be
if ($line =~ /'(.*)' => bless( do{(my $o = '(.*)')}, 'Mac::PropertyList::string'/) { printf("$1 => $2n"); } elsif ($line =~ /bless/){
This is a start, but there is a problem. When I use sed, I need to put backslashes before the parenthesis to mark them as special. Perl is the opposite. A backslash means the character is NOT special, or NOT a metacharacter.
So I need to put backslashes around the other parenthesis
if ($line =~ /'(.*)' => bless( do{\(my $o = '(.*)')}, 'Mac::PropertyList::string'/){
I also needed to put a backslash before the backslash. Running this gives me the error
Global symbol "$o" requires explicit package name at ./ParseBug.pl line 6.
(Smack forehead) I also need to put before the ‘$’
Sometimes I need to experiment with the regex, and the second line, where the ‘/bless/ is show, is a guess. Note that this also prints out lines that don;t match anything.
Eventually, I get this right. The final version is
- #!/usr/bin/perl -w use strict; #my filename="List.out"; my $line; while (defined($line=<>)) { if ($line =~ /'(.*)' => bless( do{\(my $o = '(.*)')}, 'Mac::PropertyList::string'/){ printf("%s=%sn", $2, $1); } elsif ($line =~ /bless/) { printf("missed line: $linen"); } elsif ($line =~ /VAR1/) { } elsif ($line =~ /Mac::PropertyList::dict/) { }else { printf("Can't parse line: $linen"); } }
This prints out all of the recipes