Chameleon

Chameleon Svn Source Tree

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

Archive Download this file

Revision: 709