Exemple #1
0
HRESULT WINAPI DXGISwapChainProxy::QueryInterface(REFIID riid, void** obj)
{
    hadesmem::detail::LastErrorPreserver last_error_preserver;

    last_error_preserver.Revert();
    auto const ret = swap_chain_->QueryInterface(riid, obj);
    last_error_preserver.Update();

    if (SUCCEEDED(ret))
    {
        HADESMEM_DETAIL_TRACE_A("Succeeded.");

        if (*obj == swap_chain_)
        {
            refs_++;
            *obj = this;
        }
        else
        {
            HADESMEM_DETAIL_TRACE_A("WARNING! Unhandled interface.");
            HADESMEM_DETAIL_ASSERT(false);
            static_cast<IUnknown*>(*obj)->Release();
            return E_NOINTERFACE;
        }
    }
    else
    {
        HADESMEM_DETAIL_TRACE_A("Failed.");
    }

    return ret;
}
Exemple #2
0
Config::Config()
{
  HADESMEM_DETAIL_TRACE_A("Initializing Config.");

  auto const config_path = hadesmem::detail::CombinePath(
    hadesmem::detail::GetSelfDirPath(), L"hadesmem.xml");
  HADESMEM_DETAIL_TRACE_FORMAT_W(L"Looking for config file. Path: [%s].",
                                 config_path.c_str());
  if (hadesmem::detail::DoesFileExist(config_path))
  {
    HADESMEM_DETAIL_TRACE_A("Got config file.");
    LoadFile(config_path);
  }
}
Exemple #3
0
void HandleWindowChange(HWND wnd)
{
  WindowInfo& window_info = GetWindowInfo();

  if (wnd == nullptr)
  {
    if (window_info.hooked_ && window_info.old_wndproc_ != nullptr)
    {
      ::SetWindowLongPtrW(window_info.old_hwnd_,
                          GWLP_WNDPROC,
                          reinterpret_cast<LONG_PTR>(window_info.old_wndproc_));
      HADESMEM_DETAIL_TRACE_FORMAT_A("Reset window procedure located at %p.",
                                     window_info.old_wndproc_);
    }

    HADESMEM_DETAIL_TRACE_A("Clearing window hook data.");

    window_info.old_hwnd_ = nullptr;
    window_info.old_wndproc_ = nullptr;
    window_info.hooked_ = false;

    return;
  }

  if (!window_info.hooked_)
  {
    window_info.old_hwnd_ = wnd;
    ::SetLastError(0);
    window_info.old_wndproc_ = reinterpret_cast<WNDPROC>(::SetWindowLongPtrW(
      wnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(&WindowProc)));
    if (!window_info.old_wndproc_)
    {
      DWORD const last_error = ::GetLastError();
      if (last_error)
      {
        HADESMEM_DETAIL_THROW_EXCEPTION(
          Error{} << ErrorString{"SetWindowLongPtrW failed."}
                  << ErrorCodeWinLast{last_error});
      }
    }
    window_info.hooked_ = true;
    HADESMEM_DETAIL_TRACE_FORMAT_A("Replaced window procedure located at %p.",
                                   window_info.old_wndproc_);
  }
  else
  {
    HADESMEM_DETAIL_TRACE_A("Window is already hooked. Skipping hook request.");
  }
}
Exemple #4
0
void D3D10DeviceProxy::Cleanup()
{
  HADESMEM_DETAIL_TRACE_A("Called.");

  auto& callbacks = GetOnReleaseD3D10Callbacks();
  callbacks.Run(device_);
}
Exemple #5
0
  virtual void RemovePatch() override
  {
    hadesmem::detail::AcquireSRWLock const lock(
      &GetSrwLock(), hadesmem::detail::SRWLockType::Exclusive);

    HADESMEM_DETAIL_TRACE_A("Unsetting DR hook.");

    auto& dr_hooks = GetDrHooks();
    auto const thread_id = ::GetCurrentThreadId();
    auto const iter = dr_hooks.find(thread_id);
    HADESMEM_DETAIL_ASSERT(iter != std::end(dr_hooks));
    auto const dr_index = iter->second;

    Thread const thread(thread_id);
    auto context = GetThreadContext(thread, CONTEXT_DEBUG_REGISTERS);

    // Clear the appropriate DR
    *(&context.Dr0 + dr_index) = 0;
    // Clear appropriate L0-L3 flag
    context.Dr7 &= ~static_cast<std::uintptr_t>(1ULL << (dr_index * 2));

    SetThreadContext(thread, context);

    auto const dr_hooks_removed = dr_hooks.erase(thread_id);
    (void)dr_hooks_removed;
    HADESMEM_DETAIL_ASSERT(dr_hooks_removed);

    auto& veh_hooks = GetVehHooks();
    auto const veh_hooks_removed = veh_hooks.erase(target_);
    (void)veh_hooks_removed;
    HADESMEM_DETAIL_ASSERT(veh_hooks_removed);
  }
Exemple #6
0
void DXGISwapChainProxy::Cleanup()
{
    HADESMEM_DETAIL_TRACE_A("Called.");

    auto& callbacks = GetOnReleaseDXGICallbacks();
    callbacks.Run(swap_chain_);
}
HRESULT WINAPI DirectInput8WProxy::CreateDevice(REFGUID rguid,
                                                LPDIRECTINPUTDEVICE8W* device,
                                                LPUNKNOWN outer)
{
  hadesmem::detail::LastErrorPreserver last_error_preserver;

  HADESMEM_DETAIL_TRACE_FORMAT_A(
    "Args: [%p] [%p] [%p] [%p].", this, &rguid, device, outer);

  last_error_preserver.Revert();
  auto const ret = direct_input_->CreateDevice(rguid, device, outer);
  last_error_preserver.Update();

  HADESMEM_DETAIL_TRACE_FORMAT_A("Ret: [%ld].", ret);

  if (SUCCEEDED(ret))
  {
    auto const device_type = DeviceGuidToEnum(direct_input_, rguid);
    HADESMEM_DETAIL_TRACE_FORMAT_A("Got new IDirectInputDevice8W. Type: [%s].",
                                   DeviceTypeToString(device_type).c_str());
    *device = new DirectInputDevice8WProxy(*device, device_type);
  }
  else
  {
    HADESMEM_DETAIL_TRACE_A("Failed.");
  }

  return ret;
}
Exemple #8
0
// TODO: Fix up the format of the config file so it's all process oriented
// rather than plugin oriented. i.e. You should specify your process, and then
// attach your config for plugins/gui/etc to that (and also have some sort of
// 'global' config which applies for cases where there is no process-specific
// override). That way you can override the GUI on a per-process basis so it
// matches the plugins you're trying to load.
void Config::LoadImpl(pugi::xml_document const& doc)
{
  auto const hadesmem_root = doc.child(L"HadesMem");
  if (!hadesmem_root)
  {
    HADESMEM_DETAIL_THROW_EXCEPTION(hadesmem::Error{} << hadesmem::ErrorString{
                                      "Failed to find 'HadesMem' root node."});
  }

  auto const cerberus_node = hadesmem_root.child(L"Cerberus");
  if (!cerberus_node)
  {
    HADESMEM_DETAIL_TRACE_A("No Cerberus node.");
    return;
  }

  for (auto const& plugin_node : cerberus_node.children(L"Plugin"))
  {
    auto const path =
      hadesmem::detail::pugixml::GetAttributeValue(plugin_node, L"Path");
    auto const process = hadesmem::detail::pugixml::GetOptionalAttributeValue(
      plugin_node, L"Process");
    HADESMEM_DETAIL_TRACE_FORMAT_A(
      "Got Plugin entry. Path: [%s]. Process: [%s].",
      path.c_str(),
      process.c_str());
    plugins_.emplace_back(Plugin{path, process});
  }

  // TODO: Find a better way to do this.
  auto const gui =
    hadesmem::detail::pugixml::GetOptionalAttributeValue(cerberus_node, L"GUI");
  if (hadesmem::detail::ToUpperOrdinal(gui) == L"ANTTWEAKBAR")
  {
    HADESMEM_DETAIL_TRACE_A("AntTweakBar enabled.");
    ant_tweak_bar_enabled_ = true;
  }
  else if (hadesmem::detail::ToUpperOrdinal(gui) == L"GWEN")
  {
    HADESMEM_DETAIL_TRACE_A("GWEN enabled.");
    gwen_enabled_ = true;
  }
  else
  {
    ant_tweak_bar_enabled_ = false;
  }
}
Exemple #9
0
ChaiScriptScript::~ChaiScriptScript()
{
  try
  {
    if (chai_)
    {
      chai_->eval("CerberusScriptStop()");
    }
  }
  catch (...)
  {
    HADESMEM_DETAIL_TRACE_A(
      boost::current_exception_diagnostic_information().c_str());
  }
}
Exemple #10
0
    void CleanupUnchecked() noexcept
    {
        try
        {
            Cleanup();
        }
        catch (...)
        {
            // WARNING: Handle is leaked if 'Cleanup' fails.
            HADESMEM_DETAIL_TRACE_A(
                boost::current_exception_diagnostic_information().c_str());
            HADESMEM_DETAIL_ASSERT(false);

            handle_ = GetInvalid();
        }
    }
Exemple #11
0
 template <typename... Args> void Run(Args&&... args) const noexcept
 {
   hadesmem::detail::AcquireSRWLock lock(
     &srw_lock_, hadesmem::detail::SRWLockType::Shared);
   for (auto const& callback : callbacks_)
   {
     try
     {
       callback.second(std::forward<Args&&>(args)...);
     }
     catch (...)
     {
       HADESMEM_DETAIL_TRACE_A(
         boost::current_exception_diagnostic_information().c_str());
       HADESMEM_DETAIL_ASSERT(false);
     }
   }
 }
Exemple #12
0
  void FreeUnchecked() noexcept
  {
    try
    {
      Free();
    }
    catch (...)
    {
      // WARNING: Memory in remote process is leaked if 'Free'
      // fails.
      HADESMEM_DETAIL_TRACE_A(
        boost::current_exception_diagnostic_information().c_str());
      HADESMEM_DETAIL_ASSERT(false);

      process_ = nullptr;
      base_ = nullptr;
      size_ = 0;
    }
  }
  virtual void RemoveUnchecked() noexcept override
  {
    try
    {
      Remove();
    }
    catch (...)
    {
      // WARNING: Patch may not be removed if Remove fails.
      HADESMEM_DETAIL_TRACE_A(
        boost::current_exception_diagnostic_information().c_str());
      HADESMEM_DETAIL_ASSERT(false);

      process_ = nullptr;
      applied_ = false;

      target_ = nullptr;
      detour_ = nullptr;
      orig_ = nullptr;
    }
  }
Exemple #14
0
inline std::size_t
  WriteCall(Process const& process,
            void* address,
            void* target,
            std::vector<std::unique_ptr<Allocator>>& trampolines)
{
  HADESMEM_DETAIL_TRACE_FORMAT_A("Address = %p, Target = %p", address, target);

  std::vector<std::uint8_t> call_buf;

// TODO: Avoid using a trampoline where possible.
#if defined(HADESMEM_DETAIL_ARCH_X64)
  std::unique_ptr<Allocator> trampoline = AllocatePageNear(process, address);

  PVOID tramp_addr = trampoline->GetBase();

  HADESMEM_DETAIL_TRACE_FORMAT_A("Using trampoline call. Trampoline = %p.",
                                 tramp_addr);

  Write(process, tramp_addr, reinterpret_cast<std::uintptr_t>(target));

  trampolines.emplace_back(std::move(trampoline));

  call_buf = GenCallTramp64(address, tramp_addr);
  HADESMEM_DETAIL_ASSERT(call_buf.size() == PatchConstants::kCallSize64);
#elif defined(HADESMEM_DETAIL_ARCH_X86)
  (void)trampolines;
  HADESMEM_DETAIL_TRACE_A("Using relative call.");
  call_buf = GenCall32(address, target);
  HADESMEM_DETAIL_ASSERT(call_buf.size() == PatchConstants::kCallSize32);
#else
#error "[HadesMem] Unsupported architecture."
#endif

  WriteVector(process, address, call_buf);

  return call_buf.size();
}
Exemple #15
0
  virtual void Apply() override
  {
    if (applied_)
    {
      return;
    }

    if (detached_)
    {
      HADESMEM_DETAIL_ASSERT(false);
      return;
    }

    // Reset the trampolines here because we don't do it in remove, otherwise
    // there's a potential race condition where we want to unhook and unload
    // safely, so we unhook the function, then try waiting on our ref count to
    // become zero, but we haven't actually called the trampoline yet, so we end
    // up jumping to the memory we just free'd!
    trampoline_ = nullptr;
    trampolines_.clear();
    stub_gate_ = nullptr;

    SuspendedProcess const suspended_process{process_->GetId()};

    std::uint32_t const kMaxInstructionLen = 15;
    std::uint32_t const kTrampSize = kMaxInstructionLen * 3;

    trampoline_ = std::make_unique<Allocator>(*process_, kTrampSize);
    auto tramp_cur = static_cast<std::uint8_t*>(trampoline_->GetBase());

    auto const detour_raw = detour_.target<DetourFuncRawT>();
    if (detour_raw || detour_)
    {
      HADESMEM_DETAIL_TRACE_FORMAT_A(
        "Target = %p, Detour = %p, Trampoline = %p.",
        target_,
        detour_raw,
        trampoline_->GetBase());
    }
    else
    {
      HADESMEM_DETAIL_TRACE_FORMAT_A(
        "Target = %p, Detour = INVALID, Trampoline = %p.",
        target_,
        trampoline_->GetBase());
    }

    auto const buffer =
      ReadVector<std::uint8_t>(*process_, target_, kTrampSize);

    ud_t ud_obj;
    ud_init(&ud_obj);
    ud_set_input_buffer(&ud_obj, buffer.data(), buffer.size());
    ud_set_syntax(&ud_obj, UD_SYN_INTEL);
    ud_set_pc(&ud_obj, reinterpret_cast<std::uint64_t>(target_));
#if defined(HADESMEM_DETAIL_ARCH_X64)
    ud_set_mode(&ud_obj, 64);
#elif defined(HADESMEM_DETAIL_ARCH_X86)
    ud_set_mode(&ud_obj, 32);
#else
#error "[HadesMem] Unsupported architecture."
#endif

    stub_gate_ = detail::AllocatePageNear(*process_, target_);

    std::size_t const patch_size = GetPatchSize();

    std::uint32_t instr_size = 0;
    do
    {
      std::uint32_t const len = ud_disassemble(&ud_obj);
      if (len == 0)
      {
        HADESMEM_DETAIL_THROW_EXCEPTION(Error{}
                                        << ErrorString{"Disassembly failed."});
      }

#if !defined(HADESMEM_NO_TRACE)
      char const* const asm_str = ud_insn_asm(&ud_obj);
      char const* const asm_bytes_str = ud_insn_hex(&ud_obj);
      HADESMEM_DETAIL_TRACE_FORMAT_A(
        "%s. [%s].",
        (asm_str ? asm_str : "Invalid."),
        (asm_bytes_str ? asm_bytes_str : "Invalid."));
#endif

      ud_operand_t const* const op = ud_insn_opr(&ud_obj, 0);
      bool is_jimm = op && op->type == UD_OP_JIMM;
      // Handle JMP QWORD PTR [RIP+Rel32]. Necessary for hook chain support.
      bool is_jmem = op && op->type == UD_OP_MEM && op->base == UD_R_RIP &&
                     op->index == UD_NONE && op->scale == 0 && op->size == 0x40;
      if ((ud_obj.mnemonic == UD_Ijmp || ud_obj.mnemonic == UD_Icall) && op &&
          (is_jimm || is_jmem))
      {
        std::uint16_t const size = is_jimm ? op->size : op->offset;
        HADESMEM_DETAIL_TRACE_FORMAT_A("Operand/offset size is %hu.", size);
        std::int64_t const insn_target = [&]() -> std::int64_t
        {
          switch (size)
          {
          case sizeof(std::int8_t) * CHAR_BIT:
            return op->lval.sbyte;
          case sizeof(std::int16_t) * CHAR_BIT:
            return op->lval.sword;
          case sizeof(std::int32_t) * CHAR_BIT:
            return op->lval.sdword;
          case sizeof(std::int64_t) * CHAR_BIT:
            return op->lval.sqword;
          default:
            HADESMEM_DETAIL_ASSERT(false);
            HADESMEM_DETAIL_THROW_EXCEPTION(
              Error{} << ErrorString{"Unknown instruction size."});
          }
        }();

        auto const resolve_rel =
          [](std::uint64_t base, std::int64_t target, std::uint32_t insn_len)
        {
          return reinterpret_cast<std::uint8_t*>(
                   static_cast<std::uintptr_t>(base)) +
                 target + insn_len;
        };

        std::uint64_t const insn_base = ud_insn_off(&ud_obj);
        std::uint32_t const insn_len = ud_insn_len(&ud_obj);

        auto const resolved_target =
          resolve_rel(insn_base, insn_target, insn_len);
        void* const jump_target =
          is_jimm ? resolved_target : Read<void*>(*process_, resolved_target);
        HADESMEM_DETAIL_TRACE_FORMAT_A("Jump/call target = %p.", jump_target);
        if (ud_obj.mnemonic == UD_Ijmp)
        {
          HADESMEM_DETAIL_TRACE_A("Writing resolved jump.");
          tramp_cur += detail::WriteJump(
            *process_, tramp_cur, jump_target, true, &trampolines_);
        }
        else
        {
          HADESMEM_DETAIL_ASSERT(ud_obj.mnemonic == UD_Icall);
          HADESMEM_DETAIL_TRACE_A("Writing resolved call.");
          tramp_cur +=
            detail::WriteCall(*process_, tramp_cur, jump_target, trampolines_);
        }
      }
      else
      {
        std::uint8_t const* const raw = ud_insn_ptr(&ud_obj);
        Write(*process_, tramp_cur, raw, raw + len);
        tramp_cur += len;
      }

      instr_size += len;
    } while (instr_size < patch_size);

    HADESMEM_DETAIL_TRACE_A("Writing jump back to original code.");

    tramp_cur +=
      detail::WriteJump(*process_,
                        tramp_cur,
                        reinterpret_cast<std::uint8_t*>(target_) + instr_size,
                        true,
                        &trampolines_);

    FlushInstructionCache(
      *process_, trampoline_->GetBase(), trampoline_->GetSize());

    detail::WriteStubGate<TargetFuncT>(*process_,
                                       stub_gate_->GetBase(),
                                       &*stub_,
                                       &GetOriginalArbitraryUserPtrPtr);

    orig_ = ReadVector<std::uint8_t>(*process_, target_, patch_size);

    detail::VerifyPatchThreads(process_->GetId(), target_, orig_.size());

    WritePatch();

    FlushInstructionCache(*process_, target_, instr_size);

    applied_ = true;
  }
  virtual void RemovePatch()
  {
    HADESMEM_DETAIL_TRACE_A("Restoring original pointer.");

    Write(*process_, target_, orig_);
  }
  virtual void WritePatch()
  {
    HADESMEM_DETAIL_TRACE_A("Writing pointer to stub.");

    Write(*process_, target_, stub_gate_->GetBase());
  }
void DirectInput8WProxy::Cleanup()
{
  HADESMEM_DETAIL_TRACE_A("Called.");
}
Exemple #19
0
  virtual void WritePatch() override
  {
    hadesmem::detail::AcquireSRWLock const lock(
      &GetSrwLock(), hadesmem::detail::SRWLockType::Exclusive);

    auto& veh_hooks = GetVehHooks();

    HADESMEM_DETAIL_ASSERT(veh_hooks.find(target_) == std::end(veh_hooks));
    veh_hooks[target_] = this;

    auto const veh_cleanup_hook = [&]()
    {
      auto const veh_hooks_removed = veh_hooks.erase(target_);
      (void)veh_hooks_removed;
      HADESMEM_DETAIL_ASSERT(veh_hooks_removed);
    };
    auto scope_veh_cleanup_hook =
      hadesmem::detail::MakeScopeWarden(veh_cleanup_hook);

    HADESMEM_DETAIL_TRACE_A("Setting DR hook.");

    auto& dr_hooks = GetDrHooks();
    auto const thread_id = ::GetCurrentThreadId();
    HADESMEM_DETAIL_ASSERT(dr_hooks.find(thread_id) == std::end(dr_hooks));

    Thread const thread(thread_id);
    auto context = GetThreadContext(thread, CONTEXT_DEBUG_REGISTERS);

    std::uint32_t dr_index = static_cast<std::uint32_t>(-1);
    for (std::uint32_t i = 0; i < 4; ++i)
    {
      // Check whether the DR is available according to the control register
      bool const control_available = !(context.Dr7 & (1ULL << (i * 2)));
      // Check whether the DR is zero. Pobably not actually necessary, but
      // it's a nice additional sanity check. This may require a
      // user-controlable flag in future though if the code being hooked is
      // 'hostile'.
      bool const dr_available = !(&context.Dr0)[i];
      if (control_available && dr_available)
      {
        dr_index = i;
        break;
      }
    }

    if (dr_index == static_cast<std::uint32_t>(-1))
    {
      HADESMEM_DETAIL_THROW_EXCEPTION(
        Error{} << ErrorString{"No free debug registers."});
    }

    dr_hooks[ ::GetCurrentThreadId()] = dr_index;

    auto const dr_cleanup_hook = [&]()
    {
      auto const dr_hooks_removed = dr_hooks.erase(::GetCurrentThreadId());
      (void)dr_hooks_removed;
      HADESMEM_DETAIL_ASSERT(dr_hooks_removed);
    };
    auto scope_dr_cleanup_hook =
      hadesmem::detail::MakeScopeWarden(dr_cleanup_hook);

    (&context.Dr0)[dr_index] = reinterpret_cast<std::uintptr_t>(target_);
    // Set appropriate L0-L3 flag
    context.Dr7 |= static_cast<std::uintptr_t>(1ULL << (dr_index * 2));
    // Set appropriate RW0-RW3 field (Execution)
    std::uintptr_t break_type = 0;
    context.Dr7 |= (break_type << (16 + 4 * dr_index));
    // Set appropriate LEN0-LEN3 field (1 byte)
    std::uintptr_t break_len = 0;
    context.Dr7 |= (break_len << (18 + 4 * dr_index));
    // Set LE flag
    std::uintptr_t local_enable = 1 << 8;
    context.Dr7 |= local_enable;

    SetThreadContext(thread, context);

    scope_veh_cleanup_hook.Dismiss();
    scope_dr_cleanup_hook.Dismiss();
  }
Exemple #20
0
void DumpMemory()
{
  HADESMEM_DETAIL_TRACE_A("Dumping image memory to disk.");

  hadesmem::Process const process{::GetCurrentProcessId()};

  hadesmem::ModuleList modules(process);
  for (auto const& module : modules)
  {
    HADESMEM_DETAIL_TRACE_A("Checking for valid headers.");
    try
    {
      hadesmem::PeFile const pe_file(process,
                                     module.GetHandle(),
                                     hadesmem::PeFileType::Image,
                                     static_cast<DWORD>(module.GetSize()));
      hadesmem::NtHeaders nt_headers(process, pe_file);
    }
    catch (std::exception const& /*e*/)
    {
      HADESMEM_DETAIL_TRACE_A("WARNING! Invalid headers.");
      return;
    }

    HADESMEM_DETAIL_TRACE_A("Reading memory.");
    auto raw = hadesmem::ReadVectorEx<std::uint8_t>(
      process,
      module.GetHandle(),
      module.GetSize(),
      hadesmem::ReadFlags::kZeroFillReserved);
    hadesmem::Process const local_process(::GetCurrentProcessId());
    hadesmem::PeFile const pe_file(local_process,
                                   raw.data(),
                                   hadesmem::PeFileType::Image,
                                   static_cast<DWORD>(raw.size()));
    hadesmem::NtHeaders nt_headers(local_process, pe_file);

    HADESMEM_DETAIL_TRACE_A("Copying headers.");
    std::vector<std::uint8_t> raw_new;
    std::copy(std::begin(raw),
              std::begin(raw) + nt_headers.GetSizeOfHeaders(),
              std::back_inserter(raw_new));

    HADESMEM_DETAIL_TRACE_A("Copying section data.");
    hadesmem::SectionList const sections(local_process, pe_file);
    std::vector<std::pair<DWORD, DWORD>> raw_datas;
    for (auto const& section : sections)
    {
      auto const section_size =
        (std::max)(section.GetVirtualSize(), section.GetSizeOfRawData());
      auto const ptr_raw_data_new =
        section.GetPointerToRawData() < raw_new.size()
          ? static_cast<DWORD>(
              RoundUp(raw_new.size(), nt_headers.GetFileAlignment()))
          : section.GetPointerToRawData();
      raw_datas.emplace_back(ptr_raw_data_new, section_size);

      if (ptr_raw_data_new > raw_new.size())
      {
        raw_new.resize(ptr_raw_data_new);
      }

      auto const raw_data = raw.data() + section.GetVirtualAddress();
      auto const raw_data_end = raw_data + section_size;
      raw_new.reserve(raw_new.size() + section_size);
      std::copy(raw_data, raw_data_end, std::back_inserter(raw_new));
    }

    HADESMEM_DETAIL_ASSERT(raw_new.size() <
                           (std::numeric_limits<DWORD>::max)());
    hadesmem::PeFile const pe_file_new(local_process,
                                       raw_new.data(),
                                       hadesmem::PeFileType::Data,
                                       static_cast<DWORD>(raw_new.size()));

    HADESMEM_DETAIL_TRACE_A("Fixing NT headers.");
    hadesmem::NtHeaders nt_headers_new(local_process, pe_file_new);
    nt_headers_new.SetImageBase(
      reinterpret_cast<ULONG_PTR>(module.GetHandle()));
    nt_headers_new.UpdateWrite();

    HADESMEM_DETAIL_TRACE_A("Fixing section headers.");
    hadesmem::SectionList sections_new(local_process, pe_file_new);
    std::size_t n = 0;
    for (auto& section : sections_new)
    {
      section.SetPointerToRawData(raw_datas[n].first);
      section.SetSizeOfRawData(raw_datas[n].second);
      section.UpdateWrite();
      ++n;
    }

    HADESMEM_DETAIL_TRACE_A("Fixing imports.");
    hadesmem::ImportDirList const import_dirs(local_process, pe_file);
    hadesmem::ImportDirList const import_dirs_new(local_process, pe_file_new);
    auto i = std::begin(import_dirs), j = std::begin(import_dirs_new);
    bool thunk_mismatch = false;
    for (; i != std::end(import_dirs) && j != std::end(import_dirs_new);
         ++i, ++j)
    {
      hadesmem::ImportThunkList const import_thunks(
        local_process, pe_file, i->GetOriginalFirstThunk());
      hadesmem::ImportThunkList import_thunks_new(
        local_process, pe_file_new, j->GetFirstThunk());
      auto a = std::begin(import_thunks);
      auto b = std::begin(import_thunks_new);
      for (; a != std::end(import_thunks) && b != std::end(import_thunks_new);
           ++a, ++b)
      {
        b->SetFunction(a->GetFunction());
        b->UpdateWrite();
      }
      thunk_mismatch = thunk_mismatch || ((a != std::end(import_thunks)) ^
                                          (b != std::end(import_thunks_new)));
    }
    bool const dir_mismatch =
      (i != std::end(import_dirs)) ^ (j != std::end(import_dirs));

    HADESMEM_DETAIL_TRACE_A("Writing file.");
    auto const proc_path = hadesmem::GetPath(process);
    auto const proc_name = proc_path.substr(proc_path.rfind(L'\\') + 1);
    auto const proc_pid_str = std::to_wstring(process.GetId());
    std::wstring dump_path;
    std::uint32_t c = 0;
    do
    {
      dump_path = proc_name + L"_" + proc_pid_str + L"_" + module.GetName() +
                  L"_" + std::to_wstring(c++) + L".dmp";
    } while (hadesmem::detail::DoesFileExist(dump_path) && c < 10);
    auto const dump_file = hadesmem::detail::OpenFile<char>(
      dump_path, std::ios::out | std::ios::binary);
    if (!*dump_file)
    {
      HADESMEM_DETAIL_THROW_EXCEPTION(hadesmem::Error()
                                      << hadesmem::ErrorString(
                                        "Unable to open dump file."));
    }
    if (!dump_file->write(reinterpret_cast<char const*>(raw_new.data()),
                          raw_new.size()))
    {
      HADESMEM_DETAIL_THROW_EXCEPTION(hadesmem::Error()
                                      << hadesmem::ErrorString(
                                        "Unable to write to dump file."));
    }

    if (dir_mismatch)
    {
      HADESMEM_DETAIL_THROW_EXCEPTION(hadesmem::Error()
                                      << hadesmem::ErrorString(
                                        "Mismatch in import dir processing."));
    }

    if (thunk_mismatch)
    {
      HADESMEM_DETAIL_THROW_EXCEPTION(
        hadesmem::Error() << hadesmem::ErrorString(
          "Mismatch in import thunk processing."));
    }
  }
}
Exemple #21
0
inline std::size_t
  WriteJump(Process const& process,
            void* address,
            void* target,
            bool push_ret_fallback,
            std::vector<std::unique_ptr<Allocator>>* trampolines)
{
  HADESMEM_DETAIL_TRACE_FORMAT_A(
    "Address = %p, Target = %p, Push Ret Fallback = %u.",
    address,
    target,
    static_cast<std::uint32_t>(push_ret_fallback));

  std::vector<std::uint8_t> jump_buf;

#if defined(HADESMEM_DETAIL_ARCH_X64)
  if (IsNear(address, target))
  {
    HADESMEM_DETAIL_TRACE_A("Using relative jump.");
    jump_buf = GenJmp32(address, target);
    HADESMEM_DETAIL_ASSERT(jump_buf.size() == PatchConstants::kJmpSize32);
  }
  else
  {
    std::unique_ptr<Allocator> trampoline;

    if (trampolines)
    {
      try
      {
        trampoline = AllocatePageNear(process, address);
      }
      catch (std::exception const& /*e*/)
      {
        // Don't need to do anything, we'll fall back to PUSH/RET.
      }
    }

    if (trampoline)
    {
      void* tramp_addr = trampoline->GetBase();

      HADESMEM_DETAIL_TRACE_FORMAT_A("Using trampoline jump. Trampoline = %p.",
                                     tramp_addr);

      Write(process, tramp_addr, target);

      trampolines->emplace_back(std::move(trampoline));

      jump_buf = GenJmpTramp64(address, tramp_addr);
      HADESMEM_DETAIL_ASSERT(jump_buf.size() == PatchConstants::kJmpSize64);
    }
    else
    {
      if (!push_ret_fallback)
      {
        // We're out of options...
        HADESMEM_DETAIL_THROW_EXCEPTION(
          Error{} << ErrorString{"Unable to use a relative or trampoline "
                                 "jump, and push/ret fallback is disabled."});
      }

      HADESMEM_DETAIL_TRACE_A("Using push/ret 'jump'.");

      auto const target_high = static_cast<std::uint32_t>(
        (reinterpret_cast<std::uintptr_t>(target) >> 32) & 0xFFFFFFFF);
      if (target_high)
      {
        HADESMEM_DETAIL_TRACE_A("Push/ret 'jump' is big.");
        jump_buf = GenPush64Ret(target);
        HADESMEM_DETAIL_ASSERT(jump_buf.size() ==
                               PatchConstants::kPushRetSize64);
      }
      else
      {
        HADESMEM_DETAIL_TRACE_A("Push/ret 'jump' is small.");
        jump_buf = GenPush32Ret(target);
        HADESMEM_DETAIL_ASSERT(jump_buf.size() ==
                               PatchConstants::kPushRetSize32);
      }
    }
  }
#elif defined(HADESMEM_DETAIL_ARCH_X86)
  (void)push_ret_fallback;
  (void)trampolines;
  HADESMEM_DETAIL_TRACE_A("Using relative jump.");
  jump_buf = GenJmp32(address, target);
  HADESMEM_DETAIL_ASSERT(jump_buf.size() == PatchConstants::kJmpSize32);
#else
#error "[HadesMem] Unsupported architecture."
#endif

  WriteVector(process, address, jump_buf);

  return jump_buf.size();
}