I'm facing some issues while debugging an iOS application for educational purposes. This is an iOS application found on the App Store and has some anti-debugging capabilities built in it.
Pretext: The assembly instruction you see below is a snippet of the anti debugging checks done by the application. The instructions below are going to do the following:
- Load Register X8 with a memory address of (Current PC Register - 0xa70)
- No Operation
- Load Byte from register X8 into w8
- Compare Register w8 if it is equal to 0xff
Exact Code block for Instructions as seen in IDA
ADR X8, __cB5JgDa_QrhRN_ ; +[cB5JgDa QrhRN]
NOP
LDRB W8, [X8]
CMP W8, #0xFF
Corresponding Byte Code Block for Same Instruction Set
88 AC FF 10
1F 20 03 D5
08 01 40 39
1F FD 03 71
Now, let's jump the gun and see what happens when it tries to load the byte from register x8 int w8. As you can see, I did a register read of both w8 and X8. In theory, based on the memory contents of X8, this instruction should have loaded 0xff into w8 through the assembly instruction ldrb w8, [x8]
Problem Faced: Upon evaluation of the instruction the actual memory contents in w8 was 0xfe and NOT 0xff. This is really an unexpected behaviour as my ARM knowledge tells me that the correct value for that evaluation should be 0xff and not 0xfe.
Things I have tried but failed to explain this behaviour
- Creating mini Xcode iOS app and trying to replicate the same instruction set and memory state
- Setting watchpoints to observe if any part of the program is editing the memory in-between
- Looking for memory protections such as mprotect if they have modified that memory region
I would be highly appreciative of anyone who can point me in any right direction or ways so that I can better understand this mysterious behaviour. If it was my misunderstanding in ARM instruction, please do tell me too. Thank you so much everyone.