├── 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, "", 2);
243 | tmp = mempcpy(tmp, tag, endtag - tag);
244 | 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 |
--------------------------------------------------------------------------------