Index: lib/interception/interception_win.cc =================================================================== --- lib/interception/interception_win.cc +++ lib/interception/interception_win.cc @@ -35,60 +35,88 @@ dst_c[i] = src_c[i]; } -#if SANITIZER_WINDOWS64 -static void WriteIndirectJumpInstruction(char *jmp_from, uptr *indirect_target) { // NOLINT - // jmp [rip + XXYYZZWW] = FF 25 WW ZZ YY XX, where - // XXYYZZWW is an offset from jmp_from. - // The displacement is still 32-bit in x64, so indirect_target must be located - // within +/- 2GB range. - int offset = (int)(indirect_target - (uptr *)jmp_from); - jmp_from[0] = '\xFF'; - jmp_from[1] = '\x25'; - *(int*)(jmp_from + 2) = offset; +bool ChangeMemoryProtection(uptr address, uptr size, DWORD* old_protection) { + return ::VirtualProtect((void *)address, size, + PAGE_EXECUTE_READWRITE, + old_protection) != FALSE; +} + +bool RestoreMemoryProtection(uptr address, uptr size, DWORD old_protection) { + DWORD unused; + return ::VirtualProtect((void *)address, size, + old_protection, + &unused) != FALSE; +} + +static bool FunctionHasPadding(uptr address, uptr size) { + u8* function = (u8*)address; + for (int i = 0; i < size; ++i) + if (function[i] != 0x90 && function[i] != 0xCC) + return false; + return true; } -#else -static void WriteJumpInstruction(char *jmp_from, char *to) { - // jmp XXYYZZWW = E9 WW ZZ YY XX, where XXYYZZWW is an offset from jmp_from - // to the next instruction to the destination. - ptrdiff_t offset = to - jmp_from - 5; - *jmp_from = '\xE9'; - *(ptrdiff_t*)(jmp_from + 1) = offset; + +static void WritePadding(uptr from, uptr size) { + _memset((void*)from, 0xCC, (size_t)size); +} + +static void CopyInstructions(uptr from, uptr to, uptr size) { + _memcpy((void*)from, (void*)to, (size_t)size); +} + +static void WriteJumpInstruction(uptr from, uptr indirect_target) { + // jmp XXYYZZWW = E9 WW ZZ YY XX, where XXYYZZWW is a relative offset. + // The offset is the distance from then end of the jump instruction to the + // targeted instruction. + const int kInstructionLength = 5; + ptrdiff_t offset = indirect_target - from - kInstructionLength; + u8* raw_bytes = (u8*)from; + raw_bytes[0] = 0xE9; + *(u32*)(raw_bytes + 1) = offset; +} + +#if SANITIZER_WINDOWS64 +static void WriteIndirectJumpInstruction(uptr from, uptr target) { + // jmp [rip + XXYYZZWW] = FF 25 WW ZZ YY XX where XXYYZZWW is a relative + // offset. + // The offset is the distance from then end of the jump instruction to the + // memory location containing the targeted address. The displacement is still + // 32-bit in x64, so indirect_target must be located within +/- 2GB range. + const int kInstructionLength = 6; + u8* raw_bytes = (u8*)from; + int offset = target - from - kInstructionLength; + raw_bytes[0] = 0xFF; + raw_bytes[1] = 0x25; + *(u32*)(raw_bytes + 2) = offset; } #endif -static void WriteTrampolineJumpInstruction(char *jmp_from, char *to) { +static void WriteIndirectJumpToTarget(uptr from, uptr indirect_target, uptr target) { #if SANITIZER_WINDOWS64 - // Emit an indirect jump through immediately following bytes: - // jmp_from: - // jmp [rip + 6] - // .quad to // Store the address. - uptr *indirect_target = (uptr *)(jmp_from + 6); - *indirect_target = (uptr)to; + *(u64*)indirect_target = target; + // Write the indirect jump. - WriteIndirectJumpInstruction(jmp_from, indirect_target); + WriteIndirectJumpInstruction(from, indirect_target); #else - WriteJumpInstruction(jmp_from, to); + (void)indirect_target; + WriteJumpInstruction(from, target); #endif } -static void WriteInterceptorJumpInstruction(char *jmp_from, char *to) { +static void WriteDirectJumpInstruction(uptr from, uptr target) { #if SANITIZER_WINDOWS64 // Emit an indirect jump through immediately following bytes: - // jmp_from: - // jmp [rip - 8] - // .quad to - // Store the address. - uptr *indirect_target = (uptr *)(jmp_from - 8); - *indirect_target = (uptr)to; - // Write the indirect jump. - WriteIndirectJumpInstruction(jmp_from, indirect_target); + // jmp [rip + 6] + // .quad + const int kInstructionLength = 6; + WriteIndirectJumpToTarget(from, from + kInstructionLength, target); #else - WriteJumpInstruction(jmp_from, to); + WriteJumpInstruction(from, target); #endif } -static char *GetMemoryForTrampoline(size_t size) { +static uptr GetMemoryForTrampoline(size_t size) { // Trampolines are allocated from a common pool. const int POOL_SIZE = 1024; static char *pool = NULL; @@ -108,11 +136,12 @@ char *ret = pool + pool_used; pool_used += size; - return ret; + return (uptr)ret; } // Returns 0 on error. -static size_t RoundUpToInstrBoundary(size_t size, char *code) { +static size_t RoundUpToInstrBoundary(size_t size, uptr address) { + char* code = (char*)address; #if SANITIZER_WINDOWS64 // Win64 RoundUpToInstrBoundary is a work in progress. size_t cursor = 0; @@ -307,8 +336,6 @@ // wrapper, in this case we need to keep the leading 5+ (6+ on Win64) // bytes ('head') of the original code somewhere with a "jmp ". // We call these 'head'+5/6 bytes of instructions a "trampoline". - char *old_bytes = (char *)old_func; - #if SANITIZER_WINDOWS64 size_t kHeadMin = 6; // The minimum size of the head to contain the 'jmp'. size_t kTrampolineJumpSize = 14; // The total bytes used at the end of @@ -322,43 +349,46 @@ size_t kTrampolineJumpSize = 5; size_t kExtraPrevBytes = 0; #endif - size_t head = kHeadMin; + size_t HeadSize = kHeadMin; + + // Check if the targetd address can be encoded in the function padding. + uptr IndirectAddress = 0; +#if SANITIZER_WINDOWS64 + if (!FunctionHasPadding(old_func, kExtraPrevBytes)) + return false; + IndirectAddress = old_func - kExtraPrevBytes; +#endif + if (orig_old_func) { // Find out the number of bytes of the instructions we need to copy // to the trampoline and store it in 'head'. - head = RoundUpToInstrBoundary(kHeadMin, old_bytes); - if (!head) + HeadSize = RoundUpToInstrBoundary(kHeadMin, old_func); + if (!HeadSize) return false; // Put the needed instructions into the trampoline bytes. - char *trampoline = GetMemoryForTrampoline(head + kTrampolineJumpSize); - if (!trampoline) + uptr Trampoline = GetMemoryForTrampoline(HeadSize + kTrampolineJumpSize); + if (!Trampoline) return false; - _memcpy(trampoline, old_bytes, head); - WriteTrampolineJumpInstruction(trampoline + head, old_bytes + head); - *orig_old_func = (uptr)trampoline; + CopyInstructions(Trampoline, old_func, HeadSize); + WriteDirectJumpInstruction(Trampoline + HeadSize, old_func + HeadSize); + *orig_old_func = Trampoline; } - // Now put the "jmp " instruction at the original code location. - // We should preserve the EXECUTE flag as some of our own code might be - // located in the same page (sic!). FIXME: might consider putting the - // __interception code into a separate section or something? - DWORD old_prot, unused_prot; - // TODO(wwchrome): Properly handle access violations when finding a safe - // region to store the indirect jump target address. - // Need to mark extra 8 bytes for Win64 because jmp [rip -8] - if (!VirtualProtect((void *)(old_bytes - kExtraPrevBytes), - head + kExtraPrevBytes, PAGE_EXECUTE_READWRITE, - &old_prot)) + // Change memory protection to writable. + uptr PatchAddress= old_func - kExtraPrevBytes; + uptr PatchLen = HeadSize + kExtraPrevBytes; + DWORD protection = 0; + if (!ChangeMemoryProtection(PatchAddress, PatchLen, &protection)) return false; - WriteInterceptorJumpInstruction(old_bytes, (char *)new_func); - _memset(old_bytes + kHeadMin, 0xCC /* int 3 */, head - kHeadMin); + // Patch the original function. + WriteIndirectJumpToTarget(old_func, IndirectAddress, new_func); + WritePadding(old_func + kHeadMin, HeadSize - kHeadMin); - // Restore the original permissions. - if (!VirtualProtect((void *)(old_bytes - kExtraPrevBytes), - head + kExtraPrevBytes, old_prot, &unused_prot)) - return false; // not clear if this failure bothers us. + // Restore previous memory protection. + if (!RestoreMemoryProtection(PatchAddress, PatchLen, protection)) + return false; return true; } @@ -461,7 +491,7 @@ RVAPtr dos_stub(module, 0); RVAPtr headers(module, dos_stub->e_lfanew); if (!module || dos_stub->e_magic != IMAGE_DOS_SIGNATURE || // "MZ" - headers->Signature != IMAGE_NT_SIGNATURE || // "PE\0\0" + headers->Signature != IMAGE_NT_SIGNATURE || // "PE\0\0" headers->FileHeader.SizeOfOptionalHeader < sizeof(IMAGE_OPTIONAL_HEADER)) { return false;