مکانیزم LD_PRELOAD یکی از قابلیتهای قدرتمند در سیستمعامل لینوکس است که میتواند هم برای اهداف امنیتی (مانند مانیتورینگ) و هم برای اهداف مخرب (مانند روتکیتها) مورد استفاده قرار گیرد. این مقاله به بررسی دقیق مفهوم میانگذاری توابع (Function Interposition) و نحوه سوءاستفاده از آن برای تغییر رفتار برنامهها میپردازد.
مفهوم فنی میانگذاری توابع (Function Interposition)
وقتی یک برنامه به صورت پویا (Dynamically Linked) اجرا میشود، لینککننده پویا (Dynamic Linker) کتابخانههای مورد نیاز را بارگذاری میکند. با استفاده از متغیر محیطی LD_PRELOAD، میتوان به لینککننده دستور داد تا یک کتابخانه مشترک (Shared Library) خاص را قبل از سایر وابستگیهای برنامه بارگذاری کند. این کار باعث میشود نمادها (Symbols) موجود در کتابخانه جدید، در فرآیند حل نمادها (Symbol Resolution) اولویت داشته باشند.
یک مهاجم یا تحلیلگر میتواند با استفاده از تابع dlsym و فلگ RTLD_NEXT، آدرس تابع اصلی را پیدا کرده و سپس یک Wrapper (پوششدهنده) حول آن تابع ایجاد کند. این کار اجازه میدهد تا قبل از اجرای تابع اصلی، عملیاتی (مانند ثبت لاگ یا فیلتر کردن دادهها) انجام شود.
تکنیک مانیتورینگ و Audit
در یک سناریوی دفاعی، میتوان از این قابلیت برای ایجاد یک ابزار مانیتورینگ استفاده کرد. برای مثال، با بازنویسی تابع readdir()، میتوان تمام ورودیهای یک دایرکتوری را قبل از اینکه به برنامه اصلی برسند، ثبت (Log) کرد. در این حالت، Wrapper فقط اطلاعات را گزارش کرده و سپس اجازه میدهد تابع اصلی بدون تغییر، خروجی خود را برگرداند.
تکنیک پنهانسازی در روتکیتهای User-space
مهاجمان از همین مکانیزم برای ساخت روتکیتهای سطح کاربر (User-space Rootkits) استفاده میکنند. برخلاف روتکیتهای سطح هسته (Kernel-mode) که ساختار فایلسیستم را تغییر میدهند، روتکیتهای مبتنی بر LD_PRELOAD تنها “دید” برنامه را تغییر میدهند.
به عنوان مثال، یک روتکیت میتواند در تابع readdir()، بررسی کند که آیا نام فایل با یک پیشوند خاص (مثلاً secret_) شروع میشود یا خیر. اگر چنین بود، آن ورودی را نادیده گرفته و به جای بازگرداندن آن به برنامه، مستقیماً به سراغ ورودی بعدی میرود. در نتیجه، فایل همچنان در دیسک وجود دارد، اما برنامه (مانند دستور ls) آن را مشاهده نمیکند.
روش شناسایی و تشخیص (Detection)
یکی از بهترین راهها برای شناسایی این نوع دستکاری، مقایسه خروجی سطح کاربر با فراخوانیهای مستقیم سیستم (System Calls) است. اگر از ابزار strace استفاده کنیم، میتوانیم فراخوانی getdents64() را در سطح هسته (Kernel) رصد کنیم.
- در سطح کاربر: برنامه ممکن است تعداد کمی فایل را نشان دهد (به دلیل فیلتر شدن توسط Wrapper).
- در سطح هسته: فراخوانی
getdents64()تمام فایلها را، از جمله فایلهای مخفی، در بافر خود برمیگرداند.
هرگونه عدم تطابق (Mismatch) بین آنچه برنامه نمایش میدهد و آنچه هسته گزارش میکند، میتواند نشانهای جدی از وجود یک روتکیت یا دستکاری در محیط اجرای برنامه باشد.