كيفية عمل Port Scanner في بايثون

port scanning هي طريقة مسح لتحديد المنافذ المفتوحة على جهاز الشبكة ، سواء كانت خادمًا أو جهاز الراوتر أو جهازًا عاديًا. port scanner هو مجرد برنامج نصي أو برنامج مصمم لفحص مضيف للمنافذ  (ports)المفتوحة.

في هذا البرنامج التعليمي ، ستتمكن من إنشاء ماسح ضوئي للمنافذ(ports)  في Python باستخدام مكتبة socket . الفكرة الأساسية وراء هذا الماسح الضوئي البسيط للمنافذ هي محاولة الاتصال بمضيف معين (موقع ويب أو خادم أو أي جهاز متصل بالإنترنت / الشبكة) من خلال قائمة المنافذ ، إذا تم إنشاء اتصال ناجح ، فهذا يعني أن المنفذ مفتوح .

على سبيل المثال ، عندما قمت بتحميل صفحة الويب هذه ، قمت بإجراء اتصال بهذا الموقع على المنفذ 80 ، وبالمثل ، سيحاول هذا البرنامج النصي الاتصال بمضيف ولكن على منافذ متعددة. هذا النوع من الأدوات مفيد للهاكرز ومختبري الاختراق ، لذلك لا تستخدم هذه الأداة مع مضيف ليس لديك إذن باختباره!

اختياريًا ، تحتاج إلى تثبيت وحدة Colorama للطباعة fancy:

pip3 install colorama

Port Scanner بسيط

في هذا القسم ، سنكتب Port Scanner بسيط ،  لنبدأ باستيراد وحدة socket :

import socket 
from colorama import init, Fore
init()
GREEN = Fore.GREEN
RESET = Fore.RESET
GRAY = Fore.LIGHTBLACK_EXCode language: JavaScript (javascript)

ملاحظة:  وحدة socket مثبتة بالفعل في جهازك ، وهي مدمجة في وحدة نمطية في مكاتب Python الاساسية ، لذلك لن تضطر إلى تثبيت أي شيء.

تزودنا وحدة socket بعمليات اsocket ، ووظائف المهام المتعلقة بالشبكة ، وما إلى ذلك ، وهي مستخدمة على نطاق واسع على الإنترنت ، حيث إنها وراء أي اتصال بأي شبكة. أي اتصال بالشبكة يمر عبر socket ، مزيد من التفاصيل موجودة في وثائق Python الرسمية.

سنستخدم colorama هنا فقط للطباعة بألوان خضراء عندما يكون المنفذ مفتوحًا ، والرمادي عندما يكون مغلقًا.

دعنا نحدد الوظيفة المسؤولة عن تحديد ما إذا كان المنفذ مفتوحًا:

def is_port_open(host, port):
    s = socket.socket()
    try:
        s.connect((host, port))
    except:
        return False
    else:
        return TrueCode language: PHP (php)

تحاول وظيفة s.connect ((host ، port)) توصيل sockt بعنوان بعيد (مضيف ، منفذ) ، ستثير استثناءً عندما يفشل في الاتصال بهذا المضيف ، ولهذا السبب قمنا بلف هذا السطر من التعليمات البرمجية في كتلة try و except ، لذلك كلما ظهر استثناء ، فهذا مؤشر لنا على أن المنفذ مغلق بالفعل ، وإلا فإنه مفتوح.

الآن دعنا نستخدم الوظيفة المذكورة أعلاه ونكررها عبر مجموعة من المنافذ:

host = input("Enter the host:")
for port in range(1, 1025):
    if is_port_open(host, port):
        print(f"{GREEN}[+] {host}:{port} is open      {RESET}")
    else:
        print(f"{GRAY}[!] {host}:{port} is closed    {RESET}", end="r")Code language: PHP (php)

سيقوم الكود أعلاه بمسح المنافذ التي تتراوح من 1 إلى 1024 ، ويمكنك تغيير النطاق إلى 65535 إذا كنت تريد ، ولكن هذا سيستغرق وقتًا أطول للانتهاء.

عندما تحاول تشغيله ، ستلاحظ على الفور أن البرنامج النصي بطيء جدًا ، حسنًا ، يمكننا التخلص من ذلك إذا حددنا مهلة قدرها 200 مللي ثانية أو نحو ذلك (باستخدام طريقة Settimeout (0.2)). ومع ذلك ، يمكن أن يقلل هذا في الواقع من دقة الاستطلاع ، خاصة عندما يكون وقت الاستجابة مرتفعًا جدًا. نتيجة لذلك ، نحتاج إلى طريقة أفضل لتسريع ذلك.

Port Scanner سريع

الآن دعنا نأخذ port scanner البسيط إلى مستوى أعلى. في هذا القسم ، سنقوم بكتابة ماسح ضوئي للمنافذ المترابطة يمكنه مسح 200 منفذ أو أكثر في وقت واحد.

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

import argparse
import socket 
from colorama import init, Fore
from threading import Thread, Lock
from queue import Queue
init()
GREEN = Fore.GREEN
RESET = Fore.RESET
GRAY = Fore.LIGHTBLACK_EX
N_THREADS = 200
q = Queue()
print_lock = Lock()
def port_scan(port):
    try:
        s = socket.socket()
        s.connect((host, port))
    except:
        with print_lock:
            print(f"{GRAY}{host:15}:{port:5} is closed  {RESET}", end='r')
    else:
        with print_lock:
            print(f"{GREEN}{host:15}:{port:5} is open    {RESET}")
    finally:
        s.close()Code language: JavaScript (javascript)

لذا هذه المرة لا تقوم الوظيفة بإرجاع أي شيء ، نريد فقط طباعة ما إذا كان المنفذ مفتوحًا.

استخدمنا فئة Queue () من وحدة قائمة queue المدمجة التي ستساعدنا في استهلاك المنافذ ، والوظيفتان التاليتان هما لإنتاج وتعبئة قائمة Queue  بأرقام المنافذ واستخدام الخيوط لاستهلاكها:

def scan_thread():
    global q
    while True:
        worker = q.get()
        port_scan(worker)
        q.task_done()
def main(host, ports):
    global q
    for t in range(N_THREADS):
        # for each thread, start it
        t = Thread(target=scan_thread)
        # when we set daemon to true, that thread will end when the main thread ends
        t.daemon = True
        t.start()
    for worker in ports:
        q.put(worker)
    q.join()Code language: PHP (php)

وظيفة scan_thread () هي الحصول على أرقام المنافذ من قائمة Queue ومسحها ضوئيًا ، ثم إضافتها إلى المهام المنجزة ، في حين أن الدالة main() مسؤولة عن ملء قائمة الانتظار بأرقام المنافذ وتوليد خيوط N_THREADS لاستهلاكها معهم.

لاحظ أن q.get () سيتم حظره حتى يتوفر عنصر واحد في قائمة Queue. يضع q.put () عنصرًا واحدًا في قائمة Queue وينتظر q.join () حتى تنتهي كافة سلاسل العمليات (مسح قائمة الانتظار).

أخيرًا ، لنقم بعمل محلل وسيطة بسيط حتى نتمكن من تمرير نطاق أرقام المضيف والمنافذ من سطر الأوامر:

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Simple port scanner")
    parser.add_argument("host", help="Host to scan.")
    parser.add_argument("--ports", "-p", dest="port_range", default="1-65535", help="Port range to scan, default is 1-65535 (all ports)")
    args = parser.parse_args()
    host, port_range = args.host, args.port_range
    start_port, end_port = port_range.split("-")
    start_port, end_port = int(start_port), int(end_port)
    ports = [ p for p in range(start_port, end_port)]
    main(host, ports)Code language: JavaScript (javascript)

هذه لقطة شاشة عندما حاولت مسح جهاز الراوتر الخاص بي:

خاتمة

مدهش! انتهى من مسح 5000 منفذ في أقل من ثانيتين! يمكنك استخدام النطاق الافتراضي (من 1 إلى 65535) وسيستغرق الأمر بضع ثوانٍ للانتهاء.

إذا رأيت أن Port Scanner الخاص بك يتجمد على منفذ واحد ، فهذه علامة تحتاج إلى تقليل عدد سلاسل الرسائل الخاصة بك ، إذا كان الخادم الذي تبحث عنه يحتوي على اختبار ping مرتفع ، فيجب عليك تقليل N_THREADS إلى 100 أو 50 أو حتى أقل ، حاول لتجربة هذه المعلمة.

يثبت فحص المنافذ أنه مفيد في كثير من الحالات ، يمكن لمختبِر الاختراق المعتمد استخدام هذه الأداة لمعرفة المنافذ المفتوحة والكشف عن وجود أجهزة أمان محتملة مثل جدران الحماية ، وكذلك اختبار أمان الشبكة وقوة الجهاز.

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

لا يوجد اعجابات