كيف يعمل ProGuard


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

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

كثير من الناس مخطئون بشأن ProGuard ، معتقدين خطأً أن هذا الأمر غامض ، وهو ليس لعدة أسباب:

  • إنه لا يخلط الكود
  • لا تشفير الرمز.

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

إذن ما الذي نتعامل معه؟


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

لذلك يمكننا أن نستنتج أن ProGuard هو ملف جرة يعيد ترتيب الأحرف في الكود لدينا ، ويحسنها ، ويبدو أنه يحسن الأمان. بشكل افتراضي ، يعمل ProGuard مع 26 حرفًا كبيرًا وصغيرًا.

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

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

إذا نظرت إلى أدلة مشروع proguard ، يمكنك تحديد وظائفه الرئيسية على الفور.


حتى الآن ، يبدو أن كل شيء واضح. لذلك دعونا نلقي نظرة على الطبقة الرئيسية.

public class ProGuard { //… private final MultiValueMap<String, String> injectedClassNameMap = new MultiValueMap<String, String>(); //... /** * The main method for ProGuard. */ public static void main(String[] args) { if (args.length == 0) { System.out.println(VERSION); System.out.println("Usage: java proguard.ProGuard [options ...]"); System.exit(1); } // Create the default options. Configuration configuration = new Configuration(); try { // Parse the options specified in the command line arguments. ConfigurationParser parser = new ConfigurationParser(args, System.getProperties()); //... 

حسنًا ، دعنا ننتقل إلى الطريقة الرئيسية الواضحة هنا ، والتي نرى فيها كيف يتم تعيين القواعد الافتراضية ويتم تحليل القواعد التي وضعها المطور نفسه.

بالإضافة إلى ذلك ، يوجد الكائن المتوقع injectedClassNameMap ، والذي نحصل على بناء / إخراج الملف / proguard / release / mapping.txt فيه يبدو كالتالي:


لذلك إذا أردنا فجأة فتح الكود الخاص بنا وجعله قابلاً للقراءة ، باستخدام mapping.txt يمكننا القيام بذلك. للقيام بذلك ، عند نشر ملف apk ، تحتاج إلى تنزيل إصدار mapping.txt في Google Play Console.

الآن يمكنك إلقاء نظرة على محلل التكوينات التي يحددها المطور.

 public class ConfigurationParser { //... /** * Parses and returns the configuration. * @param configuration the configuration that is updated as a side-effect. * @throws ParseException if the any of the configuration settings contains * a syntax error. * @throws IOException if an IO error occurs while reading a configuration. */ public void parse(Configuration configuration) throws ParseException, IOException { while (nextWord != null) { lastComments = reader.lastComments(); // First include directives. if (ConfigurationConstants.AT_DIRECTIVE .startsWith(nextWord) || ConfigurationConstants.INCLUDE_DIRECTIVE .startsWith(nextWord)) configuration.lastModified = parseIncludeArgument(configuration.lastModified); else if (ConfigurationConstants.BASE_DIRECTORY_DIRECTIVE .startsWith(nextWord)) parseBaseDirectoryArgument(); // Then configuration options with or without arguments. else if (ConfigurationConstants.INJARS_OPTION .startsWith(nextWord)) configuration.programJars = parseClassPathArgument(configuration.programJars, false); else if (ConfigurationConstants.OUTJARS_OPTION .startsWith(nextWord)) configuration.programJars = parseClassPathArgument(configuration.programJars, true); //… else if (ConfigurationConstants.KEEP_CLASSES_WITH_MEMBER_NAMES_OPTION .startsWith(nextWord)) configuration.keep = parseKeepClassSpecificationArguments(configuration.keep, false, true, true, null); else if (ConfigurationConstants.PRINT_SEEDS_OPTION .startsWith(nextWord)) configuration.printSeeds = parseOptionalFile(); // After '-keep'. else if (ConfigurationConstants.KEEP_DIRECTORIES_OPTION .startsWith(nextWord)) configuration.keepDirectories = parseCommaSeparatedList("directory name", true, true, false, true, false, true, true, false, false, configuration.keepDirectories); //... 

أه ، أنا لم أقل تعتيمًا ، إنه لا يعتريها ، لكن هنا ترى دليل التعتيم كله. كيف ذلك؟


إذا نظرت إلى الشاشة أعلاه ، يمكنك بسهولة العثور على الفئات المسؤولة عن إعادة تسمية الكائنات (SimpleNameFactory ، ClassRenamer ...). كما قلت أعلاه ، يتم استخدام 26 حرفا اللاتينية بشكل افتراضي.

 public class SimpleNameFactory implements NameFactory { private static final int CHARACTER_COUNT = 26; private static final List cachedMixedCaseNames = new ArrayList(); private static final List cachedLowerCaseNames = new ArrayList(); private final boolean generateMixedCaseNames; private int index = 0; //… 

في فئة SimpleNameFactory ، هناك طريقة خاصة للتحقق من printNameSamples () والتي ستمنحنا القيم المتوقعة

  public static void main(String[] args) { System.out.println("Some mixed-case names:"); printNameSamples(new SimpleNameFactory(true), 60); System.out.println("Some lower-case names:"); printNameSamples(new SimpleNameFactory(false), 60); System.out.println("Some more mixed-case names:"); printNameSamples(new SimpleNameFactory(true), 80); System.out.println("Some more lower-case names:"); printNameSamples(new SimpleNameFactory(false), 80); } private static void printNameSamples(SimpleNameFactory factory, int count) { for (int counter = 0; counter < count; counter++) { System.out.println(" ["+factory.nextName()+"]"); } } 

 Some mixed-case names: [a] [b] [c] [d] [e] [f] [g] [h] [i] [j] [k] ... 

فئة Obfuscator هي المسؤولة عن "التعتيم" ، حيث توجد طريقة تنفيذ واحدة ، حيث يتم نقل المجموعة المجمعة بأكملها من فئات المشروع نفسه وجميع المكتبات المضافة إليه.

 public class Obfuscator { private final Configuration configuration; //... public void execute(ClassPool programClassPool, ClassPool libraryClassPool) throws IOException { // Check if we have at least some keep commands. if (configuration.keep == null && configuration.applyMapping == null && configuration.printMapping == null) { throw new IOException("You have to specify '-keep' options for the obfuscation step."); } //... 

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

  /** * Performs the optimization step. */ private boolean optimize(int currentPass, int maxPasses) throws IOException { if (configuration.verbose) { System.out.println("Optimizing (pass " + currentPass + "/" + maxPasses + ")..."); } // Perform the actual optimization. return new Optimizer(configuration).execute(programClassPool, libraryClassPool, injectedClassNameMap); } 

يمكن تقسيم عمل proguard إلى عدة مراحل:

  1. قراءة قواعد محددة مسبقا
  2. التحسين
  3. حذف الأصول التي تم الإبلاغ عنها للتحسين
  4. إعادة تسمية الكائنات
  5. سجل المشروع في الدليل المحدد في شكل منقح

يمكنك بدء proguard يدويًا باستخدام الأمر:

 java -jar proguard.jar @android.pro 

حيث proguard.jar هو المشروع الذي تم تجميعه بواسطة ProGuard ، و android.pro هي قواعد عملها مع معلمات الإدخال والإخراج.

لماذا كتابة ProGuard الخاصة بك مؤلمة للغاية


في الحقيقة ، بينما كنت أتسلق رمز ProGuard ، رأيت اسمًا واحدًا فقط في عمود المؤلف - إريك لافورتون. عن طريق googling بسرعة ، وجدت موقعه الشخصي ، إذا كان أي شخص مهتمًا ، فيمكنك التعرف عليه.

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

لماذا استخدام ProGuard لا طائل منه


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

الشركات التي يختبئ رمزها يمثل أولوية لها ، عليك الالتزام بـ ProGuard وتعديلها لتناسب احتياجاتها ، وبالتالي الحصول على حل فريد.

لماذا ، لماذا ، لديك شيء لتفعله؟


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

كُتِب المقال منذ حوالي ستة أشهر ، والحارس يتطور باستمرار ، لأن بعض الأجزاء قد لا تتزامن.

ملاحظة: أنشر جميع المجموعات كما هو الحال دائمًا في قناة التلغراف @ paradisecurity ، ويمكن العثور على الرابط في ملفي الشخصي ، أو العثور عليه في البحث عن البرقيات بالاسم.

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


All Articles