Dynamic code analysis in reverse engineering binaries is where we execute the binary for analysis, adding breakpoints, inspecting processor state and such. This is very useful to understand the program's algorithm and values much easier than static binary analysis. However when we analyse malware, running them is dangerous. But we can have an isolated machine where the program can be run and analysed remotely. This is an explanation of these techniques as well as a tutorial on how to reverse OS X crackme challenge (1-Sandwich.zip | mirror).

This is a small app (by HAWKE, 2009) which requires us to find the correct serial key. The goal is to produce a keygen as opposed to patching. Patching is just easy as changing one instruction.

1. Load the Sandwich.app in Hopper Disassembler, choose the defaults and proceed to disassembly screen.

2. Instead of remote server, we will connect to the current system using Hopper Debugger Server. Download, install and run the app which will launch the debug server locally.

Hopper Debug Server

3. Now in Hopper, navigate to Debug->Select Debugger... and the server will be listed under local servers section. Double click to launch the server window.

4. The path will be auto filled to something like /Users/.../Sandwich.app/Contents/MacOS/Sandwich. Click the Play icon under controls which will run the Sandwich.app under the debugger.
5. Check the left pane for the available methods. We can see -[SandwichAppDelegate validateSerial:] method. Check the pseudo code to get an idea of the algorithm.

App methods

6. Now is the time to add breakpoints so that we can step and check register values as the program runs. So add to the initial address 00001b2d 55 push ebp under that section.

7. Go back to the running program and enter some serial number and click validate, which will make the debugger stop at the breakpoint.

8. The first conditional check is cmp eax, 0x13 which checks if the serial number is 19 decimals long.

9. Second check is mov dword [esp+0x28+var_20], 0x2034 ; @"-" which then checks the number of components separated by - and compares it with cmp eax, 0x4, which means, the serial key has four dashes in it. So the format is xxxx-xxxx-xxxx-xxxxx.

Check length

10. If all the above passes, it gets each block (chunk separated by dash) and checks if the length is four.

Chunk length

11. Next it takes the first block 0x0 gets the int value and place it into esi register, mov esi, eax.

Block 1 check

12. Then it takes the second block 0x1, gets the int value and adds it to the block 1 esi value from above step and place it into esi register itself.

Block 2 check

13. There is no check for third block which means it can be any random characters of length four.

14. The last block is constructed by computing shift arithmetic right by two of the current esi which is, sar esi, 0x2 from step 12 and then subtracting it from 0x19c5 the HEX for 6597 loaded into edx and then comparing the obtained value to the last block from the serial key loaded into eax.

Block 4 check

15. If it matches, the serial key is correct and a success dialog will be shown. Now that we know the algorithm, we can write the keygen.

Dynamic debugging

The keygen code

#!/usr/bin/python
 
"""Sandwich OS X crackme keygen.
 
App: https://reverse.put.as/wp-content/uploads/2010/05/1-Sandwich.zip
Tue 28 Nov 17
"""
 
import random
 
class SandwichKeygen:
    """Sandwich key generator."""
 
    __block_3_mn = 6597  # block 3 magic number
 
    def _gen_random(self):
        """Return random number in range of 4 digits."""
        return random.randrange(1000, 9999)
 
    def _get_block(self):
        """Get a block containing 4 digits."""
        return str(self._gen_random())
 
    def _get_block_3(self, block_1, block_2):
        """Generate the last block from block 1."""
        sar_2 = (int(block_1) + int(block_2)) >> 2
        return str(self.__block_3_mn - sar_2)
 
    def generate(self):
        """Generates the key."""
        block_0 = self._get_block()
        block_1 = self._get_block()
        return '-'.join([block_0, block_1, str(self._gen_random()), self._get_block_3(block_0, block_1)])
 
if __name__ == '__main__':
    keygen = SandwichKeygen()
    print keygen.generate()

Few keys
1111-2222-0000-5764
2681-8944-2259-3691
6773-1497-8343-4530
5903-2519-1731-4492
9613-1094-3343-3921