Skip to content

Commit

Permalink
Adds support for UTF-8/wchar throughout the application
Browse files Browse the repository at this point in the history
itisluiz committed Sep 19, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent df708d6 commit 8b1c539
Showing 12 changed files with 127 additions and 61 deletions.
6 changes: 3 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[submodule "lib/cxxopts"]
path = lib/cxxopts
url = [email protected]:jarro2783/cxxopts.git
[submodule "lib/cli11"]
path = lib/cli11
url = [email protected]:CLIUtils/CLI11.git
8 changes: 4 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.21)
project("dllforward" VERSION 1.2.2 LANGUAGES CXX)
project("dllforward" VERSION 1.3 LANGUAGES CXX)

## Project's base, containing all that's to be inherited
set(PROJECT_BASE ${PROJECT_NAME}-base)
@@ -11,11 +11,11 @@ target_include_directories(${PROJECT_BASE} INTERFACE "include")
target_compile_definitions(${PROJECT_BASE} INTERFACE PROJECT_VERSION="${PROJECT_VERSION}" BUILD_SHARED_LIBS=$<BOOL:${BUILD_SHARED_LIBS}>)

## Project's dependencies adding and linking
if(NOT TARGET cxxopts)
add_subdirectory("lib/cxxopts")
if(NOT TARGET CLI11)
add_subdirectory("lib/cli11")
endif()

target_link_libraries(${PROJECT_BASE} INTERFACE cxxopts::cxxopts imagehlp)
target_link_libraries(${PROJECT_BASE} INTERFACE CLI11 imagehlp)

## Source globbing, exclude main file
file(GLOB_RECURSE PROJECT_SOURCES "src/*.cpp" "src/*.cxx" "src/*.cc")
15 changes: 10 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# DLL Forward
![my machine badge](https://forthebadge.com/images/badges/pretty-risque.svg)
![silly badge](https://img.shields.io/badge/Made%20with-hacky%20opcodes-blue)
![workflow badge](https://github.com/itisluiz/DLLForward/actions/workflows/build.yml/badge.svg)

DLL Forward is a tool that allows for creation of, x86 or x64, mangled or unmangled signature, DLL proxies, redirecting the exports of an arbitrary DLL through your DLL instead.
@@ -8,12 +8,13 @@ DLL Forward is a tool that allows for creation of, x86 or x64, mangled or unmang
After downloading the binaries or building the project for yourself, you may use the generated executable as follows:

```bash
DLLForward [--def] <input> <output (optional)>
DLLForward [--def] [--version] <input> <output (optional)>
```
Where the parameters represent:
- **input**: The input DLL file path which will be proxied, choose the desired architecture instance (x86 or x64) of the DLL to proxy.
- **output**: The output path, which is a file built from the the input DLL's data, by default a **header (.h)** with the same name as the proxied DLL.
- **def**: Or `-d` for short, if set will generate a **module definition (.def)** instead of a proxy header from the input DLL.
- **def**: Or `-d` for short, if set will generate a **module definition (.def)** instead of a proxy header from the input DLL.
- **version**: Or `-v` for short, will print the version of the program and exit.

### Creating a proxy example
```bash
@@ -28,8 +29,9 @@ Example project using the generated header (The project must be configured to bu
```cpp
#include "Windows.h"

// You can specify the path to the original through a macro
// You can specify the path to the original through a macro, or it's wide character version
// #define DLLFORWARD_ORIGINALDLLPATH ".\\msimg32.original.dll"
// #define DLLFORWARD_ORIGINALDLLPATH_W L".\\msimg32.original.dll"
#include "msimg32.h"

BOOL WINAPI DllMain(HMODULE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
@@ -50,6 +52,9 @@ BOOL WINAPI DllMain(HMODULE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
Here are some macros you can define **before importing the generated header** to modify behavior:
- `DLLFORWARD_ORIGINALDLLPATH`: Sets the path to the original (proxied) DLL. If not specified, will be the absolute path to the DLL used to create the proxy header.
- `DLLFORWARD_ORIGINALDLLPATH_W`: Same as above but for wide characters, has priority over the non-wide version.
Export resolution methods:
- `DLLFORWARD_RESOLVEPROC_NAME`: Resolve exports through the names. **This is the default option**.
- `DLLFORWARD_RESOLVEPROC_RVA`: Resolve exports through the relative virtual addresses
- `DLLFORWARD_RESOLVEPROC_ORDINAL`: Resolve exports through the ordinals
@@ -81,7 +86,7 @@ This will generate a stub library `msimg32.lib` at the current directory, which
- [x] Allow for x86 and x64 proxy DLLs, debug or release.
- [x] Allow for control of the proxy DLL's header with macros defined before including the header.
- [ ] Optional smarter and more robust ways of locating the original DLL.
- [ ] Support wide characters in paths.
- [x] Support wide characters in paths.
- [ ] Create a parent project for generically generating and compiling a proxy DLL, with options to load other DLLs on a text file.

## Building (With Visual Studio)
9 changes: 9 additions & 0 deletions include/encoding.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#pragma once
#include <fstream>
#include <filesystem>

namespace fs = std::filesystem;

std::wstring escapedPathString(const fs::path& path);
std::string ansiPathString(const fs::path& path);
std::string utf8String(const std::wstring& wstr);
6 changes: 4 additions & 2 deletions include/headerboilerplate/bottom.inl
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
R"(
static HMODULE setup()
{
#ifdef DLLFORWARD_ORIGINALDLLPATH
#ifdef DLLFORWARD_ORIGINALDLLPATH_W
HMODULE hProxiedDLL{ LoadLibraryW(DLLFORWARD_ORIGINALDLLPATH_W) };
#elif defined(DLLFORWARD_ORIGINALDLLPATH)
HMODULE hProxiedDLL{ LoadLibraryA(DLLFORWARD_ORIGINALDLLPATH) };
#else
HMODULE hProxiedDLL{ LoadLibraryA(internal::originalProxiedDll) };
HMODULE hProxiedDLL{ LoadLibraryW(internal::originalProxiedDll) };
#endif
if (!hProxiedDLL)
1 change: 1 addition & 0 deletions lib/cli11
Submodule cli11 added at f4f225
1 change: 0 additions & 1 deletion lib/cxxopts
Submodule cxxopts deleted from 78b90d
7 changes: 4 additions & 3 deletions src/builder.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <builder.hh>
#include <encoding.hh>
#include <fstream>

#define BUILD_EXPORT_IDENTIFIER(ordinal) "__EXPORT_DUMMY" << ordinal
@@ -18,7 +19,7 @@ void buildResultHeader(const fs::path& dllPath, const fs::path& outFile, Archite
#include <headerboilerplate/top.inl>
<< '\n';

file << "// Proxy header generated for " << dllPath.filename().string();
file << "// Proxy header generated for " << utf8String(dllPath.filename().wstring());

if (architecture != Architecture::kUnknown)
{
@@ -36,7 +37,7 @@ void buildResultHeader(const fs::path& dllPath, const fs::path& outFile, Archite

file << "#pragma optimize(\"\", on)" "\n\n";

file << "constexpr char originalProxiedDll[]{ " << dllPath << " };" "\n";
file << "constexpr wchar_t originalProxiedDll[]{ L" << utf8String(escapedPathString(dllPath)) << " };" "\n";
file << "constexpr Export exports[]{ ";
for (const Export& exportEntry : exports)
file << BUILD_EXPORT_ENTRY(exportEntry.name, exportEntry.ordinal, exportEntry.rva);
@@ -57,7 +58,7 @@ void buildResultDefinition(const fs::path& dllPath, const fs::path& outFile, con

file << "; DLLForward by itisluiz v" PROJECT_VERSION << '\n';

file << "LIBRARY " << dllPath.filename().replace_extension().string() << '\n';
file << "LIBRARY " << utf8String(dllPath.filename().replace_extension().wstring()) << '\n';
file << "EXPORTS";

for (const Export& exportEntry : exports)
60 changes: 60 additions & 0 deletions src/encoding.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#include <encoding.hh>
#include <Windows.h>
#include <sstream>

std::wstring escapedPathString(const fs::path& path)
{
std::wstringstream wss;
wss << path;
return wss.str();
}

// Taken and modified from https://gist.github.com/pavel-a/090cbe4b2aea9a054c55974b7c7be634 (Pavel A.)
std::string ansiPathString(const fs::path& path)
{
std::wstring widePath{ path.wstring() };

std::string resultPath;
resultPath.reserve(widePath.size());

for (const WCHAR& w : widePath)
{
if (w == 0 || w > 0xFF)
break;

resultPath += static_cast<char>(w & 0xFF);
}

if (resultPath.size() == widePath.size())
return resultPath;

WCHAR shortPath[MAX_PATH]{ };
DWORD r{ GetShortPathNameW(widePath.c_str(), shortPath, MAX_PATH) };

if (r == 0 || r >= MAX_PATH)
return { };

std::wstring shortPathWstr{ shortPath };
resultPath.clear();

for (const WCHAR& w : shortPathWstr)
{
if (w == 0)
break;

if (w > 0xFF)
return { };

resultPath += static_cast<char>(w & 0xFF);
}

return resultPath;
}

std::string utf8String(const std::wstring& wstr)
{
int size{ WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, nullptr, 0, nullptr, nullptr) };
std::string buffer(size - 1, '\0');
WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, buffer.data(), size - 1, nullptr, nullptr);
return buffer;
}
5 changes: 3 additions & 2 deletions src/forwarder.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <forwarder.hh>
#include <parser.hh>
#include <builder.hh>
#include <encoding.hh>
#include <iostream>
#include <fstream>

@@ -48,7 +49,7 @@ bool makeHeader(const fs::path& dllPath, const fs::path& outFile)
std::pair<Architecture, std::vector<Export>> archExports{ acquireArchExports(dllPath) };

buildResultHeader(dllPath, outFile, archExports.first, archExports.second);
std::cout << "Generated output at \"" << fs::absolute(outFile).string() << "\"" "\n";
std::cout << "Generated output at " << utf8String(escapedPathString(fs::absolute(outFile))) << '\n';
}
catch (const std::system_error& e)
{
@@ -71,7 +72,7 @@ bool makeDefinition(const fs::path& dllPath, const fs::path& outFile)
std::pair<Architecture, std::vector<Export>> archExports{ acquireArchExports(dllPath) };

buildResultDefinition(dllPath, outFile, archExports.second);
std::cout << "Generated output at \"" << fs::absolute(outFile).string() << "\"" "\n";
std::cout << "Generated output at " << utf8String(escapedPathString(fs::absolute(outFile))) << '\n';
}
catch (const std::system_error& e)
{
67 changes: 27 additions & 40 deletions src/main.cc
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#include <forwarder.hh>
#include <CLI/CLI.hpp>
#include <iostream>
#include <filesystem>
#include <cxxopts.hpp>

namespace fs = std::filesystem;

@@ -17,60 +17,47 @@ struct ExitCodes

int main(int argc, char* argv[])
{
cxxopts::Options options(fs::path(argv[0]).filename().string(), "Generate files for creating a DLL Proxy (Man in the middle) to any other DLL.");
CLI::App app("Generate files for creating a DLL Proxy (Man in the middle) to any other DLL.", "DLLForward");

fs::path dllPath, outFile;
bool defMode{ false };

options.add_options()
("i,input", "Input DLL path", cxxopts::value<std::string>(), "The DLL that will be proxied, be mindful of the DLL's architecture/bitness.")
("o,output", "Output header file path", cxxopts::value<std::string>()->default_value("./"), "Output path of the resulting file generated from the input DLL.")
("d,def", "Create a module definition (.def file) instead of a header for proxying.")
("h,help", "Print usage");
app.add_option("input", dllPath, "The DLL that will be proxied, be mindful of the DLL's architecture/bitness.")->required()->check(CLI::ExistingFile);
app.add_option("output", outFile, "Output path of the resulting file generated from the input DLL.");
app.add_flag("-d,--def", defMode, "Generate a .def file instead of a .h file.");
app.set_version_flag("-v,--version", "DLLForward v" PROJECT_VERSION);

options.parse_positional({ "input", "output" });
options.positional_help("input output");
options.show_positional_help();

bool argDef;
fs::path argInput,argOutput;
try
{
cxxopts::ParseResult result{ options.parse(argc, argv) };

if (result["help"].count())
argv = app.ensure_utf8(argv);
app.parse(argc, argv);
}
catch (const CLI::ParseError& e)
{
if (e.get_exit_code() == 0)
{
std::cout << options.help();
std::cout << e.what() << '\n';
return ExitCodes::kSuccess;
}

argInput = result["input"].as<std::string>();
argOutput = result["output"].as<std::string>();
argDef = result["def"].count();
}
catch (const cxxopts::exceptions::exception& e)
{
std::cerr << options.help() << '\n' << e.what();
std::cerr << "Bad argument provided: " << e.what() << '\n';
return ExitCodes::kBadArgs;
}

if (outFile.empty() || fs::is_directory(outFile))
outFile /= dllPath.filename().replace_extension();

if (!fs::is_regular_file(argInput))
{
std::cerr << "Invalid input DLL path " << argInput << '\n';
return ExitCodes::kBadArgs;
}
if (!outFile.has_extension())
outFile = outFile.replace_extension(defMode ? ".def" : ".h");

if (!fs::is_directory(argOutput.parent_path()))
if (!fs::is_directory(fs::absolute(outFile).parent_path()))
{
std::cerr << "Invalid output path " << argOutput << '\n';
std::cerr << "Output directory does not exist" "\n";
return ExitCodes::kBadArgs;
}

if (fs::is_directory(argOutput))
argOutput /= argInput.filename().replace_extension();

if (!argOutput.has_extension())
argOutput.replace_extension(argDef ? ".def" : ".h");
if (defMode)
return makeDefinition(dllPath, outFile) ? ExitCodes::kSuccess : ExitCodes::kFailed;

if (argDef)
return makeDefinition(argInput, argOutput) ? ExitCodes::kSuccess : ExitCodes::kFailed;
else
return makeHeader(argInput, argOutput) ? ExitCodes::kSuccess : ExitCodes::kFailed;
return makeHeader(dllPath, outFile) ? ExitCodes::kSuccess : ExitCodes::kFailed;
}
3 changes: 2 additions & 1 deletion src/parser.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <parser.hh>
#include <encoding.hh>
#include <fstream>
#include <iostream>
#include <Windows.h>
@@ -57,7 +58,7 @@ std::vector<Export> parseExports(const fs::path& path)
throw std::system_error(std::error_code(GetLastError(), std::system_category()), "Failed to disable Wow64Fs redirection");
#endif

if (!MapAndLoad(path.u8string().c_str(), nullptr, &LoadedImage, TRUE, TRUE))
if (!MapAndLoad(ansiPathString(path).c_str(), nullptr, &LoadedImage, TRUE, TRUE))
throw std::system_error(std::error_code(GetLastError(), std::system_category()), "MapAndLoad failed to load DLL");

#ifndef _WIN64

0 comments on commit 8b1c539

Please sign in to comment.