وضع غير قانوني للمدخلات وغير المحظورة على الحول

بطبيعة الحال ، من غير المحتمل أن تتبادر فكرة كتابة لعبة بلغة التجميع إلى ذهن شخص بمفرده ، ومع ذلك ، فهو شكل معقد من التقارير تم ممارسته منذ فترة طويلة في السنة الأولى من VMK بجامعة موسكو الحكومية. ولكن نظرًا لأن التقدم لا يقف ساكنًا ، يصبح كل من DOS و Masm تاريخًا ، ويأتي nasm و Linux في طليعة إعداد العزاب. ربما في غضون عشر سنوات ، ستكتشف قيادة الكلية الثعبان ، ولكن هذا ليس حول ذلك الآن.

برمجة Assembler في Linux ، بكل مزاياها ، تجعل من المستحيل استخدام مقاطعات BIOS ، ونتيجة لذلك ، تحرمهم من الوظائف. بدلاً من ذلك ، يجب عليهم استخدام مكالمات النظام والاتصال بواجهة برمجة التطبيقات الطرفية. لذلك ، كتابة محاكاة لعبة ورق أو معركة بحرية لا يسبب صعوبات كبيرة ، وهناك مشاكل مع الثعبان العادي. والحقيقة هي أن نظام الإدخال والإخراج يتحكم فيه الطرف ، ولا يمكن استخدام وظائف النظام C مباشرة. لذلك ، عند كتابة ألعاب بسيطة إلى حد ما ، تولد كتلتان متعثرتان: كيفية تحويل المحطة الطرفية إلى الوضع غير القانوني وكيفية جعل إدخال لوحة المفاتيح غير قابل للحظر. سيتم مناقشة هذا في المقالة.

1. الوضع غير القانوني للمحطة


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

struct termios saved_attributes; void reset_input_mode (void) { tcsetattr (STDIN_FILENO, TCSANOW, &saved_attributes); } void set_input_mode (void) { struct termios tattr; /* Save the terminal attributes so we can restore them later. */ tcgetattr (STDIN_FILENO, &saved_attributes); /* Set the funny terminal modes. */ tcgetattr (STDIN_FILENO, &tattr); tattr.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */ tcsetattr (STDIN_FILENO, TCSAFLUSH, &tattr); } 

في هذا الكود ، يعني STDIN_FILENO مقبض دفق الإدخال الذي نعمل معه (بشكل افتراضي هو 0) ، و ICANON هي علامة التمكين لنفس الإدخال الأساسي ، و ECHO هي علامة لعرض أحرف الإدخال على الشاشة ، و TCSANOW و TCSAFLUSH عبارة عن وحدات ماكرو محددة بالمكتبة. وهكذا ، تبدو الخوارزمية "العارية" ، الخالية من الفحوصات الأمنية ، كما يلي:

  1. الحفاظ على هيكل النمل الأصلي ؛
  2. نسخ محتوياته مع تغيير علامتي ICANON و ECHO ؛
  3. إرسال الهيكل المتغير إلى المحطة ؛
  4. عند الانتهاء من العمل ، ارجع إلى الهيكل المحفوظ.

يبقى أن نفهم ما تقوم به المكتبة tcsetattr و tcgetattr. في الواقع ، إنهم يفعلون الكثير من الأشياء ، ولكن استدعاء نظام ioctl هو مفتاح عملهم. الحجة الأولى التي تأخذها هي واصف دفق (0 في حالتنا) ، والثانية هي مجموعة من العلامات التي تم تعريفها للتو من قبل وحدات الماكرو TCSANOW و TCSAFLUSH ، والثالث هو مؤشر للبنية (في مصطلحات الحالة الخاصة بنا). على بناء جملة nasm وتحت اتفاقية استدعاءات النظام على لينكس ، سيأخذ الشكل التالي:

  mov rax, 16 ;   ioctl mov rdi, 0 ;    mov rsi, TCGETS ;  mov rdx, tattr ;     syscall 

بشكل عام ، هذه هي النقطة الكاملة لوظائف tcsetattr و tcgetattr. بالنسبة لبقية الشفرة ، نحتاج إلى معرفة حجم وهيكل بنية النهايات ، والتي يسهل العثور عليها أيضًا في الوثائق الرسمية . حجمها افتراضيًا هو 60 بايت ، وصفيف الأعلام التي نحتاجها هو 4 بايت في الحجم ويقع في المركز الرابع على التوالي. يبقى كتابة إجراءين ودمجهما في رمز واحد.

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

الانتقال إلى الوضع غير القانوني
 %define ICANON 2 %define ECHO 8 %define TCGETS 21505 ;    %define TCPUTS 21506 ;    global setcan ;     global setnoncan ;     section .bss stty resb 12 ; termios - 60  slflag resb 4 ;slflag    3*4   srest resb 44 tty resb 12 lflag resb 4 brest resb 44 section .text setnoncan: push stty call tcgetattr push tty call tcgetattr and dword[lflag], (~ICANON) and dword[lflag], (~ECHO) call tcsetattr add rsp, 16 ret setcan: push stty call tcsetattr add rsp, 8 ret tcgetattr: mov rdx, qword[rsp+8] push rax push rbx push rcx push rdi push rsi mov rax, 16 ;ioctl system call mov rdi, 0 mov rsi, TCGETS syscall pop rsi pop rdi pop rcx pop rbx pop rax ret tcsetattr: mov rdx, qword[rsp+8] push rax push rbx push rcx push rdi push rsi mov rax, 16 ;ioctl system call mov rdi, 0 mov rsi, TCPUTS syscall pop rsi pop rdi pop rcx pop rbx pop rax ret 


2. المدخلات غير المحظورة في المحطة


بالنسبة لمدخلات الأموال غير المحظورة ، فإن المحطة ليست كافية بالنسبة لنا. سنكتب وظيفة تتحقق من المخزن المؤقت القياسي للتدفق من أجل الاستعداد لإرسال المعلومات: إذا كان هناك رمز في المخزن المؤقت ، فسوف يعيد رمزه ؛ إذا كان المخزن المؤقت فارغًا ، فسوف يعود 0. لهذا الغرض ، يمكنك استخدام مكالمتين للنظام - الاستطلاع () أو تحديد (). كلاهما قادر على عرض تيارات المدخلات والمخرجات المختلفة على حقيقة أي حدث. على سبيل المثال ، إذا وصلت المعلومات في أي من التدفقات ، فيمكن لكل من مكالمات النظام هذه التقاط هذا وعرضه في البيانات التي تم إرجاعها. ومع ذلك ، فإن الثاني منهم هو في الأساس نسخة محسنة من الأولى وهو مفيد عند العمل مع سلاسل رسائل متعددة. ليس لدينا مثل هذا الهدف (نحن نعمل فقط مع الدفق القياسي) ، لذلك سنستخدم مكالمة الاستطلاع ().

كما يقبل ثلاث معلمات كمدخلات:

  1. مؤشر إلى بنية البيانات ، والذي يحتوي على معلومات حول واصفات التدفقات المراقبة (سنناقشها أدناه) ؛
  2. عدد الخيوط المعالجة (لدينا واحدة) ؛
  3. الوقت بالمللي ثانية الذي يمكن خلاله توقع حدث (نحتاج إلى حدوثه على الفور ، بحيث تكون هذه المعلمة 0).

من الوثائق يمكنك معرفة أن بنية البيانات المطلوبة لديها الجهاز التالي:

 struct pollfd { int fd; /*   */ short events; /*   */ short revents; /*   */ }; 

يتم استخدام واصفها كواصف ملف (نحن نعمل مع دفق قياسي ، وبالتالي فهو 0) ، والأحداث المطلوبة نستخدم علامات مختلفة ، نحتاج فقط إلى العلم لوجود البيانات في المخزن المؤقت. لها اسم POLLIN وتساوي 1. نتجاهل مجال الأحداث التي تم إرجاعها ، لأننا لا نقدم أي معلومات لدفق الإدخال. ثم ستبدو مكالمة النظام المطلوبة كما يلي:

 section .data fd dd 0 ;    eve dw 1 ;   - POLLIN rev dw 0 ;  section .text poll: nop push rbx push rcx push rdx push rdi push rsi mov rax, 7 ;   poll mov rdi, fd ;   mov rsi, 1 ;   mov rdx, 0 ;     syscall 

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

المدخلات غير المحظورة في المحطة
 section .data fd dd 0 ;    eve dw 1 ;   - POLLIN rev dw 0 ;  sym db 1 section .text poll: nop push rbx push rcx push rdx push rdi push rsi mov rax, 7 ;   poll mov rdi, fd ;   mov rsi, 1 ;   mov rdx, 0 ;     syscall test rax, rax ;    0 jz .e mov rax, 0 mov rdi, 0 ;   mov rsi, sym ;   read mov rdx, 1 syscall xor rax, rax mov al, byte[sym] ;  ,     .e: pop rsi pop rdi pop rdx pop rcx pop rbx ret 


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

الوظائف الثلاث الموضحة أعلاه (setcan و setnoncan و poll) كافية لضبط الإدخال الطرفي لنفسك أنت. فهي بسيطة للغاية لفهمها واستخدامها. ومع ذلك ، في لعبة حقيقية ، سيكون من الجيد تأمينها وفقًا لمنهج C المعتاد ، ولكن هذا بالفعل عمل مبرمج.

مصادر


1) مصادر الدالتين tcgetattr و tcsetattr ؛
2) توثيق استدعاء نظام ioctl ؛
3) وثائق استدعاء نظام الاقتراع .
4) وثائق النهايات .
5) جدول استدعاء النظام تحت Linux x64 .

Source: https://habr.com/ru/post/ar414309/


All Articles