Подтвердить что ты не робот

Как скрыть имя экспортируемых символов в общей библиотеке

Для VC я могу написать файл DEF и использовать директиву "NONAME", чтобы оставить только порядковый номер в таблице экспорта dll.

Как я могу сделать то же самое с общей библиотекой gcc и ELF?

Или, есть ли что-то эквивалентное в ELF-общей библиотеке, например порядковый номер в DL-формате формата PE? Если нет, как я могу скрыть экспортированное имя символа в общей библиотеке?

======================================

ОБНОВЛЕНИЕ: Дополнительные описания:

В Windows вы можете экспортировать функцию только поместить целочисленный идентификатор (порядковый номер) с пустым именем.

Чтобы показать это, нормальный макет для таблицы экспорта dll выглядит следующим образом: http://home.hiwaay.net/~georgech/WhitePapers/Exporting/HowTo22.gif.

"NONAME" выглядит следующим образом: http://home.hiwaay.net/~georgech/WhitePapers/Exporting/HowTo23.gif.

Обратите внимание, что имя функции - "N/A" во втором изображении. Вот полное объяснение этого: hxxp://home.hiwaay.net/~georgech/WhitePapers/Exporting/Exp.htm.

======================================

ОБНОВЛЕНИЕ: Большое спасибо за каждого, кто делает мне совет. Наконец, я решил сохранить статическую библиотеку на платформах linux/posix. Но извлеките небольшую "специальную часть" (которая использует некоторые функции, не подходящие для статического lib, например: TLS Slot и т.д.) В обычную разделяемую библиотеку. Поскольку небольшая обычная разделяемая библиотека выполняет только несколько вещей, и эти работы полностью нечувствительны, поэтому нет необходимости скрывать/скрывать свои API-интерфейсы.

Я думаю, что это самый простой способ решить мою проблему: -D

4b9b3361

Ответ 1

Предыдущие ответы, касающиеся атрибута ((видимость ( "скрытый" )), хороши, если вы хотите сохранить код в долгосрочной перспективе, но если у вас есть только несколько символов, которые вы хотите видеть и хотите быстро исправить... На символы, которые вы хотите экспортировать, добавьте

__attribute__ ((visibility ("default"))) 

Затем вы можете передать -fvisibility=hidden в компилятор

Здесь есть подробное объяснение:

http://gcc.gnu.org/wiki/Visibility

Изменить: альтернативой было бы создание статической библиотеки/архива (сделать архив .a с помощью ar -cru mylib.a *.o) или объединить объекты в один файл объекта в соответствии с этим объединить два GCC скомпилировал .o объектные файлы в третий файл .o

Если вы спрашиваете: "Зачем комбинировать объектные файлы вместо создания статической библиотеки?"... потому что компоновщик будет обрабатывать файлы .o иначе, чем .a файлы (я не знаю, почему, именно так оно и есть), в частности, это позволит вам связать файл .o в общей библиотеке или двоичном файле, даже если все символы скрыты (даже те, которые вы используете). Это имеет дополнительное преимущество для сокращения времени запуска (на один меньше DSO и намного меньше символов для поиска) и двоичного размера (символы обычно составляют ~ 20% от размер и зачистка только заботятся о половине этого - только внешне видимые части)

для двоичных файлов strip --strip-all -R .note -R .comment mybinary

для библиотек strip --strip-unneeded -R .note -R .comment mylib.so

Подробнее о преимуществах статической ссылки здесь: http://sta.li/faq, но они не обсуждают вопросы лицензирования, которые являются основной причиной не используйте статическую библиотеку, и поскольку вы хотите скрыть свой API, это может быть проблемой

Теперь, когда мы знаем, есть объект, который является "символом чистым", мы можем использовать наш комбинированный объект для создания libpublic.so, связывая private.o и public.c(который псевдонизирует/экспортирует только то, что вы хотите публиковать ) в общую библиотеку.

Этот метод хорошо подходит для поиска "лишнего кода", который также не используется в вашем публичном API. Если вы добавите -fdata-sections -ffunction-sections в свои сборки объектов, когда вы свяжетесь с -Wl,--gc-sections,--print-gc-sections, он устранит неиспользуемые разделы и распечатает вывод того, что было удалено.

Изменить 2 - или вы можете скрыть весь API и псевдоним только те функции, которые вы хотите экспортировать

псевдоним ( "target" )

Атрибут alias заставляет объявление выдаваться как псевдоним для другого символа, который должен быть указан. Например,

          void __f () { /* Do something. */; }
          void f () __attribute__ ((weak, alias ("__f")));

определяет f' to be a weak alias for __ f '. В С++ должно использоваться искомое имя для цели. Это ошибка, если `__f 'не определен в одной и той же единицы перевода.

Не все целевые машины поддерживают этот атрибут.

Ответ 2

Вы можете использовать атрибут функции GCC для видимости и сделать его скрытым, то есть добавив __attribute__((visibility ("hidden"))) во многие соответствующие места в файле заголовка.

Затем вы скроете свои бесполезные символы и сохраните хорошие.

Это расширение GCC (возможно, поддерживается другими компиляторами, такими как Clang или Icc).

добавлений

В мире Linux общая библиотека должна экспортировать функции (или, возможно, глобальные данные) по их именам, как опубликовано в файлах заголовков. В противном случае не называйте эти функции "экспортированными" - их нет!

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

Чтобы быть более конкретным, вы могли бы иметь в своей основной программе глобальный массив указателей на функции

 // in a global header.h
  // signature of some functions
 typedef void signature_t(int, char*);
 #define MAX_NBFUN 100
 // global array of function pointers
 extern signature_t *funtab[MAX_NBFUN];

то в вашем main.c файле вашей программы

 signature_t *funtab[MAX_NBFUN];

Затем в вашем общем объекте (.e.g. в myshared.c файле, скомпилированном в libmyshared.so) функция-конструктор:

 static my_constructor(void) __attribute__((constructor));

 static myfun(int, char*); // defined elsewhere is the same file
 static void 
 my_constructor(void) { // called at shared object initialization
    funtab[3] = myfun;
 }

Позже ваша основная программа (или какой-либо другой общий объект) может вызвать

 funtab[3](124, "foo");

но я бы никогда не назвал такие вещи "экспортированными" функциями, только достижимыми функциями.

Пример программы, выполняющей подобные вещи, - это MELT (которые не используют массивы, но более сложные значения, выделенные кучей). Другим примером программы, выполняющей трюки указателей функций массива, является программа J.Pitrat CAIA/Malice. BTW, его книга о Искусственные существа (совесть сознательной машины) очень интересны (и упоминает тот трюк, который я предложил ему - в приложение).

Ответ 3

Чтобы скрыть значение экспортируемых функций в UNIX, вы можете просто обфускать их имена простым переименованием, используя #defines. Вот так:

#define YourGoodFunction_CreateSomething              MeaninglessFunction1
#define YourGoodFunction_AddSomethingElseToSomething  lxstat__
#define YourGoodFunction_SaveSomething                GoAway_Cracker
#define YourGoodFunction_ReleaseSomething             Abracadabra

и т.д.

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

  • получите список имен ваших реальных функций, используйте grep, awk, cut и т.д.
  • подготовить словарь бессмысленных имен
  • напишите генератор script (или двоичный), который выведет заголовочный файл C С#defines, как показано выше.

Вопрос только в том, как вы можете получить словарь. Ну, я вижу здесь несколько вариантов:

  • вы можете попросить своих сотрудников случайно ввести их на клавиатуре; -)
  • генерировать случайные строки типа: read (/dev/urandom, 10-20 bytes) | base64
  • используйте некоторый настоящий словарь (общий английский, определенный домен)
  • собирайте имена API реальной системы и немного измените их: __lxstat -> lxstat__

это ограничено только вашим воображением.

Ответ 4

Вы можете написать версию script и передать ее компоновщику для этого.

Простой script выглядит следующим образом:

testfile.exp:

{
global:
  myExportedFunction1;
  myExportedFunction2;

local: *;
}

Затем свяжите исполняемый файл со следующими параметрами:

  -Wl,--version-script=testfile.exp

При применении к общей библиотеке это все равно будет отображать символы в файле .so для целей отладки, но невозможно получить доступ к ним из-за пределов библиотеки.

Ответ 5

Я искал решение для той же проблемы. Таким образом, далеко не удалось найти надежное решение. Однако в качестве доказательства концепции я использовал objcopy для достижения желаемых результатов. В принципе, после компиляции объектного файла я переопределяю некоторые его символы. Затем переведенный объектный файл используется для создания конечного общего объекта или исполняемого файла. В результате имена классов/методов, которые можно использовать в качестве подсказки для обратной инженерии моего алгоритма, полностью переименованы в некоторые бессмысленные имена m1, m2, m3.

Вот тест, который я использовал, чтобы гарантировать, что идея работает:

Makefile:

all: libshared_object.so executable.exe

clean:
    rm *.o *.so *.exe

libshared_object.so : shared_object.o
    g++ -fPIC --shared -O2 $< -o [email protected]
    strip [email protected]

shared_object.o : shared_object.cpp interface.h
    g++ -fPIC -O2 $< -c -o [email protected]
    objcopy --redefine-sym _ZN17MyVerySecretClass14secret_method1Ev=m1 \
            --redefine-sym _ZN17MyVerySecretClass14secret_method2Ev=m2 \
            --redefine-sym _ZN17MyVerySecretClass14secret_method3Ev=m3 [email protected]


executable.exe : executable.o libshared_object.so
    g++ -O2 -lshared_object -L. $< -o [email protected]
    strip [email protected]

executable.o : executable.cpp interface.h
    g++ -O2 -lshared_object -L. $< -c -o [email protected]
    objcopy --redefine-sym _ZN17MyVerySecretClass14secret_method1Ev=m1 \
            --redefine-sym _ZN17MyVerySecretClass14secret_method2Ev=m2 \
            --redefine-sym _ZN17MyVerySecretClass14secret_method3Ev=m3 [email protected]

run: all
    LD_LIBRARY_PATH=. ./executable.exe

interface.h

class MyVerySecretClass
{
private:
    int secret_var;
public:
    MyVerySecretClass();
    ~MyVerySecretClass();
    void secret_method1();
    void secret_method2();
    void secret_method3();
};

shared_object.cpp

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

#include "interface.h"

MyVerySecretClass::MyVerySecretClass()
    : secret_var(0)
{}

MyVerySecretClass::~MyVerySecretClass()
{
    secret_var = -1;
}

void MyVerySecretClass::secret_method1()
{
    ++secret_var;
}

void MyVerySecretClass::secret_method2()
{
    printf("The value of secret variable is %d\n", secret_var);
}

void MyVerySecretClass::secret_method3()
{
    char cmdln[128];
    sprintf( cmdln, "pstack %d", getpid() );
    system( cmdln );
}

executable.cpp

#include "interface.h"

int main ( void )
{
    MyVerySecretClass o;
    o.secret_method1();
    o.secret_method2();
    o.secret_method1();
    o.secret_method2();
    o.secret_method1();
    o.secret_method2();
    o.secret_method3();
    return 0;
}