├── kernel.db ├── kext ├── SimpleDriver ├── SimpleDriver_info.c ├── SimpleDriver.h ├── UserKernelShared.h ├── test_serv.c ├── SimpleDriver.cpp ├── SimpleUserClient.h ├── Info-SimpleUserClient.plist └── SimpleUserClient.cpp ├── Makefile ├── makedb.txt ├── makedb.sh ├── README ├── tfp0.plist ├── kdb.h ├── kdb.c ├── makedb.c ├── patchfinder.c └── kexty.c /kernel.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerub/kexty/HEAD/kernel.db -------------------------------------------------------------------------------- /kext/SimpleDriver: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerub/kexty/HEAD/kext/SimpleDriver -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS += -Wall -W -pedantic -std=c99 2 | CFLAGS += -I. -O2 3 | LDLIBS += -lsqlite3 4 | -------------------------------------------------------------------------------- /makedb.txt: -------------------------------------------------------------------------------- 1 | makedb.sh needs a Mac/Linux-compiled kexty 2 | 3 | sqlite3 kernel.db "SELECT Value FROM Symbols JOIN Kernels ON Symbols.Kernel=Kernels.Kernel WHERE Version=935 AND Device='iPhone6,1' AND Symbol='_kOSBooleanTrue';" 4 | -------------------------------------------------------------------------------- /makedb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ $# -ne 2 ]; then 4 | echo "usage: $0 kernel database" 5 | exit 6 | fi 7 | 8 | ./makedb "$1" "$2" \ 9 | `./kexty -solve "$1" __ZN6OSKext21withPrelinkedInfoDictEP12OSDictionary` 10 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | kernel loader for 7.x-9.x 2 | 3 | arm64 kexty can load 64bit kexts 4 | arm32 kexty can load 32bit kexts 5 | 6 | in order to provide missing symbols: 7 | 1. add it manually to the db 8 | 2. add code to the 3 solvers at the bottom of the source 9 | 10 | -xerub 11 | -------------------------------------------------------------------------------- /tfp0.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | get-task-allow 6 | 7 | run-unsigned-code 8 | 9 | task_for_pid-allow 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /kext/SimpleDriver_info.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern kern_return_t _start(kmod_info_t *ki, void *data); 4 | extern kern_return_t _stop(kmod_info_t *ki, void *data); 5 | 6 | __attribute__((visibility("default"))) KMOD_EXPLICIT_DECL(com.xerub.driver.SimpleDriver, "1.0.0", _start, _stop) 7 | __private_extern__ kmod_start_func_t *_realmain = 0; 8 | __private_extern__ kmod_stop_func_t *_antimain = 0; 9 | __private_extern__ int _kext_apple_cc = __APPLE_CC__ ; 10 | -------------------------------------------------------------------------------- /kdb.h: -------------------------------------------------------------------------------- 1 | #ifndef KDB_H 2 | #define KDB_H 3 | 4 | struct kdb; 5 | 6 | /* 7 | CREATE TABLE Symbols(Kernel int, Symbol varchar(255), Value int); 8 | INSERT INTO "Symbols" VALUES(1166736973,'_kOSBooleanTrue',2150771920); 9 | INSERT INTO "Symbols" VALUES(1166736973,'_kOSBooleanFalse',2150771924); 10 | 11 | 1166736973 = djb_hash("Darwin Kernel Version 14.0.0: Fri Sep 27 23:00:47 PDT 2013; root:xnu-2423.3.12~1/RELEASE_ARM_S5L8950X") 12 | */ 13 | 14 | struct kdb *kdb_init(const char *database, const char *kernel); 15 | unsigned long long kdb_find(struct kdb *k, const char *symbol); 16 | void kdb_term(struct kdb *k); 17 | 18 | unsigned int djb_hash(const char *key); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /kext/SimpleDriver.h: -------------------------------------------------------------------------------- 1 | /* 2 | File: SimpleDriver.h 3 | 4 | Description: This file shows how to implement a basic I/O Kit driver kernel extension (KEXT). 5 | 6 | Copyright: Copyright © 2001-2008 Apple Inc. All rights reserved. 7 | */ 8 | 9 | #include 10 | #include "UserKernelShared.h" 11 | 12 | class XerubDriver : public IOService 13 | { 14 | // Declare the metaclass information that is used for runtime type checking of I/O Kit objects. 15 | 16 | OSDeclareDefaultStructors(XerubDriver) 17 | 18 | public: 19 | // IOService methods 20 | virtual bool start(IOService *provider); 21 | 22 | // SimpleDriver methods 23 | virtual IOReturn testMe(uint32_t *demo); 24 | }; 25 | -------------------------------------------------------------------------------- /kext/UserKernelShared.h: -------------------------------------------------------------------------------- 1 | /* 2 | File: UserKernelShared.h 3 | 4 | Description: Definitions shared between SimpleUserClient (kernel) and SimpleUserClientTool (userland). 5 | 6 | Copyright: Copyright © 2001-2008 Apple Inc. All rights reserved. 7 | */ 8 | 9 | #define kXerubDriverClassName "XerubDriver" 10 | 11 | // Data structure passed between the tool and the user client. This structure and its fields need to have 12 | // the same size and alignment between the user client, 32-bit processes, and 64-bit processes. 13 | // To avoid invisible compiler padding, align fields on 64-bit boundaries when possible 14 | // and make the whole structure's size a multiple of 64 bits. 15 | 16 | // User client method dispatch selectors. 17 | enum { 18 | kMyTestMethod, 19 | kNumberOfMethods // Must be last 20 | }; 21 | -------------------------------------------------------------------------------- /kext/test_serv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int 7 | main(void) 8 | { 9 | kern_return_t ret; 10 | io_connect_t conn = 0; 11 | io_service_t dev = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("XerubDriver")); 12 | if (dev) { 13 | ret = IOServiceOpen(dev, mach_task_self(), 0, &conn); 14 | if (ret == kIOReturnSuccess) { 15 | uint64_t scalarO_64 = 0; 16 | uint32_t outputCount = 1; 17 | ret = IOConnectCallScalarMethod(conn, 0, NULL, 0, &scalarO_64, &outputCount); 18 | if (ret == 0) { 19 | printf("scalarO_64 = %x\n", (uint32_t)scalarO_64); 20 | } 21 | IOServiceClose(conn); 22 | } 23 | IOObjectRelease(dev); 24 | } 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /kext/SimpleDriver.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | File: SimpleDriver.cpp 3 | 4 | Description: This file shows how to implement a basic I/O Kit driver kernel extension (KEXT). 5 | 6 | Copyright: Copyright © 2001-2008 Apple Inc. All rights reserved. 7 | Copyright: Copyright © 2016 xerub 8 | */ 9 | 10 | 11 | #include 12 | #include "SimpleDriver.h" 13 | 14 | #define super IOService 15 | 16 | // Even though we are defining the convenience macro super for the superclass, you must use the actual class name 17 | // in the OS*MetaClass macros. Note that the class name is different when supporting Mac OS X 10.4. 18 | 19 | OSDefineMetaClassAndStructors(XerubDriver, IOService) 20 | 21 | bool 22 | XerubDriver::start(IOService *provider) 23 | { 24 | bool success; 25 | 26 | IOLog("%s[%p]::%s(%p)\n", getName(), this, __FUNCTION__, provider); 27 | 28 | success = super::start(provider); 29 | 30 | if (success) { 31 | // Publish ourselves so clients can find us 32 | registerService(); 33 | } 34 | 35 | return success; 36 | } 37 | 38 | IOReturn 39 | XerubDriver::testMe(uint32_t *demo) 40 | { 41 | IOLog("%s[%p]::%s()\n", getName(), this, __FUNCTION__); 42 | 43 | *demo = 0xdeaf0000; 44 | 45 | return kIOReturnSuccess; 46 | } 47 | -------------------------------------------------------------------------------- /kext/SimpleUserClient.h: -------------------------------------------------------------------------------- 1 | /* 2 | File: SimpleUserClient.h 3 | 4 | Description: This file shows how to implement a simple I/O Kit user client that is Rosetta-aware. 5 | 6 | Copyright: Copyright © 2001-2008 Apple Inc. All rights reserved. 7 | */ 8 | 9 | 10 | #include 11 | #include 12 | #include "SimpleDriver.h" 13 | 14 | class XerubUserClient : public IOUserClient { 15 | OSDeclareDefaultStructors(XerubUserClient) 16 | 17 | protected: 18 | XerubDriver *fProvider; 19 | task_t fTask; 20 | bool fCrossEndian; 21 | static const IOExternalMethodDispatch sMethods[kNumberOfMethods]; 22 | 23 | public: 24 | // IOUserClient methods 25 | virtual bool start(IOService *provider); 26 | 27 | virtual bool initWithTask(task_t owningTask, void *securityToken, UInt32 type, OSDictionary *properties); 28 | 29 | protected: 30 | // KPI for supporting access from both 32-bit and 64-bit user processes beginning with Mac OS X 10.5. 31 | virtual IOReturn externalMethod(uint32_t selector, IOExternalMethodArguments *arguments, IOExternalMethodDispatch *dispatch, OSObject *target, void *reference); 32 | 33 | // SimpleUserClient methods 34 | static IOReturn sTestMe(XerubDriver *target, void *reference, IOExternalMethodArguments *arguments); 35 | }; 36 | -------------------------------------------------------------------------------- /kext/Info-SimpleUserClient.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | SimpleDriver 9 | CFBundleIdentifier 10 | com.xerub.driver.SimpleDriver 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | SimpleDriver 15 | CFBundlePackageType 16 | KEXT 17 | CFBundleShortVersionString 18 | 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0.0 23 | IOKitPersonalities 24 | 25 | SimpleUserClient 26 | 27 | CFBundleIdentifier 28 | com.xerub.driver.SimpleDriver 29 | IOClass 30 | XerubDriver 31 | #if DEBUG 32 | IOKitDebug 33 | 65535 34 | #endif 35 | IOMatchCategory 36 | XerubDriver 37 | IOProbeScore 38 | 1000 39 | IOProviderClass 40 | IOResources 41 | IOResourceMatch 42 | IOKit 43 | IOUserClientClass 44 | XerubUserClient 45 | 46 | 47 | OSBundleLibraries 48 | 49 | com.apple.kpi.iokit 50 | 9.0.0 51 | com.apple.kpi.libkern 52 | 9.0.0 53 | com.apple.kpi.mach 54 | 9.0.0 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /kdb.c: -------------------------------------------------------------------------------- 1 | /* 2 | * kernel symbol resolver 3 | * 4 | * Copyright (c) 2015 xerub 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "kdb.h" 13 | 14 | struct kdb { 15 | sqlite3 *db; 16 | sqlite3_stmt *stmt; 17 | }; 18 | 19 | unsigned int 20 | djb_hash(const char *key) 21 | { 22 | unsigned int hash = 5381; 23 | 24 | for (; *key; key++) { 25 | hash = ((hash << 5) + hash) + (*key); 26 | } 27 | 28 | return hash; 29 | } 30 | 31 | struct kdb * 32 | kdb_init(const char *database, const char *kernel) 33 | { 34 | int rv; 35 | char *sql; 36 | sqlite3 *db; 37 | sqlite3_stmt *stmt; 38 | struct kdb *k; 39 | 40 | if (!kernel) { 41 | return NULL; 42 | } 43 | 44 | k = malloc(sizeof(struct kdb)); 45 | if (!k) { 46 | return NULL; 47 | } 48 | 49 | rv = sqlite3_open_v2(database, &db, SQLITE_OPEN_READONLY, NULL); 50 | if (rv) { 51 | sqlite3_close(db); 52 | free(k); 53 | return NULL; 54 | } 55 | 56 | sql = "SELECT Value FROM Symbols WHERE Kernel IS ?1 AND Symbol IS ?2"; 57 | rv = sqlite3_prepare_v2(db, sql, strlen(sql) + 1, &stmt, NULL); 58 | if (rv) { 59 | sqlite3_close(db); 60 | free(k); 61 | return NULL; 62 | } 63 | 64 | rv = sqlite3_bind_int64(stmt, 1, djb_hash(kernel)); 65 | if (rv) { 66 | sqlite3_finalize(stmt); 67 | sqlite3_close(db); 68 | free(k); 69 | return NULL; 70 | } 71 | 72 | k->db = db; 73 | k->stmt = stmt; 74 | return k; 75 | } 76 | 77 | unsigned long long 78 | kdb_find(struct kdb *k, const char *symbol) 79 | { 80 | int rv; 81 | int err = 0; 82 | int row = 0; 83 | 84 | sqlite3_stmt *stmt; 85 | sqlite3_int64 x = 0; 86 | 87 | if (!k) { 88 | return 0; 89 | } 90 | stmt = k->stmt; 91 | 92 | rv = sqlite3_reset(stmt); 93 | if (rv) { 94 | return 0; 95 | } 96 | 97 | rv = sqlite3_bind_text(stmt, 2, symbol, strlen(symbol), SQLITE_STATIC); 98 | if (rv) { 99 | return 0; 100 | } 101 | 102 | while (1) { 103 | rv = sqlite3_step(stmt); 104 | if (rv == SQLITE_ROW) { 105 | if (row) { 106 | err = 1; 107 | break; 108 | } 109 | x = sqlite3_column_int64(stmt, 0); 110 | row++; 111 | #if 666 /* a bit faster */ 112 | break; 113 | #endif 114 | } else if (rv == SQLITE_DONE) { 115 | break; 116 | } else { 117 | err = 2; 118 | break; 119 | } 120 | } 121 | 122 | if (err || !row) { 123 | return 0; 124 | } 125 | return x; 126 | } 127 | 128 | void 129 | kdb_term(struct kdb *k) 130 | { 131 | if (k) { 132 | sqlite3_finalize(k->stmt); 133 | sqlite3_close(k->db); 134 | free(k); 135 | } 136 | } 137 | 138 | #if 0 139 | int 140 | main(void) 141 | { 142 | struct kdb *k; 143 | unsigned long long x; 144 | 145 | k = kdb_init("kernel.db", "Darwin Kernel Version 14.0.0: Fri Sep 27 23:00:47 PDT 2013; root:xnu-2423.3.12~1/RELEASE_ARM_S5L8950X"); 146 | 147 | x = kdb_find(k, "_kOSBooleanTrue"); 148 | printf("0x%llx\n", x); 149 | 150 | x = kdb_find(k, "_kOSBooleanFalse"); 151 | printf("0x%llx\n", x); 152 | 153 | kdb_term(k); 154 | return 0; 155 | } 156 | #endif 157 | -------------------------------------------------------------------------------- /kext/SimpleUserClient.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | File: SimpleUserClient.cpp 3 | 4 | Description: This file shows how to implement a simple I/O Kit user client that is Rosetta-aware. 5 | 6 | Copyright: Copyright © 2001-2008 Apple Inc. All rights reserved. 7 | Copyright: Copyright © 2016 xerub 8 | */ 9 | 10 | 11 | #include 12 | #include 13 | #include 14 | #include "SimpleUserClient.h" 15 | 16 | 17 | #define super IOUserClient 18 | 19 | // Even though we are defining the convenience macro super for the superclass, you must use the actual class name 20 | // in the OS*MetaClass macros. 21 | 22 | OSDefineMetaClassAndStructors(XerubUserClient, IOUserClient) 23 | 24 | // This is the technique which supports both 32-bit and 64-bit user processes starting with Mac OS X 10.5. 25 | // 26 | // User client method dispatch table. 27 | // 28 | // The user client mechanism is designed to allow calls from a user process to be dispatched to 29 | // any IOService-based object in the kernel. Almost always this mechanism is used to dispatch calls to 30 | // either member functions of the user client itself or of the user client's provider. The provider is 31 | // the driver which the user client is connecting to the user process. 32 | // 33 | // It is recommended that calls be dispatched to the user client and not directly to the provider driver. 34 | // This allows the user client to perform error checking on the parameters before passing them to the driver. 35 | // It also allows the user client to do any endian-swapping of parameters in the cross-endian case. 36 | 37 | const IOExternalMethodDispatch XerubUserClient::sMethods[kNumberOfMethods] = { 38 | { // kMyTestMethod 39 | (IOExternalMethodAction) &XerubUserClient::sTestMe, // Method pointer. 40 | 0, // No scalar input values. 41 | 0, // No struct input value. 42 | 1, // One scalar output value. 43 | 0 // No struct output value. 44 | } 45 | }; 46 | 47 | IOReturn 48 | XerubUserClient::externalMethod(uint32_t selector, IOExternalMethodArguments *arguments, IOExternalMethodDispatch *dispatch, OSObject *target, void *reference) 49 | { 50 | IOLog("%s[%p]::%s(%d, %p, %p, %p, %p)\n", getName(), this, __FUNCTION__, selector, arguments, dispatch, target, reference); 51 | 52 | if (selector < (uint32_t) kNumberOfMethods) { 53 | dispatch = (IOExternalMethodDispatch *) &sMethods[selector]; 54 | if (!target) { 55 | target = fProvider; 56 | } 57 | } 58 | return super::externalMethod(selector, arguments, dispatch, target, reference); 59 | } 60 | 61 | // There are two forms of IOUserClient::initWithTask, the second of which accepts an additional OSDictionary* parameter. 62 | // If your user client needs to modify its behavior when it's being used by a process running using Rosetta, 63 | // you need to implement the form of initWithTask with this additional parameter. 64 | // 65 | // initWithTask is called as a result of the user process calling IOServiceOpen. 66 | bool 67 | XerubUserClient::initWithTask(task_t owningTask, void *securityToken, UInt32 type, OSDictionary *properties) 68 | { 69 | bool success; 70 | 71 | success = super::initWithTask(owningTask, securityToken, type, properties); 72 | 73 | // This IOLog must follow super::initWithTask because getName relies on the superclass initialization. 74 | IOLog("%s[%p]::%s(%p, %p, %u, %p)\n", getName(), this, __FUNCTION__, owningTask, securityToken, (unsigned)type, properties); 75 | 76 | if (success) { 77 | } 78 | 79 | fTask = owningTask; 80 | fProvider = NULL; 81 | 82 | return success; 83 | } 84 | 85 | // start is called after initWithTask as a result of the user process calling IOServiceOpen. 86 | bool 87 | XerubUserClient::start(IOService *provider) 88 | { 89 | bool success; 90 | 91 | IOLog("%s[%p]::%s(%p)\n", getName(), this, __FUNCTION__, provider); 92 | 93 | // Verify that this user client is being started with a provider that it knows 94 | // how to communicate with. 95 | fProvider = OSDynamicCast(XerubDriver, provider); 96 | success = (fProvider != NULL); 97 | 98 | if (success) { 99 | // It's important not to call super::start if some previous condition 100 | // (like an invalid provider) would cause this function to return false. 101 | // I/O Kit won't call stop on an object if its start function returned false. 102 | success = super::start(provider); 103 | } 104 | 105 | return success; 106 | } 107 | 108 | IOReturn 109 | XerubUserClient::sTestMe(XerubDriver *target, void *reference, IOExternalMethodArguments *arguments) 110 | { 111 | return target->testMe((uint32_t *)&arguments->scalarOutput[0]); 112 | } 113 | -------------------------------------------------------------------------------- /makedb.c: -------------------------------------------------------------------------------- 1 | /* 2 | * kernel symbol grabber 3 | * 4 | * Copyright (c) 2015, 2016 xerub 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #define FOR_SQLITE 19 | 20 | #ifdef FOR_SQLITE 21 | #include 22 | 23 | struct ctx { 24 | sqlite3_stmt *stmt; 25 | unsigned int hash; 26 | }; 27 | #endif 28 | 29 | #define IS64(image) (*(uint8_t *)(image) & 1) 30 | 31 | #define MACHO(p) ((*(unsigned int *)(p) & ~1) == 0xfeedface) 32 | 33 | static size_t align = 0xFFF; 34 | 35 | static __inline size_t 36 | round_page(size_t size) 37 | { 38 | return (size + align) & ~align; 39 | } 40 | 41 | /* generic stuff *************************************************************/ 42 | 43 | #define UCHAR_MAX 255 44 | 45 | static unsigned char * 46 | boyermoore_horspool_memmem(const unsigned char* haystack, size_t hlen, 47 | const unsigned char* needle, size_t nlen) 48 | { 49 | size_t last, scan = 0; 50 | size_t bad_char_skip[UCHAR_MAX + 1]; /* Officially called: 51 | * bad character shift */ 52 | 53 | /* Sanity checks on the parameters */ 54 | if (nlen <= 0 || !haystack || !needle) 55 | return NULL; 56 | 57 | /* ---- Preprocess ---- */ 58 | /* Initialize the table to default value */ 59 | /* When a character is encountered that does not occur 60 | * in the needle, we can safely skip ahead for the whole 61 | * length of the needle. 62 | */ 63 | for (scan = 0; scan <= UCHAR_MAX; scan = scan + 1) 64 | bad_char_skip[scan] = nlen; 65 | 66 | /* C arrays have the first byte at [0], therefore: 67 | * [nlen - 1] is the last byte of the array. */ 68 | last = nlen - 1; 69 | 70 | /* Then populate it with the analysis of the needle */ 71 | for (scan = 0; scan < last; scan = scan + 1) 72 | bad_char_skip[needle[scan]] = last - scan; 73 | 74 | /* ---- Do the matching ---- */ 75 | 76 | /* Search the haystack, while the needle can still be within it. */ 77 | while (hlen >= nlen) 78 | { 79 | /* scan from the end of the needle */ 80 | for (scan = last; haystack[scan] == needle[scan]; scan = scan - 1) 81 | if (scan == 0) /* If the first byte matches, we've found it. */ 82 | return (void *)haystack; 83 | 84 | /* otherwise, we need to skip some bytes and start again. 85 | Note that here we are getting the skip value based on the last byte 86 | of needle, no matter where we didn't match. So if needle is: "abcd" 87 | then we are skipping based on 'd' and that value will be 4, and 88 | for "abcdd" we again skip on 'd' but the value will be only 1. 89 | The alternative of pretending that the mismatched character was 90 | the last character is slower in the normal case (E.g. finding 91 | "abcd" in "...azcd..." gives 4 by using 'd' but only 92 | 4-2==2 using 'z'. */ 93 | hlen -= bad_char_skip[haystack[last]]; 94 | haystack += bad_char_skip[haystack[last]]; 95 | } 96 | 97 | return NULL; 98 | } 99 | 100 | /* kernel stuff **************************************************************/ 101 | 102 | uint8_t *kernel = MAP_FAILED; 103 | size_t kernel_size = 0; 104 | int kernel_fd = -1; 105 | 106 | static int 107 | init_kernel(const char *filename) 108 | { 109 | kernel_fd = open(filename, O_RDONLY); 110 | if (kernel_fd < 0) { 111 | return -1; 112 | } 113 | 114 | kernel_size = lseek(kernel_fd, 0, SEEK_END); 115 | 116 | kernel = mmap(NULL, kernel_size, PROT_READ, MAP_PRIVATE, kernel_fd, 0); 117 | if (kernel == MAP_FAILED) { 118 | close(kernel_fd); 119 | kernel_fd = -1; 120 | return -1; 121 | } 122 | 123 | return 0; 124 | } 125 | 126 | static void 127 | term_kernel(void) 128 | { 129 | munmap(kernel, kernel_size); 130 | close(kernel_fd); 131 | } 132 | 133 | static int 134 | show_syms(size_t offset, int thumbize, int (*callback)(unsigned long long value, const char *symbol, void *opaque), void *opaque) 135 | { 136 | uint32_t i; 137 | const uint8_t *p, *q; 138 | const struct mach_header *hdr; 139 | size_t eseg, size, next; 140 | int is64; 141 | 142 | again: 143 | #ifndef FOR_SQLITE 144 | printf("offset = %zx\n", offset); 145 | #endif 146 | if (offset >= kernel_size - 3) { 147 | return 0; 148 | } 149 | 150 | size = 0; 151 | next = 0; 152 | p = kernel + offset; 153 | hdr = (struct mach_header *)p; 154 | q = p + sizeof(struct mach_header); 155 | is64 = 0; 156 | 157 | if (!MACHO(p)) { 158 | return 0; 159 | } 160 | if (IS64(p)) { 161 | is64 = 4; 162 | } 163 | 164 | q = p + sizeof(struct mach_header) + is64; 165 | 166 | for (i = 0; i < hdr->ncmds; i++) { 167 | const struct load_command *cmd = (struct load_command *)q; 168 | if (cmd->cmd == LC_SEGMENT) { 169 | const struct segment_command *seg = (struct segment_command *)q; 170 | if (!strcmp(seg->segname, "__PRELINK_TEXT")) { 171 | next = seg->fileoff; 172 | } 173 | if (!strcmp(seg->segname, "__PAGEZERO")) { 174 | goto cont; 175 | } 176 | if (seg->vmaddr == 0) { 177 | eseg = round_page(seg->fileoff + seg->vmsize); 178 | if (offset + eseg < kernel_size - 3 && *(uint32_t *)(kernel + offset + eseg) != *(uint32_t *)kernel) { 179 | align = 0x3FFF; /* promote alignment and hope for the best */ 180 | eseg = round_page(seg->fileoff + seg->vmsize); 181 | } 182 | } else { 183 | eseg = seg->fileoff + round_page(seg->vmsize); 184 | } 185 | if (size < eseg) { 186 | size = eseg; 187 | } 188 | } 189 | if (cmd->cmd == LC_SEGMENT_64) { 190 | const struct segment_command_64 *seg = (struct segment_command_64 *)q; 191 | if (!strcmp(seg->segname, "__PRELINK_TEXT")) { 192 | next = seg->fileoff; 193 | } 194 | if (!strcmp(seg->segname, "__PAGEZERO")) { 195 | goto cont; 196 | } 197 | if (seg->vmaddr == 0) { 198 | eseg = round_page(seg->fileoff + seg->vmsize); 199 | if (offset + eseg < kernel_size - 3 && *(uint32_t *)(kernel + offset + eseg) != *(uint32_t *)kernel) { 200 | align = 0x3FFF; /* promote alignment and hope for the best */ 201 | eseg = round_page(seg->fileoff + seg->vmsize); 202 | } 203 | } else { 204 | eseg = seg->fileoff + round_page(seg->vmsize); 205 | } 206 | if (size < eseg) { 207 | size = eseg; 208 | } 209 | } 210 | if (cmd->cmd == LC_SYMTAB) { 211 | const struct symtab_command *sym = (struct symtab_command *)q; 212 | const char *stroff = (const char *)p + sym->stroff; 213 | if (is64) { 214 | uint32_t k; 215 | const struct nlist_64 *s = (struct nlist_64 *)(p + sym->symoff); 216 | for (k = 0; k < sym->nsyms; k++) { 217 | if (s[k].n_type & N_STAB) { 218 | continue; 219 | } 220 | if (s[k].n_value && (s[k].n_type & N_TYPE) != N_INDR) { 221 | if (callback(s[k].n_value, stroff + s[k].n_un.n_strx, opaque)) { 222 | return -1; 223 | } 224 | } 225 | } 226 | } else { 227 | uint32_t k; 228 | const struct nlist *s = (struct nlist *)(p + sym->symoff); 229 | for (k = 0; k < sym->nsyms; k++) { 230 | if (s[k].n_type & N_STAB) { 231 | continue; 232 | } 233 | if (s[k].n_value && (s[k].n_type & N_TYPE) != N_INDR) { 234 | int thumb = thumbize && (s[k].n_desc & N_ARM_THUMB_DEF); 235 | if (callback(s[k].n_value + thumb, stroff + s[k].n_un.n_strx, opaque)) { 236 | return -1; 237 | } 238 | } 239 | } 240 | } 241 | } 242 | cont: q = q + cmd->cmdsize; 243 | } 244 | 245 | if (next) { 246 | return show_syms(next, thumbize, callback, opaque); 247 | } 248 | if (size) { 249 | offset += size; 250 | goto again; 251 | } 252 | return 0; 253 | } 254 | 255 | 256 | static int 257 | callback(unsigned long long value, const char *symbol, void *opaque) 258 | { 259 | #ifdef FOR_SQLITE 260 | int rv; 261 | unsigned int hash = ((struct ctx *)opaque)->hash; 262 | sqlite3_stmt *stmt = ((struct ctx *)opaque)->stmt; 263 | printf("INSERT INTO \"Symbols\" VALUES(%u,'%s',%llu);\n", hash, symbol, value); 264 | rv = sqlite3_reset(stmt); 265 | if (rv) { 266 | return -1; 267 | } 268 | rv = sqlite3_bind_text(stmt, 2, symbol, strlen(symbol), SQLITE_STATIC); 269 | if (rv) { 270 | return -1; 271 | } 272 | rv = sqlite3_bind_int64(stmt, 3, value); 273 | if (rv) { 274 | return -1; 275 | } 276 | rv = sqlite3_step(stmt); 277 | if (rv != SQLITE_DONE) { 278 | fprintf(stderr, "sqlite error: %d\n", rv); 279 | return -1; 280 | } 281 | return 0; 282 | #else 283 | (void)opaque; 284 | printf("%08llx %s\n", value, symbol); 285 | return 0; 286 | #endif 287 | } 288 | 289 | #ifdef FOR_SQLITE 290 | static unsigned int 291 | djb_hash(const char *key) 292 | { 293 | unsigned int hash = 5381; 294 | 295 | for (; *key; key++) { 296 | hash = ((hash << 5) + hash) + (*key); 297 | } 298 | 299 | return hash; 300 | } 301 | 302 | static int 303 | make_db(const char *database, const char *version, int argc, char **argv) 304 | { 305 | int rv; 306 | int newdb = 0; 307 | struct stat st; 308 | struct ctx ctx; 309 | sqlite3 *db; 310 | sqlite3_stmt *stmt; 311 | unsigned int hash; 312 | char *str; 313 | int i; 314 | 315 | rv = stat(database, &st); 316 | newdb = (rv != 0); 317 | 318 | rv = sqlite3_open(database, &db); 319 | if (rv) { 320 | fprintf(stderr, "[e] cannot open database\n"); 321 | return -1; 322 | } 323 | 324 | hash = djb_hash(version); 325 | 326 | if (newdb) { 327 | printf("CREATE TABLE Symbols(Kernel int, Symbol varchar(255), Value int);\n"); 328 | rv = sqlite3_exec(db, "CREATE TABLE Symbols(Kernel int, Symbol varchar(255), Value int);", NULL, NULL, &str); 329 | if (rv) { 330 | fprintf(stderr, "sqlite error: %s\n", str); 331 | sqlite3_free(str); 332 | sqlite3_close(db); 333 | return -1; 334 | } 335 | } else { 336 | char tmp[256]; 337 | printf("DELETE FROM \"Symbols\" WHERE Kernel=%u;\n", hash); 338 | snprintf(tmp, sizeof(tmp), "DELETE FROM \"Symbols\" WHERE Kernel=%u;\n", hash); 339 | rv = sqlite3_exec(db, tmp, NULL, NULL, &str); 340 | if (rv) { 341 | fprintf(stderr, "sqlite error: %s\n", str); 342 | sqlite3_free(str); 343 | sqlite3_close(db); 344 | return -1; 345 | } 346 | } 347 | 348 | str = "INSERT INTO \"Symbols\" VALUES(?1,?2,?3);"; 349 | rv = sqlite3_prepare_v2(db, str, strlen(str) + 1, &stmt, NULL); 350 | if (rv) { 351 | sqlite3_close(db); 352 | fprintf(stderr, "[e] cannot make statement\n"); 353 | return -1; 354 | } 355 | 356 | rv = sqlite3_bind_int64(stmt, 1, hash); 357 | if (rv) { 358 | sqlite3_finalize(stmt); 359 | sqlite3_close(db); 360 | fprintf(stderr, "[e] cannot bind statement\n"); 361 | return -1; 362 | } 363 | 364 | rv = sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, NULL); 365 | 366 | ctx.stmt = stmt; 367 | ctx.hash = hash; 368 | show_syms(0, 1, callback, &ctx); 369 | 370 | for (i = 3; i < argc; i++) { 371 | unsigned long long v; 372 | char *p = argv[i]; 373 | char *q = strchr(p, '='); 374 | if (q) { 375 | char *rem; 376 | *q++ = '\0'; 377 | errno = 0; 378 | v = strtoull(q, &rem, 0); 379 | if (errno == 0 && *rem == '\0') { 380 | callback(v, p, &ctx); 381 | continue; 382 | } 383 | } 384 | fprintf(stderr, "[w] ignoring %s\n", p); 385 | } 386 | 387 | sqlite3_finalize(stmt); 388 | 389 | rv = sqlite3_exec(db, "END TRANSACTION;", NULL, NULL, NULL); 390 | if (rv) { 391 | sqlite3_close(db); 392 | fprintf(stderr, "[e] cannot end transaction\n"); 393 | return -1; 394 | } 395 | 396 | #if 1 397 | printf("DROP INDEX symbol_index;\n"); 398 | rv = sqlite3_exec(db, "DROP INDEX symbol_index;", NULL, NULL, NULL); 399 | printf("CREATE INDEX symbol_index on Symbols (Kernel, Symbol);\n"); 400 | rv = sqlite3_exec(db, "CREATE INDEX symbol_index on Symbols (Kernel, Symbol);", NULL, NULL, &str); 401 | if (rv) { 402 | fprintf(stderr, "sqlite error: %s\n", str); 403 | sqlite3_free(str); 404 | } 405 | #endif 406 | 407 | sqlite3_close(db); 408 | return 0; 409 | } 410 | #endif 411 | 412 | int 413 | main(int argc, char **argv) 414 | { 415 | int rv; 416 | const char *version; 417 | 418 | if (argc < 3) { 419 | fprintf(stderr, "usage: %s kernel database [sym=value...]\n", argv[0]); 420 | return 1; 421 | } 422 | 423 | rv = init_kernel(argv[1]); 424 | if (rv) { 425 | fprintf(stderr, "[e] cannot read kernel\n"); 426 | return -1; 427 | } 428 | version = (char *)boyermoore_horspool_memmem(kernel, kernel_size, (uint8_t *)"Darwin Kernel Version", sizeof("Darwin Kernel Version") - 1); 429 | if (!version) { 430 | fprintf(stderr, "[e] cannot find version\n"); 431 | term_kernel(); 432 | return -1; 433 | } 434 | 435 | #ifdef FOR_SQLITE 436 | make_db(argv[2], version, argc, argv); 437 | #else 438 | printf("%s\n", version); 439 | show_syms(0, 1, callback, (void *)version); 440 | #endif 441 | 442 | term_kernel(); 443 | return 0; 444 | } 445 | -------------------------------------------------------------------------------- /patchfinder.c: -------------------------------------------------------------------------------- 1 | /* --- planetbeing patchfinder --- */ 2 | 3 | static uint32_t bit_range(uint32_t x, int start, int end) 4 | { 5 | x = (x << (31 - start)) >> (31 - start); 6 | x = (x >> end); 7 | return x; 8 | } 9 | 10 | static uint32_t ror(uint32_t x, int places) 11 | { 12 | return (x >> places) | (x << (32 - places)); 13 | } 14 | 15 | static int thumb_expand_imm_c(uint16_t imm12) 16 | { 17 | if (bit_range(imm12, 11, 10) == 0) { 18 | switch (bit_range(imm12, 9, 8)) { 19 | case 0: 20 | return bit_range(imm12, 7, 0); 21 | case 1: 22 | return (bit_range(imm12, 7, 0) << 16) | bit_range(imm12, 7, 0); 23 | case 2: 24 | return (bit_range(imm12, 7, 0) << 24) | (bit_range(imm12, 7, 0) << 8); 25 | case 3: 26 | return (bit_range(imm12, 7, 0) << 24) | (bit_range(imm12, 7, 0) << 16) | (bit_range(imm12, 7, 0) << 8) | bit_range(imm12, 7, 0); 27 | default: 28 | return 0; 29 | } 30 | } else { 31 | uint32_t unrotated_value = 0x80 | bit_range(imm12, 6, 0); 32 | return ror(unrotated_value, bit_range(imm12, 11, 7)); 33 | } 34 | } 35 | 36 | static int insn_is_32bit(uint16_t * i) 37 | { 38 | return (*i & 0xe000) == 0xe000 && (*i & 0x1800) != 0x0; 39 | } 40 | 41 | static int insn_is_bl(uint16_t * i) 42 | { 43 | if ((*i & 0xf800) == 0xf000 && (*(i + 1) & 0xd000) == 0xd000) 44 | return 1; 45 | else if ((*i & 0xf800) == 0xf000 && (*(i + 1) & 0xd001) == 0xc000) 46 | return 1; 47 | else 48 | return 0; 49 | } 50 | 51 | static uint32_t insn_bl_imm32(uint16_t * i) 52 | { 53 | uint16_t insn0 = *i; 54 | uint16_t insn1 = *(i + 1); 55 | uint32_t s = (insn0 >> 10) & 1; 56 | uint32_t j1 = (insn1 >> 13) & 1; 57 | uint32_t j2 = (insn1 >> 11) & 1; 58 | uint32_t i1 = ~(j1 ^ s) & 1; 59 | uint32_t i2 = ~(j2 ^ s) & 1; 60 | uint32_t imm10 = insn0 & 0x3ff; 61 | uint32_t imm11 = insn1 & 0x7ff; 62 | uint32_t imm32 = (imm11 << 1) | (imm10 << 12) | (i2 << 22) | (i1 << 23) | (s ? 0xff000000 : 0); 63 | return imm32; 64 | } 65 | 66 | static int insn_is_b_conditional(uint16_t * i) 67 | { 68 | return (*i & 0xF000) == 0xD000 && (*i & 0x0F00) != 0x0F00 && (*i & 0x0F00) != 0xE; 69 | } 70 | 71 | static int insn_is_b_unconditional(uint16_t * i) 72 | { 73 | if ((*i & 0xF800) == 0xE000) 74 | return 1; 75 | else if ((*i & 0xF800) == 0xF000 && (*(i + 1) & 0xD000) == 9) 76 | return 1; 77 | else 78 | return 0; 79 | } 80 | 81 | static int insn_is_ldr_literal(uint16_t * i) 82 | { 83 | return (*i & 0xF800) == 0x4800 || (*i & 0xFF7F) == 0xF85F; 84 | } 85 | 86 | static int insn_ldr_literal_rt(uint16_t * i) 87 | { 88 | if ((*i & 0xF800) == 0x4800) 89 | return (*i >> 8) & 7; 90 | else if ((*i & 0xFF7F) == 0xF85F) 91 | return (*(i + 1) >> 12) & 0xF; 92 | else 93 | return 0; 94 | } 95 | 96 | static int insn_ldr_literal_imm(uint16_t * i) 97 | { 98 | if ((*i & 0xF800) == 0x4800) 99 | return (*i & 0xFF) << 2; 100 | else if ((*i & 0xFF7F) == 0xF85F) 101 | return (*(i + 1) & 0xFFF) * (((*i & 0x0800) == 0x0800) ? 1 : -1); 102 | else 103 | return 0; 104 | } 105 | 106 | // TODO: More encodings 107 | static int insn_is_ldr_imm(uint16_t * i) 108 | { 109 | uint8_t opA = bit_range(*i, 15, 12); 110 | uint8_t opB = bit_range(*i, 11, 9); 111 | 112 | return opA == 6 && (opB & 4) == 4; 113 | } 114 | 115 | static int insn_ldr_imm_rt(uint16_t * i) 116 | { 117 | return (*i & 7); 118 | } 119 | 120 | static int insn_ldr_imm_rn(uint16_t * i) 121 | { 122 | return ((*i >> 3) & 7); 123 | } 124 | 125 | static int insn_ldr_imm_imm(uint16_t * i) 126 | { 127 | return ((*i >> 6) & 0x1F); 128 | } 129 | 130 | // TODO: More encodings 131 | static int insn_is_ldrb_imm(uint16_t * i) 132 | { 133 | return (*i & 0xF800) == 0x7800; 134 | } 135 | 136 | static int insn_ldrb_imm_rt(uint16_t * i) 137 | { 138 | return (*i & 7); 139 | } 140 | 141 | static int insn_ldrb_imm_rn(uint16_t * i) 142 | { 143 | return ((*i >> 3) & 7); 144 | } 145 | 146 | static int insn_ldrb_imm_imm(uint16_t * i) 147 | { 148 | return ((*i >> 6) & 0x1F); 149 | } 150 | 151 | static int insn_is_ldr_reg(uint16_t * i) 152 | { 153 | if ((*i & 0xFE00) == 0x5800) 154 | return 1; 155 | else if ((*i & 0xFFF0) == 0xF850 && (*(i + 1) & 0x0FC0) == 0x0000) 156 | return 1; 157 | else 158 | return 0; 159 | } 160 | 161 | static int insn_ldr_reg_rn(uint16_t * i) 162 | { 163 | if ((*i & 0xFE00) == 0x5800) 164 | return (*i >> 3) & 0x7; 165 | else if ((*i & 0xFFF0) == 0xF850 && (*(i + 1) & 0x0FC0) == 0x0000) 166 | return (*i & 0xF); 167 | else 168 | return 0; 169 | } 170 | 171 | int insn_ldr_reg_rt(uint16_t * i) 172 | { 173 | if ((*i & 0xFE00) == 0x5800) 174 | return *i & 0x7; 175 | else if ((*i & 0xFFF0) == 0xF850 && (*(i + 1) & 0x0FC0) == 0x0000) 176 | return (*(i + 1) >> 12) & 0xF; 177 | else 178 | return 0; 179 | } 180 | 181 | int insn_ldr_reg_rm(uint16_t * i) 182 | { 183 | if ((*i & 0xFE00) == 0x5800) 184 | return (*i >> 6) & 0x7; 185 | else if ((*i & 0xFFF0) == 0xF850 && (*(i + 1) & 0x0FC0) == 0x0000) 186 | return *(i + 1) & 0xF; 187 | else 188 | return 0; 189 | } 190 | 191 | static int insn_ldr_reg_lsl(uint16_t * i) 192 | { 193 | if ((*i & 0xFE00) == 0x5800) 194 | return 0; 195 | else if ((*i & 0xFFF0) == 0xF850 && (*(i + 1) & 0x0FC0) == 0x0000) 196 | return (*(i + 1) >> 4) & 0x3; 197 | else 198 | return 0; 199 | } 200 | 201 | static int insn_is_add_reg(uint16_t * i) 202 | { 203 | if ((*i & 0xFE00) == 0x1800) 204 | return 1; 205 | else if ((*i & 0xFF00) == 0x4400) 206 | return 1; 207 | else if ((*i & 0xFFE0) == 0xEB00) 208 | return 1; 209 | else 210 | return 0; 211 | } 212 | 213 | static int insn_add_reg_rd(uint16_t * i) 214 | { 215 | if ((*i & 0xFE00) == 0x1800) 216 | return (*i & 7); 217 | else if ((*i & 0xFF00) == 0x4400) 218 | return (*i & 7) | ((*i & 0x80) >> 4); 219 | else if ((*i & 0xFFE0) == 0xEB00) 220 | return (*(i + 1) >> 8) & 0xF; 221 | else 222 | return 0; 223 | } 224 | 225 | static int insn_add_reg_rn(uint16_t * i) 226 | { 227 | if ((*i & 0xFE00) == 0x1800) 228 | return ((*i >> 3) & 7); 229 | else if ((*i & 0xFF00) == 0x4400) 230 | return (*i & 7) | ((*i & 0x80) >> 4); 231 | else if ((*i & 0xFFE0) == 0xEB00) 232 | return (*i & 0xF); 233 | else 234 | return 0; 235 | } 236 | 237 | static int insn_add_reg_rm(uint16_t * i) 238 | { 239 | if ((*i & 0xFE00) == 0x1800) 240 | return (*i >> 6) & 7; 241 | else if ((*i & 0xFF00) == 0x4400) 242 | return (*i >> 3) & 0xF; 243 | else if ((*i & 0xFFE0) == 0xEB00) 244 | return *(i + 1) & 0xF; 245 | else 246 | return 0; 247 | } 248 | 249 | static int insn_is_movt(uint16_t * i) 250 | { 251 | return (*i & 0xFBF0) == 0xF2C0 && (*(i + 1) & 0x8000) == 0; 252 | } 253 | 254 | static int insn_movt_rd(uint16_t * i) 255 | { 256 | return (*(i + 1) >> 8) & 0xF; 257 | } 258 | 259 | static int insn_movt_imm(uint16_t * i) 260 | { 261 | return ((*i & 0xF) << 12) | ((*i & 0x0400) << 1) | ((*(i + 1) & 0x7000) >> 4) | (*(i + 1) & 0xFF); 262 | } 263 | 264 | static int insn_is_mov_imm(uint16_t * i) 265 | { 266 | if ((*i & 0xF800) == 0x2000) 267 | return 1; 268 | else if ((*i & 0xFBEF) == 0xF04F && (*(i + 1) & 0x8000) == 0) 269 | return 1; 270 | else if ((*i & 0xFBF0) == 0xF240 && (*(i + 1) & 0x8000) == 0) 271 | return 1; 272 | else 273 | return 0; 274 | } 275 | 276 | static int insn_mov_imm_rd(uint16_t * i) 277 | { 278 | if ((*i & 0xF800) == 0x2000) 279 | return (*i >> 8) & 7; 280 | else if ((*i & 0xFBEF) == 0xF04F && (*(i + 1) & 0x8000) == 0) 281 | return (*(i + 1) >> 8) & 0xF; 282 | else if ((*i & 0xFBF0) == 0xF240 && (*(i + 1) & 0x8000) == 0) 283 | return (*(i + 1) >> 8) & 0xF; 284 | else 285 | return 0; 286 | } 287 | 288 | static int insn_mov_imm_imm(uint16_t * i) 289 | { 290 | if ((*i & 0xF800) == 0x2000) 291 | return *i & 0xFF; 292 | else if ((*i & 0xFBEF) == 0xF04F && (*(i + 1) & 0x8000) == 0) 293 | return thumb_expand_imm_c(((*i & 0x0400) << 1) | ((*(i + 1) & 0x7000) >> 4) | (*(i + 1) & 0xFF)); 294 | else if ((*i & 0xFBF0) == 0xF240 && (*(i + 1) & 0x8000) == 0) 295 | return ((*i & 0xF) << 12) | ((*i & 0x0400) << 1) | ((*(i + 1) & 0x7000) >> 4) | (*(i + 1) & 0xFF); 296 | else 297 | return 0; 298 | } 299 | 300 | static int insn_is_cmp_imm(uint16_t * i) 301 | { 302 | if ((*i & 0xF800) == 0x2800) 303 | return 1; 304 | else if ((*i & 0xFBF0) == 0xF1B0 && (*(i + 1) & 0x8F00) == 0x0F00) 305 | return 1; 306 | else 307 | return 0; 308 | } 309 | 310 | static int insn_cmp_imm_rn(uint16_t * i) 311 | { 312 | if ((*i & 0xF800) == 0x2800) 313 | return (*i >> 8) & 7; 314 | else if ((*i & 0xFBF0) == 0xF1B0 && (*(i + 1) & 0x8F00) == 0x0F00) 315 | return *i & 0xF; 316 | else 317 | return 0; 318 | } 319 | 320 | static int insn_cmp_imm_imm(uint16_t * i) 321 | { 322 | if ((*i & 0xF800) == 0x2800) 323 | return *i & 0xFF; 324 | else if ((*i & 0xFBF0) == 0xF1B0 && (*(i + 1) & 0x8F00) == 0x0F00) 325 | return thumb_expand_imm_c(((*i & 0x0400) << 1) | ((*(i + 1) & 0x7000) >> 4) | (*(i + 1) & 0xFF)); 326 | else 327 | return 0; 328 | } 329 | 330 | static int insn_is_and_imm(uint16_t * i) 331 | { 332 | return (*i & 0xFBE0) == 0xF000 && (*(i + 1) & 0x8000) == 0; 333 | } 334 | 335 | static int insn_and_imm_rn(uint16_t * i) 336 | { 337 | return *i & 0xF; 338 | } 339 | 340 | static int insn_and_imm_rd(uint16_t * i) 341 | { 342 | return (*(i + 1) >> 8) & 0xF; 343 | } 344 | 345 | static int insn_and_imm_imm(uint16_t * i) 346 | { 347 | return thumb_expand_imm_c(((*i & 0x0400) << 1) | ((*(i + 1) & 0x7000) >> 4) | (*(i + 1) & 0xFF)); 348 | } 349 | 350 | static int insn_is_push(uint16_t * i) 351 | { 352 | if ((*i & 0xFE00) == 0xB400) 353 | return 1; 354 | else if (*i == 0xE92D) 355 | return 1; 356 | else if (*i == 0xF84D && (*(i + 1) & 0x0FFF) == 0x0D04) 357 | return 1; 358 | else 359 | return 0; 360 | } 361 | 362 | static int insn_push_registers(uint16_t * i) 363 | { 364 | if ((*i & 0xFE00) == 0xB400) 365 | return (*i & 0x00FF) | ((*i & 0x0100) << 6); 366 | else if (*i == 0xE92D) 367 | return *(i + 1); 368 | else if (*i == 0xF84D && (*(i + 1) & 0x0FFF) == 0x0D04) 369 | return 1 << ((*(i + 1) >> 12) & 0xF); 370 | else 371 | return 0; 372 | } 373 | 374 | static int insn_is_preamble_push(uint16_t * i) 375 | { 376 | return insn_is_push(i) && (insn_push_registers(i) & (1 << 14)) != 0; 377 | } 378 | 379 | static int insn_is_str_imm(uint16_t * i) 380 | { 381 | if ((*i & 0xF800) == 0x6000) 382 | return 1; 383 | else if ((*i & 0xF800) == 0x9000) 384 | return 1; 385 | else if ((*i & 0xFFF0) == 0xF8C0) 386 | return 1; 387 | else if ((*i & 0xFFF0) == 0xF840 && (*(i + 1) & 0x0800) == 0x0800) 388 | return 1; 389 | else 390 | return 0; 391 | } 392 | 393 | static int insn_str_imm_postindexed(uint16_t * i) 394 | { 395 | if ((*i & 0xF800) == 0x6000) 396 | return 1; 397 | else if ((*i & 0xF800) == 0x9000) 398 | return 1; 399 | else if ((*i & 0xFFF0) == 0xF8C0) 400 | return 1; 401 | else if ((*i & 0xFFF0) == 0xF840 && (*(i + 1) & 0x0800) == 0x0800) 402 | return (*(i + 1) >> 10) & 1; 403 | else 404 | return 0; 405 | } 406 | 407 | static int insn_str_imm_wback(uint16_t * i) 408 | { 409 | if ((*i & 0xF800) == 0x6000) 410 | return 0; 411 | else if ((*i & 0xF800) == 0x9000) 412 | return 0; 413 | else if ((*i & 0xFFF0) == 0xF8C0) 414 | return 0; 415 | else if ((*i & 0xFFF0) == 0xF840 && (*(i + 1) & 0x0800) == 0x0800) 416 | return (*(i + 1) >> 8) & 1; 417 | else 418 | return 0; 419 | } 420 | 421 | static int insn_str_imm_imm(uint16_t * i) 422 | { 423 | if ((*i & 0xF800) == 0x6000) 424 | return (*i & 0x07C0) >> 4; 425 | else if ((*i & 0xF800) == 0x9000) 426 | return (*i & 0xFF) << 2; 427 | else if ((*i & 0xFFF0) == 0xF8C0) 428 | return (*(i + 1) & 0xFFF); 429 | else if ((*i & 0xFFF0) == 0xF840 && (*(i + 1) & 0x0800) == 0x0800) 430 | return (*(i + 1) & 0xFF); 431 | else 432 | return 0; 433 | } 434 | 435 | static int insn_str_imm_rt(uint16_t * i) 436 | { 437 | if ((*i & 0xF800) == 0x6000) 438 | return (*i & 7); 439 | else if ((*i & 0xF800) == 0x9000) 440 | return (*i >> 8) & 7; 441 | else if ((*i & 0xFFF0) == 0xF8C0) 442 | return (*(i + 1) >> 12) & 0xF; 443 | else if ((*i & 0xFFF0) == 0xF840 && (*(i + 1) & 0x0800) == 0x0800) 444 | return (*(i + 1) >> 12) & 0xF; 445 | else 446 | return 0; 447 | } 448 | 449 | static int insn_str_imm_rn(uint16_t * i) 450 | { 451 | if ((*i & 0xF800) == 0x6000) 452 | return (*i >> 3) & 7; 453 | else if ((*i & 0xF800) == 0x9000) 454 | return 13; 455 | else if ((*i & 0xFFF0) == 0xF8C0) 456 | return (*i & 0xF); 457 | else if ((*i & 0xFFF0) == 0xF840 && (*(i + 1) & 0x0800) == 0x0800) 458 | return (*i & 0xF); 459 | else 460 | return 0; 461 | } 462 | 463 | // Given an instruction, search backwards until an instruction is found matching the specified criterion. 464 | static uint16_t *find_last_insn_matching(uint32_t region, uint8_t * kdata, size_t ksize, uint16_t * current_instruction, int (*match_func) (uint16_t *)) 465 | { 466 | while ((uintptr_t) current_instruction > (uintptr_t) kdata) { 467 | if (insn_is_32bit(current_instruction - 2) && !insn_is_32bit(current_instruction - 3)) { 468 | current_instruction -= 2; 469 | } else { 470 | --current_instruction; 471 | } 472 | 473 | if (match_func(current_instruction)) { 474 | return current_instruction; 475 | } 476 | } 477 | 478 | return NULL; 479 | } 480 | 481 | // Given an instruction and a register, find the PC-relative address that was stored inside the register by the time the instruction was reached. 482 | static uint32_t find_pc_rel_value(uint32_t region, uint8_t * kdata, size_t ksize, uint16_t * insn, int reg) 483 | { 484 | // Find the last instruction that completely wiped out this register 485 | int found = 0; 486 | uint16_t *current_instruction = insn; 487 | while ((uintptr_t) current_instruction > (uintptr_t) kdata) { 488 | if (insn_is_32bit(current_instruction - 2)) { 489 | current_instruction -= 2; 490 | } else { 491 | --current_instruction; 492 | } 493 | 494 | if (insn_is_mov_imm(current_instruction) && insn_mov_imm_rd(current_instruction) == reg) { 495 | found = 1; 496 | break; 497 | } 498 | 499 | if (insn_is_ldr_literal(current_instruction) && insn_ldr_literal_rt(current_instruction) == reg) { 500 | found = 1; 501 | break; 502 | } 503 | } 504 | 505 | if (!found) 506 | return 0; 507 | 508 | // Step through instructions, executing them as a virtual machine, only caring about instructions that affect the target register and are commonly used for PC-relative addressing. 509 | uint32_t value = 0; 510 | while ((uintptr_t) current_instruction < (uintptr_t) insn) { 511 | if (insn_is_mov_imm(current_instruction) && insn_mov_imm_rd(current_instruction) == reg) { 512 | value = insn_mov_imm_imm(current_instruction); 513 | } else if (insn_is_ldr_literal(current_instruction) && insn_ldr_literal_rt(current_instruction) == reg) { 514 | value = *(uint32_t *) (kdata + (((((uintptr_t) current_instruction - (uintptr_t) kdata) + 4) & 0xFFFFFFFC) + insn_ldr_literal_imm(current_instruction))); 515 | } else if (insn_is_movt(current_instruction) && insn_movt_rd(current_instruction) == reg) { 516 | value |= insn_movt_imm(current_instruction) << 16; 517 | } else if (insn_is_add_reg(current_instruction) && insn_add_reg_rd(current_instruction) == reg) { 518 | if (insn_add_reg_rm(current_instruction) != 15 || insn_add_reg_rn(current_instruction) != reg) { 519 | // Can't handle this kind of operation! 520 | return 0; 521 | } 522 | 523 | value += ((uintptr_t) current_instruction - (uintptr_t) kdata) + 4; 524 | } 525 | 526 | current_instruction += insn_is_32bit(current_instruction) ? 2 : 1; 527 | } 528 | 529 | return value; 530 | } 531 | 532 | // Find PC-relative references to a certain address (relative to kdata). This is basically a virtual machine that only cares about instructions used in PC-relative addressing, so no branches, etc. 533 | static uint16_t *find_literal_ref(uint32_t region, uint8_t * kdata, size_t ksize, uint16_t * insn, uint32_t address) 534 | { 535 | uint16_t *current_instruction = insn; 536 | uint32_t value[16]; 537 | memset(value, 0, sizeof(value)); 538 | 539 | while ((uintptr_t) current_instruction < (uintptr_t) (kdata + ksize)) { 540 | if (insn_is_mov_imm(current_instruction)) { 541 | value[insn_mov_imm_rd(current_instruction)] = insn_mov_imm_imm(current_instruction); 542 | } else if (insn_is_ldr_literal(current_instruction)) { 543 | uintptr_t literal_address = (uintptr_t) kdata + ((((uintptr_t) current_instruction - (uintptr_t) kdata) + 4) & 0xFFFFFFFC) + insn_ldr_literal_imm(current_instruction); 544 | if (literal_address >= (uintptr_t) kdata && (literal_address + 4) <= ((uintptr_t) kdata + ksize)) { 545 | value[insn_ldr_literal_rt(current_instruction)] = *(uint32_t *) (literal_address); 546 | } 547 | } else if (insn_is_movt(current_instruction)) { 548 | value[insn_movt_rd(current_instruction)] |= insn_movt_imm(current_instruction) << 16; 549 | } else if (insn_is_add_reg(current_instruction)) { 550 | int reg = insn_add_reg_rd(current_instruction); 551 | if (insn_add_reg_rm(current_instruction) == 15 && insn_add_reg_rn(current_instruction) == reg) { 552 | value[reg] += ((uintptr_t) current_instruction - (uintptr_t) kdata) + 4; 553 | if (value[reg] == address) { 554 | return current_instruction; 555 | } 556 | } 557 | } 558 | 559 | current_instruction += insn_is_32bit(current_instruction) ? 2 : 1; 560 | } 561 | 562 | return NULL; 563 | } 564 | 565 | // This points to kernel_pmap. Use that to change the page tables if necessary. 566 | uint32_t find_pmap_location(uint32_t region, uint8_t * kdata, size_t ksize) 567 | { 568 | // Find location of the pmap_map_bd string. 569 | uint8_t *pmap_map_bd = memmem(kdata, ksize, "\"pmap_map_bd\"", sizeof("\"pmap_map_bd\"")); 570 | if (!pmap_map_bd) 571 | return 0; 572 | 573 | // Find a reference to the pmap_map_bd string. That function also references kernel_pmap 574 | uint16_t *ptr = find_literal_ref(region, kdata, ksize, (uint16_t *) kdata, (uintptr_t) pmap_map_bd - (uintptr_t) kdata); 575 | if (!ptr) 576 | return 0; 577 | 578 | // Find the beginning of it (we may have a version that throws panic after the function end). 579 | while (*ptr != 0xB5F0) { 580 | if ((uint8_t *)ptr == kdata) 581 | return 0; 582 | ptr--; 583 | } 584 | 585 | // Find the end of it. 586 | const uint8_t search_function_end[] = { 0xF0, 0xBD }; 587 | ptr = memmem(ptr, ksize - ((uintptr_t) ptr - (uintptr_t) kdata), search_function_end, sizeof(search_function_end)); 588 | if (!ptr) 589 | return 0; 590 | 591 | // Find the last BL before the end of it. The third argument to it should be kernel_pmap 592 | uint16_t *bl = find_last_insn_matching(region, kdata, ksize, ptr, insn_is_bl); 593 | if (!bl) 594 | return 0; 595 | 596 | // Find the last LDR R2, [R*] before it that's before any branches. If there are branches, then we have a version of the function that assumes kernel_pmap instead of being passed it. 597 | uint16_t *ldr_r2 = NULL; 598 | uint16_t *current_instruction = bl; 599 | while ((uintptr_t) current_instruction > (uintptr_t) kdata) { 600 | if (insn_is_32bit(current_instruction - 2) && !insn_is_32bit(current_instruction - 3)) { 601 | current_instruction -= 2; 602 | } else { 603 | --current_instruction; 604 | } 605 | 606 | if (insn_ldr_imm_rt(current_instruction) == 2 && insn_ldr_imm_imm(current_instruction) == 0) { 607 | ldr_r2 = current_instruction; 608 | break; 609 | } else if (insn_is_b_conditional(current_instruction) || insn_is_b_unconditional(current_instruction)) { 610 | break; 611 | } 612 | } 613 | 614 | // The function has a third argument, which must be kernel_pmap. Find out its address 615 | if (ldr_r2) 616 | return find_pc_rel_value(region, kdata, ksize, ldr_r2, insn_ldr_imm_rn(ldr_r2)); 617 | 618 | // The function has no third argument, Follow the BL. 619 | uint32_t imm32 = insn_bl_imm32(bl); 620 | uint32_t target = ((uintptr_t) bl - (uintptr_t) kdata) + 4 + imm32; 621 | if (target > ksize) 622 | return 0; 623 | 624 | // Find the first PC-relative reference in this function. 625 | int found = 0; 626 | 627 | int rd; 628 | current_instruction = (uint16_t *) (kdata + target); 629 | while ((uintptr_t) current_instruction < (uintptr_t) (kdata + ksize)) { 630 | if (insn_is_add_reg(current_instruction) && insn_add_reg_rm(current_instruction) == 15) { 631 | found = 1; 632 | rd = insn_add_reg_rd(current_instruction); 633 | current_instruction += insn_is_32bit(current_instruction) ? 2 : 1; 634 | break; 635 | } 636 | 637 | current_instruction += insn_is_32bit(current_instruction) ? 2 : 1; 638 | } 639 | 640 | if (!found) 641 | return 0; 642 | 643 | return find_pc_rel_value(region, kdata, ksize, current_instruction, rd); 644 | } 645 | 646 | // Function to find the syscall 0 function pointer. Used to modify the syscall table to call our own code. 647 | uint32_t find_syscall0(uint32_t region, uint8_t * kdata, size_t ksize) 648 | { 649 | // Search for the preamble to syscall 1 650 | const uint8_t syscall1_search[] = { 0x90, 0xB5, 0x01, 0xAF, 0x82, 0xB0, 0x09, 0x68, 0x01, 0x24, 0x00, 0x23 }; 651 | void *ptr = memmem(kdata, ksize, syscall1_search, sizeof(syscall1_search)); 652 | if (!ptr) 653 | return 0; 654 | 655 | // Search for a pointer to syscall 1 656 | uint32_t ptr_address = (uintptr_t) ptr - (uintptr_t) kdata + region; 657 | uint32_t function = ptr_address | 1; 658 | void *syscall1_entry = memmem(kdata, ksize, &function, sizeof(function)); 659 | if (!syscall1_entry) 660 | return 0; 661 | 662 | // Calculate the address of syscall 0 from the address of the syscall 1 entry 663 | return (uintptr_t) syscall1_entry - (uintptr_t) kdata - 0x18; // XXX 9.x should use - 0x10 664 | } 665 | 666 | /* --- planetbeing patchfinder --- */ 667 | -------------------------------------------------------------------------------- /kexty.c: -------------------------------------------------------------------------------- 1 | /* 2 | * kext loader 3 | * 4 | * Copyright (c) 2015, 2016 xerub 5 | */ 6 | 7 | #define _POSIX_C_SOURCE 200809L 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "kdb.h" 22 | #include "kdb.c" 23 | 24 | #ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ 25 | #include 26 | #undef round_page 27 | #define HOST_KERNEL_PORT 4 28 | #endif 29 | 30 | //#define MY_LOGGER // debugging stuff for 32bit (7.x, maybe 8.x...) 31 | #define MY_LOGGER_SIZE (64 * 1024) 32 | 33 | typedef unsigned long long addr_t; 34 | 35 | struct dependency { 36 | uint8_t *buf; 37 | size_t size; 38 | addr_t base; 39 | const char *name; 40 | }; 41 | 42 | #define round_page(size) ((size + 0xFFF) & ~0xFFF) 43 | 44 | #define IS64(image) (*(uint8_t *)(image) & 1) 45 | 46 | #define MACHO(p) ((*(unsigned int *)(p) & ~1) == 0xfeedface) 47 | 48 | /* generic stuff *************************************************************/ 49 | 50 | #define UCHAR_MAX 255 51 | 52 | static unsigned char * 53 | boyermoore_horspool_memmem(const unsigned char* haystack, size_t hlen, 54 | const unsigned char* needle, size_t nlen) 55 | { 56 | size_t last, scan = 0; 57 | size_t bad_char_skip[UCHAR_MAX + 1]; /* Officially called: 58 | * bad character shift */ 59 | 60 | /* Sanity checks on the parameters */ 61 | if (nlen <= 0 || !haystack || !needle) 62 | return NULL; 63 | 64 | /* ---- Preprocess ---- */ 65 | /* Initialize the table to default value */ 66 | /* When a character is encountered that does not occur 67 | * in the needle, we can safely skip ahead for the whole 68 | * length of the needle. 69 | */ 70 | for (scan = 0; scan <= UCHAR_MAX; scan = scan + 1) 71 | bad_char_skip[scan] = nlen; 72 | 73 | /* C arrays have the first byte at [0], therefore: 74 | * [nlen - 1] is the last byte of the array. */ 75 | last = nlen - 1; 76 | 77 | /* Then populate it with the analysis of the needle */ 78 | for (scan = 0; scan < last; scan = scan + 1) 79 | bad_char_skip[needle[scan]] = last - scan; 80 | 81 | /* ---- Do the matching ---- */ 82 | 83 | /* Search the haystack, while the needle can still be within it. */ 84 | while (hlen >= nlen) 85 | { 86 | /* scan from the end of the needle */ 87 | for (scan = last; haystack[scan] == needle[scan]; scan = scan - 1) 88 | if (scan == 0) /* If the first byte matches, we've found it. */ 89 | return (void *)haystack; 90 | 91 | /* otherwise, we need to skip some bytes and start again. 92 | Note that here we are getting the skip value based on the last byte 93 | of needle, no matter where we didn't match. So if needle is: "abcd" 94 | then we are skipping based on 'd' and that value will be 4, and 95 | for "abcdd" we again skip on 'd' but the value will be only 1. 96 | The alternative of pretending that the mismatched character was 97 | the last character is slower in the normal case (E.g. finding 98 | "abcd" in "...azcd..." gives 4 by using 'd' but only 99 | 4-2==2 using 'z'. */ 100 | hlen -= bad_char_skip[haystack[last]]; 101 | haystack += bad_char_skip[haystack[last]]; 102 | } 103 | 104 | return NULL; 105 | } 106 | 107 | static void * 108 | mempcpy(void *dest, const void *src, size_t n) 109 | { 110 | return (char *)memmove(dest, src, n) + n; 111 | } 112 | 113 | static char * 114 | read_file(const char *filename, size_t *sz) 115 | { 116 | int rv; 117 | int fd; 118 | char *buf; 119 | struct stat st; 120 | *sz = 0; 121 | fd = open(filename, O_RDONLY); 122 | if (fd < 0) { 123 | return NULL; 124 | } 125 | rv = fstat(fd, &st); 126 | if (rv) { 127 | close(fd); 128 | return NULL; 129 | } 130 | buf = malloc(st.st_size + 1); 131 | if (!buf) { 132 | close(fd); 133 | return NULL; 134 | } 135 | *sz = read(fd, buf, st.st_size); 136 | close(fd); 137 | if (*sz != (size_t)st.st_size) { 138 | free(buf); 139 | return NULL; 140 | } 141 | buf[*sz] = '\0'; 142 | return buf; 143 | } 144 | 145 | /* xml stuff *****************************************************************/ 146 | 147 | static char * 148 | xml_newval(const char *key, const char *value, char *buf, size_t *sz, int type) 149 | { 150 | char *p; 151 | char *ptr, *tmp; 152 | const char *tag, *endtag; 153 | size_t newsize; 154 | 155 | p = strstr(buf, ""); /* first tag */ 156 | assert(p); 157 | 158 | switch (type) { 159 | case 0: 160 | tag = ""; 161 | endtag = ""; 162 | break; 163 | case 1: 164 | tag = ""; 165 | endtag = ""; 166 | break; 167 | case 2: 168 | tag = ""; 169 | endtag = ""; 170 | break; 171 | default: 172 | assert(0); 173 | } 174 | 175 | p += sizeof("") - 1; 176 | 177 | newsize = *sz + 15 + strlen(key) + strlen(tag) + strlen(value) + strlen(endtag); 178 | 179 | ptr = malloc(newsize + 1); 180 | assert(ptr); 181 | 182 | tmp = mempcpy(ptr, buf, p - buf); 183 | *tmp++ = '\n'; 184 | *tmp++ = '\t'; 185 | tmp = mempcpy(tmp, "", 5); 186 | tmp = mempcpy(tmp, key, strlen(key)); 187 | tmp = mempcpy(tmp, "", 6); 188 | *tmp++ = '\n'; 189 | *tmp++ = '\t'; 190 | tmp = stpcpy(tmp, tag); 191 | tmp = stpcpy(tmp, value); 192 | tmp = stpcpy(tmp, endtag); 193 | tmp = memcpy(tmp, p, *sz - (p - buf) + 1); 194 | 195 | free(buf); 196 | buf = ptr; 197 | 198 | *sz = newsize; 199 | return buf; 200 | } 201 | 202 | static char * 203 | xml_setval(const char *key, const char *value, char *buf, size_t *sz, int type) 204 | { 205 | char *p, *q; 206 | char *ptr, *tmp; 207 | size_t oldlen, newlen; 208 | 209 | p = strstr(buf, key); /* find key */ 210 | if (!p) { 211 | return xml_newval(key, value, buf, sz, type); 212 | } 213 | p = strchr(p, '>'); /* end of key tag */ 214 | assert(p); 215 | p = strchr(p + 1, '>'); /* end of value tag */ 216 | assert(p); 217 | 218 | for (q = p; q > buf && isspace(q[-1]); q--) { 219 | continue; 220 | } 221 | if (q[-1] == '/') { 222 | char *tag, *endtag; 223 | for (tag = q; tag > buf && tag[-1] != '<'; tag--) { 224 | continue; 225 | } 226 | while (isspace(*tag)) { 227 | tag++; 228 | } 229 | endtag = tag; 230 | while (!isspace(*endtag) && endtag < q - 1) { 231 | endtag++; 232 | } 233 | 234 | q[-1] = '>'; 235 | 236 | newlen = strlen(value); 237 | ptr = malloc((q - buf) + newlen + (endtag - tag) + *sz - (p - buf) + 3); 238 | assert(ptr); 239 | 240 | tmp = mempcpy(ptr, buf, q - buf); 241 | tmp = mempcpy(tmp, value, newlen); 242 | tmp = mempcpy(tmp, "", 1); 245 | tmp = memcpy(tmp, p + 1, *sz - (p - buf)); 246 | free(buf); 247 | *sz = (q - buf) + newlen + (endtag - tag) + *sz - (p - buf) + 2; 248 | return ptr; 249 | } 250 | 251 | p++; 252 | q = strchr(p, '<'); 253 | 254 | oldlen = q - p; 255 | newlen = strlen(value); 256 | if (oldlen >= newlen) { 257 | /* can use old block */ 258 | memcpy(p, value, newlen); 259 | memmove(p + newlen, q, *sz - (q - buf) + 1); 260 | } else { 261 | /* we need a new block */ 262 | ptr = malloc(*sz - oldlen + newlen + 1); 263 | assert(ptr); 264 | memcpy(ptr, buf, p - buf); 265 | memcpy(ptr + (p - buf), value, newlen); 266 | memcpy(ptr + (p - buf) + newlen, q, *sz - (q - buf) + 1); 267 | free(buf); 268 | buf = ptr; 269 | } 270 | *sz = *sz - oldlen + newlen; 271 | return buf; 272 | } 273 | 274 | static char * 275 | xml_sethex(const char *key, addr_t value, char *buf, size_t *sz, int type) 276 | { 277 | char tmp[256]; 278 | snprintf(tmp, sizeof(tmp), "0x%llx", value); 279 | return xml_setval(key, tmp, buf, sz, type); 280 | } 281 | 282 | /* generic macho *************************************************************/ 283 | 284 | static int 285 | make_all_sections_visible(uint8_t *buf, size_t size) 286 | { 287 | unsigned i, j; 288 | struct mach_header *hdr = (struct mach_header *)buf; 289 | uint8_t *q = buf + sizeof(struct mach_header); 290 | 291 | (void)size; 292 | 293 | if (!MACHO(buf)) { 294 | return -1; 295 | } 296 | if (IS64(buf)) { 297 | q += 4; 298 | } 299 | 300 | for (i = 0; i < hdr->ncmds; i++) { 301 | struct load_command *cmd = (struct load_command *)q; 302 | if (cmd->cmd == LC_SEGMENT) { 303 | struct segment_command *seg = (struct segment_command *)q; 304 | struct section *sec = (struct section *)(seg + 1); 305 | for (j = 0; j < seg->nsects; j++) { 306 | /*printf("%.16s/%.16s:%.16s\n", seg->segname, sec[j].segname, sec[j].sectname);*/ 307 | if (sec[j].flags == S_ZEROFILL) { 308 | sec[j].flags = S_REGULAR; 309 | sec[j].offset = seg->fileoff + sec[j].addr - seg->vmaddr; 310 | } 311 | } 312 | } 313 | if (cmd->cmd == LC_SEGMENT_64) { 314 | struct segment_command_64 *seg = (struct segment_command_64 *)q; 315 | struct section_64 *sec = (struct section_64 *)(seg + 1); 316 | for (j = 0; j < seg->nsects; j++) { 317 | /*printf("%.16s/%.16s:%.16s\n", seg->segname, sec[j].segname, sec[j].sectname);*/ 318 | if (sec[j].flags == S_ZEROFILL) { 319 | sec[j].flags = S_REGULAR; 320 | sec[j].offset = seg->fileoff + sec[j].addr - seg->vmaddr; 321 | } 322 | } 323 | } 324 | q = q + cmd->cmdsize; 325 | } 326 | 327 | return 0; 328 | } 329 | 330 | static addr_t 331 | get_base(uint8_t *buf, size_t size) 332 | { 333 | unsigned i; 334 | const struct mach_header *hdr = (struct mach_header *)buf; 335 | const uint8_t *q = buf + sizeof(struct mach_header); 336 | 337 | (void)size; 338 | 339 | if (!MACHO(buf)) { 340 | return -1; 341 | } 342 | if (IS64(buf)) { 343 | q += 4; 344 | } 345 | 346 | for (i = 0; i < hdr->ncmds; i++) { 347 | const struct load_command *cmd = (struct load_command *)q; 348 | if (cmd->cmd == LC_SEGMENT) { 349 | const struct segment_command *seg = (struct segment_command *)q; 350 | if (!strcmp(seg->segname, "__TEXT")) { 351 | return seg->vmaddr; 352 | } 353 | } 354 | if (cmd->cmd == LC_SEGMENT_64) { 355 | const struct segment_command_64 *seg = (struct segment_command_64 *)q; 356 | if (!strcmp(seg->segname, "__TEXT")) { 357 | return seg->vmaddr; 358 | } 359 | } 360 | q = q + cmd->cmdsize; 361 | } 362 | 363 | return -1; 364 | } 365 | 366 | static addr_t 367 | get_sect_data(const uint8_t *p, size_t size, const char *segname, const char *sectname, size_t *sz) 368 | { 369 | unsigned i, j; 370 | const struct mach_header *hdr = (struct mach_header *)p; 371 | const uint8_t *q = p + sizeof(struct mach_header); 372 | 373 | (void)size; 374 | 375 | if (sz) *sz = 0; 376 | 377 | if (!MACHO(p)) { 378 | return 0; 379 | } 380 | if (IS64(p)) { 381 | q += 4; 382 | } 383 | 384 | for (i = 0; i < hdr->ncmds; i++) { 385 | const struct load_command *cmd = (struct load_command *)q; 386 | if (cmd->cmd == LC_SEGMENT) { 387 | const struct segment_command *seg = (struct segment_command *)q; 388 | if (!strcmp(seg->segname, segname)) { 389 | const struct section *sec = (struct section *)(seg + 1); 390 | if (sectname == NULL) { 391 | if (sz) *sz = seg->filesize; 392 | return seg->fileoff; 393 | } 394 | for (j = 0; j < seg->nsects; j++) { 395 | if (!strcmp(sec[j].sectname, sectname)) { 396 | if (sz) *sz = sec[j].size; 397 | if (sec[j].flags == S_ZEROFILL) { 398 | return seg->fileoff + sec[j].addr - seg->vmaddr; 399 | } 400 | return sec[j].offset; 401 | } 402 | } 403 | } 404 | } 405 | if (cmd->cmd == LC_SEGMENT_64) { 406 | const struct segment_command_64 *seg = (struct segment_command_64 *)q; 407 | if (!strcmp(seg->segname, segname)) { 408 | const struct section_64 *sec = (struct section_64 *)(seg + 1); 409 | if (sectname == NULL) { 410 | if (sz) *sz = seg->filesize; 411 | return seg->fileoff; 412 | } 413 | for (j = 0; j < seg->nsects; j++) { 414 | if (!strcmp(sec[j].sectname, sectname)) { 415 | if (sz) *sz = sec[j].size; 416 | if (sec[j].flags == S_ZEROFILL) { 417 | return seg->fileoff + sec[j].addr - seg->vmaddr; 418 | } 419 | return sec[j].offset; 420 | } 421 | } 422 | } 423 | } 424 | q = q + cmd->cmdsize; 425 | } 426 | 427 | return 0; 428 | } 429 | 430 | static addr_t 431 | pre_alloc_sect(uint8_t *p, size_t size, size_t need) 432 | { 433 | unsigned i; 434 | const struct mach_header *hdr = (struct mach_header *)p; 435 | const uint8_t *q = p + sizeof(struct mach_header); 436 | unsigned hdrsize; 437 | 438 | (void)size; 439 | 440 | if (!MACHO(p)) { 441 | return 0; 442 | } 443 | if (IS64(p)) { 444 | q += 4; 445 | } 446 | hdrsize = q - p + hdr->sizeofcmds; 447 | 448 | for (i = 0; i < hdr->ncmds; i++) { 449 | const struct load_command *cmd = (struct load_command *)q; 450 | if (cmd->cmd == LC_SEGMENT) { 451 | const struct segment_command *seg = (struct segment_command *)q; 452 | struct section *sec = (struct section *)(seg + 1); 453 | if (seg->nsects && sec->offset - need >= hdrsize) { 454 | sec->offset -= need; 455 | sec->addr -= need; 456 | sec->size += need; 457 | return sec->offset; 458 | } 459 | break; 460 | } 461 | if (cmd->cmd == LC_SEGMENT_64) { 462 | const struct segment_command_64 *seg = (struct segment_command_64 *)q; 463 | struct section_64 *sec = (struct section_64 *)(seg + 1); 464 | if (seg->nsects && sec->offset - need >= hdrsize) { 465 | sec->offset -= need; 466 | sec->addr -= need; 467 | sec->size += need; 468 | return sec->offset; 469 | } 470 | break; 471 | } 472 | q = q + cmd->cmdsize; 473 | } 474 | 475 | return 0; 476 | } 477 | 478 | static addr_t 479 | get_vaddr(const uint8_t *p, size_t size, addr_t where) 480 | { 481 | unsigned i; 482 | const struct mach_header *hdr = (struct mach_header *)p; 483 | const uint8_t *q = p + sizeof(struct mach_header); 484 | 485 | (void)size; 486 | 487 | if (!MACHO(p)) { 488 | return 0; 489 | } 490 | if (IS64(p)) { 491 | q += 4; 492 | } 493 | 494 | for (i = 0; i < hdr->ncmds; i++) { 495 | const struct load_command *cmd = (struct load_command *)q; 496 | if (cmd->cmd == LC_SEGMENT) { 497 | const struct segment_command *seg = (struct segment_command *)q; 498 | if (where >= seg->fileoff && where < seg->fileoff + seg->filesize) { 499 | return seg->vmaddr + where - seg->fileoff; 500 | } 501 | } 502 | if (cmd->cmd == LC_SEGMENT_64) { 503 | const struct segment_command_64 *seg = (struct segment_command_64 *)q; 504 | if (where >= seg->fileoff && where < seg->fileoff + seg->filesize) { 505 | return seg->vmaddr + where - seg->fileoff; 506 | } 507 | } 508 | q = q + cmd->cmdsize; 509 | } 510 | 511 | return 0; 512 | } 513 | 514 | /* assembler *****************************************************************/ 515 | 516 | static unsigned int 517 | make_bl32(int blx, int pos, int tgt) 518 | { 519 | int delta; 520 | unsigned short pfx; 521 | unsigned short sfx; 522 | 523 | unsigned int omask = 0xF800; 524 | unsigned int amask = 0x7FF; 525 | 526 | if (blx) { /* XXX untested */ 527 | omask = 0xE800; 528 | amask = 0x7FE; 529 | pos &= ~3; 530 | } 531 | 532 | delta = tgt - pos - 4; /* range: 0x400000 */ 533 | pfx = 0xF000 | ((delta >> 12) & 0x7FF); 534 | sfx = omask | ((delta >> 1) & amask); 535 | 536 | return (unsigned int)pfx | ((unsigned int)sfx << 16); 537 | } 538 | 539 | static uint32_t 540 | make_movw(unsigned reg, unsigned imm) 541 | { 542 | unsigned lo, hi; 543 | 544 | unsigned imm4 = (imm >> 12) & 15; 545 | unsigned i = (imm >> 11) & 1; 546 | unsigned imm3 = (imm >> 8) & 7; 547 | unsigned imm8 = imm & 255; 548 | 549 | reg &= 15; 550 | 551 | lo = (0xF240 << 0) | (i << 10) | imm4; 552 | hi = (imm3 << 12) | (reg << 8) | imm8; 553 | 554 | return lo | (hi << 16); 555 | } 556 | 557 | static uint32_t 558 | make_movt(unsigned reg, unsigned imm) 559 | { 560 | unsigned lo, hi; 561 | 562 | unsigned imm4 = (imm >> 12) & 15; 563 | unsigned i = (imm >> 11) & 1; 564 | unsigned imm3 = (imm >> 8) & 7; 565 | unsigned imm8 = imm & 255; 566 | 567 | reg &= 15; 568 | 569 | lo = (0xF2C0 << 0) | (i << 10) | imm4; 570 | hi = (imm3 << 12) | (reg << 8) | imm8; 571 | 572 | return lo | (hi << 16); 573 | } 574 | 575 | static uint64_t 576 | make_move(unsigned reg, unsigned imm) 577 | { 578 | uint32_t lo, hi; 579 | lo = make_movw(reg, imm); 580 | hi = make_movt(reg, imm >> 16); 581 | return ((uint64_t)hi << 32) | lo; 582 | } 583 | 584 | /* patchfinder ***************************************************************/ 585 | 586 | #pragma GCC diagnostic push 587 | #pragma GCC diagnostic ignored "-Wunused-parameter" 588 | #define static /* pragma ignored "-Wunused-function" is broken on some gcc */ 589 | #define memmem(a, b, c, d) (void *)boyermoore_horspool_memmem((const uint8_t *)(a), b, (const uint8_t *)(c), d) 590 | #include "patchfinder.c" 591 | #undef static 592 | #pragma GCC diagnostic pop 593 | 594 | static addr_t 595 | step_thumb(const uint8_t *buf, addr_t start, size_t length, uint32_t what, uint32_t mask) 596 | { 597 | addr_t end = start + length; 598 | while (start < end) { 599 | uint32_t x = *(uint32_t *)(buf + start); 600 | if ((x & mask) == what) { 601 | return start; 602 | } 603 | start += insn_is_32bit((uint16_t *)(buf + start)) ? 4 : 2; 604 | } 605 | return 0; 606 | } 607 | 608 | static addr_t 609 | bof32(const uint8_t *buf, addr_t start, addr_t where) 610 | { 611 | for (where &= ~1; where >= start; where -= 2) { 612 | uint16_t op = *(uint16_t *)(buf + where); 613 | if ((op & 0xFF00) == 0xB500) { 614 | //printf("%x: PUSH {LR}\n", where); 615 | return where; 616 | } 617 | if (where - 4 >= start && (buf[where - 3] & 0xF8) > 0xE0) { 618 | where -= 2; 619 | } 620 | } 621 | return 0; 622 | } 623 | 624 | static addr_t 625 | calc32(const uint8_t *buf, addr_t start, addr_t end, int r) 626 | { 627 | addr_t i; 628 | uint32_t value[16]; 629 | 630 | memset(value, 0, sizeof(value)); 631 | 632 | end &= ~1; 633 | for (i = start & ~1; i < end; ) { 634 | uint16_t *current_instruction = (uint16_t *)(buf + i); 635 | if (insn_is_mov_imm(current_instruction)) { 636 | value[insn_mov_imm_rd(current_instruction)] = insn_mov_imm_imm(current_instruction); 637 | } else if (insn_is_ldr_literal(current_instruction)) { 638 | addr_t literal_address = (i & ~3) + 4 + insn_ldr_literal_imm(current_instruction); 639 | value[insn_ldr_literal_rt(current_instruction)] = *(uint32_t *) (buf + literal_address); 640 | } else if (insn_is_movt(current_instruction)) { 641 | value[insn_movt_rd(current_instruction)] |= insn_movt_imm(current_instruction) << 16; 642 | } else if (insn_is_add_reg(current_instruction)) { 643 | int reg = insn_add_reg_rd(current_instruction); 644 | if (insn_add_reg_rm(current_instruction) == 15 && insn_add_reg_rn(current_instruction) == reg) { 645 | value[reg] += i + 4; 646 | } 647 | } else if ((*current_instruction & 0xFF00) == 0x4600) { 648 | uint8_t regs = *current_instruction; 649 | int rn = (regs >> 3) & 15; 650 | int rd = (regs & 7) | ((regs & 0x80) >> 4); 651 | value[rd] = value[rn]; 652 | } 653 | i += insn_is_32bit(current_instruction) ? 4 : 2; 654 | } 655 | return value[r]; 656 | } 657 | 658 | static addr_t 659 | xref32(const uint8_t *buf, addr_t start, addr_t end, addr_t what) 660 | { 661 | addr_t i; 662 | uint32_t value[16]; 663 | 664 | memset(value, 0, sizeof(value)); 665 | 666 | end &= ~1; 667 | for (i = start & ~1; i < end; ) { 668 | uint16_t *current_instruction = (uint16_t *)(buf + i); 669 | if (insn_is_mov_imm(current_instruction)) { 670 | value[insn_mov_imm_rd(current_instruction)] = insn_mov_imm_imm(current_instruction); 671 | } else if (insn_is_ldr_literal(current_instruction)) { 672 | addr_t literal_address = (i & ~3) + 4 + insn_ldr_literal_imm(current_instruction); 673 | value[insn_ldr_literal_rt(current_instruction)] = *(uint32_t *) (buf + literal_address); 674 | } else if (insn_is_movt(current_instruction)) { 675 | value[insn_movt_rd(current_instruction)] |= insn_movt_imm(current_instruction) << 16; 676 | } else if (insn_is_add_reg(current_instruction)) { 677 | int reg = insn_add_reg_rd(current_instruction); 678 | if (insn_add_reg_rm(current_instruction) == 15 && insn_add_reg_rn(current_instruction) == reg) { 679 | value[reg] += i + 4; 680 | if (value[reg] == what) { 681 | return i; 682 | } 683 | } 684 | } 685 | i += insn_is_32bit(current_instruction) ? 4 : 2; 686 | } 687 | return 0; 688 | } 689 | 690 | static addr_t 691 | step_64(const uint8_t *buf, addr_t start, size_t length, uint32_t what, uint32_t mask) 692 | { 693 | addr_t end = start + length; 694 | while (start < end) { 695 | uint32_t x = *(uint32_t *)(buf + start); 696 | if ((x & mask) == what) { 697 | return start; 698 | } 699 | start += 4; 700 | } 701 | return 0; 702 | } 703 | 704 | static addr_t 705 | step_64_back(const uint8_t *buf, addr_t start, size_t length, uint32_t what, uint32_t mask) 706 | { 707 | addr_t end = start - length; 708 | while (start >= end) { 709 | uint32_t x = *(uint32_t *)(buf + start); 710 | if ((x & mask) == what) { 711 | return start; 712 | } 713 | start -= 4; 714 | } 715 | return 0; 716 | } 717 | 718 | static addr_t 719 | bof64(const uint8_t *buf, addr_t start, addr_t where) 720 | { 721 | for (; where >= start; where -= 4) { 722 | uint32_t op = *(uint32_t *)(buf + where); 723 | if ((op & 0xFFC003FF) == 0x910003FD) { 724 | unsigned delta = (op >> 10) & 0xFFF; 725 | //printf("%x: ADD X29, SP, #0x%x\n", where, delta); 726 | if ((delta & 0xF) == 0) { 727 | addr_t prev = where - ((delta >> 4) + 1) * 4; 728 | uint32_t au = *(uint32_t *)(buf + prev); 729 | if ((au & 0xFFC003E0) == 0xA98003E0) { 730 | //printf("%x: STP x, y, [SP,#-imm]!\n", prev); 731 | return prev; 732 | } 733 | } 734 | } 735 | } 736 | return 0; 737 | } 738 | 739 | static addr_t 740 | xref64(const uint8_t *buf, addr_t start, addr_t end, addr_t what) 741 | { 742 | addr_t i; 743 | uint64_t value[32]; 744 | 745 | memset(value, 0, sizeof(value)); 746 | 747 | end &= ~3; 748 | for (i = start & ~3; i < end; i += 4) { 749 | uint32_t op = *(uint32_t *)(buf + i); 750 | unsigned reg = op & 0x1F; 751 | if ((op & 0x9F000000) == 0x90000000) { 752 | unsigned adr = ((op & 0x60000000) >> 17) | ((op & 0xFFFFE0) << 9); 753 | //printf("%llx: ADRP X%d, 0x%x\n", i, reg, adr); 754 | value[reg] = adr + (i & ~0xFFF); 755 | } else if ((op & 0xFF000000) == 0x91000000) { 756 | unsigned rn = (op >> 5) & 0x1F; 757 | unsigned shift = (op >> 22) & 3; 758 | unsigned imm = (op >> 10) & 0xFFF; 759 | if (shift == 1) { 760 | imm <<= 12; 761 | } else { 762 | assert(shift == 0); 763 | } 764 | //printf("%llx: ADD X%d, X%d, 0x%x\n", i, reg, rn, imm); 765 | value[reg] = value[rn] + imm; 766 | } else if ((op & 0xF9C00000) == 0xF9400000) { 767 | unsigned rn = (op >> 5) & 0x1F; 768 | unsigned imm = ((op >> 10) & 0xFFF) << 3; 769 | //printf("%llx: LDR X%d, [X%d, 0x%x]\n", i, reg, rn, imm); 770 | value[reg] = value[rn] + imm; // XXX address, not actual value 771 | } else if ((op & 0x9F000000) == 0x10000000) { 772 | unsigned adr = ((op & 0x60000000) >> 29) | ((op & 0xFFFFE0) >> 3); 773 | //printf("%llx: ADR X%d, 0x%llx\n", i, reg, adr + i); 774 | value[reg] = adr + i; 775 | } else if ((op & 0xFF000000) == 0x58000000) { 776 | unsigned adr = (op & 0xFFFFE0) >> 3; 777 | //printf("%llx: LDR X%d, =0x%llx\n", i, reg, adr + i); 778 | value[reg] = adr + i; // XXX address, not actual value 779 | } 780 | if (value[reg] == what) { 781 | return i; 782 | } 783 | } 784 | return 0; 785 | } 786 | 787 | static addr_t 788 | calc64(const uint8_t *buf, addr_t start, addr_t end, int r) 789 | { 790 | addr_t i; 791 | uint64_t value[32]; 792 | 793 | memset(value, 0, sizeof(value)); 794 | 795 | end &= ~3; 796 | for (i = start & ~3; i < end; i += 4) { 797 | uint32_t op = *(uint32_t *)(buf + i); 798 | unsigned reg = op & 0x1F; 799 | if ((op & 0x9F000000) == 0x90000000) { 800 | unsigned adr = ((op & 0x60000000) >> 17) | ((op & 0xFFFFE0) << 9); 801 | //printf("%llx: ADRP X%d, 0x%x\n", i, reg, adr); 802 | value[reg] = adr + (i & ~0xFFF); 803 | } else if ((op & 0xFF000000) == 0x91000000) { 804 | unsigned rn = (op >> 5) & 0x1F; 805 | unsigned shift = (op >> 22) & 3; 806 | unsigned imm = (op >> 10) & 0xFFF; 807 | if (shift == 1) { 808 | imm <<= 12; 809 | } else { 810 | assert(shift == 0); 811 | } 812 | //printf("%llx: ADD X%d, X%d, 0x%x\n", i, reg, rn, imm); 813 | value[reg] = value[rn] + imm; 814 | } else if ((op & 0xF9C00000) == 0xF9400000) { 815 | unsigned rn = (op >> 5) & 0x1F; 816 | unsigned imm = ((op >> 10) & 0xFFF) << 3; 817 | //printf("%llx: LDR X%d, [X%d, 0x%x]\n", i, reg, rn, imm); 818 | value[reg] = value[rn] + imm; // XXX address, not actual value 819 | } else if ((op & 0x9F000000) == 0x10000000) { 820 | unsigned adr = ((op & 0x60000000) >> 29) | ((op & 0xFFFFE0) >> 3); 821 | //printf("%llx: ADR X%d, 0x%llx\n", i, reg, adr + i); 822 | value[reg] = adr + i; 823 | } else if ((op & 0xFF000000) == 0x58000000) { 824 | unsigned adr = (op & 0xFFFFE0) >> 3; 825 | //printf("%llx: LDR X%d, =0x%llx\n", i, reg, adr + i); 826 | value[reg] = adr + i; // XXX address, not actual value 827 | } 828 | } 829 | return value[r]; 830 | } 831 | 832 | /* machofinder ***************************************************************/ 833 | 834 | static addr_t 835 | find_dref(const uint8_t *buf, size_t size, addr_t data, int nth, int bof) 836 | { 837 | addr_t x; 838 | addr_t offset, end; 839 | size_t sz; 840 | 841 | offset = get_sect_data(buf, size, "__TEXT", "__text", &sz); 842 | if (!offset) { 843 | return 0; 844 | } 845 | end = offset + sz; 846 | 847 | if (IS64(buf)) { 848 | addr_t off = offset; 849 | do { 850 | x = xref64(buf, off, end, data); 851 | if (!x) { 852 | return 0; 853 | } 854 | off = x + 4; 855 | } while (nth--); 856 | if (bof) { 857 | x = bof64(buf, offset, x); 858 | } 859 | } else { 860 | addr_t off = offset; 861 | do { 862 | x = xref32(buf, off, end, data); 863 | if (!x) { 864 | return 0; 865 | } 866 | off = x + 4; 867 | } while (nth--); 868 | if (bof) { 869 | x = bof32(buf, offset, x); 870 | } 871 | } 872 | return x; 873 | } 874 | 875 | static addr_t 876 | find_sref(const uint8_t *buf, size_t size, const char *string, int bof) 877 | { 878 | unsigned char *str = boyermoore_horspool_memmem(buf, size, (const void *)string, strlen(string)); 879 | if (!str) { 880 | return 0; 881 | } 882 | return find_dref(buf, size, str - buf, 0, bof); 883 | } 884 | 885 | static addr_t 886 | find_metaclass32(uint8_t *buf, size_t size, addr_t base, const char *driver) 887 | { 888 | unsigned i, j; 889 | uint8_t *str; 890 | addr_t call, ref = 0, bof = 0; 891 | addr_t ctors, sect; 892 | addr_t addr; 893 | size_t sz; 894 | 895 | /* find the metaclass name */ 896 | str = boyermoore_horspool_memmem(buf, size, (uint8_t *)driver, strlen(driver) + 1); 897 | if (!str) { 898 | return 0; 899 | } 900 | 901 | /* find constructor section */ 902 | ctors = get_sect_data(buf, size, "__DATA", "__mod_init_func", &sz); 903 | if (!ctors) { 904 | ctors = get_sect_data(buf, size, "__DATA", "__constructor", &sz); 905 | } 906 | if (!ctors || !sz) { 907 | return 0; 908 | } 909 | 910 | /* find __text section */ 911 | sect = get_sect_data(buf, size, "__TEXT", "__text", NULL); 912 | if (!sect) { 913 | return 0; 914 | } 915 | 916 | /* check all references to driver name... */ 917 | for (j = 0; !ref; j++) { 918 | addr_t tmp = find_dref(buf, size, str - buf, j, 0); 919 | if (!tmp) { 920 | return 0; 921 | } 922 | bof = bof32(buf, sect, tmp); 923 | if (!bof) { 924 | return 0; 925 | } 926 | /* ... and see if we landed in a constructor */ 927 | for (i = 0; i < sz / 4; i++) { 928 | if (bof + base == (((uint32_t *)(buf + ctors))[i] & ~1)) { 929 | ref = tmp; 930 | break; 931 | } 932 | } 933 | } 934 | if (!ref) { 935 | return 0; 936 | } 937 | 938 | /* find next call */ 939 | call = step_thumb(buf, ref, 0x20, 0xD000F000, 0xD000F800); 940 | if (!call) { 941 | return 0; 942 | } 943 | 944 | /* calculate R0 at the time of call */ 945 | addr = calc32(buf, bof, call, 0); 946 | if (!addr) { 947 | return 0; 948 | } 949 | 950 | /* find __const section */ 951 | sect = get_sect_data(buf, size, "__DATA", "__const", &sz); 952 | if (!sect) { 953 | return 0; 954 | } 955 | 956 | /* now get a reference to the value above */ 957 | for (i = 0; i < sz / 4; i++) { 958 | if (addr + base == ((uint32_t *)(buf + sect))[i]) { 959 | return base + sect + i * 4; 960 | } 961 | } 962 | 963 | return 0; 964 | } 965 | 966 | static addr_t 967 | find_metaclass64(uint8_t *buf, size_t size, addr_t base, const char *driver) 968 | { 969 | unsigned i, j; 970 | uint8_t *str; 971 | addr_t call, ref = 0, bof = 0; 972 | addr_t ctors, sect; 973 | addr_t addr; 974 | size_t sz; 975 | 976 | /* find the metaclass name */ 977 | str = boyermoore_horspool_memmem(buf, size, (uint8_t *)driver, strlen(driver) + 1); 978 | if (!str) { 979 | return 0; 980 | } 981 | 982 | /* find constructor section */ 983 | ctors = get_sect_data(buf, size, "__DATA", "__mod_init_func", &sz); 984 | if (!ctors || !sz) { 985 | return 0; 986 | } 987 | 988 | /* find __text section */ 989 | sect = get_sect_data(buf, size, "__TEXT", "__text", NULL); 990 | if (!sect) { 991 | return 0; 992 | } 993 | 994 | /* check all references to driver name... */ 995 | for (j = 0; !ref; j++) { 996 | addr_t tmp = find_dref(buf, size, str - buf, j, 0); 997 | if (!tmp) { 998 | return 0; 999 | } 1000 | bof = bof64(buf, sect, tmp); 1001 | if (!bof) { 1002 | return 0; 1003 | } 1004 | /* ... and see if we landed in a constructor */ 1005 | for (i = 0; i < sz / 8; i++) { 1006 | if (bof + base == (((uint64_t *)(buf + ctors))[i] & ~1)) { 1007 | ref = tmp; 1008 | break; 1009 | } 1010 | } 1011 | } 1012 | if (!ref) { 1013 | return 0; 1014 | } 1015 | 1016 | /* find next call */ 1017 | call = step_64(buf, ref, 0x40, 0x94000000, 0xFC000000); 1018 | if (!call) { 1019 | return 0; 1020 | } 1021 | 1022 | /* calculate X0 at the time of call */ 1023 | addr = calc64(buf, bof, call, 0); 1024 | if (!addr) { 1025 | return 0; 1026 | } 1027 | 1028 | /* find __const section */ 1029 | sect = get_sect_data(buf, size, "__DATA", "__const", &sz); 1030 | if (!sect) { 1031 | return 0; 1032 | } 1033 | 1034 | /* now get a reference to the value above */ 1035 | for (i = 0; i < sz / 8; i++) { 1036 | if (addr + base == ((uint64_t *)(buf + sect))[i]) { 1037 | return base + sect + i * 8; 1038 | } 1039 | } 1040 | 1041 | return 0; 1042 | } 1043 | 1044 | static addr_t 1045 | find_vtable(const uint8_t *buf, size_t size, int which, unsigned *sz) 1046 | { 1047 | unsigned i, j; 1048 | int state = 0; 1049 | size_t const_size; 1050 | addr_t const_off = get_sect_data(buf, size, "__DATA", "__const", &const_size); 1051 | 1052 | if (sz) *sz = 0; 1053 | 1054 | if (!const_off) { 1055 | return 0; 1056 | } 1057 | 1058 | if (IS64(buf)) { 1059 | const uint64_t *abuf = (uint64_t *)(buf + const_off); 1060 | for (i = 0; i < const_size / sizeof(abuf[0]) - 2; i++) { 1061 | if (abuf[i] == 0 && abuf[i + 1] == 0 && abuf[i + 2] != 0 && state++ == which) { 1062 | for (j = i + 2; j < const_size / sizeof(abuf[0]); j++) { 1063 | if (abuf[j] == 0) { 1064 | if (sz) *sz = j - i; 1065 | break; 1066 | } 1067 | } 1068 | return const_off + i * sizeof(abuf[0]); 1069 | } 1070 | } 1071 | } else { 1072 | const uint32_t *abuf = (uint32_t *)(buf + const_off); 1073 | for (i = 0; i < const_size / sizeof(abuf[0]) - 2; i++) { 1074 | if (abuf[i] == 0 && abuf[i + 1] == 0 && abuf[i + 2] != 0 && state++ == which) { 1075 | for (j = i + 2; j < const_size / sizeof(abuf[0]); j++) { 1076 | if (abuf[j] == 0) { 1077 | if (sz) *sz = j - i; 1078 | break; 1079 | } 1080 | } 1081 | return const_off + i * sizeof(abuf[0]); 1082 | } 1083 | } 1084 | } 1085 | 1086 | return 0; 1087 | } 1088 | 1089 | /* kernel stuff **************************************************************/ 1090 | 1091 | #ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ 1092 | #include 1093 | 1094 | #ifdef __LP64__ 1095 | #define KDELTA 0x4000 /* XXX 7.x-8.x: 0x2000 */ 1096 | #else 1097 | #define KDELTA 0x1000 1098 | #endif 1099 | 1100 | kern_return_t bootstrap_look_up(mach_port_t bp, const char *service_name, mach_port_t *sp); 1101 | 1102 | static vm_address_t 1103 | get_kernel_base(task_t *kernel_task) 1104 | { 1105 | kern_return_t rv; 1106 | vm_region_submap_info_data_64_t info; 1107 | vm_size_t size; 1108 | mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64; 1109 | unsigned int depth = 0; 1110 | vm_address_t addr = 0x81200000; /* arm64: addr = 0xffffff8000000000 */ 1111 | 1112 | #ifdef HOST_KERNEL_PORT 1113 | *kernel_task = 0; 1114 | rv = host_get_special_port(mach_host_self(), HOST_LOCAL_NODE, HOST_KERNEL_PORT, kernel_task); 1115 | if (rv != KERN_SUCCESS || *kernel_task == 0) 1116 | #endif 1117 | rv = task_for_pid(mach_task_self(), 0, kernel_task); 1118 | if (rv != KERN_SUCCESS) { 1119 | *kernel_task = 0; 1120 | rv = bootstrap_look_up(bootstrap_port, "com.apple.kernel_task", kernel_task); 1121 | if (rv != KERN_SUCCESS || *kernel_task == 0) 1122 | return -1; 1123 | } 1124 | 1125 | while ((rv = vm_region_recurse_64(*kernel_task, &addr, &size, &depth, (vm_region_info_t)&info, &info_count)) == KERN_SUCCESS) { 1126 | if (size > 1024 * 1024 * 1024) { 1127 | #ifdef __LP64__ 1128 | vm_address_t where = 16 * 0x200000; 1129 | #else 1130 | vm_address_t where = 1 * 0x200000; 1131 | #endif 1132 | for (where += addr; where >= addr; where -= 0x200000) { 1133 | vm_size_t sz; 1134 | uint8_t head[2048]; 1135 | sz = sizeof(head); 1136 | rv = vm_read_overwrite(*kernel_task, where + KDELTA, sizeof(head), (vm_address_t)head, &sz); 1137 | if (rv == 0 && sz == sizeof(head) && (*(uint32_t *)head & ~1) == 0xfeedface 1138 | && boyermoore_horspool_memmem(head, sizeof(head), (const uint8_t *)"__KLD", 5)) { 1139 | return where + KDELTA; 1140 | } 1141 | #ifdef __LP64__ 1142 | sz = sizeof(head); 1143 | rv = vm_read_overwrite(*kernel_task, where + KDELTA / 2, sizeof(head), (vm_address_t)head, &sz); 1144 | if (rv == 0 && sz == sizeof(head) && (*(uint32_t *)head & ~1) == 0xfeedface 1145 | && boyermoore_horspool_memmem(head, sizeof(head), (const uint8_t *)"__KLD", 5)) { 1146 | return where + KDELTA / 2; 1147 | } 1148 | #endif 1149 | } 1150 | break; 1151 | } 1152 | addr += size; 1153 | } 1154 | 1155 | return -1; 1156 | } 1157 | 1158 | uint8_t *kernel = NULL; 1159 | #ifdef __LP64__ 1160 | size_t kernel_size = 0x2600000; 1161 | #else 1162 | size_t kernel_size = 0x1200000; 1163 | #endif 1164 | task_t kernel_task = TASK_NULL; 1165 | uint64_t kernel_base = 0; 1166 | struct kdb *kernel_db; 1167 | 1168 | static vm_size_t 1169 | kread(vm_address_t where, uint8_t *p, vm_size_t size) 1170 | { 1171 | int rv; 1172 | size_t offset = 0; 1173 | while (offset < size) { 1174 | vm_size_t sz, chunk = 2048; 1175 | if (chunk > size - offset) { 1176 | chunk = size - offset; 1177 | } 1178 | rv = vm_read_overwrite(kernel_task, where + offset, chunk, (vm_address_t)(p + offset), &sz); 1179 | if (rv || sz == 0) { 1180 | fprintf(stderr, "[e] error reading kernel @0x%zx\n", offset + where); 1181 | break; 1182 | } 1183 | offset += sz; 1184 | } 1185 | return offset; 1186 | } 1187 | 1188 | static vm_size_t 1189 | kwrite(vm_address_t where, const uint8_t *p, vm_size_t size) 1190 | { 1191 | int rv; 1192 | size_t offset = 0; 1193 | while (offset < size) { 1194 | vm_size_t chunk = 2048; 1195 | if (chunk > size - offset) { 1196 | chunk = size - offset; 1197 | } 1198 | rv = vm_write(kernel_task, where + offset, (vm_offset_t)p + offset, chunk); 1199 | if (rv) { 1200 | fprintf(stderr, "[e] error writing kernel @0x%zx\n", offset + where); 1201 | break; 1202 | } 1203 | offset += chunk; 1204 | } 1205 | return offset; 1206 | } 1207 | 1208 | static vm_size_t 1209 | kwrite_uint64(vm_address_t where, uint64_t value) 1210 | { 1211 | return kwrite(where, (uint8_t *)&value, sizeof(value)); 1212 | } 1213 | 1214 | static vm_size_t 1215 | kwrite_uint32(vm_address_t where, uint32_t value) 1216 | { 1217 | return kwrite(where, (uint8_t *)&value, sizeof(value)); 1218 | } 1219 | 1220 | static vm_size_t 1221 | kwrite_undo(vm_address_t where, vm_size_t size) 1222 | { 1223 | return kwrite(where, kernel + where - kernel_base, size); 1224 | } 1225 | 1226 | static vm_address_t 1227 | kalloc(vm_size_t size) 1228 | { 1229 | int rv; 1230 | vm_address_t addr = 0; 1231 | rv = vm_allocate(kernel_task, &addr, round_page(size), 1); 1232 | assert(rv == KERN_SUCCESS); 1233 | return addr; 1234 | } 1235 | 1236 | static void 1237 | kfree(vm_address_t addr, vm_size_t size) 1238 | { 1239 | vm_deallocate(kernel_task, addr, round_page(size)); 1240 | } 1241 | 1242 | static int 1243 | init_kernel(const char *filename) 1244 | { 1245 | (void)filename; 1246 | 1247 | kernel_base = get_kernel_base(&kernel_task); 1248 | if (kernel_base == (vm_address_t)-1) { 1249 | return -1; 1250 | } 1251 | 1252 | kernel = malloc(kernel_size); 1253 | if (!kernel) { 1254 | return -1; 1255 | } 1256 | 1257 | kernel_size = kread(kernel_base, kernel, kernel_size); 1258 | return 0; 1259 | } 1260 | 1261 | static void 1262 | term_kernel(void) 1263 | { 1264 | free(kernel); 1265 | } 1266 | 1267 | #else /* !__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ */ 1268 | 1269 | uint8_t *kernel = MAP_FAILED; 1270 | size_t kernel_size = 0; 1271 | int kernel_fd = -1; 1272 | uint64_t kernel_base = 0; 1273 | struct kdb *kernel_db; 1274 | 1275 | static size_t 1276 | kread(addr_t where, uint8_t *p, size_t size) 1277 | { 1278 | (void)(where && p); 1279 | return size; 1280 | } 1281 | 1282 | static size_t 1283 | kwrite(addr_t where, const uint8_t *p, size_t size) 1284 | { 1285 | (void)(where && p); 1286 | return size; 1287 | } 1288 | 1289 | static size_t 1290 | kwrite_uint64(addr_t where, uint64_t value) 1291 | { 1292 | return kwrite(where, (uint8_t *)&value, sizeof(value)); 1293 | } 1294 | 1295 | static size_t 1296 | kwrite_uint32(addr_t where, uint32_t value) 1297 | { 1298 | return kwrite(where, (uint8_t *)&value, sizeof(value)); 1299 | } 1300 | 1301 | static size_t 1302 | kwrite_undo(addr_t where, size_t size) 1303 | { 1304 | return kwrite(where, kernel + where - kernel_base, size); 1305 | } 1306 | 1307 | static uintptr_t 1308 | kalloc(size_t size) 1309 | { 1310 | return (uintptr_t)malloc(size); 1311 | } 1312 | 1313 | static void 1314 | kfree(uintptr_t addr, size_t size) 1315 | { 1316 | (void)size; 1317 | free((void *)addr); 1318 | } 1319 | 1320 | static int 1321 | init_kernel(const char *filename) 1322 | { 1323 | kernel_fd = open(filename, O_RDONLY); 1324 | if (kernel_fd < 0) { 1325 | return -1; 1326 | } 1327 | 1328 | kernel_size = lseek(kernel_fd, 0, SEEK_END); 1329 | 1330 | kernel = mmap(NULL, kernel_size, PROT_READ, MAP_PRIVATE, kernel_fd, 0); 1331 | if (kernel != MAP_FAILED) { 1332 | if ((*(uint32_t *)kernel & ~1) == 0xfeedface) { 1333 | kernel_base = get_base(kernel, kernel_size); 1334 | return 0; 1335 | } 1336 | munmap(kernel, kernel_size); 1337 | kernel = MAP_FAILED; 1338 | } 1339 | 1340 | close(kernel_fd); 1341 | kernel_fd = -1; 1342 | return -1; 1343 | } 1344 | 1345 | static void 1346 | term_kernel(void) 1347 | { 1348 | munmap(kernel, kernel_size); 1349 | close(kernel_fd); 1350 | } 1351 | #endif /* !__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ */ 1352 | 1353 | /* kernel kext ***************************************************************/ 1354 | 1355 | static addr_t 1356 | off_PRELINK_TEXT(void) 1357 | { 1358 | unsigned i; 1359 | const uint8_t *p = kernel; 1360 | addr_t offset = 0; 1361 | const struct mach_header *hdr = (struct mach_header *)p; 1362 | const uint8_t *q = p + sizeof(struct mach_header); 1363 | 1364 | if (kernel_size < 0x1000) { 1365 | return 0; 1366 | } 1367 | 1368 | if (IS64(p)) { 1369 | q += 4; 1370 | } 1371 | 1372 | for (i = 0; i < hdr->ncmds; i++) { 1373 | const struct load_command *cmd = (struct load_command *)q; 1374 | if (cmd->cmd == LC_SEGMENT) { 1375 | const struct segment_command *seg = (struct segment_command *)q; 1376 | if (!strcmp(seg->segname, "__PRELINK_TEXT")) { 1377 | offset = seg->fileoff; 1378 | break; 1379 | } 1380 | } 1381 | if (cmd->cmd == LC_SEGMENT_64) { 1382 | const struct segment_command_64 *seg = (struct segment_command_64 *)q; 1383 | if (!strcmp(seg->segname, "__PRELINK_TEXT")) { 1384 | offset = seg->fileoff; 1385 | break; 1386 | } 1387 | } 1388 | q = q + cmd->cmdsize; 1389 | } 1390 | 1391 | return offset; 1392 | } 1393 | 1394 | static addr_t 1395 | find_kext(const char *kextid, addr_t *next) 1396 | { 1397 | addr_t koff, kbase, ksize; 1398 | addr_t offset = off_PRELINK_TEXT(); 1399 | size_t len = strlen(kextid) + 1; /* XXX with zero, too */ 1400 | const uint8_t *p; 1401 | 1402 | *next = 0; 1403 | 1404 | if (offset == 0) { 1405 | return 0; 1406 | } 1407 | 1408 | while (1) { 1409 | p = boyermoore_horspool_memmem(kernel + offset + 1, kernel_size - offset - 1, (const void *)kextid, len); 1410 | if (!p) { 1411 | return 0; 1412 | } 1413 | offset = p - kernel; 1414 | 1415 | if (((int *)p)[-1] == 0 || !isdigit(p[64])) { 1416 | continue; 1417 | } 1418 | 1419 | if (IS64(kernel)) { 1420 | kbase = *(uint64_t *)(p - 0x10 + 0x9c); 1421 | ksize = *(uint64_t *)(p - 0x10 + 0xa4); 1422 | } else { 1423 | kbase = *(uint32_t *)(p - 0xc + 0x94); 1424 | ksize = *(uint32_t *)(p - 0xc + 0x98); 1425 | } 1426 | if (((kbase | ksize) & 0xFFF) || kbase < kernel_base || ksize > 0x1000000) { 1427 | continue; 1428 | } 1429 | 1430 | for (koff = offset & ~0xFFF; koff && offset - koff < 0x100000; koff -= 0x1000) { 1431 | if (*(uint32_t *)(kernel + koff) == *(uint32_t *)kernel) { 1432 | addr_t other = (offset & ~0xFFF) + 0x1000; 1433 | while (other < kernel_size && other < offset + 0x100000) { 1434 | uint32_t magic = *(uint32_t *)(kernel + other); 1435 | if (magic == *(uint32_t *)kernel || magic == 0x6369643C) { 1436 | *next = other; 1437 | return koff; 1438 | } 1439 | other += 0x1000; 1440 | } 1441 | break; 1442 | } 1443 | } 1444 | } 1445 | 1446 | return 0; 1447 | } 1448 | 1449 | static int 1450 | load_dep(const char *kextid, struct dependency *dep) 1451 | { 1452 | uint8_t *buf; 1453 | size_t size; 1454 | addr_t kext, next; 1455 | 1456 | kext = find_kext(kextid, &next); 1457 | if (!kext) { 1458 | return -1; 1459 | } 1460 | size = next - kext; 1461 | 1462 | printf("%s: 0x%llx -> 0x%llx\n", kextid, kext, next); 1463 | 1464 | buf = malloc(size); 1465 | if (!buf) { 1466 | return -1; 1467 | } 1468 | 1469 | memcpy(buf, kernel + kext, size); 1470 | 1471 | dep->buf = buf; 1472 | dep->size = size; 1473 | dep->base = get_base(buf, size); 1474 | dep->name = kextid; 1475 | return 0; 1476 | } 1477 | 1478 | static void 1479 | free_dep(struct dependency *dep) 1480 | { 1481 | free(dep->buf); 1482 | } 1483 | 1484 | /* fat ***********************************************************************/ 1485 | 1486 | struct macho { 1487 | int fd; 1488 | size_t off, end; 1489 | }; 1490 | 1491 | static int 1492 | mclose(struct macho *macho) 1493 | { 1494 | int rv = -1; 1495 | if (macho) { 1496 | rv = close(macho->fd); 1497 | free(macho); 1498 | } 1499 | return rv; 1500 | } 1501 | 1502 | static ssize_t 1503 | mread(struct macho *macho, void *buf, size_t count, off_t offset) 1504 | { 1505 | size_t len; 1506 | size_t off; 1507 | if (!macho) { 1508 | return -1; 1509 | } 1510 | off = offset + macho->off; 1511 | if (off < macho->off) { 1512 | return -1; 1513 | } 1514 | if (off >= macho->end) { 1515 | return 0; 1516 | } 1517 | len = macho->end - off; 1518 | if (len > count) { 1519 | len = count; 1520 | } 1521 | return pread(macho->fd, buf, len, off); 1522 | } 1523 | 1524 | static struct macho * 1525 | mopen(const char *filename, int mode, const struct mach_header *target) 1526 | { 1527 | int rv; 1528 | int fd; 1529 | size_t size; 1530 | unsigned i, n; 1531 | struct stat st; 1532 | struct fat_header fat_buf; 1533 | struct mach_header hdr; 1534 | struct macho *macho; 1535 | 1536 | macho = malloc(sizeof(struct macho)); 1537 | if (macho == NULL) { 1538 | return NULL; 1539 | } 1540 | 1541 | fd = open(filename, mode); 1542 | if (fd < 0) { 1543 | free(macho); 1544 | return NULL; 1545 | } 1546 | macho->fd = fd; 1547 | 1548 | rv = fstat(fd, &st); 1549 | if (rv) { 1550 | mclose(macho); 1551 | return NULL; 1552 | } 1553 | 1554 | size = read(fd, &fat_buf, sizeof(fat_buf)); 1555 | if (size != sizeof(fat_buf)) { 1556 | mclose(macho); 1557 | return NULL; 1558 | } 1559 | 1560 | if (fat_buf.magic != FAT_CIGAM) { 1561 | if (fat_buf.magic == target->magic && (cpu_type_t)fat_buf.nfat_arch == target->cputype) { 1562 | size = read(fd, &n, sizeof(n)); 1563 | if (size == sizeof(n) && (cpu_subtype_t)n <= target->cpusubtype) { 1564 | macho->off = 0; 1565 | macho->end = st.st_size; 1566 | return macho; 1567 | } 1568 | } 1569 | mclose(macho); 1570 | return NULL; 1571 | } 1572 | 1573 | n = __builtin_bswap32(fat_buf.nfat_arch); 1574 | for (i = 0; i < n; i++) { 1575 | size_t off, end; 1576 | struct fat_arch arch_buf; 1577 | size = pread(fd, &arch_buf, sizeof(arch_buf), sizeof(fat_buf) + i * sizeof(arch_buf)); 1578 | if (size != sizeof(arch_buf)) { 1579 | break; 1580 | } 1581 | off = __builtin_bswap32(arch_buf.offset); 1582 | end = off + __builtin_bswap32(arch_buf.size); 1583 | if (end < off || (off_t)end > st.st_size) { 1584 | break; 1585 | } 1586 | macho->off = off; 1587 | macho->end = end; 1588 | size = mread(macho, &hdr, sizeof(hdr), 0); 1589 | if (size != sizeof(hdr)) { 1590 | break; 1591 | } 1592 | if (hdr.magic == target->magic && hdr.cputype == target->cputype && hdr.cpusubtype <= target->cpusubtype) { 1593 | return macho; 1594 | } 1595 | } 1596 | 1597 | mclose(macho); 1598 | return NULL; 1599 | } 1600 | 1601 | /* the real mccoy ************************************************************/ 1602 | 1603 | static addr_t solver_code(uint8_t *p, size_t size, addr_t base, const char *symbol); 1604 | static addr_t solver_kern(addr_t vm_kernel_slide, const char *symbol); 1605 | static addr_t solver_deps(uint8_t *p, size_t size, addr_t base, addr_t vm_kernel_slide, const char *symbol, struct dependency *deps, int ndep); 1606 | 1607 | static addr_t 1608 | solver(uint8_t *p, size_t size, addr_t base, addr_t vm_kernel_slide, const char *symbol, struct dependency *deps, int ndep) 1609 | { 1610 | addr_t val; 1611 | 1612 | if (p) { 1613 | val = solver_code(p, size, base, symbol); 1614 | if (val) { 1615 | return val; 1616 | } 1617 | } 1618 | 1619 | if (deps && ndep > 0) { 1620 | val = solver_deps(p, size, base, vm_kernel_slide, symbol, deps, ndep); 1621 | if (val) { 1622 | return val; 1623 | } 1624 | } 1625 | 1626 | val = solver_kern(vm_kernel_slide, symbol); 1627 | if (val) { 1628 | return val; 1629 | } 1630 | 1631 | val = kdb_find(kernel_db, symbol); 1632 | if (!val) { 1633 | printf("UNDEFINED: %s\n", symbol); 1634 | } 1635 | assert(val); 1636 | return val + vm_kernel_slide; 1637 | } 1638 | 1639 | static uint8_t * 1640 | load_kext(const char *filename, addr_t vm_kernel_slide, addr_t *dest, size_t *sz, struct dependency *deps, int ndep) 1641 | { 1642 | int rv; 1643 | struct macho *macho; 1644 | uint8_t *buf; 1645 | uint8_t p[0x1000]; 1646 | size_t size, offset; 1647 | struct symtab_command *ksym = NULL; 1648 | struct dysymtab_command *kdys = NULL; 1649 | size_t linkdelta = 0; 1650 | int is64 = 0; 1651 | unsigned i, j, k; 1652 | const struct mach_header *hdr; 1653 | const uint8_t *q; 1654 | size_t hdrsz; 1655 | 1656 | *sz = 0; 1657 | *dest = 0; 1658 | 1659 | /* since I'm not going to rewrite a full dyld-like code, I'm gonna make some assumptions: 1660 | * segments (including LINKEDIT) come in order 1661 | * sections are nicely laid down inside segments 1662 | * after segments come the other commands: SYMTAB, DYSYMTAB 1663 | * symbols, relocations are inside LINKEDIT 1664 | */ 1665 | 1666 | macho = mopen(filename, O_RDONLY, (struct mach_header *)kernel); 1667 | assert(macho); 1668 | 1669 | size = mread(macho, p, sizeof(p), 0); 1670 | assert(size == sizeof(p)); 1671 | 1672 | /* parse header, calculate total in-memory size */ 1673 | 1674 | hdr = (struct mach_header *)p; 1675 | q = p + sizeof(struct mach_header); 1676 | 1677 | if (IS64(p)) { 1678 | is64 = 4; 1679 | } 1680 | q += is64; 1681 | 1682 | hdrsz = q - p + hdr->sizeofcmds; 1683 | assert(hdrsz <= sizeof(p)); 1684 | 1685 | size = 0; 1686 | for (i = 0; i < hdr->ncmds; i++) { 1687 | const struct load_command *cmd = (struct load_command *)q; 1688 | if (cmd->cmd == LC_SEGMENT) { 1689 | struct segment_command *seg = (struct segment_command *)q; 1690 | seg->vmsize = round_page(seg->vmsize); 1691 | size += seg->vmsize; 1692 | } 1693 | if (cmd->cmd == LC_SEGMENT_64) { 1694 | struct segment_command_64 *seg = (struct segment_command_64 *)q; 1695 | seg->vmsize = round_page(seg->vmsize); 1696 | size += seg->vmsize; 1697 | } 1698 | q = q + cmd->cmdsize; 1699 | } 1700 | 1701 | #ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ 1702 | *dest = kalloc(size); 1703 | #endif 1704 | rv = posix_memalign((void **)&buf, 0x1000, size); 1705 | assert(rv == 0); 1706 | memset(buf, 0, size); /* XXX take care of S_ZEROFILL */ 1707 | #ifndef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ 1708 | if (is64) { 1709 | *dest = 0xdead000000000000; 1710 | } else { 1711 | *dest = 0xdead0000; 1712 | } 1713 | #endif 1714 | 1715 | /* read segments in, calculate linkedit delta */ 1716 | 1717 | q = p + sizeof(struct mach_header) + is64; 1718 | 1719 | offset = 0; 1720 | for (i = 0; i < hdr->ncmds; i++) { 1721 | const struct load_command *cmd = (struct load_command *)q; 1722 | if (cmd->cmd == LC_SEGMENT) { 1723 | struct segment_command *seg = (struct segment_command *)q; 1724 | struct section *sec = (struct section *)(seg + 1); 1725 | if (!strcmp(seg->segname, "__LINKEDIT")) { 1726 | linkdelta = offset - seg->fileoff; 1727 | } 1728 | if (seg->filesize > seg->vmsize) { 1729 | seg->filesize = seg->vmsize; 1730 | } 1731 | seg->fileoff = offset; 1732 | seg->vmaddr += *dest - vm_kernel_slide; 1733 | for (j = 0; j < seg->nsects; j++) { 1734 | sec[j].addr += *dest - vm_kernel_slide; 1735 | assert(sec->reloff == 0 && sec->nreloc == 0); 1736 | } 1737 | size = mread(macho, buf + offset, seg->filesize, seg->fileoff); 1738 | assert(size == seg->filesize); 1739 | offset += seg->vmsize; 1740 | } 1741 | if (cmd->cmd == LC_SEGMENT_64) { 1742 | struct segment_command_64 *seg = (struct segment_command_64 *)q; 1743 | struct section_64 *sec = (struct section_64 *)(seg + 1); 1744 | if (!strcmp(seg->segname, "__LINKEDIT")) { 1745 | linkdelta = offset - seg->fileoff; 1746 | } 1747 | if (seg->filesize > seg->vmsize) { 1748 | seg->filesize = seg->vmsize; 1749 | } 1750 | seg->fileoff = offset; 1751 | seg->vmaddr += *dest - vm_kernel_slide; 1752 | for (j = 0; j < seg->nsects; j++) { 1753 | sec[j].addr += *dest - vm_kernel_slide; 1754 | assert(sec->reloff == 0 && sec->nreloc == 0); 1755 | } 1756 | size = mread(macho, buf + offset, seg->filesize, seg->fileoff); 1757 | assert(size == seg->filesize); 1758 | offset += seg->vmsize; 1759 | } 1760 | q = q + cmd->cmdsize; 1761 | } 1762 | 1763 | mclose(macho); 1764 | 1765 | /* fix header */ 1766 | 1767 | memcpy(buf, p, hdrsz); 1768 | 1769 | /* solve imports, spot relocs */ 1770 | 1771 | q = buf + sizeof(struct mach_header) + is64; 1772 | 1773 | #define SLIDE(x) do { if (x) x += linkdelta; } while (0) 1774 | for (i = 0; i < hdr->ncmds; i++) { 1775 | const struct load_command *cmd = (struct load_command *)q; 1776 | if (cmd->cmd == LC_SYMTAB) { 1777 | struct symtab_command *sym = (struct symtab_command *)q; 1778 | ksym = sym; 1779 | SLIDE(sym->symoff); 1780 | SLIDE(sym->stroff); 1781 | if (is64) { 1782 | struct nlist_64 *s = (struct nlist_64 *)(buf + sym->symoff); 1783 | for (k = 0; k < sym->nsyms; k++) { 1784 | if (s[k].n_type & N_STAB) { 1785 | continue; 1786 | } 1787 | if ((s[k].n_type & N_EXT) && GET_LIBRARY_ORDINAL(s[k].n_desc) == DYNAMIC_LOOKUP_ORDINAL && s[k].n_value == 0) { 1788 | s[k].n_value = solver(buf, offset, *dest, vm_kernel_slide, (char *)buf + sym->stroff + s[k].n_un.n_strx, deps, ndep); 1789 | continue; 1790 | } 1791 | s[k].n_value += *dest - vm_kernel_slide; 1792 | } 1793 | } else { 1794 | struct nlist *s = (struct nlist *)(buf + sym->symoff); 1795 | for (k = 0; k < sym->nsyms; k++) { 1796 | if (s[k].n_type & N_STAB) { 1797 | continue; 1798 | } 1799 | if ((s[k].n_type & N_EXT) && GET_LIBRARY_ORDINAL(s[k].n_desc) == DYNAMIC_LOOKUP_ORDINAL && s[k].n_value == 0) { 1800 | s[k].n_value = solver(buf, offset, *dest, vm_kernel_slide, (char *)buf + sym->stroff + s[k].n_un.n_strx, deps, ndep); 1801 | continue; 1802 | } 1803 | s[k].n_value += *dest - vm_kernel_slide; 1804 | } 1805 | } 1806 | } 1807 | if (cmd->cmd == LC_DYSYMTAB) { 1808 | struct dysymtab_command *dys = (struct dysymtab_command *)q; 1809 | kdys = dys; 1810 | SLIDE(dys->tocoff); 1811 | SLIDE(dys->modtaboff); 1812 | SLIDE(dys->extrefsymoff); 1813 | SLIDE(dys->indirectsymoff); 1814 | SLIDE(dys->extreloff); 1815 | SLIDE(dys->locreloff); 1816 | } 1817 | q = q + cmd->cmdsize; 1818 | } 1819 | #undef SLIDE 1820 | 1821 | /* apply relocs */ 1822 | 1823 | if (kdys && kdys->locreloff) { 1824 | const struct relocation_info *r = (struct relocation_info *)(buf + kdys->locreloff); 1825 | if (is64) { 1826 | for (k = 0; k < kdys->nlocrel; k++, r++) { 1827 | if ( 1828 | #if 1 /* XXX horrible hack to reduce size */ 1829 | (((uint32_t *)r)[1] >> 24) != 6 1830 | #else 1831 | r->r_pcrel || r->r_length != 3 || r->r_extern || r->r_type > GENERIC_RELOC_VANILLA 1832 | #endif 1833 | ) { 1834 | assert(0); 1835 | } 1836 | if (r->r_address & R_SCATTERED) { 1837 | assert(0); 1838 | } 1839 | *(uint64_t *)(buf + r->r_address) += *dest; 1840 | } 1841 | } else { 1842 | for (k = 0; k < kdys->nlocrel; k++, r++) { 1843 | if ( 1844 | #if 1 /* XXX horrible hack to reduce size */ 1845 | (((uint32_t *)r)[1] >> 24) != 4 1846 | #else 1847 | r->r_pcrel || r->r_length != 2 || r->r_extern || r->r_type > GENERIC_RELOC_VANILLA 1848 | #endif 1849 | ) { 1850 | assert(0); 1851 | } 1852 | if (r->r_address & R_SCATTERED) { 1853 | assert(0); 1854 | } 1855 | *(uint32_t *)(buf + r->r_address) += *dest; 1856 | } 1857 | } 1858 | } 1859 | 1860 | /* apply externs */ 1861 | 1862 | if (kdys && kdys->extreloff && ksym->symoff) { 1863 | const struct relocation_info *r = (struct relocation_info *)(buf + kdys->extreloff); 1864 | if (is64) { 1865 | const struct nlist_64 *s = (struct nlist_64 *)(buf + ksym->symoff); 1866 | for (k = 0; k < kdys->nextrel; k++, r++) { 1867 | assert(!r->r_pcrel && r->r_length == 3 && r->r_extern && r->r_type == GENERIC_RELOC_VANILLA); 1868 | *(uint64_t *)(buf + r->r_address) = s[r->r_symbolnum].n_value; 1869 | } 1870 | } else { 1871 | const struct nlist *s = (struct nlist *)(buf + ksym->symoff); 1872 | for (k = 0; k < kdys->nextrel; k++, r++) { 1873 | assert(!r->r_pcrel && r->r_length == 2 && r->r_extern && r->r_type == GENERIC_RELOC_VANILLA); 1874 | *(uint32_t *)(buf + r->r_address) = s[r->r_symbolnum].n_value; 1875 | } 1876 | } 1877 | } 1878 | 1879 | if (kdys) { 1880 | kdys->nlocrel = 0; /* XXX nuke relocs */ 1881 | kdys->nextrel = 0; /* XXX nuke exts */ 1882 | } 1883 | 1884 | *sz = offset; 1885 | return buf; 1886 | } 1887 | 1888 | static char * 1889 | populate_xml(char *xml, size_t *sz, addr_t obase, size_t osize, addr_t kmod_info, char *id) 1890 | { 1891 | char *p = strchr(id, '.'); 1892 | if (p) { 1893 | p++; 1894 | } else { 1895 | p = id; 1896 | } 1897 | 1898 | xml = xml_setval("CFBundleIdentifier", id, xml, sz, 0); 1899 | assert(xml); 1900 | xml = xml_sethex("_PrelinkExecutableSize", osize, xml, sz, 2); 1901 | assert(xml); 1902 | xml = xml_sethex("_PrelinkExecutableLoadAddr", obase, xml, sz, 2); 1903 | assert(xml); 1904 | xml = xml_sethex("_PrelinkExecutableSourceAddr", obase, xml, sz, 2); 1905 | assert(xml); 1906 | xml = xml_sethex("_PrelinkKmodInfo", obase + kmod_info, xml, sz, 2); 1907 | assert(xml); 1908 | if (!strstr(xml, "_PrelinkBundlePath")) { 1909 | char tmp[1024]; 1910 | snprintf(tmp, sizeof(tmp), "/System/Library/Extensions/%s.kext", p); 1911 | xml = xml_setval("_PrelinkBundlePath", tmp, xml, sz, 0); 1912 | assert(xml); 1913 | } 1914 | if (!strstr(xml, "_PrelinkExecutableRelativePath")) { 1915 | xml = xml_setval("_PrelinkExecutableRelativePath", p, xml, sz, 0); 1916 | assert(xml); 1917 | } 1918 | return xml; 1919 | } 1920 | 1921 | static void 1922 | dump_file(const char *filename, int n, void *buf, size_t size) 1923 | { 1924 | FILE *f; 1925 | char tmp[256]; 1926 | if (n >= 0) { 1927 | snprintf(tmp, sizeof(tmp), "%s%d", filename, n); 1928 | filename = tmp; 1929 | } 1930 | f = fopen(filename, "wb"); 1931 | fwrite(buf, 1, size, f); 1932 | fclose(f); 1933 | } 1934 | 1935 | static const uint8_t ret0_arm32[] = { 1936 | 0x00, 0x20, 0x70, 0x47 1937 | }; 1938 | 1939 | static const uint8_t ret0_arm64[] = { 1940 | 0x00, 0x00, 0x80, 0xD2, 0xC0, 0x03, 0x5F, 0xD6 1941 | }; 1942 | 1943 | static const uint8_t bcopy_armv7[] = { 1944 | 0x4F, 0xF0, 0x01, 0x09, 0x81, 0x42, 0x88, 0xBF, 0x4F, 0xF0, 0xFF, 0x39, 0x00, 0x2A, 0x08, 0xBF, 1945 | 0x70, 0x47, 0x4F, 0xF0, 0x00, 0x0C, 0x81, 0x42, 0x88, 0xBF, 0xA2, 0xF1, 0x01, 0x0C, 0x10, 0xF8, 1946 | 0x0C, 0x30, 0x01, 0x3A, 0x01, 0xF8, 0x0C, 0x30, 0xCC, 0x44, 0xF8, 0xD1, 0x70, 0x47 1947 | }; 1948 | 1949 | static const uint8_t bcopy_arm64[] = { 1950 | 0xE9, 0x03, 0x40, 0xB2, 0x3F, 0x00, 0x00, 0xEB, 0xE8, 0x7F, 0x60, 0xB2, 0x08, 0x81, 0x02, 0x8B, 1951 | 0x08, 0xFD, 0x60, 0x93, 0x0A, 0x00, 0x80, 0x92, 0xE8, 0x93, 0x88, 0x9A, 0x29, 0x91, 0x8A, 0x9A, 1952 | 0xC2, 0x00, 0x00, 0xB4, 0x42, 0x04, 0x00, 0xD1, 0x0A, 0x68, 0x68, 0x38, 0x2A, 0x68, 0x28, 0x38, 1953 | 0x08, 0x01, 0x09, 0x8B, 0x82, 0xFF, 0xFF, 0xB5, 0xC0, 0x03, 0x5F, 0xD6 1954 | }; 1955 | 1956 | // XXX I should call OSSafeRelease(infoDict) aka infoDict->release() 1957 | //#define ALL_MATCH 1 /* start matching for all kexts, not just this one */ 1958 | //#define MATCH_NOW 1 /* if ALL_MATCH, start matching NOW */ 1959 | static uint8_t stuff32[] = { 1960 | 0xbc, 0xb5, /* push {r2-r5, r7, lr} */ 1961 | 0x04, 0xaf, /* add r7, sp, #16 */ 1962 | 0x0c, 0x4d, /* ldr r5, [pc, #48] ; rv */ 1963 | 0x0d, 0x48, /* ldr r0, [pc, #52] ; xml */ 1964 | 0x00, 0x21, /* movs r1, #0 */ 1965 | 0x0d, 0x4c, /* ldr r4, [pc, #52] ; OSUnserializeXML */ 1966 | 0xa0, 0x47, /* blx r4 */ 1967 | 0x88, 0xb1, /* cbz r0, .Ldone */ 1968 | 0x0c, 0x4c, /* ldr r4, [pc, #48] ; OSKext::withPrelinkedInfoDict */ 1969 | 0xa0, 0x47, /* blx r4 */ 1970 | 0x01, 0x35, /* adds r5, #1 */ 1971 | 0x68, 0xb1, /* cbz r0, .Ldone */ 1972 | 0x0b, 0x48, /* ldr r0, [pc, #44] ; ident */ 1973 | 0x00, 0x21, /* movs r1, #0 */ 1974 | 0x00, 0x22, /* movs r2, #0 */ 1975 | #ifdef ALL_MATCH 1976 | 0x02, 0x23, /* movs r3, #2 */ 1977 | #else 1978 | 0x00, 0x23, /* movs r3, #0 */ 1979 | #endif 1980 | 0x00, 0x93, /* str r3, [sp, #0] */ 1981 | 0x01, 0x91, /* str r1, [sp, #4] */ 1982 | 0x00, 0x23, /* movs r3, #0 */ 1983 | 0x09, 0x4c, /* ldr r4, [pc, #36] ; OSKext::loadKextWithIdentifier */ 1984 | 0xa0, 0x47, /* blx r4 */ 1985 | 0x05, 0x46, /* mov r5, r0 */ 1986 | 0x10, 0xb9, /* cbnz r0, .Ldone */ 1987 | #ifdef MATCH_NOW 1988 | 0x01, 0x20, /* movs r0, #1 */ 1989 | #else 1990 | 0x00, 0x20, /* movs r0, #0 */ 1991 | #endif 1992 | 0x07, 0x4c, /* ldr r4, [pc, #28] ; OSKext::sendAllKextPersonalitiesToCatalog */ 1993 | #ifdef ALL_MATCH 1994 | 0xa0, 0x47, /* blx r4 */ 1995 | #else 1996 | 0x00, 0xbf, /* nop */ 1997 | #endif 1998 | 0x28, 0x46, /* mov r0, r5 */ 1999 | 0xbc, 0xbd, /* pop {r2-r5, r7, pc} */ 2000 | /* 0x38 */ 2001 | 1, 1, 1, 1, /* rv */ 2002 | 2, 2, 2, 2, /* xml */ 2003 | 3, 3, 3, 3, /* OSUnserializeXML */ 2004 | 4, 4, 4, 4, /* OSKext::withPrelinkedInfoDict */ 2005 | 5, 5, 5, 5, /* ident */ 2006 | 6, 6, 6, 6, /* OSKext::loadKextWithIdentifier */ 2007 | 7, 7, 7, 7, /* OSKext::sendAllKextPersonalitiesToCatalog */ 2008 | }; 2009 | 2010 | static uint32_t stuff64[] = { 2011 | 0xa9bf7bfd, /* stp x29, x30, [sp,#-16]! */ 2012 | 0x910003fd, /* mov x29, sp */ 2013 | 0xa9bf4ff4, /* stp x20, x19, [sp,#-16]! */ 2014 | 0x58000374, /* ldr x20, 78 */ 2015 | 0x58000380, /* ldr x0, 80 */ 2016 | 0xd2800001, /* mov x1, #0x0 */ 2017 | 0x58000388, /* ldr x8, 88 */ 2018 | 0xd63f0100, /* blr x8 */ 2019 | 0xb4000240, /* cbz x0, .Ldone */ 2020 | 0x58000368, /* ldr x8, 90 */ 2021 | 0xd63f0100, /* blr x8 */ 2022 | 0x91000694, /* add x20, x20, #0x1 */ 2023 | 0xb40001c0, /* cbz x0, .Ldone */ 2024 | 0x58000320, /* ldr x0, 98 */ 2025 | 0xd2800001, /* mov x1, #0x0 */ 2026 | 0xd2800002, /* mov x2, #0x0 */ 2027 | 0xd2800003, /* mov x3, #0x0 */ 2028 | #ifdef ALL_MATCH 2029 | 0xd2800044, /* mov x4, #0x2 */ 2030 | #else 2031 | 0xd2800004, /* mov x4, #0x0 */ 2032 | #endif 2033 | 0xd2800005, /* mov x5, #0x0 */ 2034 | 0x580002a8, /* ldr x8, a0 */ 2035 | 0xd63f0100, /* blr x8 */ 2036 | 0xaa0003f4, /* mov x20, x0 */ 2037 | 0xb5000080, /* cbnz x0, .Ldone */ 2038 | #ifdef MATCH_NOW 2039 | 0xd2800020, /* mov x0, #0x1 */ 2040 | #else 2041 | 0xd2800000, /* mov x0, #0x0 */ 2042 | #endif 2043 | 0x58000248, /* ldr x8, a8 */ 2044 | #ifdef ALL_MATCH 2045 | 0xd63f0100, /* blr x8 */ 2046 | #else 2047 | 0xd503201f, /* nop */ 2048 | #endif 2049 | 0xaa1403e0, /* mov x0, x20 */ 2050 | 0xa8c14ff4, /* ldp x20, x19, [sp],#16 */ 2051 | 0xa8c17bfd, /* ldp x29, x30, [sp],#16 */ 2052 | 0xd65f03c0, /* ret */ 2053 | /* 0x78 */ 2054 | 1, 1, /* rv */ 2055 | 2, 2, /* xml */ 2056 | 3, 3, /* OSUnserializeXML */ 2057 | 4, 4, /* OSKext_withPrelinkedInfoDict */ 2058 | 5, 5, /* ident */ 2059 | 6, 6, /* OSKext_loadKextWithIdentifier */ 2060 | 7, 7, /* OSKext_sendAllKextPersonalitiesToCatalog */ 2061 | }; 2062 | 2063 | #ifdef MY_LOGGER 2064 | static uint8_t logger32[] = { 2065 | /*00000000 <_hook1>*/ 2066 | 0xc0, 0x46, /* nop */ 2067 | 0xc0, 0x46, /* nop */ 2068 | 0xc0, 0x46, /* nop */ 2069 | 0xc0, 0x46, /* nop */ 2070 | 0xc0, 0x46, /* nop */ 2071 | 0xc0, 0x46, /* nop */ 2072 | 0x00, 0xb5, /* push {lr} */ 2073 | 0x00, 0xf0, /* bl */ 2074 | 0x0c, 0xf8, /* _putc */ 2075 | 0xe6, 0x46, /* mov lr, r12 */ 2076 | 0x00, 0xbd, /* pop {pc} */ 2077 | /*00000016 <_hook2>*/ 2078 | 0xc0, 0x46, /* nop */ 2079 | 0xc0, 0x46, /* nop */ 2080 | 0xc0, 0x46, /* nop */ 2081 | 0xc0, 0x46, /* nop */ 2082 | 0xc0, 0x46, /* nop */ 2083 | 0x01, 0xb5, /* push {r0, lr} */ 2084 | 0x30, 0x46, /* mov r0, r6 */ 2085 | 0x00, 0xf0, /* bl */ 2086 | 0x01, 0xf8, /* _putc */ 2087 | 0x01, 0xbd, /* pop {r0, pc} */ 2088 | /*0000002a <_putc>*/ 2089 | 0x0e, 0xb5, /* push {r1-r3, lr} */ 2090 | 0x04, 0x49, /* ldr r1, [pc, #16] ; block */ 2091 | 0x0a, 0x68, /* ldr r2, [r1, #0] */ 2092 | 0x04, 0x32, /* adds r2, #4 */ 2093 | 0x04, 0x4b, /* ldr r3, [pc, #16] ; limit */ 2094 | 0x9a, 0x42, /* cmp r2, r3 */ 2095 | 0x02, 0xd2, /* bcs .Ldone */ 2096 | 0x88, 0x54, /* strb r0, [r1, r2] */ 2097 | 0x03, 0x3a, /* subs r2, #3 */ 2098 | 0x0a, 0x60, /* str r2, [r1, #0] */ 2099 | 0x0e, 0xbd, /* pop {r1-r3, pc} */ 2100 | 1, 1, 1, 1, /* block */ 2101 | 2, 2, 2, 2, /* limit */ 2102 | }; 2103 | 2104 | // XXX 32bit only here 2105 | static uint32_t 2106 | find_callee_with_str(const uint8_t *buf, size_t size, const char *str) 2107 | { 2108 | uint32_t callee = 0; 2109 | addr_t ref = find_sref(buf, size, str, 0); 2110 | if (ref) { 2111 | addr_t site = step_thumb(buf, ref, 0x10, 0xD000F000, 0xD000F800); 2112 | if (site) { 2113 | callee = site + insn_bl_imm32((uint16_t *)(buf + site)) + 4; 2114 | } 2115 | } 2116 | return callee; 2117 | } 2118 | 2119 | static uint32_t 2120 | find_logger_hook1(void) 2121 | { 2122 | // _iolog_logputc+0x12 (IOLog) 2123 | addr_t IOLog = find_callee_with_str(kernel, kernel_size, "iokit terminate done"); 2124 | /*printf("IOLog = 0x%x\n", IOLog);*/ 2125 | if (IOLog) { 2126 | addr_t a = step_thumb(kernel, IOLog, 0x100, 0x447A, 0xFFFF); 2127 | if (a) { 2128 | addr_t b = calc32(kernel, IOLog, a + 2, 2); 2129 | if (b) { 2130 | a = step_thumb(kernel, b & ~1, 0x40, 0x4478, 0xFF78); 2131 | if (a) { 2132 | return a + 2; 2133 | } 2134 | } 2135 | } 2136 | } 2137 | return 0; 2138 | } 2139 | 2140 | static uint32_t 2141 | find_logger_hook2(void) 2142 | { 2143 | // conslog_putc#tail+0x32 (printf) 2144 | addr_t printf_ = find_callee_with_str(kernel, kernel_size, "iBoot version: %s\n"); 2145 | /*printf("printf_ = 0x%x\n", printf_);*/ 2146 | if (printf_) { 2147 | addr_t a = step_thumb(kernel, printf_, 0x100, 0xD000F000, 0xD000F800); 2148 | if (a) { 2149 | uint32_t b = calc32(kernel, printf_, a, 3); 2150 | if (b) { 2151 | a = step_thumb(kernel, b & ~1, 0x100, 0x9000F000, 0xD000F800); 2152 | if (a) { 2153 | b = a + insn_bl_imm32((uint16_t *)(kernel + a)) + 4; 2154 | if (b) { 2155 | a = step_thumb(kernel, b, 0x40, 0x4478, 0xFF78); 2156 | if (a) { 2157 | a = step_thumb(kernel, a + 2, 0x40, 0x4478, 0xFF78); 2158 | if (a) { 2159 | return a + 2; 2160 | } 2161 | } 2162 | } 2163 | } 2164 | } 2165 | } 2166 | } 2167 | return 0; 2168 | } 2169 | // XXX no 32bit only here 2170 | #endif /* MY_LOGGER */ 2171 | 2172 | static int 2173 | call_kernel(addr_t TRAMPOLINE_ADDR) 2174 | { 2175 | int ret = -1; 2176 | struct dependency dep; 2177 | 2178 | size_t rv; 2179 | size_t size; 2180 | addr_t i, sect; 2181 | addr_t val, site = 0; 2182 | 2183 | rv = load_dep("com.apple.iokit.IOCryptoAcceleratorFamily", &dep); 2184 | assert(rv == 0); 2185 | 2186 | val = find_sref(dep.buf, dep.size, "_internalTest", 1); 2187 | assert(val); 2188 | 2189 | val += dep.base; 2190 | printf("IOAESAcceleratorUserClient::_internalTest: 0x%llx\n", val); 2191 | 2192 | sect = get_sect_data(dep.buf, dep.size, "__DATA", "__const", &size); 2193 | assert(sect); 2194 | if (IS64(kernel)) { 2195 | const uint64_t *ptr = (uint64_t *)(dep.buf + sect); 2196 | for (i = 0; i < size / 8; i++) { 2197 | if (ptr[i] == val) { 2198 | site = i * 8; 2199 | break; 2200 | } 2201 | } 2202 | } else { 2203 | const uint32_t *ptr = (uint32_t *)(dep.buf + sect); 2204 | for (i = 0; i < size / 4; i++) { 2205 | if (ptr[i] == val + 1) { 2206 | site = i * 4; 2207 | break; 2208 | } 2209 | } 2210 | } 2211 | assert(site); 2212 | val = site + sect + dep.base; 2213 | 2214 | free_dep(&dep); 2215 | 2216 | printf("_internalTest: 0x%llx\n", val); 2217 | 2218 | if (IS64(kernel)) { 2219 | rv = kwrite_uint64(val, TRAMPOLINE_ADDR); 2220 | assert(rv == sizeof(uint64_t)); 2221 | } else { 2222 | rv = kwrite_uint32(val, TRAMPOLINE_ADDR + 1); 2223 | assert(rv == sizeof(uint32_t)); 2224 | } 2225 | 2226 | fflush(stdout); 2227 | #ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ 2228 | sync(); 2229 | sleep(1); 2230 | sync(); 2231 | sleep(1); 2232 | sync(); 2233 | sleep(1); 2234 | 2235 | io_connect_t conn = 0; 2236 | io_service_t dev = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOAESAccelerator")); 2237 | if (dev) { 2238 | ret = IOServiceOpen(dev, mach_task_self(), 0, &conn); 2239 | if (ret == kIOReturnSuccess) { 2240 | ret = IOConnectCallStructMethod(conn, 2/*kIOAESAcceleratorTest*/, NULL, 0, NULL, 0); 2241 | IOServiceClose(conn); 2242 | } 2243 | IOObjectRelease(dev); 2244 | } 2245 | #else 2246 | ret = 0; 2247 | #endif 2248 | 2249 | if (IS64(kernel)) { 2250 | rv = kwrite_undo(val, sizeof(uint64_t)); 2251 | assert(rv == sizeof(uint64_t)); 2252 | } else { 2253 | rv = kwrite_undo(val, sizeof(uint32_t)); 2254 | assert(rv == sizeof(uint32_t)); 2255 | } 2256 | 2257 | return ret; 2258 | } 2259 | 2260 | int 2261 | main(int argc, char **argv) 2262 | { 2263 | int rc; 2264 | size_t rv; 2265 | uint8_t *obuf; 2266 | size_t osize; 2267 | addr_t obase; 2268 | addr_t kmod_info; 2269 | char *kernel_ver; 2270 | addr_t vm_kernel_slide; 2271 | char *xml; 2272 | size_t xml_size; 2273 | struct dependency *deps = NULL; 2274 | int i, ndep; 2275 | char *kextid; 2276 | addr_t TRAMPOLINE_ADDR; 2277 | uint8_t *stuff; 2278 | size_t sizeof_stuff; 2279 | addr_t xbase = 0; 2280 | 2281 | if (argc < 3) { 2282 | printf("usage: %s kext plist [deps...]\n", argv[0]); 2283 | return 1; 2284 | } 2285 | #ifndef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ 2286 | /* gross hack ahead */ 2287 | if (!strcmp(argv[1], "-solve")) { 2288 | rv = init_kernel(argv[2]); 2289 | if (rv) { 2290 | fprintf(stderr, "[e] cannot read kernel\n"); 2291 | return -1; 2292 | } 2293 | for (i = 3; i < argc; i++) { 2294 | printf("%s=0x%llx\n", argv[i], solver(NULL, 0, 0, 0, argv[i], NULL, 0)); 2295 | } 2296 | return 0; 2297 | } 2298 | #endif 2299 | ndep = argc - 3; 2300 | 2301 | /* read in kernel, solve some shit, etc. */ 2302 | 2303 | rv = init_kernel("krnl"); 2304 | if (rv) { 2305 | fprintf(stderr, "[e] cannot read kernel\n"); 2306 | return -1; 2307 | } 2308 | dump_file("_krnl", -1, kernel, kernel_size); 2309 | 2310 | kernel_ver = (char *)boyermoore_horspool_memmem(kernel, kernel_size, (unsigned char *)"Darwin Kernel Version", sizeof("Darwin Kernel Version") - 1); 2311 | kernel_db = kdb_init("kernel.db", kernel_ver); 2312 | 2313 | /* read our kext, xml, etc. */ 2314 | 2315 | if (ndep > 0) { 2316 | deps = malloc(ndep * sizeof(struct dependency)); 2317 | assert(deps); 2318 | for (i = 0; i < ndep; i++) { 2319 | rv = load_dep(argv[i + 3], deps + i); 2320 | assert(rv == 0); 2321 | make_all_sections_visible(deps[i].buf, deps[i].size); 2322 | dump_file("_dep", i + 1, deps[i].buf, deps[i].size); 2323 | } 2324 | } 2325 | 2326 | if (IS64(kernel)) { 2327 | unsigned v = 0; 2328 | if (kernel_ver) { 2329 | kernel_ver = strstr(kernel_ver, "root:xnu-"); 2330 | if (kernel_ver) { 2331 | v = atoi(kernel_ver + 9); 2332 | } 2333 | } 2334 | assert(v); 2335 | if (v < 2783) { 2336 | vm_kernel_slide = kernel_base - 0xFFFFFF8000202000; // 7.x 2337 | } else if (v >= 3248) { 2338 | vm_kernel_slide = kernel_base - 0xFFFFFF8004004000; // 9.x 2339 | } else { 2340 | vm_kernel_slide = kernel_base - 0xFFFFFF8002002000; // 8.x 2341 | } 2342 | stuff = (uint8_t *)stuff64; 2343 | sizeof_stuff = sizeof(stuff64); 2344 | } else { 2345 | vm_kernel_slide = kernel_base - 0x80001000; 2346 | stuff = stuff32; 2347 | sizeof_stuff = sizeof(stuff32); 2348 | } 2349 | printf("vm_kernel_slide = 0x%llx\n", vm_kernel_slide); 2350 | obuf = load_kext(argv[1], vm_kernel_slide, &obase, &osize, deps, ndep); 2351 | xml = read_file(argv[2], &xml_size); 2352 | assert(obuf && xml); 2353 | 2354 | printf("obase = 0x%llx\n", obase); 2355 | 2356 | kmod_info = get_sect_data(obuf, osize, "__DATA", "__data", &rv); 2357 | assert(kmod_info); 2358 | printf("kmod_info: 0x%llx\n", kmod_info + obase); 2359 | 2360 | if (IS64(kernel)) { 2361 | *(uint64_t *)(obuf + kmod_info + 0x9c) = obase - vm_kernel_slide; 2362 | *(uint64_t *)(obuf + kmod_info + 0xa4) = osize; 2363 | kextid = (char *)obuf + kmod_info + 0x10; 2364 | } else { 2365 | *(uint32_t *)(obuf + kmod_info + 0x94) = obase - vm_kernel_slide; 2366 | *(uint32_t *)(obuf + kmod_info + 0x98) = osize; 2367 | kextid = (char *)obuf + kmod_info + 0xC; 2368 | } 2369 | 2370 | xml = populate_xml(xml, &xml_size, obase - vm_kernel_slide, osize, kmod_info, kextid); 2371 | 2372 | dump_file("_xml", -1, xml, xml_size); 2373 | dump_file("_kext", -1, obuf, osize); 2374 | 2375 | if (ndep > 0) { 2376 | for (i = 0; i < ndep; i++) { 2377 | free_dep(deps + i); 2378 | } 2379 | free(deps); 2380 | } 2381 | 2382 | /* upload and execute */ 2383 | 2384 | rv = kwrite(obase, obuf, osize); 2385 | assert(rv == osize); 2386 | 2387 | xbase = kalloc(xml_size + 1); 2388 | printf("xml addr = 0x%llx\n", xbase); 2389 | rv = kwrite(xbase, (uint8_t *)xml, xml_size); 2390 | assert(rv == xml_size); 2391 | 2392 | TRAMPOLINE_ADDR = kalloc(sizeof_stuff); 2393 | printf("trampoline: 0x%llx\n", TRAMPOLINE_ADDR); 2394 | 2395 | addr_t OSUnserializeXML = solver(NULL, 0, 0, vm_kernel_slide, "__Z16OSUnserializeXMLPKcPP8OSString", NULL, 0); 2396 | addr_t OSKext_withPrelinkedInfoDict = solver(NULL, 0, 0, vm_kernel_slide, "__ZN6OSKext21withPrelinkedInfoDictEP12OSDictionary", NULL, 0); 2397 | addr_t OSKext_loadKextWithIdentifier = solver(NULL, 0, 0, vm_kernel_slide, "__ZN6OSKext22loadKextWithIdentifierEPKcbbhhP7OSArray", NULL, 0); 2398 | addr_t OSKext_sendAllKextPersonalitiesToCatalog = solver(NULL, 0, 0, vm_kernel_slide, "__ZN6OSKext33sendAllKextPersonalitiesToCatalogEb", NULL, 0); 2399 | 2400 | printf("OSUnserializeXML = 0x%llx\n", OSUnserializeXML); 2401 | printf("OSKext::withPrelinkedInfoDict = 0x%llx\n", OSKext_withPrelinkedInfoDict); 2402 | printf("OSKext::loadKextWithIdentifier = 0x%llx\n", OSKext_loadKextWithIdentifier); 2403 | printf("OSKext::sendAllKextPersonalitiesToCatalog = 0x%llx\n", OSKext_sendAllKextPersonalitiesToCatalog); 2404 | 2405 | if (IS64(kernel)) { 2406 | ((uint64_t *)stuff64)[15] = 0xDC0080F0; 2407 | ((uint64_t *)stuff64)[16] = xbase; 2408 | ((uint64_t *)stuff64)[17] = OSUnserializeXML; 2409 | ((uint64_t *)stuff64)[18] = OSKext_withPrelinkedInfoDict; 2410 | ((uint64_t *)stuff64)[19] = kmod_info + obase + 0x10; /* identifier */ 2411 | ((uint64_t *)stuff64)[20] = OSKext_loadKextWithIdentifier; 2412 | ((uint64_t *)stuff64)[21] = OSKext_sendAllKextPersonalitiesToCatalog; 2413 | } else { 2414 | ((uint32_t *)stuff32)[14] = 0xDC0080F0; 2415 | ((uint32_t *)stuff32)[15] = xbase; 2416 | ((uint32_t *)stuff32)[16] = OSUnserializeXML; 2417 | ((uint32_t *)stuff32)[17] = OSKext_withPrelinkedInfoDict; 2418 | ((uint32_t *)stuff32)[18] = kmod_info + obase + 0xC; /* identifier */ 2419 | ((uint32_t *)stuff32)[19] = OSKext_loadKextWithIdentifier; 2420 | ((uint32_t *)stuff32)[20] = OSKext_sendAllKextPersonalitiesToCatalog; 2421 | } 2422 | dump_file("_tramp", -1, stuff, sizeof_stuff); 2423 | 2424 | rv = kwrite(TRAMPOLINE_ADDR, stuff, sizeof_stuff); 2425 | assert(rv == sizeof_stuff); 2426 | 2427 | #ifdef MY_LOGGER 2428 | // XXX 32bit only here 2429 | assert(!IS64(kernel)); 2430 | uint8_t relog[] = { 2431 | 0x10, 0x46, /* mov r0, r2 */ 2432 | 0x19, 0x46, /* mov r1, r3 */ 2433 | 0, 0, 0, 0, /* bl ... */ 2434 | 0x0F, 0xBC, /* pop {r0-r3} */ 2435 | 0x0F, 0x46, /* mov r7, r1 */ 2436 | 0x10, 0x47 /* bx r2 */ 2437 | }; 2438 | addr_t OSKextLog = find_callee_with_str(kernel, kernel_size, "Jettisoning kext bootstrap segments."); 2439 | printf("OSKextLog = 0x%llx\n", OSKextLog); 2440 | addr_t logger_hook1 = find_logger_hook1(); 2441 | printf("logger_hook1 = 0x%llx\n", logger_hook1); 2442 | addr_t logger_hook2 = find_logger_hook2(); 2443 | printf("logger_hook2 = 0x%llx\n", logger_hook2); 2444 | if (OSKextLog) { 2445 | addr_t y = 0; 2446 | OSKextLog += kernel_base; 2447 | if (logger_hook1) { 2448 | // we found _iolog_logputc, it's best to reroute OSKextLog to IOLogv 2449 | y = solver(NULL, 0, 0, vm_kernel_slide, "_IOLogv", NULL, 0); 2450 | if (y) { 2451 | printf("OSKextLog -> IOLog\n"); 2452 | ((uint32_t *)relog)[1] = make_bl32(0, OSKextLog + 16 + 4, y & ~1); 2453 | } 2454 | } else if (logger_hook2) { 2455 | // we found conslog_putc#tail, it's best to reroute OSKextLog to vprintf 2456 | y = solver(NULL, 0, 0, vm_kernel_slide, "_vprintf", NULL, 0); 2457 | if (y) { 2458 | printf("OSKextLog -> vprintf\n"); 2459 | ((uint32_t *)relog)[1] = make_bl32(0, OSKextLog + 16 + 4, y & ~1); 2460 | } 2461 | } 2462 | if (y == 0) { // we could still reroute to either IOLogv or vprintf, but we did't hook any putc, anyway 2463 | OSKextLog = 0; 2464 | } 2465 | } 2466 | 2467 | addr_t LOGGER_ADDR = (TRAMPOLINE_ADDR + sizeof(stuff32) + 3) & ~3; 2468 | addr_t logger_block = 0; 2469 | 2470 | if (OSKextLog) { 2471 | rv = kwrite(OSKextLog + 16, relog, sizeof(relog)); 2472 | assert(rv == sizeof(relog)); 2473 | } 2474 | if (logger_hook1 || logger_hook2) { 2475 | logger_block = kalloc(MY_LOGGER_SIZE); 2476 | 2477 | void *heck = calloc(1, MY_LOGGER_SIZE); 2478 | rv = kwrite(logger_block, heck, MY_LOGGER_SIZE); 2479 | assert(rv == MY_LOGGER_SIZE); 2480 | free(heck); 2481 | 2482 | printf("logger block: 0x%llx\n", logger_block); 2483 | 2484 | ((uint32_t *)logger32)[16] = logger_block; 2485 | ((uint32_t *)logger32)[17] = MY_LOGGER_SIZE; 2486 | 2487 | if (logger_hook1) { 2488 | memcpy(logger32 + 0x00, kernel + logger_hook1, 12); 2489 | logger_hook1 += kernel_base; 2490 | } 2491 | if (logger_hook2) { 2492 | memcpy(logger32 + 0x16, kernel + logger_hook2, 10); 2493 | logger_hook2 += kernel_base; 2494 | } 2495 | 2496 | rv = kwrite(LOGGER_ADDR, logger32, sizeof(logger32)); 2497 | assert(rv == sizeof(logger32)); 2498 | 2499 | if (logger_hook1) { 2500 | uint8_t hook[] = { 2501 | 0xf4, 0x46, /* mov r12, lr */ 2502 | 0, 0, 0, 0, 2503 | 0, 0, 0, 0, 2504 | 0x88, 0x47, /* blx r1 */ 2505 | }; 2506 | *(uint64_t *)(hook + 2) = make_move(1, LOGGER_ADDR + 0x00 + 1); 2507 | rv = kwrite(logger_hook1, hook + 0, 12); 2508 | assert(rv == 12); 2509 | } 2510 | if (logger_hook2) { 2511 | uint8_t hook[] = { 2512 | 0xf0, 0xde, /* unused */ 2513 | 0, 0, 0, 0, 2514 | 0, 0, 0, 0, 2515 | 0xa0, 0x47, /* blx r4 */ 2516 | }; 2517 | *(uint64_t *)(hook + 2) = make_move(4, LOGGER_ADDR + 0x16 + 1); 2518 | rv = kwrite(logger_hook2, hook + 2, 10); 2519 | assert(rv == 10); 2520 | } 2521 | 2522 | dump_file("_logr", -1, logger32, sizeof(logger32)); 2523 | } 2524 | // XXX no 32bit only here 2525 | #endif /* MY_LOGGER */ 2526 | 2527 | #ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ 2528 | vm_prot_t prot = VM_PROT_READ | VM_PROT_EXECUTE; 2529 | rv = vm_protect(kernel_task, TRAMPOLINE_ADDR, round_page(sizeof_stuff), 1, prot); 2530 | assert(rv == KERN_SUCCESS); 2531 | rv = vm_protect(kernel_task, TRAMPOLINE_ADDR, round_page(sizeof_stuff), 0, prot); 2532 | assert(rv == KERN_SUCCESS); 2533 | #endif /* __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ */ 2534 | 2535 | rc = call_kernel(TRAMPOLINE_ADDR); 2536 | 2537 | kfree(xbase, xml_size + 1); 2538 | 2539 | printf("OSKext -> 0x%x\n", rc); 2540 | #ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ 2541 | if (rc == 0) { 2542 | // XXX seems redundant 2543 | rv = IOCatalogueModuleLoaded(kIOMasterPortDefault, kextid); 2544 | printf("IOKit -> 0x%zx\n", rv); 2545 | } 2546 | #endif 2547 | 2548 | rv = kread(obase, obuf, osize); 2549 | dump_file("_kext", 2, obuf, rv); 2550 | 2551 | #ifdef MY_LOGGER 2552 | // XXX 32bit only here 2553 | if (logger_hook1 || logger_hook2) { 2554 | #ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ 2555 | for (rv = 0; rv < 10; rv++) { 2556 | sleep(1); 2557 | printf("."); 2558 | fflush(stdout); 2559 | } 2560 | printf("\n"); 2561 | #endif 2562 | 2563 | uint32_t *heck = calloc(1, MY_LOGGER_SIZE); 2564 | assert(heck); 2565 | rv = kread(logger_block, (void *)heck, MY_LOGGER_SIZE); 2566 | dump_file("_log", -1, heck + 1, *heck); 2567 | free(heck); 2568 | 2569 | // undo logger hooks 2570 | if (logger_hook1) { 2571 | rv = kwrite_undo(logger_hook1, 12); 2572 | assert(rv == 12); 2573 | } 2574 | if (logger_hook2) { 2575 | rv = kwrite_undo(logger_hook2, 10); 2576 | assert(rv == 10); 2577 | } 2578 | 2579 | kfree(logger_block, MY_LOGGER_SIZE); 2580 | } 2581 | if (OSKextLog) { 2582 | rv = kwrite_undo(OSKextLog + 16, sizeof(relog)); 2583 | assert(rv == sizeof(relog)); 2584 | } 2585 | // XXX no 32bit only here 2586 | #endif /* MY_LOGGER */ 2587 | 2588 | kfree(TRAMPOLINE_ADDR, sizeof_stuff); 2589 | 2590 | // XXX let's see what changed 2591 | rv = kread(kernel_base, kernel, kernel_size); 2592 | assert(rv == kernel_size); 2593 | dump_file("_krnl", 2, kernel, kernel_size); 2594 | 2595 | printf("done\n"); 2596 | 2597 | free(xml); 2598 | free(obuf); 2599 | kdb_term(kernel_db); 2600 | term_kernel(); 2601 | return rc; 2602 | } 2603 | 2604 | /* 2605 | * there are 3 solvers below: 2606 | * solver_code: create small missing functions (nullsubs) 2607 | * solver_kern: use patchfinder to find missing symbols 2608 | * solver_deps: do nasty stuff to satisfy kext dependencies 2609 | */ 2610 | 2611 | static addr_t 2612 | solver_code(uint8_t *p, size_t size, addr_t base, const char *symbol) 2613 | { 2614 | if (!strcmp(symbol, "_bcopy")) { 2615 | static addr_t val = 0; 2616 | if (val) { 2617 | return val; 2618 | } 2619 | if (IS64(p)) { 2620 | val = pre_alloc_sect(p, size, (sizeof(bcopy_arm64) + 3) & ~3); 2621 | if (!val) { 2622 | return 0; 2623 | } 2624 | memcpy(p + val, bcopy_arm64, sizeof(bcopy_arm64)); 2625 | } else { 2626 | val = pre_alloc_sect(p, size, (sizeof(bcopy_armv7) + 3) & ~3); 2627 | if (!val) { 2628 | return 0; 2629 | } 2630 | memcpy(p + val, bcopy_armv7, sizeof(bcopy_armv7)); 2631 | val++; 2632 | } 2633 | val += base; 2634 | return val; 2635 | } 2636 | if ( 2637 | #if 0 2638 | !strcmp(symbol, "_current_task") || 2639 | #endif 2640 | !strcmp(symbol, "__ZN12IOUserClient18clientHasPrivilegeEPvPKc") || 2641 | 0) { 2642 | static addr_t val = 0; 2643 | if (val) { 2644 | return val; 2645 | } 2646 | if (IS64(p)) { 2647 | val = pre_alloc_sect(p, size, (sizeof(ret0_arm64) + 3) & ~3); 2648 | if (!val) { 2649 | return 0; 2650 | } 2651 | memcpy(p + val, ret0_arm64, sizeof(ret0_arm64)); 2652 | } else { 2653 | val = pre_alloc_sect(p, size, (sizeof(ret0_arm32) + 3) & ~3); 2654 | if (!val) { 2655 | return 0; 2656 | } 2657 | memcpy(p + val, ret0_arm32, sizeof(ret0_arm32)); 2658 | val++; 2659 | } 2660 | val += base; 2661 | return val; 2662 | } 2663 | return 0; 2664 | } 2665 | 2666 | static addr_t 2667 | solver_kern(addr_t vm_kernel_slide, const char *symbol) 2668 | { 2669 | addr_t val; 2670 | if (!strcmp(symbol, "_kernel_base")) { 2671 | return kernel_base; 2672 | } 2673 | if (!strcmp(symbol, "_get_task_ipcspace")) { 2674 | val = solver(NULL, 0, 0, vm_kernel_slide, "_ipc_port_copyout_send", NULL, 0); 2675 | if (val) { 2676 | /* look in pthread_callbacks, _get_task_ipcspace is right above _ipc_port_copyout_send */ 2677 | size_t size; 2678 | addr_t i, data = get_sect_data(kernel, kernel_size, "__DATA", "__data", &size); 2679 | if (IS64(kernel)) { 2680 | const uint64_t *kptr = (uint64_t *)(kernel + data); 2681 | for (i = 0; i < size / 8; i++) { 2682 | if (kptr[i] == val) { 2683 | return kptr[i - 1]; 2684 | } 2685 | } 2686 | } else { 2687 | const uint32_t *kptr = (uint32_t *)(kernel + data); 2688 | for (i = 0; i < size / 4; i++) { 2689 | if (kptr[i] == val) { 2690 | return kptr[i - 1]; 2691 | } 2692 | } 2693 | } 2694 | } 2695 | } 2696 | if (!strcmp(symbol, "_ipc_port_copyout_send")) { 2697 | size_t size; 2698 | addr_t x, z, end; 2699 | /* find fileport_alloc() */ 2700 | x = find_sref(kernel, kernel_size, "\"Couldn't allocate send right for fileport!\\n\"", 0); 2701 | if (!x) { 2702 | return 0; 2703 | } 2704 | if (IS64(kernel)) { 2705 | uint64_t y; 2706 | long long w; 2707 | /* find panic() call */ 2708 | x = step_64(kernel, x, 0x10, 0x94000000, 0xFC000000); 2709 | if (!x) { 2710 | return 0; 2711 | } 2712 | /* next call should be to ipc_port_copyout_send() if fileport_alloc() was inline */ 2713 | z = step_64(kernel, x + 4, 0x10, 0x94000000, 0xFC000000); 2714 | if (z) { 2715 | /* follow the call and return that */ 2716 | w = *(uint32_t *)(kernel + z) & 0x3FFFFFF; 2717 | w <<= 64 - 26; 2718 | w >>= 64 - 26 - 2; 2719 | return z + w + kernel_base; 2720 | } 2721 | /* no next call, fileport_alloc() was probably not inline, find ret nearby */ 2722 | x = step_64(kernel, x, 0x18, 0xd65f03c0, 0xFFFFFFFF); 2723 | if (!x) { 2724 | return 0; 2725 | } 2726 | /* found ret, now go to bof... */ 2727 | z = get_sect_data(kernel, kernel_size, "__TEXT", "__text", &size); 2728 | y = bof64(kernel, z, x); 2729 | if (!y) { 2730 | return 0; 2731 | } 2732 | /* ... and find an xref call to it */ 2733 | for (end = z + size; z < end; z += 4) { 2734 | z = step_64(kernel, z, end - z, 0x94000000, 0xFC000000); 2735 | if (!z) { 2736 | break; 2737 | } 2738 | w = *(uint32_t *)(kernel + z) & 0x3FFFFFF; 2739 | w <<= 64 - 26; 2740 | w >>= 64 - 26 - 2; 2741 | x = z + w; 2742 | if (x == y) { 2743 | /* ok got the caller of fileport_alloc(), now find next call */ 2744 | z = step_64(kernel, z + 4, 0x20, 0x94000000, 0xFC000000); 2745 | if (z) { 2746 | w = *(uint32_t *)(kernel + z) & 0x3FFFFFF; 2747 | w <<= 64 - 26; 2748 | w >>= 64 - 26 - 2; 2749 | return z + w + kernel_base; 2750 | } 2751 | break; 2752 | } 2753 | } 2754 | } else { 2755 | uint32_t y, w; 2756 | /* find panic() call */ 2757 | x = step_thumb(kernel, x, 0x10, 0xD000F000, 0xD000F800); 2758 | if (!x) { 2759 | return 0; 2760 | } 2761 | /* next call should be to ipc_port_copyout_send() if fileport_alloc() was inline */ 2762 | z = step_thumb(kernel, x + 4, 0x10, 0xD000F000, 0xD000F800); 2763 | if (z) { 2764 | /* follow the call and return that */ 2765 | y = z + insn_bl_imm32((uint16_t *)(kernel + z)) + 4; 2766 | return y + kernel_base + 1; 2767 | } 2768 | /* no next call, fileport_alloc() was probably not inline, find ret nearby */ 2769 | x = step_thumb(kernel, x, 0x10, 0xBD00, 0xFF00); 2770 | if (!x) { 2771 | return 0; 2772 | } 2773 | /* found ret, now go to bof... */ 2774 | z = get_sect_data(kernel, kernel_size, "__TEXT", "__text", &size); 2775 | y = bof32(kernel, z, x); 2776 | if (!y) { 2777 | return 0; 2778 | } 2779 | /* ... and find an xref call to it */ 2780 | for (end = z + size; z < end; z += 4) { 2781 | z = step_thumb(kernel, z, end - z, 0xD000F000, 0xD000F800); 2782 | if (!z) { 2783 | break; 2784 | } 2785 | w = z + insn_bl_imm32((uint16_t *)(kernel + z)) + 4; 2786 | if (w == y) { 2787 | /* ok got the caller of fileport_alloc(), now find next call */ 2788 | z = step_thumb(kernel, z + 4, 0x20, 0xD000F000, 0xD000F800); 2789 | if (z) { 2790 | y = z + insn_bl_imm32((uint16_t *)(kernel + z)) + 4; 2791 | return y + kernel_base + 1; 2792 | } 2793 | break; 2794 | } 2795 | } 2796 | } 2797 | } 2798 | if (!strcmp(symbol, "_kernel_pmap")) { 2799 | if (IS64(kernel)) { 2800 | addr_t bof; 2801 | addr_t ret; 2802 | addr_t call; 2803 | size_t size; 2804 | addr_t sect; 2805 | // find beginning of function containing this string 2806 | bof = find_sref(kernel, kernel_size, "\"pmap_map_bd\"", 1); 2807 | if (!bof) { 2808 | return 0; 2809 | } 2810 | // get text section info 2811 | sect = get_sect_data(kernel, kernel_size, "__TEXT", "__text", &size); 2812 | if (!sect) { 2813 | return 0; 2814 | } 2815 | // find ret 2816 | ret = step_64(kernel, bof, 0x1000, 0xD65F03C0, 0xFFFFFFFF); 2817 | if (!ret) { 2818 | return 0; 2819 | } 2820 | // find last call before ret 2821 | call = step_64_back(kernel, ret, 0x40, 0x94000000, 0xFC000000); 2822 | if (!call) { 2823 | return 0; 2824 | } 2825 | // calculate x2 ref at the time of call 2826 | val = calc64(kernel, bof, call, 2); 2827 | } else { 2828 | val = find_pmap_location(kernel_base, kernel, kernel_size); 2829 | } 2830 | if (val) { 2831 | return kernel_base + val; 2832 | } 2833 | } 2834 | if (!strcmp(symbol, "gPhysAddr64")) { 2835 | if (IS64(kernel)) { 2836 | addr_t bof; 2837 | addr_t sub; 2838 | addr_t site; 2839 | unsigned insn; 2840 | // find function containing this string 2841 | site = find_sref(kernel, kernel_size, "\"mdevadd: attempt to add overlapping memory device at %016llX-%016llX\\n\"", 0); 2842 | if (!site) { 2843 | return 0; 2844 | } 2845 | // find beginning of this function 2846 | bof = bof64(kernel, 0, site); 2847 | if (!bof) { 2848 | return 0; 2849 | } 2850 | // find sub reg, reg 2851 | sub = step_64_back(kernel, site, 0x40, 0x4B000000, 0x7FC0FC00); 2852 | if (!sub) { 2853 | return 0; 2854 | } 2855 | // find subtracted register address 2856 | insn = *(uint32_t *)(kernel + sub); 2857 | val = calc64(kernel, bof, sub, (insn >> 16) & 0x1F); 2858 | if (val) { 2859 | return kernel_base + val; 2860 | } 2861 | } 2862 | } 2863 | if (!strcmp(symbol, "__Z16OSUnserializeXMLPKcPP8OSString")) { 2864 | // find bof of sref to "OSUnserializeXML: %s near line %d\n" 2865 | // WARNING: but that may be __Z21OSUnserializeXMLparsePv 2866 | } 2867 | if (!strcmp(symbol, "__ZN6OSKext21withPrelinkedInfoDictEP12OSDictionary")) { 2868 | // find sref to "OSBundleRamDiskOnly" in "__KLD" 2869 | // from there, find first BEQ/BNE and follow it 2870 | // from there, find first BL call and follow it 2871 | // WARNING: doesn't work online on a running kernel, because __KLD segment is gone 2872 | // WARNING: doesn't work offline on a dumped kernel, because addresses mismatch 2873 | // (works offline on a decrypted kernel) 2874 | #ifndef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ 2875 | addr_t x; 2876 | uint8_t *str; 2877 | size_t size; 2878 | addr_t sect; 2879 | 2880 | str = boyermoore_horspool_memmem(kernel, kernel_size, (uint8_t *)"OSBundleRamDiskOnly", sizeof("OSBundleRamDiskOnly")); 2881 | if (!str) { 2882 | return 0; 2883 | } 2884 | sect = get_sect_data(kernel, kernel_size, "__KLD", "__text", &size); 2885 | if (!sect) { 2886 | return 0; 2887 | } 2888 | if (IS64(kernel)) { 2889 | uint64_t y; 2890 | x = xref64(kernel, sect, sect + size, str - kernel); 2891 | if (!x) { 2892 | return 0; 2893 | } 2894 | x = step_64(kernel, x, 0x100, 0x36000000, 0x7E000000); 2895 | if (!x) { 2896 | return 0; 2897 | } 2898 | x += (*(uint32_t *)(kernel + x) & 0xFFFE0) >> 3; 2899 | x = step_64(kernel, x, 0x10, 0x94000000, 0xFC000000); 2900 | if (!x) { 2901 | return 0; 2902 | } 2903 | y = get_vaddr(kernel, kernel_size, x); 2904 | if (!y) { 2905 | return 0; 2906 | } 2907 | return y + ((*(int *)(kernel + x) << 6) >> 4); 2908 | } else { 2909 | uint32_t y; 2910 | x = xref32(kernel, sect, sect + size, str - kernel); 2911 | if (!x) { 2912 | return 0; 2913 | } 2914 | x = step_thumb(kernel, x, 0x100, 0xD000, 0xFE00); 2915 | if (!x) { 2916 | return 0; 2917 | } 2918 | x += kernel[x] * 2 + 4; 2919 | x = step_thumb(kernel, x, 0x10, 0xD000F000, 0xD000F800); 2920 | if (!x) { 2921 | return 0; 2922 | } 2923 | y = get_vaddr(kernel, kernel_size, x); 2924 | if (!y) { 2925 | return 0; 2926 | } 2927 | return y + insn_bl_imm32((uint16_t *)(kernel + x)) + 4 + 1; 2928 | } 2929 | #endif /* __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ */ 2930 | } 2931 | if (!strcmp(symbol, "__ZN6OSKext22loadKextWithIdentifierEPKcbbhhP7OSArray")) { 2932 | val = find_sref(kernel, kernel_size, "Can't load kext %s - not found.", 1); 2933 | if (val) { 2934 | if (IS64(kernel)) { 2935 | return val + kernel_base; 2936 | } else { 2937 | return val + kernel_base + 1; 2938 | } 2939 | } 2940 | } 2941 | if (!strcmp(symbol, "__ZN6OSKext33sendAllKextPersonalitiesToCatalogEb")) { 2942 | val = find_sref(kernel, kernel_size, "but not starting matching", 1); 2943 | if (val) { 2944 | if (IS64(kernel)) { 2945 | return val + kernel_base; 2946 | } else { 2947 | return val + kernel_base + 1; 2948 | } 2949 | } 2950 | } 2951 | if (!strcmp(symbol, "_IOSimpleLockLock")) { 2952 | return solver(NULL, 0, 0, vm_kernel_slide, "_lck_spin_lock", NULL, 0); 2953 | } 2954 | if (!strcmp(symbol, "_IOSimpleLockUnlock")) { 2955 | return solver(NULL, 0, 0, vm_kernel_slide, "_lck_spin_unlock", NULL, 0); 2956 | } 2957 | return 0; 2958 | } 2959 | 2960 | static addr_t 2961 | solver_deps(uint8_t *p, size_t size, addr_t base, addr_t vm_kernel_slide, const char *symbol, struct dependency *deps, int ndep) 2962 | { 2963 | addr_t val; 2964 | 2965 | while (ndep > 0) { 2966 | if (!strcmp(deps->name, "com.apple.driver.AppleARMPlatform")) { 2967 | if (!strcmp(symbol, "__ZN22AppleARMNORFlashDevice9metaClassE")) { 2968 | if (IS64(kernel)) { 2969 | return find_metaclass64(deps->buf, deps->size, deps->base, "AppleARMNORFlashDevice"); 2970 | } else { 2971 | return find_metaclass32(deps->buf, deps->size, deps->base, "AppleARMNORFlashDevice"); 2972 | } 2973 | } 2974 | } 2975 | ndep--; 2976 | deps++; 2977 | } 2978 | return 0; 2979 | } 2980 | --------------------------------------------------------------------------------