




السمعة:
- إنضم6 مارس 2024
- المشاركات 115
- الحلول 4
- مستوى التفاعل 220
- النقاط 43



نظام التشغيل والتطبيقات اللي بنستخدمها بتتكتب بلغات برمجة زي C++، Java، وPython. لكن المكونات المادية زي المعالج والذاكرة مش بتفهم اللغات دي لأنها بتعتمد على معالجة البيانات الثنائية (1 و 0).
هنا بيجي دور لغة التجميع (Assembly)، اللي بتكتب تعليمات مباشرة وبسيطة للمعالج زي الأمر add rax, 1 اللي بيزوّد قيمة الـ rax بمقدار 1 الأمر ده سهل الفهم مقارنةً بالكود المكافئ ليه في Shellcode اللي هو 4883C001، أو حتى في الكود الثنائي اللي هو 01001000 10000011 11000000 00000001.
لغة التجميع بتسهل كتابة الأوامر للمعالج بشكل أقرب للبشر وبتتحول بعد كده لكود الآلة اللي بيشتغل عليه المعالج الكود ده بيتمثّل أحيانًا في شكل Shellcode اللي بيكون تمثيل hex للكود الثنائي وبيتترجم بسهولة للـ Assembly وبيتنفذ مباشرةً في الذاكرة كتعليمات ثنائية
High-level vs. Low-level
في الماضي كان لازم تتكتب التطبيقات بلغة التجميع لكل معالج عشان كل معالج بيفهم مجموعة مختلفة من التعليمات وده كان بيصعب عملية تطوير تطبيقات لمجموعة متنوعة من المعالجات في السبعينيات اتطورت لغات برمجة عالية المستوى زي C عشان تتيح كتابة كود واحد بسيط يشتغل على أي معالج من غير ما نحتاج نعيد كتابته لكل معالج وده بقى ممكن بفضل المترجمات اللي بُنيت لكل لغةلما بيتترجم الكود المكتوب بلغة عالية المستوى بيتحول لتعليمات تجميع خاصة بالمعالج اللي بيتترجم له وبعدين بيتحول لكود الآلة عشان يشتغل على المعالج عشان كده اتبنت مترجمات مختلفة للغات ومعالجات مختلفة لتحويل الكود العالي المستوى لكود تجميع وبعدين لكود الآلة اللي يتماشى مع المعالج
بعد كده ظهرت interpreted languages زي Python وPHP وBash وJavaScript وغيرها اللي مش بيتعملها ترجمة لكن بتتفسر وقت التشغيل اللغات دي بتستخدم مكتبات مبنية مسبقًا عشان تشغل تعليماتها والمكتبات دي غالبًا مكتوبة ومترجمة بلغات عالية المستوى زي C أو C++ لما بننفذ أمر في لغة تفسيرية بيستخدم المكتبة المترجمة عشان ينفذ الأمر اللي بيستخدم كود التجميع أو كود الآلة بتاعها لتنفيذ كل التعليمات اللازمة لتشغيل الأمر ده على المعالج
Compilation Stages => مراحل التجميع

لما بنكتب برنامج Hello World في لغة برمجة تفسيرية زي Python بيكون الكود بسيط جدًا زي كده
Python:
print("Hello World!")
الكود ده بيشتغل بفضل المترجم الداخلي للغة Python اللي بيحول الأمر لعمليات بتفهمها المكتبات المبنية مسبقًا اللي غالبًا مكتوبة بلغة C أو C++
لما بنترجم الكود ده للغة C بيكون بالشكل التالي
C:
#include <unistd.h>
int main()
{
write(1, "Hello World!", 12);
_exit(0);
}
الفرق هنا إن Python بيخبي التفاصيل التقنية وبيخلينا نكتب الكود بشكل سهل وبسيط بينما في لغة C بنحتاج نكتب أوامر أكثر تفصيلًا عشان نوجه المعالج مباشرة لتنفيذ المهمة
الكود المكتوب بلغة C السابق يستخدم استدعاء النظام write في Linux، بينما في لغة التجميع (Assembly) الكود يظهر بالشكل التالي:
كود:
mov rax, 1
mov rdi, 1
mov rsi, message
mov rdx, 12
syscall
mov rax, 60
mov rdi, 0
syscall
كما نرى، عند استدعاء write سواء في C أو Assembly، يتم استخدام الرقم 1 للنص و12 كطول الرسالة ك argument هذا الموضوع سيتم تغطيته بمزيد من التفصيل لاحقًا
الكود السابق بلغة التجميع يمكن تحويله إلى كود آلة hex كما يلي
Rich (BB code):
48 c7 c0 01
48 c7 c7 01
48 8b 34 25
48 c7 c2 0d
0f 05
48 c7 c0 3c
48 c7 c7 00
0f 05
عشان المعالج ينفذ التعليمات المرتبطة بهذا الكود يجب ترجمتها إلى تنسيق ثنائي والذي سيبدو كالتالي
كود:
01001000 11000111 11000000 00000001
01001000 11000111 11000111 00000001
01001000 10001011 00110100 00100101
01001000 11000111 11000010 00001101
00001111 00000101
01001000 11000111 11000000 00111100
01001000 11000111 11000111 00000000
00001111 00000101
المعالج يستخدم شحنات كهربائية مختلفة للتمثيل 1 و0 وبالتالي يمكنه حساب هذه التعليمات من البيانات الثنائية بمجرد استلامها
ملاحظة مع لغات متعددة المنصات زي Java الكود يتم تجميعه إلى Java Bytecode اللي هو نفس الشيء لكل المعالجات والأنظمة وبعد كده يتم تجميعه إلى كود آلة بواسطة بيئة تشغيل Java المحلية وهذا هو السبب في أن Java تعتبر أبطأ نسبيًا من لغات أخرى زي C++ اللي تتجمع مباشرة إلى كود آلة لغات زي C++ تكون أكثر ملاءمة للتطبيقات اللي تحتاج معالج قوي زي الألعاب
التعديل الأخير بواسطة المشرف: