stringtranslate.com

инъекция DLL

В компьютерном программировании внедрение DLL — это метод, используемый для запуска кода в адресном пространстве другого процесса путем принудительной загрузки им динамически подключаемой библиотеки . [1] Внедрение DLL часто используется внешними программами для влияния на поведение другой программы таким образом, который ее авторы не предполагали или не намеревались сделать. [1] [2] [3] Например, внедренный код может перехватывать вызовы системных функций, [4] [5] или считывать содержимое текстовых полей пароля , что невозможно сделать обычным способом. [6] Программа, используемая для внедрения произвольного кода в произвольные процессы, называется инжектором DLL .

Подходы к Microsoft Windows

В Microsoft Windows существует несколько способов заставить процесс загрузить и выполнить код в DLL, который не был задуман авторами:

Подходы к Unix-подобным системам

В операционных системах типа Unix с динамическим компоновщиком на основе ld.so (в BSD ) и ld-linux.so (в Linux ) произвольные библиотеки могут быть связаны с новым процессом, если указать путь к библиотеке в переменной среды LD_PRELOAD , которая может быть установлена ​​глобально или индивидуально для одного процесса. [37]

Например, в системе Linux эта команда запускает команду «prog» с общей библиотекой из файла «test.so», связанной с ней во время запуска:

LD_PRELOAD = "./test.so"  прог

Такая библиотека может быть создана таким же образом, как и другие общие объекты . С GCC это включает компиляцию исходного файла, содержащего новые глобальные переменные для связывания, с опцией -fpic или -fPIC , [38] и связывание с опцией -shared . [39] Библиотека имеет доступ к внешним символам, объявленным в программе, как и любая другая библиотека.

В macOS следующая команда запускает команду «prog» с общей библиотекой из файла «test.dylib», связанной с ней во время запуска: [40]

DYLD_INSERT_LIBRARIES = "./test.dylib" DYLD_FORCE_FLAT_NAMESPACE = 1 программа  

Также возможно использовать методы отладчика в Unix-подобных системах. [41]

Пример кода

Копирование загруженной с помощью LoadLibrary библиотеки DLL в удаленный процесс

Поскольку нет LoadLibrary()вызова для загрузки DLL в сторонний процесс, вам придется скопировать локально загруженную DLL в удаленно выделенную память. Следующий прокомментированный код показывает, как это сделать.

#include <Windows.h> #include <TlHelp32.h> #include <iostream> #include <memory> #include <system_error> #include <charconv> #include <vector> #include <cassert>        #если определено(_MSC_VER) #pragma предупреждение(отключить: 6387) #endifс использованием пространства имен std ;  using XHANDLE = unique_ptr < void , decltype ([]( void * h ) { h && h != INVALID_HANDLE_VALUE && CloseHandle ( ( HANDLE ) h ); }) > ; using XHMODULE = unique_ptr < remove_reference_t < decltype ( * HMODULE ()) > , decltype ([]( HMODULE hm ) { hm && FreeLibrary ( hm ); }) > ;                               MODULEENTRY32W getModuleDescription ( HMODULE hmModule ); size_t maxReadableRange ( void * pRegion ); string getAbsolutePathA ( char const * fileName , char const * err ); DWORD dumbParseDWORD ( wchar_t const * str ); wstring getAbsolutePath ( wchar_t const * makeAbsolute , char const * errStr ); [[ noreturn ]] void throwSysErr ( char const * str );                                  constexpr wchar_t const * LOADER_DLL_NAME = L "loaderDll.dll" ; constexpr char const * LOADER_THREAD_PROC = "loadLibraryThread" ;          int wmain ( int argc , wchar_t ** argv ) { try { if ( argc < 3 ) return EXIT_FAILURE ; wchar_t const * processId = argv [ 1 ], * remoteLoadedDll = argv [ 2 ], * initData = argc >= 4 ? argv [ 3 ] : L "" ; DWORD dwProcessId = dumbParseDWORD ( processId ); XHANDLE xhProcess ( OpenProcess ( PROCESS_ALL_ACCESS , FALSE , dwProcessId ) ); if ( ! xhProcess . get () ) throwSysErr ( "невозможно открыть удаленный процесс с неограниченным доступом" ); XHMODULE xhmLocalLoader ; MODULEENTRY32W meLocalLoader ; for ( ; ; ) { xhmLocalLoader . reset ( LoadLibraryW ( LOADER_DLL_NAME ) ); if ( ! xhmLocalLoader . get () ) throwSysErr ( "невозможно локально загрузить DLL загрузчика" ); // получить начальный адрес и размер модуля meLocalLoader = getModuleDescription ( ( HMODULE ) xhmLocalLoader . get () ); // попытаться выделить область памяти во внешнем процессе с тем же размером, который занимает DLL в нашем процессе if ( VirtualAllocEx ( xhProcess . get (), meLocalLoader . modBaseAddr , meLocalLoader . modBaseSize , MEM_RESERVE | MEM_COMMIT , PAGE_EXECUTE_READWRITE ) ) break ; // выделение не удалось, освободить библиотеку xhmLocalLoader .                                                                   reset ( nullptr ); // попытка зарезервировать диапазон адресов, который библиотека занимала ранее, чтобы предотвратить // повторное использование этого диапазона адресов при следующем вызове LoadLibrary(). if ( ! VirtualAlloc ( meLocalLoader . modBaseAddr , meLocalLoader . modBaseSize , MEM_RESERVE , PAGE_NOACCESS ) ) throwSysErr ( "невозможно зарезервировать диапазон адресов ранее сопоставленной DLL" ); } LPTHREAD_START_ROUTINE loaderThreadProc = ( LPTHREAD_START_ROUTINE ) GetProcAddress ( ( HMODULE ) xhmLocalLoader . get (), :: LOADER_THREAD_PROC ); if ( ! loaderThreadProc ) throwSysErr ( "невозможно получить точку входа в процедуру" ); // копируем все читаемое содержимое DLL в целевой процесс if ( SIZE_T скопировано ; ! WriteProcessMemory ( xhProcess . get (), meLocalLoader . modBaseAddr , meLocalLoader . modBaseAddr , meLocalLoader . modBaseSize , & скопировано ) && GetLastError () != ERROR_PARTIAL_COPY ) throwSysErr ( "невозможно скопировать DLL загрузчика в удаленный процесс" ); // создаем две объединенные строки C, содержащие DLL для загрузки, а также параметр, // переданный удаленно загруженной DLL wstring data ( getAbsolutePath ( remoteLoadedDll , "невозможно получить абсолютный путь к DLL для удаленной загрузки" ) ); data += L '\0' ; data += initData ; data += L '\0' ; size_t dataSize = data . размер () * размер ( wchar_t ); авто initStrErr = []() { throwSysErr (                                                            "не удалось скопировать данные инициализации в DLL-загрузчик" ); }; void * remoteData ; // удаленно выделяем память, достаточную для хранения как минимум обеих наших строк if ( ! ( remoteData = VirtualAllocEx ( xhProcess . get (), nullptr , dataSize , MEM_RESERVE | MEM_COMMIT , PAGE_READWRITE )) initStrErr ( ); // записываем обе наши строки в удаленную память if ( SIZE_T скопировано ; ! WriteProcessMemory ( xhProcess . get (), remoteData , data . data (), dataSize , & скопировано ) || скопировано != dataSize ) initStrErr (); // создаем поток удаленного загрузчика DLL; указанная точка входа имеет тот же адрес в нашем процессе , что и удаленный адрес // передаем этому потоку адрес наших обеих удаленно скопированных строк XHANDLE xhRemoteInitThread ( CreateRemoteThread ( xhProcess.get (), nullptr , 0 , loaderThreadProc , remoteData , 0 , nullptr ) ); if ( ! xhRemoteInitThread.get ( ) ) throwSysErr ( "failed to create remote initializaton thread" ); // ждем завершения нашего удаленного потока загрузчика // он должен это сделать очень скоро , поскольку его единственная задача - скопировать строки для удаленно загруженной DLL и загрузить эту DLL if ( WaitForSingleObject ( xhRemoteInitThread.get ( ), INFINITE ) == WAIT_FAILED ) throwSysErr ( "can't wait for remote initialization thread" ); DWORD dwInitThreadExitCode ; если ( ! GetExitCodeThread ( xhRemoteInitThread . get (), & dwInitThreadExitCode ) ) throwSysErr                                                         ( "невозможно получить код успеха потока инициализации" ); // проверка кода выхода удаленного загрузчика, он должен быть NO_ERROR (0) if ( dwInitThreadExitCode != NO_ERROR ) throw system_error ( ( int ) dwInitThreadExitCode , system_category (), "Ошибка LoadLibrary() в dll удаленного загрузчика" ); } catch ( exception const & se ) { cout << se . what () << endl ; } }                   MODULEENTRY32W getModuleDescription ( HMODULE hmModule ) { // возвращает абсолютный путь для заданного дескриптора модуля auto getModulePath = []( HMODULE hm , char const * err ) -> wstring { wchar_t modulePath [ MAX_PATH ]; if ( DWORD dwRet = GetModuleFileNameW ( hm , modulePath , MAX_PATH ); ! dwRet || dwRet >= MAX_PATH ) throwSysErr ( err ); return modulePath ; }; // путь к модулю локальной DLL wstring moduleAbsolute ( getModulePath ( hmModule , "невозможно получить абсолютный путь для локальной DLL-загрузчика" ) ); XHANDLE xhToolHelp ( CreateToolhelp32Snapshot ( TH32CS_SNAPMODULE , GetCurrentProcessId () ) ); auto toolHelpErr = []() { throwSysErr ( "невозможно перечислить модули в процессе внедрения" ); }; if ( xhToolHelp . get () == INVALID_HANDLE_VALUE ) toolHelpErr (); MODULEENTRY32W me ; me . dwSize = sizeof me ; if ( ! Module32FirstW ( xhToolHelp . get (), & me ) ) toolHelpErr (); for ( ; ; ) { // имеет ли текущее изображение в снимке тот же путь, что и DLL, которая задана дескриптором модуля // нет необходимости сравнивать без учета регистра, поскольку мы получили оба пути из ядра, так что они должны точно совпадать if ( getModulePath ( me . hModule , "can't get absolute path for toolhelp-enumerated DLL name" ) == moduleAbsolute ) return me                                                                              ; me.dwSize = sizeof me ; if ( ! Modules32NextW ( xhToolHelp.get ( ) , & me ) ) toolHelpErr ( ) ; } }        [[ noreturn ]] void throwSysErr ( char const * str ) { throw system_error ( ( int ) GetLastError (), system_category (), str ); }          DWORD dumbParseDWORD ( wchar_t const * str ) { // from_chars идиота, потому что для символов юникода нет from_chars DWORD dwRet = 0 ; while ( * str ) dwRet = dwRet * 10 + ( unsigned char )( * str ++ - L '0' ); return dwRet ; }                    wstring getAbsolutePath ( wchar_t const * makeAbsolute , char const * errStr ) { // получить абсолютный путь для заданного относительного пути wstring path ( MAX_PATH , L '\0' ); DWORD dwLength ; if ( ! ( dwLength = GetFullPathNameW ( makeAbsolute , MAX_PATH , path . data (), nullptr )) ) throwSysErr ( errStr ); // если deRet == MAX_PATH , мы можем пропустить нулевой символ завершения, рассматривать это как ошибку, иначе if ( dwLength >= MAX_PATH ) throw invalid_argument ( errStr ); path . resize ( dwLength ); return path ; }                                   

Основная проблема, решенная здесь, заключается в том, что локально загруженная DLL, скопированная в удаленный процесс, должна занимать те же адреса, что и в процессе внедрения. Приведенный выше код делает это, выделяя память для того же диапазона адресов, который был занят ранее в процессе внедрения. Если это не удается, DLL локально освобождается, прежний диапазон адресов помечается как зарезервированный, и вызов LoadLibrary()повторяется. Резервируя прежний диапазон адресов, код предотвращает LoadLibrary()назначение следующего диапазона адресов, который использовался ранее.

Главным недостатком такого подхода является то, что DLL, скопированная во внешний процесс, заключается в том, что нет никаких других зависимостей библиотеки DLL этой DLL, загруженной во внешнее адресное пространство, или указателей, например вызовов функций, на DLL, загруженные внешним процессом, которые корректируются в соответствии с зависимостями скопированной DLL. К счастью, DLL обычно имеют предпочтительные адреса загрузки, которые учитываются загрузчиком ядра . Некоторые DLL, например, kernel32.dllнадежно загружаются в начале, когда адресное пространство процесса занято исполняемым образом и его зависимыми DLL. Они обычно имеют надежные и неконфликтующие адреса. Таким образом, скопированная DLL может использовать любые kernel32.dllвызовы, например, для загрузки другой DLL со всеми преимуществами локально загруженной DLL, т. е. имея все относительные зависимости библиотек. Путь к этой DLL копируется во внешнее адресное пространство и задается как параметр void для функции потока. Вышеуказанная реализация также позволяет иметь дополнительные параметры, которые передаются удаленно скопированной DLL после строки с удаленно загруженной DLL, чтобы передать ее в эту DLL.

Следующий код является источником удаленно скопированной DLL-библиотеки загрузчика, которая выполняет только kernel32.dllвызовы:

#include <Windows.h> #include <атомарный>  с использованием пространства имен std ;  BOOL APIENTRY DllMain ( HMODULE hModule , DWORD ul_reason_for_call , LPVOID lpReserved ) { return TRUE ; }          DWORD WINAPI loadLibraryThread ( LPVOID lpvThreadParam );     // MSVC / clang-cl искажение #if defined(_M_IX86) #pragma comment(linker, "/export:loadLibraryThread=?loadLibraryThread@@YGKPAX@Z") #elif defined(_M_X64) #pragma comment(linker, "/export:loadLibraryThread=?loadLibraryThread@@YAKPEAX@Z") #else #error неподдерживаемая платформа #endifDWORD WINAPI loadLibraryThread ( LPVOID lpvThreadParam ) { // используем атомарные типы, чтобы не дать "оптимизатору" заменить мой код на // вызовы библиотек wsclen или memcpy на внешние адреса, которые на самом деле недопустимы // с этой скопированной DLL // игнорируем любые барьеры атомарной загрузки, так как это не должно быть быстро atomic_wchar_t const // путь к библиотеке для загрузки изнутри * libPath = ( atomic_wchar_t * ) lpvThreadParam , // указатель на параметры, заданные для этой библиотеки * data = libPath ; // передаем данные в фактические параметры while ( * data ++ ); HANDLE hOutboundEvent ; // создаем именованное событие для уведомления удаленной DLL о том, что данные уже скопированы // необходимо, поскольку выполнение удаленной DLL начинается сразу после LoadLibrary()S if ( ! ( hOutboundEvent = CreateEventA ( nullptr , FALSE , FALSE , "nasty hackers" )) ) return GetLastError (); // размер параметров, переданных DLL size_t dataSize = 0 ; while ( data [ dataSize ++ ] ); if ( dataSize >= MAX_PATH ) return ERROR_INVALID_PARAMETER ; // очищаем LoadLibrary() со всеми зависимостями DLL HMODULE hm = LoadLibraryW ( ( wchar_t * ) libPath ); if ( ! hm ) return GetLastError (); // получить адрес экспорта параметров из загруженной DLL wchar_t volatile ( & initData )[ MAX_PATH ] = * ( wchar_t ( * )[ MAX_PATH ]) GetProcAddress ( hm , "initData" ); // загруженная DLL не предоставляет такой экспорт, т. е. она не полагается на параметры ? if ( !                                                    initData ) return NO_ERROR ; // копируем параметры в DLL for ( size_t i = 0 ; i != dataSize ; initData [ i ] = data [ i ], ++ i ); // уведомляем о доступности параметров if ( ! SetEvent ( hOutboundEvent ) ) return GetLastError (); return NO_ERROR ; }                    

Последний код показывает пример DLL, загруженной загрузчиком DLL, который выводит параметры в файл.

#include <Windows.h> #include <fstream> #include <atomic>   с использованием пространства имен std ;  #if defined(_MSC_VER) #pragma warning(disable: 6387) // возвращаемый дескриптор может быть нулевым #endif#if defined(_M_IX86) #pragma comment(linker, "/export:DllMain=_DllMain@12") #elif defined(_M_X64) #pragma comment(linker, "/export:DllMain=_DllMain@12") #else #error неподдерживаемая платформа #endifс использованием пространства имен std ;  DWORD WINAPI myThread ( LPVOID lpvThreadParam );     BOOL APIENTRY DllMain ( HMODULE hModule , DWORD dwReason , LPVOID lpReserved ) { switch ( dwReason ) { case DLL_PROCESS_ATTACH : // создать поток, так как нет экспорта, вызванного из загрузчика DLL CreateThread ( nullptr , 0 , myThread , nullptr , 0 , nullptr ); default : break ; } return TRUE ; }                    extern "C" __declspec ( dllexport ) wchar_t initData [ MAX_PATH ] = { 0 };      DWORD WINAPI myThread ( LPVOID lpvThreadParam ) { // ждем заполнения initData загрузчиком DLL // пропустите это, если вы не полагаетесь на initData // так как именованное событие "nasty hackers" было создано нашими собственными DLL // LoadLibrary() мы просто подключаемся к именованному событию, но не создаем его if ( WaitForSingleObject ( CreateEventA ( nullptr , FALSE , FALSE , "nasty hackers" ), INFINITE ) != WAIT_OBJECT_0 ) return 0 ; // записываем параметры в файл для проверки функции // следующий код не работает, если DLL не связана статически по неизвестным причинам wofstream wofs ; wofs . open ( "c: \\ Users \\ xxx \\ test.txt" , ofstream :: out | ofstream :: trunc ); wofs << initData << endl ; return 0 ; }                             

Важным фактом является то, что нет экспортов, вызываемых из DLL-загрузчика, вместо этого вся инициализация выполняется из DllMain. Единственный экспорт — это initData, который получает параметры, заданные процессом внедрения через DLL-загрузчик. И нужно знать, что поток, созданный из функции DllMain, не планируется до тех пор, пока его DLL_THREAD_ATTACHфункция не завершится успешно. Поэтому может не быть никакой синхронизации изнутри DllMainс созданным потоком.

Ссылки

  1. ^ ab Джеймс Шьюмейкер (2006). "Анализ внедрения DLL" (PDF) . Презентация GSM . Bluenotch. Архивировано из оригинала (PDF) 3 декабря 2008 г. . Получено 31 августа 2008 г. .
  2. ^ ab Iczelion (август 2002 г.). "Учебник 24: Windows Hooks". Домашняя страница Iczelion Win32 Assembly . Архивировано из оригинала 1 августа 2008 г. Получено 31 августа 2008 г.
  3. Rocky Pulley (19 мая 2005 г.). «Расширение диспетчера задач с помощью внедрения DLL». CodeProject . Архивировано из оригинала 6 февраля 2009 г. . Получено 1 сентября 2008 г. .
  4. ^ ab Nasser R. Rowhani (23 октября 2003 г.). "Учебник по внедрению DLL и перехвату функций". CodeProject . Архивировано из оригинала 15 апреля 2018 г. . Получено 31 августа 2008 г. .
  5. ^ abc Ivo Ivanov (2 декабря 2002 г.). "API hooking reveal". CodeProject . Получено 31 августа 2008 г. .
  6. ^ abcd Роберт Кустер (20 августа 2003 г.). "Три способа внедрить свой код в другой процесс". CodeProject . Получено 31 августа 2008 г. .
  7. ^ "Работа со значением реестра AppInit_DLLs". Справка и поддержка Microsoft . Microsoft. 21 ноября 2006 г. Получено 31 августа 2008 г.
  8. Raymond Chen (13 декабря 2007 г.). «AppInit_DLLs следует переименовать в Deadlock_Or_Crash_Randomly_DLLs». The Old New Thing . Microsoft . Получено 31 августа 2008 г. .
  9. ^ "dllmain.c". ReactOS . ReactOS Foundation. 8 июля 2008 г. Получено 31 августа 2008 г.[ постоянная мертвая ссылка ]
  10. ^ AppInit_DLL в Windows 7 и Windows Server 2008 R2
  11. ^ "AppInit DLLs and Secure Boot". MSDN . Получено 29 марта 2016 г. .
  12. ^ "'AtomBombing' Microsoft Windows через внедрение кода". Dark Reading . Получено 20 апреля 2017 г. .
  13. ^ abcd Трент Уоддингтон. "InjectDLL". Архивировано из оригинала 30 декабря 2019 г. Получено 31 августа 2008 г.
  14. ^ "Dll ​​Injection". DreamInCode.net . MediaGroup1. 4 мая 2006 г. Архивировано из оригинала 2 сентября 2008 г. Получено 31 августа 2008 г.{{cite web}}: CS1 maint: неподходящий URL ( ссылка )
  15. ^ Грег Дженкинс (ноябрь 2007 г.). "DLL Injection Framework". Ring3 Circus . WordPress. Архивировано из оригинала 28 июня 2020 г. Получено 31 августа 2008 г.
  16. Дрю Бентон (17 августа 2007 г.). «Более полное решение для внедрения DLL с использованием CreateRemoteThread». CodeProject . Получено 1 сентября 2008 г.
  17. ^ "CreateProcess". Platform SDK для Windows XP SP2 . Microsoft . Получено 31 августа 2008 г. .
  18. ^ "PROCESS_INFORMATION". Platform SDK для Windows XP SP2 . Microsoft . Получено 31 августа 2008 г. .
  19. ^ "GetWindowThreadProcessId Function". Platform SDK для Windows XP SP2 . Microsoft . Получено 31 августа 2008 г. .
  20. ^ "EnumProcesses". Platform SDK для Windows XP SP2 . Microsoft . Получено 31 августа 2008 г. .
  21. ^ "GetModuleBaseName". Platform SDK для Windows XP SP2 . Microsoft . Получено 31 августа 2008 г. .
  22. ^ "VirtualAllocEx". Platform SDK для Windows XP SP2 . Microsoft . Получено 31 августа 2008 г. .
  23. ^ "WriteProcessMemory". Platform SDK для Windows XP SP2 . Microsoft . Получено 31 августа 2008 г. .
  24. ^ "Outpost Обход Самозащиты с помощью Расширенной Внедрения DLL с Уязвимостью Кражи Дескриптора". Matousec . 1 декабря 2006 г. Архивировано из оригинала 6 февраля 2009 г. Получено 31 августа 2008 г.
  25. ^ "CreateRemoteThread". Platform SDK для Windows XP SP2 . Microsoft . Получено 31 августа 2008 г. .
  26. ^ "LoadLibrary". Platform SDK для Windows XP SP2 . Microsoft . Получено 31 августа 2008 г. .
  27. ^ ab "DllMain". Platform SDK для Windows XP SP2 . Microsoft . Получено 31 августа 2008 г. .
  28. ^ «Функция SetWindowsHookEx» . Платформенный SDK для Windows XP SP2 . Майкрософт . Проверено 31 августа 2008 г.
  29. ^ "Значение реестра AppInit_DLLs и Windows 95". Справка и поддержка Microsoft . Microsoft. 1 марта 2005 г. Получено 31 августа 2008 г.
  30. ^ "Внедрение DLL с использованием метода SetWindowsHookEx()". Game Reversal . 3 апреля 2008 г. Получено 1 сентября 2008 г.
  31. ^ "SetThreadContext DLL Injection". 16 января 2007 г. Архивировано из оригинала 23 августа 2011 г. Получено 1 сентября 2008 г.
  32. Ben Botto (6 сентября 2008 г.). "DLL Injector". Архивировано из оригинала 7 февраля 2009 г. Получено 1 сентября 2008 г.
  33. ^ «Небезопасная загрузка библиотеки может привести к удаленному выполнению кода». Microsoft . 10 июня 2011 г. Получено 20 апреля 2016 г.
  34. ^ «Безопасная загрузка библиотек для предотвращения атак предварительной загрузки DLL». Microsoft . 10 июня 2011 г. Получено 8 августа 2012 г.
  35. ^ "Microsoft Security Advisory: Небезопасная загрузка библиотеки может привести к удаленному выполнению кода". Microsoft . 10 июня 2011 г. . Получено 20 апреля 2016 г. .
  36. ^ Николя Фальер (26 сентября 2010 г.). «Заражение проектов Step 7 вирусом Stuxnet». Symantec.
  37. ^ Линус Торвальдс ; Дэвид Энгель; Эрик Янгдейл; Питер Макдональд; Хунцзю Лу; Ларс Вирзениус; Митч Д'Соуза (14 марта 1998 г.). "ld.so/ld-linux.so – dynamic linker/loader". Страницы руководства UNIX . Архивировано из оригинала 6 февраля 2009 г. Получено 31 августа 2008 г.
  38. ^ "Code Gen Options". Использование коллекции компиляторов GNU (GCC) . Free Software Foundation . Получено 31 августа 2008 г. Генерация позиционно-независимого кода (PIC), подходящего для использования в общей библиотеке, если поддерживается целевой машиной. sqq.-fpic
  39. ^ "Параметры ссылки". Использование коллекции компиляторов GNU (GCC) . Free Software Foundation . Получено 31 августа 2008 г. Создать общий объект, который затем можно связать с другими объектами для формирования исполняемого файла. sqq.-shared
  40. ^ "Трюк LD_PRELOAD". Питер Голдсборо . Получено 17 мая 2017 г.
  41. Григорий Шпитальник (12 февраля 2009 г.). «Внедрение кода в работающее приложение Linux». Code Project . Получено 18 ноября 2010 г.