سؤال

وصديقي إنتاج صغيرة إثبات صحة مفهوم المجمع التي عملت على إلى x86. قررت أن الميناء لمدة x86_64 كذلك، ولكن أنا ضربت على الفور مشكلة.

ولقد كتبت قطعة صغيرة من البرنامج في C، ثم تجميعها وobjdumped التعليمات البرمجية. بعد أن تم إدراج لبلدي النصي الثعبان، لذلك رمز x86_64 هو الصحيح:

from ctypes import cast, CFUNCTYPE, c_char_p, c_long

buffer = ''.join(map(chr, [ #0000000000000000 <add>:
  0x55,                     # push   %rbp
  0x48, 0x89, 0xe5,         # mov    %rsp,%rbp
  0x48, 0x89, 0x7d, 0xf8,   # mov    %rdi,-0x8(%rbp)
  0x48, 0x8b, 0x45, 0xf8,   # mov    -0x8(%rbp),%rax
  0x48, 0x83, 0xc0, 0x0a,   # add    $0xa,%rax
  0xc9,                     # leaveq 
  0xc3,                     # retq
]))

fptr = cast(c_char_p(buffer), CFUNCTYPE(c_long, c_long))
print fptr(1234)

والآن، لماذا هذا السيناريو يبقى به خطأ تجزئة كلما يمكنني تشغيله؟

وليس لدي حتى الآن على سؤال حول mprotect وليس العلم التنفيذ. ويقال للحماية من معظم يستغل الأمنية الأساسية مثل التجاوزات العازلة. ولكن ما هو السبب الحقيقي وانها في الاستخدام؟ هل يمكن أن تبقي فقط على الكتابة حتى تصل إلى .text، ثم حقن التعليمات الخاصة بك إلى لطيفة، -area PROT_EXEC. ما لم يكن، بالطبع، يمكنك استخدام الحماية ضد الكتابة في .text

ولكن بعد ذلك، لماذا يكون هذا PROT_EXEC في كل مكان على أية حال؟ لن يكون فقط يساعد الى حد كبير أن القسم .text الخاص بك محمي ضد الكتابة؟

هل كانت مفيدة؟

المحلول

فنسنت والذكر، ويرجع هذا إلى صفحة المخصصة يحتفى كما غير قابل للتنفيذ. المعالجات الجديدة تدعم هذه href="http://en.wikipedia.org/wiki/NX_bit" rel="nofollow ، واستخدمت بوصفها طبقة إضافية من الأمن من خلال لOS التي تدعمها . والفكرة هي لحماية ضد بعض الهجمات تجاوز سعة المخزن المؤقت. على سبيل المثال. هجوم مشترك هو تجاوز متغير كومة، وإعادة كتابة عنوان المرسل للإشارة إلى رمز قمت بإدراجها. مع كومة غير قابل للتنفيذ وهذا ينتج الآن سوى segfault، بدلا من السيطرة على العملية. وجود هجمات مماثلة أيضا للذاكرة الكومة.

ليحصل من حوله، تحتاج إلى تغيير الحماية. لا يمكن إلا أن يتم تنفيذ هذا على صفحة محاذاة الذاكرة، لذلك عليك ربما تحتاج إلى تغيير التعليمات البرمجية لشيء من هذا القبيل أدناه:

libc = CDLL('libc.so')

# Some constants
PROT_READ = 1
PROT_WRITE = 2
PROT_EXEC = 4

def executable_code(buffer):
    """Return a pointer to a page-aligned executable buffer filled in with the data of the string provided.
    The pointer should be freed with libc.free() when finished"""

    buf = c_char_p(buffer)
    size = len(buffer)
    # Need to align to a page boundary, so use valloc
    addr = libc.valloc(size)
    addr = c_void_p(addr)

    if 0 == addr:  
        raise Exception("Failed to allocate memory")

    memmove(addr, buf, size)
    if 0 != libc.mprotect(addr, len(buffer), PROT_READ | PROT_WRITE | PROT_EXEC):
        raise Exception("Failed to set protection on buffer")
    return addr

code_ptr = executable_code(buffer)
fptr = cast(code_ptr, CFUNCTYPE(c_long, c_long))
print fptr(1234)
libc.free(code_ptr)

ملحوظة: قد يكون فكرة جيدة لإلغاء تعيين العلم قابل للتنفيذ قبل تحرير الصفحة. معظم المكتبات C في الواقع لا تعود الذاكرة إلى OS عند القيام به، ولكن نأخذ في حوض السباحة الخاصة بهم. وهذا قد يعني أنها سوف إعادة استخدام الصفحة في أي مكان آخر دون مسح بت EXEC، وتجاوز مصلحة الأمن.

لاحظ أيضا أن هذا هو إلى حد ما غير المحمولة. لقد اختبرت ذلك على لينكس، ولكن ليس على أي نظام تشغيل آخر. فهو لن يعمل على ويندوز، وشراء قد القيام به على من Unixes الآخر (BSD، OSX؟).

نصائح أخرى

والقيام ببعض الأبحاث مع صديقي وتبين هذه مسألة منصة محددة. ونعتقد أن على بعض الأنظمة الأساسية malloc mmaps الذاكرة دون PROT_EXEC وعلى الآخرين يفعل.

ولذا فمن الضروري تغيير مستوى الحماية مع mprotect بعد ذلك.

والشيء عرجاء، استغرق بعض الوقت لمعرفة ما يجب القيام به.

from ctypes import (
    cast, CFUNCTYPE, c_long, sizeof, addressof, create_string_buffer, pythonapi
)

PROT_NONE, PROT_READ, PROT_WRITE, PROT_EXEC = 0, 1, 2, 4
mprotect = pythonapi.mprotect

buffer = ''.join(map(chr, [ #0000000000000000 <add>:
    0x55,                     # push   %rbp
    0x48, 0x89, 0xe5,         # mov    %rsp,%rbp
    0x48, 0x89, 0x7d, 0xf8,   # mov    %rdi,-0x8(%rbp)
    0x48, 0x8b, 0x45, 0xf8,   # mov    -0x8(%rbp),%rax
    0x48, 0x83, 0xc0, 0x0a,   # add    $0xa,%rax
    0xc9,                     # leaveq 
    0xc3,                     # retq
]))

pagesize = pythonapi.getpagesize()
cbuffer = create_string_buffer(buffer)#c_char_p(buffer)
addr = addressof(cbuffer)
size = sizeof(cbuffer)
mask = pagesize - 1
if mprotect(~mask&addr, mask&addr + size, PROT_READ|PROT_WRITE|PROT_EXEC) < 0:
    print "mprotect failed?"
else:
    fptr = cast(cbuffer, CFUNCTYPE(c_long, c_long))
    print repr(fptr(1234))

وأعتقد أنك لا يمكن تنفيذ بحرية أي الذاكرة المخصصة دون تحديد لأول مرة أنه قابل للتنفيذ. لم أحاول أبدا نفسي، ولكنك قد ترغب في التحقق من يونيكس وظيفة mprotect:

http://linux.about.com/library/cmd/blcmdl2_mprotect.htm

وVirtualProtect يبدو أن تفعل الشيء نفسه على النوافذ:

http://msdn.microsoft.com/ أون لنا / مكتبة / aa366898 (VS.85) .aspx اتصال

هل الثعبان حتى يسمح هذا الاستخدام؟ وأود أن تعلم أنه ثم ...

وأعتقد أن لا تتوقع المترجم أي سجل للتغيير. حاول حفظ السجلات التي تستخدمها داخل الدالة إذا كنت تخطط لاستخدام الانتاج المجمع الخاصة بك مثل هذا.

وراجع للشغل، اتفاقية دعوة x86_64 مختلفة من إلى x86 منتظم. قد يكون لديك مشكلة إذا فقدت كومة محاذاة المؤشر وخلط الكائنات الخارجية ولدت مع غيرها من الأدوات.

وهناك نهج أكثر بساطة لقد برزت فقط لكن في الآونة الأخيرة التي لا تنطوي على mprotect. mmap بوضوح الفضاء القابل للتنفيذ البرنامج مباشرة. في هذه الأيام الثعبان لديه وحدة لفعل هذا بالضبط، على الرغم من أنني لم أجد طريقة للحصول على عنوان رمز. باختصار وكنت تخصيص ذاكرة يدعو mmap بدلا من استخدام مخازن سلسلة وتعيين إشارة التنفيذ بشكل غير مباشر. هذا هو أسهل وأكثر أمنا، يمكنك التأكد من التعليمات البرمجية الخاصة بك فقط يمكن تنفيذه الآن.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top