أساسيات الشبكات للمخترقين: كيف تعمل بروتوكولات TCP/IP و DNS؟
ماذا سنتعلم اليوم؟ سنغوص في أعماق بروتوكولات الشبكات الأساسية TCP/IP و DNS، وكيفية التفاعل معها برمجياً باستخدام بايثون، لفهم آليات عملها التي يستغلها المخترقون.
سنتعلم كيفية بناء أدوات بسيطة لفهم الاتصال الموجه (TCP) وكيفية ترجمة أسماء النطاقات (DNS)، وهي اللبنات الأساسية لأي عملية اختراق أو دفاع.
الخطوة 1: فهم أساسيات TCP/IP - الاتصال الموجه (TCP)
بروتوكول TCP (Transmission Control Protocol) هو حجر الزاوية للاتصالات الموثوقة عبر الإنترنت. يضمن وصول البيانات بترتيبها الصحيح وبدون فقدان، وذلك من خلال آليات معقدة مثل المصافحة ثلاثية الاتجاهات (Three-Way Handshake) للبدء بالاتصال، وتأكيدات الاستلام (Acknowledgments)، وإعادة إرسال الحزم المفقودة. فهم كيفية عمل TCP يسمح للمخترقين بتحديد نقاط الضعف مثل المنافذ المفتوحة أو حالات رفض الخدمة (DoS).
ملاحظة تقنية: TCP يعمل في طبقة النقل (Transport Layer) من نموذج OSI/TCP-IP. يوفر خدمة "موجهة للاتصال" (Connection-Oriented)، مما يعني أنه يتم إنشاء اتصال مستقر بين الطرفين قبل تبادل البيانات، ويتم إغلاقه بشكل منظم بعد الانتهاء. هذا يختلف عن UDP (User Datagram Protocol) الذي يعتبر "غير موجه للاتصال" (Connectionless) وأسرع ولكنه غير موثوق.
لنبدأ بكتابة خادم TCP بسيط جداً يستمع على منفذ محدد ويستقبل رسالة واحدة، ثم عميل يتصل به.
الجزء الأول: خادم TCP بسيط (الاستماع)
هذا الكود ينشئ مأخذ توصيل (socket)، يربطه بعنوان IP ومنفذ، ثم يبدأ بالاستماع للاتصالات الواردة.
import socket # استيراد مكتبة الـ sockets للتعامل مع الشبكات
HOST = '127.0.0.1' # عنوان IP الذي سيستمع عليه الخادم (localhost)
PORT = 65432 # المنفذ الذي سيستمع عليه الخادم (اختر منفذاً غير مستخدم)
# إنشاء مأخذ توصيل (socket)
# socket.AF_INET: يشير إلى استخدام IPv4
# socket.SOCK_STREAM: يشير إلى استخدام بروتوكول TCP (اتصال موجه)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind((HOST, PORT)) # ربط المأخذ بالعنوان والمنفذ المحددين
s.listen() # بدء الاستماع للاتصالات الواردة
print(f"[*] الخادم يستمع على {HOST}:{PORT}")
conn, addr = s.accept() # قبول اتصال جديد (conn هو مأخذ التوصيل للعميل، addr هو عنوان العميل)
with conn:
print(f"[*] تم الاتصال بـ {addr}")
data = conn.recv(1024) # استقبال بيانات من العميل (1024 بايت كحد أقصى)
if data:
print(f"[*] تم استلام: {data.decode('utf-8')}") # طباعة البيانات المستلمة بعد فك تشفيرها
conn.sendall(b'Hello from server!') # إرسال رد إلى العميل
الخطوة 2: استكشاف DNS - مترجم الإنترنت
نظام أسماء النطاقات (DNS - Domain Name System) هو بمثابة دليل الهاتف للإنترنت. بدلاً من تذكر عناوين IP الرقمية المعقدة (مثل 192.0.2.1)، نستخدم أسماء نطاقات سهلة التذكر (مثل google.com). يقوم DNS بترجمة هذه الأسماء إلى عناوين IP المقابلة لها، مما يسمح لأجهزتنا بالعثور على الخوادم الصحيحة على الشبكة. فهم كيفية عمل DNS ضروري للمخترقين لاكتشاف البنى التحتية، وتحديد النطاقات الفرعية، وحتى تنفيذ هجمات مثل انتحال DNS (DNS Spoofing).
ملاحظة تقنية: عملية ترجمة اسم النطاق إلى IP تسمى "استعلام DNS" (DNS Query). تتم هذه العملية عادةً بشكل هرمي، حيث يتم توجيه الاستعلام من خادم DNS المحلي لديك إلى خوادم DNS الجذرية (Root Servers)، ثم إلى خوادم TLD (Top-Level Domain)، وأخيراً إلى الخوادم المعتمدة (Authoritative Servers) للنطاق المطلوب.
سنقوم بكتابة سكربت بايثون بسيط لترجمة اسم نطاق معين إلى عنوان IP الخاص به.
import socket # استيراد مكتبة الـ sockets للتعامل مع الشبكات
def resolve_domain(domain_name):
try:
# socket.gethostbyname تقوم بترجمة اسم النطاق إلى عنوان IP
ip_address = socket.gethostbyname(domain_name)
print(f"[*] اسم النطاق '{domain_name}' يترجم إلى IP: {ip_address}")
return ip_address
except socket.gaierror:
print(f"[!] لا يمكن ترجمة اسم النطاق '{domain_name}'. قد يكون غير موجود أو هناك مشكلة في الشبكة.")
return None
# مثال على الاستخدام:
# resolve_domain("example.com")
الخطوة 3: دمج المفاهيم - فحص المنافذ الأساسي باستخدام TCP و DNS
الآن بعد أن فهمنا كيف يعمل TCP للاتصال وكيف يترجم DNS أسماء النطاقات، يمكننا دمج هذه المفاهيم لإنشاء ماسح منافذ (Port Scanner) أساسي. ماسح المنافذ هو أداة تستخدم لتحديد المنافذ المفتوحة على خادم معين. المنافذ المفتوحة تشير إلى خدمات تعمل على هذا الخادم، والتي قد تكون نقاط دخول محتملة للمخترقين.
ملاحظة تقنية: فحص المنافذ هو خطوة أولى وحاسمة في أي عملية استطلاع (Reconnaissance). يمكن أن يكشف عن خدمات الويب (HTTP/HTTPS)، SSH، FTP، قواعد البيانات، وغيرها، مما يمنح المهاجم فكرة عن سطح الهجوم المحتمل.
سنقوم بتوسيع سكربت DNS لترجمة اسم النطاق أولاً، ثم نحاول الاتصال بمجموعة من المنافذ الشائعة على عنوان IP الناتج.
import socket # استيراد مكتبة الـ sockets للتعامل مع الشبكات
def simple_port_scanner(target_host, ports):
print(f"[*] بدء فحص المنافذ على المضيف: {target_host}")
for port in ports:
try:
# إنشاء مأخذ توصيل TCP
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(1) # تعيين مهلة للاتصال (ثانية واحدة) لتجنب التعليق
result = s.connect_ex((target_host, port)) # محاولة الاتصال بالمنفذ
if result == 0: # إذا كان result صفراً، فهذا يعني أن الاتصال ناجح (المنفذ مفتوح)
print(f" [+] المنفذ {port} مفتوح")
else:
print(f" [-] المنفذ {port} مغلق أو تم تصفية الاتصال")
s.close() # إغلاق مأخذ التوصيل
except socket.error as e:
print(f" [!] خطأ أثناء فحص المنفذ {port}: {e}")
print(f"[*] انتهى فحص المنافذ على {target_host}")
# مثال على الاستخدام:
# target_domain = "scanme.nmap.org" # استخدم نطاقاً يسمح بالفحص لأغراض تعليمية
# common_ports = [21, 22, 23, 25, 80, 110, 139, 443, 445, 3389]
#
# ip = resolve_domain(target_domain) # استخدم دالة resolve_domain من الخطوة 2
# if ip:
# simple_port_scanner(ip, common_ports)
الكود النهائي الكامل
هذا السكربت يجمع الأجزاء الثلاثة: خادم TCP بسيط، دالة لترجمة أسماء النطاقات، وماسح منافذ أساسي. يمكنك تشغيل الخادم في نافذة طرفية منفصلة، ثم تشغيل العميل وماسح المنافذ في نافذة أخرى.
import socket
import threading
import time
# --- خادم TCP بسيط (لتشغيله في نافذة طرفية منفصلة) ---
def start_tcp_server(host, port):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind((host, port))
s.listen()
print(f"[SERVER] الخادم يستمع على {host}:{port}")
conn, addr = s.accept()
with conn:
print(f"[SERVER] تم الاتصال بـ {addr}")
data = conn.recv(1024)
if data:
print(f"[SERVER] تم استلام: {data.decode('utf-8')}")
conn.sendall(b'Hello from server!')
print("[SERVER] تم إغلاق اتصال الخادم.")
# --- دالة ترجمة اسم النطاق (DNS Resolver) ---
def resolve_domain(domain_name):
try:
ip_address = socket.gethostbyname(domain_name)
print(f"[*] اسم النطاق '{domain_name}' يترجم إلى IP: {ip_address}")
return ip_address
except socket.gaierror:
print(f"[!] لا يمكن ترجمة اسم النطاق '{domain_name}'.")
return None
# --- ماسح المنافذ الأساسي ---
def simple_port_scanner(target_host, ports):
print(f"[*] بدء فحص المنافذ على المضيف: {target_host}")
open_ports = []
for port in ports:
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(0.5) # مهلة أقصر للفحص السريع
result = s.connect_ex((target_host, port))
if result == 0:
print(f" [+] المنفذ {port} مفتوح")
open_ports.append(port)
else:
# يمكن طباعة المنافذ المغلقة أو تصفيتها إذا لزم الأمر
pass
s.close()
except socket.error as e:
# يمكن طباعة الأخطاء إذا لزم الأمر
pass
print(f"[*] انتهى فحص المنافذ على {target_host}. المنافذ المفتوحة: {open_ports}")
return open_ports
# --- وظيفة العميل لتجربة الخادم ---
def run_client(host, port):
time.sleep(1) # انتظر قليلاً ليتأكد الخادم من البدء
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((host, port))
message = b'Hello from client!'
s.sendall(message)
print(f"[CLIENT] أرسل: {message.decode('utf-8')}")
data = s.recv(1024)
print(f"[CLIENT] استقبل: {data.decode('utf-8')}")
except ConnectionRefusedError:
print(f"[CLIENT] فشل الاتصال بالخادم على {host}:{port}. تأكد من تشغيله.")
except Exception as e:
print(f"[CLIENT] حدث خطأ: {e}")
if __name__ == "__main__":
# إعدادات الخادم والعميل
LOCAL_HOST = '127.0.0.1'
LOCAL_PORT = 65432
# تشغيل الخادم في خيط منفصل لكي لا يحظر البرنامج الرئيسي
server_thread = threading.Thread(target=start_tcp_server, args=(LOCAL_HOST, LOCAL_PORT))
server_thread.daemon = True # سيتم إغلاق الخيط عند انتهاء البرنامج الرئيسي
server_thread.start()
# تشغيل العميل بعد فترة قصيرة
client_thread = threading.Thread(target=run_client, args=(LOCAL_HOST, LOCAL_PORT))
client_thread.daemon = True
client_thread.start()
# انتظر قليلاً لانتهاء تفاعل الخادم والعميل
time.sleep(3)
print("\n" + "="*50 + "\n")
# --- جزء فحص المنافذ و DNS ---
target_domain = "scanme.nmap.org" # نطاق مسموح بفحصه لأغراض تعليمية
# target_domain = "google.com" # يمكن استخدام نطاقات أخرى بحذر ومسؤولية
common_ports = [21, 22, 80, 443, 8080] # منافذ شائعة للفحص
target_ip = resolve_domain(target_domain)
if target_ip:
simple_port_scanner(target_ip, common_ports)
# للحفاظ على الخيوط النشطة لفترة أطول إذا لزم الأمر
# input("اضغط Enter للخروج...")
النتيجة المتوقعة
عند تشغيل السكربت النهائي، ستلاحظ ما يلي:
- سيتم تشغيل خادم TCP بسيط في الخلفية، وسيطبع رسالة تفيد بأنه يستمع على
127.0.0.1:65432. - بعد فترة وجيزة، سيتصل العميل بالخادم، ويرسل رسالة
"Hello from client!"، ويستقبل رداً"Hello from server!". ستظهر هذه الرسائل في مخرجات الطرفية. - بعد انتهاء تفاعل الخادم والعميل، سيبدأ جزء DNS وماسح المنافذ.
- سيتم ترجمة اسم النطاق المحدد (مثلاً
scanme.nmap.org) إلى عنوان IP الخاص به، وستتم طباعة هذا IP. - بعد ذلك، سيبدأ ماسح المنافذ بمحاولة الاتصال بالمنافذ الشائعة على عنوان IP المستهدف.
- ستتم طباعة رسالة لكل منفذ توضح ما إذا كان "مفتوحاً" أو "مغلقاً/تمت تصفيته".
- في النهاية، سيتم عرض قائمة بالمنافذ المفتوحة التي تم العثور عليها.
هذه المخرجات تظهر بوضوح كيفية عمل بروتوكولات TCP/IP و DNS على مستوى منخفض، وكيف يمكن استخدامها لمهام الاستطلاع الأساسية في الأمن السيبراني.