为什么修补函数在g ++中工作,而在VC 2013/2015中却没有?

时间:2015-11-04 21:02:56

标签: c++ visual-studio-2010 visual-studio-2012 mingw hook

我正在尝试覆盖内存中的一个函数。到目前为止,完美的工作(32位测试)。

但是,当我想取消内存中覆盖的功能时, 在UNPATCH之后运行RE_RenderScene时出现错误。

此外,当我在Visual Studio 2013中运行它时,当我从修改后的函数调用原始函数时出现错误。

当我用g ++编译它时,一切都按预期/正常工作。

有什么问题?以及如何解决?

此外,RE_RenderScene的地址在运行使用Visual Studio编译的程序时给了我一个有趣的价值:

>MyIntercept.exe
Sizeof address: 32 bit
Address of RE_RenderScene: 0x013C10A0
Address of RE_RenderScene: 0x013c10a0

当我使用g ++运行它时,我得到了一些更合理的值:

>g++ MyIntercept.cpp -o myinter
>myinter
Sizeof address: 32 bit
Address of RE_RenderScene: 0x00401690
Address of RE_RenderScene: 0x00401690

这是gdb(g ++)

的反汇编

gdb

这是VC 2015(IDA)的反汇编

ida

此外,在使用Visual Studio 2013的计算机上,它给了我一个权限错误(我不是那里的管理员)。
所以我在另一台使用Visual Studio 2015的计算机上再次运行它,作为完全管理员(root),因此权限不会成为问题。

使用Visual Studio 2015和detourlength 9,它会一直运行,但最后我收到了这条消息:

Unpatch message

我不明白call-convention如何成为一个问题,因为所讨论的所有函数都是在extern "C"块中声明的。

另外,当我启用优化时,我收到了额外的编译错误。
优化结束时,情况并非如此。

这是Visual C ++运行时错误吗?

您也可以在此处找到源代码:
https://github.com/ststeiger/FunctionInterception

#include <stdio.h>
#include <stdlib.h>


#ifndef OPCODE_NOT_DEFINED
#define OPCODE_NOT_DEFINED 1
#else
// Defined IA32-80x86 by default, but abort program at runtime because OP-Code is not defined
#undef OPCODE_NOT_DEFINED
#define OPCODE_NOT_DEFINED 1
#endif


// ========================

#ifndef JMP_OPCODE
#define JMP_OPCODE 0xE9
#endif

#ifndef OPCODE_LENGTH
#define OPCODE_LENGTH 1
#endif

#ifndef DATATYPE_ADDRESS
#define DATATYPE_ADDRESS long
#endif

#ifndef ADDRESS_LENGTH
#define ADDRESS_LENGTH (sizeof(DATATYPE_ADDRESS))
#endif

#ifndef MIN_REQUIRED_FOR_DETOUR
#define MIN_REQUIRED_FOR_DETOUR (OPCODE_LENGTH + ADDRESS_LENGTH)
#endif

#ifndef INT_DETOUR_FACTOR
#define INT_DETOUR_FACTOR 1
#endif

#ifdef OPCODE_NOT_DEFINED
    #undef OPCODE_NOT_DEFINED
    #define OPCODE_NOT_DEFINED 0
#endif




// Select Processor
#if ( defined (__x86__) || defined (__x86) || defined (_x86) || defined (x86) || \
    defined (__i386__) || defined (__i386) || defined (_i386) || defined (i386) || \
    defined (__m_ix86__) || defined (__m_ix86) || defined (_m_ix86) || defined (m_ix86))
// little endian
#undef JMP_OPCODE
#undef OPCODE_LENGTH
#undef DATATYPE_ADDRESS
#undef ADDRESS_LENGTH
#undef MIN_REQUIRED_FOR_DETOUR
#undef INT_DETOUR_FACTOR
#undef OPCODE_NOT_DEFINED

// Defines for IA-32 80x86 Architecture
#define JMP_OPCODE 0xE9
#define OPCODE_LENGTH 1
#define DATATYPE_ADDRESS long
#define ADDRESS_LENGTH (sizeof(DATATYPE_ADDRESS))
#define MIN_REQUIRED_FOR_DETOUR (OPCODE_LENGTH + ADDRESS_LENGTH)
#define INT_DETOUR_FACTOR 1
#define OPCODE_NOT_DEFINED 0

// For Mac only
#define MAC_DETLEN_RE_RenderScene 7
#define MAC_DETLEN_RE_Com_MD5File 7
#define MAC_DETLEN_RE_VM_Create 7
#define MAC_DETLEN_RE_VM_CallCompiled 7
#define MAC_DETLEN_RE_VM_Free 7
#define MAC_DETLEN_RE_Sys_ConsoleInputShutdown 7
// end of Mac only
#endif


#if ( defined (__x86_64__) || defined (__x86_64) || defined (_x86_64)|| defined (x86_64) || \
    defined (__ia64__) || defined (__ia64) || defined (_ia64) || defined (ia64) || \
    defined (__m_ia64__) || defined (__m_ia64) || defined (_m_ia64) || defined (m_ia64) || \
    defined (__amd64__) || defined (__amd64) || defined (_amd64) || defined (amd64))
// switchable endian
#undef JMP_OPCODE
#undef OPCODE_LENGTH
#undef DATATYPE_ADDRESS
#undef ADDRESS_LENGTH
#undef MIN_REQUIRED_FOR_DETOUR
#undef INT_DETOUR_FACTOR
#undef OPCODE_NOT_DEFINED

// Defines for IA-64 80x86 architecture 
// To be done: don't know if they work if it's truly 64 bit, but assume full 32-bit compatibility
#define JMP_OPCODE 0xE9
#define OPCODE_LENGTH 1
#define DATATYPE_ADDRESS long
#define ADDRESS_LENGTH (sizeof(DATATYPE_ADDRESS))
#define MIN_REQUIRED_FOR_DETOUR (OPCODE_LENGTH + ADDRESS_LENGTH)
#define INT_DETOUR_FACTOR 1
#define OPCODE_NOT_DEFINED 0

// For Mac only
#define MAC_DETLEN_RE_RenderScene 7
#define MAC_DETLEN_RE_Com_MD5File 7
#define MAC_DETLEN_RE_VM_Create 7
#define MAC_DETLEN_RE_VM_CallCompiled 7
#define MAC_DETLEN_RE_VM_Free 7
#define MAC_DETLEN_RE_Sys_ConsoleInputShutdown 7
// end of Mac only
#endif



// ====================

#if ( defined(__MACOS_X__) || defined(__MACOS_X) || defined(_MACOS_X) || defined(MACOS_X) || \
    defined(__DARWIN__) || defined(__DARWIN) || defined(_DARWIN) || defined(DARWIN) || \
    defined(__MACOSX__) || defined(__MACOSX) || defined(_MACOSX) || defined(MACOSX) || \
    defined(__OSX__) || defined(__OSX) || defined(_OSX) || defined(OSX) || \
    defined(__APPLE__) || defined(__APPLE) || defined(_APPLE) || defined(APPLE) || \
    defined(__MACINTOSH__) || defined(__MACINTOSH) || defined(_MACINTOSH) || defined(MACINTOSH) || \
    defined(__MAC__) || defined(__MAC) || defined(_MAC) || defined(MAC))

#include <sys/param.h>
#include <mach-o/dyld.h>
#endif


#if ( defined (_WIN32) || defined (_WIN64) )
    #include <windows.h>
    #include <conio.h>
DWORD oldprot;
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa366898(v=vs.85).aspx
// BOOL WINAPI VirtualProtect
// If the function succeeds, the return value is nonzero.
// If the function fails, the return value is zero.To get extended error information, call GetLastError.
#define unprotect(addr,len) (VirtualProtect(addr, len, PAGE_EXECUTE_READWRITE, &oldprot))
#define GETPAGESIZE()        getpagesize()
#define EXPORT               __declspec (dllexport)
// popen in Microbosoft C++: _popen
#define POPEN(x,y)           _popen (x, y)
#define PCLOSE(x)            _pclose(x)

unsigned long getpagesize(void)
{
    static long g_pagesize = 0;
    if (!g_pagesize)
    {
        SYSTEM_INFO system_info;
        GetSystemInfo(&system_info);
        g_pagesize = system_info.dwPageSize;
    }
    return (unsigned long)g_pagesize;
}

unsigned long uslngPageSize = GETPAGESIZE();
unsigned long uslngPageMask = (~(uslngPageSize - 1));

#else // LINUX / UNIX / OS X
// #include <unistd.h> //STDIN_FILENO
#include <termios.h> //termios, TCSANOW, ECHO, ICANON
#include <sys/mman.h>

// int mprotect(void *addr, size_t len, int prot);
// On success, mprotect() returns zero. On error, -1 is returned, and errno is set appropriately.
#define unprotect(addr,len)  (mprotect(addr,len,PROT_READ|PROT_WRITE|PROT_EXEC))
#define GETPAGESIZE()         sysconf (_SC_PAGE_SIZE)
#define POPEN(x,y)            popen (x, y)
#define PCLOSE(x)             pclose(x)

#endif





// ==================

// http://stackoverflow.com/questions/1798511/how-to-avoid-press-enter-with-any-getchar
int WaitChar()
{
#if defined(_WIN32)
    int c = getch();

    fflush(stdin);
    return c;
#elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__)
    int c;
    static struct termios oldt, newt;
    /*tcgetattr gets the parameters of the current terminal
    STDIN_FILENO will tell tcgetattr that it should write the settings
    of stdin to oldt*/
    tcgetattr(STDIN_FILENO, &oldt);
    /*now the settings will be copied*/
    newt = oldt;

    /*ICANON normally takes care that one line at a time will be processed
    that means it will return if it sees a "\n" or an EOF or an EOL*/
    newt.c_lflag &= ~(ICANON);

    /*Those new settings will be set to STDIN
    TCSANOW tells tcsetattr to change attributes immediately. */
    tcsetattr(STDIN_FILENO, TCSANOW, &newt);

    c = getchar();

    // restore the old settings
    tcsetattr(STDIN_FILENO, TCSANOW, &oldt);

    fflush(stdin);
    return c;
#else
    // system("/bin/stty raw");
    int c;
    printf("Note: Waitchar for OS not supported, waits for ENTER-key instead.\n");
    c = getchar();

    fflush(stdin);
    return c;
#endif

    return 0;
}

// ===========================================



// offset[ENGINE][FUNCTION_NAME] ;
// detourlength[ENGINE][FUNCTION_NAME]

#define HOTPATCH(FUNCTION_NAME) \
    original_##FUNCTION_NAME = TemplateFuncInterceptFunction(\
    original_##FUNCTION_NAME, \
    reinterpret_cast<unsigned long> (&FUNCTION_NAME), \
    reinterpret_cast<unsigned long> (&modified_##FUNCTION_NAME), \
    static_cast<unsigned long> (FUNCTION_NAME##_COPY) \
    )

#define UNPATCH(FUNCTION_NAME) \
    unpatchfunc(reinterpret_cast<void*>(reinterpret_cast<unsigned long>(&FUNCTION_NAME)), reinterpret_cast<unsigned char*> (reinterpret_cast<unsigned long>(original_##FUNCTION_NAME)), static_cast<unsigned long> (FUNCTION_NAME##_COPY))



#define NATURALIZE(FUNCTION_NAME) \
    Naturalized_##FUNCTION_NAME = FuncConvertAddress(Naturalized_##FUNCTION_NAME, &FUNCTION_NAME)


template <class DataType>
DataType FuncConvertAddress(const DataType dt_FunctionPointer, unsigned long uslng_FunctionAddress)
{
    return reinterpret_cast<DataType> (uslng_FunctionAddress);
}




void* FuncGetPage(const unsigned long &uslngVirtualMemoryAddress)
{
    return reinterpret_cast<void*> (uslngVirtualMemoryAddress & uslngPageMask);
}


void* InterceptFunction(void* voidptr_AddressOfDetouredFunction, unsigned long uslng_CopyLength, void* voidptr_AddressOfDetourFunction)
{
    DATATYPE_ADDRESS Relocation;
    //printf("copy length: %ld\n", uslng_CopyLength);
    //printf("MIN_REQUIRED_FOR_DETOUR : %d\n", MIN_REQUIRED_FOR_DETOUR );
    void* voidptr_BackupForOriginalFunction = malloc(uslng_CopyLength + MIN_REQUIRED_FOR_DETOUR);
    //printf("Sizeof Backuppointer %ld\n", sizeof(voidptr_BackupForOriginalFunction));
    //printf("Sizeof AddrDetouredFunction %d\n", sizeof(voidptr_AddressOfDetouredFunction));

    // printf("Here 1\n");
    memcpy(voidptr_BackupForOriginalFunction, voidptr_AddressOfDetourFunction, uslng_CopyLength);
    // printf("Here 2\n");


    if (OPCODE_NOT_DEFINED)
    {
        printf("Error: OP-Code not defined\n.");
        exit(EXIT_FAILURE);
    }

    // printf("JMP_OPCODE 0x%X\n", JMP_OPCODE);
    // printf("OPCODE_LENGTH %d\n", OPCODE_LENGTH);
    // printf("MIN_REQUIRED_FOR_DETOUR %d\n", MIN_REQUIRED_FOR_DETOUR);


    memset(reinterpret_cast<void*> (reinterpret_cast<unsigned long> (voidptr_BackupForOriginalFunction)+uslng_CopyLength),
        JMP_OPCODE, OPCODE_LENGTH);
    // printf("Here 3\n");


    Relocation = static_cast<DATATYPE_ADDRESS> (reinterpret_cast<unsigned long> (voidptr_AddressOfDetouredFunction)
        -(reinterpret_cast<unsigned long> (voidptr_BackupForOriginalFunction)
        +MIN_REQUIRED_FOR_DETOUR));
    // printf("Here 4\n");

    memcpy(reinterpret_cast<void*> (reinterpret_cast<unsigned long> (voidptr_BackupForOriginalFunction)
        +uslng_CopyLength + OPCODE_LENGTH), &Relocation, ADDRESS_LENGTH);
    // printf("Here 5\n");


    int retUnprotect = unprotect(FuncGetPage(reinterpret_cast <unsigned long> (voidptr_AddressOfDetouredFunction)), uslngPageSize);
    printf("Patch unprotect: %d\n", retUnprotect);


    memset(voidptr_AddressOfDetouredFunction, JMP_OPCODE, OPCODE_LENGTH);

    Relocation = static_cast<DATATYPE_ADDRESS> (reinterpret_cast<unsigned long> (voidptr_AddressOfDetourFunction)
        -(reinterpret_cast<unsigned long> (voidptr_AddressOfDetouredFunction)
        +MIN_REQUIRED_FOR_DETOUR));

    memcpy(reinterpret_cast<void*> (reinterpret_cast<unsigned long> (voidptr_AddressOfDetouredFunction)
        +OPCODE_LENGTH), &Relocation, ADDRESS_LENGTH);
    int retReprotect = unprotect(FuncGetPage(reinterpret_cast <unsigned long> (voidptr_BackupForOriginalFunction)), uslngPageSize);
    printf("Patch reprotect: %d\n", retReprotect);


    return voidptr_BackupForOriginalFunction;
}




void unpatchfunc(void* patched_function, unsigned char* original_function, unsigned long uslng_DetourLength)
{
    //DWORD dw_OldProtect;
    //VirtualProtect(patched_function, uslng_DetourLength, PAGE_EXECUTE_READWRITE, &dw_OldProtect);
    int retUnprotect = unprotect(FuncGetPage(reinterpret_cast<unsigned long>(patched_function)), uslngPageSize);
    printf("Unpatch: unprotect: %d\n", retUnprotect);

    unsigned int intIndex;
    for (intIndex = 0; intIndex < uslng_DetourLength; ++intIndex)
        *((unsigned char*)patched_function + intIndex) = *(original_function + intIndex);

    //VirtualProtect(patched_function, uslng_DetourLength, dw_OldProtect, &dw_OldProtect);
    int retReprotect = unprotect(FuncGetPage(reinterpret_cast<unsigned long>(patched_function)), uslngPageSize);
    printf("Unpatch reprotect: %d\n", retReprotect);

    if (original_function != NULL)
        free((void*)original_function);
}


// C++ is typesafe, they said...
// I say: Yes, but at which price ?
template <class DataType>
DataType TemplateFuncInterceptFunction(DataType dt_Original_Function, unsigned long uslng_FunctionAddress,
    unsigned long uslng_modified_FunctionName, unsigned long uslng_DetourLength)
{
    return reinterpret_cast<DataType>
        (reinterpret_cast<unsigned long>
        (InterceptFunction(reinterpret_cast<void*> (uslng_FunctionAddress),
        uslng_DetourLength,
        reinterpret_cast<void*> (uslng_modified_FunctionName)
        )
        )
        );
}




extern "C" 
{



    void RE_RenderScene()
    {
        printf("This is the original RE_RenderScene\n");
    }

    void (*original_RE_RenderScene)(); // = &RE_RenderScene;

    void modified_RE_RenderScene()
    {
        printf("Entering the modified RenderScene\n");

        printf("Calling the original RenderScene in the modified RenderScene\n");
        printf("Address of RE_RenderScene: %p\n", &RE_RenderScene);
        printf("Address of original_RE_RenderScene: %p\n", *original_RE_RenderScene);
        printf("Address of original_RE_RenderScene: %p\n", original_RE_RenderScene);
        // (*original_RE_RenderScene)();

        printf("Finished calling the original RenderScene in the modified RenderScene\n");

        printf("Exiting modified RenderScene\n");
    }


}

// gdb MyIntercept.exe
// info address RE_RenderScene
// disas RE_RenderScene
// disas 0x012E10CD


// Define detour-length
// #define RE_RenderScene_COPY 9 // Visual Studio 2015
#define RE_RenderScene_COPY 6 // g++

// int _tmain(int argc, _TCHAR* argv[])
int main(int argc, char* argv[])
{
    printf("Sizeof address: %d bit\n", sizeof(void*) * 8);
    printf("Address of %s: 0x%08p\n", "RE_RenderScene", &RE_RenderScene);
    printf("Address of %s: 0x%08x\n", "RE_RenderScene", &RE_RenderScene);
    printf("===================================================\n");


    unsigned char* memoryDumpPointer = (unsigned char*)&RE_RenderScene;
    int i;
    for (i = 0; i < 100; ++i)
    {
        printf("0x%02X\n", memoryDumpPointer[i]);
    }

    if (true)
    {
        RE_RenderScene(); // Calling original version

        printf("\n\n====================== Hotpatching =============================\n");
        // HOTPATCH(RE_RenderScene);

        // Overwriting the RE_RenderScene function (JMP REL32 = 0xE9)
        original_RE_RenderScene = TemplateFuncInterceptFunction(
            original_RE_RenderScene,
            reinterpret_cast<unsigned long> (&RE_RenderScene),
            reinterpret_cast<unsigned long> (&modified_RE_RenderScene),
            static_cast<unsigned long> (RE_RenderScene_COPY)
        );

        /////// Expands to
        /////// InterceptFunction(&RE_RenderScene, RE_RenderScene_COPY, &modified_RE_RenderScene);
        /////// original_RE_RenderScene = FuncInterceptFunction(RE_RenderScene, modified_RE_RenderScene);
        printf("====================== Hotpatched ==============================\n\n");

        RE_RenderScene(); // Calling the modified version
        // Second time works
        RE_RenderScene();


        printf("\n\n====================== Unpatching =============================\n");
        UNPATCH(RE_RenderScene); // Undoing modification
        printf("====================== Unpatched ==============================\n");
        // BUG shows here
        RE_RenderScene();
    }


    printf("\n\n\n--- Press any key to continue --- \n");
    WaitChar();
    return EXIT_SUCCESS;
}

0 个答案:

没有答案