Ill be honest, I gotta lock in when it comes to learning abt anti reversing techniques, so let us practice together! I wanted to pick out some VM crackme to practice so I picked [this](https://crackmes.one/crackme/6a6b5bdd59d4a7b04de30a28). The name of the author is [mintcocs](https://crackmes.one/user/mintcocs). I started by opening the challenge in ida pro and saw the following: ``` Function name Segment Start Length Locals Arguments R F L M O S B T = X _main __text 0000000100000978 000000C8 00000040 R . . . . . B T . . -[VM initWithProgram:] __text 0000000100000A40 00000110 00000050 R . . . . . B T . . -[VM fetchByte] __text 0000000100000B50 00000034 00000010 R . . . . . . T . . -[VM fetchWord] __text 0000000100000B84 0000006C 00000030 R . . . . . B T . . -[VM updateZeroFlagWithValue:] __text 0000000100000BF0 0000004C 00000020 R . . . . . . T . . -[VM run] __text 0000000100000C3C 000010EC 00000210 R . . . . . B T . . ___memset_chk __stubs 0000000100001D28 0000000C R . . . . . . . . . ___stack_chk_fail __stubs 0000000100001D34 0000000C . . . . . . . . . . _fflush __stubs 0000000100001D40 0000000C R . . . . . . T . . _fgets __stubs 0000000100001D4C 0000000C R . . . . . . T . . _objc_alloc __stubs 0000000100001D58 0000000C R . . . . . . T . . _objc_autoreleasePoolPop __stubs 0000000100001D64 0000000C R . . . . . . T . . _objc_autoreleasePoolPush __stubs 0000000100001D70 0000000C R . . . . . . T . . _objc_msgSendSuper2 __stubs 0000000100001D7C 0000000C R . . . . . . T . . _printf __stubs 0000000100001D88 0000000C R . . . . . . T . . _putchar __stubs 0000000100001D94 0000000C R . . . . . . T . . ``` So, lets look at main first! ![[ida_vm_01.jpg]] So first of all, 0x64 bytes at \_PROGRAM are copied into a buffer and \-\[VM initWithProgram:\] is called with the buffer that 0x64 bytes of \_PROGRAM were copied to. The text "give me the code:" is printed and stdout is flushed and then \-\[VM run\] is called with the result of \-\[VM initWithProgram:\] and some cleanup happens and \_main returns. Now, lets look at \-\[VM initWithProgram:\]! ![[ida_vm_02.png]]After some setup, the address of the buffer is stored at SELF + 0x110, two zero bytes are stored at SELF + 0x10C and a zero byte at SELF + 0x10E. The function \_memset\_chk is called with SELF + 8, 0 also four zero bytes are stored at \[SP + 4\], 4, and -1, also -1 as 8 bytes is stored at \[SP + 8\]. \_memset\_chk is called again with SELF + 0xC, 4 bytes at \[SP + 4\], 0x100, 8 bytes at \[SP + 8\]. The length of the argument given to \-\[VM initWithProgram:\] is stored as 8 bytes in \[SP + 0x10\]. That same length is checked if it is less than 0x100 and if it is then we jump to loc\_100000B18 else we store 8 bytes equal to 0x100 at \[SP + 0x10\] and then we branch to loc\_100000B18. At loc\_100000B18, a new buffer is made from whatever the 8 bytes starting at \[SP + 0x10\] are as the length and SELF + 0xC as the start of the buffer and the argument given to \-\[VM initWithProgram:\] is what is used to copy from. SELF is returned. The next function to look at is \-\[VM fetchByte\]! ![[ida_vm_03.png]]So, the value of 2 bytes at SELF + 0x10C is increased by 1 and SELF + 0xC is returned. The next function to look at is \-\[VM fetchWord\]! ![[ida_vm_04.jpg]]So, \-\[VM fetchByte\] is called and the result as a byte is stored in \[SP + 0xF\], and \-\[VM fetchByte\] is called again and the result as a byte is stored in \[SP + 0xE\]. The result of the bitwise or operation between byte at \[SP + 0xF\] and the byte at \[SP + 0xE\] that is shifted to the left by 8 bits, has the bitwise and operation on it and 0xFFFF and is returned. The next function to look at is \-\[VM updateZeroFlagWithValue:\], so lets do this! ![[ida_vm_05.png]]If the argument given to \-\[VM updateZeroFlagWithValue:\] is not zero then we modify SELF + 0x10E by the bitwise and operation with 0xFE and we return. If the argument is zero then we modify SELF + 0x10E by the bitwise or operation with 1 and we return. NOW! The moment I have personally been waiting for... WE GET TO REVERSE \-\[VM run\]! It starts off like this! ![[ida_vm_06.png]]SELF is stored at \[SP + 0xE0\] and we jump to loc\_100000C68 and there \-\[VM fetchByte\] is called and the result as a byte is stored in \[SP + 0xD7\] and is also stored at \[SP + 0x80\] and that value is subtracted by 0xFF and if the carry flag was set and the zero flag was cleared then we branch to def\_100000CB4 else PC is set to 0x100000C9C. Now, let us look at def\_100000CB4 first! ![[ida_vm_07.png]]Sooo, lol it is just prints "Unknown opcode: %02x\n", where %02x will be the byte at \[SP + 0xD7\] and then the function returns. Now, lets look at 0x100000C9C. ![[ida_vm_08.png]]So, X11 is set to 8 bytes at \[SP + 0x80\] and the pseudo instruction ADRL is used to load the address of a jump table into X10. We jump to 0x100000CA8 + the value of 4 bytes at (X10 + (X11 shifted to the left by 2))