C++ 端口Win32 DLL钩子到Linux

C++ 端口Win32 DLL钩子到Linux,c++,linux,dll,winapi,hook,C++,Linux,Dll,Winapi,Hook,我有一个程序(NWShader),它与另一个程序的OpenGL调用(NWN)挂钩,以实现后期处理效果等等 NWShader最初是为Windows(通常是现代版本(win32))构建的,它使用DLL导出(让Windows加载它并获取一些OpenGL函数)和迂回(连接到其他函数)。我使用的技巧是,Win在检查sysdir之前会在当前目录中查找所有DLL,因此它会加载我的DLL。我有一个使用此方法重定向的DLL: #pragma comment(linker, "/export:oldFunc=nws

我有一个程序(NWShader),它与另一个程序的OpenGL调用(NWN)挂钩,以实现后期处理效果等等

NWShader最初是为Windows(通常是现代版本(win32))构建的,它使用DLL导出(让Windows加载它并获取一些OpenGL函数)和迂回(连接到其他函数)。我使用的技巧是,Win在检查sysdir之前会在当前目录中查找所有DLL,因此它会加载我的DLL。我有一个使用此方法重定向的DLL:

#pragma comment(linker, "/export:oldFunc=nwshader.newFunc)
将它们发送到我自己的DLL中的另一个命名函数。然后我进行任何处理,并从系统DLL调用原始函数

我需要将NWShader移植到Linux(NWN在两种版本中都存在)。据我所知,我需要创建一个共享库(.so文件)。如果这是在NWN可执行文件之前预加载的(我找到了一个shell脚本来处理这个),我的函数将被调用。唯一的问题是我需要调用原始函数(我想我会为此使用各种DLL动态加载方法),并且需要能够像钩住内部函数一样绕道而行


目前,我正在构建Ubuntu 9.10 x64(带有32位编译器标志)。我在谷歌上找不到多少帮助,但我不知道*nix社区到底把它称为什么。我可以编写C++代码,但我更习惯Windows。作为OpenGL,唯一需要修改以与Linux兼容的部分是挂钩代码和调用。有没有一种简单易行的方法可以做到这一点,或者需要重新创建迂回并动态加载原始函数地址?

在共享库中编写您自己的
oldfunc
,并预加载它,如您所述。但是还要编写一些初始化,调用原始库上的
dlopen()
,以及
dlsym()
,以获取指向原始
oldfunc
的函数指针。(这些函数分别是
LoadLibrary
GetProcAddress
的Unix等价物。)

可以通过
LD\u PRELOAD
完成库的预加载。从这里,您需要查看
dlopen
dlsym
调用,以获取原始库中的实际函数。如果你想用手做事,这就是全部

您还可以查看如何修改
ltrace
,以便提供钩住的函数(通过
-e
标志),并让ltrace为您处理簿记

[编辑]手动执行此操作的示例:

#include <dlfcn.h>
#include <stdio.h>

int (*orig_puts)(const char *);

int puts (const char * str) {
    void * handle = dlopen("/lib/libc.so.6", RTLD_NOW | RTLD_GLOBAL);
    orig_puts = dlsym(handle,"puts");
    fprintf (stderr,"I have hooked your puts\n");
    return orig_puts(str);
}

你可以考虑一种方法。代码附在后面。它允许你从一个特定的模块钩住一个特定的函数。

这听起来像是你正在寻找的。你可能已经找到了解决办法,但我想我会把它传下去。我使用linux和玩NWN,并希望能够使用nwshader。OGC(本文中提到)似乎是一种通过中断Opengl工作的多人游戏欺骗,很像nwshader所做的,但目的不同

Linux和Windows的弯路

This is a basic "Hello world" detour example in C++.
It does not make use of the Microsoft detour library.
Therefore it works on Windows, Linux and Mac.

I used the detour and undetour functions from OGC, but corrected it for IA64, and I also corrected the bug that made it crash on Fedora.
Also, it works with C++. If you want to use it with pure C, you need to remove the C++ style typecasts, as well as the template.
You don't need the template in C anyway, since C lets you convert any pointer to void* without any error or even warning.
Works with IA-32 & IA-64 & AMD64 x86 processors.

To be fully working, you would need to include a disassembler and adjust relative jumps in the 5+ bytes detourlength. You would also need to take care if you are writing over to the next memory page. (It almost never happens, but it could happen.)

On IA-64, you can maximally jump 4 Gigabytes. That should be sufficient for any normal program, however.

#if ( defined (_WIN32) || defined (_WIN64) )
    #define WIN32_LEAN_AND_MEAN
    #define WIN64_LEAN_AND_MEAN
    #include <windows.h>

    #define unprotect(addr,len) (VirtualProtect(addr,len,PAGE_EXECUTE_READWRITE,&oldprot))
    #define GETPAGESIZE()        getpagesize()

    DWORD oldprot ;

    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 ;
    }

    #define CLEAR_SCREEN "cls"

#else // LINUX / UNIX / OS X
    #include <unistd.h>
    #include <sys/mman.h>

    #define unprotect(addr,len)  (mprotect(addr,len,PROT_READ|PROT_WRITE|PROT_EXEC))
    #define GETPAGESIZE()         sysconf (_SC_PAGE_SIZE)
    #define CLEAR_SCREEN "reset"
#endif


#include <iostream>
#include <cstdlib>
#include <cstring>


    unsigned long uslngPageSize = 0    ;
    unsigned long uslngPageMask = 0    ;








    #define JMP_OPCODE 0xE9
    #define OPCODE_LENGTH 1
    #define DATATYPE_ADDRESS int
    #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



// 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));

    memcpy( voidptr_BackupForOriginalFunction, voidptr_AddressOfDetouredFunction, uslng_CopyLength) ;

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

    memset( reinterpret_cast<void*> (reinterpret_cast<unsigned long> (voidptr_BackupForOriginalFunction) + uslng_CopyLength),
            JMP_OPCODE, OPCODE_LENGTH ) ;



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


    memcpy( reinterpret_cast<void*> ( reinterpret_cast<unsigned long> (voidptr_BackupForOriginalFunction)
             + uslng_CopyLength + OPCODE_LENGTH), &Relocation, ADDRESS_LENGTH) ;



    unprotect(FuncGetPage(reinterpret_cast <unsigned long> (voidptr_AddressOfDetouredFunction)),uslngPageSize) ;

    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) ;
    unprotect(FuncGetPage(reinterpret_cast <unsigned long> (voidptr_BackupForOriginalFunction)),uslngPageSize) ;

    return voidptr_BackupForOriginalFunction ;
}


// 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)
                                   )
                )
            );
}


void SayHello()
{
    printf("Hello World\n");
}


void modified_SayHello()
{
    printf("**** World\n");
}

void (*original_SayHello)();
//#define SayHello_COPY 9
#define SayHello_COPY 6






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);
    unprotect(FuncGetPage(reinterpret_cast<unsigned long>(patched_function) ), uslngPageSize) ;
    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);
    unprotect(FuncGetPage(reinterpret_cast<unsigned long>(patched_function) ), uslngPageSize) ;
    if(original_function!=NULL)
        free( (void*) original_function) ;
}


int main()
{
    system( CLEAR_SCREEN ) ;
    uslngPageSize = GETPAGESIZE() ;
    uslngPageMask = ( ~(uslngPageSize - 1) ) ;
    printf("PageSize: %ld\n", uslngPageSize) ;
    printf("PageMask: 0x%08lX\n", uslngPageMask) ;
    SayHello() ;
    printf("Hotpatching now!!!\n") ;
    HOTPATCH(SayHello) ;
    printf("Hotpatched:\n") ;
    SayHello() ;
    printf("Backup:\n") ;
    original_SayHello() ;
    printf("Unpatching now\n") ;
    UNPATCH(SayHello);
    // expands to: unpatchfunc( (void*) SayHello, (unsigned char*) original_SayHello, (int) SayHello_COPY) ;
    printf("Unpatched:\n") ;
    SayHello() ;
    printf("EXIT_SUCCESS\n") ;
    return EXIT_SUCCESS ;
}
<代码>这是C++中一个基本的“hello World”绕行示例。 它不使用Microsoft迂回库。 因此,它可以在Windows、Linux和Mac上运行。 我使用了OGC中的迂回和取消迂回功能,但针对IA64对其进行了更正,并且还更正了导致其在Fedora上崩溃的错误。 此外,它与C++一起工作。如果您想用纯C来使用它,您需要移除C++风格的类型转换以及模板。 无论如何,您不需要C中的模板,因为C允许您将任何指针转换为void*,而不会出现任何错误甚至警告。 适用于IA-32、IA-64和AMD64 x86处理器。 要完全工作,您需要包括一个反汇编程序,并在5+字节的迂回长度中调整相对跳跃。如果要写入下一个内存页,还需要小心。(这几乎从未发生过,但也有可能发生。) 在IA-64上,您最多可以跳转4G字节。然而,对于任何正常的程序来说,这应该足够了。 #如果(已定义(_WIN32)|已定义(_WIN64)) #定义WIN32_精益_和_平均值 #定义WIN64_LEAN_和_MEAN #包括 #定义取消保护(addr,len)(虚拟保护(addr,len,PAGE\u EXECUTE\u READWRITE,&oldprot)) #定义GETPAGESIZE()GETPAGESIZE() 德沃德·奥尔德普洛特; 未签名的长getpagesize(void) { 静态长g_pagesize=0; 如果(!g_pagesize) { 系统信息系统信息; 获取系统信息(和系统信息); g_pagesize=系统_info.dwPageSize; } 返回(无符号长)g_pagesize; } #定义清除屏幕“cls” #else//LINUX/UNIX/OS-X #包括 #包括 #定义取消保护(addr,len)(mpprotect(addr,len,PROT_READ | PROT_WRITE | PROT_EXEC)) #定义GETPAGESIZE()sysconf(\u SC\u PAGE\u SIZE) #定义清除屏幕“重置” #恩迪夫 #包括 #包括 #包括 无符号长uslngPageSize=0; 无符号长uslngPageMask=0; #定义JMP_操作码0xE9 #定义操作码_长度1 #定义数据类型\地址int #定义地址长度(sizeof(数据类型地址)) #定义绕行所需的最小值(操作码长度+地址长度) #定义内部迂回系数1 #定义操作码\u未定义\u 0 //偏移量[引擎][功能名称]; //绕道长度[引擎][功能名称] #定义热补丁(函数名称)\ 原始函数名=TemplateFuncInterceptFunction(\ 原函数名\ 重新解释转换(&F)函数名称\ 重新解释强制转换(&修改函数名)\ 静态转换(函数名复制)\ ) #定义取消匹配(函数名称)\ 取消匹配功能(重新解释强制转换(重新解释强制转换)
$ ./a.out
Hello World
$ LD_PRELOAD=./libhook ./a.out
I have hooked your puts
Hello World
This is a basic "Hello world" detour example in C++.
It does not make use of the Microsoft detour library.
Therefore it works on Windows, Linux and Mac.

I used the detour and undetour functions from OGC, but corrected it for IA64, and I also corrected the bug that made it crash on Fedora.
Also, it works with C++. If you want to use it with pure C, you need to remove the C++ style typecasts, as well as the template.
You don't need the template in C anyway, since C lets you convert any pointer to void* without any error or even warning.
Works with IA-32 & IA-64 & AMD64 x86 processors.

To be fully working, you would need to include a disassembler and adjust relative jumps in the 5+ bytes detourlength. You would also need to take care if you are writing over to the next memory page. (It almost never happens, but it could happen.)

On IA-64, you can maximally jump 4 Gigabytes. That should be sufficient for any normal program, however.

#if ( defined (_WIN32) || defined (_WIN64) )
    #define WIN32_LEAN_AND_MEAN
    #define WIN64_LEAN_AND_MEAN
    #include <windows.h>

    #define unprotect(addr,len) (VirtualProtect(addr,len,PAGE_EXECUTE_READWRITE,&oldprot))
    #define GETPAGESIZE()        getpagesize()

    DWORD oldprot ;

    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 ;
    }

    #define CLEAR_SCREEN "cls"

#else // LINUX / UNIX / OS X
    #include <unistd.h>
    #include <sys/mman.h>

    #define unprotect(addr,len)  (mprotect(addr,len,PROT_READ|PROT_WRITE|PROT_EXEC))
    #define GETPAGESIZE()         sysconf (_SC_PAGE_SIZE)
    #define CLEAR_SCREEN "reset"
#endif


#include <iostream>
#include <cstdlib>
#include <cstring>


    unsigned long uslngPageSize = 0    ;
    unsigned long uslngPageMask = 0    ;








    #define JMP_OPCODE 0xE9
    #define OPCODE_LENGTH 1
    #define DATATYPE_ADDRESS int
    #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



// 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));

    memcpy( voidptr_BackupForOriginalFunction, voidptr_AddressOfDetouredFunction, uslng_CopyLength) ;

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

    memset( reinterpret_cast<void*> (reinterpret_cast<unsigned long> (voidptr_BackupForOriginalFunction) + uslng_CopyLength),
            JMP_OPCODE, OPCODE_LENGTH ) ;



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


    memcpy( reinterpret_cast<void*> ( reinterpret_cast<unsigned long> (voidptr_BackupForOriginalFunction)
             + uslng_CopyLength + OPCODE_LENGTH), &Relocation, ADDRESS_LENGTH) ;



    unprotect(FuncGetPage(reinterpret_cast <unsigned long> (voidptr_AddressOfDetouredFunction)),uslngPageSize) ;

    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) ;
    unprotect(FuncGetPage(reinterpret_cast <unsigned long> (voidptr_BackupForOriginalFunction)),uslngPageSize) ;

    return voidptr_BackupForOriginalFunction ;
}


// 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)
                                   )
                )
            );
}


void SayHello()
{
    printf("Hello World\n");
}


void modified_SayHello()
{
    printf("**** World\n");
}

void (*original_SayHello)();
//#define SayHello_COPY 9
#define SayHello_COPY 6






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);
    unprotect(FuncGetPage(reinterpret_cast<unsigned long>(patched_function) ), uslngPageSize) ;
    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);
    unprotect(FuncGetPage(reinterpret_cast<unsigned long>(patched_function) ), uslngPageSize) ;
    if(original_function!=NULL)
        free( (void*) original_function) ;
}


int main()
{
    system( CLEAR_SCREEN ) ;
    uslngPageSize = GETPAGESIZE() ;
    uslngPageMask = ( ~(uslngPageSize - 1) ) ;
    printf("PageSize: %ld\n", uslngPageSize) ;
    printf("PageMask: 0x%08lX\n", uslngPageMask) ;
    SayHello() ;
    printf("Hotpatching now!!!\n") ;
    HOTPATCH(SayHello) ;
    printf("Hotpatched:\n") ;
    SayHello() ;
    printf("Backup:\n") ;
    original_SayHello() ;
    printf("Unpatching now\n") ;
    UNPATCH(SayHello);
    // expands to: unpatchfunc( (void*) SayHello, (unsigned char*) original_SayHello, (int) SayHello_COPY) ;
    printf("Unpatched:\n") ;
    SayHello() ;
    printf("EXIT_SUCCESS\n") ;
    return EXIT_SUCCESS ;
}