/مقدمة: يحرص منتجو البرامج على نقطتين أساسين : الأمن و الملكية الفكرية.ترتبط مسألة الأمن بفكرة الانتشار الواسع للبرنامج، أما الملكية الفكرية فهي شرط لضمان ديمومة المداخيل و هذا الذي يفسر الاهتمام المتزايد بها.
أ/مخاطر الهندسة العكسية
تتمثل الهندسة العكسية في إمكانية الحصول على الكود المصدري للبرنامج من هيئته المترجمة (compiled ) المقدمة من طرف الشركة المبرمجة. يمكن ذلك من فهم أدق تفاصيل البرنامج.
قد يلجأ منافسك إلى الهندسة العكسية لمعرفة الخوارزميات المستعملة في برنامجك و لسرقة أسرارها .
و قد يتعدى ذلك إلى معرفة كلمات مرور مستعملة في برنامجك للاتصال بقواعد بيانات أو خدمات أخرى يحتاجها برنامجك مما يشكل تهديدا صريحا لك.
ب/تقنيات الحماية
هناك أربع تقنيات أساسية لحماية برنامجك من الهندسة العكسية
- Obfuscation : تحويل الكود المصدري قبل مرحلة الترجمة بطريقة تجعله غير مفهوم لقارئه (الإنسان طبعا و ليس المترجم)
- التشفير : تضمن حماية تامة لكودك المصدري مادام خوارزمية التشفير المستعملة لم تكسر بعد و المفتاح المستعمل لم يتم إيجاده
- استعمال البرنامج عن بعد: حيث لا يقدم للزبون برنامجا جزئيا فقط .يقوم هذا البرنامج باستدعاء الأجزاء التي يحتاجها من Server محمي.
- كتابة براج لمعمارية خاصة (hardware خاص ) يعقد من مهمة إيجاد Decompiler لذلك
ج/لماذا نستعمل الـ Obfuscation
.تتميز هذه التقنية بـ:
الأقل تكلفة من منافساتها
الأسهل من ناحية الاستعمال (لا تتطلب معمارية معينة)
لا تظهر آثاره للمستخدم العادي
لا تأثر على إنتاجية المبرمج.
لا توجد فروق واضحة بين برنامج طبقت عليه هذه التقنية و برنامج عادي سوى أنه الحصول على كوده المصدري ليس بالأمر الهين حتى و إن تم استخدام Decompiler متطور
توجد –بطبيعة الحال- أدوات تحاول عكس هذه العملية( DeObfuscation) . الصراع ما زال قائما بين مبرمجي أدواة الـ Obfuscation و بين مبرمجي de Obfuscation .لهذا يجب اختيار برنامج Obfuscation بدقة تسمح لك بحماية برنامجك و لو لحين
II/تقنيات Obfuscation
توجد عدة تقنيات للـ Obfuscation لعل من أهمها:
أ/تغيير Style الكود:
يتم ذلك بـ:
إعادة تسمية المعرفات : من متغيرات إلى أسماء الدوال مرورا بأسماء الـ class ...
حذف التعليقات Comments
حذف إعلانات الـ API غير المستعملة
حذف أنماط التنسيق و أنماط كتابة الكود
1/إعادة تسمية المعرفات:
تعتبر هذه التقنية أولى التقنيات المستعملة في الـ Obfuscation . يتمثل ذلك في إعادة تسمية كل من
Variables , constantes , class,methodes,attributs..
من بين الدروس التي يتلقاها الطلبة في سنواتهم الأولى هو حسن اختيار أسماء المعرفات لتسهيل فهم الكود...انس كل ذلك..بل بالعكس اعمل عكسه..استعمل أسماء غير مفهومة أو توحي بعكس ما تعمل
تسمى عملية إعادة التسمية بـ Refactoring .أن تقوم بكتابة برنامج يقوم بهذه العلمية ليس بالأمر الهين بسبب وجود ترابطات قريبة وبعيد بين أجزاء البرنامج (افرض أنك قمت بتغيير اسم class في الملف الذي يحتوي تعريفها لكنك نسيت تغييره في الملفات التي تستعمل هذه الـ class ).
لكن كيف يتم اختيار الأسماء الجديدة:
هناك عدة طرق:
الطريقة العشوائية: نقوم بتوليد أسماء عشوائية (لا يولد نفس الاسم أكثر من مرة) .
طريقة Overload Induction نقوم بتوليد أسماء بأحجام تصاعدية
نبدأ بـ a() ثم b() ثم ........ثم aa() .....
تسمح هذه الطريقة بإمكانية إعطاء نفس الاسم لعدة دوال (مبدأ Overload ). مما يسمح بتصغير حجم الكود المصدري في النهاية
طريقة الإخفاء: توليد أسماء تحوي على حروف ممنوع استعمالها في لغة البرمجة و في الـ decompilers .
مثال عن كود Java قبل عملية الـ Obfuscation
- public synchronized void put(int key, Employee value) {
- }
بعد عملية الـ Obfuscation باستعمال الطريقة العشوائية
- public synchronized void yrwla35rn3z22jd0sci9(int sbhc8wduotn7gkbr8pq6, k0j9y980ekqci18t09ju 78nrx59777f4io0qpg7t) {
- }
بعد استعمال الـOverload Induction
بعد استعمال طريقة الإخفاء
- public synchronized void #~a(int @b, f# a~) {
- }
2/حذف التعليقات:
أحيانا تقاس جودة مشروع ما بكمية التعليقات الموجودة فيه (مثل استعمال JavaDoc في الـ Java) قد تصل نسبة التعليقات في بعض المشاريع إلى نسبة 50% من إجمالي الكود. تسهل وجود تعليقات مفصلة عملية التنقيح.
لذا فإن استعمال الـ Obfuscation يقضي نهائيا على هذه التعليقات
- /**
- * Comment1
- * Comment2
- * Comment3
- * Comment4
- * Comment5
- * Comment6
- */
- Branch b = null;
- try {
- // Comment
- if ( (b = (Branch) (entries.get(number))) != null ) {
- }
- // Comment
- else {
- throw new ProblemException("BankImpl.findBranch", "no branch found !");
- }
- } catch(ProblemException e) {
- }
- return b;
- }
بعد حذف التعليقات
- Branch b = null;
- try {
- if ( (b = (Branch) (entries.get(number))) != null ) {
- }
- else {
- throw new ProblemException("BankImpl.findBranch", "no branch found !");
- }
- } catch(ProblemException e) {
- }
- return b;
- }
3/حذف أنماط التنسيق:
يسهل استعمال أنماط التنسيق لدى كتابة الكود تسهيل الوصول إلى الكود (رؤية جيدة) . حيث يبنى المشاريع عادة على نمط واحد يتفق عليه في البداية.. قد يتعدى استعمال الأنماط إلى استعمال نمط لتسمية المتغيرات.
مثال كود java يتبع نمطا معينا
- package bankServices;
- import org.omg.CORBA.*;
- import org.omg.PortableServer.*;
- import org.omg.PortableServer.POA;
- import java.util.HashMap;
- public class BankImpl extends BankPOA {
- try {
- Branch b = (Branch) (entries.get(number));
- if(b != null) {
- entries.remove(number);
- return;
- } else {
- throw new ProblemException("BankImpl.unregisterBranch","no branch reference found !");
- }
- } catch(ProblemException e) {
- }
- }
- }
بعد حذف النمط:
- package bankServices;
- import org.omg.CORBA.*;
- import org.omg.PortableServer.*;
- import org.omg.PortableServer.POA;
- import java.util.HashMap;
- public class BankImpl extends BankPOA{private HashMap entries;public void unregisterBranch(String number) throws ProblemException{
- try{Branch b=(Branch)(entries.get(number));if(b!=null){entries.remove(number);System.out.println("the branch "+number+" is deleted");
- return;}else{throw new ProblemException("BankImpl.unregisterBranch","no branch reference found !");
- }}catch(ProblemException e){System.out.println("BankImpl.unregisterBranch : error in unregistering - " + e.getMessage());}}}
4/حذف إعلانات الـ API غير المستعملة
يمكن لإعلانات الـApi المستعملة إعطاء فكرة عن نوعية الخوارزميات المستعملة
أحيانا ما يتم استدعاء مكتبات كاملة في حين يمكن استدعاء أجزاء محددة منها
الحد من هذه العملية قد يساعد على تصعيب عملية الهندسة العكسية
توجد هذه الخاصية في العديد من الـ IDE فمثلا في Eclipse يمكن عمل ذلك بـ : Source > Organize imports
مثال:
.قبل
- import org.omg.CORBA.*;
- import org.omg.PortableServer.*;
- import org.omg.PortableServer.POA;
- import java.util.HashMap;
بعد
- import org.omg.CORBA.ORB;
- import org.omg.PortableServer.POA;
- import java.util.HashMap;
حذف أوامر الـتنقيح و الترجمة
يمكن في بعض الأحيان حذف هذه الأوامر .يخص هذا الأمر لغة البرمجة التي نطبق عليها Obfuscation (ليس الكود المصدري و إنما الـ Bytecode في حالة الـ java ، managed code في حالة الـ DotNet أو أي preCompiled code الذي لم يعد بحاجة إلى مثل هذه الأوامر)
ب-البيانات
1/تشفير الجمل
يسمح تشفير الجمل من إخفاء محتوياتها .(تخفى دالة و مفتاح التسفير داخل الكود)
مثال(تشفير e-mail بالـ javaScript )
- <script type="text/javascript"> <!--
- Ch=new Array(4);
- Res=new Array(4);
- Ch[0]='le_club_des_developeur';
- Ch[1]='ÙÆÈÏàä';
- Ch[2]='¬×ÄÇÍØÖÈÓÓ';
- Ch[3]='ÐÊÕÈØäÒÏÉß¡ÂÓÒ';
- for(y=1;y<4;y++){
- Res[y]="";
- for(x=0;x<Ch[y].length;x++)
- Res[y]+=String.fromCharCode(Ch[y].charCodeAt(x)-Ch[0].charCodeAt(x));
- }
- document.write('<a href="'+Res[1]+':webmaster'+Res[2]+'-'+Res[3]+'">webmaster'+Res[2]+'-'+Res[3]+'</a>');
- //-->
- </script>
2/تغيير نوع المتغير تغيير نوع المتغيرات يزيد من تعقيد و حجم الكود الذي يتم تحليله لاستخراج الكود المصدري
فمثلا في java استعمال integer بدل int يزيد من تعقيدات De-obfuscater
إضافة إلى أنه إذا تم تغيير نوع متغير من نوعه الأول إلى نوع آخر مركب فإن درجة التعقيد تزيد أكثر
3/إعادة ترتيب الجداول
قد يوحي محتوى بعض الجداول بنوعية الخوارزمية المستعملة (مثال على جدول يحتوي على رموز البلدان أو أسعار العملات...)
و عليه فإن تغيير ترتيب محتويات الجداول تعقد من مهمة الـ De-obfuscater
يجب التنويه إلى ضرورة كتابة دوال تعيد ترتيب الجداول في حال ما إذا كان هذا الترتيب مهما في البرنامج.
4/ تغيير مدى صلاحية المتغيرات
تحويل جميع المتغيرات الظرفية (local) إلى عامة (global) يعقد من مهمة De-obfuscater خاصة إذا كان هناك عدة متغيرات local تحمل نفس الاسم: نفس المعرف لعدة دوال يصعب من فهم أي دالة تقوم بأي عمل.
5/تعديد معرفات نفس المتغير:
يؤدي ذلك إلى (طبعا باستعمال المؤشرات Pointer ) إلى إطالة زمن عملية De-obfuscater و إخفاء مبدأ عمل أجزاء(قد تكون جد مهمة) من الخوارزميات.
ج/بنية البرنامج:
تغيير بنية البرنامج هي الحجز الأساس في عملية الـ obfuscater
1/ تغيير بنى الـ class
يتم تغيير بنى الـ class بتجميع البعض في class أشمل أو تقسيم أخرى أو تعميق سلسلة التوريث (مثلا الـ class A يرث من الـ class B نقوم بإنشاء عدة class تقوم بالتوريث لبعضها البعض لصنع سلسلة من التوريثات تبدأ بـ B لتنتهي عند A )
2/ control structures
توجد العديد من الطرق التي تساعد في خداع محلل الكود
- إضافة control structures و التي تعطي دائما نفس النتيجة (لا تأثر على بقية البرنامج) و إيهام المحلل أن هذه الـ control structures تلعب دورا أساسيا في البرنامج
- تكبير حجم الكود بإضافة دوال و class لا يحتاجها البرنامج
- إضافة كود "ميت" لا يؤثر على مسار البرنامج (لكن يؤثر في زمن التشغيل)
- مثال عن إضافة كود "ميت" في C#
- private void InitializeComponent()
- {
- this.DontDoAnyThing(“APXS”); // كود ميت
- this.AlwaysNoThing(50, -10) ; // كود ميت
- this.SuspendLayout() ;
- this.AnOtherNoThing() ; // كود ميت
- }
- مثال عن إضافة control structure لغة C
قيل
- for(int i=1; i<=n; i++) {
- for(int j=1; j<=n; j++) {
- tab[i,j] = fct(i,j);
- }
- }
بعد
- for(int I=1; I<=n; I+64) {
- for(int J=1; J<=n; J+64) {
- for(int i=I; i<=min(I+63,n); i++) {
- for(int j=J; j<=min(J+63,n); j++) {
- tab[i,j] = fct(i,j);
- }
- }
- }
- }
- تغيير ترتيب الـ Boolean expression
- إضافة Arguments إضافية للدوال (Arguments غير مستعملة)
- تغليف الـ data structure المستعملة بإضافة طبقات من الـ control structure عليها (دون تأثير على الكود)
- تعقيد بنية الجداول (استعمال جداول ثلاثية الأبعاد بدل ثنائية الأبعاد ...)
- تعقيد الحلقات التكرارية (إضافة حلقات دون التأثير في سير البرنامج)
- تكرير ظهور متغيرات في العمليات الحسابية (مثل إضافة و طرح نفس القيمة)
- تحويل الكود من تسلسلي إلى تفرعي : الغرض من ذلك ليس تحسين أداء البرنامج و إنما يصعب على برامج De-obfuscater تحليل الكود التفرعي
يمكن تحويل أي كود تسلسلي إلى تفرعي ما لم يكن هناك ارتباطات بين أقسامه فمثلا يمكن استعمال الـ threads في الـ java و إذا وجدنا ترابطات في أجزاء الكود نحل هذه المعضلة باللجوء إلى الـ Synchronisation
مثال عن كود تفرعي في الـ java
قبل
- void methode() {
- // instruction 1
- // instruction 2
- // instruction 3
- }
بعد
- // 1st thread
- methodeA() {
- }
- public void run() {
- // instruction 1
- // instruction 2
- }
- }
- // 2nd thread
- methodeB() {
- }
- public void run() {
- // instruction 3
- }
- }
- void methodeC() {
- methodeA mA = new methodeA();
- mA.run();
- methodeB mB = new methodeB();
- mB.run();
- }
- تحويل كود من class إلى آخرى
- تقسيم بعض الدوال إلى مجموعة دوال جزئية
مثال عن تقسيم دالة في الـ PHP
قبل
- $fichier=$id.".cpt";
- }
- $hits++;
- }
بعد
- $fichier=$id.".cpt";
- create($fichier);
- write($fichier);
- }
- function create($fichier) {
- }
- }
- function write($fichier) {
- $fp=open($fichier,"r+");
- $hits = calculate($hits);
- }
- function open($fichier, $opt) {
- return $fp;
- }
- function calculate($hits) {
- return $hits++;
- }
- تجميع أقسام بعض الدوال في دالة واحدة
- استعمال دوال شبيهة بدل الدالة الأصلية
- if(!Recherchersommet(s)) {
- Cellgraphe c = new Cellgraphe(s);
- listcell.Insererapres(c);
- }
- return this;
- }
مثال1 عن دالة شبيهة
- // clone n°1
- if(!Recherchersommet(sommet)) {
- Cellgraphe cell = new Cellgraphe(sommet);
- listcell.Insererapres(cell);
- }
- return this;
- }
مثال2 عن دالة شبيهة
- // clone n°2
- if(Recherchersommet(s) != 0) {
- Cellgraphe c = new Cellgraphe(s);
- listcell.Insererapres(c);
- }
- return this;
- }
مثال3 عن دالة شبيهة
- // clone n°3
- if(!Recherchersommet(obj))
- Cellgraphe c = new Cellgraphe(obj);
- if(!Recherchersommet(obj))
- listcell.Insererapres(c);
- return this;
- }
III/فعالية الـ Obfuscation
تتجلى فعالية الـ Obfuscation في الجهد المبذول لكتابة برنامج de- Obfuscation و في الوقت المبذول و في نوعية و كمية الموارد المستعملة في عملية de- Obfuscation .
يمكن مقارنة Obfuscation بالتشفير : حيث أنه يجب كتابة برنامج ذو كفاءة عالية يجعل من الصعب جدا إيجاد حل له (خصوصا في ما يتعلق بمعامل الزمن). لكن و مع التقدم التكنولوجي السريع و السرعات المتزايدة المتوفرة في أجهزة الحاسب يقلص من زمن حماية البرامج
.IV /حدود الـ Obfuscation :
- محدودية زمن الحماية
- لا يوجد تأثير بخصوص الثغرات الأمنية
- تعقيدات الحماية تزيد من الزمن المستغرق لتشغيل البرنامج
- تدني مستوى الخوارزميات المستعملة مما يحرمها من إمكانية الحصول على certification
- استحالة تمويه استعمال الـ API الخارجية
- لجوء مبرمجي برامج de-Obfuscation لتجريب برامج Obfuscation المتوفرة لتجريبها (تطبيقها على كود معين و من ثم تحليل النتائج) مما قد يدفع أحيانا إلى استعمال برنامج Obfuscation خاص بك مما يؤدي إلى تدني مستوى الحماية (حلقة مفرغة)
.IV /خاتمة:
قدمنا لكم في هذا المقال أساسيات Obfuscation و سردا لمحاسنها و مساوئها. الحرب لا تزال قائمة بين الـ Obfuscators و الـ de-Obfuscators مثلما هو الحال بالنسبة للمشفرين و كاسري التشفير. هذا الحال يدفع عجلة تطوير هذا المجال.
لا يمكن اعتبار الـ Obfuscation حلا مثاليا و طويل المدى لحماية برامجك (حقوق الملكية الفكرية). إضافة إلى التدني المعتبر لمستوى و نوعية البرنامج .
يجب التأني و دراسة الوضع جيدا قبل التفكير جديا في استعمال هذه التقنية لحماية برامجك
عنوان المقال الأصلي:.
Obfuscation : protection du code source contre le reverse engineering
رابط المقال الأصلي :
http://cyberzoide.developpez.com/securite/obfuscation/
كاتب المقال الأصلي:
تمت الترجمة في 17/07/2008




