كيف اكتشفت فيروسًا يحاول بيع الأحذية



أنا واحد من هؤلاء الأشخاص الذين يحاولون ، مع بداية فصل الخريف ، قضاء وقت أقل في الشارع. في موسكو ، هذا ليس صعبًا: أنت مقيد بالطريق من المنزل إلى المكتب والعودة. ومع ذلك ، يمكن أن يتسبب الطقس الرطب في عدم الراحة في الغرفة ، خاصة إذا كان مكان عملك ، مثل لي ، في النافذة ، ويطلب كل زميل آخر ، يشكو من الازدحام ، تهوية المكتب. لكي لا تقع في الطحال ، قمت في هذا الخريف بتحديث خزانة الملابس.

التفكير في مصير الأشياء غير الضرورية ، تساءلت ماذا أفعل بها: رميها ، قطعها إلى الخرق ، وإعطائها لأخي الأصغر لحملها؟ ولكن لسبب واحد ، لم تكن أي من هذه الطرق مناسبة: كانت أحذية جلدية 44 بحجم لائق ، لكنهم أزعجوني بالترتيب. قررت بيعها على Avito. لقد قمت بتحميل الصور ، وأشرت إلى اسم زائف (أمن المعلومات هو نفسه) ، ووضعت حذائي ، وبعض الأشياء الأخرى ، ونمت. كيف أعرف أن هذا سيؤدي إلى تحليل طويل للتطبيق للتهديدات الخفية؟



مفاجأة سارة


في اليوم التالي بعد مكالمتين مشكوك فيهما ، تلقيت رسالة SMS مثيرة للاهتمام تحتوي على المحتويات التالية:



بعد يومين ، تلقيت رسالة أخرى مماثلة:



فوجئت بأن شخصًا ما كان قادرًا بطريقة ما على تحويل الأموال لي على الإنترنت (على ما يبدو ، أنا الوحيد القديم - ما زلت أستخدم كتب التوفير الورقية) ، نقرت على الرابط في الرسائل القصيرة.

بعد ذلك ، عرض علي تنزيل تطبيق Android (ملف apk). لحسن الحظ تنزيل الملف ، رأيت ما يلي:



إنها ذات مصداقية! أردت بتلهف تثبيت كل شيء بسرعة وإنهائه.
ولكن هنا ، كالعادة ، لم يسمح لي نظام التشغيل Android المزعج لسبب ما بتشغيل الملف. "أعطني المال بالفعل!" كنت ساخط. اضطررت للذهاب إلى الإعدادات وتشغيل بعض الخيار "مصادر غير معروفة" ، هل الهاتف غبي حقًا في 2018؟ بالمناسبة ، هاتفي هو Xiaomi Remdi مع Andoid 6.0.1 (ملاحظة للتكنولوجيا).



تبع ذلك سلسلة من الأحداث الغريبة. استمر الهاتف في الإبلاغ عن مصادر غير موثوقة. لكن أفيتو مصدر موثوق ! اضطررت إلى البحث في Google ، ومعرفة كيفية الالتفاف حول هذا ، ثم إيقاف تشغيله في الإعدادات. سرعان ما ظهر مضاد فيروسات معين لم أقم بتثبيته.



أخيرًا ، رأيت نافذة التثبيت القياسية المرغوبة - ليس من المنطقي أن ننظر إلى الأذونات في الوقت الحاضر ، والآن لن يبدأ تشغيل الكمبيوتر الدفتري حتى تمنحها حق الوصول الكامل إلى الهاتف. لحظة سعيدة لإكمال تثبيت التطبيق وبدء! كنت أتطلع إلى الأموال الموعودة. بالإضافة إلى ذلك ، طلب التطبيق امتيازات المسؤول ، والتي وافقت عليها بسرور. لسوء الحظ ، تصرف التطبيق بشكل غريب ولم يرغب في الدفع ، وسرعان ما اختفى تمامًا من قائمة التطبيقات على الشاشة المشتركة.





المفسد
في وقت لاحق ، تحققت من هاتف آخر - Lenovo مع Android 4.4.2 على متن الطائرة. تبين أن قائمة الأذونات أثناء التثبيت أكبر بكثير. ولا تتدخل حماية Play و Anti-Virus ، فأنت تحتاج فقط للسماح بالتثبيت من مصادر غير موثوقة.





الإيثار



إذن ، ما الذي وصلنا إليه:

  • استغرق التثبيت 20 دقيقة.
  • لم أستلم المال.

اعتقدت أن المشكلة كانت خطأ في الكود ، كما هو الحال غالبًا مع المبرمجين. قررت تحديد الأخطاء التي تحدث عند تشغيل التطبيق على هاتفي ، وإرسال تقرير عنها إلى المطور.

المفسد
الآن لم يبق سوى عدد قليل من الأشخاص المهتمين ، أحدهم هو أنا.

من الواضح أن هذا النوع من التطبيقات يجب أن يعمل عند اتصال الإنترنت ، لذلك حاولت في البداية تحليل حركة المرور بين الهاتف وخادم التطبيق.

الدليل الفني لتكوين الوكيل
يمكنك الاستماع إلى حركة مرور الإنترنت في أي وقت على طول المسار من الهاتف إلى الخادم ، سواء كان جهاز توجيه منزلي أو مزود إنترنت محلي أو العمود الفقري للإنترنت أو خادم التطبيق.

المشاكل:

  • الوصول إلى هذا الجهاز مطلوب.
  • مطلوب لفصل حركة مرور التطبيق المطلوب عن الباقي.
  • في حالة التشفير (وفي عام 2018 تم تشفير كل شيء بالفعل) - من الضروري معرفة المفتاح.

قررت أن أذهب بطريقة أكثر كلاسيكية وتهيئة الوكلاء على الهاتف باستخدام خادم وكيل على جهاز الكمبيوتر المحمول الخاص بي. للتخفيف من مشكلة التشفير ، قررت استيراد شهادتي ، ولم أقم بتشغيل تطبيقات أخرى لفصل حركة مرور التطبيق.
اخترت مجموعة التجشؤ كبرنامج الخادم الوكيل. لقد قمت بإعداد خادم وكيل وتصدير شهادة إلى هاتفي.





بعد إعداد برنامج متخصص ، تمكنت من رؤية الطلبات التي يرسلها التطبيق إلى الخادم:



كما ترى ، لم يتم إرسال البيانات (يشار إلى قيمة المضيف غير المعروفة في عمود IP) ، علاوة على ذلك ، لا يمكن تحليلها بسبب وجود تشفير إضافي على مستوى التطبيق. إذا حكمنا بالخطأ ، لم يتمكن الهاتف من تحديد عنوان IP الخاص بالخادم ونطاقاته الفرعية https: //*.sky-sync.pw حسب اسم المجال الخاص به.

قد يعني هذا فقط الخيارات التالية:

  • لقد توقف اسم المجال من الوجود
    • تم حظره من قبل المالك نفسه.
    • وقد منعه المسجل بسبب الشكوى.
  • مشكلة في خادم DNS
    • لا يعرف خادم DNS العنوان ، لأن المطور في الإنتاج طرح عنوان DNS المحلي.
    • حظر خادم DNS هذا الطلب على وجه التحديد ، وهذا ليس مفاجئًا في عصر الرقابة على الإنترنت.

للتحقق من افتراض وجود مشكلة في خادم DNS ، حاولت تقديم طلبات من خوادم DNS كبيرة مختلفة: Google و Yandex و OpenDNS (عادة ما يتم حظر DNS المحلي):



يمكن ملاحظة أنه لا أحد منهم يعرف أي شيء عن هذا الاسم. بعد ذلك ، ألقيت نظرة على معلومات whois حول تسجيل النطاق:



فضولي: تم تسجيل النطاق ، أي أنه على الأرجح ليس محليًا ، ولكن نظرًا لعدم حل النطاق ، فربما تم حظره من قِبل المسجل بسبب إساءة الاستخدام. لكن لماذا؟ ما الخطأ الذي فعله؟

لمعرفة ما هو هذا التطبيق تمامًا وكيف يمكنني أخذ أموالي ، قررت استخدام سحر الهندسة العكسية.

الهاوية


إذا كنت إنسانيًا وقرأت في هذا المكان ، فهذا أمر جيد - لتنمية اللاحقة أنت تستحق جائزة بعد وفاتها .

مجموعة أدوات


لمعرفة ما "تحت غطاء" التطبيق ، نحتاج إلى تنزيل أدوات متخصصة. يمكنك تنزيلها بشكل فردي:

  • فك حزمة APK
    • كلاسيك - ApkTool .
    • يمكنك فك ضغطه باستخدام أرشيفي تقليدي ، ولكن بعد ذلك ستكون جميع الموارد الثنائية ، بما في ذلك التطبيقات وملف البيان ، غير قابلة للقراءة.
  • فك شفرة Smali
    • المعيار هو Dex2Jar ، ولكن تعلم أن هذا البرنامج يعمل في كثير من الأحيان بشكل منحرف.
    • تحتاج إلى التعامل مع الأمر بعناية شديدة ، لأن المترجم إلى المترجم مختلف ، سننظر في ذلك لاحقًا.
  • برنامج لعرض التعليمات البرمجية المترجمة ، أوصي بـ jd-gui

أو يمكنك استخدام المنتج ، المدفوع عادة ، حيث يوجد كل شيء في وقت واحد. أنا أفضل JebDecompiler : يمكنه فقط تقديم تطبيق apk لمدخلاته ، وسوف يرتب كل شيء في علامات التبويب ، بالإضافة إلى أنه مناسب للتبديل بين كود Java smali و decompiled.

بشكل منفصل ، أود أن أشير إلى:


فقط كبار السن يخوضون المعركة


مراجعة


عند فتح الشفرة المترجمة ، يتضح على الفور أنها مشوشة.



كيف أفهم ذلك؟

  • أسماء الفئات التي يمكن قراءتها بواسطة الإنسان
    isqpwcmx.isfdztgb.adscjobz.nxscomkr.jypbdxnt.utagwpym.wprtdznb.swldgrhm.yrbjpktq.wukovicq;
  • رمز لا يمكن الوصول إليه
     if(0 != 0) {</li> String v1 = "flnwznvh";</li> if(v1.length() != 661 && v1.charAt(0) == 104) {</li> v1.length();</li> } 
  • تشفير السلسلة
     vcgrnfjx.execSQL(nvhdzjfo.xipswfqb(new String[]{"f741f04a4991fc2f0a0029f610bbd1c250dfe115fb7770b892f75d8718b822d273251013991b4407e224fa3f9d4e92f6","378f40211b6e32a5406cd97e85bcf9ad","6378a459b1c20edf", "gexnfwok", "meazfhdp", "bsmotaxn"}) 

من غير المحتمل أن يكون المبرمج مجنونًا لدرجة أنه طور الشفرة في البداية. على الأرجح ، استخدم أحد الغامض المتاح للجمهور. هذه الخطوة شائعة جدًا لتعقيد تحليل الشفرة من أجل حماية الملكية الفكرية ، على سبيل المثال ، ولكن أي نوع من "الالتواء" بالنسبة للباحث.

دعونا ننتبه إلى وظيفة التشفير الرئيسية:



تقبل وظيفة التشفير نفسها 3 أسطر من المدخلات (إذا كان الأمر أكثر ، فإن الباقي لا معنى له):

  1. نص مشفر
  2. المفتاح
  3. ناقل التهيئة لـ CBC - AES

تتم الإشارة إلى هذه الوظيفة في البرنامج 213 مرة على الأقل:



ألاحظ أنه مفتاح مهم لتحليل الكود العادي. بعد ذلك ، تحتاج إلى التفكير في أن لدينا الطرق التالية لتحليل البرنامج:

  1. قم باستعادة منطق الوظيفة ، وجمع كل المكالمات في التحليل الثابت ، وفك تشفير الخطوط. قد يكون الأمر صعبًا وطويلًا ، ولكنه سيعطي نتيجة 100 ٪.
  2. قم بإجراء تغييرات على رمز smali للتطبيق ، وقم بالترجمة مرة أخرى ، وقم بتشغيل التطبيق والتقاط الأسطر التي تم فك تشفيرها في السجلات. من السهل القيام بذلك ، ولكن الطريقة التي سيتصرف بها التطبيق في عملية تشغيل معينة غير معروفة ، وقد لا ترى الصورة كاملة (لا تحصل على مكالمات لجميع الوظائف). بالإضافة إلى ذلك ، قد تكون هناك مشاكل في الفحص الذاتي من خلال تطبيق الشهادة و (أو) التكامل.
  3. إذا كان من الصعب استعادة منطق الوظيفة ، فيمكنك حينئذٍ جمع كل استدعاءات الوظائف وسحب هذه الوظائف نفسها مع المعلمات الضرورية مباشرة في الديناميكيات (باستخدام ، على سبيل المثال ، برنامج Frida .

سنختار الطريقة رقم 1 باعتبارها الأكثر موثوقية.

التجفيف


قم بالحجز على الفور ، غالبًا ما تكون إزالة التعتيم عملية طويلة ومملة ، لذلك تحتاج إلى تقييم الإطار الزمني الخاص بك بشكل صحيح. بالنسبة لتحليلنا ، يكفي فك تشفير جميع الخطوط بطريقة ما على الأقل والقيام بذلك في الحد الأدنى من الوقت ، حتى بطريقة عكسية ، من العبث لمدة شهر من أجل خيار مثالي غامض.

يكون إلغاء التعتيم النوعي منطقيًا في حالة الهندسة العكسية الكاملة ، على سبيل المثال ، يتعين على لصوص الملكية الفكرية القيام بذلك عندما يحاولون نسخ حل منافس ، أو إذا كان عليك غالبًا تحليل البرامج التي تتم معالجتها بواسطة مظلل واحد ، ولكن هذه ليست قضيتنا.

شفرة المصدر بعد JEB Decompiler v.1.4 decompiler

المفسد
 public static String podxiwkt(String[] args) { int v6; int v4; byte[] v2; Cipher v1; String v10 = args[0]; String v7 = args[1]; String v0 = args[2]; if(v10 == null) { goto label_9; } if(v10.length() != 0) { goto label_11; } goto label_9; label_11: IvParameterSpec v5 = new IvParameterSpec(v0.getBytes()); try { v1 = Cipher.getInstance("AES/CBC/NoPadding"); goto label_15; } catch(NoSuchPaddingException v3) { } catch(NoSuchAlgorithmException v3_1) { } String v11 = ""; goto label_10; label_15: SecretKeySpec v9 = new SecretKeySpec(v7.getBytes(), "AES"); int v11_1 = 2; try { v1.init(v11_1, ((Key)v9), ((AlgorithmParameterSpec)v5)); v2 = Base64.decode(v1.doFinal(bwdoclkr.xkvasepi(v10)), 0); if(v2.length <= 0) { goto label_48; } v4 = 0; v6 = v2.length - 1; label_29: if(v6 < 0) { goto label_38; } if(v2[v6] != 0) { goto label_33; } } catch(Exception v3_2) { goto label_51; } ++v4; label_33: --v6; goto label_29; label_38: if(v4 <= 0) { goto label_48; } try { byte[] v8 = new byte[v2.length - v4]; System.arraycopy(v2, 0, v8, 0, v2.length - v4); v2 = v8; } catch(Exception v3_2) { label_51: v11 = ""; goto label_10; } label_48: v11 = new String(v2); goto label_10; label_9: v11 = ""; label_10: return v11; } } 

ملاحظة جهاز التحليل
بالمناسبة ، غالبًا ما يتعطل dex2jar. لذا ، في الشكل أدناه ، يمكن ملاحظة أن dex2jar الإصدار 2.0 لا يمكنه التأقلم وأصدر للتو رمزًا smali.



أحدث إصدار لها ، تم جمعه من المصادر ، أنتج كودًا مترجمًا لهذه الوظيفة ، لكنه لم يستطع فك العديد من اللغات الأخرى (هذه هي الحيلة).





خلاصة القول: فكر بعناية في اختيار جهاز فك الشفرة - سيوفر لك هذا الكثير من الوقت وسيكون أسهل من تحليل رمز smali.


لذا ، إذا قمنا فقط بلصق هذا الرمز في IDE الآن ، فلن يعمل بسبب الأخطاء.

من المهم أن تتذكر: لا يحتاج المترجم إلى إنتاج كود صالح يكتبه المطور. إنه مجرد منقذ في التحليل ويضع افتراضات حول كيفية كتابة الشفرة. في معظم الحالات ، بعد التحسين من قبل المترجم ، تتوقف مهمة استعادة الشفرة الأصلية تمامًا عن كونها تافهة.

مثال سيء للترجمة:

 if(v10 == null) { goto label_9; } if(v10.length() != 0) { goto label_11; } goto label_9; … label_9: v11 = ""; return v11; 

نرى أنه اتضح بشكل سيئ وغير عملي. نعيد كتابة:

 if ((v10 == null) || (v10.length() == 0)) { return ""; } 

الآن أصبح الأمر أكثر وضوحًا ، إليك فحص الإدخال المعتاد. في هذه الحالة ، نحتاج إلى:

  • استبدل جميع "goto" ببنيات لغة أخرى ، مثل لطالما كان "Goto" عامل تشغيل غير صالح.
  • استبدل مكالمات مكتبة Android بمكالمات مكتبة Java (إذا حاولنا تنفيذ التعليمات البرمجية في Java IDE).
  • إدراج الفئات التابعة المشار إليها بواسطة التعليمات البرمجية الخاصة بنا.
  • فكر في نفسك ما هو الخطأ.

نتيجة لذلك ، نحصل على:

 package com.company; //package isqpwcmx.isfdztgb.adscjobz.nxscomkr.jypbdxnt.utagwpym.wprtdznb.swldgrhm.yrbjpktq; import java.util.Base64; //import android.util.Base64; //import bnxvhlyg.nkhoirul.zfxogwqi.mdpqejcw.srnepbly.pcbvwxrs.vixdqclm.wnuqvrhp.bnvceayd.bwdoclkr; //   import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.spec.AlgorithmParameterSpec; import javax.crypto.Cipher; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; public abstract class Main { public Main() { super(); } //hex to ascii public static byte[] xkvasepi(String str) { byte[] v0 = null; if(str != null && str.length() >= 2) { int v2 = str.length() / 2; v0 = new byte[v2]; int v1; for(v1 = 0; v1 < v2; ++v1) { v0[v1] = ((byte)Integer.parseInt(str.substring(v1 * 2, v1 * 2 + 2), 16)); } } return v0; } public static String podxiwkt(String[] args) { int v6; int v4; byte[] v2; Cipher v1; String v10 = args[0]; //text String v7 = args[1]; //key String v0 = args[2]; //IV //check if ((v10 == null) || (v10.length() == 0)) { return ""; } IvParameterSpec v5 = new IvParameterSpec(v0.getBytes()); try { v1 = Cipher.getInstance("AES/CBC/NoPadding"); } catch(NoSuchPaddingException v3) { return ""; } catch(NoSuchAlgorithmException v3_1) { return ""; } SecretKeySpec v9 = new SecretKeySpec(v7.getBytes(), "AES"); int v11_1 = 2; try { v1.init(v11_1, ((Key)v9), ((AlgorithmParameterSpec)v5)); //v2 = Base64.decode(v1.doFinal(bwdoclkr.xkvasepi(v10)), 0); v2=v1.doFinal(xkvasepi(v10)); //check if(v2.length <= 0) { return new String(v2); } } catch(Exception v3_2) { return ""; } v4=0; for (v6=v2.length-1;v6>=0;v6--){ if (v2[v6]==0) ++v4; } if(v4 > 0) { try { byte[] v8 = new byte[v2.length - v4]; System.arraycopy(v2, 0, v8, 0, v2.length - v4); v2 = v8; } catch (Exception v3_2) { return ""; } } v2 = Base64.getDecoder().decode(v2); return new String(v2); } public static void main(String[] args) { // write your code here System.out.println(podxiwkt(new String[] { "b1acd584a6eae4ca6321b1f7cdf9ba9617112b4fb39e76c8def876346e3032fbd32b2d188a09715f27124c1bf9facfdc", "637904cd08aeb2d3f6a21b5c7e84f519", "8f4c796d5a3120eb", "zcmwgvdn", "mkngbsyr", "rwcdaieu" })); } } { "b1acd584a6eae4ca6321b1f7cdf9ba9617112b4fb39e76c8def876346e3032fbd32b2d188a09715f27124c1bf9facfdc"، "637904cd08aeb2d3f6a21b5c7e84f519"، "8f4c796d5a3120eb"، "zcmwgvdn"، "mkngbsyr"، "rwcdaieu"}))؛ package com.company; //package isqpwcmx.isfdztgb.adscjobz.nxscomkr.jypbdxnt.utagwpym.wprtdznb.swldgrhm.yrbjpktq; import java.util.Base64; //import android.util.Base64; //import bnxvhlyg.nkhoirul.zfxogwqi.mdpqejcw.srnepbly.pcbvwxrs.vixdqclm.wnuqvrhp.bnvceayd.bwdoclkr; //   import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.spec.AlgorithmParameterSpec; import javax.crypto.Cipher; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; public abstract class Main { public Main() { super(); } //hex to ascii public static byte[] xkvasepi(String str) { byte[] v0 = null; if(str != null && str.length() >= 2) { int v2 = str.length() / 2; v0 = new byte[v2]; int v1; for(v1 = 0; v1 < v2; ++v1) { v0[v1] = ((byte)Integer.parseInt(str.substring(v1 * 2, v1 * 2 + 2), 16)); } } return v0; } public static String podxiwkt(String[] args) { int v6; int v4; byte[] v2; Cipher v1; String v10 = args[0]; //text String v7 = args[1]; //key String v0 = args[2]; //IV //check if ((v10 == null) || (v10.length() == 0)) { return ""; } IvParameterSpec v5 = new IvParameterSpec(v0.getBytes()); try { v1 = Cipher.getInstance("AES/CBC/NoPadding"); } catch(NoSuchPaddingException v3) { return ""; } catch(NoSuchAlgorithmException v3_1) { return ""; } SecretKeySpec v9 = new SecretKeySpec(v7.getBytes(), "AES"); int v11_1 = 2; try { v1.init(v11_1, ((Key)v9), ((AlgorithmParameterSpec)v5)); //v2 = Base64.decode(v1.doFinal(bwdoclkr.xkvasepi(v10)), 0); v2=v1.doFinal(xkvasepi(v10)); //check if(v2.length <= 0) { return new String(v2); } } catch(Exception v3_2) { return ""; } v4=0; for (v6=v2.length-1;v6>=0;v6--){ if (v2[v6]==0) ++v4; } if(v4 > 0) { try { byte[] v8 = new byte[v2.length - v4]; System.arraycopy(v2, 0, v8, 0, v2.length - v4); v2 = v8; } catch (Exception v3_2) { return ""; } } v2 = Base64.getDecoder().decode(v2); return new String(v2); } public static void main(String[] args) { // write your code here System.out.println(podxiwkt(new String[] { "b1acd584a6eae4ca6321b1f7cdf9ba9617112b4fb39e76c8def876346e3032fbd32b2d188a09715f27124c1bf9facfdc", "637904cd08aeb2d3f6a21b5c7e84f519", "8f4c796d5a3120eb", "zcmwgvdn", "mkngbsyr", "rwcdaieu" })); } } 

يفي هذا الرمز بنجاح. بعد أن يصبح عمله واضحًا ، يمكن تبسيطه وتبسيطه ، مما يؤدي إلى النوع المقتضب المفترض الذي كتبه المبرمج (ما لم تكن يداه بالطبع ملتوية في البداية).

ملاحظة
بالمناسبة ، في هذه الحالة ، يمكن إظهار فك تشفير السلاسل باستخدام مجموعة من الموارد عبر الإنترنت. مثال على استدعاء سلسلة مشفرة داخل برنامج:



هنا ، يجب تحويل متجه التهيئة أولاً إلى تنسيق Hex:



استبدل جميع القيم:



وفي النهاية ، فك الشفرة من base64:



ونتيجة لذلك ، نحصل على السلسلة المعتادة ، وتلقي المكالمة نظرة ذات معنى.

وبالتالي ، تحتاج إلى مراجعة الرمز بالكامل وجمع جميع السلاسل المشفرة ، والآن يمكننا فك تشفيرها بشكل مستقل. النقطة المهمة هنا هي أنه في مرحلة "التعديل والتعليق على الكود" يمكننا العمل على مستوى smali وعلى مستوى Java (smali decompiled).

إيجابيات التعديلسلبيات تعديل
سمالييمكنك إجراء تغييرات وإعادة تجميع في dex وفك مع خطوط جديدةليس من السهل دائمًا العمل باستخدام رمز smali. إذا كان التغيير غير صحيح ، فلن يتم ترجمة التطبيق
جافاغالبًا ما يكون استخراج البيانات من عمليات المستوى الأعلى أسهل كثيرًا.لا يستطيع معظم مشاهدي كود Java التعديل.

مثال سطر آخر

 vcgrnfjx.execSQL(nvhdzjfo.xipswfqb(new String[]{"f741f04a4991fc2f0a0029f610bbd1c250dfe115fb7770b892f75d8718b822d273251013991b4407e224fa3f9d4e92f6","378f40211b6e32a5406cd97e85bcf9ad","6378a459b1c20edf", "gexnfwok", "meazfhdp", "bsmotaxn"}) 

من هنا ، من السهل جدًا اختيار المعلمات باستخدام تعبير عادي ، بدلاً من كتابة حرف عادي على الكود التالي:

مثال كود سمالي 1

 00000280 new-instance v13, Ljava/lang/StringBuilder; 00000284 invoke-direct {v13}, Ljava/lang/StringBuilder;-><init>()V 0000028A const/4 v14, 0x6 0000028C new-array v14, v14, [Ljava/lang/String; 00000290 const/4 v15, 0x0 00000292 const-string v16, "f741f04a4991fc2f0a0029f610bbd1c250dfe115fb7770b892f75d8718b822d273251013991b4407e224fa3f9d4e92f6" 00000296 aput-object v16, v14, v15 0000029A const/4 v15, 0x1 0000029C const-string v16, "378f40211b6e32a5406cd97e85bcf9ad" 000002A0 aput-object v16, v14, v15 000002A4 const/4 v15, 0x2 000002A6 const-string v16, "6378a459b1c20edf" 000002AA aput-object v16, v14, v15 000002AE const/4 v15, 0x3 000002B0 const-string v16, "gexnfwok" 000002B4 aput-object v16, v14, v15 000002B8 const/4 v15, 0x4 000002BA const-string v16, "meazfhdp" 000002BE aput-object v16, v14, v15 000002C2 const/4 v15, 0x5 000002C4 const-string v16, "bsmotaxn" 000002C8 aput-object v16, v14, v15 


مثال على رمز Smali 2

 0000008E new-array v0, v0, [Ljava/lang/String; 00000092 move-object/from16 v89, v0 00000096 const/16 v90, 0x0 0000009A const-string v91, "4500b5e2e2ad26b7545eb54d70ab360ae28c9d031e2afcc3f6a2b2ac488ea440" 0000009E aput-object v91, v89, v90 000000A2 const/16 v90, 0x1 000000A6 const-string v91, "da96f678922d4b07350b3a184ecc1f5e" 000000AA aput-object v91, v89, v90 000000AE const/16 v90, 0x2 000000B2 const-string v91, "0cf69e3d2745a1b8" 000000B6 aput-object v91, v89, v90 000000BA const/16 v90, 0x3 000000BE const-string v91, "jhiqsaoe" 000000C2 aput-object v91, v89, v90 000000C6 const/16 v90, 0x4 000000CA const-string v91, "khbqxurn" 000000CE aput-object v91, v89, v90 


مثال على رمز سمالي 3

 00000D3E new-array v0, v0, [Ljava/lang/String; 00000D42 move-object/16 v298, v0 00000D48 const/4 v0, 0x0 00000D4A move/16 v299, v0 00000D50 const-string v0, "b286945744e085f4d5c19916fd261481" 00000D54 move-object/16 v300, v0 00000D5A move-object/from16 v0, v300 00000D5E move-object/from16 v1, v298 00000D62 move/from16 v2, v299 00000D66 aput-object v0, v1, v2 00000D6A const/4 v0, 0x1 00000D6C move/16 v299, v0 00000D72 const-string v0, "df6883742b2911ac5ac7b4dee065390f" 00000D76 move-object/16 v300, v0 00000D7C move-object/from16 v0, v300 00000D80 move-object/from16 v1, v298 00000D84 move/from16 v2, v299 00000D88 aput-object v0, v1, v2 00000D8C const/4 v0, 0x2 00000D8E move/16 v299, v0 00000D94 const-string v0, "90a463ce2df17b58" 00000D98 move-object/16 v300, v0 00000D9E move-object/from16 v0, v300 00000DA2 move-object/from16 v1, v298 00000DA6 move/from16 v2, v299 00000DAA aput-object v0, v1, v2 00000DAE const/4 v0, 0x3 00000DB0 move/16 v299, v0 00000DB6 const-string v0, "cupyzsgq" 00000DBA move-object/16 v300, v0 00000DC0 move-object/from16 v0, v300 00000DC4 move-object/from16 v1, v298 00000DC8 move/from16 v2, v299 00000DCC aput-object v0, v1, v2 


كما نرى ، تتغير المتغيرات الداخلية ، ويختلف تسلسل الأوامر ، كما يختلف عدد الحجج ، بالإضافة إلى ذلك ، في البرنامج ، لا يتم استدعاء وظيفة فك التشفير بشكل مباشر ، ولكن من خلال وظائف الطبقة. حاول كتابة قاعدة بنفسك للبحث عن هذه البنية ، وتجنب الأخطاء في التقاط السلاسل من الوظائف الأخرى ، وقم بكل ذلك بسرعة ( حظًا سعيدًا ).

خطة المصيدة:

  1. سنقوم باستخراج كل القيم من الكود المتحلل.
  2. فك تشفير.
  3. استبدل النص المشفر بالنص المفتوح في كود smali. نستبدل ، على سبيل المثال ، بدلاً من عامل التشغيل الأول. (سيكون من الأكثر احترافية قطع مكالمة الوظيفة بالكامل وترك السلسلة التي تم فك تشفيرها ، ولكن مرة أخرى هناك خطر كبير لكسر البرنامج).
  4. دعنا نجمع كود smali في ملف dex.
  5. سيكون من الملائم النظر إلى مزيد من المعلومات في محلل الشفرة ، حيث بدأنا.

إذا جمعت كل الشفرة المترجمة في ملف واحد ، فستحصل على حوالي 20000 سطر ، وهو ما يتطلب للتحليل اليدوي الكثير من الوقت ، والذي يكلف بوضوح أكثر من الأحذية التي عرضتها للبيع. أولاً ، اجمع كل الخطوط بتعبير عادي.



نرى 593 مباراة ، بالإضافة إلى اثني عشر من الذين لم يندرجوا تحت هذه القاعدة ، لدى الأسرة خرافها السوداء . مثال:



ترتيب ، تصفية ، إجمالي 422 سطرًا فريدًا:



نمر من خلال وظيفة فك التشفير التي استعادناها في وقت سابق. النتيجة:



استبدل النص المشفر بالنص المفتوح برمز smali باستخدام Python:

 import os words_replace=dict() words_replace["0018aacad3d146266317d8d8c51785fd"]="imei" words_replace["016d15e4d0a72667c61428e736a6f3b8"]="WakeLock" words_replace["032c534efb6c9990cd845a08c5a08b95"]="inbox" #…  .. # smali- #      def change(path): print("file="+path) file_handle = open(path, 'r') context_full = file_handle.read() file_handle.close() for i in words_replace: context_full=context_full.replace(i, words_replace[i]) #print (i+""+words_replace[i]) file_handle = open(path, 'w') context_full = file_handle.write(context_full) file_handle.close() #      smali- for top, dirs, files in os.walk('C:\\work\\test'): for nm in files: path=os.path.join(top, nm) print (path) change(path) 

نجمع ملفات smali في dex:



الآن يمكن تحليل هذا بطريقة أو بأخرى (من خلال قراءة الحجة الأولى من البناء بأكمله):



تحليل


لذلك ، لدينا 20000 سطر من التعليمات البرمجية يمكن قراءتها أكثر أو أقل ، لا نحتاج إلى إجراء تحليل كامل. من الضروري فهم الوظيفة ككل. هنا ، في الواقع ، مطلوب فقط القدرة على قراءة شفرة مصدر جافا. تجول في الكود ، وانظر إلى المراجع التبادلية ، وأعد تسمية المتغيرات والوظائف.

ما هي أفضل طريقة لتحليل تطبيق Android ، وخاصة التطبيق الكبير؟

الخيار 1: يمكنك الانتقال من ملف البيان

على سبيل المثال ، حاول بشكل تسلسلي من LAUNCHER فك سلسلة المكالمات بالكامل. بالمناسبة ، لا تنس أنه لا يزال هناك "المتلقي" و "الخدمة" التي يمكن أن تغير التنفيذ الخطي للبرنامج.



ملف البيان الكامل
 <?xml version="1.0" encoding="utf-8" standalone="no"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="internalOnly" package="xfmpuwon.mtnbupnc.ihqdgjal.ndgmqawx.bjunzerq.cznfpnoq.fzevcuym.jmpdiqft"> <uses-permission android:name="xfmpuwon.mtnbupnc.ihqdgjal.ndgmqawx.bjunzerq.cznfpnoq.fzevcuym.jmpdiqft.permission.C2D_MESSAGE"/> <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/> <uses-permission android:name="android.permission.SEND_SMS"/> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.READ_PHONE_STATE"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/> <uses-permission android:name="android.permission.RECEIVE_SMS"/> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <uses-permission android:name="android.permission.QUICKBOOT_POWERON"/> <uses-permission android:name="android.permission.READ_SMS"/> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/> <permission android:name="xfmpuwon.mtnbupnc.ihqdgjal.ndgmqawx.bjunzerq.cznfpnoq.fzevcuym.jmpdiqft.permission.C2D_MESSAGE" android:protectionLevel="signature"/> <application android:allowBackup="true" android:icon="@drawable/icon" android:label="@string/tgiwmpqy" android:noHistory="true"> <activity android:configChanges="orientation" android:excludeFromRecents="true" android:label="@string/tgiwmpqy" android:launchMode="singleTop" android:name="zemquyog.csrtmnak.xrkfygen.wkahrnjd.acnfunjh.rgipxbuf.lruiwxeg.blqndche.dcjihbou" android:screenOrientation="portrait"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <activity android:configChanges="orientation" android:launchMode="singleTop" android:name="xbfrscou.hxrvwnoi.djvpcqri.enlnrfio.aoegxbiu.heywzmnb.znfnxcht.nazcxobq" android:screenOrientation="portrait"/> <activity android:configChanges="orientation" android:launchMode="singleTop" android:name="hcfkagds.timkagsd.oetvghzr.fcioynvl.psynofdj.slcghdjz.tapnwsdk.gzvwnban.htenafdb.qwebhzgy" android:noHistory="true" android:screenOrientation="portrait"/> <activity android:configChanges="orientation" android:excludeFromRecents="true" android:launchMode="singleTop" android:name="njfbwmre.voefarqx.ftuxvngl.wrmshxqj.zdenywgn.eiwyunlg.jysgkbam.yrijthab.vstqxpuo.iplamgxf" android:priority="2147483647" android:screenOrientation="portrait"/> <receiver android:name="gfbaznoc.asyoqtnm.kbetoqca.mqysobzu.gqwfibrv.dorxijuk.wgzkmiep.ywnnurzv.csfpqhrn" android:permission="android.permission.BIND_DEVICE_ADMIN"> <meta-data android:name="@string/pkzrlscm" android:resource="@xml/ynqukvnb"/> <intent-filter android:priority="2147483646"> <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/> </intent-filter> </receiver> <receiver android:name="ykwbodxc.gymjhibn.kgmdfqor.hbasvmfz.yegkmaif.ortzknvm.quplincn.cuxytvhs.fqonzuts.cyuoxgqi.znumwyct" android:permission="com.google.android.c2dm.permission.SEND"> <intent-filter> <action android:name="com.google.android.c2dm.intent.RECEIVE"/> <action android:name="com.google.android.c2dm.intent.REGISTRATION"/> <action android:name="com.google.android.c2dm.intent.UNREGISTRATION"/> <category android:name="xfmpuwon.mtnbupnc.ihqdgjal.ndgmqawx.bjunzerq.cznfpnoq.fzevcuym.jmpdiqft"/> </intent-filter> </receiver> <receiver android:enabled="true" android:exported="true" android:name="kqwihjot.nvkqjloc.grjnyknm.owydvckh.mugknwdx.enhcyvja.mhvbpcue.ztbwjhfo"> <intent-filter android:priority="2147483646"> <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED"/> <action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/> <action android:name="android.intent.action.QUICKBOOT_POWERON"/> <action android:name="android.intent.action.BOOT_COMPLETED"/> <action android:name="android.intent.action.USER_PRESENT"/> <action android:name="android.intent.action.BATTERY_OKAY"/> <action android:name="android.intent.action.BATTERY_LOW"/> <action android:name="android.intent.action.ACTION_POWER_CONNECTED"/> <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/> <action android:name="android.intent.action.APP_ERROR"/> <action android:name="android.intent.action.HEADSET_PLUG"/> <action android:name="android.intent.action.PHONE_STATE"/> <action android:name="android.intent.action.NEW_OUTGOING_CALL"/> <action android:name="android.provider.Telephony.SMS_RECEIVED"/> <action android:name="android.intent.action.TIME_TICK"/> <action android:name="android.intent.action.SCREEN_ON"/> <action android:name="android.intent.action.SCREEN_OFF"/> <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/> <action android:name="android.net.wifi.WIFI_STATE_CHANGED"/> <action android:name="android.intent.action.DREAMING_STOPPED"/> <category android:name="android.intent.category.HOME"/> </intent-filter> </receiver> <receiver android:name="btnsxnuz.wmjizbky.lynvjxqz.zinomjuv.yizlgcnf.qwoikgnc.wnrskjea.wfqgmeny.lcgvqrms.ocwkgblp"> <intent-filter android:priority="2147483646"> <action android:name="android.provider.Telephony.SMS_RECEIVED"/> </intent-filter> </receiver> <service android:name="ltvsrezg.ehxndrat.twnnyxrf.nqynefws.dhbalcnr.ynjkuxod.nhoxmsbq.nackoyhn.voycgfek.znhwkqba.taxvnfyn"/> <service android:name="rbnakfzo.qsreiubk.pwvlnngs.twoxnhfv.mftarcnd.pfioxcub.xjlaftqr.nxrqvlwh"/> <service android:enabled="true" android:name="xfmpuwon.mtnbupnc.ihqdgjal.ndgmqawx.bjunzerq.cznfpnoq.fzevcuym.jmpdiqft.ugshpjvo"/> </application> </manifest> 


الخيار 2: يمكنك الانتقال من خطوط مثيرة للاهتمام



جزء من سلاسل مشفرة
 system_update.apk () () , error = , unregistered = ,  .permission.C2D_MESSAGE //sky-sync.pw/ //sms/inbox /system_update.apk ALLCONTACTS ALLMSG AUTHENTICATION_FAILED Acquiring wakelock Application BLOCKER_BANKING_START BLOCKER_EXTORTIONIST_START BLOCKER_STOP BLOCKER_UPDATE_START Banking CHANGE_GCM_ID CONTACTS CONTACTS_PRO CREATE TABLE IF NOT EXISTS END Error|No process list|No access Extortionist Foreground GCM returned invalid number of GCMBaseIntentService GCMBroadcastReceiver GCMIntentService- GCMRegistrar GCM_LIB GET MESSAGE Mobile Network NEWMSG Not retrying failed operation ONLINE PAGE POST Process finished with exit code 0 RESTART Received deleted messages Registering receiver Releasing wakelock SERVICE_NOT_AVAILABLE SSL START STOP Saving regId on app version Scheduling registration retry, backoff = Setting registeredOnServer status as Stop System UNBLOCK UPDATE UPDATE_PATTERNS URL UTF-8 Update WakeLock Wakelock reference is null Wi-Fi WiMax _success add_msg_ok address android.intent.action.QUICKBOOT_POWERON answer_text answer_to api_url app appVersion application application/vnd.android.package-archive apps_list ask backoff_ms blocker blocker_banking blocker_banking_autolock blocker_banking_forced_access blocker_banking_success blocker_extortionist blocker_extortionist_autolock blocker_extortionist_forced_access blocker_extortionist_success blocker_update blocker_update_forced_access blocker_update_success body build callback cardSuccess check com.android.settings com.google.android.c2dm.intent.RECEIVE com.google.android.c2dm.intent.REGISTER com.google.android.c2dm.intent.REGISTRATION com.google.android.c2dm.intent.UNREGISTER com.google.android.gcm com.google.android.gcm.intent.RETRY com.google.android.gsf com.htc.intent.action.QUICKBOOT_POWERON command command_receive contactslist country data date delete deleted_messages device_block disableDataConnectivity enableDataConnectivity error failure file deleted. first_start force-locked gafzpjxb.cix gcm gcm_id gcm_register gcm_register_ok getITelephony get_message_list id integer primary key autoincrement, id=? imei immunity inbox init_bootable init_imei is_admin is_awake_display is_imunnity is_locked is_network_type is_top_activity job job_date job_id komgejif.hqr locked message message_delivered message_type method model msg msg_id msglist name not nypjtinq.nvp ok onServer onServerExpirationTime onServerLifeSpan operator org.android.sys.admin.disabled org.android.sys.admin.enabled org.android.sys.admin.request org.android.sys.command.receive org.android.sys.launch.first org.android.sys.sms.pro.sent org.android.sys.sms.push org.android.sys.sms.sent outbox page params pattern patterns personal phone phone_list privet process_list protocol qwertyuiopasdfghjklzxcvbnm receive regId regex register register_ok registrationId = registration_id repeat resetting backoff for ru save_contacts_list save_message_history sender sent sent_status sid ss status stop_blocker text text, text/html time token total_deleted type unknown unregistered until url useragent utf-8 value version xpls yes   !     ...                   !  ?                ! 

الخيار 3: يمكنك الانتقال من موارد مثيرة للاهتمام (الأصول ، المكتبات)

في هذه الحالة ، يفضل الخيار 3. هناك ثلاثة ملفات html مثيرة للاهتمام في مجلد / asset (حاوية apk). هنا وجهة نظرهم في المتصفح:





يبدو الأمر مشكوكًا فيه بالنسبة لبرنامج Avito الرسمي لتحويل الدفع ، ألا يبدو لك ذلك؟ دعونا نتتبع ما يحدث عند الضغط على المفتاح لإرسال البيانات المصرفية على الصفحة مع شعار Sberbank. تستدعي JavaScript وظيفة sendCardData() :



ثم يتم تحويلها إلى كود Java من خلال المكالمة ok.performClick() :



في كود Java ، يتم تنفيذ المعالجة:



علاوة على ذلك ، يتم تشفير كل هذا في فئة mcrypt :



داخل الوظيفة ، يتم تشفير البيانات بنفس الطريقة التي تم اعتبارها سابقًا:



ولكن بالنسبة للباقي ، فإن المفاتيح مشدودة:



نحن نحاول فك التشفير من خلال المورد عبر الإنترنت:



وتحويل من base64. نجاح! يمكننا فك تشفير جميع بيانات التطبيق: تم اختباره على حركة المرور التي تم التقاطها في وقت سابق.

تقارير التطبيق إلى الخادم حول جميع الأحداث
 { "sid":15, "imei":"861117030537111", "phone":"System", "message":"     22.10.2018 23:30:47", "time":"1540240247", "msg_id":1, "status":"unknown", "type":"inbox", "method":"message" } 

كما أنه ينقل بشكل دوري جميع التطبيقات قيد التشغيل
 { "sid": 15, "imei": "861117030537111", "country": "ru", "operator": "MTS RUS", "phone": "", "model": "Xiaomi Redmi 3X", "version": "6.0.1", "application": "", "build": "30.0.2", "process_list": [ "Background|com.android.bluetooth|com.android.bluetooth.hid.HidService", "Background|com.android.settings:remote|com.android.settings.wifi.MiuiWifiService", "Background|com.android.phone|org.codeaurora.ims.ImsService", "Background|system|com.qualcomm.location.LocationService", ..., "Background|xfmpuwon.mtnbupnc.ihqdgjal.ndgmqawx.bjunzerq.cznfpnoq.fzevcuym.jmpdiqft|ltvsrezg.ehxndrat.twnnyxrf.nqynefws.dhbalcnr.ynjkuxod.nhoxmsbq.nackoyhn.voycgfek.znhwkqba.taxvnfyn" ], "apps_list": [ "com.introspy.config", "com.google.android.youtube", "com.google.android.googlequicksearchbox", "org.telegram.messenger", ..., "com.google.android.inputmethod.latin", "jakhar.aseem.diva" ], "method": "register" } 

إذا كانت لدي نافذة لإدخال البيانات المصرفية في الديناميكيات ، فستكون البيانات في حركة المرور. وهكذا ، يمكننا أن نستنتج أن هذا هو تطبيق "التصيد".

أولئك الذين كانوا حذرين لاحظوا أن ملف Manifest لديه عدد غير قليل من الأذونات ، وأن التطبيق يحتوي على وظائف أكثر ثراء. سنقوم بإجراء تحليل عميق للوظيفة في مقال آخر. في هذه الأثناء ، النجاح!

الاستنتاجات


أشعر بخيبة أمل لأنني لم أبيع الأحذية. وكانت الاستنتاجات كما يلي:

  • لا تبيع الأحذية على Avito
  • لا تنقر على الروابط الغامضة (حتى لو كان من الأصدقاء وحتى إذا كنت "بحاجة إلى اقتراض 100 روبل بشكل عاجل - مسألة حياة أو موت")
  • لا تقم بتنزيل تطبيقات بخلاف Google Play أو AppStore
    • افصل التثبيت من "مصادر غير موثوقة" إذا كنت لا تفهم حقًا ما هو.
    • لا تفصل "حماية التشغيل".
    • تذكر أنه قد يكون هناك برامج ضارة على Google Play
  • قم بتثبيت برنامج مكافحة الفيروسات على الهاتف (إنه يعمل حقًا).
  • إذا كنت مطورًا ، لا تشوه الرمز ، دع الناس يتأكدون من حسن نواياك ( مجرد مزاح )
  • إذا كنت باحثًا ، فلا تعمل من أجل الطعام ، وقم بتحليل التطبيقات في وقت فراغك ونشر التقارير. معًا سنجعل العالم مكانًا أفضل.

ملاحظة: حاولت كتابة المقالة قليلاً بتنسيق فكاهي وإرسالها بأبسط ما يمكن ، لأنني حتى لا أريد أن أقرأ على الأرجح يوم الجمعة رحلة طويلة خطيرة تسمى "الهندسة العكسية لتطبيق Android الخبيث المشوش".

Source: https://habr.com/ru/post/ar430074/


All Articles