إذن exec غير متوقع من mmap عند ملفات التجميع المضمنة في المشروع




linux assembly (2)

أنا ضجيج رأسي في الجدار مع هذا.

في مشروعي ، عندما أقوم بتخصيص ذاكرة باستخدام ملف mmap يظهر التعيين ( /proc/self/maps ) أنها منطقة قابلة للقراءة وقابلة للتنفيذ على الرغم من أنني طلبت فقط ذاكرة قابلة للقراءة.

بعد النظر إلى المضيق (الذي كان يبدو جيدًا) وغيرها من عمليات تصحيح الأخطاء ، تمكنت من تحديد الشيء الوحيد الذي يبدو أنه يتجنب هذه المشكلة الغريبة: إزالة ملفات التجميع من المشروع وترك فقط C النقي (ماذا ؟!)

إذن هذا هو المثال الغريب الذي أعمل به ، فأنا أعمل على Ubunbtu 19.04 و gcc default.

إذا قمت بترجمة الهدف القابل للتنفيذ باستخدام ملف ASM (وهو فارغ) ، mmap منطقة قابلة للقراءة وقابلة للتنفيذ ، إذا كنت تقوم بالبناء بدونها ، فإنها تتصرف بشكل صحيح. اطلع على مخرجات /proc/self/maps التي قمت بدمجها في مثالي.

example.c

#include <stdio.h>
#include <string.h>
#include <sys/mman.h>

int main()
{
    void* p;
    p = mmap(NULL, 8192,PROT_READ,MAP_ANONYMOUS|MAP_PRIVATE,-1,0);

    {
        FILE *f;
        char line[512], s_search[17];
        snprintf(s_search,16,"%lx",(long)p);
        f = fopen("/proc/self/maps","r");
        while (fgets(line,512,f))
        {
            if (strstr(line,s_search)) fputs(line,stderr);
        }

        fclose(f);
    }

    return 0;
}

example.s : ملف فارغ!

المخرجات

مع نسخة ASM المدرجة

VirtualBox:~/mechanics/build$ gcc example.c example.s -o example && ./example
7f78d6e08000-7f78d6e0a000 r-xp 00000000 00:00 0 

بدون نسخة ASM المدرجة

VirtualBox:~/mechanics/build$ gcc example.c -o example && ./example
7f1569296000-7f1569298000 r--p 00000000 00:00 0 

كبديل لتعديل ملفات التجميع الخاصة بك مع متغيرات توجيهات قسم GNU الخاصة ، يمكنك إضافة -Wa,--noexecstack إلى سطر الأوامر الخاص بك لإنشاء ملفات التجميع. على سبيل المثال ، انظر كيف أفعل ذلك في configure musl:

https://git.musl-libc.org/cgit/musl/commit/configure?id=adefe830dd376be386df5650a09c313c483adf1a

أعتقد أن بعض إصدارات --noexecstack على -Wa مع -Wa المجمّع المتكاملة قد تتطلب تمريرها كـ - --noexecstack (بدون -Wa ) ، لذلك من المحتمل أن يتحقق البرنامج النصي للتكوين من كلاهما ويرى ما هو مقبول.

يمكنك أيضًا استخدام -Wl,-z,noexecstack في وقت الارتباط (في LDFLAGS ) للحصول على نفس النتيجة. عيب هذا هو أنه لا يساعد إذا كان مشروعك ينتج ملفات مكتبة ثابتة (. .a ) لاستخدامها من قبل برامج أخرى ، حيث لا يمكنك حينئذ التحكم في خيارات وقت الارتباط عند استخدامه بواسطة برامج أخرى.


يحتوي Linux على مجال تنفيذ يسمى READ_IMPLIES_EXEC ، مما يؤدي أيضًا إلى منح PROT_EXEC جميع الصفحات المخصصة لـ PROT_EXEC . سيوضح لك هذا البرنامج ما إذا تم تمكينه لنفسه:

#include <stdio.h>
#include <sys/personality.h>

int main(void) {
    printf("Read-implies-exec is %s\n", personality(0xffffffff) & READ_IMPLIES_EXEC ? "true" : "false");
    return 0;
}

إذا قمت بترجمة ذلك مع ملف .s فارغ ، فسترى أنه ممكّن ، لكن بدون ملف واحد ، سيتم تعطيله. القيمة الأولية لهذا تأتي من معلومات ELF الوصفية في ملف التعريف الخاص بك . هل readelf -Wl example سترى هذا السطر عند التحويل البرمجي بدون ملف .s الفارغ:

  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW  0x10

ولكن هذا واحد عندما جمعت به:

  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RWE 0x10

لاحظ RWE بدلاً من RW فقط. السبب في ذلك هو أن الرابط يفترض أن ملفات التجميع الخاصة بك تتطلب read-implies-exec ما لم يتم إخبارها صراحة بأنها لا تفعل ذلك ، وإذا كان أي جزء من البرنامج يتطلب read-implies-exec ، فسيتم تمكينه للبرنامج بأكمله . تخبرها ملفات التجميع التي تجمعها دول مجلس التعاون الخليجي أنها لا تحتاج إلى هذا ، بهذا السطر (سترى هذا إذا كنت تقوم بترجمة باستخدام -S ):

        .section        .note.GNU-stack,"",@progbits

ضع هذا السطر في example.s ، وسيعمل على إخبار الرابط بأنه لا يحتاج إليه أيضًا ، وسيعمل البرنامج بعد ذلك كما هو متوقع.





mmap