Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/catter-hook/win/payload/exports.def
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
LIBRARY catter-hook-win64
EXPORTS
93 changes: 7 additions & 86 deletions src/catter-hook/win/payload/main.cc
Original file line number Diff line number Diff line change
@@ -1,91 +1,12 @@
#include <array>
#include <cstddef>
#include <format>
#include <span>
#include <string>
#include <string_view>
#include <print>
#include <variant>
#include <vector>

#include <MinHook.h>
#include <windows.h>

#include "win/env.h"

namespace catter::win {
namespace {
template <CharT char_t>
std::basic_string<char_t> get_app_name(const char_t* application_name, const char_t* command_line) {
if(application_name == nullptr) {
if(command_line == nullptr) {
return {};
}
auto view = std::basic_string_view<char_t>(command_line);
auto first_space = view.find_first_of(char_t(' '));
return std::basic_string<char_t>(view.substr(0, first_space));
} else {
return std::basic_string<char_t>(application_name);
}
}

template <CharT char_t>
DWORD FixGetEnvironmentVariable(const char_t* name, char_t* buffer, DWORD size) {
if constexpr(std::is_same_v<char_t, char>) {
return GetEnvironmentVariableA(name, buffer, size);
} else {
return GetEnvironmentVariableW(name, buffer, size);
}
}

template <CharT char_t>
std::basic_string<char_t> get_proxy_path() {
constexpr size_t buffer_size = 256;
char_t buffer[buffer_size];

auto len = FixGetEnvironmentVariable<char_t>(catter::win::ENV_VAR_PROXY_PATH<char_t>,
buffer,
buffer_size);
if(len == 0) {
return {};
}

if(len < buffer_size) {
return std::basic_string<char_t>(buffer, len);
}

std::basic_string<char_t> path;
path.resize(len);
FixGetEnvironmentVariable<char_t>(catter::win::ENV_VAR_PROXY_PATH<char_t>, path.data(), len);
path.pop_back();
return path;
}

template <CharT char_t>
std::basic_string<char_t> get_ipc_id() {
constexpr size_t buffer_size = 64;
char_t buffer[buffer_size];

auto len =
FixGetEnvironmentVariable<char_t>(catter::win::ENV_VAR_IPC_ID<char_t>, buffer, buffer_size);
if(len == 0) {
return {};
}

if(len < buffer_size) {
return std::basic_string<char_t>(buffer, len);
}

std::basic_string<char_t> id;
id.resize(len);
FixGetEnvironmentVariable<char_t>(catter::win::ENV_VAR_IPC_ID<char_t>, id.data(), len);
id.pop_back();
return id;
}

} // namespace

} // namespace catter::win
#include "win/payload/util.h"

// Use anonymous namespace to avoid exporting symbols
namespace {
Expand All @@ -110,9 +31,9 @@ struct CreateProcessA {

auto converted_cmdline =
std::format("{} -p {} --exec {} -- {}",
catter::win::get_proxy_path<char>(),
catter::win::get_ipc_id<char>(),
catter::win::get_app_name<char>(lpApplicationName, lpCommandLine),
catter::win::payload::get_proxy_path<char>(),
catter::win::payload::get_ipc_id<char>(),
catter::win::payload::resolve_abspath(lpApplicationName, lpCommandLine),
std::string_view(lpCommandLine ? lpCommandLine : ""));

return original(nullptr,
Expand Down Expand Up @@ -146,9 +67,9 @@ struct CreateProcessW {

auto converted_cmdline =
std::format(L"{} -p {} --exec {} -- {}",
catter::win::get_proxy_path<wchar_t>(),
catter::win::get_ipc_id<wchar_t>(),
catter::win::get_app_name<wchar_t>(lpApplicationName, lpCommandLine),
catter::win::payload::get_proxy_path<wchar_t>(),
catter::win::payload::get_ipc_id<wchar_t>(),
catter::win::payload::resolve_abspath(lpApplicationName, lpCommandLine),
std::wstring_view(lpCommandLine ? lpCommandLine : L""));

return original(nullptr,
Expand Down
177 changes: 177 additions & 0 deletions src/catter-hook/win/payload/resolver.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
#include "resolver.h"

namespace catter::win::payload {
namespace detail {

template <CharT char_t>
constexpr char_t k_path_delimiter = char_t(';');

template <CharT char_t>
constexpr char_t k_path_sep = char_t('\\');

template <CharT char_t>
std::vector<std::basic_string<char_t>> split_path_var(std::basic_string_view<char_t> value) {
std::vector<std::basic_string<char_t>> segments;
size_t start = 0;
while(start <= value.size()) {
auto split_pos = value.find(k_path_delimiter<char_t>, start);
auto token =
value.substr(start,
split_pos == std::basic_string_view<char_t>::npos ? value.size() - start
: split_pos - start);
if(!token.empty()) {
segments.emplace_back(token);
}

if(split_pos == std::basic_string_view<char_t>::npos) {
break;
}
start = split_pos + 1;
}
return segments;
}

template <CharT char_t>
void push_if_non_empty(std::vector<std::basic_string<char_t>>& out,
std::basic_string<char_t> value) {
if(!value.empty()) {
out.push_back(std::move(value));
}
}

template <CharT char_t>
std::basic_string<char_t> get_current_drive_root() {
auto cwd = GetCurrentDirectoryDynamic<char_t>();
if(cwd.size() < 2 || cwd[1] != char_t(':')) {
return {};
}

std::basic_string<char_t> root;
root.push_back(cwd[0]);
root.push_back(char_t(':'));
root.push_back(k_path_sep<char_t>);
return root;
}

template <CharT char_t>
constexpr std::basic_string_view<char_t> k_path_env_name = {};

template <>
constexpr std::basic_string_view<char> k_path_env_name<char> = "PATH";

template <>
constexpr std::basic_string_view<wchar_t> k_path_env_name<wchar_t> = L"PATH";

template <CharT char_t>
constexpr std::basic_string_view<char_t> k_system16_name = {};

template <>
constexpr std::basic_string_view<char> k_system16_name<char> = "System";

template <>
constexpr std::basic_string_view<wchar_t> k_system16_name<wchar_t> = L"System";

template <CharT char_t>
constexpr std::basic_string_view<char_t> exe_suffix;
template <>
constexpr std::basic_string_view<char> exe_suffix<char> = ".exe";
template <>
constexpr std::basic_string_view<wchar_t> exe_suffix<wchar_t> = L".exe";
} // namespace detail

template <CharT char_t>
std::basic_string<char_t> Resolver<char_t>::resolve(std::basic_string_view<char_t> app_name) const {
if(app_name.empty()) {
return {};
}

auto original_name = std::basic_string<char_t>(app_name);

auto search_paths = join_search_paths(m_search_paths);
auto resolved = SearchPathDynamic<char_t>(search_paths.empty() ? nullptr : search_paths.c_str(),
app_name,
detail::exe_suffix<char_t>.data());
if(!resolved.empty()) {
return resolved;
}

// Keep original input when search paths do not resolve an existing file.
return original_name;
}

template <CharT char_t>
std::basic_string<char_t> Resolver<char_t>::join_search_paths(
const std::vector<std::basic_string<char_t>>& search_paths) {
std::basic_string<char_t> merged;
for(const auto& path: search_paths) {
if(path.empty()) {
continue;
}
if(!merged.empty()) {
merged.push_back(char_t(';'));
}
merged.append(path);
}
return merged;
}

template <CharT char_t>
Resolver<char_t> create_app_name_resolver() {
std::vector<std::basic_string<char_t>> search_paths;
search_paths.reserve(2);
// Search order for explicit application_name:
// 1) current drive root, 2) current directory.
detail::push_if_non_empty(search_paths, detail::get_current_drive_root<char_t>());
detail::push_if_non_empty(search_paths, GetCurrentDirectoryDynamic<char_t>());
return Resolver<char_t>(std::move(search_paths));
}

template <CharT char_t>
Resolver<char_t> create_command_line_resolver() {
std::vector<std::basic_string<char_t>> search_paths;
search_paths.reserve(64);
// Search order for command line token:
// 1) directory of current process image.
// 2) current directory.
// 3) 32-bit system directory.
// 4) 16-bit system directory named "System" under Windows directory.
// 5) Windows directory.
// 6) directories listed in PATH.
auto module_dir = GetModuleDirectory<char_t>(nullptr);
auto current_dir = GetCurrentDirectoryDynamic<char_t>();
auto system_dir = GetSystemDirectoryDynamic<char_t>();
auto windows_dir = GetWindowsDirectoryDynamic<char_t>();

detail::push_if_non_empty(search_paths, std::move(module_dir));
detail::push_if_non_empty(search_paths, std::move(current_dir));
detail::push_if_non_empty(search_paths, std::move(system_dir));
if(!windows_dir.empty()) {
auto system16_dir = windows_dir;
if(system16_dir.back() != detail::k_path_sep<char_t> &&
system16_dir.back() != char_t('/')) {
system16_dir.push_back(detail::k_path_sep<char_t>);
}
system16_dir.append(detail::k_system16_name<char_t>);
detail::push_if_non_empty(search_paths, std::move(system16_dir));
}
detail::push_if_non_empty(search_paths, windows_dir);

auto path_value =
GetEnvironmentVariableDynamic<char_t>(detail::k_path_env_name<char_t>.data(), 4096);
auto path_segments = detail::split_path_var<char_t>(path_value);
for(auto& segment: path_segments) {
detail::push_if_non_empty(search_paths, std::move(segment));
}

return Resolver<char_t>(std::move(search_paths));
}

template class Resolver<char>;
template class Resolver<wchar_t>;

template Resolver<char> create_app_name_resolver();
template Resolver<wchar_t> create_app_name_resolver();
template Resolver<char> create_command_line_resolver();
template Resolver<wchar_t> create_command_line_resolver();

} // namespace catter::win::payload
37 changes: 37 additions & 0 deletions src/catter-hook/win/payload/resolver.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#pragma once

#include <algorithm>
#include <string>
#include <string_view>
#include <type_traits>
#include <utility>
#include <vector>

#include "winapi.h"

namespace catter::win::payload {

template <CharT char_t>
class Resolver {
public:
explicit Resolver(std::vector<std::basic_string<char_t>> search_paths) :
m_search_paths(std::move(search_paths)) {}

std::basic_string<char_t> resolve(std::basic_string_view<char_t> app_name) const;

private:
static std::basic_string<char_t>
join_search_paths(const std::vector<std::basic_string<char_t>>& search_paths);
std::vector<std::basic_string<char_t>> m_search_paths;
};

template <CharT char_t>
Resolver<char_t> create_app_name_resolver();

template <CharT char_t>
Resolver<char_t> create_command_line_resolver();

extern template class Resolver<char>;
extern template class Resolver<wchar_t>;

} // namespace catter::win::payload
Loading
Loading