Двумерный массив указателей на функции PVOID

Использую хуки Detours и пытаюсь реализовать указатели на функции в двумерном массиве, чтобы DetourAttach производить по индексу. Что имеем:

// объявление функции двойника
    HIMAGELIST WINAPI MyImageList_Create(
        int  cx,
        int  cy,
        UINT flags,
        int  cInitial,
        int  cGrow
    );
    
// объявление указателя на оригинальную функцию
static HIMAGELIST(WINAPI* actualImageList_Create)(int cx, int cy, UINT flags, int cInitial, int cGrow) = ImageList_Create;
    
// подключение хука на функцию
DetourAttach(&(PVOID&)actualImageList_Create, MyImageList_Create);

// описание функции двойника
HIMAGELIST WINAPI MyImageList_Create(
    int  cx,
    int  cy,
    UINT flags,
    int  cInitial,
    int  cGrow
)
{
    
    return actualImageList_Create(cx, cy, flags, cInitial, cGrow);
}

Данный пример работает исправно, но неудобен при использовании множества хуков на функции. Необходимо указатели на двойника и оригинальную функции поместить в двух мерный массив, чтобы подключать хук по индексу примерно так: DetourAttach(functptr[0][0], functptr[0][1]); Соответственно, в описании функции двойника вызывать оригинальную функцию так же из массива по индексу как-то так: functptr[0][0](cx, cy, flags, cInitial, cGrow);

Что требуется реализовать:

// объявление функции двойника
        HIMAGELIST WINAPI MyImageList_Create(
            int  cx,
            int  cy,
            UINT flags,
            int  cInitial,
            int  cGrow
        );

// Объявление целевого массива
PVOID(*functptr[][2]) = { { /*...*/ , (PVOID*)MyImageList_Create} };

// объявление указателя на оригинальную функцию,
// который надо разместить в массиве в первой колонке, 
// во второй колонке разместить указатель на функцию двойника
// ...

// подключение хука на функцию
DetourAttach(&(PVOID&)functptr[0][0], functptr[0][1]);

 // описание функции двойника
    HIMAGELIST WINAPI MyImageList_Create(
        int  cx,
        int  cy,
        UINT flags,
        int  cInitial,
        int  cGrow
    )
    {
        
        return functptr[0][0](cx, cy, flags, cInitial, cGrow);
    }

Описание Detours https://www.codeproject.com/Articles/30140/API-Hooking-with-MS-Detours


Ответы (2 шт):

Автор решения: avp

У меня нет винды, поэтому мог что-то напутать с моделированием виндовых typedefs.
В частности, пользуясь советом enSO, пришлось определить

#define WINAPI __stdcall
#define __stdcall

а HIMAGELIST, полазив по гуглу, я определил так

struct _IMAGELIST;
typedef struct _IMAGELIST * HIMAGELIST;

-- это ответ --

Я думаю, что прежде всего, для упрощения описания двумерного массива функций functptr[][2] стоит определить typedef для указателя на функцию, назовем этот тип -- img_foo_t.

typedef HIMAGELIST  (WINAPI *img_foo_t)(int cx, int cy, UINT flags, int cInitial, int cGrow);

После этого у меня получилось скомпилировать вот такой код (с вашими функциями)

// объявление указателя на оригинальную функцию
static HIMAGELIST
       (WINAPI *actualImageList_Create)(int cx, int cy, 
                                        UINT flags, int cInitial, int cGrow) = ImageList_Create;


int
main (int ac, char *av[])
{

      
  // подключение хука на функцию
  // DetourAttach(&(PVOID&)actualImageList_Create, MyImageList_Create);
  DetourAttach((PVOID *)&actualImageList_Create, MyImageList_Create);


  img_foo_t functptr[][2] = {
    {ImageList_Create, MyImageList_Create}
  };
  DetourAttach((PVOID *)&functptr[0][0], functptr[0][1]);

  return 0;
}

Обратите внимание на изменения в передаче первого параметра в DetourAttach
(реально мы передаем адрес переменной с указателем на функцию (как описано в вашей ссылке на Detours API Hooking)).

P.S. Понятно, что для функций ImageList_Create и DetourAttach я использовал заглушки.

→ Ссылка
Автор решения: Стас Палыч

Вот таким образом я решил осуществить свои задачи. Код был взят из чужих проектов, поэтому надо что-то почистить и подправить. DetoursHelper.hpp:

#pragma once

namespace MyProject::HookHelper
{
    template <size_t hookCount, size_t possibleImportCount>
    using DetoursDispatcherDependency = std::array<std::tuple<std::array<std::string_view, possibleImportCount>, LPCSTR, PVOID, PVOID>, hookCount>;

    template <size_t hookCount, size_t possibleImportCount>

    struct DetoursDispatcher
    {
        const DetoursDispatcherDependency<hookCount, possibleImportCount>& DetoursDependency{};
        //std::array<std::pair<OffsetStorage, std::optional<OffsetStorage>>, hookCount> hookInfoCache{}; // iat mem offset
        std::array<std::pair<PVOID, std::optional<HMODULE>>, hookCount> hookTable{}; // iat org value
        std::array<size_t, hookCount> hookRef{};
        mutable wil::srwlock hookLock{};
        PVOID moduleAddress{};
        void CacheHookData()
        {
            /*auto dllPath{ wil::GetModuleFileNameW<std::wstring, MAX_PATH + 1>(reinterpret_cast<HMODULE>(moduleAddress)) };

            if (dllPath.empty())
            {
                return;
            }*/

            PVOID* functionAddress{ nullptr };
            HMODULE* moduleHandle{ nullptr };
            
            auto imageMapper{ ImageMapper(dllPath) };
            
            /*auto initialize_offset_storage = [&](size_t index)
                {
                    auto [possibleImport, functionName, detourFunction, typeFunction] {DetoursDependency[index]};
                    moduleHandle = GetModuleHandleW(L"user32.dll");
                    typeFunction = reinterpret_cast<decltype(&detourFunction)>(GetProcAddress(moduleHandle, functionName));
                    functionAddress = typeFunction;
                    hookInfoCache[index].first = moduleHandle;
                    hookInfoCache[index].second = functionAddress;
                    
                };*/

            auto bind = [&](const std::pair<HMODULE*, PVOID*> value)
                {
                    functionAddress = value.second;
                    moduleHandle = value.first;

                    return functionAddress != nullptr;
                };

            for (size_t i = 0; i < DetoursDependency.size(); i++)
            {
                auto [possibleImport, functionName, detourFunction, typeFunction] {DetoursDependency[i]};
                for (auto importDll : possibleImport)
                {
                    std::string dll{importDll};
// здесь получаю указатель на оригинальную функцию
                    typeFunction = reinterpret_cast<decltype(&detourFunction)>(GetProcAddress(GetModuleHandleA(dll.c_str()), functionName));
                    if (typeFunction)
                    {
                        OutputDebugStringA(
                            std::format(
                                "{} - {}  0x{:x} DetoursHelper\n",
                                importDll,
                                functionName,
                                (DWORD)typeFunction
                            ).c_str()
                        );
                        
                        break;
                    }
                }
                
            }
            
        }

        bool IsHookEnabled(size_t index) const { auto lock{ hookLock.lock_shared() }; return hookRef[index] > 0; }

        bool EnableHook(size_t index, bool enable)
        {
            /*if (!moduleAddress)
            {
                return false;
            }

            auto lock{ hookLock.lock_exclusive() };*/

// защита от повторного вызова
            hookRef[index] += enable ? 1 : -1;
            bool hookChanged{ false };
            if ((enable && hookRef[index] == 1) || (!enable && hookRef[index] == 0))
            {
                hookChanged = true;
            }

            auto& [functionAddressOffset, moduleHandleOffset] {hookInfoCache[index]};
            auto iatFunctionAddress{ reinterpret_cast<PVOID*>(functionAddressOffset.To(moduleAddress)) };
            auto iatModuleAddress{ moduleHandleOffset.has_value() ? std::optional{reinterpret_cast<HMODULE*>(moduleHandleOffset.value().To(moduleAddress))} : std::nullopt };

            /*DetourTransactionBegin();
            DetourUpdateThread(GetCurrentThread());

            DetourAttach(&(PVOID&)iatFunctionAddress, detourFunction);

            DetourTransactionCommit();*/
        }
        
        template <size_t index, typename T = PVOID>
        [[nodiscard]] __forceinline auto GetOrg() const { auto lock{ hookLock.lock_shared() }; return reinterpret_cast<T>(hookTable[index].first); }
        DetoursDispatcher(
            const DetoursDispatcherDependency<hookCount, possibleImportCount>& dependency,
            PVOID targetDll = nullptr
        ) : DetoursDependency{ dependency }, moduleAddress{ targetDll }
        {
        }
        DetoursDispatcher() = delete;
    };

    template <size_t hookCount, size_t possibleImportCount>
    DetoursDispatcher(
        const DetoursDispatcherDependency<hookCount, possibleImportCount>&,
        PVOID, PVOID
    ) -> DetoursDispatcher<hookCount, possibleImportCount>;

    template <typename TDetoursDispatcherDependency>
    struct TDetoursDispatcher;

    template <size_t hookCount, size_t possibleImportCount>
    struct TDetoursDispatcher<DetoursDispatcherDependency<hookCount, possibleImportCount>> { using type = DetoursDispatcher<hookCount, possibleImportCount>; };
}

WinHook.cpp:

using namespace MyProject;
namespace MyProject::WinHooks
{
    using namespace std::literals;

    typedef int(WINAPI* TypeDrawTextW)(HDC hdc, LPCWSTR lpchText, int cchText, LPRECT lprc, UINT format);
    TypeDrawTextW actualDrawTextW = nullptr;
// [еще аналогичные объявления  функций...]

// заполнение массива данными
    HookHelper::DetoursDispatcherDependency g_DetoursDependency
    {
        std::tuple
        {
            std::array
            {
                "user32.dll"sv,
                "ext-ms-win-ntuser-draw-l1-1-0.dll"sv,
                "ext-ms-win-ntuser-misc-l1-1-0.dll"sv
            },
            "DrawTextW",
            reinterpret_cast<PVOID>(MyDrawTextW),
            reinterpret_cast<PVOID>(actualDrawTextW)
        }
        /*std::tuple
        {
            std::array
            {
                "module.dll"sv,
                "module-ntuser-draw-l1-1-0.dll"sv,
                "module-win-ntuser-misc-l1-1-0.dll"sv
            },
            "MoreFunc",
            reinterpret_cast<PVOID>(MyMoreFunc),
            reinterpret_cast<PVOID>(actualMoreFunc)
        }
        [...]*/
    };

    HookHelper::DetoursDispatcher g_DetoursDispatcher
    {
        g_DetoursDependency
    };

}
// кэшируем адреса функций, здесь переменным actualDrawTextW нужно присвоить значения
g_DetoursDispatcher.CacheHookData();

// подключаем хук
g_DetoursDispatcher.EnableHook(index, attach);

// проблема заключается в получении оригинальной функции 
// для возврата из функции двойника.
// Нужно реализовать одно из двух:
// либо переменным actualDrawTextW присвоить значения в функции CacheHookData,
// либо вернуть значения как в этом примере:
int WINAPI WinHooks::MyDrawTextW(HDC hdc, LPCWSTR lpchText, int cchText, LPRECT lprc, UINT format)
{
    auto actualDrawTextW{ g_DetoursDispatcher.GetOrg<index, decltype(&MyDrawTextW)>() };
    
    return actualDrawTextW(hdc, lpchText, cchText, lprc, format);
}
→ Ссылка