Skip to content
Open
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
61 changes: 61 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,64 @@
*.exe
*.out
*.app

CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
CMakeScripts
Testing
Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
_deps

.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets

# Local History for Visual Studio Code
.history/

# Built Visual Studio Code Extensions
*.vsix

# Prerequisites
*.d

# Compiled Object files
*.slo
*.lo
*.o
*.obj

# Precompiled Headers
*.gch
*.pch

# Compiled Dynamic libraries
*.so
*.dylib
*.dll

# Fortran module files
*.mod
*.smod

# Compiled Static libraries
*.lai
*.la
*.a
*.lib

# Executables
*.exe
*.out
*.app

build/
.cache/
8 changes: 8 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.0.0)
project(modifyexports VERSION 0.1.0)

set(CMAKE_CXX_STANDARD 23)

add_library(modifyexports ModifyExports.cpp ModifyExprots.hpp)

add_subdirectory(tests)
123 changes: 44 additions & 79 deletions ModifyExports.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,101 +6,66 @@ Alsch092 @ github
#include <iostream>
#include <Windows.h>
#include <winternl.h>
#include <format>
#include <ImageHlp.h>
#pragma comment(lib, "ImageHlp")

using namespace std;
#include "ModifyExprots.hpp"

bool ModifyDLLExportName(string dllName, string functionName, string newName)
#ifdef _DEBUG
#define DCOUT std::cout
#define DCERR std::cerr
#else
#define DCOUT 0 && std::cout
#define DCERR 0 && std::cerr
#endif

bool modify_exports::ModifyDLLExportName(std::string_view dllName, std::string_view functionName, std::string_view newName)
{
DWORD* dNameRVAs(0); //addresses of export names
_IMAGE_EXPORT_DIRECTORY* ImageExportDirectory;
unsigned long cDirSize;
_LOADED_IMAGE LoadedImage;
string sName;

if (MapAndLoad(dllName.c_str(), NULL, &LoadedImage, TRUE, TRUE))
{
ImageExportDirectory = (_IMAGE_EXPORT_DIRECTORY*)ImageDirectoryEntryToData(LoadedImage.MappedAddress, false, IMAGE_DIRECTORY_ENTRY_EXPORT, &cDirSize);

if (ImageExportDirectory != NULL)
{
//load list of function names from DLL, the third parameter is an RVA to the data we want
dNameRVAs = (DWORD*)ImageRvaToVa(LoadedImage.FileHeader, LoadedImage.MappedAddress, ImageExportDirectory->AddressOfNames, NULL);
_LOADED_IMAGE LoadedImage {};
std::string sName;

for (size_t i = 0; i < ImageExportDirectory->NumberOfNames; i++)
{
//get RVA
sName = (char*)ImageRvaToVa(LoadedImage.FileHeader, LoadedImage.MappedAddress, dNameRVAs[i], NULL);

if (strcmp(functionName.c_str(), sName.c_str()) == 0)
{
UINT64 funcName_Address = (UINT64)GetModuleHandleA(dllName.c_str()) + dNameRVAs[i]; //get VA From RVA + imagebase
printf("funcname_addr: %llX\n", funcName_Address);
DWORD oldProt = 0;

if (!VirtualProtect((LPVOID)funcName_Address, 1024, PAGE_EXECUTE_READWRITE, &oldProt))
{
printf("VirtualProtect failed: %d\n", GetLastError());
return false;
}
else
{
strcpy_s((char*)funcName_Address, 100, newName.c_str());
printf("Copied over export function name..\n");
}
}
}
}
else
{
printf("[ERROR] ImageExportDirectory was NULL!\n");
UnMapAndLoad(&LoadedImage);
return false;
}
}
else
{
printf("MapAndLoad failed: %d\n", GetLastError());
if (!MapAndLoad(dllName.data(), NULL, &LoadedImage, TRUE, TRUE)) {
DCERR << "MapAndLoad failed: " << std::hex << GetLastError() << std::endl;
return false;
}

UnMapAndLoad(&LoadedImage);

return true;
}


int main(void)
{
HMODULE user32 = GetModuleHandleW(L"USER32");

if (!user32)
{
printf("Could not find user32.dll: %d\n", GetLastError());
return 0;
}

UINT64 MsgBoxW = (UINT64)GetProcAddress(user32, "MessageBoxW");

if (MsgBoxW == NULL) {
printf("GetProcAddress failed!\n");
return 0;
ImageExportDirectory = (_IMAGE_EXPORT_DIRECTORY*)ImageDirectoryEntryToData(LoadedImage.MappedAddress, false, IMAGE_DIRECTORY_ENTRY_EXPORT, &cDirSize);
if (ImageExportDirectory == NULL) {
DCERR << "ImageDirectoryEntryToData failed: " << std::hex << GetLastError() << std::endl;
UnMapAndLoad(&LoadedImage);
return false;
}

printf("MessageBoxW: %llX\n", (UINT64)MsgBoxW);

ModifyDLLExportName("USER32.DLL", "MessageBoxW", "MessageBoxA"); //now we have two MessageBoxA symbols

HMODULE program = GetModuleHandleW(L"USER32");
dNameRVAs = (DWORD*)ImageRvaToVa(LoadedImage.FileHeader, LoadedImage.MappedAddress,
ImageExportDirectory->AddressOfNames, NULL);

if (program)
for (auto i = 0; i < ImageExportDirectory->NumberOfNames; i++)
{
UINT64 addr_W = (UINT64)GetProcAddress(program, "MessageBoxW"); //we call GetProcAddress again, which now returns 0
UINT64 addr_A = (UINT64)GetProcAddress(program, "MessageBoxA");

printf("New MessageBoxW: %llX\n", addr_W);
auto rva = dNameRVAs[i];
sName = (char*)ImageRvaToVa(LoadedImage.FileHeader, LoadedImage.MappedAddress, rva, NULL);
if (functionName == sName)
{
auto funcName_Address = (UINT64)GetModuleHandleA(dllName.data()) + rva; //get VA From RVA + imagebase
DCOUT << "funcname_addr:" << std::hex << funcName_Address << std::endl;

if (DWORD oldProt = 0;
!VirtualProtect((LPVOID)funcName_Address, 1024, PAGE_EXECUTE_READWRITE, &oldProt))
{
std::cerr << "VirtualProtect failed: " << std::hex << GetLastError() << std::endl;
return false;
}
else
{
strcpy_s((char*)funcName_Address, 100, newName.data());
DCERR << "Copied over export function name.." << std::endl;
}
}
}
UnMapAndLoad(&LoadedImage);

return 0;
return true;
}
8 changes: 8 additions & 0 deletions ModifyExprots.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#pragma once

#include <string>
#include <string_view>

namespace modify_exports {
bool ModifyDLLExportName(std::string_view dllName, std::string_view functionName, std::string_view newName);
}
10 changes: 10 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
find_package(Catch2 3 REQUIRED)

link_libraries(Catch2::Catch2WithMain modifyexports)

add_executable(modift_test modify_test.cc)
target_link_libraries(modift_test PRIVATE user32)

include(CTest)
include(Catch)
catch_discover_tests(modift_test)
33 changes: 33 additions & 0 deletions tests/modify_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@

#include <catch2/catch_test_macros.hpp>
#include "../ModifyExprots.hpp"

#include <Windows.h>
#include <iostream>
#include <libloaderapi.h>

TEST_CASE("ModifyExports")
{
auto user32 = GetModuleHandleW(L"user32");
if (!user32) {
LoadLibraryA("user32.dll");
user32 = GetModuleHandleW(L"user32");
}
REQUIRE(user32);

auto MsgBoxW = (UINT64)GetProcAddress(user32, "MessageBoxW");
REQUIRE(MsgBoxW);

std::cout << "MessageBoxW: " << std::hex << MsgBoxW << std::endl;

modify_exports::ModifyDLLExportName("user32.dll", "MessageBoxW", "MessageBoxA"); //now we have two MessageBoxA symbols

auto program = GetModuleHandleW(L"user32");
REQUIRE(program);

auto addr_W = (UINT64)GetProcAddress(program, "MessageBoxW"); //we call GetProcAddress again, which now returns 0
auto addr_A = (UINT64)GetProcAddress(program, "MessageBoxA");

REQUIRE(addr_W == 0);
REQUIRE(addr_A != 0);
}