Как скрыть имя экспортируемых символов в общей библиотеке - PullRequest
13 голосов
/ 10 марта 2012

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

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

Или есть ли что-то эквивалентное в разделяемой библиотеке ELF, например, порядковый номер в DLL формата 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. Но извлеките небольшую «специальную часть» (которая использует некоторые функции, не подходящие для статической библиотеки, например, слот TLS и т. Д.) В обычную общую библиотеку . Поскольку небольшая обычная разделяемая библиотека выполняет лишь несколько задач, а эта работа абсолютно нечувствительна, поэтому нет необходимости скрывать / скрывать ее API.

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

Ответы [ 5 ]

11 голосов
/ 10 марта 2012

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

__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 (который псевдоним / экспортирует только то, что вы хотите public ) в общую библиотеку.

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

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

псевдоним ("цель")

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

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

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

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

7 голосов
/ 10 марта 2012

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

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

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

addenda

В мире 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];

Затем в вашем общем объекте (.eg в 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 .Кстати, его книга о Искусственных Существах (совесть сознательной машины) очень интересна (и упоминает, что уловка - которую я предложил ему - в приложении).

6 голосов
/ 09 ноября 2012

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

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

и т. Д.

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

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

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

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

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

1 голос
/ 10 марта 2012

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

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

testfile.exp:

{
global:
  myExportedFunction1;
  myExportedFunction2;

local: *;
}

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

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

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

0 голосов
/ 03 марта 2015

Я искал решение для той же проблемы.Так что пока я не смог найти надежного решения.Тем не менее, в качестве подтверждения концепции я использовал 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 $@
    strip $@

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


executable.exe : executable.o libshared_object.so
    g++ -O2 -lshared_object -L. $< -o $@
    strip $@

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

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

исполняемый.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;
}
...