Порт Win32 DLL подключить к Linux - PullRequest
4 голосов
/ 16 апреля 2010

У меня есть программа (NWShader), которая подключается к вызовам OpenGL (NWN) второй программы для выполнения эффектов постобработки и еще много чего.

NWShader изначально был создан для Windows, как правило, в современных версиях (win32) и использует как экспорт DLL (для загрузки Windows и загрузки некоторых функций OpenGL), так и Detours (для подключения к другим функциям). Я использую трюк, где Win будет искать в текущем каталоге любые библиотеки DLL перед проверкой sysdir, поэтому он загружает мой. Я имею на DLL, которая перенаправляет с этим методом:

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

Чтобы отправить их в другую именованную функцию в моей собственной DLL. Затем я выполняю любую обработку и вызываю исходную функцию из системной DLL.

Мне нужно перенести NWShader в Linux (NWN существует в обоих вариантах). Насколько я могу судить, мне нужно сделать общую библиотеку (файл .so). Если это предварительно загружено до исполняемого файла NWN (я нашел сценарий оболочки для обработки этого), мои функции будут вызваны. Единственная проблема заключается в том, что мне нужно вызвать исходную функцию (я думаю, что для этого мне понадобятся различные методы динамической загрузки DLL), и я должен быть в состоянии выполнять обход внутренних функций, как Detour.

В данный момент я работаю на Ubuntu 9.10 x64 (с 32-битными флагами компилятора). Мне не удалось найти что-либо в Google, чтобы помочь с этим, но я точно не знаю, что сообщество * nix называет это. Я могу кодировать C ++, но я больше привык к Windows. Будучи OpenGL, единственной частью, которую необходимо изменить для совместимости с Linux, является код перехвата и вызовы. Есть ли простой и легкий способ сделать это, или это потребует воссоздания Detours и динамической загрузки исходных адресов функций?

Ответы [ 4 ]

5 голосов
/ 16 апреля 2010

Предварительную загрузку библиотеки можно выполнить через LD_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);
}

И с такой программой, как

#include <stdio.h>

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

Вы получаете следующее:

$ ./a.out
Hello World
$ LD_PRELOAD=./libhook ./a.out
I have hooked your puts
Hello World
3 голосов
/ 08 октября 2010

Это звучит как то, что вы ищете. Возможно, вы уже нашли решение, но я думал, что передам это. Я использую Linux и играю в NWN, и мне очень хотелось бы использовать nwshader. OGC (упоминаемая в статье), похоже, является неким многопользовательским читом, который работает, прерывая Opengl, очень похоже на то, что делает nwshader, но для другой цели.

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

Объезды для 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 ;
}

Edit: обратите внимание, что если вы включите эту функцию в 64-битную разделяемую библиотеку / dll в Linux, вы получите ошибку сегментации. Это связано с тем, что 64-битные разделяемые библиотеки могут быть скомпилированы только с -fPIC, что делает обход более сложным, поскольку вы должны читать PLT перед каждым переходом. Вам необходимо скомпилировать разделяемую библиотеку как 32-битный общий объект (-m32) и запустить ее с 32-битным исполняемым файлом.

2 голосов
/ 16 апреля 2010

Напишите свой собственный oldfunc в общей библиотеке и предварительно загрузите его, как вы уже упоминали. Но также напишите некоторую инициализацию, которая вызывает dlopen() в исходной библиотеке и dlsym(), чтобы получить указатель функции на исходную oldfunc. (Эти функции являются эквивалентами Unix LoadLibrary и GetProcAddress соответственно.)

0 голосов
/ 15 июня 2010

Вы можете посмотреть метод перенаправления функций в общих библиотеках ELF . Там код прилагается. Позволяет подключить определенную функцию из определенного модуля.

...