Bug 1208944 - Part 4. nsWindowsDllInterceptor supports IMM32 API hook. r=ehsan
authorMakoto Kato <m_kato@ga2.so-net.ne.jp>
Tue, 29 Dec 2015 22:57:38 +0900
changeset 315136 2d8290be2a8242a2ddaec51b2b3e7f6e997d6a0f
parent 315135 9c21f80beba9ec2cf30dd7241e17cd9dcc6a3795
child 315137 c25b4ada2dd01caeeebe4e4ec20c94196186b0e6
push id1079
push userjlund@mozilla.com
push dateFri, 15 Apr 2016 21:02:33 +0000
treeherdermozilla-release@575fbf6786d5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan
bugs1208944
milestone46.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1208944 - Part 4. nsWindowsDllInterceptor supports IMM32 API hook. r=ehsan
toolkit/xre/test/win/TestDllInterceptor.cpp
xpcom/build/nsWindowsDllInterceptor.h
--- a/toolkit/xre/test/win/TestDllInterceptor.cpp
+++ b/toolkit/xre/test/win/TestDllInterceptor.cpp
@@ -154,15 +154,18 @@ int main()
       TestHook("kernel32.dll", "SetUnhandledExceptionFilter") &&
 #ifdef _M_IX86
       // Bug 670967: xpcom/base/AvailableMemoryTracker.cpp
       TestHook("kernel32.dll", "VirtualAlloc") &&
       TestHook("kernel32.dll", "MapViewOfFile") &&
       TestHook("gdi32.dll", "CreateDIBSection") &&
       TestHook("kernel32.dll", "CreateFileW") &&
 #endif
+      TestHook("imm32.dll", "ImmGetContext") &&
+      TestHook("imm32.dll", "ImmGetCompositionStringW") &&
+      TestHook("imm32.dll", "ImmSetCandidateWindow") &&
       TestDetour("ntdll.dll", "LdrLoadDll")) {
     printf("TEST-PASS | WindowsDllInterceptor | all checks passed\n");
     return 0;
   }
 
   return 1;
 }
--- a/xpcom/build/nsWindowsDllInterceptor.h
+++ b/xpcom/build/nsWindowsDllInterceptor.h
@@ -94,17 +94,17 @@ private:
   size_t const mSize;
   DWORD const mNewProtect;
   DWORD mOldProtect;
   bool mSuccess;
 };
 
 class WindowsDllNopSpacePatcher
 {
-  typedef unsigned char* byteptr_t;
+  typedef uint8_t* byteptr_t;
   HMODULE mModule;
 
   // Dumb array for remembering the addresses of functions we've patched.
   // (This should be nsTArray, but non-XPCOM code uses this class.)
   static const size_t maxPatchedFns = 128;
   byteptr_t mPatchedFns[maxPatchedFns];
   int mPatchedFnsLen;
 
@@ -366,31 +366,91 @@ protected:
   const static int kPageSize = 4096;
   const static int kHookSize = 128;
 
   HMODULE mModule;
   byteptr_t mHookPage;
   int mMaxHooks;
   int mCurHooks;
 
+#if defined(_M_X64)
+  // To patch for JMP and JE
+
+  enum JumpType {
+   Je,
+   Jmp
+  };
+
+  struct JumpPatch {
+    JumpPatch()
+      : mHookOffset(0), mJumpAddress(0), mType(JumpType::Jmp)
+    {
+    }
+
+    JumpPatch(size_t aOffset, intptr_t aAddress, JumpType aType = JumpType::Jmp)
+      : mHookOffset(aOffset), mJumpAddress(aAddress), mType(aType)
+    {
+    }
+
+    void AddJumpPatch(size_t aHookOffset, intptr_t aAbsJumpAddress,
+                     JumpType aType = JumpType::Jmp)
+    {
+      mHookOffset = aHookOffset;
+      mJumpAddress = aAbsJumpAddress;
+      mType = aType;
+    }
+
+    size_t GenerateJump(uint8_t* aCode)
+    {
+      size_t offset = mHookOffset;
+      if (mType == JumpType::Je) {
+        // JNE RIP+14
+        aCode[offset]     = 0x75;
+        aCode[offset + 1] = 14;
+        offset += 2;
+      }
+
+      // JMP [RIP+0]
+      aCode[offset] = 0xff;
+      aCode[offset + 1] = 0x25;
+      *reinterpret_cast<int32_t*>(aCode + offset + 2) = 0;
+
+      // Jump table
+      *reinterpret_cast<int64_t*>(aCode + offset + 2 + 4) = mJumpAddress;
+
+      return offset + 2 + 4 + 8;
+    }
+
+    bool HasJumpPatch() const
+    {
+      return !!mJumpAddress;
+    }
+
+    size_t mHookOffset;
+    intptr_t mJumpAddress;
+    JumpType mType;
+  };
+
+#endif
+
   void CreateTrampoline(void* aOrigFunction, intptr_t aDest, void** aOutTramp)
   {
     *aOutTramp = nullptr;
 
     byteptr_t tramp = FindTrampolineSpace();
     if (!tramp) {
       return;
     }
 
     byteptr_t origBytes = (byteptr_t)aOrigFunction;
 
     int nBytes = 0;
-    int pJmp32 = -1;
 
 #if defined(_M_IX86)
+    int pJmp32 = -1;
     while (nBytes < 5) {
       // Understand some simple instructions that might be found in a
       // prologue; we might need to extend this as necessary.
       //
       // Note!  If we ever need to understand jump instructions, we'll
       // need to rewrite the displacement argument.
       if (origBytes[nBytes] >= 0x88 && origBytes[nBytes] <= 0x8B) {
         // various MOVs
@@ -442,22 +502,22 @@ protected:
         // jmp [disp32]
         nBytes += 6;
       } else {
         //printf ("Unknown x86 instruction byte 0x%02x, aborting trampoline\n", origBytes[nBytes]);
         return;
       }
     }
 #elif defined(_M_X64)
-    byteptr_t directJmpAddr;
+    JumpPatch jump;
 
     while (nBytes < 13) {
 
       // if found JMP 32bit offset, next bytes must be NOP or INT3
-      if (pJmp32 >= 0) {
+      if (jump.HasJumpPatch()) {
         if (origBytes[nBytes] == 0x90 || origBytes[nBytes] == 0xcc) {
           nBytes++;
           continue;
         }
         return;
       }
       if (origBytes[nBytes] == 0x0f) {
         nBytes++;
@@ -468,16 +528,25 @@ protected:
               (origBytes[nBytes] & 0x7) == 0x04) {
             nBytes += 3;
           } else {
             return;
           }
         } else if (origBytes[nBytes] == 0x05) {
           // syscall
           nBytes++;
+        } else if (origBytes[nBytes] == 0x84) {
+          // je rel32
+          jump.AddJumpPatch(nBytes - 1,
+                            (intptr_t)
+                              origBytes + nBytes + 5 +
+                            *(reinterpret_cast<int32_t*>(origBytes +
+                                                         nBytes + 1)),
+                            JumpType::Je);
+          nBytes += 5;
         } else {
           return;
         }
       } else if (origBytes[nBytes] == 0x40 ||
                  origBytes[nBytes] == 0x41) {
         // Plain REX or REX.B
         nBytes++;
 
@@ -511,16 +580,23 @@ protected:
         } else if (origBytes[nBytes] == 0x83 &&
                    (origBytes[nBytes + 1] & 0xf8) == 0xe8) {
           // sub r, byte
           nBytes += 3;
         } else if (origBytes[nBytes] == 0x83 &&
                    (origBytes[nBytes + 1] & 0xf8) == 0x60) {
           // and [r+d], imm8
           nBytes += 5;
+        } else if (origBytes[nBytes] == 0x85) {
+          // 85 /r => TEST r/m32, r32
+          if ((origBytes[nBytes + 1] & 0xc0) == 0xc0) {
+            nBytes += 2;
+          } else {
+            return;
+          }
         } else if ((origBytes[nBytes] & 0xfd) == 0x89) {
           // MOV r/m64, r64 | MOV r64, r/m64
           if ((origBytes[nBytes + 1] & 0xc0) == 0x40) {
             if ((origBytes[nBytes + 1] & 0x7) == 0x04) {
               // R/M=[SIB+disp8], REG=r64
               nBytes += 4;
             } else {
               // R/M=[r64+disp8], REG=r64
@@ -541,25 +617,26 @@ protected:
           if (origBytes[nBytes + 1] == 0x44) {
             // MOV [r64+disp8], imm32
             // ModR/W + SIB + disp8 + imm32
             nBytes += 8;
           } else {
             return;
           }
         } else if (origBytes[nBytes] == 0xff) {
-          pJmp32 = nBytes - 1;
           // JMP /4
           if ((origBytes[nBytes + 1] & 0xc0) == 0x0 &&
               (origBytes[nBytes + 1] & 0x07) == 0x5) {
             // [rip+disp32]
             // convert JMP 32bit offset to JMP 64bit direct
-            directJmpAddr =
-              (byteptr_t)*((uint64_t*)(origBytes + nBytes + 6 +
-                                       (*((int32_t*)(origBytes + nBytes + 2)))));
+            jump.AddJumpPatch(nBytes - 1,
+                              *reinterpret_cast<intptr_t*>(
+                                origBytes + nBytes + 6 +
+                              *reinterpret_cast<int32_t*>(origBytes + nBytes +
+                                                          2)));
             nBytes += 6;
           } else {
             // not support yet!
             return;
           }
         } else {
           // not support yet!
           return;
@@ -571,21 +648,26 @@ protected:
         // nop
         nBytes++;
       } else if (origBytes[nBytes] == 0xb8) {
         // MOV 0xB8: http://ref.x86asm.net/coder32.html#xB8
         nBytes += 5;
       } else if (origBytes[nBytes] == 0xc3) {
         // ret
         nBytes++;
+      } else if (origBytes[nBytes] == 0xcc) {
+        // int 3
+        nBytes++;
       } else if (origBytes[nBytes] == 0xe9) {
-        pJmp32 = nBytes;
-        // convert JMP 32bit offset to JMP 64bit direct
-        directJmpAddr = origBytes + pJmp32 + 5 + (*((int32_t*)(origBytes + pJmp32 + 1)));
         // jmp 32bit offset
+        jump.AddJumpPatch(nBytes,
+                          // convert JMP 32bit offset to JMP 64bit direct
+                          (intptr_t)
+                            origBytes + nBytes + 5 +
+                          *(reinterpret_cast<int32_t*>(origBytes + nBytes + 1)));
         nBytes += 5;
       } else if (origBytes[nBytes] == 0xff) {
         nBytes++;
         if ((origBytes[nBytes] & 0xf8) == 0xf0) {
           // push r64
           nBytes++;
         } else {
           return;
@@ -620,37 +702,26 @@ protected:
       // Adjust jump target displacement to jump location in the trampoline.
       *((intptr_t*)(tramp + pJmp32 + 1)) += origBytes - tramp;
     } else {
       tramp[nBytes] = 0xE9; // jmp
       *((intptr_t*)(tramp + nBytes + 1)) =
         (intptr_t)trampDest - (intptr_t)(tramp + nBytes + 5); // target displacement
     }
 #elif defined(_M_X64)
-    // If JMP32 opcode found, we don't insert to trampoline jump
-    if (pJmp32 >= 0) {
-      // mov r11, address
-      tramp[pJmp32]   = 0x49;
-      tramp[pJmp32 + 1] = 0xbb;
-      *((intptr_t*)(tramp + pJmp32 + 2)) = (intptr_t)directJmpAddr;
-
-      // jmp r11
-      tramp[pJmp32 + 10] = 0x41;
-      tramp[pJmp32 + 11] = 0xff;
-      tramp[pJmp32 + 12] = 0xe3;
+    // If JMP/JE opcode found, we don't insert to trampoline jump
+    if (jump.HasJumpPatch()) {
+      size_t offset = jump.GenerateJump(tramp);
+      if (jump.mType != JumpType::Jmp) {
+        JumpPatch patch(offset, reinterpret_cast<intptr_t>(trampDest));
+        patch.GenerateJump(tramp);
+      }
     } else {
-      // mov r11, address
-      tramp[nBytes] = 0x49;
-      tramp[nBytes + 1] = 0xbb;
-      *((intptr_t*)(tramp + nBytes + 2)) = (intptr_t)trampDest;
-
-      // jmp r11
-      tramp[nBytes + 10] = 0x41;
-      tramp[nBytes + 11] = 0xff;
-      tramp[nBytes + 12] = 0xe3;
+      JumpPatch patch(nBytes, reinterpret_cast<intptr_t>(trampDest));
+      patch.GenerateJump(tramp);
     }
 #endif
 
     // The trampoline is now valid.
     *aOutTramp = tramp;
 
     // ensure we can modify the original code
     AutoVirtualProtect protect(aOrigFunction, nBytes, PAGE_EXECUTE_READWRITE);