
آلة افتراضية لأندرويد ومُزِيل التعتيم
يقوم Simplify بتنفيذ التطبيق افتراضيًا لفهم سلوكه، ثم يحاول تحسين الكود بحيث يتصرف بنفس الطريقة ولكنه يكون أسهل للفهم البشري. كل نوع من أنواع التحسين بسيط وعام، لذا لا يهم نوع التعتيم المستخدم.
الكود على اليسار هو فك ترجمة لتطبيق معتم، والكود على اليمين تم إلغاء تعقيده.
يتكون المشروع من ثلاثة أجزاء: smalivm، simplify، وتطبيق العرض التوضيحي.
if أو switch بقيمة غير معروفة يؤدي إلى اتخاذ كلا الفرعين.usage: java -jar simplify.jar <input> [options]
deobfuscates a dalvik executable
-et,--exclude-types <pattern> استبعاد الفئات والطرق التي تتضمن تعبيرًا منتظمًا (REGEX)، مثال: "com/android"، يتم تطبيقه بعد include-types
-h,--help عرض هذه الرسالة
-ie,--ignore-errors تجاهل الأخطاء أثناء تنفيذ وتحسين الدوال. قد يؤدي هذا إلى سلوك غير متوقع.
--include-support محاولة تنفيذ وتحسين الفئات في حزم مكتبة دعم أندرويد، الافتراضي: false
-it,--include-types <pattern> قصر التنفيذ على الفئات والطرق التي تتضمن تعبيرًا منتظمًا (REGEX)، مثال: ";->targetMethod\("
--max-address-visits <N> التخلي عن تنفيذ دالة بعد زيارة نفس العنوان N مرة، يحد من الحلقات، الافتراضي: 10000
--max-call-depth <N> عدم استدعاء دوال بعد الوصول إلى عمق استدعاء N، يحد من التكرار وسلاسل الدوال الطويلة، الافتراضي: 50
--max-execution-time <N> التخلي عن تنفيذ دالة بعد N ثانية، الافتراضي: 300
--max-method-visits <N> التخلي عن تنفيذ دالة بعد تنفيذ N تعليمة في تلك الدالة، الافتراضي: 1000000
--max-passes <N> عدم تشغيل المحسنات على دالة أكثر من N مرة، الافتراضي: 100
-o,--output <file> إخراج الإدخال المبسط إلى FILE
--output-api-level <LEVEL> تعيين توافق DEX API للمخرجات إلى LEVEL، الافتراضي: 15
-q,--quiet كن هادئًا
--remove-weak إزالة الكود حتى في حالة وجود آثار جانبية ضعيفة، الافتراضي: true
-v,--verbose <LEVEL> تعيين مستوى التفاصيل إلى LEVEL، الافتراضي: 0
يتطلب البناء تثبيت Java Development Kit 8 (JDK).
نظرًا لأن هذا المشروع يحتوي على وحدات فرعية لأطر عمل أندرويد، قم إما بالاستنساخ باستخدام --recursive:
git clone --recursive https://github.com/CalebFenton/simplify.git
أو قم بتحديث الوحدات الفرعية في أي وقت باستخدام:
git submodule update --init --recursive
ثم، لبناء jar واحد يحتوي على جميع التبعيات:
./gradlew fatjar
سيكون ملف Simplify jar في simplify/build/libs/. يمكنك اختبار عمله عن طريق تبسيط تطبيق المثال المعتم المقدم. إليك كيفية تشغيله (قد تحتاج إلى تغيير simplify.jar):
java -jar simplify/build/libs/simplify.jar -it "org/cf/obfuscated" -et "MainActivity" simplify/obfuscated-app.apk
لفهم ما يتم إلغاء تعقيده، اطلع على README تطبيق Obfuscated App.
إذا فشل Simplify، جرب هذه التوصيات بالترتيب:
-it.--max-address-visits و --max-call-depth و --max-method-visits.-v أو -v 2 وأبلغ عن المشكلة مع السجلات وقيمة هاش (hash) لـ DEX أو APK.إذا كنت تبني على Windows وفشل البناء بخطأ مشابه لـ:
Could not find tools.jar. Please check that C:\Program Files\Java\jre1.8.0_151 contains a valid JDK installation.
هذا يعني أن Gradle غير قادر على العثور على مسار JDK صحيح. تأكد من تثبيت JDK، واضبط متغير البيئة JAVA_HOME على مسار JDK الخاص بك، وتأكد من إغلاق وإعادة فتح موجه الأوامر الذي تستخدمه للبناء.
لا تكن خجولًا. أعتقد أن التنفيذ الافتراضي وإلغاء التعقيد مشكلتان رائعتان. أي شخص مهتم فهو رائع تلقائيًا، والمساهمات مرحب بها، حتى لو كانت مجرد تصحيح خطأ إملائي. لا تتردد في طرح الأسئلة في القضايا (issues) وتقديم طلبات السحب (pull requests).
يرجى تضمين رابط لـ APK أو DEX والأمر الكامل الذي تستخدمه. هذا يسهل كثيرًا إعادة إنتاج (وبالتالي إصلاح) مشكلتك.
إذا لم تتمكن من مشاركة العينة، فالرجاء تضمين هاش الملف (SHA1، SHA256، إلخ).
إذا وضعت عملية (op) قيمة من نوع يمكن تحويلها إلى ثابت مثل سلسلة نصية أو رقم أو قيمة منطقية، فسيستبدل هذا التحسين تلك العملية بالثابت. على سبيل المثال:
const-string v0, "VGVsbCBtZSBvZiB5b3VyIGhvbWV3b3JsZCwgVXN1bC4="
invoke-static {v0}, Lmy/string/Decryptor;->decrypt(Ljava/lang/String;)Ljava/lang/String;
# Decrypts to: "Tell me of your homeworld, Usul."
move-result v0
في هذا المثال، يتم فك تشفير سلسلة مشفرة ووضعها في v0. نظرًا لأن السلاسل النصية "قابلة للتحويل إلى ثوابت"، يمكن استبدال move-result v0 بـ const-string:
const-string v0, "VGVsbCBtZSBvZiB5b3VyIGhvbWV3b3JsZCwgVXN1bC4="
invoke-static {v0}, Lmy/string/Decryptor;->decrypt(Ljava/lang/String;)Ljava/lang/String;
const-string v0, "Tell me of your homeworld, Usul."
الكود ميت إذا كانت إزالته لا يمكن أن تغير سلوك التطبيق. الحالة الأكثر وضوحًا هي إذا كان الكود غير قابل للوصول، مثل if (false) { // dead }). إذا كان الكود قابلًا للوصول، فقد يُعتبر ميتًا إذا لم يؤثر على أي حالة خارج الدالة، أي ليس له تأثير جانبي. على سبيل المثال، قد لا يؤثر الكود على القيمة المُعادة للدالة، أو يغير أي متغيرات فئة، أو ينفذ أي إدخال/إخراج. هذا أمر صعب التحديد في التحليل الثابت. لحسن الحظ، لا يحتاج smalivm إلى أن يكون ذكيًا. فهو فقط ينفذ كل شيء بغباء ويفترض وجود آثار جانبية إذا لم يكن متأكدًا. ضع في اعتبارك المثال من نشر الثوابت:
const-string v0, "VGVsbCBtZSBvZiB5b3VyIGhvbWV3b3JsZCwgVXN1bC4="
invoke-static {v0}, Lmy/string/Decryptor;->decrypt(Ljava/lang/String;)Ljava/lang/String;
const-string v0, "Tell me of your homeworld, Usul."
في هذا الكود، لم يعد invoke-static يؤثر على القيمة المُعادة للدالة ولنفترض أنه لا يفعل أي شيء غريب مثل كتابة بايتات إلى نظام الملفات أو مأخذ شبكة، لذلك ليس له آثار جانبية. يمكن إزالته ببساطة.
const-string v0, "VGVsbCBtZSBvZiB5b3VyIGhvbWV3b3JsZCwgVXN1bC4="
const-string v0, "Tell me of your homeworld, Usul."
أخيرًا، يقوم أول const-string بتعيين قيمة لسجل، لكن هذه القيمة لا تُستخدم أبدًا، أي أن التعيين ميت. يمكن إزالته أيضًا.