Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- * feedface: An injection, scanning, and general interaction tool for Mac OS X (for interaction with 32 bit and 64 bit x86 processes).
- *
- * Written for Mac OS X 10.6+ as a 64 bit application.
- *
- * Compilation instructions:
- * clang feedface.m -o feedface -framework Foundation
- * gcc feedface.m -o feedface -std=gnu99 -framework Foundation
- * llvm-gcc feedface.m -o feedface -std=gnu99 -framework Foundation
- *
- * Executable: http://www.mediafire.com/?fk3u06yn3338cg3
- *
- * Version: 0.21
- * Changes:
- * Added support for relocating an address for a specific image in the target task.
- */
- #import <Foundation/Foundation.h>
- #import <mach/mach.h>
- #import <mach/mach_vm.h>
- #import <mach-o/dyld_images.h>
- #import <mach-o/loader.h>
- void DisplayUsage(void);
- int GetBytesFromString(const char * restrict, const size_t, uint8_t * restrict);
- int InjectBytes(vm_map_t, mach_vm_address_t, mach_vm_address_t, const size_t);
- int ReadBytes(vm_map_t, mach_vm_address_t, mach_vm_address_t, const size_t, size_t *);
- int IsProc64(vm_map_t, _Bool * const);
- void *Locate_dlsym(vm_map_t);
- int SuspendTaskWithNoExecutionInRange(vm_map_t, mach_vm_address_t, mach_vm_address_t); //Wow what a name :P
- int AddressFromString(vm_map_t, const char *, mach_vm_address_t *);
- int RelocateAddressInTask(vm_map_t, mach_vm_address_t * const, const char * const);
- int Command_Bytes(vm_map_t, vm_map_t, const char **, const int);
- int Command_String(vm_map_t, vm_map_t, const char **, const int);
- int Command_Codecave(vm_map_t, vm_map_t, const char **, const int);
- //int Command_Bundle(vm_map_t, vm_map_t, const char **, const int);
- //See mach-o/dyld_images.h to update for later versions.
- struct dyld_all_image_infos32 {
- uint32_t version;
- uint32_t infoArrayCount;
- uint32_t infoArray;
- uint32_t notification;
- bool processDetachedFromSharedRegion;
- bool libSystemInitialized;
- uint32_t dyldImageLoadAddress;
- uint32_t jitInfo;
- uint32_t dyldVersion;
- uint32_t errorMessage;
- uint32_t terminationFlags;
- uint32_t coreSymbolicationShmPage;
- uint32_t systemOrderFlag;
- uint32_t uuidArrayCount;
- uint32_t uuidArray;
- uint32_t dyldAllImageInfosAddress;
- uint32_t initialImageCount;
- uint32_t errorKind;
- uint32_t errorClientOfDylibPath;
- uint32_t errorTargetDylibPath;
- uint32_t errorSymbol;
- uint32_t sharedCacheSlide;
- };
- struct dyld_image_info32 {
- uint32_t imageLoadAddress;
- uint32_t imageFilePath;
- uint32_t imageFileModDate;
- };
- struct dyld_all_image_infos64 {
- uint32_t version;
- uint32_t infoArrayCount;
- uint64_t infoArray;
- uint64_t notification;
- bool processDetachedFromSharedRegion;
- bool libSystemInitialized;
- uint64_t dyldImageLoadAddress;
- uint64_t jitInfo;
- uint64_t dyldVersion;
- uint64_t errorMessage;
- uint64_t terminationFlags;
- uint64_t coreSymbolicationShmPage;
- uint64_t systemOrderFlag;
- uint64_t uuidArrayCount;
- uint64_t uuidArray;
- uint64_t dyldAllImageInfosAddress;
- uint64_t initialImageCount;
- uint64_t errorKind;
- uint64_t errorClientOfDylibPath;
- uint64_t errorTargetDylibPath;
- uint64_t errorSymbol;
- uint64_t sharedCacheSlide;
- };
- struct dyld_image_info64 {
- uint64_t imageLoadAddress;
- uint64_t imageFilePath;
- uint64_t imageFileModDate;
- };
- const struct {
- const char *flag;
- const char *example;
- int min, max;
- int (*execute)(vm_map_t, vm_map_t, const char **, const int);
- } Options[] = {
- { .flag = "-bytes", .example = "[bytes in hex (no spaces)] [address in process to inject to]", 2, 2, Command_Bytes },
- { .flag = "-string", .example = "[string (use \\\\x for hex values)] [address in process to inject to]", 2, 2, Command_String },
- { .flag = "-codecave", .example = "[address in process to codecave from] [optional (min = 5 for 32 bit or 14 for 64 bit, if not used defaults to min): -s followed by the size of original code to remove] [code to be injected: [optional: -oc will place the original code before your code] [optional: bytes in hex (no spaces)]]", 2, INT_MAX, Command_Codecave },
- //{ .flag = "-bundle", .example = "", 0, 0, Command_Bundle }
- };
- int main(int argc, char *argv[])
- {
- if (argc < 3)
- {
- DisplayUsage();
- return EXIT_FAILURE;
- }
- errno = 0;
- pid_t Target = (pid_t)strtol(argv[1], NULL, 10);
- if (errno)
- {
- //Don't bother checking just report invalid number
- printf("Invalid format for pid: %s\n", argv[1]);
- return EXIT_FAILURE;
- }
- vm_map_t RemoteTask, TaskSelf = mach_task_self();
- mach_error_t err = task_for_pid(TaskSelf, Target, &RemoteTask);
- if (err != KERN_SUCCESS)
- {
- mach_error("task_for_pid", err);
- if (err == 5) printf("Invalid PID or not running as root\n");
- return EXIT_FAILURE;
- }
- for (size_t Loop = 0; Loop < sizeof(Options) / sizeof(typeof(*Options)); Loop++)
- {
- if (!strcmp(Options[Loop].flag, argv[2]))
- {
- if ((argc < 3 + Options[Loop].min) || (argc > (unsigned int)(3 + Options[Loop].max)))
- {
- DisplayUsage();
- return EXIT_FAILURE;
- }
- return Options[Loop].execute(TaskSelf, RemoteTask, (const char**)&argv[3], argc - 3);
- }
- }
- printf("Invalid option: %s\n", argv[2]);
- DisplayUsage();
- return EXIT_FAILURE;
- }
- void DisplayUsage(void)
- {
- printf("inject pid Option\n"
- "Options:\n");
- for (size_t Loop = 0; Loop < sizeof(Options) / sizeof(typeof(*Options)); Loop++)
- {
- printf("\t%s %s\n", Options[Loop].flag, Options[Loop].example);
- }
- }
- int GetBytesFromString(const char * restrict Str, const size_t Length, uint8_t * restrict Bytes)
- {
- errno = 0;
- const char *Hex = Str;
- for (size_t Loop = 0; Loop < Length; Loop += 2, Hex += 2)
- {
- Bytes[Loop / 2] = strtol((char[3]){ Hex[0], Hex[1], 0 }, NULL, 16);
- if (errno)
- {
- //Don't bother checking just report invalid number
- printf("Invalid hexadecimal value (%2s) in bytes: %s\n", Hex, Str);
- return -1;
- }
- }
- return 0;
- }
- int InjectBytes(vm_map_t RemoteTask, mach_vm_address_t Address, mach_vm_address_t Bytes, const size_t Size)
- {
- mach_vm_size_t RegionSize;
- vm_region_basic_info_data_64_t Info;
- mach_port_t ObjectName;
- mach_msg_type_number_t Count = VM_REGION_BASIC_INFO_COUNT_64;
- mach_error_t err = mach_vm_region(RemoteTask, &(mach_vm_address_t){ Address }, &RegionSize, VM_REGION_BASIC_INFO_64, (vm_region_info_t)&Info, &Count, &ObjectName);
- if (err != KERN_SUCCESS)
- {
- mach_error("mach_vm_region", err);
- printf("Region error: %u\n", err);
- return -1;
- }
- err = mach_vm_protect(RemoteTask, Address, Size, FALSE, VM_PROT_ALL);
- if (err != KERN_SUCCESS)
- {
- mach_error("mach_vm_protect", err);
- printf("Protection error: %u\n", err);
- return -1;
- }
- err = mach_vm_write(RemoteTask, Address, Bytes, (mach_msg_type_number_t)Size);
- if (err != KERN_SUCCESS)
- {
- mach_error("mach_vm_write", err);
- printf("Writing error: %u\n", err);
- return -1;
- }
- err = mach_vm_protect(RemoteTask, Address, Size, FALSE, Info.protection);
- if (err != KERN_SUCCESS)
- {
- mach_error("mach_vm_protect", err);
- printf("Protection error: %u\n", err);
- /*
- While the injection should have succeeded, was unable to return
- the protection for that region back to it's original rights.
- */
- printf("Current protection left as VM_PROT_ALL, but was originally:");
- if (Info.protection & VM_PROT_READ)
- {
- printf(" VM_PROT_READ");
- }
- if (Info.protection & VM_PROT_WRITE)
- {
- printf(" VM_PROT_WRITE");
- }
- if (Info.protection & VM_PROT_EXECUTE)
- {
- printf(" VM_PROT_EXECUTE");
- }
- printf("\n");
- return -1;
- }
- return 0;
- }
- int ReadBytes(vm_map_t RemoteTask, mach_vm_address_t Address, mach_vm_address_t Store, const size_t Size, size_t *ReadSize)
- {
- /*
- This much like InjectBytes is a rather redundant process, it should really only be used when unsure of the current access rights.
- */
- mach_vm_size_t RegionSize;
- vm_region_basic_info_data_64_t Info;
- mach_port_t ObjectName;
- mach_msg_type_number_t Count = VM_REGION_BASIC_INFO_COUNT_64;
- mach_error_t err = mach_vm_region(RemoteTask, &(mach_vm_address_t){ Address }, &RegionSize, VM_REGION_BASIC_INFO_64, (vm_region_info_t)&Info, &Count, &ObjectName);
- if (err != KERN_SUCCESS)
- {
- mach_error("mach_vm_region", err);
- printf("Region error: %u\n", err);
- return -1;
- }
- err = mach_vm_protect(RemoteTask, Address, Size, FALSE, VM_PROT_ALL);
- if (err != KERN_SUCCESS)
- {
- mach_error("mach_vm_protect", err);
- printf("Protection error: %u\n", err);
- return -1;
- }
- mach_vm_size_t Read;
- err = mach_vm_read_overwrite(RemoteTask, Address, Size, Store, &Read);
- if (err != KERN_SUCCESS)
- {
- mach_error("mach_vm_read_overwrite", err);
- printf("Read error: %u\n", err);
- return -1;
- }
- if (ReadSize) *ReadSize = Read;
- err = mach_vm_protect(RemoteTask, Address, Size, FALSE, Info.protection);
- if (err != KERN_SUCCESS)
- {
- mach_error("mach_vm_protect", err);
- printf("Protection error: %u\n", err);
- printf("Current protection left as VM_PROT_ALL, but was originally:");
- if (Info.protection & VM_PROT_READ)
- {
- printf(" VM_PROT_READ");
- }
- if (Info.protection & VM_PROT_WRITE)
- {
- printf(" VM_PROT_WRITE");
- }
- if (Info.protection & VM_PROT_EXECUTE)
- {
- printf(" VM_PROT_EXECUTE");
- }
- printf("\n");
- return -1;
- }
- return 0;
- }
- int IsProc64(vm_map_t Task, _Bool * const Is64)
- {
- task_dyld_info_data_t DyldInfo;
- mach_error_t err = task_info(Task, TASK_DYLD_INFO, (task_info_t)&DyldInfo, &(mach_msg_type_number_t){ TASK_DYLD_INFO_COUNT });
- if (err != KERN_SUCCESS)
- {
- mach_error("task_info", err);
- printf("Task info error: %u\n", err);
- return -1;
- }
- if (!DyldInfo.all_image_info_addr)
- {
- printf("Error\n");
- return -1;
- }
- *Is64 = (uint64_t)DyldInfo.all_image_info_addr > UINT32_MAX; //Quick cheap hack, but as of now it should always work because of what regions are reserved for what.
- return 0;
- }
- void *Locate_dlsym(vm_map_t RemoteTask)
- {
- void *dlsymPtr = NULL;
- mach_msg_type_number_t Count = TASK_DYLD_INFO_COUNT;
- task_dyld_info_data_t DyldInfo;
- mach_error_t err = task_info(RemoteTask, TASK_DYLD_INFO, (task_info_t)&DyldInfo, &Count);
- if (err != KERN_SUCCESS)
- {
- mach_error("task_info", err);
- printf("Task info error: %u\n", err);
- return NULL;
- }
- if (!DyldInfo.all_image_info_addr)
- {
- printf("Error\n");
- return NULL;
- }
- mach_vm_size_t ReadSize;
- union {
- struct dyld_all_image_infos32 infos32;
- struct dyld_all_image_infos64 infos64;
- } ImageInfos;
- size_t ImageInfosSize = DyldInfo.all_image_info_size;
- if (sizeof(ImageInfos) < ImageInfosSize) ImageInfosSize = sizeof(ImageInfos); //Later version being used. If needing new elements add them.
- err = mach_vm_read_overwrite(RemoteTask, DyldInfo.all_image_info_addr, ImageInfosSize, (mach_vm_address_t)&ImageInfos, &ReadSize);
- if (err != KERN_SUCCESS)
- {
- mach_error("mach_vm_read_overwrite", err);
- printf("Read error: %u\n", err);
- return NULL;
- }
- _Bool Is64 = (uint64_t)DyldInfo.all_image_info_addr > UINT32_MAX; //Quick cheap hack, but as of now it should always work because of what regions are reserved for what.
- mach_vm_address_t DyldImageLoadAddress = (mach_vm_address_t)(Is64? ImageInfos.infos64.dyldImageLoadAddress : ImageInfos.infos32.dyldImageLoadAddress);
- struct mach_header DyldHeader;
- err = mach_vm_read_overwrite(RemoteTask, DyldImageLoadAddress, sizeof(DyldHeader), (mach_vm_address_t)&DyldHeader, &ReadSize);
- if (err != KERN_SUCCESS)
- {
- mach_error("mach_vm_read_overwrite", err);
- printf("Read error: %u\n", err);
- return NULL;
- }
- void *LoadCommands = malloc(DyldHeader.sizeofcmds);
- if (!LoadCommands)
- {
- printf("Could not allocate memory to copy libdyld.dylib's load commands\n");
- return NULL;
- }
- size_t MachHeaderSize = (DyldHeader.magic == MH_MAGIC_64 ? sizeof(struct mach_header_64) : sizeof(struct mach_header));
- err = mach_vm_read_overwrite(RemoteTask, DyldImageLoadAddress + MachHeaderSize, DyldHeader.sizeofcmds, (mach_vm_address_t)LoadCommands, &ReadSize);
- if (err != KERN_SUCCESS)
- {
- mach_error("mach_vm_read_overwrite", err);
- printf("Read error: %u\n", err);
- free(LoadCommands);
- return NULL;
- }
- ptrdiff_t CommandSize = 0;
- for (uint32_t Loop2 = 0; Loop2 < DyldHeader.ncmds; Loop2++)
- {
- const uint32_t cmd = ((struct load_command*)(LoadCommands + CommandSize))->cmd;
- if ((Is64 = (cmd == LC_SEGMENT_64)) || (cmd == LC_SEGMENT))
- {
- const void *DataSections;
- uint32_t NSects;
- const char *Segname;
- size_t SectionSize, SectionMember_sectname, SectionMember_size, SectionMember_addr;
- uint64_t Cap;
- if (Is64)
- {
- const struct segment_command_64* const Segment = (struct segment_command_64*)(LoadCommands + CommandSize);
- Segname = Segment->segname;
- NSects = Segment->nsects;
- DataSections = (struct section_64*)(LoadCommands + CommandSize + sizeof(struct segment_command_64));
- SectionSize = sizeof(struct section_64);
- SectionMember_sectname = offsetof(struct section_64, sectname); //Not needed (nor would it be changed anytime in the near future, since it would break things), but will be used for clarity.
- SectionMember_size = offsetof(struct section_64, size);
- SectionMember_addr = offsetof(struct section_64, addr);
- Cap = UINT64_MAX;
- }
- else
- {
- const struct segment_command* const Segment = (struct segment_command*)(LoadCommands + CommandSize);
- Segname = Segment->segname;
- NSects = Segment->nsects;
- DataSections = (struct section*)(LoadCommands + CommandSize + sizeof(struct segment_command));
- SectionSize = sizeof(struct section);
- SectionMember_sectname = offsetof(struct section, sectname);
- SectionMember_size = offsetof(struct section, size);
- SectionMember_addr = offsetof(struct section, addr);
- Cap = UINT32_MAX;
- }
- if (!strcmp("__DATA", Segname))
- {
- for (uint32_t Loop3 = 0; Loop3 < NSects; Loop3++)
- {
- const void *TempDataSection = DataSections + (Loop3 * SectionSize);
- if (!strcmp("__const", (char*)(TempDataSection + SectionMember_sectname))) //__DATA,__const contains the dyld_func table
- {
- void * const DyldFuncs = malloc(*(uint64_t*)(TempDataSection + SectionMember_size) & Cap);
- if (!DyldFuncs)
- {
- printf("Could not allocate memory to copy __dyld__dyld_funcs\n");
- free(LoadCommands);
- return NULL;
- }
- /*
- Account for relocation, since the header contains fixed offsets.
- e.g.
- fixed
- all_image_info_addr: 0x7fff5fc36d10
- dyldImageLoadAddress: 0x7fff5fc00000
- __DATA,__const: 0x7fff5fc35900
- not-fixed
- all_image_info_addr: 0x7fff6bdeed10
- dyldImageLoadAddress: 0x7fff6bdb8000
- __DATA,__const: 0x7fff5fc35900
- Correct address in the second one would be 0x7fff6bded900
- Or for 32 bit
- fixed
- dyldImageLoadAddress: 0x8fe00000
- __DATA,__const: 0x8fe336c0
- not-fixed
- dyldImageLoadAddress: 0x8fe84000
- __DATA,__const: 0x8fe336c0
- Correct address in the second one would be 0x8feb76c0
- */
- ptrdiff_t Base = DyldImageLoadAddress;
- Base += (*(uint64_t*)(TempDataSection + SectionMember_addr) /*& Cap*/) & 0xfffff;
- err = mach_vm_read_overwrite(RemoteTask, (mach_vm_address_t)Base, *(uint64_t*)(TempDataSection + SectionMember_size) & Cap, (mach_vm_address_t)DyldFuncs, &ReadSize);
- if (err != KERN_SUCCESS)
- {
- mach_error("mach_vm_read_overwrite", err);
- printf("Read error: %u\n", err);
- free(LoadCommands);
- free(DyldFuncs);
- return NULL;
- }
- //If the table's location ever changes, instead should scan through it until coming up to table.
- const size_t RowSize = Is64? sizeof(uint64_t) * 2 : sizeof(uint32_t) * 2, TableElement_addr = Is64? sizeof(uint64_t) : sizeof(uint32_t);
- for (uint64_t Loop4 = 0, DyldFuncCount = (*(uint64_t*)(TempDataSection + SectionMember_size) & Cap) / RowSize; Loop4 < DyldFuncCount; Loop4++)
- {
- void *TempRow = DyldFuncs + (Loop4 * RowSize);
- mach_vm_address_t Name = *(uint64_t*)(TempRow) & Cap;
- if (!Name) break;
- char FunctionName[512]; //Just an estimate. If it's ever a problem, change it.
- /*
- A better way to go about it may be to copy the __TEXT,__cstring section and adjust the pointers
- appropriately.
- */
- err = mach_vm_read_overwrite(RemoteTask, (mach_vm_address_t)Name, sizeof(FunctionName), (mach_vm_address_t)FunctionName, &ReadSize);
- if (err != KERN_SUCCESS)
- {
- mach_error("mach_vm_read_overwrite", err);
- printf("Read error: %u\n", err);
- break;
- }
- if (!strcmp("__dyld_dlsym", FunctionName))
- {
- dlsymPtr = (void*)(*(uint64_t*)(TempRow + TableElement_addr) & Cap);
- break;
- }
- }
- free(DyldFuncs);
- break;
- }
- }
- break;
- }
- }
- CommandSize += ((struct load_command*)(LoadCommands + CommandSize))->cmdsize;
- }
- free(LoadCommands);
- return dlsymPtr;
- }
- int SuspendTaskWithNoExecutionInRange(vm_map_t RemoteTask, mach_vm_address_t Start, mach_vm_address_t End)
- {
- _Bool Is64;
- if (IsProc64(RemoteTask, &Is64) == -1) return -1;
- mach_error_t err = task_suspend(RemoteTask);
- if (err != KERN_SUCCESS)
- {
- mach_error("task_suspend", err);
- printf("Task suspension error: %u\n", err);
- return -1;
- }
- thread_act_port_array_t Threads;
- mach_msg_type_number_t Count;
- err = task_threads(RemoteTask, &Threads, &Count);
- if (err != KERN_SUCCESS)
- {
- mach_error("task_threads", err);
- printf("Task thread query error: %u\n", err);
- err = task_resume(RemoteTask);
- if (err != KERN_SUCCESS)
- {
- mach_error("task_resume", err);
- printf("Task continuation error: %u\n", err);
- }
- return -1;
- }
- if (Is64)
- {
- x86_thread_state64_t ThreadState;
- for (size_t Loop = 0; Loop < Count; Loop++)
- {
- mach_msg_type_number_t ThreadStateCount = x86_THREAD_STATE64_COUNT;
- err = thread_get_state(Threads[Loop], x86_THREAD_STATE64, (thread_state_t)&ThreadState, &ThreadStateCount);
- if (err != KERN_SUCCESS)
- {
- mach_error("thread_get_state", err);
- printf("Thread state query error: %u\n", err);
- err = task_resume(RemoteTask);
- if (err != KERN_SUCCESS)
- {
- mach_error("task_resume", err);
- printf("Task continuation error: %u\n", err);
- }
- return -1;
- }
- if ((ThreadState.__rip >= Start) && (ThreadState.__rip <= End))
- {
- err = task_resume(RemoteTask);
- if (err != KERN_SUCCESS)
- {
- mach_error("task_resume", err);
- printf("Task continuation error: %u\n", err);
- return -1;
- }
- [NSThread sleepForTimeInterval: 0.25];
- //Hope infinite recursions will not occur (obviously producing a crash)
- return SuspendTaskWithNoExecutionInRange(RemoteTask, Start, End);
- }
- }
- }
- else
- {
- x86_thread_state32_t ThreadState;
- for (size_t Loop = 0; Loop < Count; Loop++)
- {
- mach_msg_type_number_t ThreadStateCount = x86_THREAD_STATE32_COUNT;
- err = thread_get_state(Threads[Loop], x86_THREAD_STATE32, (thread_state_t)&ThreadState, &ThreadStateCount);
- if (err != KERN_SUCCESS)
- {
- mach_error("thread_get_state", err);
- printf("Thread state query error: %u\n", err);
- err = task_resume(RemoteTask);
- if (err != KERN_SUCCESS)
- {
- mach_error("task_resume", err);
- printf("Task continuation error: %u\n", err);
- }
- return -1;
- }
- if ((ThreadState.__eip >= Start) && (ThreadState.__eip <= End))
- {
- err = task_resume(RemoteTask);
- if (err != KERN_SUCCESS)
- {
- mach_error("task_resume", err);
- printf("Task continuation error: %u\n", err);
- return -1;
- }
- [NSThread sleepForTimeInterval: 0.25];
- return SuspendTaskWithNoExecutionInRange(RemoteTask, Start, End);
- }
- }
- }
- return 0;
- }
- int RelocateAddressInTask(vm_map_t Task, mach_vm_address_t * const Address, const char * const Image)
- {
- mach_msg_type_number_t Count = TASK_DYLD_INFO_COUNT;
- task_dyld_info_data_t DyldInfo;
- mach_error_t err = task_info(Task, TASK_DYLD_INFO, (task_info_t)&DyldInfo, &Count);
- if (err != KERN_SUCCESS)
- {
- mach_error("task_info", err);
- printf("Task info error: %u\n", err);
- return -1;
- }
- if (!DyldInfo.all_image_info_addr)
- {
- printf("Error\n");
- return -1;
- }
- mach_vm_size_t ReadSize;
- union {
- struct dyld_all_image_infos32 infos32;
- struct dyld_all_image_infos64 infos64;
- } ImageInfos;
- size_t ImageInfosSize = DyldInfo.all_image_info_size;
- if (sizeof(ImageInfos) < ImageInfosSize) ImageInfosSize = sizeof(ImageInfos); //Later version being used. If needing new elements add them.
- err = mach_vm_read_overwrite(Task, DyldInfo.all_image_info_addr, ImageInfosSize, (mach_vm_address_t)&ImageInfos, &ReadSize);
- if (err != KERN_SUCCESS)
- {
- mach_error("mach_vm_read_overwrite", err);
- printf("Read error: %u\n", err);
- return -1;
- }
- _Bool Is64 = (uint64_t)DyldInfo.all_image_info_addr > UINT32_MAX; //Quick cheap hack, but as of now it should always work because of what regions are reserved for what.
- uint32_t InfoArrayCount;
- uint64_t InfoArray, ImageInfoSize;
- if (Is64)
- {
- InfoArrayCount = ImageInfos.infos64.infoArrayCount;
- InfoArray = ImageInfos.infos64.infoArray;
- ImageInfoSize = sizeof(struct dyld_image_info64);
- }
- else
- {
- InfoArrayCount = ImageInfos.infos32.infoArrayCount;
- InfoArray = ImageInfos.infos32.infoArray;
- ImageInfoSize = sizeof(struct dyld_image_info32);
- }
- union {
- struct dyld_image_info64 info64;
- struct dyld_image_info32 info32;
- } ImageInfo;
- if (Image)
- {
- const _Bool FullPath = (strchr(Image, '/') != NULL);
- _Bool Match = FALSE;
- for (uint32_t Loop = 0; (Loop < InfoArrayCount) && (!Match); Loop++)
- {
- err = mach_vm_read_overwrite(Task, (mach_vm_address_t)(InfoArray + (ImageInfoSize * Loop)), ImageInfoSize, (mach_vm_address_t)&ImageInfo, &ReadSize);
- if (err != KERN_SUCCESS)
- {
- mach_error("mach_vm_read_overwrite", err);
- printf("Read error: %u\n", err);
- return -1;
- }
- char FilePath[PATH_MAX];
- err = mach_vm_read_overwrite(Task, (mach_vm_address_t)(Is64? ImageInfo.info64.imageFilePath : ImageInfo.info32.imageFilePath), sizeof(FilePath), (mach_vm_address_t)FilePath, &ReadSize);
- if (err != KERN_SUCCESS)
- {
- mach_error("mach_vm_read_overwrite", err);
- printf("Read error: %u\n", err);
- return -1;
- }
- if (FullPath)
- {
- Match = !strcmp(Image, FilePath);
- }
- else
- {
- NSAutoreleasePool *Pool = [NSAutoreleasePool new];
- Match = !strcmp(Image, [[[[NSString stringWithUTF8String: FilePath] pathComponents] lastObject] UTF8String]);
- [Pool drain];
- }
- }
- if (!Match)
- {
- printf("Could not find image: %s\n", Image);
- return -1;
- }
- }
- else
- {
- //Assumes first element in info array is the app itself.
- err = mach_vm_read_overwrite(Task, (mach_vm_address_t)InfoArray, ImageInfoSize, (mach_vm_address_t)&ImageInfo, &ReadSize);
- if (err != KERN_SUCCESS)
- {
- mach_error("mach_vm_read_overwrite", err);
- printf("Read error: %u\n", err);
- return -1;
- }
- }
- *Address = (*Address & 0xfffffff) + (mach_vm_address_t)(Is64? ImageInfo.info64.imageLoadAddress : ImageInfo.info32.imageLoadAddress);
- return 0;
- }
- int AddressFromString(vm_map_t Task, const char *String, mach_vm_address_t * const Address)
- {
- errno = 0;
- if (!strncmp(String, "rel", 3))
- {
- *Address = strtol(&String[4], NULL, 16);
- if (errno)
- {
- //Don't bother checking just report invalid number
- printf("Invalid address, value must be hexadecimal: %s\n", String);
- return -1;
- }
- char *ImageFile = NULL;
- char File[PATH_MAX];
- size_t Image = strcspn(&String[4], ",:- ") + 5;
- if (String[Image - 1])
- {
- size_t Length = strlen(&String[Image]) - (String[3] != ' ');
- strncpy(File, &String[Image], Length);
- File[Length] = 0;
- ImageFile = File;
- }
- return RelocateAddressInTask(Task, Address, ImageFile);
- }
- else
- {
- *Address = strtol(String, NULL, 16);
- if (errno)
- {
- //Don't bother checking just report invalid number
- printf("Invalid address, value must be hexadecimal: %s\n", String);
- return -1;
- }
- }
- return 0;
- }
- int Command_Bytes(vm_map_t TaskSelf, vm_map_t RemoteTask, const char **Args, const int Argc)
- {
- mach_vm_address_t Address;
- if (AddressFromString(RemoteTask, Args[1], &Address) == -1) return EXIT_FAILURE;
- const size_t TotalLength = strlen(Args[0]);
- if (TotalLength & 1)
- {
- printf("Bytes must be multiples of 2\n");
- return EXIT_FAILURE;
- }
- mach_vm_address_t Bytes;
- const size_t Length = TotalLength / 2;
- mach_error_t err = mach_vm_allocate(TaskSelf, &Bytes, Length, VM_FLAGS_ANYWHERE);
- if (err != KERN_SUCCESS)
- {
- mach_error("mach_vm_allocate", err);
- printf("Allocation error: %u\n", err);
- return EXIT_FAILURE;
- }
- int RetVal;
- if (GetBytesFromString(Args[0], TotalLength, (uint8_t*)Bytes) == -1)
- {
- RetVal = EXIT_FAILURE;
- }
- else
- {
- RetVal = InjectBytes(RemoteTask, Address, Bytes, Length) == -1 ? EXIT_FAILURE : EXIT_SUCCESS;
- }
- mach_vm_deallocate(TaskSelf, Bytes, Length);
- /*
- Just assume this passes, otherwise just let OS clean up. So still return
- success, as the injection operation has succeeded.
- */
- return RetVal;
- }
- int Command_String(vm_map_t TaskSelf, vm_map_t RemoteTask, const char **Args, const int Argc)
- {
- mach_vm_address_t Address;
- if (AddressFromString(RemoteTask, Args[1], &Address) == -1) return EXIT_FAILURE;
- size_t Length = strlen(Args[0]);
- mach_vm_address_t Bytes;
- mach_error_t err = mach_vm_allocate(TaskSelf, &Bytes, Length, VM_FLAGS_ANYWHERE);
- if (err != KERN_SUCCESS)
- {
- mach_error("mach_vm_allocate", err);
- printf("Allocation error: %u\n", err);
- return EXIT_FAILURE;
- }
- size_t Size = 0;
- const char *String = Args[0];
- for (size_t Loop = 0; Loop < Length; Loop++)
- {
- if (Loop + 3 < Length)
- {
- if ((String[Loop] == '\\') && (String[Loop + 1] == 'x'))
- {
- const unsigned char Val = strtol((char[3]){ String[Loop + 2], String[Loop + 3], 0 }, NULL, 16);
- if (!errno)
- {
- ((unsigned char*)Bytes)[Size++] = Val;
- Loop += 3;
- }
- else ((unsigned char*)Bytes)[Size++] = String[Loop];
- continue;
- }
- }
- ((unsigned char*)Bytes)[Size++] = String[Loop];
- }
- int RetVal = InjectBytes(RemoteTask, Address, Bytes, Size) == -1 ? EXIT_FAILURE : EXIT_SUCCESS;
- mach_vm_deallocate(TaskSelf, Bytes, Length);
- /*
- Just assume this passes, otherwise just let OS clean up. So still return
- success, as the injection operation has succeeded.
- */
- return RetVal;
- }
- int Command_Codecave(vm_map_t TaskSelf, vm_map_t RemoteTask, const char **Args, const int Argc)
- {
- mach_vm_address_t Address;
- if (AddressFromString(RemoteTask, Args[0], &Address) == -1) return EXIT_FAILURE;
- _Bool Is64;
- if (IsProc64(RemoteTask, &Is64) == -1)
- {
- return EXIT_FAILURE;
- }
- size_t Size = Is64? 14 : 5, ArgIndex = 1;
- if (!strcmp("-s", Args[ArgIndex]))
- {
- size_t TempSize = strtol(Args[++ArgIndex], NULL, 10);
- if (errno)
- {
- printf("Invalid size, value must be decimal: %s\n", Args[2]);
- return EXIT_FAILURE;
- }
- if (TempSize > Size) Size = TempSize;
- ArgIndex++;
- }
- void *dlsymPtr = Locate_dlsym(RemoteTask);
- if (!dlsymPtr)
- {
- printf("Could not located dlsym\n"); //Later change it to prompt user
- return EXIT_FAILURE;
- }
- size_t ReadSize = 0;
- void *OriginalCode = malloc(Size);
- if (OriginalCode)
- {
- if (ReadBytes(RemoteTask, Address, (mach_vm_address_t)OriginalCode, Size, &ReadSize) == -1)
- {
- free(OriginalCode);
- OriginalCode = NULL;
- }
- }
- size_t CodeSize = 0;
- uint8_t *Data = NULL;
- int RetVal = EXIT_SUCCESS;
- for (size_t Loop = ArgIndex; Loop < Argc; Loop++)
- {
- if (!strcmp("-oc", Args[Loop]))
- {
- const size_t NewCodeSize = CodeSize + ReadSize;
- uint8_t *TempData = realloc(Data, sizeof(uint8_t) * NewCodeSize);
- if (!TempData)
- {
- printf("Allocation error\n");
- RetVal = EXIT_FAILURE;
- break;
- }
- Data = TempData;
- memcpy(Data + CodeSize, OriginalCode, ReadSize);
- CodeSize = NewCodeSize;
- }
- else if (!strncmp("rel", Args[Loop], 3))
- {
- mach_vm_address_t RelAddr;
- if (AddressFromString(RemoteTask, Args[Loop], &RelAddr) == -1)
- {
- RetVal = EXIT_FAILURE;
- break;
- }
- if (Is64)
- {
- const size_t NewCodeSize = CodeSize + sizeof(uint64_t);
- uint8_t *TempData = realloc(Data, sizeof(uint8_t) * NewCodeSize);
- if (!TempData)
- {
- printf("Allocation error\n");
- RetVal = EXIT_FAILURE;
- break;
- }
- Data = TempData;
- memcpy(Data + CodeSize, &RelAddr, sizeof(uint64_t));
- CodeSize = NewCodeSize;
- }
- else
- {
- const size_t NewCodeSize = CodeSize + sizeof(uint32_t);
- uint8_t *TempData = realloc(Data, sizeof(uint8_t) * NewCodeSize);
- if (!TempData)
- {
- printf("Allocation error\n");
- RetVal = EXIT_FAILURE;
- break;
- }
- Data = TempData;
- memcpy(Data + CodeSize, (&(uint32_t){ (uint32_t)RelAddr }), sizeof(uint32_t));
- CodeSize = NewCodeSize;
- }
- }
- else
- {
- const size_t TotalLength = strlen(Args[Loop]);
- if (TotalLength & 1)
- {
- printf("Bytes must be multiples of 2\n");
- RetVal = EXIT_FAILURE;
- break;
- }
- const size_t NewCodeSize = CodeSize + (TotalLength / 2);
- uint8_t *TempData = realloc(Data, sizeof(uint8_t) * NewCodeSize);
- if (!TempData)
- {
- printf("Allocation error\n");
- RetVal = EXIT_FAILURE;
- break;
- }
- Data = TempData;
- if (GetBytesFromString(Args[Loop], TotalLength, Data + CodeSize) == -1)
- {
- RetVal = EXIT_FAILURE;
- break;
- }
- CodeSize = NewCodeSize;
- }
- }
- if (OriginalCode) free(OriginalCode);
- if ((RetVal == EXIT_SUCCESS) && (Data))
- {
- const size_t Length = CodeSize + (Is64? sizeof(uint64_t) + 14 : sizeof(uint32_t) + 5);
- mach_vm_address_t Bytes = 0;
- mach_error_t err = mach_vm_allocate(TaskSelf, &Bytes, Length, VM_FLAGS_ANYWHERE);
- if (err != KERN_SUCCESS)
- {
- mach_error("mach_vm_allocate", err);
- printf("Allocation error: %u\n", err);
- free(Data);
- return EXIT_FAILURE;
- }
- memcpy((void*)Bytes, Data, CodeSize);
- free(Data);
- mach_vm_address_t Codecave;
- err = mach_vm_allocate(RemoteTask, &Codecave, Length, VM_FLAGS_ANYWHERE);
- if (err != KERN_SUCCESS)
- {
- mach_error("mach_vm_allocate", err);
- printf("Allocation error: %u\n", err);
- return EXIT_FAILURE;
- }
- if (Is64)
- {
- /*
- jmpq *(%rip)
- .quad address
- */
- uint8_t Return[14] = { 0xff, 0x25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
- *(uint64_t*)&Return[6] = Address + 14;
- memcpy((void*)(Bytes + CodeSize), Return, sizeof(Return));
- memcpy((void*)(Bytes + CodeSize + sizeof(Return)), &dlsymPtr, sizeof(uint64_t));
- }
- else
- {
- uint8_t Return[5] = { 0xe9 }; //jmp rel32
- *(uint32_t*)&Return[1] = (uint32_t)(Address - (Codecave + CodeSize));
- memcpy((void*)(Bytes + CodeSize), Return, sizeof(Return));
- memcpy((void*)(Bytes + CodeSize + sizeof(Return)), &(uint32_t){ (uint32_t)dlsymPtr }, sizeof(uint32_t));
- }
- err = mach_vm_protect(RemoteTask, Codecave, Length, FALSE, VM_PROT_ALL);
- if (err != KERN_SUCCESS)
- {
- mach_error("mach_vm_protect", err);
- printf("Protection error: %u\n", err);
- return EXIT_FAILURE;
- }
- err = mach_vm_write(RemoteTask, Codecave, Bytes, (mach_msg_type_number_t)Length);
- if (err != KERN_SUCCESS)
- {
- mach_error("mach_vm_write", err);
- printf("Writing error: %u\n", err);
- return EXIT_FAILURE;
- }
- mach_vm_deallocate(TaskSelf, Bytes, Length);
- printf("Injected to: %p\n", (void*)Codecave);
- if (!SuspendTaskWithNoExecutionInRange(RemoteTask, Address, Address + Size))
- {
- mach_vm_address_t Jump;
- err = mach_vm_allocate(TaskSelf, &Jump, Size, VM_FLAGS_ANYWHERE);
- if (err != KERN_SUCCESS)
- {
- mach_error("mach_vm_allocate", err);
- printf("Allocation error: %u\n", err);
- RetVal = EXIT_FAILURE;
- }
- else
- {
- memset((void*)Jump, 0x90 /*nops*/, Size);
- if (Is64)
- {
- uint8_t Patch[14] = { 0xff, 0x25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
- *(uint64_t*)&Patch[6] = Codecave;
- memcpy((void*)Jump, Patch, 14);
- }
- else
- {
- *(uint8_t*)Jump = 0xe9;
- *(uint32_t*)(Jump + 1) = (uint32_t)(Codecave - (Address + 5));
- }
- if (InjectBytes(RemoteTask, Address, Jump, Size) == -1) RetVal = EXIT_FAILURE;
- mach_vm_deallocate(TaskSelf, Jump, Size);
- }
- err = task_resume(RemoteTask);
- if (err != KERN_SUCCESS)
- {
- mach_error("task_resume", err);
- printf("Task continuation error: %u\n", err);
- RetVal = EXIT_FAILURE;
- }
- }
- else
- {
- RetVal = EXIT_FAILURE;
- }
- }
- else if (Data)
- {
- free(Data);
- }
- return RetVal;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement