Chameleon

Chameleon Svn Source Tree

Root/branches/meklort/i386/boot2/kernel_patcher.c

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

Archive Download this file

Revision: 158