I recently discovered some alternatives to GetModuleHandle and GetProcAddress that can be used when you need to avoid the original functions due to anticheat or antivirus software.

Use this to store the FNV1A hahses stealthy: https://jnns.de/posts/cpp-compile-time-hashing-fnv1a/

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
struct LIST_ENTRY
{
    struct LIST_ENTRY* Flink;
    struct LIST_ENTRY* Blink;
};

struct LDR_DATA_TABLE_ENTRY
{
    LIST_ENTRY InLoadOrderLinks;
    LIST_ENTRY InMemoryOrderLinks;
    LIST_ENTRY InInitializationOrderLinks;
    void* DllBase;
    void* EntryPoint;
    size_t SizeOfImage;
    unsigned short FullDllNameLength;
    unsigned short FullDllNameMaximumLength;
    wchar_t* FullDllNameBuffer;
    unsigned short BaseDllNameLength;
    unsigned short BaseDllNameMaximumLength;
    wchar_t* BaseDllNameBuffer;
    unsigned long Flags;
    unsigned short LoadCount;
    unsigned short TlsIndex;
    union
    {
        LIST_ENTRY HashLinks;
        struct
        {
            void* SectionPointer;
            unsigned long CheckSum;
        };
    };
    union
    {
        unsigned long TimeDateStamp;
        void* LoadedImports;
    };
    void* EntryPointActivationContext;
    void* PatchInformation;
    LIST_ENTRY ForwarderLinks;
    LIST_ENTRY ServiceTagLinks;
    LIST_ENTRY StaticLinks;
};

/// <summary>
/// Returns a pointer to the current PEB.
/// </summary>
/// <returns>PEB Pointer</returns>
__forceinline intptr_t GetPeb() noexcept
{
#ifdef _WIN64
    return __readgsqword(0x60);
#else
    return __readfsdword(0x30);
#endif
}

/// <summary>
/// Returns a pointer to the current PEB_LDR_DATA.
/// </summary>
/// <returns>PEB_LDR_DATA Pointer</returns>
__forceinline intptr_t GetPebLdrData() noexcept
{
#ifdef _WIN64
    return *reinterpret_cast<intptr_t*>(GetPeb() + 0x18);
#else
    return *reinterpret_cast<intptr_t*>(GetPeb() + 0x0C);
#endif
}

/// <summary>
/// Get a modules handle.
/// </summary>
/// <param name="modHash">FNV1A hash of the modules name, case-insensitive</param>
/// <param name="moduleSize">Returns the modules size</param>
/// <returns>Handle to the module or nullptr</returns>
__forceinline void* ModuleHandle(FNV1A::fnvhash modHash, unsigned int* moduleSize = nullptr) noexcept
{
#ifdef _WIN64
    auto ldrData = *reinterpret_cast<LDR_DATA_TABLE_ENTRY**>(GetPebLdrData() + 0x10);
#else
    auto ldrData = *reinterpret_cast<LDR_DATA_TABLE_ENTRY**>(GetPebLdrData() + 0x0C);
#endif
    const auto firstLdrData = ldrData;
    const auto charTypeSize = sizeof(std::remove_pointer_t<decltype(ldrData->BaseDllNameBuffer)>);

    do
    {
        if (FNV1A::IHashFixed(ldrData->BaseDllNameBuffer, ldrData->BaseDllNameLength / charTypeSize) == modHash)
        {
            if (moduleSize) { *moduleSize = ldrData->SizeOfImage; }
            return ldrData->DllBase;
        }
    } while (firstLdrData != (ldrData = reinterpret_cast<decltype(ldrData)>(ldrData->InLoadOrderLinks.Flink)));

    return nullptr;
}

/// <summary>
/// Get the address of an exported function.
/// </summary>
/// <param name="mod">Modules base address</param>
/// <param name="fnHash">FNV1A hash of the exports name, case-sensitive</param>
/// <returns>Functions address or nullptr</returns>
__forceinline void* ProcAddress(void* mod, FNV1A::fnvhash fnHash)
{
    if (const auto dosHeader = reinterpret_cast<IMAGE_DOS_HEADER*>(mod))
    {
        if (dosHeader->e_magic == 0x5A4D)
        {
            const auto base = reinterpret_cast<char*>(mod);
            const auto ntHeaders = reinterpret_cast<IMAGE_NT_HEADERS*>(base + dosHeader->e_lfanew);

            if (ntHeaders->Signature == 0x4550)
            {
                const auto exportDirectory = reinterpret_cast<IMAGE_EXPORT_DIRECTORY*>(base + ntHeaders->OptionalHeader.DataDirectory[0].VirtualAddress);
                const auto addressesOfNames = reinterpret_cast<unsigned long*>(base + exportDirectory->AddressOfNames);
                const auto addressesOfOrdinals = reinterpret_cast<unsigned short*>(base + exportDirectory->AddressOfNameOrdinals);
                const auto addressesOfFunctions = reinterpret_cast<unsigned long*>(base + exportDirectory->AddressOfFunctions);

                for (decltype(exportDirectory->NumberOfNames) i = 0; i < exportDirectory->NumberOfNames; ++i)
                {
                    if (FNV1A::Hash(base + addressesOfNames[i]) == fnHash)
                    {
                        return base + addressesOfFunctions[addressesOfOrdinals[i]];
                    }
                }
            }
        }
    }

    return nullptr;
}