Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  SharedSection.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */


#include "SharedSection.h"

#include <algorithm>
#include "CheckForCaller.h"
#include "mozilla/BinarySearch.h"

namespace {

bool AddString(mozilla::Span<wchar_t> aBuffer, const UNICODE_STRING& aStr) {
  size_t offsetElements = 0;
  while (offsetElements < aBuffer.Length()) {
    UNICODE_STRING uniStr;
    ::RtlInitUnicodeString(&uniStr, aBuffer.data() + offsetElements);

    if (uniStr.Length == 0) {
      // Reached to the array's last item.
      break;
    }

    if (::RtlCompareUnicodeString(&uniStr, &aStr, TRUE) == 0) {
      // Already included in the array.
      return true;
    }

    // Go to the next string.
    offsetElements += uniStr.MaximumLength / sizeof(wchar_t);
  }

  // Ensure enough space including the last empty string at the end.
  if (offsetElements * sizeof(wchar_t) + aStr.Length + sizeof(wchar_t) +
          sizeof(wchar_t) >
      aBuffer.LengthBytes()) {
    return false;
  }

  auto newStr = aBuffer.Subspan(offsetElements);
  memcpy(newStr.data(), aStr.Buffer, aStr.Length);
  memset(newStr.data() + aStr.Length / sizeof(wchar_t), 0, sizeof(wchar_t));
  return true;
}

}  // anonymous namespace

namespace mozilla {
namespace freestanding {

SharedSection gSharedSection;

// Why don't we use ::GetProcAddress?
// If the export table of kernel32.dll is tampered in the current process,
// we cannot transfer an RVA because the function pointed by the RVA may not
// exist in a target process.
// We can use ::GetProcAddress with additional check to detect tampering, but
// FindExportAddressTableEntry fits perfectly here because it returns nullptr
// if the target entry is outside the image, which means it's tampered or
// forwarded to another DLL.
#define INIT_FUNCTION(exports, name)                                 \
  do {                                                               \
    auto rvaToFunction = exports.FindExportAddressTableEntry(#name); \
    if (!rvaToFunction) {                                            \
      return;                                                        \
    }                                                                \
    m##name = reinterpret_cast<decltype(m##name)>(*rvaToFunction);   \
  } while (0)

#define RESOLVE_FUNCTION(base, name)             \
  m##name = reinterpret_cast<decltype(m##name)>( \
      base + reinterpret_cast<uintptr_t>(m##name))

void Kernel32ExportsSolver::Init() {
  interceptor::MMPolicyInProcess policy;
  auto k32Exports = nt::PEExportSection<interceptor::MMPolicyInProcess>::Get(
      ::GetModuleHandleW(L"kernel32.dll"), policy);
  if (!k32Exports) {
    return;
  }

  // Please make sure these functions are not forwarded to another DLL.
  INIT_FUNCTION(k32Exports, FlushInstructionCache);
  INIT_FUNCTION(k32Exports, GetModuleHandleW);
  INIT_FUNCTION(k32Exports, GetSystemInfo);
  INIT_FUNCTION(k32Exports, VirtualProtect);
}

bool Kernel32ExportsSolver::Resolve() {
  const UNICODE_STRING k32Name = MOZ_LITERAL_UNICODE_STRING(L"kernel32.dll");

  // We cannot use GetModuleHandleW because this code can be called
  // before IAT is resolved.
  auto k32Module = nt::GetModuleHandleFromLeafName(k32Name);
  if (k32Module.isErr()) {
    // Probably this is called before kernel32.dll is loaded.
    return false;
  }

  uintptr_t k32Base =
      nt::PEHeaders::HModuleToBaseAddr<uintptr_t>(k32Module.unwrap());

  RESOLVE_FUNCTION(k32Base, FlushInstructionCache);
  RESOLVE_FUNCTION(k32Base, GetModuleHandleW);
  RESOLVE_FUNCTION(k32Base, GetSystemInfo);
  RESOLVE_FUNCTION(k32Base, VirtualProtect);

  return true;
}

HANDLE SharedSection::sSectionHandle = nullptr;
SharedSection::Layout* SharedSection::sWriteCopyView = nullptr;
RTL_RUN_ONCE SharedSection::sEnsureOnce = RTL_RUN_ONCE_INIT;
nt::SRWLock SharedSection::sLock;

void SharedSection::Reset(HANDLE aNewSectionObject) {
  nt::AutoExclusiveLock{sLock};
  if (sWriteCopyView) {
    nt::AutoMappedView view(sWriteCopyView);
    sWriteCopyView = nullptr;
    ::RtlRunOnceInitialize(&sEnsureOnce);
  }

  if (sSectionHandle != aNewSectionObject) {
    if (sSectionHandle) {
      ::CloseHandle(sSectionHandle);
    }
    sSectionHandle = aNewSectionObject;
  }
}

void SharedSection::ConvertToReadOnly() {
  if (!sSectionHandle) {
    return;
  }

  HANDLE readonlyHandle;
  if (!::DuplicateHandle(nt::kCurrentProcess, sSectionHandle,
                         nt::kCurrentProcess, &readonlyHandle, GENERIC_READ,
                         FALSE, 0)) {
    return;
  }

  Reset(readonlyHandle);
}

LauncherVoidResult SharedSection::Init() {
  static_assert(
      kSharedViewSize >= sizeof(Layout),
      "kSharedViewSize is too small to represent SharedSection::Layout.");

  HANDLE section =
      ::CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0,
                           kSharedViewSize, nullptr);
  if (!section) {
    return LAUNCHER_ERROR_FROM_LAST();
  }
  Reset(section);

  // The initial contents of the pages in a file mapping object backed by
  // the operating system paging file are 0 (zero).  No need to zero it out
  // ourselves.
  // https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-createfilemappingw
  nt::AutoMappedView writableView(sSectionHandle, PAGE_READWRITE);
  if (!writableView) {
    return LAUNCHER_ERROR_FROM_LAST();
  }

  Layout* view = writableView.as<Layout>();
  view->mK32Exports.Init();
  view->mState = Layout::State::kInitialized;
  // Leave view->mDependentModulePathArrayStart to be zero to indicate
  // we can add blocklist entries
  return Ok();
}

LauncherVoidResult SharedSection::AddDependentModule(PCUNICODE_STRING aNtPath) {
  nt::AutoMappedView writableView(sSectionHandle, PAGE_READWRITE);
  if (!writableView) {
    return LAUNCHER_ERROR_FROM_WIN32(::RtlGetLastWin32Error());
  }

  Layout* view = writableView.as<Layout>();
  if (!view->mDependentModulePathArrayStart) {
    // This is the first time AddDependentModule is called.  We set the initial
    // value to mDependentModulePathArrayStart, which *closes* the blocklist.
    // After this, AddBlocklist is no longer allowed.
    view->mDependentModulePathArrayStart =
        FIELD_OFFSET(Layout, mFirstBlockEntry) + sizeof(DllBlockInfo);
  }

  if (!AddString(view->GetDependentModules(), *aNtPath)) {
    return LAUNCHER_ERROR_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
  }

  return Ok();
}

LauncherVoidResult SharedSection::SetBlocklist(
    const DynamicBlockList& aBlocklist, bool isDisabled) {
  if (!aBlocklist.GetPayloadSize()) {
    return Ok();
  }

  nt::AutoMappedView writableView(sSectionHandle, PAGE_READWRITE);
  if (!writableView) {
    return LAUNCHER_ERROR_FROM_WIN32(::RtlGetLastWin32Error());
  }

  Layout* view = writableView.as<Layout>();
  if (view->mDependentModulePathArrayStart > 0) {
    // If the dependent module array is already available, we must not update
    // the blocklist.
    return LAUNCHER_ERROR_FROM_WIN32(ERROR_INVALID_STATE);
  }

  view->mBlocklistIsDisabled = isDisabled ? 1 : 0;
  uintptr_t bufferEnd = reinterpret_cast<uintptr_t>(view) + kSharedViewSize;
  size_t bytesCopied = aBlocklist.CopyTo(
      view->mFirstBlockEntry,
      bufferEnd - reinterpret_cast<uintptr_t>(view->mFirstBlockEntry));
  if (!bytesCopied) {
    return LAUNCHER_ERROR_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
  }

  // Setting mDependentModulePathArrayStart to a non-zero value means
  // we no longer accept blocklist entries
  // Just to be safe, make sure we don't overwrite mFirstBlockEntry even
  // if there are no entries.
  view->mDependentModulePathArrayStart =
      FIELD_OFFSET(Layout, mFirstBlockEntry) +
      std::max(bytesCopied, sizeof(DllBlockInfo));
  return Ok();
}

/* static */
ULONG NTAPI SharedSection::EnsureWriteCopyViewOnce(PRTL_RUN_ONCE, PVOID,
                                                   PVOID*) {
  if (!sWriteCopyView) {
    nt::AutoMappedView view(sSectionHandle, PAGE_WRITECOPY);
    if (!view) {
      return TRUE;
    }
    sWriteCopyView = view.as<Layout>();
    view.release();
  }
  return sWriteCopyView->Resolve() ? TRUE : FALSE;
}

SharedSection::Layout* SharedSection::EnsureWriteCopyView(
    bool requireKernel32Exports /*= false */) {
  ::RtlRunOnceExecuteOnce(&sEnsureOnce, &EnsureWriteCopyViewOnce, nullptr,
                          nullptr);
  if (!sWriteCopyView) {
    return nullptr;
  }
  auto requiredState = requireKernel32Exports
                           ? Layout::State::kResolved
                           : Layout::State::kLoadedDynamicBlocklistEntries;
  return sWriteCopyView->mState >= requiredState ? sWriteCopyView : nullptr;
}

bool SharedSection::Layout::Resolve() {
  if (mState == State::kResolved) {
    return true;
  }
  if (mState == State::kUninitialized) {
    return false;
  }
  if (mState == State::kInitialized) {
    if (!mNumBlockEntries) {
      uintptr_t arrayBase = reinterpret_cast<uintptr_t>(mFirstBlockEntry);
      uint32_t numEntries = 0;
      for (DllBlockInfo* entry = mFirstBlockEntry;
           entry->mName.Length && numEntries < GetMaxNumBlockEntries();
           ++entry) {
        entry->mName.Buffer = reinterpret_cast<wchar_t*>(
            arrayBase + reinterpret_cast<uintptr_t>(entry->mName.Buffer));
        ++numEntries;
      }
      mNumBlockEntries = numEntries;
      // Sort by name so that we can binary-search
      std::sort(mFirstBlockEntry, mFirstBlockEntry + mNumBlockEntries,
                [](const DllBlockInfo& a, const DllBlockInfo& b) {
                  return ::RtlCompareUnicodeString(&a.mName, &b.mName, TRUE) <
                         0;
                });
    }
    mState = State::kLoadedDynamicBlocklistEntries;
  }

  if (!mK32Exports.Resolve()) {
    return false;
  }

  mState = State::kResolved;
  return true;
}

Span<wchar_t> SharedSection::Layout::GetDependentModules() {
  if (!mDependentModulePathArrayStart) {
    return nullptr;
  }
  return Span<wchar_t>(
      reinterpret_cast<wchar_t*>(reinterpret_cast<uintptr_t>(this) +
                                 mDependentModulePathArrayStart),
      (kSharedViewSize - mDependentModulePathArrayStart) / sizeof(wchar_t));
}

bool SharedSection::Layout::IsDisabled() const {
  return !!mBlocklistIsDisabled;
}

const DllBlockInfo* SharedSection::Layout::SearchBlocklist(
    const UNICODE_STRING& aLeafName) const {
  MOZ_ASSERT(mState >= State::kLoadedDynamicBlocklistEntries);
  DllBlockInfoComparator comp(aLeafName);
  size_t match;
  if (!BinarySearchIf(mFirstBlockEntry, 0, mNumBlockEntries, comp, &match)) {
    return nullptr;
  }
  return &mFirstBlockEntry[match];
}

Kernel32ExportsSolver* SharedSection::GetKernel32Exports() {
  Layout* writeCopyView = EnsureWriteCopyView(true);
  return writeCopyView ? &writeCopyView->mK32Exports : nullptr;
}

Maybe<Vector<const wchar_t*>> SharedSection::GetDependentModules() {
  Layout* writeCopyView = EnsureWriteCopyView();
  if (!writeCopyView) {
    return Nothing();
  }

  mozilla::Span<wchar_t> dependentModules =
      writeCopyView->GetDependentModules();
  // Convert a null-delimited string set to a string vector.
  Vector<const wchar_t*> paths;
  for (const wchar_t* p = dependentModules.data();
       (p - dependentModules.data() <
            static_cast<long long>(dependentModules.size()) &&
        *p);) {
    if (MOZ_UNLIKELY(!paths.append(p))) {
      return Nothing();
    }
    while (*p) {
      ++p;
    }
    ++p;
  }
  return Some(std::move(paths));
}

Span<const DllBlockInfo> SharedSection::GetDynamicBlocklist() {
  Layout* writeCopyView = EnsureWriteCopyView();
  return writeCopyView ? writeCopyView->GetModulePathArray() : nullptr;
}

const DllBlockInfo* SharedSection::SearchBlocklist(
    const UNICODE_STRING& aLeafName) {
  Layout* writeCopyView = EnsureWriteCopyView();
  return writeCopyView ? writeCopyView->SearchBlocklist(aLeafName) : nullptr;
}

bool SharedSection::IsDisabled() {
  Layout* writeCopyView = EnsureWriteCopyView();
  return writeCopyView ? writeCopyView->IsDisabled() : false;
}

LauncherVoidResult SharedSection::TransferHandle(
    nt::CrossExecTransferManager& aTransferMgr, DWORD aDesiredAccess,
    HANDLE* aDestinationAddress) {
  HANDLE remoteHandle;
  if (!::DuplicateHandle(nt::kCurrentProcess, sSectionHandle,
                         aTransferMgr.RemoteProcess(), &remoteHandle,
                         aDesiredAccess, FALSE, 0)) {
    return LAUNCHER_ERROR_FROM_LAST();
  }

  return aTransferMgr.Transfer(aDestinationAddress, &remoteHandle,
                               sizeof(remoteHandle));
}

}  // namespace freestanding
}  // namespace mozilla

Messung V0.5
C=90 H=96 G=93

¤ Dauer der Verarbeitung: 0.5 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge