Mach_vm_write returns (os/kern) invalid address

Hi,
I’m using sankeninny’s HippocampHairSalon_iOS

In iOS 9.1 I’m getting an “(os/kern) invalid address" when I try to write to memory. Is something else required for this to work in iOS9? The searching part still seems to work.

Run as root w/ entitlements from the GitHub. I also noticed that GamePlayer & GameGem work in iOS9 so it’s certainly possible to write to process-space virtual memory.

Thanks!

Enter the value to search: 5108
Search result 3: 5108 at 0x65270044 (writable)

  1. Modify search results;
  2. Review search results;
  3. Search something else.
    Please choose your next action: 1
    Enter the address of modification: 0x65270044
    Enter the new value: 0
    mach_vm_write failed, error 1: (os/kern) invalid address

EDIT by admin:
Fix broken Markdown

In my humble opinion, mach_vm_write seems to have trouble handling ASLR and maybe that’s the reason.

Nevertheless, @snakeninny

HippocampHairSalon is not compatible with iOS 9 yet, and I don’t have any plan to update it.

BTW you can do some research and try to update it for the community haha

Thanks! I found the solution here and confirmed that it works in iOS 9.
https://github.com/gdbinit/readmem/blob/master/readmem/main.c
The change required is to wrap mach_vm_write with task_suspend/mach_vm_protect/task_resume.

Haven’t quite worked the change back into HippocampHairSalon yet, but it should be fairly easy now that I know the sequence.

Will post the diff once done.

Congrats!

Looking forward to see your post!

In case anyone needs this.
git diff

+++ b/main.m
@@ -41,6 +41,9 @@
                vm_region_info_t         info,
                mach_msg_type_number_t  *count,
                mach_port_t             *object_name);
+
+extern kern_return_t mach_vm_protect(vm_map_t, mach_vm_address_t, mach_vm_size_t, boolean_t, vm_prot_t);
+
 #else
 #include <mach/mach_vm.h>
 #endif
@@ -147,15 +150,9 @@ int main(int argc, char *argv[])
                        if ((substring = memmem((const void *)buffer, bufferSize, &oldValue, sizeof(oldValue))) != NULL)
                        {
                                occurranceCount++;
-#if CGFLOAT_IS_DOUBLE
                                long realAddress = (long)substring - (long)buffer + (long)address;
                                printf("Search result %2d: %d at 0x%0lx (%s)\n", occurranceCount, oldValue, realAddress, (protection & VM_PROT_WRITE) != 0 ? "writable" : "non-writable");
                                [substringArray addObject:[NSNumber numberWithLong:realAddress]];
-#else
-                               int realAddress = (int)substring - (int)buffer + (int)address;
-                               printf("Search result %2d: %d at 0x%0x (%s)\n", occurranceCount, oldValue, realAddress, (protection & VM_PROT_WRITE) != 0 ? "writable" : "non-writable");
-                               [substringArray addObject:[NSNumber numberWithInt:realAddress]];
-#endif
                                [protectionArray addObject:[NSString stringWithUTF8String:(protection & VM_PROT_WRITE) != 0 ? "writable" : "non-writable"]];
                        }
                }
@@ -190,8 +187,44 @@ int main(int argc, char *argv[])
                                printf("Enter the new value: ");
                                int newValue; // change type: unsigned int, long, unsigned long, etc. Should be customizable!
                                scanf("%d", &newValue);
+
+
+                               //Changes for iOS9 ASLR
+                               //get original memory protection
+                               mach_vm_size_t size = 0;
+                               mach_port_t object_name = 0;
+                               vm_region_basic_info_data_64_t info = {0};
+                               mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64;
+                               /* mach_vm_region will return the address of the map into the address argument so we need to make a copy */
+                               mach_vm_address_t dummyadr = modAddress;
+                               if ( (kret = mach_vm_region(task, &dummyadr, &size, VM_REGION_BASIC_INFO_64, (vm_region_info_t)&info, &count, &object_name)) )
+                               {
+                                       printf("mach_vm_region failed with error %d", kret);
+                                       exit(1);
+                               }
+
+                               //change protections, write, and restore original protection
+                               task_suspend(task);
+                               if ( (kret = mach_vm_protect(task, modAddress, sizeof(newValue), FALSE, VM_PROT_WRITE | VM_PROT_READ | VM_PROT_COPY)) )
+                               {
+                                       printf("mach_vm_protect failed with error %d.", kret);
+                                       exit(1);
+                               }
+
+                               if ( (kret = mach_vm_write(task, modAddress, (pointer_t)&newValue, sizeof(newValue))) )
+                               {
+                                       printf("mach_vm_write failed at 0x%llx with error %d.", modAddress, kret);
+                                       exit(1);
+                               }

-                               if ((kret = mach_vm_write(task, modAddress, (pointer_t)&newValue, sizeof(newValue))) != KERN_SUCCESS) printf("mach_vm_write failed, error %d: %s\n", kret, mach_error_string(kret));
+                               // restore original protection
+                               if ( (kret = mach_vm_protect(task, modAddress, sizeof(newValue), FALSE, info.protection)) )
+                               {
+                                       printf("mach_vm_protect failed with error %d.", kret);
+                                       exit(1);
+                               }
+                               task_resume(task);
+
                                goto NextAction;
                        }
                case 2:
@@ -202,21 +235,12 @@ int main(int argc, char *argv[])
                                        pointer_t buffer;
                                        size = sizeof(int); // because oldValue and newValue are int
                                        mach_msg_type_number_t bufferSize = size;
-#if CGFLOAT_IS_DOUBLE
                                        long substring = [substringNumber longValue];
                                        if ((kret = mach_vm_read(task, (mach_vm_address_t)substring, size, &buffer, &bufferSize)) == KERN_SUCCESS)
                                        {
                                                printf("Search result %2d: %ld at 0x%0llx (%s)\n", i + 1, *(long *)buffer, (mach_vm_address_t)substring, [[protectionArray objectAtIndex:i] UTF8String]);
                                        }
                                        else printf("mach_vm_read failed at 0x%0llx, error %d: %s\n", (mach_vm_address_t)substring, kret, mach_error_string(kret));
-#else
-                                       int substring = [substringNumber intValue];
-                                       if ((kret = mach_vm_read(task, (mach_vm_address_t)substring, size, &buffer, &bufferSize)) == KERN_SUCCESS)
-                                       {
-                                               printf("Search result %2d: %ld at 0x%0llx (%s)\n", i + 1, *(long *)buffer, (mach_vm_address_t)substring, [[protectionArray objectAtIndex:i] UTF8String]);
-                                       }
-                                       else printf("mach_vm_read failed at 0x%0llx, error %d: %s\n", (mach_vm_address_t)substring, kret, mach_error_string(kret));
-#endif
                                }
                                goto NextAction;
                        }

What’re these <, > and the hex numbers like 44,46d43?