قبلا اینجایه کد نوشتم که پسورد میگیره و هش میکنه. و برای داشتن پسورد هم از یک سایت تولید پسورد استفاده کردم، اما با خودم گفتم چرا خودم یه چیزی ننویسم که پسورد تولید کنه؟! و در نتیجه…
اول میخوام یه کد رو بگم که درک نحوه کارش خیلی سادست ولی از نظر مدت زمان اجرا بهینه نیست و نمیتونه از تمام توان سی پی یو استفاده کنه و در ادامش کدی رو میخوام بگم که ممکنه یه مقدار درک نحوه کارکردش سخت تر باشه ولی درعوض خیلی سریعتره و از تمام توان سی پی یو هم میتونه استفاده کنه.
فرض کنیم که میخوام تمام پسوردهای ممکن ۴ کاراکتری که با حروف الفبای کوچیک و بزرگ انگلیسی و همینطور ارقام ۰ تا ۹ میشه نوشت رو داشته باشم. کدش خیلی سادست
for a1 in {{a..z},{A..Z},{0..9}} do for a2 in {{a..z},{A..Z},{0..9}} do for a3 in {{a..z},{A..Z},{0..9}} do for a4 in {{a..z},{A..Z},{0..9}} do echo $a1$a2$a3$a4 >> 4 done done done done
حلقههاش که چیزی عجیبی ندارن و مسلما چون میخوام پسوردهام ۴کاراکتری باشن ۴تا حلقه گذاشتم و در انتها هم محتوای ۴تا حلقه در کنار هم در فایلی به اسم ۴ ذخیره میشن. و فایل ۴، ۱۴۷۷۶۳۳۶ خط خواهد داشت که هر خط یکی از حالات ممکن خواهد بود. خب کد رو تو فایلی به اسم passgen-old.sh ذخیره میکنم و
قبل از اجرای کد همونطور که توی عکس مشخص هست از دستور time استفاده کردم تا مدت زمان اجرای کد رو هم داشته باشم! و همونطور که میبینین مدت زمان اجرای این کد ۶ دقیقه و ۵۵ ثانیه هست که عدد بزرگیه. اگه دارین با خودتون میگین ۶-۷ دقیق زمان زیادی نیست به این فکر کنین که اگر پسوردهای ۵کاراکتری رو میخواستیم این زمان حداقل ۶۲ برابر میشد و خیلی وحشتناک میشد! حالا این رو بذارید کنار اینکه الآن اکثرا از پسوردهایی استفاده میکنن که ۸ کاراکتر و یا بیشتر هست. حجم فایلی خروجی هم که اسمش ۴ هستش مشخصه و ۷۱ مگ هست. چون این اسکرین شات رو قبلا گرفتم یادم رفته تعداد خطوط داخل فایل ۴ رو بگیرم، ولی بهم اطمینان کنید، تعداد خطوطش ۱۴۷۷۶۳۳۶ هست 😀 اوه! یه نگاهی هم به وضعیت سی پی یو در طول اجرای برنامه داشته باشیم با این توضیح که سی پی یو من دو هسته داره و با برنامه htop مصرف منابع رو داشتم مانیتور میکردم:
مشخصه که کد داره فقط از یک هسته سی پی یو استفاده میکنه. پس بریم سراغ کد دوم که خیلی باحالتره 😛
for a1 in {{a..z},{A..Z},{0..9}} do echo "$a1" >> 1 done source=1 dest=2 for a in {2..4} do for a1 in {{a..z},{A..Z},{0..9}} do sed "s/$/$a1/" $source >> $dest & done wait source=$((source+1)) dest=$((dest+1)) done
حلقه اول که مشخصه چیکار میکنه، میاد تمام حروف کوچیک و بزرگ انگلیسی به همرا ارقام ۰ تا ۹ تو یه فایل به اسم ۱ قرار میده. خوشبختانه این دستور زیر یک ثانیه اجراش کار داره پس ما هم دیگه کاریش نداریم. اما بریم سراغ بقیه کد که اصل کار هست و باعث میشه سرعت اجرای کد حسابی بره بالا! ما الآن در واقع لیستی از پسوردهای یک کاراکتری داریم، برای اینکه پسوردهای دو کاراکتری رو بسازیم میایم و در آخر تمام خطوط فایل پسوردهای یک کاراکتریمون کاراکتر a رو قرار میدیم و در فایل دو میریزیم. در مرحله بعد میایم و کاراکتر b رو آخر تمام خطوط فایل پسوردهای یک کاراکتریمون قرار میدیم و در فایل دو میریزیم و الی آخر. همین کار باعث میشه که کلی سرعت کارمون بره بالا. اما چیکار میتونیم بکنیم که کد از تمام توان سی پی یو استفاده بکنه؟قبلش بگم که دو متغییر source و dest مسئول این هستند که کنترل کنند پسوردها از چه فایلی باید خونده بشن و بعد اضافه شدن کاراکتر جدید بهشون داخل چه فایلی قرار بگیرن. خطی هم که اولش با sed شروع شده مسئول اضافه کردن کاراکترا هست. اگر دقت کنین میبینین که در آخر خطش یک علامت & قرار داره و این کاراکتر میگه وای نستا تا کد قبل من به اتمام برسه و بری سراغ ادامه کد، میگه همینجوری که داری اینو اجرا میکنی برو سراغ بقیه کد و بقیه کد هم که یعنی برو ادامه کار حلقه رو انجام بده. و چون قراره که حلقه ۶۲ بار اجرا بشه و عملیات های این ۶۲ حلقه به صورت موازی انجام بشن پس نتیجه این میشه که کد ما از سی پی یویی با ۶۲ هسته پشتیبانی میتونه بکنه و حداکثر سرعت ممکن رو میتونه بگیره. و نکتهای که فراموشش کردم درباره خط for a in {2..4} هست. چون ما پسوردهای ۲ تا ۴ کاراکتری رو باید بسازی این حلق رو گذاشتم. اون ۲ که همیشه ثابت هست ولی ۴ رو میشه کم یا زیاد کرد. مثلا میشه بجای ۴ عدد ۱۰ رو گذاشت تا تمام پسوردهای ممکن ۱ کاراکتری تا ۱۰ کاراکتری رو داشته باشیم.
یه نگاهی هم داشته باشیم به دستو wait.تو حلقهای که داریم بعد از آخرین باری که حلقه اجرا میشه میرسه به & و این یعنی کار رو ادامه بده در حالی که نباید ادامه بده! چون هنوز کلی حلقه در حال اجرا داریم! اینجاست که دستور wait کد رو نگه میداره تا تمام حلقههای قبلش اجراشون به پایان برسه و بعد بره سراغ ادامه کار. اما ببینیم نتیجه اجرای این کد روی سیستم من چی میشه! فعلا حس اسکرین شات گرفتن ندارم پس به نوشتههام اعتماد کنید… نتیجه کار میشه چهارتا فایل با اسمهای 1 و 2 و 3 و 4 که داخلشنون پسوردهایی که میشه ساخت و ۱ یا ۲ یا ۳ یا ۴ کاراکتر داشته باشن قرار داره. قسمت جالب قضیه هم اینه که اجرای کد تنها ۵ثانیه زمان برد! حالا خودتون ۷دقیقه رو با ۵ ثانیه مقایسه کنید و اینکه کد از هر دو هسته سی پی یوم استفاده کرد و لود جفتشون رو برد روی ۱۰۰درصد و اگر تعداد هستههای سی پی یوم بیشتر میبود حتما کد سریعتر اجرا میشد.
تا اینجای کد رو داشته باشین چون قراره یه مقایسه خیلی حساس انجام بدیم! برای تولید پسورد برنامهای وجود داره به اسم crunch. من کار با این برنامه رو از اینجا یاد گرفتم. و برای نصبش چون تو مخازن موجود نبود مجبور شدم از سورس نصبش کنم که خوشبختانه کار آسونی بود:
wget http://netix.dl.sourceforge.net/project/crunch-wordlist/crunch-wordlist/crunch-3.6.tgz tar -xvf crunch-3.6.tgz cd crunch-3.6 make
بعد از نصبش هم اینجوری اجراش کردم:
time ./crunch 1 4 -f "/home/hamed/Desktop/crunch-3.6/charset.lst" mixalpha-numeric -o "/home/hamed/Desktop/crunch-3.6/pass.txt"
اجرای برنامه ۱۳ ثانیه طول کشید که تقریبا دوبرابر کد من هست و برنامه نتونست از تمام توان سی پی یو استفاده کنه. پس همینجا با افتخار اعلام میکنم که کد من از نظر زمانی بهینه تر هست 🙂 و حالا که از نظر زمانی بهینه تر هست پس بیایم کد رو کاملتر کنیم تا تمام کاراکترهایی که crunch باهاشون قادر به تولید پسورد هست رو کد ما هم بتونه باهاشون پسورد درست کنه. crunch با این کاراکترها (بعلاوه فاصله) میتونه پسورد درست کنه
0123456789
ABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyz
!@#$%^&*()-_+=~`[]{}|\:;”‘<>,.?/
پس کد رو تغییر میدم تا با تمام این کاراکترها بتونه پسورد درست کنه.
IFS=$'\n' for a1 in {{a..z},{A..Z},{0..9},' ','!','@','#','$','%','^','*','(',')','-','_','+','=','~','`','[',']','{','}','|',':',';','"',"'",'<','>',',','.','?','&','/','\'} do echo "$a1" >> 1 done source=1 dest=2 for a in {2..4} do for a1 in {{a..z},{A..Z},{0..9},' ','!','@','#','$','%','^','*','(',')','-','_','+','=','~','`','[',']','{','}','|',':',';','"',"'",'<','>',',','.','?'} do sed "s/$/$a1/" $source >> $dest & done awk '{print $0"&"}' $source >> $dest & awk '{print $0"/"}' $source >> $dest & awk '{print $0"\\"}' $source >> $dest source=$((source+1)) dest=$((dest+1)) done
یه چندتا توضیح کوچیک هم درباره این کد بدم. IFS رو تغییر دادم تا کد بتونه فاصله رو هم به عنوان یک کاراکتر بشناسه و در ساخت پسورد ازش استفاده کنه. اگه خواستین کاراکتر دیگهای رو هم جهت ساخت پسورد اضافه کنین (من خودم چندتا حرف فارسی رو تست کردم و جواب داد) میتونین توی هر دوتا حلقه کاراکتر مورد نظرتون رو در حالی که بین ‘ ‘ قرار داره قرارش بدین. درباره دستور awk هم که استفاده کردم بگم که sed با سه تا کاراکتر / و \ و & مشکل داشت، به همین خاطر مجبور شدم به صورت جدا گانه این سه تا کاراکتر رو با awk اضافه کنم. و چون \ یک کاراکتر با مفهوم خاص هست مجبور شدم در awk سوم دوتا \ کنار هم بذارم تا بفهمه که فقط باید یک \ در آخر خطوط اضافه کنه.
توضیحات کد تا اینجا تموم شدست، ولی چون سیستم من اصلا برای اجرای این کد جالب نیست تصمیم گرفتم یه سرور مجازی توپ بگیرم تا کد رو روش اجرا کنم. سروری که گرفتم این مشخصات رو داره:
24 CPU
65536MB MEMORY
700 GB STORAGE (SSD)
چه شود! خب کد رو تنظیم میکنم تا فقط پسوردهایی که شامل حروف کوچیک و بزرگ انگلیسی و اعداد هستن رو تولید کنه. و طول پسوردها هم تا ۶ کاراکتر باشه. نتیجه رو ببینیم:
همونجور که مشخصه اسکریپت ۴۸ دقیقه زمان برد و فایل پسوردهای ۶کاراکتریمون ۳۷۱ گیگ شد! آخر کار هم برای دونستن خودم سرعت هارد سرور رو اندازه گرفتم. یه نگاهی هم به وضعیت سی پی یو بندازیم:
هستههای سی پی یو که وضعیتشون مشخصه، رم هم در آخر کار یکم رفت بالا و به حدودای ۲/۲گیگ رسید. اگر هم دقت کنید میبینین که تو ستون command کاملا مشخصه که دستورات دارن همزمان انجام میشن و اینکه چه دستوری در حال اجراست.
وای خدا چقدر زیاد نوشتم… :)))
سلام
اگه میشه شماره یا ایدی تلگرامتون رو به ایمیلم بفرستید تعدادی سوال ازتون دارم