/* * Initializes the program counter and returns the number of instructions. */ void mapProgram(FILE* program) { Seq_T words = Seq_new(PROGRAM_HINT); int c = getc(program); while(c != EOF) { UM_Word temp = 0; temp = Bitpack_newu(temp, 8, 24, c); for(int bit = 16; bit >=0; bit -=8){ int b = getc(program); temp = Bitpack_newu(temp, 8, bit, b); } UM_Word* instr; NEW(instr); *instr = temp; Seq_addhi(words, instr); c = getc(program); } mapSegment(memorySegments, 0); mapInstructions(Seq_length(words)); int length = instructionLength(); for(int locToLoad = 0; locToLoad < length; locToLoad++){ UM_Word* value = (UM_Word*)Seq_get(words, locToLoad); loadInstruction(locToLoad, *value); FREE(value); } Seq_free(&words); }
/* * Creates memeory for the program to run and gets the instruction code along * with the registers. */ void build_and_execute_um(FILE* program){ memorySegments = newMem(); instantiateMem(memorySegments, INITIAL_SET_SIZE); initializeRegisters(registers, numRegisters); mapProgram(program); programCounter = 0; numInstructions = instructionLength(); //programPointer = getInstructions(memorySegments); while(programCounter < numInstructions){ UM_Word instruction = getInstruction(programCounter); Instruction instr = parseInstruction(instruction); execute_instruction(instr); if(instr.op == HALT) break; } freeMem(memorySegments); }
BOOL InterceptGlobalFunction(FARPROC dwAddressToIntercept, FARPROC dwReplaced, FARPROC dwTrampoline, bool trampolineOnly, BOOL rvOnSkip) { ENTER(dwAddressToIntercept, dwReplaced, dwTrampoline, trampolineOnly, rvOnSkip); if(!dwAddressToIntercept) return FALSE; enum { JMP_REL32 = 0xE9 }; BYTE* pTargetHead = (BYTE*)dwAddressToIntercept; BYTE* pTargetTail = pTargetHead; BYTE* pTramp = (BYTE*)dwTrampoline; BYTE* pHook = (BYTE*)dwReplaced; // trampolines work by running a copy of the first few bytes of the target function // and then jumping to the remainder of the target function. // this totally breaks down if the first few bytes of the target function is a jump. // so, the first thing we do is detect if the target starts with a jump, // and if so, we follow where the jump goes instead of hooking the target directly. //if(tasflags.movieVersion >= 65) //{ for(int i = 0; *pTargetTail == JMP_REL32; i++) { int diff = *(DWORD*)(pTargetTail+1) + 5; pTargetTail += diff; if(pTargetTail == pHook) { if(i == 0) LOG() << "already hooked. skipping."; else LOG() << "already hooked (from " << pTargetTail - diff << " to " << pTargetTail << ") although the hook chain is longer than expected. skipping."; return rvOnSkip; } else { if(i < 64) LOG() << "rejump detected in target (from " << pTargetTail - diff << " to " << pTargetTail << "). following chain..."; else { LOG() << "hook chain is too long. target function jumps to itself? skipping."; return rvOnSkip; } } } if(pTargetHead == pHook || pTramp == pHook || pTargetHead == pTramp) { LOG() << "bad input? target=" << pTargetHead << ", hook=" << pHook << ", tramp=" << pTramp << ". skipping hook."; return rvOnSkip; } //} /*else // old version. if avast! is installed, this results in an incorrect trampoline which will crash the game. { if(*pTargetHead == JMP_REL32) { int diff = *(DWORD*)(pTargetHead+1) + 5; pTargetHead += diff; debugprintf("rejump detected (from 0x%X to 0x%X). attempting to remove...\n", dwAddressToIntercept, pTargetHead); dwAddressToIntercept = (FARPROC)pTargetHead; } pTargetTail = pTargetHead; if(pTargetHead == pHook) { debugprintf("already hooked (0x%X). skipping.\n", pTargetHead); return rvOnSkip; } }*/ LOG() << "want to hook " << pTargetHead << " to call " << pHook << ", and want trampoline " << pTramp << " to call " << pTargetTail; if(*pTramp == JMP_REL32) { int diff = *(DWORD*)(pTramp+1) + 5; LOG() << "rejump detected in trampoline (from " << pTramp << " to " << pTramp + diff << ". giving up."; return rvOnSkip; } // we'll have to overwrite 5 bytes, which means we have to backup at least 5 bytes (up to next instruction boundary) int offset = 0; while(offset < 5) offset += instructionLength(pTargetTail + offset); DWORD dwOldProtect; // in the trampoline, write the first 5+ bytes of the target function followed by a jump to our hook VirtualProtect((void*)dwTrampoline, 5+offset, PAGE_EXECUTE_READWRITE, &dwOldProtect); for(int i=0; i<offset; i++) *pTramp++ = *pTargetTail++; *pTramp++ = JMP_REL32; *((int*)pTramp) = (int)(pTargetTail - (pTramp + 4)); VirtualProtect((void*)dwTrampoline, 5+offset, PAGE_EXECUTE, &dwOldProtect); if(!trampolineOnly) { // overwrite the first 5 bytes of the target function with a jump to our hook VirtualProtect((void*)dwAddressToIntercept, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect); *pTargetHead++ = JMP_REL32; *((signed int *)(pTargetHead)) = (int)(pHook - (pTargetHead +4)); VirtualProtect((void*)dwAddressToIntercept, 5, PAGE_EXECUTE, &dwOldProtect); } // flush the instruction cache to make sure the modified code is executed FlushInstructionCache(GetCurrentProcess(), NULL, NULL); return TRUE; }
/* * Executes the Instruction based on the opcode. */ static inline void execute_instruction(Instruction instr){ switch(instr.op) { case MOVE:{ conditionalMove(registers, instr.reg1, instr.reg2, instr.reg3); programCounter++; break; } case SEGLOAD:{ UM_Word ID = registers[instr.reg2]; UM_Word offset = registers[instr.reg3]; UM_Word toStore = segmentedLoad(memorySegments, ID, offset); registers[instr.reg1] = toStore; programCounter++; break; } case SEGSTORE:{ UM_Word ID = registers[instr.reg1]; UM_Word offset = registers[instr.reg2]; UM_Word value = registers[instr.reg3]; segmentedStore(memorySegments, ID, offset, value); programCounter++; break; } case ADD:{ addition(registers, instr.reg1, instr.reg2, instr.reg3); programCounter++; break; } case MULTIPLY:{ multiplication(registers, instr.reg1, instr.reg2, instr.reg3); programCounter++; break; } case DIVIDE:{ division(registers, instr.reg1, instr.reg2, instr.reg3); programCounter++; break; } case NAND:{ bitwiseNAND(registers, instr.reg1, instr.reg2, instr.reg3); programCounter++; break; } case HALT: { programCounter = 0; break; } case MAP:{ UM_Word length = registers[instr.reg3]; registers[instr.reg2] = mapSegment(memorySegments, length); programCounter++; break; } case UNMAP:{ UM_Word ID = registers[instr.reg3]; unmapSegment(memorySegments, ID); programCounter++; break; } case OUTPUT:{ output(registers, instr.reg3); programCounter++; break; } case INPUT:{ input(registers, instr.reg3); programCounter++; break; } case LOADPROG:{ UM_Word ID = registers[instr.reg2]; if(ID != 0){ loadProgram(memorySegments, ID); numInstructions = instructionLength(memorySegments); //programPointer = getInstructions(memorySegments); } programCounter = registers[instr.reg3]; break; } case LOADVAL:{ registers[instr.reg1] = instr.value; programCounter++; break; } } }