How to harden an iPhone/Android app so its tough to reverse-engineer it?
Preventing the reverse engineering of software is a difficult problem.
Preventing the reverse engineering of application software on a general computing platform with weak hardware assistance (PC, iPhone, or Android phone) for a significant period of time (months) is an exceptionally difficult problem.
Generally instead of protecting the entire application a common alternative is to attempt protection of the critical data used by the application and give up on protecting the entire application.
[1] Make the app hard to crack, as the binary will hold some secret tokens.
Your though here is exposing the 'protect the data' concept. What you really want the application to protect is a few pieces of critical data: the secret tokens.
How you protect data when the application can not protect itself?
You pass the responsibility to the operating system. There are two types of protection that operating systems can offer: access control and cryptography. Most of the protections we use when we allow or deny reading data or performing a specific action are access controls.
An access control takes as input an action and an identifier of who wants to do the action. Its output is either approve or deny for permission to perform the action. An access control uses a set or rules or a table to determine if a identifier should be allowed to perform an action.
For example: Alice has a game FunGame on a computer. The computer's access control has a rule that only Alice may run FunGame. Bob sits down at the computer and attempts to run FunGame. The computer's access control takes as input 'run FunGame' and 'Bob'. Based on the rule 'only Alice may run FunGame' the access control's output is deny.
Access control provides a direct and general way to make decisions about allow or deny, while cryptography provides a more indirect method of allow or deny. With encryption and decryption we can transform data so that the data makes sense or makes no sense.
Encryption and decryption require at least one cryptographic key. The value of the key becomes an access control with the two rules 'allow anyone with the key to take comprehensible data and make it incomprehensible' and 'allow anyone with the key to take incomprehensible data and make it comprehensible'. Making data incomprehensible only protects the data from bring understood. A individual may still be able to read the data, but it will not be in a form they comprehend.
Notice that the key instead of the identifier of an actor becomes the critical component. Anyone who has the key, no matter how they obtain it, can access the data. This makes storing the key a problem. If you store the key on the same system as the data you are providing anyone with the means to access your data. This is why passphrases require memorization. Memorizing a passphrase stores the key (passpharase) in your head instead of on the system.
So, to protect your tokens you can use the operating system's access controls, or you can encrypt the tokens, or both. If someone has a operating system that does not enforces it's own access controls (jailbroken iPhone or rooted Android phone) then you can not rely on the operating system's access controls. However, as long as the key used to encrypt your tokens does not reside on the same system as the the encrypted tokens, your tokens will still be protected even if the operating system is compromised.
However keeping the key off the system in question is another difficult problem called key management.
[2] If it still can be cracked, is there any way the app can tell someone or its own self that it has been cracked(like checking against some checksum or certificate or the OS doing it for the app) and take some action?
The app binary can get infected or bad code can get injected while the program is in memory as well, so suggested methods should be able to deal with both these cases
The concept of examining a piece of data to see if it is unaltered since it was last inspected is called integrity checking. An efficient way of evaluating the state of a chunk of data is called cryptographic hashing. To perform integrity checking you must first calculate the cryptographic hash of a piece of data and then protect the resulting hash value. At a later time recalculate the cryptographic hash on the same piece of data. Compare the recalculated hash value to the protected hash value. If the two vales are identical then the data is unaltered. If the two hash values differ then the data has been altered.
You may treat the application binary in storage as a piece of data and perform integrity checking on it. You may also treat the application binary in memory as a piece of data and perform integrity check on it.
The concept of examining a running program to see if it is processing as designed is called attestation. Checking all the memory used by the running code is typically infeasible so attestation usually checks something a little simpler such as the values of certain data sets or the state of the running program and its transition between states. Attestation is difficult to achieve so it is not widely used in commercial software.