بسم الله الرحمن الرحيم
في
هذا الشرح البسيط ستتعلم كيف تبرمج برنامج بسيط بالبايثون يظهر لك المنافذ
المفتوحة على اي جهاز مشبوك على الشبكة الخاصة ببرتوكول TCP , لكن قبل ان
ابدأ يجب ان انبه ان هذا البرنامج للأغراض التعليمية و يجب الا تستخدمه الا
على انظمة يحق لك الوصول له .
البرنامج يستخدم عملية handshake في برتوكول TCP في حالة اتمام الاتصال يقوم البرنامج بأنذار المستخدم برقم المنفذ (Port).
هذه الصورة تبين عملية اتصال ناجحة لبرتوكول TCP
|
مصدر الصورة :https://commons.wikimedia.org/wiki/File:Tcp_normal_2.png
|
1. يرسل الجهاز للسيرفر رسالة TCP -SYN
2. في حالة توافر المنفذ يرد السيرفر برسالة SYN-ACK
3. لتأكيد الاتصال يرسل الجهاز للسيرفر رسالة ACK لتأكيد الاتصال
طبعا هذا الشرح مبسط جدا , لغاية عدم تعقيد الموضوع , يمكن في المستقبل اخصص موضوع خاص ببرتوكول TCP .
في
حالة عدم نجاح عملية الاتصال سوف يتم اعتبار المنفذ مغلق , و الأن لشرح
بسيط لبرمجة الشبكات في البايثون قبل ان نبدأ , بشكل عام جميع لغات البرمجة
تتشابه في الخطوات التالية :
طبعا
لمثالنا سوف يكون برنامجنا هو Client لكن من دون ارسال اي بيانات فقط
سيحاول الاتصال و سيقوم بعرض النتيجة للاتصال ثم اغلاق الاتصال .
و الأن للنص البرمجي الكامل لمثالنا :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 | #!/usr/bin/python3
import sys
import socket
if len(sys.argv) == 2:
print("Start Scanning : {}".format(sys.argv[1]))
target = sys.argv[1]
else:
print(("Usage:{} target".format(sys.argv[0])))
exit(2)
try:
for port in range(1,65535):
sos = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
result = sos.connect_ex((target,port))
if result == 0 :
print ( "{} is Open".format(port))
sos.close()
except KeyboardInterrupt:
print("\nExiting the program")
exit(0)
except socket.gaierror:
print("Host couldn't be reslove")
exit(1)
except socket.error:
print("Couldn't connect to {}".format(target))
exit(2)
|
بندأ بشرح برنامجنا بالتفصيل , أولا نبدأ في السطرين الأولين :
1
2 | import socket
import sys |
المكتبة
البرمجة socket بنستخدمها لعملية الاتصال , هذه المكتبة تستخدم لجميع
عمليات الشبكات الاساسية , و مكتبة sys التي بنحتاجها لمعالجة المعطيات من
سطر الاوامر .
و الان الى السطر الذي يعالج معطيات سطر الأوامر :
1
2
3
4
5
6 | if len(sys.argv) == 2: #اذا طول معطيات سطر الاوامر يساوي 2
print("Start Scanning : {}".format(sys.argv[1])) #اطبع اسم الهدف
target = sys.argv[1] #احفظ اسم الهدف في المتغير target
else:
print(("Usage:{} target".format(sys.argv[0]))) #في حال عدم تطابق الشرط اطبع رسالة الاستخدام و اخرج من البرنامج
exit(2)
|
هذا
مثال على استخدام برنامجنا , في بايثون يكون معاملات البرنامج (program
parameters) تبدأ دائما باسم ملف البرنامج , و يتم تخزين المعاملات في
مصفوفة (array) يمكن الوصول لها باستخدام sys.argv
kh@DESKTOP-3444:~$ ./myscript target
و الأن إلى الجزء الذي يقوم بالعمل الفعلي :
1
2
3
4
5
6
7 | for port in range(1,65535): # المدى يمثل جميع منافذ برتوكول TCP
sos = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #انشاء مقبس الشبكة
result = sos.connect_ex((target,port)) # محاولة الاتصال
if result == 0 : #اذا كانت نتيجة الاتصال صفر فالاتصال ناجح
print ( "{} is Open".format(port)) #طباعة رسالة للمستخدم
sos.close() #اغلاق الاتصال
|
قمت بوضع هذه الأوامر في كتلة Try-Except للتعامل مع الإستثاءات التي قد تواجه التنفيذ , والأن لشرح هذه الاسطر بتفصيل اكثر:
1 | sos = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
|
هذا السطر الذي ينشيء المقبس (socket) , و يخبر النظام لحجز الموارد كما يحدد نوع نظام العناوين و نوع الاتصال :
- AF_INET
: نظام عناوين الانترنت النسخة الرابعة Internet protocol version 4 أو
اختصارا IPv4 , هو النظام الحالي المستخدم لعنونة الاجهزة على شبكة
الانترنت
- SOCK_STREAM : استخدام برتوكول TCP كنوع البرتوكول المستخدم لتوصيل البيانات .
للعلم يمكنك كتابة امر تحديد المقبس (socket) من دون تحديد نظام العناوين و نوع الاتصال حيث انه القيم الافتراضية .
و
الأن بعد انشاء الاتصال سنحاول اجراء الاتصال باستخدام connect_ex , في
حالة نجاح الاتصال سنحصل على القيمة صفر , و في هذه الحالة فقط سيتم طباعة
رقم المنفذ :
1
2
3 | result = sos.connect_ex((target,port))
if result == 0 :
print ( "{} is Open".format(port))
|
و في الاخير سنقوم بأغلاق المنفذ :
و للتعامل مع جميع الاستثناءات (exceptions) قمنا باستخدام try..except :
1
2
3
4
5
6
7
8
9
10
11
12
13 | try:
except KeyboardInterrupt:
print("\nExiting the program")
exit(0)
except socket.gaierror:
print("Host couldn't be reslove")
exit(1)
except socket.error:
print("Couldn't connect to {}".format(target))
exit(2)
|
و هذا شرح للاستثناءات :
- KeyboardInterrupt : في حالة ضغط المستخدم Ctrl+C سيتم اغلاق البرنامج
- socket.gaierror : في حالة فشل البرنامج بتحويل النطاق (Domain) إلى عنوان رقمي IP
- socket.error : في حالة فشل انشاء مقبس (socket)
هذا
شرح مبسط لهذا البرنامج البسيط , لكن هل هذا البرنامج يعمل , اذا اردت
تجربة البرنامج تستطيع استخدام جهازك مباشرة باستخدام العنوان localhost .
لكن هل هذا البرنامج عملي ؟ , بصراحة هذا البرنامج يعمل و لكن هنالك الكثير من الامور الذي يجب توضيحها
- اولا
هذا البرنامج سيكون بطيء بشكل كبير حيث يتم تنفيذه بشكل خطي و اذا اردنا
تسريعه يجب ان يتم التنفيذ بأستخدام التنفيذ متزامن (concurrent execution )
.
- عملية مسح المنافذ (port scan ) باستخدام جميع المنافذ و
بالتسلسل و بدون وجود وقت انتظار سيجعل ابسط برنامج جدار نار (Firewall)
ينذر المستخدم .
- عملية المسح باستخدام عملية الاتصال كاملة , "مصافحة برتوكول TCP" الكاملة بطيئة و مكشوفة للبرامج جدران النار.
في النهاية هذا رابط لموضوع عن برمجة الشبكات ببايثون بالتفصيل :