Chameleon

Chameleon Svn Source Tree

Root/branches/azimutz/Chazileon/i386/boot2/kernel_patcher.c

1/*
2 * Copyright (c) 2009 Evan Lojewski. All rights reserved.
3 *
4 */
5
6#include "libsaio.h"
7#include "kernel_patcher.h"
8#include "platform.h"
9
10extern PlatformInfo_t Platform;
11
12
13#define SYMBOL_CPUID_SET_INFO0
14#define SYMBOL_PANIC1
15#define SYMBOL_PMCPUEXITHALTTOOFF2
16#define SYMBOL_LAPIC_INIT3
17#define SYMBOL_COMMPAGE_STUFF_ROUTINE4 // not needed to boot -legacy :)
18#define NUM_SYMBOLS5 // change according nr of simbols
19
20#define SYMBOL_CPUID_SET_INFO_STRING"_cpuid_set_info"
21#define SYMBOL_PANIC_STRING"_panic"
22#define SYMBOL_PMCPUEXITHALTTOOFF_STRING"_pmCPUExitHaltToOff"
23#define SYMBOL_LAPIC_INIT_STRING"_lapic_init"
24#define SYMBOL_COMMPAGE_STUFF_ROUTINE_STRING"_commpage_stuff_routine"
25
26char* kernelSymbols[NUM_SYMBOLS] = {
27SYMBOL_CPUID_SET_INFO_STRING,
28SYMBOL_PANIC_STRING,
29SYMBOL_PMCPUEXITHALTTOOFF_STRING,
30SYMBOL_LAPIC_INIT_STRING,
31SYMBOL_COMMPAGE_STUFF_ROUTINE_STRING
32};
33
34UInt32 kernelSymbolAddresses[NUM_SYMBOLS] = {
350,
360,
370,
380,
390
40};
41
42
43UInt32 textSection = 0;
44UInt32 textAddress = 0;
45
46
47void patch_kernel(void* kernelData)
48{
49switch (locate_symbols((void*)kernelData)) {
50case KERNEL_32:
51patch_kernel_32((void*)kernelData);
52break;
53
54case KERNEL_64:
55default:
56patch_kernel_64((void*)kernelData);
57break;
58}
59}
60
61// patches a 64bit kernel.
62void patch_kernel_64(void* kernelData)
63{
64// At the moment, the kernel patching code fails when used
65// in 64bit mode, so we don't patch it. This is due to 32bit vs 64bit
66// pointers as well as changes in structure sizes
67printf("Unable to patch 64bit kernel. Please use arch=i386 or -legacy flags.\n");
68}
69
70
71/**
72 ** patch_kernel_32
73 **patches kernel based on cpu info determined earlier in the boot process.
74 **It the machine is vmware, remove the artificial lapic panic
75 **If the CPU is not supported, remove the cpuid_set_info panic
76 **If the CPU is and Intel Atom, inject the penryn cpuid info.
77 **/
78void patch_kernel_32(void* kernelData)
79{
80// Remove panic in commpage
81patch_commpage_stuff_routine(kernelData);
82
83//patch_pmCPUExitHaltToOff(kernelData);// Not working as intended, disabled for now
84
85//if(vmware_detected)
86{
87patch_lapic_init(kernelData);
88}
89
90switch(Platform.CPU.Model)
91{
92// Known good CPU's, no reason to patch kernel
93case 13:
94case CPUID_MODEL_YONAH:
95case CPUID_MODEL_MEROM:
96case CPUID_MODEL_PENRYN:
97case CPUID_MODEL_NEHALEM:
98case CPUID_MODEL_FIELDS:
99case CPUID_MODEL_DALES:
100case CPUID_MODEL_NEHALEM_EX:
101break;
102
103// Known unsuported CPU's
104case CPUID_MODEL_ATOM:
105// TODO: Impersonate CPU based on user selection
106patch_cpuid_set_info(kernelData, CPUFAMILY_INTEL_PENRYN, CPUID_MODEL_PENRYN);// Impersonate Penryn CPU
107break;
108
109// Unknown CPU's
110default:
111// TODO: Impersonate CPU based on user selection
112//patch_cpuid_set_info(kernelData, CPUFAMILY_INTEL_PENRYN, CPUID_MODEL_PENRYN); // Impersonate CPU
113patch_cpuid_set_info(kernelData, 0, 0);// Remove Panic Call
114
115break;
116}
117}
118
119
120/**
121 **This functions located the kernelSymbols[i] symbols in the mach-o header.
122 **as well as determines the start of the __TEXT segment and __TEXT,__text sections
123 **/
124int locate_symbols(void* kernelData)
125{
126UInt16 symbolIndexes[NUM_SYMBOLS];
127
128struct load_command *loadCommand;
129struct symtab_command *symtableData;
130struct nlist *symbolEntry;
131
132char* symbolString;
133
134UInt32 kernelIndex = 0;
135kernelIndex += sizeof(struct mach_header);
136
137if(((struct mach_header*)kernelData)->magic != MH_MAGIC) return KERNEL_64;
138
139
140//printf("%d load commands beginning at 0x%X\n", (unsigned int)header->ncmds, (unsigned int)kernelIndex);
141//printf("Commands take up %d bytes\n", header->sizeofcmds);
142
143
144int cmd = 0;
145while(cmd < ((struct mach_header*)kernelData)->ncmds)// TODO: for loop instead
146{
147cmd++;
148
149loadCommand = kernelData + kernelIndex;
150
151UInt cmdSize = loadCommand->cmdsize;
152
153// Locate start of _panic and _cpuid_set_info in the symbol tabe.
154// Load commands should be anded with 0x7FFFFFFF to ignore theLC_REQ_DYLD flag
155if((loadCommand->cmd & 0x7FFFFFFF) == LC_SYMTAB)// We only care about the symtab segment
156{
157//printf("Located symtable, length is 0x%X, 0x%X\n", (unsigned int)loadCommand->cmdsize, (unsigned int)sizeof(symtableData));
158
159symtableData = kernelData + kernelIndex;
160kernelIndex += sizeof(struct symtab_command);
161
162cmdSize -= sizeof(struct symtab_command);
163
164// Loop through symbol table untill all of the symbols have been found
165
166symbolString = kernelData + symtableData->stroff;
167
168
169UInt16 symbolIndex = 0;
170UInt8 numSymbolsFound = 0;
171
172while(symbolIndex < symtableData->nsyms && numSymbolsFound < NUM_SYMBOLS)// TODO: for loop
173{
174int i = 0;
175while(i < NUM_SYMBOLS)
176{
177if(strcmp(symbolString, kernelSymbols[i]) == 0)
178{
179symbolIndexes[i] = symbolIndex;
180numSymbolsFound++;
181}
182i++;
183
184}
185symbolString += strlen(symbolString) + 1;
186symbolIndex++;
187}
188
189// loop again
190symbolIndex = 0;
191numSymbolsFound = 0;
192while(symbolIndex < symtableData->nsyms && numSymbolsFound < NUM_SYMBOLS)// TODO: for loop
193{
194
195symbolEntry = kernelData + symtableData->symoff + (symbolIndex * sizeof(struct nlist));
196
197int i = 0;
198while(i < NUM_SYMBOLS)
199{
200if(symbolIndex == (symbolIndexes[i] - 4))
201{
202kernelSymbolAddresses[i] = (UInt32)symbolEntry->n_value;
203numSymbolsFound++;
204}
205i++;
206
207}
208
209symbolIndex ++;
210}
211// Load commands should be anded with 0x7FFFFFFF to ignore theLC_REQ_DYLD flag
212} else if((loadCommand->cmd & 0x7FFFFFFF) == LC_SEGMENT)// We only care about the __TEXT segment, any other load command can be ignored
213{
214
215struct segment_command *segCommand;
216
217segCommand = kernelData + kernelIndex;
218
219//printf("Segment name is %s\n", segCommand->segname);
220
221if(strcmp("__TEXT", segCommand->segname) == 0)
222{
223UInt32 sectionIndex;
224
225sectionIndex = sizeof(struct segment_command);
226
227struct section *sect;
228
229while(sectionIndex < segCommand->cmdsize)
230{
231sect = kernelData + kernelIndex + sectionIndex;
232
233sectionIndex += sizeof(struct section);
234
235
236if(strcmp("__text", sect->sectname) == 0)
237{
238// __TEXT,__text found, save the offset and address for when looking for the calls.
239textSection = sect->offset;
240textAddress = sect->addr;
241break;
242}
243}
244}
245
246
247kernelIndex += cmdSize;
248} else {
249kernelIndex += cmdSize;
250}
251}
252
253return KERNEL_32;
254}
255
256
257/**
258 ** Locate the fisrt instance of _panic inside of _cpuid_set_info, and either remove it
259 ** Or replace it so that the cpuid is set to a valid value.
260 **/
261void patch_cpuid_set_info(void* kernelData, UInt32 impersonateFamily, UInt8 impersonateModel)
262{
263UInt8* bytes = (UInt8*)kernelData;
264UInt32 patchLocation = (kernelSymbolAddresses[SYMBOL_CPUID_SET_INFO] - textAddress + textSection);
265UInt32 jumpLocation = 0;
266UInt32 panicAddr = kernelSymbolAddresses[SYMBOL_PANIC] - textAddress;
267if(kernelSymbolAddresses[SYMBOL_CPUID_SET_INFO] == 0)
268{
269printf("Unable to locate _cpuid_set_info\n");
270return;
271
272}
273if(kernelSymbolAddresses[SYMBOL_PANIC] == 0)
274{
275printf("Unable to locate _panic\n");
276return;
277}
278
279//TODO: don't assume it'll always work (Look for *next* function address in symtab and fail once it's been reached)
280while(
281 (bytes[patchLocation -1] != 0xE8) ||
282 ( ( (UInt32)(panicAddr - patchLocation - 4) + textSection ) != (UInt32)((bytes[patchLocation + 0] << 0 |
283bytes[patchLocation + 1] << 8 |
284bytes[patchLocation + 2] << 16 |
285bytes[patchLocation + 3] << 24)))
286 )
287{
288patchLocation++;
289}
290patchLocation--;
291
292
293// Remove panic call, just in case the following patch routines fail
294bytes[patchLocation + 0] = 0x90;
295bytes[patchLocation + 1] = 0x90;
296bytes[patchLocation + 2] = 0x90;
297bytes[patchLocation + 3] = 0x90;
298bytes[patchLocation + 4] = 0x90;
299
300
301// Locate the jump call, so that 10 bytes can be reclamed.
302// NOTE: This will *NOT* be located on pre 10.6.2 kernels
303jumpLocation = patchLocation - 15;
304while((bytes[jumpLocation - 1] != 0x77 ||
305 bytes[jumpLocation] != (patchLocation - jumpLocation - -8)) &&
306 (patchLocation - jumpLocation) < 0xF0)
307{
308jumpLocation--;
309}
310
311printf("Mode: %d Family %d P - JMP: 0x%X\n", impersonateModel, impersonateFamily, patchLocation - jumpLocation);
312// If found... AND we want to impersonate a specific cpumodel / family...
313if(impersonateFamily &&
314 impersonateModel &&
315 ((patchLocation - jumpLocation) < 0xF0))
316{
317printf("Patching CPUID to %d.%d\n", impersonateFamily, impersonateModel);
318
319bytes[jumpLocation] -= 10;// sizeof(movl$0x6b5a4cd2,0x00872eb4) = 10bytes
320
321/*
322 * Inpersonate the specified CPU FAMILY and CPU Model
323 */
324
325// bytes[patchLocation - 17] = 0xC7;// already here... not needed to be done
326// bytes[patchLocation - 16] = 0x05;// see above
327UInt32 cpuid_cpufamily_addr =bytes[patchLocation - 15] << 0 |
328bytes[patchLocation - 14] << 8 |
329bytes[patchLocation - 13] << 16 |
330bytes[patchLocation - 12] << 24;
331
332// NOTE: may change, determined based on cpuid_info struct
333UInt32 cpuid_model_addr = cpuid_cpufamily_addr - 299;
334
335
336// cpufamily = CPUFAMILY_INTEL_PENRYN
337bytes[patchLocation - 11] = (impersonateFamily & 0x000000FF) >> 0;
338bytes[patchLocation - 10] = (impersonateFamily & 0x0000FF00) >> 8;
339bytes[patchLocation - 9] = (impersonateFamily & 0x00FF0000) >> 16;
340bytes[patchLocation - 8] = (impersonateFamily & 0xFF000000) >> 24;
341
342// NOPS, just in case if the jmp call wasn't patched, we'll jump to a
343// nop and continue with the rest of the patch
344// Yay two free bytes :), 10 more can be reclamed if needed, as well as a few
345// from the above code (only cpuid_model needs to be set.
346bytes[patchLocation - 7] = 0x90;
347bytes[patchLocation - 6] = 0x90;
348
349bytes[patchLocation - 5] = 0xC7;
350bytes[patchLocation - 4] = 0x05;
351bytes[patchLocation - 3] = (cpuid_model_addr & 0x000000FF) >> 0;
352bytes[patchLocation - 2] = (cpuid_model_addr & 0x0000FF00) >> 8;
353bytes[patchLocation - 1] = (cpuid_model_addr & 0x00FF0000) >> 16;
354bytes[patchLocation - 0] = (cpuid_model_addr & 0xFF000000) >> 24;
355
356// Note: I could have just copied the 8bit cpuid_model in and saved about 4 bytes
357// so if this function need a different patch it's still possible. Also, about ten bytes previous can be freed.
358bytes[patchLocation + 1] = impersonateModel;// cpuid_model
359bytes[patchLocation + 2] = 0x01;// cpuid_extmodel
360bytes[patchLocation + 3] = 0x00;// cpuid_extfamily
361bytes[patchLocation + 4] = 0x02;// cpuid_stepping
362
363}
364else if(impersonateFamily && impersonateModel)
365{
366// pre 10.6.2 kernel
367// Locate the jump to directly *after* the panic call,
368jumpLocation = patchLocation - 4;
369while((bytes[jumpLocation - 1] != 0x77 ||
370 bytes[jumpLocation] != (patchLocation - jumpLocation + 4)) &&
371 (patchLocation - jumpLocation) < 0x20)
372{
373jumpLocation--;
374}
375// NOTE above isn't needed (I was going to use it, but I'm not, so instead,
376// I'll just leave it to verify the binary stucture.
377
378// NOTE: the cpumodel_familt data is not set in _cpuid_set_info
379// so we don't need to set it here, I'll get set later based on the model
380// we set now.
381
382if((patchLocation - jumpLocation) < 0x20)
383{
384UInt32 cpuid_model_addr =(bytes[patchLocation - 14] << 0 |
385bytes[patchLocation - 13] << 8 |
386bytes[patchLocation - 12] << 16 |
387bytes[patchLocation - 11] << 24);
388// Remove jump
389bytes[patchLocation - 9] = 0x90;/// Was a jump if supported cpu
390bytes[patchLocation - 8] = 0x90;// jumped past the panic call, we want to override the panic
391
392bytes[patchLocation - 7] = 0x90;
393bytes[patchLocation - 6] = 0x90;
394
395bytes[patchLocation - 5] = 0xC7;
396bytes[patchLocation - 4] = 0x05;
397bytes[patchLocation - 3] = (cpuid_model_addr & 0x000000FF) >> 0;
398bytes[patchLocation - 2] = (cpuid_model_addr & 0x0000FF00) >> 8;
399bytes[patchLocation - 1] = (cpuid_model_addr & 0x00FF0000) >> 16;
400bytes[patchLocation - 0] = (cpuid_model_addr & 0xFF000000) >> 24;
401
402// Note: I could have just copied the 8bit cpuid_model in and saved about 4 bytes
403// so if this function need a different patch it's still possible. Also, about ten bytes previous can be freed.
404bytes[patchLocation + 1] = impersonateModel;// cpuid_model
405bytes[patchLocation + 2] = 0x01;// cpuid_extmodel
406bytes[patchLocation + 3] = 0x00;// cpuid_extfamily
407bytes[patchLocation + 4] = 0x02;// cpuid_stepping
408
409
410
411patchLocation = jumpLocation;
412// We now have 14 bytes available for a patch
413
414}
415else
416{
417// Patching failed, using NOP replacement done initialy
418}
419}
420else
421{
422// Either We were unable to change the jump call due to the function's sctructure
423// changing, or the user did not request a patch. As such, resort to just
424// removing the panic call (using NOP replacement above). Note that the
425// IntelCPUPM kext may still panic due to the cpu's Model ID not being patched
426}
427}
428
429
430/**
431 ** SleepEnabler.kext replacement (for those that need it)
432 ** Located the KERN_INVALID_ARGUMENT return and replace it with KERN_SUCCESS
433 **/
434void patch_pmCPUExitHaltToOff(void* kernelData)
435{
436UInt8* bytes = (UInt8*)kernelData;
437UInt32 patchLocation = (kernelSymbolAddresses[SYMBOL_PMCPUEXITHALTTOOFF] - textAddress + textSection);
438
439if(kernelSymbolAddresses[SYMBOL_PMCPUEXITHALTTOOFF] == 0)
440{
441printf("Unable to locate _pmCPUExitHaltToOff\n");
442return;
443}
444
445while(bytes[patchLocation - 1]!= 0xB8 ||
446 bytes[patchLocation]!= 0x04 ||// KERN_INVALID_ARGUMENT (0x00000004)
447 bytes[patchLocation + 1]!= 0x00 ||// KERN_INVALID_ARGUMENT
448 bytes[patchLocation + 2]!= 0x00 ||// KERN_INVALID_ARGUMENT
449 bytes[patchLocation + 3]!= 0x00)// KERN_INVALID_ARGUMENT
450{
451patchLocation++;
452}
453bytes[patchLocation] = 0x00;// KERN_SUCCESS;
454}
455
456void patch_lapic_init(void* kernelData)
457{
458UInt8 panicIndex = 0;
459UInt8* bytes = (UInt8*)kernelData;
460UInt32 patchLocation = (kernelSymbolAddresses[SYMBOL_LAPIC_INIT] - textAddress + textSection);
461UInt32 panicAddr = kernelSymbolAddresses[SYMBOL_PANIC] - textAddress;
462
463if(kernelSymbolAddresses[SYMBOL_LAPIC_INIT] == 0)
464{
465printf("Unable to locate %s\n", SYMBOL_LAPIC_INIT_STRING);
466return;
467
468}
469if(kernelSymbolAddresses[SYMBOL_PANIC] == 0)
470{
471printf("Unable to locate %s\n", SYMBOL_PANIC_STRING);
472return;
473}
474
475
476
477// Locate the (panicIndex + 1) panic call
478while(panicIndex < 3)// Find the third panic call
479{
480while(
481 (bytes[patchLocation -1] != 0xE8) ||
482 ( ( (UInt32)(panicAddr - patchLocation - 4) + textSection ) != (UInt32)((bytes[patchLocation + 0] << 0 |
483bytes[patchLocation + 1] << 8 |
484bytes[patchLocation + 2] << 16 |
485bytes[patchLocation + 3] << 24)))
486 )
487{
488patchLocation++;
489}
490patchLocation++;
491panicIndex++;
492}
493patchLocation--;// Remove extra increment from the < 3 while loop
494
495bytes[--patchLocation] = 0x90;
496bytes[++patchLocation] = 0x90;
497bytes[++patchLocation] = 0x90;
498bytes[++patchLocation] = 0x90;
499bytes[++patchLocation] = 0x90;
500
501
502}
503
504void patch_commpage_stuff_routine(void* kernelData)
505{
506UInt8* bytes = (UInt8*)kernelData;
507UInt32 patchLocation = (kernelSymbolAddresses[SYMBOL_COMMPAGE_STUFF_ROUTINE] - textAddress + textSection);
508UInt32 panicAddr = kernelSymbolAddresses[SYMBOL_PANIC] - textAddress;
509
510if(kernelSymbolAddresses[SYMBOL_COMMPAGE_STUFF_ROUTINE] == 0)
511{
512printf("Unable to locate %s\n", SYMBOL_COMMPAGE_STUFF_ROUTINE_STRING);
513return;
514
515}
516if(kernelSymbolAddresses[SYMBOL_PANIC] == 0)
517{
518printf("Unable to locate %s\n", SYMBOL_PANIC_STRING);
519return;
520}
521
522
523while(
524 (bytes[patchLocation -1] != 0xE8) ||
525 ( ( (UInt32)(panicAddr - patchLocation - 4) + textSection ) != (UInt32)((bytes[patchLocation + 0] << 0 |
526bytes[patchLocation + 1] << 8 |
527bytes[patchLocation + 2] << 16 |
528bytes[patchLocation + 3] << 24)))
529 )
530{
531patchLocation++;
532}
533patchLocation--;
534
535
536// Remove panic call, just in case the following patch routines fail
537bytes[patchLocation + 0] = 0x90;
538bytes[patchLocation + 1] = 0x90;
539bytes[patchLocation + 2] = 0x90;
540bytes[patchLocation + 3] = 0x90;
541bytes[patchLocation + 4] = 0x90;
542
543
544}
545

Archive Download this file

Revision: 296