سؤال

هل يمكن قراءتها عداد البرنامج على وحدة المعالجة المركزية Intel مباشرة (أي بدون "حيل") في وضع Kernel أو بعض الوضع الآخر؟

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

المحلول

لا، لا يمكن الوصول إلى EIP / IP مباشرة، ولكن في التعليمات البرمجية التي تعتمد على الموضع، فهي ثابتة لربط الوقت حتى تتمكن من استخدام رمز قريب (أو بعيد) كإجراء فوري.

   mov eax, nearby_label    ; in position-dependent code
nearby_label:

للحصول على EIP أو IP في رمز 32 بت مستقل:

        call _here
_here:  pop eax
; eax now holds the PC.

على وحدات المعالجة المركزية الأحدث من Pentium Pro (أو PIII ربما)، call rel32 مع REL32 = 0 يتم غسلها بشكل خاص لعدم التأثير على مكدس تنبؤ عنوان العودة. وبعد لذلك هذا فعال بالإضافة إلى مدمجة على X86 الحديثة، وهو ما يستخدمه Clang للحصول على رمز مستقل من الموقف 32 بت.

على وحدة المعالجة المركزية القديمة Pentium Pro المعالجة المركزية، هذا من شأنه عدم اعتراض مكدس توقع المكالمات / العودة، لذلك تفضل الدعوة وظيفة تعود بالفعل، لتجنب الفرع يخطئ على ما يصل إلى 15 أو مستقبلا ret تعليمات في وظائف الوالدين الخاصة بك. (ما لم تكن لن تعود، أو نادرا ما لا يهم ذلك.) ستعيد كومة تنبؤات عودة العائد، رغم ذلك.

get_retaddr_ppro:
    mov  eax, [esp]
    ret                ; keeps the return-address predictor stack balanced
                       ; even on CPUs where  call +0 isn't a no-op.

في وضع X86-64، يمكن قراءة RIP مباشرة باستخدام Rip-Emerative lea.

default rel           ; NASM directive: use RIP-relative by default

lea  rax, [_here]     ; RIP + 0
_here:

MASM أو GNU. .intel_syntax: lea rax, [rip]

بناء جملة AT & T: lea 0(%rip), %rax

نصائح أخرى

إذا كنت بحاجة إلى عنوان تعليمات محددة، عادة ما يكون هناك شيء من هذا القبيل

thisone: 
   mov (e)ax,thisone

(ملاحظة: في بعض المجمعين، قد يقوم هذا بالشيء الخطأ وقراءة كلمة من [Thisone]، ولكن عادة ما يكون هناك بعض بناء الجملة للحصول على المجمع للقيام بالأشياء الصحيحة.)

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

على x86-64 يمكنك القيام به على سبيل المثال:

lea rax,[rip] (48 8d 05 00 00 00 00)

لا توجد تعليمات لقراءة مؤشر التعليمات مباشرة (EIP) على X86. يمكنك الحصول على عنوان التعليمات الحالية التي يتم تجميعها مع القليل من الجمعية المضمنة:

// GCC inline assembler; for MSVC, syntax is different
uint32_t eip;
__asm__ __volatile__("movl $., %0", : "=r"(eip));

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

// In a C header file:
uint32_t get_eip(void);

// In a separate assembly (.S) file:
.globl _get_eip
_get_eip:
    mov 0(%esp), %eax
    ret

هذا يعني في كل مرة تريد فيها الحصول على مؤشر التعليمات، إنه أقل كفاءة قليلا لأنك تحتاج إلى مكالمة إضافية لوظيفة. لاحظ أن القيام بذلك بهذه الطريقة لا تفجير مكدس عنوان المرسل (RAS). كومة عنوان المرسل عبارة عن كومة منفصلة عن عناوين العودة المستخدمة داخليا من قبل المعالج لتسهيل فرع الهدف التنبؤ لإعادة التعليمات.

في كل مرة يكون لديك تعليمات مكالمة، يتم دفع EIP الحالي إلى RAS، وفي كل مرة يكون لديك تعليمات محدبة، برزت RAS، ويتم استخدام القيمة العليا كتنبؤ الهدف الفرعي لهذه التعليمات. إذا قمت بفوضى RAS (مثل عدم مطابقة كل مكالمة مع Ret، كما هو الحال في حل كودي)، ستحصل على مجموعة كاملة من سلطة غير معاملة غير ضرورية، إبطاء برنامجك إلى أسفل. هذه الطريقة لا تفجر RAS، لأنه يحتوي على زوج مطابق من الاتصال والإعادة التعليمات.

هناك طريقة بنية مستقلة (ولكن تعتمد على دول مجلس التعاون الخليجي) للوصول إلى العنوان الذي يتم تنفيذه باستخدام التسميات كقيم:

http://gcc.gnu.org/onlinedocs/gcc/labels-as-values.html.

void foo()
{
  void *current_address = $$current_address_label;
  current_address_label:
      ....
}

يمكنك أيضا قراءة هذا من / بروك / إحصائي. تحقق من proc manpages.

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