#define kOriginalInstructionsSize 16 // On X86 we migh need to instert an add with a 32 bit immediate after the // original instructions. #define kMaxFixupSizeIncrease 5
unsignedchar kIslandTemplate[] = { // kOriginalInstructionsSize nop instructions so that we // should have enough space to host original instructions
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // Now the real jump instruction
0xE9, 0xEF, 0xBE, 0xAD, 0xDE
};
// Optionally allocate & return the reentry island. This may contain relocated // jmp instructions and so has all the same addressing reachability requirements // the escape island has to the original function, except the escape island is // technically our original function.
BranchIsland *reentryIsland = NULL; if( !err && originalFunctionReentryIsland ) {
err = allocateBranchIsland( &reentryIsland, escapeIsland); if( !err )
*originalFunctionReentryIsland = reentryIsland;
}
#ifdefined(__ppc__) || defined(__POWERPC__) // Atomically: // o If the reentry island was allocated: // o Insert the original instruction into the reentry island. // o Target the reentry island at the 2nd instruction of the // original function. // o Replace the original instruction with the branch absolute. if( !err ) { int escapeIslandEngaged = false; do { if( reentryIsland )
err = setBranchIslandTarget( reentryIsland,
(void*) (originalFunctionPtr+1), originalInstruction ); if( !err ) {
escapeIslandEngaged = CompareAndSwap( originalInstruction,
branchAbsoluteInstruction,
(UInt32*)originalFunctionPtr ); if( !escapeIslandEngaged ) { // Someone replaced the instruction out from under us, // re-read the instruction, make sure it's still not // 'mfctr' and try again.
originalInstruction = *originalFunctionPtr; if( (originalInstruction & kMFCTRMask) == kMFCTRInstruction)
err = err_cannot_override;
}
}
} while( !err && !escapeIslandEngaged );
} #elifdefined(__i386__) || defined(__x86_64__) // Atomically: // o If the reentry island was allocated: // o Insert the original instructions into the reentry island. // o Target the reentry island at the first non-replaced // instruction of the original function. // o Replace the original first instructions with the jump relative. // // Note that on i386, we do not support someone else changing the code under our feet if ( !err ) {
uint32_t offset = (uintptr_t)originalFunctionPtr - (uintptr_t)reentryIsland;
fixupInstructions(offset, originalInstructions,
originalInstructionCount, originalInstructionSizes );
// Go to the first page before or after this region
vm_address_t new_address = forward ? address + vmsize : address - kPageSize; #if __WORDSIZE == 64 if(!jump_in_range(original_address, new_address)) break; #endif
address = new_address;
// Try to allocate this page.
kr = vm_allocate(task_self, &address, kPageSize, 0); if (kr == KERN_SUCCESS) {
*island = (BranchIsland*) address; return err_none;
} if (kr != KERN_NO_SPACE) return kr;
}
/******************************************************************************* Implementation: Sets the branch island's target, with an optional instruction.
@param island -> The branch island to insert target into. @param branchTo -> The address of the target. @param instruction -> Optional instruction to execute prior to branch. Set to zero for nop. @result <- mach_error_t
***************************************************************************/ #ifdefined(__ppc__) || defined(__POWERPC__)
mach_error_t
setBranchIslandTarget(
BranchIsland *island, constvoid *branchTo, long instruction )
{ // Copy over the template code.
bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
// Fill in the address.
((short*)island->instructions)[kAddressLo] = ((long) branchTo) & 0x0000FFFF;
((short*)island->instructions)[kAddressHi]
= (((long) branchTo) >> 16) & 0x0000FFFF;
// Fill in the (optional) instuction. if( instruction != 0 ) {
((short*)island->instructions)[kInstructionLo]
= instruction & 0x0000FFFF;
((short*)island->instructions)[kInstructionHi]
= (instruction >> 16) & 0x0000FFFF;
}
// Copy original instructions. if (instructions) {
bcopy (instructions, island->instructions, kOriginalInstructionsSize);
}
// Fill in the address.
*((uint64_t *)(island->instructions + kJumpAddress)) = (uint64_t)branchTo;
msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
return err_none;
} #endif
#ifdefined(__i386__) || defined(__x86_64__) // simplistic instruction matching typedefstruct { unsignedint length; // max 15 unsignedchar mask[15]; // sequence of bytes in memory order unsignedchar constraint[15]; // sequence of bytes in memory order
} AsmInstructionMatch;
match = ((codeValue & mask) == constraint); if (!match) break;
}
return match;
}
#ifdefined(__i386__) || defined(__x86_64__) static Boolean
eatKnownInstructions( unsignedchar *code,
uint64_t *newInstruction, int *howManyEaten, char *originalInstructions, int *originalInstructionCount,
uint8_t *originalInstructionSizes )
{
Boolean allInstructionsKnown = true; int totalEaten = 0; unsignedchar* ptr = code; int remainsToEat = 5; // a JMP instruction takes 5 bytes int instructionIndex = 0;
if (howManyEaten) *howManyEaten = 0; if (originalInstructionCount) *originalInstructionCount = 0; while (remainsToEat > 0) {
Boolean curInstructionKnown = false;
// See if instruction matches one we know
AsmInstructionMatch* curInstr = possibleInstructions; do { if ((curInstructionKnown = codeMatchesInstruction(ptr, curInstr))) break;
curInstr++;
} while (curInstr->length > 0);
// if all instruction matches failed, we don't know current instruction then, stop here if (!curInstructionKnown) {
allInstructionsKnown = false;
fprintf(stderr, "mach_override: some instructions unknown! Need to update mach_override.c\n"); break;
}
// At this point, we've matched curInstr int eaten = curInstr->length;
ptr += eaten;
remainsToEat -= eaten;
totalEaten += eaten;
if (originalInstructionSizes) originalInstructionSizes[instructionIndex] = eaten;
instructionIndex += 1; if (originalInstructionCount) *originalInstructionCount = instructionIndex;
}
if (howManyEaten) *howManyEaten = totalEaten;
if (originalInstructions) {
Boolean enoughSpaceForOriginalInstructions = (totalEaten < kOriginalInstructionsSize);
if (enoughSpaceForOriginalInstructions) {
memset(originalInstructions, 0x90 /* NOP */, kOriginalInstructionsSize); // fill instructions with NOP
bcopy(code, originalInstructions, totalEaten);
} else { // printf ("Not enough space in island to store original instructions. Adapt the island definition and kOriginalInstructionsSize\n"); returnfalse;
}
}
if (allInstructionsKnown) { // save last 3 bytes of first 64bits of codre we'll replace
uint64_t currentFirst64BitsOfCode = *((uint64_t *)code);
currentFirst64BitsOfCode = OSSwapInt64(currentFirst64BitsOfCode); // back to memory representation
currentFirst64BitsOfCode &= 0x0000000000FFFFFFLL;
// keep only last 3 instructions bytes, first 5 will be replaced by JMP instr
*newInstruction &= 0xFFFFFFFFFF000000LL; // clear last 3 bytes
*newInstruction |= (currentFirst64BitsOfCode & 0x0000000000FFFFFFLL); // set last 3 bytes
}
return allInstructionsKnown;
}
staticvoid
fixupInstructions(
uint32_t offset, void *instructionsToFix, int instructionCount,
uint8_t *instructionSizes )
{ // The start of "leaq offset(%rip),%rax" staticconst uint8_t LeaqHeader[] = {0x48, 0x8d, 0x05};
int index; for (index = 0;index < instructionCount;index += 1)
{ if (*(uint8_t*)instructionsToFix == 0xE9) // 32-bit jump relative
{
uint32_t *jumpOffsetPtr = (uint32_t*)((uintptr_t)instructionsToFix + 1);
*jumpOffsetPtr += offset;
}
// 32-bit call relative to the next addr; pop %eax if (*(uint8_t*)instructionsToFix == 0xE8)
{ // Just this call is larger than the jump we use, so we // know this is the last instruction.
assert(index == (instructionCount - 1));
assert(instructionSizes[index] == 6);
// Insert "addl $offset, %eax" in the end so that when // we jump to the rest of the function %eax has the // value it would have if eip had been pushed by the // call in its original position.
uint8_t *op = instructionsToFix;
op += 6;
*op = 0x05; // addl
uint32_t *addImmPtr = (uint32_t*)(op + 1);
*addImmPtr = offset;
}
// atomic push of value to an address // we use cmpxchg8b, which compares content of an address with // edx:eax. If they are equal, it atomically puts 64bit value // ecx:ebx in address. // We thus put contents of address in edx:eax to force ecx:ebx // in address " mov 8(%ebp), %esi;"// esi contains target address " mov 12(%ebp), %ebx;" " mov 16(%ebp), %ecx;"// ecx:ebx now contains value to put in target address " mov (%esi), %eax;" " mov 4(%esi), %edx;"// edx:eax now contains value currently contained in target address " lock; cmpxchg8b (%esi);"// atomic move.
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.