简体   繁体   English

端口Win32 DLL挂钩到Linux

[英]Port Win32 DLL hook to Linux

I have a program (NWShader) which hooks into a second program's OpenGL calls (NWN) to do post-processing effects and whatnot. 我有一个程序(NWShader)挂钩到第二个程序的OpenGL调用(NWN)来做后处理效果和诸如此类的东西。

NWShader was originally built for Windows, generally modern versions (win32), and uses both DLL exports (to get Windows to load it and grab some OpenGL functions) and Detours (to hook into other functions). NWShader最初是为Windows构建的,通常是现代版本(win32),并使用DLL导出(让Windows加载它并获取一些OpenGL函数)和Detours(挂钩到其他函数)。 I'm using the trick where Win will look in the current directory for any DLLs before checking the sysdir, so it loads mine. 我正在使用Win在查看sysdir之前在当前目录中查找任何DLL的技巧,因此它会加载我的。 I have on DLL that redirects with this method: 我在DLL上使用此方法重定向:

#pragma comment(linker, "/export:oldFunc=nwshader.newFunc)

To send them to a different named function in my own DLL. 将它们发送到我自己的DLL中的不同命名函数。 I then do any processing and call the original function from the system DLL. 然后我做任何处理并从系统DLL调用原始函数。

I need to port NWShader to Linux (NWN exists in both flavors). 我需要将NWShader移植到Linux(NWN存在于两种版本中)。 As far as I can tell, what I need to make is a shared library (.so file). 据我所知,我需要做的是一个共享库(.so文件)。 If this is preloaded before the NWN executable (I found a shell script to handle this), my functions will be called. 如果这是在NWN可执行文件之前预加载的(我找到了一个shell脚本来处理它),我的函数将被调用。 The only problem is I need to call the original function (I would use various DLL dynamic loading methods for this, I think) and need to be able to do Detour-like hooking of internal functions. 唯一的问题是我需要调用原始函数(我会使用各种DLL动态加载方法,我认为)并且需要能够像Detour一样进行内部函数挂钩。

At the moment I'm building on Ubuntu 9.10 x64 (with the 32-bit compiler flags). 目前我正在使用Ubuntu 9.10 x64(使用32位编译器标志)。 I haven't been able to find much on Google to help with this, but I don't know exactly what the *nix community refers to it as. 我无法在Google上找到太多帮助,但我不确切地知道* nix社区所指的是什么。 I can code C++, but I'm more used to Windows. 我可以编写C ++代码,但我更习惯于Windows。 Being OpenGL, the only part the needs modified to be compatible with Linux is the hooking code and the calls. 作为OpenGL,需要修改以与Linux兼容的唯一部分是挂钩代码和调用。 Is there a simple and easy way to do this, or will it involve recreating Detours and dynamically loading the original function addresses? 有没有一种简单易行的方法,或者它是否涉及重新创建Detours并动态加载原始函数地址?

The library preloading can be done through LD_PRELOAD . 库预加载可以通过LD_PRELOAD完成。 From there you want to look at the dlopen and dlsym calls to get at the actual functions in the original library. 从那里你想看看dlopendlsym调用来获取原始库中的实际函数。 This is all if you want to do things by hand. 如果你想手工做,这就是全部。

You can also look at modifying ltrace in a way such that you provide the functions to hook (via the -e flag) and let ltrace handle the bookkeeping for you. 您还可以查看修改ltrace的方式,以便提供挂钩函数(通过-e标志)并让ltrace为您处理簿记。

[Edit] An example of doing this by hand: [编辑]手动执行此操作的示例:

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

And with a program such as 并有一个程序,如

#include <stdio.h>

int main () {
    puts ("Hello World");
    return 0;
}

You get the following: 你得到以下:

$ ./a.out
Hello World
$ LD_PRELOAD=./libhook ./a.out
I have hooked your puts
Hello World

This sounds like what you are looking for. 这听起来像你在寻找。 You may have found a solution already, but I thought I would pass this on. 您可能已经找到了解决方案,但我想我会通过这个解决方案。 I use linux and play NWN and would love to be able to use nwshader. 我使用linux并玩NWN,并希望能够使用nwshader。 OGC (referred to in the article) seems like it is some kind of multiplayer cheat that works by interrupting Opengl, much like what nwshader does but for a different purpose. OGC(在文章中提到)似乎是某种多人作弊,通过打断Opengl起作用,就像nwshader所做的那样,但是出于不同的目的。

http://aimbots.net/tutorials/14575-detours-linux-windows.html http://aimbots.net/tutorials/14575-detours-linux-windows.html

Detours for Linux & Windows 适用于Linux和Windows的Detours

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

Edit: Note that if you include this function in a 64 bit shared library/dll on Linux, you will get a segmentation fault. 编辑:请注意,如果在Linux上的64位共享库/ dll中包含此函数,则会出现分段错误。 This is because 64-Bit shared libraries can only be compiled with -fPIC, which makes detouring more difficult, because you have to read the PLT before each jump. 这是因为64位共享库只能用-fPIC编译,这使得绕道更加困难,因为你必须在每次跳转之前读取PLT。 You need to compile the shared library as 32 bit Shared Object (-m32) and run it with a 32-Bit executable. 您需要将共享库编译为32位共享对象(-m32)并使用32位可执行文件运行它。

Write your own oldfunc in a shared library and preload it, as you've mentioned. 正如您所提到的,在共享库中编写自己的oldfunc并预加载它。 But also write some initialization that calls dlopen() on the original library, and dlsym() to get the function pointer to the original oldfunc . 但是还要编写一些初始化,在原始库上调用dlopen() ,并使用dlsym()来获取指向原始oldfunc的函数指针。 (These functions are the Unix equivalents of LoadLibrary and GetProcAddress respectively.) (这些函数分别是LoadLibraryGetProcAddress的Unix等价物。)

You may look at a method of functions redirection in shared ELF libraries . 您可以查看共享ELF库中函数重定向方法。 There's code attached. 附有代码。 It allows you to hook a particular function from a particular module. 它允许您从特定模块挂钩特定功能。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM