استخدام جافا سكريبت لكسر رموز التحقق
في الآونة الأخيرة، ظهر على الإنترنت برنامج نصي جافا سكريبت يمكنه كسر رموز التحقق - GreaseMonkey! يمكن لهذا البرنامج النصي الذي طوره "Shaun Friedle" حل اختبار CAPTCHA الخاص بموقع Megaupload بسهولة. إذا كنت لا تصدق ذلك، يمكنك تجربته بنفسك على http://herecomethelizards.co.uk/mu_captcha/ !
الآن، تم التغلب على اختبار CAPTCHA المقدم من موقع Megaupload بواسطة الكود أعلاه، لكي نكون صادقين تم تصميم الكود هنا لا، ليس جيدًا جدًا. ولكن الأمر الأكثر إثارة للاهتمام هو:
1. يمكن استخدام واجهة تطبيق Canvas getImageData بتنسيق HTML 5 للحصول على بيانات البكسل من صورة رمز التحقق. باستخدام Canvas، لا يمكننا فقط تضمين صورة في لوحة قماشية، ولكن أيضًا إعادة استخراجها منها لاحقًا.
2. يحتوي البرنامج النصي أعلاه على شبكة عصبية تم تنفيذها بالكامل في JavaScript.
3. بعد استخدام Canvas لاستخراج بيانات البكسل من الصورة، أرسلها إلى الشبكة العصبية، واستخدم تقنية التعرف الضوئي على الأحرف البسيطة لاستنتاج الأحرف المستخدمة في رمز التحقق.
من خلال قراءة كود المصدر، لا يمكننا فهم كيفية عمله بشكل أفضل فحسب، بل يمكننا أيضًا فهم كيفية تنفيذ رمز التحقق هذا. كما رأيت سابقًا، فإن اختبارات CAPTCHA المستخدمة هنا ليست معقدة جدًا - يتكون كل اختبار CAPTCHA من ثلاثة أحرف، ويستخدم كل حرف لونًا مختلفًا، ويستخدم فقط أحرفًا من الأبجدية المكونة من 26 حرفًا، بينما تستخدم جميع الأحرف نفس الخط.
الغرض من الخطوة الأولى واضح، وهو نسخ رمز التحقق إلى اللوحة القماشية وتحويله إلى صورة بتدرج رمادي.
وظيفة تحويل_رمادي (image_data) {
لـ (var x = 0; x < image_data.width; x++){
لـ (var y = 0; y < image_data.height; y++){
var i = x*4+y*4*image_data.width;
فار لوما = Math.floor(image_data.data[i] * 299/1000 +
image_data.data[i+1] * 587/1000 +
image_data.data[i+2] * 114/1000);
image_data.data[i] = luma;
image_data.data[i+1] = luma;
image_data.data[i+2] = luma;
image_data.data[i+3] = 255;
}
}
}
بعد ذلك، قم بتقسيم اللوحة إلى ثلاث مصفوفات بكسل منفصلة، تحتوي كل منها على حرف واحد. من السهل جدًا تحقيق هذه الخطوة لأن كل شخصية تستخدم لونًا منفصلاً، بحيث يمكن تمييزها حسب اللون.
مرشح (image_data[0]، 105)؛
مرشح(image_data[1], 120);
مرشح(image_data[2], 135);
مرشح الوظيفة (بيانات_الصورة، اللون) {
لـ (var x = 0; x < image_data.width; x++){
لـ (var y = 0; y < image_data.height; y++){
var i = x*4+y*4*image_data.width;
// تحويل كافة وحدات البكسل ذات اللون المعين إلى اللون الأبيض
إذا (image_data.data[i] == اللون) {
image_data.data[i] = 255؛
image_data.data[i+1] = 255;
image_data.data[i+2] = 255;
// كل شيء آخر إلى اللون الأسود
} آخر {
image_data.data[i] = 0;
image_data.data[i+1] = 0;
image_data.data[i+2] = 0;
}
}
}
}
وأخيرًا، يتم التخلص من جميع وحدات البكسل المتداخلة غير ذات الصلة. للقيام بذلك، يمكنك أولاً العثور على وحدات البكسل البيضاء (المتطابقة) المحاطة بوحدات بكسل سوداء (غير متطابقة) في الأمام أو الخلف، ثم حذف وحدات البكسل المطابقة.
var i = x*4+y*4*image_data.width;
فار أعلاه = x*4+(y-1)*4*image_data.width;
فار أدناه = x*4+(y+1)*4*image_data.width;
إذا (image_data.data[i] == 255 &&
image_data.data[أعلاه] == 0 &&
image_data.data[أدناه] == 0) {
image_data.data[i] = 0;
image_data.data[i+1] = 0;
image_data.data[i+2] = 0;
}
الآن بعد أن أصبح لدينا شكل تقريبي للشخصية، يقوم البرنامج النصي أيضًا بإجراء الكشف الضروري عن الحافة قبل تحميله في الشبكة العصبية. سيبحث البرنامج النصي عن وحدات البكسل الموجودة في أقصى اليسار واليمين والأعلى والأسفل للرسم، ويحولها إلى مستطيل، ثم يعيد تحويل المستطيل إلى مصفوفة بحجم 20*25 بكسل.
Cropped_canvas.getContext("2d").fillRect(0, 0, 20, 25);
var edge = find_edges(image_data[i]);
Cropped_canvas.getContext("2d").drawImage(canvas, حواف[0], حواف[1],
الحواف[2]-الحواف[0]، الحواف[3]-الحواف[1]، 0، 0،
الحواف[2]-الحواف[0]، الحواف[3]-الحواف[1])؛
image_data[i] = Cropped_canvas.getContext("2d").getImageData(0, 0,
Cropped_canvas.width، Cropped_canvas.height)؛
بعد المعالجة المذكورة أعلاه، ماذا نحصل على مصفوفة 20*25 تحتوي على مستطيل واحد مملوء باللونين الأسود والأبيض. هذا رائع!
بعد ذلك، سنقوم بتبسيط المستطيل أكثر. نقوم بشكل استراتيجي باستخراج النقاط من المصفوفة باعتبارها "مستقبلات ضوئية" سيتم تغذيتها في الشبكة العصبية. على سبيل المثال، قد يتوافق مستقبل ضوئي معين مع بكسل يقع في 9*6، مع أو بدون بكسل. يستخرج البرنامج النصي سلسلة من هذه الحالات (أقل بكثير من حساب المصفوفة 20 × 25 بالكامل - يتم استخراج 64 حالة فقط) ويغذي هذه الحالات في الشبكة العصبية.
قد تتساءل، لماذا لا نقوم فقط بمقارنة وحدات البكسل بشكل مباشر؟ هل من الضروري استخدام شبكة عصبية؟ النقطة المهمة هي أننا نريد التخلص من تلك المواقف الغامضة. إذا قمت بتجربة العرض التوضيحي السابق، ستلاحظ أن مقارنة وحدات البكسل مباشرة أكثر عرضة للخطأ من المقارنة من خلال الشبكة العصبية، على الرغم من أن ذلك لا يحدث كثيرًا. ولكن علينا أن نعترف أنه بالنسبة لمعظم المستخدمين، يجب أن تكون المقارنة المباشرة للبكسل كافية.
والخطوة التالية هي محاولة تخمين الحروف. يتم استيراد 64 قيمة منطقية (تم الحصول عليها من إحدى صور الأحرف) إلى الشبكة العصبية، بالإضافة إلى سلسلة من البيانات المحسوبة مسبقًا. أحد مفاهيم الشبكات العصبية هو أن النتائج التي نريد الحصول عليها معروفة مسبقًا، حتى نتمكن من تدريب الشبكة العصبية وفقًا لذلك بناءً على النتائج. يمكن لمؤلف البرنامج النصي تشغيل البرنامج النصي عدة مرات وجمع سلسلة من أفضل الدرجات التي يمكن أن تساعد الشبكة العصبية على تخمين الإجابة من خلال العمل بشكل عكسي من القيم التي أنتجتها، ولكن هذه الدرجات ليس لها معنى خاص.
عندما تقوم الشبكة العصبية بحساب القيم المنطقية الـ 64 المقابلة لحرف في رمز التحقق، ومقارنتها بأبجدية محسوبة مسبقًا، ثم تعطي درجة للمطابقة مع كل حرف. (قد تكون النتيجة النهائية متشابهة: 98% قد تكون الحرف A، 36% قد تكون الحرف B، وما إلى ذلك.)
بعد معالجة الأحرف الثلاثة في رمز التحقق، ستظهر النتيجة النهائية. تجدر الإشارة إلى أن هذا البرنامج النصي ليس صحيحًا بنسبة 100% (أتساءل عما إذا كان من الممكن تحسين دقة التسجيل إذا لم يتم تحويل الحروف إلى مستطيلات في البداية)، ولكنه جيد جدًا، على الأقل للغرض الحالي. قل ذلك. ويتم إكمال جميع العمليات في المتصفح بناءً على تقنية العميل القياسية!
كملحق، يجب اعتبار هذا البرنامج النصي بمثابة حالة خاصة، وقد تعمل هذه التقنية بشكل جيد مع رموز التحقق البسيطة الأخرى، ولكن بالنسبة لرموز التحقق المعقدة، فهي قليلة بعيدًا عن متناول اليد (خاصة هذا النوع من التحليل المبني على العميل). آمل أن يلهم هذا المشروع المزيد من الناس وأن يطوروا المزيد من الأشياء الرائعة، لأن إمكاناته كبيرة جدًا