1 | //␊ |
2 | // binaryPatcher.c␊ |
3 | // ␊ |
4 | // binary patcher of any kinds (kexts, kernel, ACPI tables etc..)␊ |
5 | //␊ |
6 | //␊ |
7 | ␊ |
8 | #include "binaryPatcher.h"␊ |
9 | #include "boot.h" /* to expose gDarwinBuildVerStr */␊ |
10 | ␊ |
11 | // Clover␊ |
12 | // Searches Source for Search pattern of size SearchSize and return number of occurrences␊ |
13 | //␊ |
14 | unsigned int FindAndCount(void *sourceData,␊ |
15 | UInt32 SourceSize,␊ |
16 | UInt32 StartLocation,␊ |
17 | UInt8 *Search,␊ |
18 | unsigned int SearchSize)␊ |
19 | {␊ |
20 | UInt8 *Source = ( UInt8 *)sourceData;␊ |
21 | SourceSize += StartLocation;␊ |
22 | unsigned int NumFounds = 0;␊ |
23 | UInt8 *End = Source + SourceSize;␊ |
24 | ␊ |
25 | while (Source < End) {␊ |
26 | if (memcmp(Source, Search, SearchSize) == 0) {␊ |
27 | NumFounds++;␊ |
28 | Source += SearchSize;␊ |
29 | } else {␊ |
30 | Source++;␊ |
31 | }␊ |
32 | }␊ |
33 | return NumFounds;␊ |
34 | }␊ |
35 | ␊ |
36 | // Clover␊ |
37 | //␊ |
38 | // Searches Source for Search pattern of size SearchSize␊ |
39 | // and replaces it with Replace up to MaxReplaces times.␊ |
40 | // If MaxReplaces <= 0, then there is no restriction on number of replaces.␊ |
41 | // Replace should have the same size as Search.␊ |
42 | // Returns number of replaces done.␊ |
43 | //␊ |
44 | ␊ |
45 | unsigned int FindAndReplace(void *sourceData,␊ |
46 | UInt32 SourceSize,␊ |
47 | UInt32 StartLocation,␊ |
48 | UInt8 *Search,␊ |
49 | unsigned int SearchSize,␊ |
50 | UInt8 *Replace,␊ |
51 | int MaxReplaces)␊ |
52 | {␊ |
53 | UInt8 *Source = ( UInt8 *)sourceData;␊ |
54 | Source += StartLocation;␊ |
55 | unsigned int NumReplaces = 0;␊ |
56 | bool NoReplacesRestriction = MaxReplaces <= 0;␊ |
57 | UInt8 *End = Source + SourceSize;␊ |
58 | if (!Source || !Search || !Replace || !SearchSize) {␊ |
59 | return 0;␊ |
60 | }␊ |
61 | ␊ |
62 | while ((Source < End) && (NoReplacesRestriction || (MaxReplaces > 0))) {␊ |
63 | if (memcmp(Source, Search, SearchSize) == 0) {␊ |
64 | memcpy(Source, Replace, SearchSize);␊ |
65 | NumReplaces++;␊ |
66 | MaxReplaces--;␊ |
67 | Source += SearchSize;␊ |
68 | } else {␊ |
69 | Source++;␊ |
70 | }␊ |
71 | }␊ |
72 | return NumReplaces;␊ |
73 | }␊ |
74 | ␊ |
75 | bool IsPatchEnabled (char *MatchOSEntry, char *CurrOS)␊ |
76 | {␊ |
77 | int i;␊ |
78 | bool ret = false;␊ |
79 | struct MatchOSes *mos; // = malloc(sizeof(struct MatchOSes));␊ |
80 | ␊ |
81 | if (!MatchOSEntry || !CurrOS) {␊ |
82 | return true; //undefined matched corresponds to old behavior␊ |
83 | }␊ |
84 | ␊ |
85 | mos = GetStrArraySeparatedByChar(MatchOSEntry, ',');␊ |
86 | if (!mos) {␊ |
87 | return true; //memory fails -> anyway the patch enabled␊ |
88 | }␊ |
89 | ␊ |
90 | for (i = 0; i < mos->count; ++i) {␊ |
91 | // dot represent MatchOS␊ |
92 | if (␊ |
93 | ((strstr(mos->array[i], ".") != NULL) && IsOSValid(mos->array[i], CurrOS)) || // MatchOS␊ |
94 | (strstr(mos->array[i], CurrOS) != NULL) // MatchBuild␊ |
95 | ) {␊ |
96 | ret = true;␊ |
97 | break;␊ |
98 | }␊ |
99 | }␊ |
100 | deallocMatchOSes(mos);␊ |
101 | return ret;␊ |
102 | }␊ |
103 | // FIXME␊ |
104 | // the following is an improved versione of what Clover have␊ |
105 | // but malloc in Enoch fails to allocate memory (also using the same code in Clover ).␊ |
106 | // For now we use a buffer and for this reason deallocMatchOSes() does nothing..␊ |
107 | struct MatchOSes *GetStrArraySeparatedByChar(char *str, char sep)␊ |
108 | {␊ |
109 | char buffer[strlen(str) +1];␊ |
110 | struct MatchOSes *mo;␊ |
111 | int len = 0, i = 0, inc = 1;␊ |
112 | ␊ |
113 | char doubleSep[2];␊ |
114 | ␊ |
115 | mo = malloc(sizeof(struct MatchOSes));␊ |
116 | if (!mo) {␊ |
117 | return NULL;␊ |
118 | }␊ |
119 | mo->count = countOccurrences( str, sep ) + 1;␊ |
120 | len = (int)strlen(str);␊ |
121 | doubleSep[0] = sep; doubleSep[1] = sep;␊ |
122 | ␊ |
123 | if(strstr(str, doubleSep) || !len || str[0] == sep || str[len -1] == sep) {␊ |
124 | mo->count = 0;␊ |
125 | mo->array[0] = NULL;␊ |
126 | return mo;␊ |
127 | }␊ |
128 | ␊ |
129 | if (mo->count > 1) {␊ |
130 | int *indexes = (int *) malloc(mo->count + 1);␊ |
131 | ␊ |
132 | for (i = 0; i < len; ++i) {␊ |
133 | char c = str[i];␊ |
134 | if (c == sep) {␊ |
135 | indexes[inc]=i;␊ |
136 | inc++;␊ |
137 | }␊ |
138 | }␊ |
139 | ␊ |
140 | indexes[0] = 0; // manually add first index␊ |
141 | indexes[mo->count] = len; // manually add last index␊ |
142 | ␊ |
143 | int startLocation = 0, endLocation = 0;␊ |
144 | ␊ |
145 | for (i = 0; i < mo->count; ++i) {␊ |
146 | unsigned int newLen = 0;␊ |
147 | startLocation = i ? indexes[i] + 1 : indexes[0];␊ |
148 | endLocation = (i == mo->count - 1) ? len : indexes[i + 1];␊ |
149 | ␊ |
150 | newLen = (endLocation - startLocation);␊ |
151 | ␊ |
152 | //char *lastStr = (char *) malloc(newLen+1);␊ |
153 | //strncpy(lastStr, str + startLocation, newLen);␊ |
154 | strncpy(buffer, str + startLocation, newLen);␊ |
155 | buffer[newLen /*strlen(lastStr)*/] = '\0';␊ |
156 | mo->array[i] = buffer;␊ |
157 | ␊ |
158 | //printf("%s [len = %lu]\n", mo->array[i], strlen(mo->array[i]));␊ |
159 | if (endLocation == len) break;␊ |
160 | }␊ |
161 | ␊ |
162 | free(indexes);␊ |
163 | }␊ |
164 | else {␊ |
165 | //char *lastStr = (char *) malloc(strlen(str)+1);␊ |
166 | //strncpy(lastStr, str, strlen(str));␊ |
167 | strncpy(buffer, str, strlen(str));␊ |
168 | buffer[strlen(str)] = '\0';␊ |
169 | mo->array[0] = buffer;␊ |
170 | //printf("%s [len = %lu]\n", mo->array[0], strlen(mo->array[0]));␊ |
171 | }␊ |
172 | //printf("------------\n");␊ |
173 | ␊ |
174 | return mo;␊ |
175 | }␊ |
176 | ␊ |
177 | void deallocMatchOSes(struct MatchOSes *s)␊ |
178 | {␊ |
179 | /*␊ |
180 | int i;␊ |
181 | ␊ |
182 | if (!s) {␊ |
183 | return;␊ |
184 | }␊ |
185 | ␊ |
186 | for (i = 0; i < s->count; i++) {␊ |
187 | if (s->array[i]) {␊ |
188 | free(s->array[i]);␊ |
189 | }␊ |
190 | }␊ |
191 | free(s);␊ |
192 | */␊ |
193 | }␊ |
194 | ␊ |
195 | bool IsOSValid(char *MatchOS, char *CurrOS)␊ |
196 | {␊ |
197 | /* example for valid matches are:␊ |
198 | 10.7, only 10.7 (10.7.1 will be skipped)␊ |
199 | 10.10.2 only 10.10.2 (10.10.1 or 10.10.5 will be skipped)␊ |
200 | 10.10.x (or 10.10.X), in this case is valid for all minor version of 10.10 (10.10.(0-9))␊ |
201 | */␊ |
202 | ␊ |
203 | bool ret = false;␊ |
204 | struct MatchOSes *osToc;␊ |
205 | struct MatchOSes *currOStoc;␊ |
206 | ␊ |
207 | if (!MatchOS || !CurrOS) {␊ |
208 | return true; //undefined matched corresponds to old behavior␊ |
209 | }␊ |
210 | ␊ |
211 | osToc = GetStrArraySeparatedByChar(MatchOS, '.');␊ |
212 | currOStoc = GetStrArraySeparatedByChar(CurrOS, '.');␊ |
213 | ␊ |
214 | if (osToc->count == 2) {␊ |
215 | if (strcmp(osToc->array[0], currOStoc->array[0]) == 0␊ |
216 | && strcmp(osToc->array[1], currOStoc->array[1]) == 0) {␊ |
217 | ret = true;␊ |
218 | }␊ |
219 | } else if (osToc->count == 3) {␊ |
220 | if (currOStoc->count == 3) {␊ |
221 | if (strcmp(osToc->array[0], currOStoc->array[0]) == 0␊ |
222 | && strcmp(osToc->array[1], currOStoc->array[1]) == 0␊ |
223 | && strcmp(osToc->array[2], currOStoc->array[2]) == 0) {␊ |
224 | ret = true;␊ |
225 | } else if (strcmp(osToc->array[0], currOStoc->array[0]) == 0␊ |
226 | && strcmp(osToc->array[1], currOStoc->array[1]) == 0␊ |
227 | && (strcmp(osToc->array[2], "x") == 0 || strcmp(osToc->array[2], "X") == 0)) {␊ |
228 | ret = true;␊ |
229 | }␊ |
230 | } else if (currOStoc->count == 2) {␊ |
231 | if (strcmp(osToc->array[0], currOStoc->array[0]) == 0␊ |
232 | && strcmp(osToc->array[1], currOStoc->array[1]) == 0) {␊ |
233 | ret = true;␊ |
234 | } else if (strcmp(osToc->array[0], currOStoc->array[0]) == 0␊ |
235 | && strcmp(osToc->array[1], currOStoc->array[1]) == 0␊ |
236 | && (strcmp(osToc->array[2], "x") == 0 || strcmp(osToc->array[2], "X") == 0)) {␊ |
237 | ret = true;␊ |
238 | }␊ |
239 | }␊ |
240 | ␊ |
241 | }␊ |
242 | ␊ |
243 | deallocMatchOSes(osToc);␊ |
244 | deallocMatchOSes(currOStoc);␊ |
245 | return ret;␊ |
246 | }␊ |
247 | ␊ |
248 | int countOccurrences( char *s, char c )␊ |
249 | {␊ |
250 | return *s == '\0'␊ |
251 | ? 0␊ |
252 | : countOccurrences( s + 1, c ) + (*s == c);␊ |
253 | }␊ |
254 | // End of MatchOS␊ |
255 | ␊ |
256 | ␊ |
257 | //␊ |
258 | // Micky1979␊ |
259 | //␊ |
260 | // Function to iterate the kernel.plist/kexts.plist (Acpi.plist?) and apply patches␊ |
261 | //␊ |
262 | void pach_binaryUsingDictionary(void *data,␊ |
263 | UInt32 dataLen,␊ |
264 | UInt32 StartLocation,␊ |
265 | char *reason,␊ |
266 | TagPtr config)␊ |
267 | {␊ |
268 | bool canPatch = false;␊ |
269 | UInt8 *bytes = (UInt8 *)data;␊ |
270 | int␉numPatch, count;␊ |
271 | numPatch = -1;␊ |
272 | count = 0;␊ |
273 | char * realReason;␊ |
274 | TagPtr␉␉PatchTag = NULL;␊ |
275 | ␊ |
276 | if (!config) return;␊ |
277 | ␊ |
278 | /* coming soon..␊ |
279 | if (!strcmp(reason, "AcpiPatches")) {␊ |
280 | // on all the acpi tables␊ |
281 | realReason = "Acpi";␊ |
282 | }␊ |
283 | else␊ |
284 | if (!strcmp(reason, "DsdtPatches")) {␊ |
285 | // only on dsdt␊ |
286 | realReason = "Dsdt";␊ |
287 | }␊ |
288 | else␊ |
289 | */␊ |
290 | if (!strcmp(reason, "KernelPatches")) {␊ |
291 | realReason = "kernel";␊ |
292 | }␊ |
293 | else␊ |
294 | {␊ |
295 | realReason = reason;␊ |
296 | }␊ |
297 | ␊ |
298 | if ((PatchTag = XMLCastArray(XMLGetProperty(config, (const char *)reason))))␊ |
299 | {␊ |
300 | count = XMLTagCount(PatchTag);␊ |
301 | ␊ |
302 | /* for (i=0; i<count; i++) normal */␊ |
303 | for (unsigned i = count ; i-- > 0 ;) /* reversed iteration since xml.c add it reversed */␊ |
304 | {␊ |
305 | numPatch ++;␊ |
306 | if (!bytes) {␊ |
307 | verbose("\t Skipping patch for %s because no bytes to patch..\n", realReason);␊ |
308 | return;␊ |
309 | }␊ |
310 | ␊ |
311 | TagPtr index = XMLGetElement( PatchTag, i );␊ |
312 | if (index)␊ |
313 | {␊ |
314 | char *Comment = NULL;␊ |
315 | char *MatchOS = NULL;␊ |
316 | char *MatchBuild = NULL;␊ |
317 | char *MatchUUID = NULL;␊ |
318 | ␊ |
319 | int numPatches = 0;␊ |
320 | ␊ |
321 | TagPtr FindPtr = XMLGetProperty(index, (const char*)"Find");␊ |
322 | TagPtr ReplacePtr = XMLGetProperty(index, (const char*)"Replace");␊ |
323 | ␊ |
324 | Comment = XMLCastString(XMLGetProperty(index, (const char*)"Comment"));␊ |
325 | MatchOS = XMLCastString(XMLGetProperty(index, (const char*)"MatchOS"));␊ |
326 | MatchBuild = XMLCastString(XMLGetProperty(index, (const char*)"MatchBuild"));␊ |
327 | MatchUUID = XMLCastString(XMLGetProperty(index, (const char*)"MatchUUID"));␊ |
328 | ␊ |
329 | Comment = (Comment != NULL && strlen(Comment) >0) ? Comment : "untitled";␊ |
330 | MatchOS = (MatchOS != NULL && strlen(MatchOS) >0) ? MatchOS : ""; // if lenght is 0 IsPatchEnabled() will not be called!␊ |
331 | MatchBuild = (MatchBuild != NULL && strlen(MatchBuild) >0) ? MatchBuild : "";␊ |
332 | MatchUUID = (MatchUUID != NULL && strlen(MatchUUID) >0) ? MatchUUID : "";␊ |
333 | ␊ |
334 | canPatch = true;␊ |
335 | // the order is important to skip patches␊ |
336 | if (strlen(MatchUUID))␊ |
337 | {␊ |
338 | // to be implemented␊ |
339 | // check MatchUUID and disable this patch if does not match. Usefull to load or skip patches for certain volumes␊ |
340 | // canPatch = IsPatchEnabled(MatchUUID, (char *)uuid_to_be_taken_somewhere);␊ |
341 | }␊ |
342 | else␊ |
343 | {␊ |
344 | MatchUUID = "not implemented";␊ |
345 | }␊ |
346 | ␊ |
347 | if (strlen(MatchOS))␊ |
348 | {␊ |
349 | // check MatchOS and disable this patch if does not match␊ |
350 | canPatch = IsPatchEnabled(MatchOS, (char *)gMacOSVersion);␊ |
351 | }␊ |
352 | else␊ |
353 | {␊ |
354 | MatchOS = "not set";␊ |
355 | }␊ |
356 | ␊ |
357 | if (strlen(MatchBuild))␊ |
358 | {␊ |
359 | // check MatchBuild and disable this patch if does not match␊ |
360 | canPatch = IsPatchEnabled(MatchBuild, (char *)gMacOSVersion);␊ |
361 | }␊ |
362 | else␊ |
363 | {␊ |
364 | MatchBuild = "not set";␊ |
365 | }␊ |
366 | ␊ |
367 | char buf[1024];␊ |
368 | sprintf(buf, "\tPatching %s [Item %d] (%s) MatchOS[ %s ] MatchBuild[ %s ]: ",␊ |
369 | realReason, numPatch, Comment, MatchOS, MatchBuild);␊ |
370 | verbose("%s", buf);␊ |
371 | ␊ |
372 | if (canPatch)␊ |
373 | {␊ |
374 | if (FindPtr && ReplacePtr)␊ |
375 | {␊ |
376 | if (!XMLIsData(FindPtr))␊ |
377 | {␊ |
378 | verbose("\n\t\t\tUser Error, Find not in data tag, patch skipped\n");␊ |
379 | return;␊ |
380 | }␊ |
381 | ␊ |
382 | if (!XMLIsData(ReplacePtr))␊ |
383 | {␊ |
384 | verbose("\n\t\t\tUser Error, Replace not in data tag, patch skipped\n");␊ |
385 | return;␊ |
386 | }␊ |
387 | ␊ |
388 | // don't allow patches with less than 2 bytes (it's ridiculous)␊ |
389 | if (sizeof(FindPtr->data) <= 2)␊ |
390 | {␊ |
391 | verbose("\n\t\t\tUser Error, Find is less than 2 bytes (or less), patch skipped\n");␊ |
392 | return;␊ |
393 | }␊ |
394 | ␊ |
395 | if (sizeof(FindPtr->data) != sizeof(ReplacePtr->data))␊ |
396 | {␊ |
397 | verbose("\n\t\t\tUser Error, Find and Replace does not have the same length, patch skipped\n");␊ |
398 | return;␊ |
399 | }␊ |
400 | ␊ |
401 | if (sizeof(FindPtr->data) > (dataLen - StartLocation))␊ |
402 | {␊ |
403 | verbose("\n\t\t\tUser Error, Find is bigger than the hole data, patch skipped\n");␊ |
404 | return;␊ |
405 | }␊ |
406 | }␊ |
407 | else␊ |
408 | {␊ |
409 | verbose("\n\t\t\tUser Error, Find or Replace (or both) are missing, patch skipped\n");␊ |
410 | return;␊ |
411 | }␊ |
412 | }␊ |
413 | ␊ |
414 | ␊ |
415 | // patch it␊ |
416 | if (canPatch) {␊ |
417 | numPatches = FindAndReplace(data,␊ |
418 | (UInt32)dataLen,␊ |
419 | StartLocation,␊ |
420 | FindPtr->data,␊ |
421 | sizeof(FindPtr->data),␊ |
422 | ReplacePtr->data,␊ |
423 | 0);␊ |
424 | verbose("%d substitutions made!\n", numPatches);␊ |
425 | }␊ |
426 | else␊ |
427 | {␊ |
428 | verbose("disabled!\n");␊ |
429 | }␊ |
430 | }␊ |
431 | }␊ |
432 | }␊ |
433 | }␊ |
434 | |