مقال : تطبيق على الهندسة العكسية كتابة برنامج Keygen
بسم الله الرحمن الرحيم,
في المقال السابق, تعرفنا على كيفية كتابة برنامج Patch يقوم بترقيع بعض التعليمات الموجودة في ملف تنفيذي ما, و قبل ذلك تعرفنا أيضا أن في الهندسة العكسية نقوم اما بالتعديل على الملف التنفيذي المراد دراسته, أو نقوم بكسر حمايته من خلال فهم طريقة عمل الحماية بداخله, و من ثم كتابة برنامج يحاكي تلك العملية, لنستطيع بذلك كسر حماية البرنامج بدون المساس بالملف التنفيذي.
في هذا المقال سنبدأ بأذن الله تعالى العمل على برامج الـ Crackmes, كما سنتطرق لكتابة أول برنامج Keygen و من هذا المقال فصاعدا, سأقوم بأذن الله بتنزيل حلول بعض برامج الـ Crackmes, وذلك لأن تلك الطريقة تعتبر الأفضل للتدرب على الهندسة العكسية, كما أن كتابة برامج الـ Keygen -و كما سنرى في هذا المقال- يعتبر أصعب بكثير مقارنة بكتابة برامج الـ Patch و ذلك لأننا لابد أن نفهم خوارزمية البرنامج المراد كسر حمايته بشكل دقيق جدا و أي سوء فهم لأي عدد من التعليمات -حتى و لو كانت تعليمة واحدة- من تعليمات البرنامج, لن نتمكن من كسر حمايته.
سأضع رابط تنزيل برنامج الـ Crackme الذي سنقوم بالعمل عليه في جزء المراجع بالأسفل, كما يستحسن التسجيل في موقع الـ Crackmes الذي قمت بوضعه في مقال سابق (سأعيد نشره في جزء المراجع في هذا المقال) لأن الموقع لا يسمح بتنزيل البرامج بدون تسجيل.
نبدأ أولا بفتح البرنامج لنرى تلك النافذة البسيطة.
لماذا نقوم بفتح البرنامج؟ كما قلنا في مقالات سابقة, من المهم جدا أن نقوم بدراسة مدخلات و مخرجات البرنامج, هذا يعطينا لمحة عن آلية عمل الواجهة الرسومية (هذا قد يساعدنا في أحيان كثيرة بتخمين كيفية عمل البرنامج و أي المكتبات البرمجية قام باستخدامها), كما يساعدنا هذا في تخمين بعض المعالجات التي تحدث على المدخلات بداخل البرنامج.
كما نرى البرنامج في منتهى البساطة, كل ما يطلبه هو اسم المستخدم و رقم تسلسلي, و تلك هي غالبية برامج الـ Crackmes فكل ما تقوم به هو انتظار مدخلات من المستخدم, و معالجتها ثم اخراج رسالة معينة تخبرنا اذا ما كانت المدخلات صحيحة أم لا.
هنا سأقوم بادخال أي اسم مستخدم و أي رقم تسلسلي لأعرف كيف يعمل البرنامج و ماذا سيحدث حينها. بعد عدد من المحاولات و من خلال الرسائل التي تظهر لنا بعد كل محاولة, نجد أن اسم المستخدم يجب أن يكون أكثر من اربع حروف أو أرقام, كما أن من الواجهة استطيع أن أخمن أن تلك الرسائل تظهر من خلال النداء على الدالة MessageBox و هي دالة تابعة للمكتبة البرمجية Win32 API, كما يأخذ البرنامج المدخلات من خلال الدالة GetDlgItemtext. كل هذا تخمينات فقط, قد تصيب و قد تخطئ, فلنبدأ بتطبيق الهندسة العكسية على البرنامج للتأكد من ظنوننا.
لنقم بفتح البرنامج من خلال Immunity Debugger, و لنبدأ بالتأكد من التخمينات التي قمنا بها في الخطوة السابقة من خلال معرفة الدالات التي يقوم البرنامج بالنداء عليها.
كما توقعنا, البرنامج يستخدم المكتبات البرمجية Win32 API و يستخدم فعليا الدالات التي تم ذكرها, كما يمكن أن نلاحظ أنه تم استخدام الدالة GetDlgItemtext مرتين و هو أمر منطقي لأن الواجهة الرسومية تحوي مستطيلين فقط يستقبلان مدخلات المستخدم.
الآن نضغط مرتين على أي من الدالتين, لنذهب للمكان الذي نوديا منه.
الآن لنبدأ بتحليل تلك التعليمات, أولا من معرفتنا بالدالة GetDlgItemtext, أحد مدخلاتها Parameters هي العنوان الذي ستحفظ فيه مدخلات المستخدم في الذاكرة و يسمى هذا Buffer. نستطيع أن نرى هنا أن تلك الدالة تأخذ ما يدخله المستخدم في المستطيل الأول الخاص باسم المستخدم, و تقوم بحفظه بالعنوان 0x00406349, و من معرفتنا أيضا أن دالات Win32 API بعد انتهاء أي دالة, تحفظ القيمة الراجعة Return Value في المسجل AX Register, اذا نظرنا لتعريف الدالة GetDlgItemtext في المرجع المكتبي لشركة مايكروسفت MSDN نعرف أن تلك الدالة تقوم بارجاع عدد الحروف التي تم ادخالها. نجد بعد استدعاء و الرجوع من تلك الدالة أن البرنامج يقوم بمقارنة عدد الحروف المدخلة من المستخدم بالقيمة 0x3, اذا كان عدد الحروف أكبر, يذهب الى العنوان 0x401163 ليكمل بشكل طبيعي, و ان كان عدد الحروف أقل من أو يساوي 0x3, استدعى الدالة MessageBox لتقوم باظهار رسالة محتواها نستطيع رؤيته من خلال مدخلات الدالة Function Paramters ثم يخرج من الدالة الحالية التي هو فيها و ينتظر مدخلات المستخدم من جديد. لنفترض أننا أدخلنا اسم المستخدم الآتي:
hello
هذا الاسم مكون من أكثر من ثلاث حروف, اذا سنذهب للعنوان 0x401163 لنكمل تنفيذ التعليمات هناك. يقوم البرنامج بعد ذلك بتحميل اسم المستخدم بدون أول حرف بداخل المسجل EDX و تحميل القيمة 0x5 بداخل المسجل ECX ثم يقوم بتصفير المسجلات EAX و ESI, هنا يجب أن نسأل سؤال منطقي, لماذا استخدم التعليمة XOR بدلا من أن يضع صفر داخل المسجلات من خلال التعليمة MOV ؟ هذه خصيصة من خصائص المترجمات Compiler Optimization, فقد قام المترجم أثناء ترجمة البرنامج بمقارنة التعليمتين Xor و Mov و رأى أنه باستخدام التعليمة Mov ستشغل تلك التعليمة مساحة 5 bytes في حين أن استخدام التعليمة Xor لنفس الهدف سيشغل فقط 2 bytes و هذا طبعا أفضل من حيث تخفيف مساحة الملف التنفيذي. بعد ذلك يبدأ البرنامج بالقيام بخوارزمية معينة على اسم المستخدم داخل دوراة loop.
ماذا تفعل تلك الخوارزمية؟ لنبدأ بتحليلها.
أول تعليمة تقوم بنسخ حرف ‘e’ و هو ثاني حرف في اسم المستخدم الذي أدخلناه (hello) داخل البايت الأول للمسجل ECX, كيف هذا؟ لأننا قمنا بتصفير مسجل ESI ثم جمعنا قيمته على المسجل EDX الذي يحوي عنوان اسم المستخدم بدئا من ثاني حرف. ثم يقوم بعمل XOR هذا الحرف مع byte موجود بالعنوان 0x406328, لنرى ماذا بداخل هذا العنوان في الذاكرة.
هذا العنوان يحوي قيم لا نعرفها, فنتركها جانبا و لنكمل لنعرف ماذا تفعل الـ loop في اسم المستخدم. تقوم الـ loop بعد ذلك باستبدال الحرف الناتج من عملية الـ XOR و وضعه مكان ثاني حرف في اسم المستخدم ثم تقوم بوضع ثاني حرف الأصلي ‘e’ مكان أول byte في العنوان 0x406328, تلك عملية swap, تقوم بتغيير قيمة حرف في اسم المستخدم من خلال XOR ثم تحفظ الناتج مكان الحرف و تضع الحرف المستبدل مكان العنوان الذي أشرنا اليه.
تستمر تلك الـ loop حتى نهاية اسم المستخدم زائد واحد بمعنى أنها تكمل تطبيق الخوارزمية حتى على الـ Null Byte 0x00, معدلة بذلك و مستبدلة كل حرف بناتج عملية الـ XOR. أيضا نلاحظ أنها تقوم باستخدام أول 5 bytes فقط من العنوان 0x406328 مهما كان طول اسم المستخدم, فاذا كان اسم المستخدم أطول من 5 bytes و بعد الانتهاء من أول 5 bytes فيه, تكمل الخوارزمية على باقي الحروف و لكن ترجع لتعمل و تستبدل فقط قيم أول 5 bytes في العنوان المجهول.
بعد تلك الـ loop, نجد loop ثانية.
هذه المرة, تقوم تلك الـ loop بعمل نفس الشئ, تدور على اسم المستخدم و لكن هذه المرة من آخر حرف زائد واحد لثاني حرف, و تستخدم و تستبدل ثاني 5 bytes في العنوان, لتبدأ من 0x40632D. فتبدأ و تأخذ آخر حرف زائد واحد (الذي كان في البداية Null byte) و تقوم بعمل XOR مع أول byte لثاني 5 bytes في العنوان المجهول و هكذا.
بعد تلك الـ loop, نجد loop ثالثة.
تعمل هذه الدوارة بنفس منطقية أول دوارة, فتقوم باستبدال و استخدام اسم المستخدم من ثاني حرف لآخر حرف زائد واحد, و تقوم بعمليات XOR مع ثالث 5 bytes في العنوان المجهول, لتبدأ من 0x406332.
بعد تلك الـ loop, نجد loop رابعة.
تعمل تلك الدوارة بنفس منطقية ثاني دوارة, فتقوم باستبدال و استخدام اسم المستخدم من آخر حرف زائد واحد حتى ثاني حرف, و تقوم بعمليات XOR مع رابع 5 bytes في العنوان المجهول, لتبدأ من 0x406337.
بعد تلك الـ loop, نجد loop خامسة.
تلك الدوارة تقوم بخوارزمية مختلفة عن سابقاتها, فتلك تقوم بجمع كل حرف بداية من الحرف السادس على أول خمس حروف بالترتيب, فتقوم بجمع سادس حرف على ثاني حرف (نعم مازلنا نبدأ من ثاني حرف و ليس الأول كما رأينا في الخوارزميات السابقة) و حفظ الناتج في العنوان 0x406345 و هو عنوان يحتوي 4 bytes فارغين (قيمتهم 0x00), ثم تقوم بجمع الحرف السابع على الحرف الثالث و تحفظ الناتج في نفس العنوان زائد واحد, ثم تقوم بجمع الحرف الثامن على الحرف الرابع و تحفظ الناتج في نفس العنوان زائد اثنان و هكذا, و اذا وصلنا في الجمع لخامس حرف تبدأ لتجمع على ثاني حرف من جديد ثم ثالث حرف و هكذا.
بعد تلك الـ loop, نجد loop سادسة.
بعد أن أنهينا آخر دوارة, نجد أننا معنا الآن ناتج مكون من 4 bytes يبدأ من العنوان 0x406345, نقوم بعملية قسمة لتلك القيمة على القيمة 0x0A, بعد ذلك نأخذ باقي القسمة (و ليس الناتج) و نجمع عليه القيمة 0x30 و نحفظ الرقم الناتج من عملية الجمع في العنوان 0x406549, ثم نأخد ناتج عملية القسمة, ونكرر القسمة على القيمة 0x0A و هكذا حتى نحصل على ناتج قسمة يساوي صفر.
بعد تلك الـ loop, نجد loop أخيرة.
كل ما تقوم به تلك الدوارة هي عكس الـ string الذي نتج عن آخر دوارة loop, فمثلا لو كان ناتج الدوارة السابقة “1234”, تقوم تلك الدوارة بجعله “4321”.
أخيرا يقوم البرنامج بمقارنة الـ string الناتج بالرقم التسلسلي الذي أدخله المستخدم, فأن كان نفسه, قام باستدعاء الدالة MessageBox تخبر المستخدم أنه تمكن من كسر حماية البرنامج.
الآن, بعد فهم خوارزمية الحماية بشكل دقيق, نستطيع أن نقول أن العنوان المجهول 0x406328 الذي رأيناه في أول دوارة loop يحوي قيم ثابتة يقوم البرنامج باستخدامها في عمليات الـ XOR, و تلك القيم في علم التشفير تسمى Keys و هي القيم التي تستخدم لتغيير قيمة حرف أو رقم ما.
كل ما علينا فعله الآن هو كتابة برنامج Keygen, من خلال ترجمة ما فهمناه الى كود بلغتنا البرمجية المفضلة. لقد قمت بكتابة برنامج بسيط بلغة الـ C, و وضعته في جزء المراجع.
لنقوم بتجربة برنامج الـ Keygen الذي كتبناه مع برنامج Crackme, يجب أن نأخذ رقم تسلسلي من برنامج الـ Keygen و نضعها في برنامج الـ Crackme, اذا كان كل ما قمنا به صحيح, يجب أن نرى رسالة تفيد بقبول الرقم التسلسلي و أنه رقم صحيح, لنبدأ بأختيار اسم مستخدم “hello” و نأخذ الرقم التسلسلي الناتج منه.
ثم نقوم بوضع الرقم في برنامج الـ Crackme و نرى تلك النتيجة المبهجة بفضل الله تعالى.
المراجع:
– برنامج LaFarge Crackme.
– موقع Crackmes.
– برنامج Keygen.
شرح جميل 🙁
انتم رجال وغيركم ذكور فقط
عشتم وعاشت أياديكم