
كل مبرمج هو في الأساس مخترق. بعد كل شيء ، تم استدعاء القرصنة في البداية البحث عن حل ماهر وغير واضح. يساعدك فهم البرمجة في العثور على الثغرات ، وتساعدك مهارات اكتشاف الثغرات على إنشاء البرامج ، لذلك يقوم العديد من المتسللين بكليهما في نفس الوقت. هناك حركات غير قياسية مثيرة للاهتمام سواء في تقنيات كتابة البرامج الأنيقة أو في تقنيات العثور على نقاط الضعف.
من أين تبدأ؟ للكتابة فوق الذاكرة باستخدام تجاوزات المخزن المؤقت ، والوصول إلى خادم بعيد واتصالات الاعتراض ، يجب عليك البرمجة في C والمجمع ، واستخدام كود shell وسجلات المعالج ، والتعرف على تفاعلات الشبكة والتشفير ، وأكثر من ذلك بكثير.
بغض النظر عن مقدار ما نريد أن نصدقه بالمعجزات ، فإن البرامج وشبكات الكمبيوتر التي تعتمد عليها حياتنا اليومية لها نقاط ضعف.
عالم بلا قراصنة هو عالم بدون فضول وحلول مبتكرة. (جون إريكسون)
الإجراءات المضادة
هناك مثل هذا الضفدع - Listolaz الرهيب (Phyllobates terribilis). تحتوي غددها الجلدية على أقوى السم. المسها فقط لتسمم قاتل. يفسر هذا العلاج القوي بحقيقة أن هذه الضفادع تتغذى على أنواع معينة من الثعابين التي طورت مناعة ضد السم. تدريجيا ، أصبح سم الضفادع أقوى وأقوى. نتيجة لهذا التطور المشترك ، لم يكن لدى متسلقي الأوراق الرهيبين أي أعداء طبيعيين آخرين. شيء مماثل يحدث مع المتسللين. لقد كانت التقنيات التي اخترعوها معروفة منذ فترة طويلة ، لذا فإن ظهور الإجراءات المضادة أمر طبيعي تمامًا. رداً على ذلك ، يبحث القراصنة عن طرق للتحايل على آليات الدفاع وتدميرها ، مما يؤدي إلى إنشاء تقنيات دفاعية جديدة.
هذه الدورة من البحث عن التدابير والإجراءات المضادة مفيدة للغاية. تصبح الفيروسات والديدان سببًا للعديد من المشاكل وتسبب خسائر كبيرة للأعمال ، ولكنها في نفس الوقت تجبر المطورين على اتخاذ إجراءات انتقامية لحل المشكلات التي نشأت. تتكاثر الديدان ذاتيًا باستخدام نقاط ضعف البرامج منخفضة الجودة. غالبًا ما تمر الأخطاء دون أن يلاحظها أحد على مر السنين ، والديدان غير الضارة نسبيًا مثل CodeRed أو Sasser تجبر مطوري البرامج على إصلاحها. يمكن مقارنة هذه العملية بجدري الماء ، وهو أفضل للمرض في مرحلة الطفولة ، وليس في مرحلة البلوغ ، عندما يمكن أن يؤدي إلى عواقب وخيمة. إذا لم تجذب ديدان الإنترنت اهتمامًا عالميًا بالثقوب الأمنية ، فستظل هذه الثقوب مفتوحة للهجوم بأغراض أكثر ضررًا بكثير من التكاثر الذاتي البسيط. بهذه الطريقة ، تسهم الديدان والفيروسات في الأمن على المدى الطويل. ولكن هناك طرق أكثر نشاطًا: تقنيات تحاول إبطال نتائج الهجوم أو تجعله مستحيلًا تمامًا. إن مفهوم "الإجراءات المضادة" غامض نوعًا ما ، وقد تعني هذه الكلمات الوسائل التقنية للأمان ، أو مجموعة من القواعد ، أو برنامجًا أو مجرد مسؤول نظام يقظ. تنقسم الإجراءات المضادة بشكل مشروط إلى مجموعتين: محاولة اكتشاف هجوم ومحاولة حماية الضعف.
0x610 أداة كشف الهجوم
تتلخص الإجراءات المضادة من المجموعة الأولى في محاولات ملاحظة التسلل في الوقت المناسب والتفاعل معه بطريقة أو بأخرى. يمكن تنفيذ عملية التعرف بأي شكل من الأشكال - من سجلات النظام التي يقرأها المشرف إلى البرامج التي تقوم بتحليل حركة مرور الشبكة. في بعض الأحيان ، يأتي رد الفعل على الغزو لفصل الاتصال تلقائيًا أو إغلاق العملية ، وفي بعض الأحيان يتعين على المسؤول دراسة محتويات وحدة التحكم بعناية.
الأساليب المعروفة لاستغلال الثغرات الأمنية ليست خطيرة لمدير النظام مثل تلك التي لم يعرف عنها بعد. كلما كان اكتشاف الاختراق أسرع ، كلما بدأ العمل به بشكل أسرع وزاد احتمال توطينه. إن التطفل الذي تم تجاهله منذ شهور سبب خطير للقلق.
للتعرف على التطفل ، تحتاج إلى توقع ما سيفعله المهاجم. يوفر هذا معلومات حول ما يجب مراقبته بالضبط. تبحث أجهزة الكشف عن أنماط الهجوم المألوفة في السجلات ، وحزم الشبكة ، وحتى في ذاكرة البرنامج. عند اكتشاف تسلل ، يمكنك منع المتسلل من الوصول إلى الجهاز ، واستعادة نظام الملفات التالف باستخدام نسخة احتياطية ، وتحديد ثقب الأمان وإصلاحه. بفضل إمكانيات النسخ الاحتياطي والاستعادة المتاحة اليوم ، تكون الإجراءات المضادة التي تقلل من اكتشاف الهجوم فعالة للغاية.
بالنسبة للمهاجم ، يعني الكشف مواجهة كل ما يفعله. بالطبع ، ليس من الممكن دائمًا ملاحظة هجوم على الفور ، لذلك هناك عدد من سيناريوهات "الاستيلاء والهرب" التي لا تكون فيها حقيقة الكشف مهمة ، ولكن حتى في هذه الحالات ، من الأفضل عدم ترك أي أثر. السرية هي واحدة من أهم صفات المتسللين. إن الوصول إلى shell الأوامر مع حقوق المسؤول عن طريق استغلال نقاط الضعف يجعل من الممكن القيام بأي شيء على النظام ، وإذا تمكنت من تجنب الكشف ، فلن يعرف أحد عن وجودك. إن الجمع بين التسامح مع الخفاء هو الذي يجعل المتسللين خطرين. يمكنهم بسهولة اعتراض كلمات المرور والبيانات على الشبكة ، وإضافة إشارات مرجعية إلى البرامج للوصول اللاحق غير المصرح به ، ومهاجمة عقد الشبكة الأخرى. للبقاء في الظل ، يجب أن يفهم الهاكر طرق التعرف المستخدمة في حالة معينة. إذا كنت تعرف بالضبط ما يبحثون عنه ، يمكنك تجنب أنماط معينة من استغلال الثغرة أو إخفاء أفعالك على أنها صالحة. إن العامل الدافع لدورة التطور المشترك لأدوات وتقنيات الكشف التي تسمح بالمرور دون أن يلاحظها أحد هي الأفكار التي لم تحدث بعد للجانب الآخر.
0x620 شياطين النظام
ومع ذلك ، من الأفضل مناقشة الإجراءات المضادة للمتسللين وحلولهم باستخدام مثال عملي. الآن سننظر في هجوم على برنامج خادم يقبل الاتصالات الواردة. على أنظمة التشغيل الشبيهة بـ UNIX ، تكون أنظمة daemons مناسبة لهذه المعايير. البرنامج الخفي هو برنامج يعمل في الخلفية وبطريقة معينة مفصولة عن محطة التحكم. صاغ هذا المصطلح في الستينيات قراصنة من معهد ماساتشوستس للتكنولوجيا. كان النموذج الأولي عبارة عن مخلوق أسطوري يقوم بفرز الجزيئات من تجربة عقلية قام بها الفيزيائي جيمس ماكسويل. امتلك شيطان ماكسويل قدرة خارقة على أداء المهام المعقدة بسهولة ، منتهكًا القانون الثاني للديناميكا الحرارية. وبالمثل ، فإن شياطين نظام Linux تؤدي مهامًا بلا كلل مثل منح الوصول إلى SSH والحفاظ على سجلات النظام. عادة ما تنتهي أسماء الشياطين بحرف d ، والتي تؤكد طبيعتها: على سبيل المثال ، sshd أو syslogd.
سوف يحول القليل من التحرير tinyweb.c من القسم 0x427 إلى تشابه واقعي مع البرنامج الخفي. يحتوي الإصدار الجديد من التعليمات البرمجية على وظيفة daemon () ، التي تولد عملية خلفية جديدة. يتم استخدامه من قبل العديد من عمليات نظام لينكس الخفي. هذه هي الصفحة المخصصة لها من الدليل:
DAEMON (3) DAEMON مرجع مبرمج Linux (3)
الاسم
الشيطان - يعمل في الخلفية
النحو
#include <unistd.h> int daemon(int nochdir, int noclose);
الوصف
تقوم الوظيفة () (daemon) بفصل البرنامج من طرف التحكم وتبدأ
لها في الخلفية كبرنامج خفي.
باستخدام وسيطة غير صفرية ، تغير الدالة nochdir ، وظيفة daemon () دليل العمل الحالي.
على الجذر ("/").
باستخدام nullose ، تقوم الدالة daemon () بإعادة توجيه مؤشرات الترابط
الإدخال القياسي والإخراج القياسي والأخطاء في / dev / null.
عودة القيمة
(تفرز هذه الوظيفة نسخة من العملية ، وإذا نجحت الشوكة ()
ينفذ الأصل _exit (0) بحيث تكون الأخطاء الإضافية مرئية فقط
العملية الفرعية.) في حالة نجاحها ، يتم إرجاع صفر. على خطأ ، الوظيفة
إرجاع daemon () -1 وتعيين رقم الخطأ إلى errno المتغير العام
من مكتبة شوكة الدالات (2) و setid (2).
لا يوجد لدى daemons النظام محطة تحكم ، لذلك سيتم إخراج رمز البرنامج الخفي الصغير الجديد إلى السجل. يتم التحكم في الشياطين عادة عن طريق الإشارات. يجب أن يكون الإصدار الجديد من tinyweb قادرًا على تلقي إشارة إكمال من أجل الخروج بشكل صحيح.
0x621 نظرة عامة على الإشارة
توفر الإشارات اتصالات بين العمليات على UNIX. بعد تلقي إشارة من خلال عملية ، يقاطع نظام التشغيل تنفيذها لاستدعاء معالج الإشارة. كل إشارة لها رقمها الخاص ومعالجها الخاص. على سبيل المثال ، عندما تضغط على تركيبة المفاتيح Ctrl + C ، يتم إرسال إشارة مقاطعة ، ينهي معالجها البرنامج المفتوح على محطة التحكم ، حتى إذا كان قد دخل في حلقة لا نهائية.
يمكنك إنشاء معالجات الإشارة الخاصة بك وتسجيلها باستخدام وظيفة signal (). دعونا نلقي نظرة على رمز يتم فيه تسجيل العديد من المعالجات لبعض الإشارات ، وفي الجزء الرئيسي هناك حلقة لا نهائية.
signal_example.c #include <stdio.h> #include <stdlib.h> #include <signal.h> /* signal.h * #define SIGHUP 1 * #define SIGINT 2 (Ctrl+C) * #define SIGQUIT 3 (Ctrl+\) * #define SIGILL 4 * #define SIGTRAP 5 * #define SIGABRT 6 * #define SIGBUS 7 * #define SIGFPE 8 * #define SIGKILL 9 * #define SIGUSR1 10 1 * #define SIGSEGV 11 * #define SIGUSR2 12 2 * #define SIGPIPE 13 , * #define SIGALRM 14 , alarm() * #define SIGTERM 15 ( kill) * #define SIGCHLD 17 * #define SIGCONT 18 , * #define SIGSTOP 19 ( ) * #define SIGTSTP 20 [] (Ctrl+Z) * #define SIGTTIN 21 * #define SIGTTOU 22 */ /* */ void signal_handler(int signal) { printf(, signal); if (signal == SIGTSTP) printf(); else if (signal == SIGQUIT) printf(); else if (signal == SIGUSR1) printf(); else if (signal == SIGUSR2) printf(); printf(); } void sigint_handler(int x) { printf(); exit(0); } int main() { /* Registering signal handlers */ signal(SIGQUIT, signal_handler); // signal_handler() signal(SIGTSTP, signal_handler); // signal(SIGUSR1, signal_handler); signal(SIGUSR2, signal_handler); signal(SIGINT, sigint_handler); // sigint_handler() SIGINT while(1) {} // }
عند تنفيذ برنامج مترجم ، يتم تسجيل معالجات الإشارة ، ثم يدخل البرنامج في حلقة لا نهائية. ولكن ، على الرغم من ذلك ، فإن الإشارات الواردة ستقاطع تنفيذها وتتحول إلى معالجات مسجلة. فيما يلي أمثلة على تطبيق الإشارات التي يمكن تنشيطها من طرف التحكم. بعد اكتمال وظيفة signal_handler () ، يعود التحكم إلى الحلقة المتقطعة ، بينما تنهي وظيفة sigint_handler () البرنامج.
reader@hacking:~/booksrc $ gcc -o signal_example signal_example.c reader@hacking:~/booksrc $ ./signal_example 20 SIGTSTP (Ctrl-Z) 3 SIGQUIT (Ctrl-\) Ctrl-C (SIGINT) Exiting. reader@hacking:~/booksrc $
يتيح لك الأمر kill إرسال مجموعة كاملة من الإشارات إلى العملية. بشكل افتراضي ، يرسل إشارة إكمال (SIGTERM). تؤدي إضافة الخيار -l إلى عرض قائمة بجميع الإشارات الممكنة. دعونا نرى كيف يرسل برنامج signal_example الذي يعمل على محطة طرفية أخرى الإشارات SIGUSR1 و SIGUSR2.
reader@hacking:~/booksrc $ kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8 43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1 64) SIGRTMAX reader@hacking:~/booksrc $ ps a | grep signal_example 24491 pts/3 R+ 0:17 ./signal_example 24512 pts/1 S+ 0:00 grep signal_example reader@hacking:~/booksrc $ kill -10 24491 reader@hacking:~/booksrc $ kill -12 24491 reader@hacking:~/booksrc $ kill -9 24491 reader@hacking:~/booksrc $
في النهاية ، يرسل الأمر kill -9 إشارة SIGKILL. لا يمكن تغيير معالج الإشارة ، لذلك يتم استخدام الأمر kill -9 دائمًا لإنهاء العمليات. يوضح برنامج إشارة_العينة الذي تم إطلاقه على محطة طرفية أخرى أنه تم اعتراض الإشارات وتم تدمير العملية.
reader@hacking:~/booksrc $ ./signal_example 10 SIGUSR1 12 SIGUSR2 Killed reader@hacking:~/booksrc $
الإشارات نفسها بسيطة للغاية ، ولكن التفاعل بين العمليات يمكن أن يتحول بسرعة إلى شبكة معقدة من التبعيات. لحسن الحظ ، في برنامجنا الصغير على الويب ، يتم استخدام الإشارات فقط للإغلاق الصحيح ، ويتم تنفيذ كل شيء بكل بساطة.
0x622 Tinyweb الشيطان
الإصدار الجديد من tinywebd هو البرنامج الخفي الذي تم إطلاقه في الخلفية دون وجود محطة تحكم. تتم كتابة بيانات الإخراج إلى السجل باستخدام طوابع زمنية ، وينتظر البرنامج نفسه إشارة SIGTERM لإكمال العمل بشكل صحيح.
التغييرات التي تم إجراؤها على الأصل ليست كبيرة للغاية ، لكنها تسمح بدراسة أكثر واقعية لاستغلال الضعف. أجزاء التعليمات البرمجية الجديدة بخط غامق.
tinywebd.c #include <sys/stat.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <time.h> #include <signal.h> #include "hacking.h" #include "hacking-network.h" #define PORT 80 // , #define WEBROOT "./webroot" // - #define LOGFILE "/var/log/tinywebd.log" // int logfd, sockfd; // void handle_connection(int, struct sockaddr_in *, int); int get_file_size(int); // // void timestamp(int); // // void handle_shutdown(int signal) { timestamp(logfd); write(logfd, " .\n", 16); close(logfd); close(sockfd); exit(0); } int main(void) { int new_sockfd, yes=1; struct sockaddr_in host_addr, client_addr; // socklen_t sin_size; logfd = open(LOGFILE, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR); if(logfd == -1) fatal(" "); if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) fatal(" "); if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) fatal(" SO_REUSEADDR"); printf(" tiny web.\n"); if(daemon(1, 0) == -1) // fatal(" "); signal(SIGTERM, handle_shutdown); // handle_shutdown signal(SIGINT, handle_shutdown); // handle_shutdown timestamp(logfd); write(logfd, ".\n", 15); host_addr.sin_family = AF_INET; // host_addr.sin_port = htons(PORT); // , host_addr.sin_addr.s_addr = INADDR_ANY; // IP memset(&(host_addr.sin_zero), '\0', 8); // if (bind(sockfd, (struct sockaddr *)&host_addr, sizeof(struct sockaddr)) == -1) fatal(" "); if (listen(sockfd, 20) == -1) fatal(" "); while(1) { // accept sin_size = sizeof(struct sockaddr_in); new_sockfd = accept(sockfd, (struct sockaddr *)&client_addr, &sin_size); if(new_sockfd == -1) fatal(" "); handle_connection(new_sockfd, &client_addr, logfd); } return 0; } void handle_connection(int sockfd, struct sockaddr_in *client_addr_ptr, int logfd) { unsigned char *ptr, request[500], resource[500], log_buffer[500]; int fd, length; length = recv_line(sockfd, request); sprintf(log_buffer, " %s:%d \"%s\"\t", inet_ntoa(client_addr_ptr->sin_addr), ntohs(client_addr_ptr->sin_port), request); ptr = strstr(request, " HTTP/"); // if(ptr == NULL) { // HTTP strcat(log_buffer, " HTTP!\n"); } else { *ptr = 0; // URL ptr = NULL; // ptr NULL ( // ) if(strncmp(request, "GET ", 4) == 0) // GET ptr = request+4; // ptr - URL if(strncmp(request, "HEAD ", 5) == 0) // HEAD ptr = request+5; // ptr - URL if(ptr == NULL) { // strcat(log_buffer, " !\n"); } else { // ptr, if (ptr[strlen(ptr) - 1] == '/') // , // '/', strcat(ptr, "index.html"); // 'index.html' strcpy(resource, WEBROOT); // resource // strcat(resource, ptr); // fd = open(resource, O_RDONLY, 0); // if(fd == -1) { // strcat(log_buffer, " 404 Not Found\n"); send_string(sockfd, "HTTP/1.0 404 NOT FOUND\r\n"); send_string(sockfd, "Server: Tiny webserver\r\n\r\n"); send_string(sockfd, "<html><head><title>404 Not Found</title> </head>"); send_string(sockfd, "<body><h1>URL not found</h1></body></html> \r\n"); } else { // strcat(log_buffer, " 200 OK\n"); send_string(sockfd, "HTTP/1.0 200 OK\r\n"); send_string(sockfd, "Server: Tiny webserver\r\n\r\n"); if(ptr == request + 4) { // GET if( (length = get_file_size(fd)) == -1) fatal(" "); if( (ptr = (unsigned char *) malloc(length)) == NULL) fatal(" "); read(fd, ptr, length); // send(sockfd, ptr, length, 0); // free(ptr); // } close(fd); // } // if / } // if } // if HTTP timestamp(logfd); length = strlen(log_buffer); write(logfd, log_buffer, length); // shutdown(sockfd, SHUT_RDWR); // } int get_file_size(int fd) { struct stat stat_struct; if(fstat(fd, &stat_struct) == -1) return -1; return (int) stat_struct.st_size; } void timestamp(fd) { time_t now; struct tm *time_struct; int length; char time_buffer[40]; time(&now); // time_struct = localtime((const time_t *)&now); // tm length = strftime(time_buffer, 40, "%m/%d/%Y %H:%M:%S> ", time_struct); write(fd, time_buffer, length); // }
ينشئ هذا البرنامج عملية مكررة تعمل في الخلفية ، وتكتب إلى ملف السجل مع الطوابع الزمنية ، وتنتهي بشكل صحيح بعد تلقي الإشارة المقابلة. يتم تعريف واصف ملف السجل والمقبس الذي يقبل الاتصال كمتغيرات عامة بحيث يمكن لوظيفة handle_shutdown () إكمال عملها بشكل صحيح. يتم تعريفه على أنه معالج رد الاتصال لإكمال ومقاطعة الإشارات ، مما يضمن إنهاء البرنامج بواسطة أمر الإيقاف.
هنا نتيجة تجميع البرنامج وتنفيذه واستكماله. انتبه إلى الطوابع الزمنية في السجل وإلى رسالة الإكمال التي ظهرت بعد أن تلقى البرنامج الإشارة المقابلة واستدعى وظيفة handle_shutdown ().
reader@hacking:~/booksrc $ gcc -o tinywebd tinywebd.c reader@hacking:~/booksrc $ sudo chown root ./tinywebd reader@hacking:~/booksrc $ sudo chmod u+s ./tinywebd reader@hacking:~/booksrc $ ./tinywebd tiny web. reader@hacking:~/booksrc $ ./webserver_id 127.0.0.1 The web server for 127.0.0.1 is Tiny webserver reader@hacking:~/booksrc $ ps ax | grep tinywebd 25058 ? Ss 0:00 ./tinywebd 25075 pts/3 R+ 0:00 grep tinywebd reader@hacking:~/booksrc $ kill 25058 reader@hacking:~/booksrc $ ps ax | grep tinywebd 25121 pts/3 R+ 0:00 grep tinywebd reader@hacking:~/booksrc $ cat /var/log/tinywebd.log cat: /var/log/tinywebd.log: Permission denied reader@hacking:~/booksrc $ sudo cat /var/log/tinywebd.log 07/22/2007 17:55:45> . 07/22/2007 17:57:00> 127.0.0.1:38127 "HEAD / HTTP/1.0" 200 OK 07/22/2007 17:57:21> . reader@hacking:~/booksrc $
يعالج برنامج tinywebd الجديد محتوى HTTP ، مثل الويب الصغير الأصلي ، ولكنه يتصرف مثل البرنامج الخفي للنظام لأنه لا يحتوي على طرف تحكم ، ويكون الإخراج إلى ملف سجل. كلا البرنامجين عرضة لنفس تجاوز سعة المخزن المؤقت - ولكن هذه ليست سوى بداية هذه الثغرة الأمنية. الآن بعد أن اخترنا miniwebd كهدف للهجوم ، سأوضح لك كيفية تجنب الكشف بعد دخولك جهاز آخر.
»يمكن العثور على مزيد من المعلومات حول الكتاب على
موقع الناشر على الويب»
المحتويات»
مقتطفاتخصم 20٪ على قسيمة Habrozhitel -
القرصنة