Chameleon

Chameleon Svn Source Tree

Root/branches/ErmaC/Enoch/i386/libsaio/binaryPatcher.c

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//
14unsigned 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
45unsigned 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
75bool 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..
107struct 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
177void 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
195bool 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
248int 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//
262void 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 intnumPatch, count;
271 numPatch = -1;
272 count = 0;
273 char * realReason;
274 TagPtrPatchTag = 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

Archive Download this file

Revision: 2870