Load command 9 cmd LC_UNIXTHREAD cmdsize 80 flavor i386_THREAD_STATE count i386_THREAD_STATE_COUNT eax 0x00000000 ebx 0x00000000 ecx 0x00000000 edx 0x00000000 edi 0x00000000 esi 0x00000000 ebp 0x00000000 esp 0x00000000 ss 0x00000000 eflags 0x00000000 eip 0x186b2662 cs 0x00000000 ds 0x00000000 es 0x00000000 fs 0x00000000 gs 0x00000000
This is from the header of my crackme and that entrypoint is a random value. When the entrypoint is the original and valid one, IDA is more or less smart and uses that information if the headers are mangled (just the offsets). Instead of modifying the entrypoint to some stub I wanted to use an invalid value. A good place for this is dyld, who is responsible for jumping to the program’s entrypoint. The interesting code snippet is (from dyld/src/dyldStartup.s):
# call dyldbootstrap::start(app_mh, argc, argv, slide) call L__dyld_start_picbase L__dyld_start_picbase: popl %ebx # set %ebx to runtime value of picbase movl __dyld_start_static_picbase-L__dyld_start_picbase(%ebx), %eax subl %eax, %ebx # slide = L__dyld_start_picbase - [__dyld_start_static_picbase] pushl %ebx # param4 = slide lea 12(%ebp),%ebx pushl %ebx # param3 = argv movl 8(%ebp),%ebx pushl %ebx # param2 = argc movl 4(%ebp),%ebx pushl %ebx # param1 = mh call __ZN13dyldbootstrap5startEPK12macho_headeriPPKcl # clean up stack and jump to result movl %ebp,%esp # restore the unaligned stack pointer addl $8,%esp # remove the mh argument, and debugger end # frame marker movl $0,%ebp # restore ebp back to zero jmp *%eax # jump to the entry point
What we need is to restore the eax value to the real entrypoint and everything will be fine. One easy way to accomplish this is to set a breakpoint at the jmp eax address, fix the eax value and continue normal execution. Another way is to modify the jmp to an absolute address. The jmp is just two bytes long – not enough for this. But there should be enough alignment space above _dyld_start. Example:
8FE01010 _offset_to_dyld_all_image_infos: 8FE01010 00 44 04 00 db 0,44h,4,0 ; add [esp+eax+0], al 8FE01010 ; --------------------------------------------------------------------------- 8FE01014 00 00 00 00 00 00+ dd 5 dup(0) 8FE01028 0F 1F 84 00 00 00+ align 10h 8FE01030 8FE01030 ; =============== S U B R O U T I N E ======================================= 8FE01030 8FE01030 8FE01030 public __dyld_start 8FE01030 __dyld_start proc near 8FE01030 6A 00 push 0 8FE01032 89 E5 mov ebp, esp 8FE01034 83 E4 F0 and esp, 0FFFFFFF0h 8FE01037 E8 00 00 00 00 call $+5 8FE0103C 8FE0103C loc_8FE0103C: ; DATA XREF: __data:__dyld_start_static_picbase 8FE0103C 5B pop ebx 8FE0103D 8B 83 64 1C 04 00 mov eax, ds:(__dyld_start_static_picbase - 8FE0103Ch)[ebx] 8FE01043 29 C3 sub ebx, eax 8FE01045 53 push ebx 8FE01046 8D 5D 0C lea ebx, [ebp+0Ch] 8FE01049 53 push ebx 8FE0104A 8B 5D 08 mov ebx, [ebp+8] 8FE0104D 53 push ebx 8FE0104E 8B 5D 04 mov ebx, [ebp+4] 8FE01051 53 push ebx 8FE01052 E8 4F 05 00 00 call __ZN13dyldbootstrap5startEPK12macho_headeriPPKcl 8FE01057 89 EC mov esp, ebp 8FE01059 83 C4 08 add esp, 8 8FE0105C BD 00 00 00 00 mov ebp, 0 8FE01061 FF E0 jmp eax 8FE01061 __dyld_start endp
Modify the jmp eax to a negative offset into the slack space and modify that space to jump into the real entrypoint. The only piece left in this puzzle is ASLR. The dyld address can be easily found, especially if we give a base value where to start searching from (refer to the dyld randomization article). Having the dyld address, you can find its symbol table. I used **dyld_start_static_picbase **symbol for 32 bit (64 bit is **dyld_start_static**). While writing this I can’t remember why I have used this symbol instead of dyld_start.
The final step is to compute the real address of the jmp eax. I used an hash of the last bytes, which should be stable enough for this:
# clean up stack and jump to result movl %ebp,%esp # restore the unaligned stack pointer addl $8,%esp # remove the mh argument, and debugger end # frame marker movl $0,%ebp # restore ebp back to zero jmp *%eax # jump to the entry point
Now we have the interesting address and can use one of the solutions above or something else you can come up with.
The problem with this approach is that it requires a constructor to manipulate dyld’s code before it is executed. Yesterday’s spoofing article and lots of junk functions could help to hide our intentions.