Exemplo n.º 1
0
// Checks if the thread is executing in a context of PatchGuard. Returns an
// address to overwrite a guest IP if the is the case. Otherwise, returns 0.
_Use_decl_annotations_ ULONG_PTR
GMonCheckExecutionContext(const GpRegisters *registers, ULONG_PTR guest_ip) {
  const auto pg_context =
      reinterpret_cast<PgContext *>(GMonIsPgExcutionContext(registers));
  if (!pg_context) {
    return 0;
  }
  HYPERPLATFORM_LOG_INFO_SAFE("PatchGuard Context = %p", pg_context);

  // An epilogue of Pg_xSelfValidation(). Now the thread can be executing a DPC
  // function Pg_xSelfValidation(). In that case, we need to return from it
  // safely.
  //
  // 48 8B C3                 mov     rax, rbx        ; Windows 7
  // 48 8B C7                 mov     rax, rdi        ; Windows 8.1 and 10
  // 4C 8D 9C 24 C0 02 00 00  lea     r11, [rsp+2C0h]
  // 49 8B 5B 30              mov     rbx, [r11+30h]
  // 49 8B 73 38              mov     rsi, [r11+38h]
  // 49 8B 7B 40              mov     rdi, [r11+40h]
  // 49 8B E3                 mov     rsp, r11
  // 41 5F                    pop     r15
  // 41 5E                    pop     r14
  // 41 5D                    pop     r13
  // 41 5C                    pop     r12
  // 5D                       pop     rbp
  // C3                       retn
  static const UCHAR kDpcEpiloguePattern8_10[] = {
      0x48, 0x8B, 0xC7, 0x4C, 0x8D, 0x9C, 0x24, 0xC0, 0x02, 0x00, 0x00, 0x49,
      0x8B, 0x5B, 0x30, 0x49, 0x8B, 0x73, 0x38, 0x49, 0x8B, 0x7B, 0x40, 0x49,
      0x8B, 0xE3, 0x41, 0x5F, 0x41, 0x5E, 0x41, 0x5D, 0x41, 0x5C, 0x5D, 0xC3,
  };
  static const UCHAR kDpcEpiloguePattern7[] = {
      0x48, 0x8B, 0xC3, 0x4C, 0x8D, 0x9C, 0x24, 0xC0, 0x02, 0x00, 0x00, 0x49,
      0x8B, 0x5B, 0x30, 0x49, 0x8B, 0x73, 0x38, 0x49, 0x8B, 0x7B, 0x40, 0x49,
      0x8B, 0xE3, 0x41, 0x5F, 0x41, 0x5E, 0x41, 0x5D, 0x41, 0x5C, 0x5D, 0xC3,
  };

  // Try Win8 and 10 pattern
  auto epilogue_address =
      UtilMemMem(reinterpret_cast<void *>(guest_ip), 0x400,
                 kDpcEpiloguePattern8_10, sizeof(kDpcEpiloguePattern8_10));
  if (!epilogue_address) {
    // Try Win7 pattern if failed
    epilogue_address =
        UtilMemMem(reinterpret_cast<void *>(guest_ip), 0x400,
                   kDpcEpiloguePattern7, sizeof(kDpcEpiloguePattern7));
  }
  HYPERPLATFORM_LOG_INFO_SAFE("DPC Epilogue Address = %p", epilogue_address);

  if (epilogue_address) {
    // Executing Pg_xSelfValidation(). Set a return address to its epilogue and
    // neutralize the context.
    GMonpNeutralizePgContextForDpc(pg_context);
    return reinterpret_cast<ULONG_PTR>(epilogue_address);
  } else {
    // Not. Likely to be in a main validation routine. Let the thread return to
    // AsmWaitForever().
    return reinterpret_cast<ULONG_PTR>(AsmWaitForever);
  }
}
Exemplo n.º 2
0
// Locate MmPfnDatabase
_Use_decl_annotations_ static NTSTATUS MmonpInitializeMmPfnDatabase() {
  PAGED_CODE();

  RTL_OSVERSIONINFOW os_version = {};
  auto status = RtlGetVersion(&os_version);
  if (!NT_SUCCESS(status)) {
    return status;
  }

  // Set appropriate patterns and based on an OS version
  struct MmPfnDatabaseSearchPattern {
    const UCHAR *bytes;
    SIZE_T bytes_size;
    bool hard_coded;
  };
  MmPfnDatabaseSearchPattern patterns[2] = {};

  if (IsX64()) {
    // Win 10 build 14316 is the first version implements randomized page tables
    if (os_version.dwMajorVersion < 10 || os_version.dwBuildNumber < 14316) {
      // PFN database is at the constant location on older x64 Windows
      g_mmonp_MmPfnDatabase = reinterpret_cast<void *>(0xfffffa8000000000);
      return STATUS_SUCCESS;
    }

    // Windows 10 x64 Build 14332+
    static const UCHAR kPatternWin10x64[] = {
        0x48, 0x8B, 0xC1,        // mov     rax, rcx
        0x48, 0xC1, 0xE8, 0x0C,  // shr     rax, 0Ch
        0x48, 0x8D, 0x14, 0x40,  // lea     rdx, [rax + rax * 2]
        0x48, 0x03, 0xD2,        // add     rdx, rdx
        0x48, 0xB8,              // mov     rax, 0FFFFFA8000000008h
    };
    patterns[0].bytes = kPatternWin10x64;
    patterns[0].bytes_size = sizeof(kPatternWin10x64);
    patterns[0].hard_coded = true;

  } else {
    // x86
    if (os_version.dwMajorVersion == 6 && os_version.dwMinorVersion == 1) {
      // Windows 7 (No PAE)
      static const UCHAR kPatternWin7[] = {
          0x6B, 0xC0, 0x18,  // imul    eax, 18h
          0x8B, 0x0D,        // mov     ecx, ds:_MmPfnDatabase
      };
      // Windows 7 (PAE)
      static const UCHAR kPatternWin7Pae[] = {
          0x6B, 0xC0, 0x1C,  // imul    eax, 1Ch
          0x8B, 0x0D,        // mov     ecx, ds:_MmPfnDatabase
      };

      if (UtilIsX86Pae()) {
        patterns[0].bytes = kPatternWin7Pae;
        patterns[0].bytes_size = sizeof(kPatternWin7Pae);
        patterns[0].hard_coded = false;
      } else {
        patterns[0].bytes = kPatternWin7;
        patterns[0].bytes_size = sizeof(kPatternWin7);
        patterns[0].hard_coded = false;
      }

    } else if ((os_version.dwMajorVersion == 6 &&
                os_version.dwMinorVersion == 3) ||
               (os_version.dwMajorVersion == 10 &&
                os_version.dwMinorVersion == 0)) {
      // Windows 8.1 and 10
      static const UCHAR kPatternWin81And10_0[] = {
          0xC1, 0xF8, 0x0C,  // sar     eax, 0Ch
          0xA1,              // mov     eax, ds:_MmPfnDatabase
      };
      static const UCHAR kPatternWin81And10_1[] = {
          0xC1, 0xE8, 0x0C,  // shr     eax, 0Ch
          0xA1,              // mov     eax, ds:_MmPfnDatabase
      };
      patterns[0].bytes = kPatternWin81And10_0;
      patterns[0].bytes_size = sizeof(kPatternWin81And10_0);
      patterns[0].hard_coded = false;
      patterns[1].bytes = kPatternWin81And10_1;
      patterns[1].bytes_size = sizeof(kPatternWin81And10_1);
      patterns[1].hard_coded = false;

    } else {
      // Unknown x86 OS version
      return STATUS_UNSUCCESSFUL;
    }
  }

  // Search the patterns from MmGetVirtualForPhysical
  const auto p_MmGetVirtualForPhysical = reinterpret_cast<UCHAR *>(
      UtilGetSystemProcAddress(L"MmGetVirtualForPhysical"));
  if (!p_MmGetVirtualForPhysical) {
    return STATUS_PROCEDURE_NOT_FOUND;
  }

  for (const auto &pattern : patterns) {
    if (!pattern.bytes) {
      break;  // no more patterns
    }

    auto found = reinterpret_cast<UCHAR *>(UtilMemMem(
        p_MmGetVirtualForPhysical, 0x20, pattern.bytes, pattern.bytes_size));
    if (!found) {
      continue;
    }

    // Get an address of PFN database
    found += pattern.bytes_size;
    if (pattern.hard_coded) {
      HYPERPLATFORM_LOG_DEBUG("Found a hard coded PFN database address at %p",
                              found);
      g_mmonp_MmPfnDatabase = *reinterpret_cast<void **>(found);
    } else {
      HYPERPLATFORM_LOG_DEBUG("Found a reference to MmPfnDatabase at %p",
                              found);
      const auto mmpfn_address = *reinterpret_cast<ULONG_PTR *>(found);
      g_mmonp_MmPfnDatabase = *reinterpret_cast<void **>(mmpfn_address);
    }

    // On Windows 10 RS, a value has 0x8. Delete it.
    g_mmonp_MmPfnDatabase = PAGE_ALIGN(g_mmonp_MmPfnDatabase);
    break;
  }

  HYPERPLATFORM_LOG_DEBUG("MmPfnDatabase = %p", g_mmonp_MmPfnDatabase);
  if (!g_mmonp_MmPfnDatabase) {
    return STATUS_UNSUCCESSFUL;
  }

  return STATUS_SUCCESS;
}
Exemplo n.º 3
0
// Locate MmPfnDatabase
_Use_decl_annotations_ static NTSTATUS MmonpInitializeMmPfnDatabase() {
  PAGED_CODE();

  if (IsX64()) {
    g_mmonp_MmPfnDatabase = reinterpret_cast<void *>(0xfffffa8000000000);
  } else {
    const auto p_MmGetVirtualForPhysical = reinterpret_cast<UCHAR *>(
        UtilGetSystemProcAddress(L"MmGetVirtualForPhysical"));
    if (!p_MmGetVirtualForPhysical) {
      return STATUS_PROCEDURE_NOT_FOUND;
    }

    RTL_OSVERSIONINFOW os_version = {};
    auto status = RtlGetVersion(&os_version);
    if (!NT_SUCCESS(status)) {
      return status;
    }
    if (os_version.dwMajorVersion == 6 && os_version.dwMinorVersion == 1) {
      // Windows 7 (No PAE)
      // 6B C0 18                          imul    eax, 18h
      // 8B 0D 14 28 56 00                 mov     ecx, ds:_MmPfnDatabase
      static const UCHAR kPatternWin7[] = {
          0x6B, 0xC0, 0x18, 0x8B, 0x0D,
      };
      // Windows 7 (PAE)
      // 6B C0 1C                          imul    eax, 1Ch
      // 8B 0D 14 28 56 00                 mov     ecx, ds:_MmPfnDatabase
      static const UCHAR kPatternWin7Pae[] = {
          0x6B, 0xC0, 0x1C, 0x8B, 0x0D,
      };
      const auto is_pae_enabled = Cr4{__readcr4()}.fields.pae;
      const auto pattern = (is_pae_enabled) ? kPatternWin7Pae : kPatternWin7;
      const auto size =
          (is_pae_enabled) ? sizeof(kPatternWin7Pae) : sizeof(kPatternWin7);
      auto found = reinterpret_cast<UCHAR *>(
          UtilMemMem(p_MmGetVirtualForPhysical, 0x20, pattern, size));
      if (found) {
        found += size;
        const auto address = *reinterpret_cast<ULONG_PTR *>(found);
        g_mmonp_MmPfnDatabase = *reinterpret_cast<void **>(address);
      }
    } else if ((os_version.dwMajorVersion == 6 &&
                os_version.dwMinorVersion == 3) ||
               (os_version.dwMajorVersion == 10 &&
                os_version.dwMinorVersion == 0)) {
      // Windows 8.1 and 10
      // C1 F8 0C                          sar     eax, 0Ch
      // A1 08 B7 62 00                    mov     eax, ds:_MmPfnDatabase
      static const UCHAR kPatternWin81And10[] = {
          0xC1, 0xF8, 0x0C, 0xA1,
      };
      auto found = reinterpret_cast<UCHAR *>(
          UtilMemMem(p_MmGetVirtualForPhysical, 0x20, kPatternWin81And10,
                     sizeof(kPatternWin81And10)));
      if (found) {
        found += sizeof(kPatternWin81And10);
        const auto address = *reinterpret_cast<ULONG_PTR *>(found);
        g_mmonp_MmPfnDatabase = *reinterpret_cast<void **>(address);
      }
    }
  }
  return (g_mmonp_MmPfnDatabase) ? STATUS_SUCCESS : STATUS_PROCEDURE_NOT_FOUND;
}
Exemplo n.º 4
0
_Use_decl_annotations_ EXTERN_C EpilogueInfo
FnparseGetEpilogueInfo(UCHAR *FunctionAddress) {
  PAGED_CODE();

  // This function presumes byte codes of the function's epilogue from unwind
  // information, then searches it in the range of the function in order to
  // find epilogue code. Apart from that, it calculates unwind stack size from
  // unwind information too.

  UCHAR *base = nullptr;
  auto entry = FnparseLookupFunctionEntry(
      UtilDataToFp(reinterpret_cast<UCHAR *>(FunctionAddress)),
      reinterpret_cast<void **>(&base));
  if (!entry) {
    return {};
  }

  // Do not suppose anything has value in Flags for the sake of ease of
  // implementation and testing.
  auto unwind =
      reinterpret_cast<UNWIND_INFO *>(entry->UnwindInfoAddress + base);
  if (unwind->Flags != UNW_FLAG_NHANDLER) {
    return {};
  }

  // Allocates a big enough memory to save epilogue byte code.
  auto epilogueBytesAllocationSize = unwind->SizeOfProlog + 1;  // +1 for ret.
  auto epilogueBytesNaked = reinterpret_cast<BYTE *>(ExAllocatePoolWithTag(
      PagedPool, epilogueBytesAllocationSize, MEOW_POOL_TAG_NAME));
  if (!epilogueBytesNaked) {
    return {};
  }
  auto epilogueBytesCleaner =
      std::experimental::make_scope_exit([epilogueBytesNaked]() {
        ExFreePoolWithTag(epilogueBytesNaked, MEOW_POOL_TAG_NAME);
      });
  memset(epilogueBytesNaked, 0, epilogueBytesAllocationSize);

  // Enumerates all unwind operation codes.
  auto current = epilogueBytesNaked;  // A current position in epilogueBytes.
  auto stackSize = 0ul;
  auto isExpectingAllocSmall = true;
  for (auto i = 0ul; i < unwind->CountOfCodes; ++i) {
    const auto &code = unwind->UnwindCode[i];

    if (isExpectingAllocSmall) {
      // Ignore until UWOP_ALLOC_SMALL shows up first.
      if (code.UnwindOp == UWOP_ALLOC_SMALL) {
        current = FnparsepInterpretAllocSmallOp(code.OpInfo, current);
        stackSize += (code.OpInfo * 8) + 8;
        isExpectingAllocSmall = false;
      }
    } else {
      // Once UWOP_ALLOC_SMALL was processed, it interprets necessary operation
      // codes.
      switch (code.UnwindOp) {
        case UWOP_PUSH_NONVOL:
          current = FnparsepInterpretPushNonVolOp(code.OpInfo, current);
          stackSize += 8;
          break;
        default:
          return {};  // Error. Unexpected operation code.
      }
    }

    // Error. Opcode interpretation failed.
    if (!current) {
      return {};
    }
  }

  // Copy a ret instruction at the end.
  memcpy(current, &EpilogueOpcode::Retn, sizeof(EpilogueOpcode::Retn));
  current++;

  // Get an actual epilogue size.
  const auto epilogueSize = current - epilogueBytesNaked;

  // Get a function length.
  const auto length = FnparseGetFunctionLength(FunctionAddress);
  if (!length) {
    return {};
  }

  // Find an address of epilogue.
  auto epilogueAddress = reinterpret_cast<UCHAR *>(
      UtilMemMem(FunctionAddress, length, epilogueBytesNaked, epilogueSize));
  if (!epilogueAddress) {
    return {};
  }

  return {
      epilogueSize,
      stackSize,
      {
          epilogueAddress,
      },
  };
}