بخشهای کد اجرایی برای باینریهای سیستم AArch64 بهطور پیشفرض فقط اجرا (غیرقابل خواندن) بهعنوان یک کاهش سختتر در برابر حملات استفاده مجدد از کد بهموقع علامتگذاری میشوند. کدی که دادهها و کدها را با هم ترکیب میکند و کدهایی که به طور هدفمند این بخشها را بررسی میکنند (بدون اینکه ابتدا بخشهای حافظه را بهعنوان قابل خواندن دوباره نقشهبرداری کنند) دیگر کار نمیکنند. اگر برنامه تلاش کند بخشهای کد کتابخانههای سیستم فقط اجرا (XOM) را در حافظه بخواند، برنامههایی با هدف SDK 10 (سطح API 29 یا بالاتر) تحت تأثیر قرار میگیرند بدون اینکه ابتدا بخش را بهعنوان قابل خواندن علامتگذاری کنند.
برای بهره مندی کامل از این کاهش، هم پشتیبانی سخت افزاری و هم هسته مورد نیاز است. بدون این حمایت، کاهش ممکن است فقط تا حدی اجرا شود. هسته مشترک اندروید 4.9 حاوی وصله های مناسب برای ارائه پشتیبانی کامل از این امر در دستگاه های ARMv8.2 است.
پیاده سازی
باینری های AArch64 تولید شده توسط کامپایلر فرض می کنند که کد و داده با هم مخلوط نمی شوند. فعال کردن این ویژگی بر عملکرد دستگاه تأثیر منفی نمی گذارد.
برای کدهایی که باید دروننگری عمدی حافظه را روی بخشهای اجرایی خود انجام دهند، توصیه میشود که mprotect
روی بخشهایی از کد که نیاز به بازرسی دارند فراخوانی کنید تا امکان خواندن آنها فراهم شود، سپس پس از اتمام بازرسی، خوانایی را حذف کنید.
این پیادهسازی باعث میشود خواندن در بخشهای حافظه که بهعنوان «فقط اجرا» علامتگذاری شدهاند، منجر به خطای بخشبندی ( SEGFAULT
) شود. این ممکن است در نتیجه یک اشکال، آسیبپذیری، دادههای ترکیب شده با کد (تجمع تحت اللفظی)، یا دروننگری عمدی حافظه رخ دهد.
پشتیبانی و تاثیر دستگاه
ممکن است دستگاههایی با سختافزار قدیمیتر یا هستههای قبلی (کمتر از 4.9) بدون وصلههای مورد نیاز، به طور کامل از این ویژگی پشتیبانی نکنند یا از آن بهرهمند نشوند. دستگاههای بدون پشتیبانی از هسته ممکن است دسترسی کاربر به حافظه اجرائی را اعمال نکنند، با این حال کد هسته که صریحاً خوانایی صفحه را بررسی میکند ممکن است همچنان این ویژگی را اعمال کند، مانند process_vm_readv()
.
پرچم هسته CONFIG_ARM64_UAO
باید در هسته تنظیم شود تا اطمینان حاصل شود که هسته به صفحات کاربر سرزمینی که فقط اجرا علامت گذاری شده اند احترام می گذارد. دستگاههای قبلی ARMv8 یا دستگاههای ARMv8.2 با لغو دسترسی کاربر (UAO) غیرفعال است، ممکن است به طور کامل از این مزیت بهره نبرند و ممکن است همچنان بتوانند صفحاتی را که فقط اجرا میشوند با استفاده از syscalls بخوانند.
کد موجود را Refactor کنید
کدی که از AArch32 منتقل شده است ممکن است حاوی داده ها و کدهای ترکیبی باشد که باعث بروز مشکلاتی می شود. در بسیاری از موارد، رفع این مشکلات به سادگی انتقال ثابت ها به بخش .data
فایل اسمبلی است.
مونتاژ دستنویس ممکن است برای جداسازی ثابتهای ادغام شده محلی نیاز به بازسازی مجدد داشته باشد.
مثال ها:
باینری های تولید شده توسط کامپایلر Clang نباید هیچ مشکلی در ترکیب داده ها در کد نداشته باشند. اگر کد تولید شده از مجموعه کامپایلر گنو (GCC) (از یک کتابخانه استاتیک) گنجانده شده است، باینری خروجی را بررسی کنید تا مطمئن شوید که ثابت ها در بخش های کد ادغام نشده اند.
اگر دروننگری کد در بخشهای کد اجرایی ضروری است، ابتدا mprotect
را فراخوانی کنید تا کد قابل خواندن علامتگذاری شود. سپس پس از اتمام عملیات، دوباره mprotect
فراخوانی کنید تا غیرقابل خواندن علامت گذاری شود.
XOM را فعال کنید
Execute-only به طور پیش فرض برای همه باینری های 64 بیتی در سیستم ساخت فعال است.
XOM را غیرفعال کنید
شما میتوانید اجرا را فقط در سطح ماژول، توسط یک درخت زیردایرکتوری کامل یا به صورت سراسری برای کل ساخت غیرفعال کنید.
با تنظیم متغیرهای LOCAL_XOM
و xom
بر روی false
میتوان XOM را برای ماژولهایی که نمیتوان دوباره فاکتور کرد یا نیاز به خواندن کد اجرایی آنها دارد، غیرفعال کرد.
// Android.mk LOCAL_XOM := false // Android.bp cc_binary { // or other module types ... xom: false, }
اگر حافظه فقط اجرا در یک کتابخانه استاتیک غیرفعال باشد، سیستم ساخت این را برای همه ماژولهای وابسته آن کتابخانه استاتیک اعمال میکند. شما می توانید با استفاده از xom: true,
.
برای غیرفعال کردن حافظه فقط اجرا در یک زیر شاخه خاص (مثلاً foo/bar/)، مقدار را به XOM_EXCLUDE_PATHS
ارسال کنید.
make -j XOM_EXCLUDE_PATHS=foo/bar
یا می توانید متغیر PRODUCT_XOM_EXCLUDE_PATHS
را در پیکربندی محصول خود تنظیم کنید.
میتوانید با ارسال ENABLE_XOM=false
به دستور make
خود، باینریهای فقط اجرا را در سراسر جهان غیرفعال کنید.
make -j ENABLE_XOM=false
اعتبار سنجی
هیچ آزمون CTS یا تاییدی برای حافظه اجرائی موجود نیست. میتوانید با استفاده از readelf
و بررسی پرچمهای بخش، باینریها را به صورت دستی تأیید کنید.