بسم الله الرحمن الرحيم,

 

منذ فترة, تحدثنا عن برامج الـ CrackMe, و بعدها قمنا بتحليل احد تلك البرامج و كتابة برنامج KeyGen يقوم بتوليد Valid Serial Numbers للبرنامج.
اليوم بأذن الله سنقوم بتحليل برنامج آخر من برامج الـ CrackMes و تحليل خوارزميته و فهمها.

كما تعودنا, برنامج اليوم سيكون متاح للتحميل في قسم المراجع آخر المقال.

تستطيع الضغط على الصورة لمشاهدتها بدقة عالية.

في البداية, سنقوم بتحميل برنامج الـ CrackMe داخل برنامج Immunity Debugger و نقوم بالبحث عن الـ Strings للبحث عن الكلمات و الجمل التي يمكن ان تساعدنا في معرفة مكان وجود (أو مكان قريب من) خوارزمية مقارنة الـ strings المُدخلة و المُولدة من خوارزمية توليد الـ Valid Serial Number.

snap1

 

نستطيع أن نرى الجملتين ( Right Pass ) و ( Wrong Pass ) و هذا مؤشر جيد, لأن جملة ( Wrong Pass ) هى التي تظهر في حال ادخال مدخل غير صحيح لبرنامج الـ CrackMe.
الآن لنضغط ضغطة مزدوجة على كلمة ( Wrong Pass ) لنتوجه للـ Text Section أو الجزء الذي يحتوي كود البرنامج الذي ذُكرت فيه تلك العبارة. و سنرى أننا قد توقفنا عند العنوان 0x004014C1, و من هنا نستطيع أن نرى دوال مشهورة (أو على الأقل أسمائها معلومة و ذات دلالة واضحة بالنسبة للرائي) و هى دلالة gets المسؤولة عن تخزين المدخلات من المستخدم للذاكرة و دالة strcmp المسؤولة عن مقارنة نصوص strings.

 

snap2

دعونا الآن نقوم بتحليل سطر النداء على الدالة strcmp من خلال وضع نقطة توقف BreakPoint عندها و اعادة تشغيل البرنامج من خلال Ctrl+F2 و ادخال أي نص كمدخل Input لبرنامج الـ CrackMe لنلاحظ توقف البرنامج بعد الضغط على Enter, و هنا توقف البرنامج عند مقارنة النص الذي ادخلناه بالنص الذي تم توليده من خوارزمية البرنامج نفسه الذي نسعى لتحليلها. أيضا اذا نظرنا لذاكرة المكدس, نستطيع رؤية مدخلات الدالة strcmp parameters و هما النص الذي ادخلناه و النص المُولد من برنامج الـ CrackMe.

snap3

 

الآن نريد تتبع هذا النص المُولد من خوارزمية البرنامج. نستطيع أن نلاحظ من خلال النظر لكود البرنامج في الـ Text Section أن النص يُحفظ في الذاكرة في العنوان 0x0022FE11 و يتم الإشارة عليه من خلال الـ EBP Register و المؤشر هنا قيمته EBP-137.
أيضا نلاحظ أن البرنامج يقوم بتوليد الرقم الصحيح Valid Serial Number قبل أخذ مدخلات المستخدم, و هذا يعني أنه لا يستخدم أي من مدخلات المستخدم في حساب الرقم الصحيح Valid Serial Number. نستطيع التأكد من ذلك من خلال وضع نقطة توقف عند بداية الدالة التي نحن بداخلها الآن أو أي نقطة قبل سطر النداء على الدالة gets, سأقوم بوضع نقطة توقف عند عنوان 0x00401340, بعد ذلك نعيد تشغيل البرنامج, سنجد أن البرنامج قد توقف و قام بتوليد الرقم الصحيح و حفظه في الذاكرة من قبل أن ندخل أي نص في البرنامج, و هذا يؤكد وجهة نظرنا أن الرقم الصحيح المُولد يتم حسابه بدون مدخلات المستخدم. و هذا يتركنا لتفسير منطقي آخر و هو أن البرنامج يستخدم مصادر الجهاز لتوليد الرقم الصحيح مثل الوقت, التاريخ, الـ CPU Speed, سعة الذاكرة RAM, الخ…

دعونا نتأكد من كل تلك التخمينات من خلال التقدم بتعليمة مفردة كل مرة Single Stepping من خلال الضغط على F7 بدأً من عنوان 0x00401340.

snap4
أول ملاحظة شيقة هى عند العنوان 0x0040135A و هي نداء للدالة time و هي دالة من الدوال المكتبية الخاصة بنظام تشغيل Windows API و التي تقوم بحساب عدد الثواني من أول منتصف ليل الأول من يناير عام 1970, و هذا يسمى System Time. سأقوم بوضع رابط للدالة time من على الموقع الرسمي لشركة Microsoft في قسم المراجع.

هنا, يقوم البرنامج بادخال القيمة NULL للدالة time كـ Parameter و يقوم بحفظ القيمة المرتجعة من الدالة في العنوان 0x0022FF34, بعد ذلك نلاحظ نداء لدالة أخرى من الدوال المكتبية لويندوز و هى الدالة localtime و التي تقوم بأخذ القيمة المرتجعة من الدالة time و تعديلها لحساب الوقت المحلي للجهاز.
الدالة localtime تأخد مدخل واحد و هو مؤشر للقيمة المرتجعة من الدالة time و نستطيع أن نلاحظ هنا أنه تم تمرير مؤشر لتلك القيمة للدالة, و ارجاع الوقت المحلي الذي تم حسابه الى البرنامج من خلال هيكلية تسمى struct tm.

على الموقع الرسمي للدوال المكتبية Windows API, نلاحظ أن تلك الهيكلية مكونة من تسعة متغيرات, و هن:

الثواني, الدقائق, الساعات, اليوم (قيمة من 1-31), الشهر (قيمة من 0-11), السنة, اليوم (قيمة من 0-6, و هو اليوم بالنسبة للاسبوع), اليوم (قيمة من 0-365, و هو اليوم بالنسبة للسنة), و طريقة التوقيت (24 ساعة أم 12 ساعة).

بعد الرجوع من الدالة localtime, يقوم البرنامج بنقل الهيكلية المرتجعة من الدالة الى العنوان 0x0022FF10.

بعد ذلك, نرى تعليمة لا تقوم سوى بالدوران 190 مرة بدون القيام بأي شئ مفيد, و هذه الطريقة تسمى Busy Loop و هى تساوي نسبيا استخدام الدالة Sleep لتوقف البرنامج بعض الوقت.

عند العنوان 0x004013CF, نلاحظ دوارة أخرى تقوم بالدوران ثلاث مرات, في كل مرة يتم النداء على الدالة GetCursorPos. تلك الدالة تقوم بأخذ مدخل واحد و هو مؤشر لهيكلية أخرى تسمى POINT structure. حسب الموقع الرسمي للدوال المكتبية Windows API, تلك الهيكلية تحتوي على متغيرين فقط, و هما مكان المؤشر Mouse cursor pointer على الشاشة أفقيا و رأسيا x_position and y_position.
في البرنامج, تلك الهيكلية بدايتها في العنوان 0x0022FD08, في كل دورة من الثلاث دورات, يقوم البرنامج بضرب قيمة الـ x_position بقيمة الـ y_position و جمع الناتج على قيمة في العنوان 0x0022FF38 و هذا العنوان يحتوي منذ بداية البرنامج على القيمة 0x0022FFF0 (هذا ليس عنوان و لكنها قيمة رقمية بالنظام السادس عشري).

بعد ذلك يقوم البرنامج بضرب الناتج في سادس متغير من متغيرات الهيكلية time structure و هي رقم الشهر, ثم يقوم بطرح ثالث متغير في الهيكلية -و هي الساعة- منها, ثم يقوم البرنامج بالنداء على الدالة itoa و هي دالة تأخذ ثلاثة مدخلات:
– رقم
– مؤشر لنص  pointer to a string

– النظام الرقمي (نظام العشري, السادس عشري, الثماني, الخ…)

تقوم الدالة itoa بتحويل الرقم المُدخل بالنظام الرقمي المُدخل لنص, و تقوم بحفظه في المؤشر المُدخل.

أخيرا يقوم البرنامج بعد تحويل الرقم الذي قمنا بحسابه مؤخرا في العنوان 0x0022FF38 لنص, بوضع النص “H!J” في نهاية النص الرقمي المُحول, و بهذا يكون النص الذي يقوم البرنامج بتوليده جاهز لمقارنته بمدخلات المستخدم.

هذا كود pseudocode بلغة شبيهة للغة الـ C و يوضح آلية توليد الرقم الصحيح Valid Serial Number:
PseudoCode

في تلك الحالة, سيكون كتابة برنامج Keygen بالطريقة التقليدية غير فعال, لأن البرنامج يقوم بحساب الرقم الصحيح Valid Serial Number باستخدام الوقت و مكان المؤشر في أثناء تشغيلنا لبرنامج الـ CrackMe, و قطعا ستكون القيمة مختلفة عما اذا قمنا نحن بكتابة برنامج keygen لأن ساعتها – و حتى ان استطعنا أن نجمد مكان مؤشر الفأرة لكي لا تتغير قيم الاحداثيات الرأسية و الأفقية له- لن نتمكن من تجميد الوقت, مما سؤدي لتغيير قيمة الوقت المستخدم في الحسابات, و هكذا سيكون الرقم الناتج من برنامج الـ keygen مختلف عن الرقم الناتج من برنامج الـ CrackMe نفسه!!

احدى الطرق للقيام بهذا هو أن نستخدم طريقة الـ Code Caving و هي طريقة للتعديل على البرنامج التنفيذي بحيث يراقب مكان الرقم المُولد, و بعد ذلك يقوم بطباعته لنا بعد حسابه و قبل اخذ مدخلات المستخدم. قطعا الطرق كثيرة و كل شخص و ابداعه.

 

المراجع: