Obfuscation: حماية البرنامج من الهندسة العكسية

Obfuscation: حماية البرنامج من الهندسة العكسية

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

أ/مخاطر الهندسة العكسية

تتمثل الهندسة العكسية في إمكانية الحصول على الكود المصدري للبرنامج من هيئته المترجمة (compiled  ) المقدمة من طرف الشركة المبرمجة. يمكن ذلك من فهم أدق تفاصيل البرنامج.

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

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

ب/تقنيات الحماية

هناك أربع تقنيات  أساسية لحماية برنامجك من الهندسة العكسية

  • Obfuscation: تحويل الكود المصدري قبل مرحلة الترجمة بطريقة تجعله غير مفهوم لقارئه (الإنسان طبعا و ليس المترجم)
  • التشفير: تضمن حماية تامة لكودك المصدري مادام خوارزمية التشفير المستعملة لم تكسر بعد و المفتاح المستعمل لم يتم إيجاده
  • استعمال البرنامج عن بعد: حيث لا يقدم للزبون برنامجا جزئيا فقط .يقوم هذا البرنامج باستدعاء الأجزاء التي يحتاجها من Server محمي.
  • كتابة برامج لمعمارية خاصة (hardware خاص ) يعقد من مهمة إيجاد Decompiler لذلك

ج/لماذا نستعمل الـ Obfuscation

.تتميز هذه التقنية بـ:

  • الأقل تكلفة من منافساتها
  • الأسهل من ناحية الاستعمال (لا تتطلب معمارية معينة)
  • لا تظهر آثاره للمستخدم العادي
  • لا تأثر على إنتاجية المبرمج.
  • لا توجد فروق واضحة بين برنامج طبقت عليه هذه التقنية و برنامج عادي سوى أنه الحصول على كوده المصدري ليس بالأمر الهين حتى و إن تم استخدام Decompiler متطور

توجد -بطبيعة الحال- أدوات  تحاول عكس هذه العملية(  DeObfuscation)   .   الصراع ما زال قائما بين مبرمجي أدواة الـ Obfuscation  و بين مبرمجي  de Obfuscation      .لهذا يجب اختيار برنامج Obfuscation    بدقة تسمح لك بحماية برنامجك و لو لحين

تقنيات 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) {

Integer I = new Integer(key);

super.put(I, (Object) value);

}

بعد عملية الـ Obfuscation     باستعمال  الطريقة العشوائية

public synchronized void yrwla35rn3z22jd0sci9(int sbhc8wduotn7gkbr8pq6, k0j9y980ekqci18t09ju 78nrx59777f4io0qpg7t) {

Integer f841593p5rh12zf88285 = new Integer(sbhc8wduotn7gkbr8pq6);

super.yrwla35rn3z22jd0sci9(f841593p5rh12zf88285, (Object) 78nrx59777f4io0qpg7t);

}

بعد استعمال الـOverload Induction

public synchronized void a(int a, b c) {

Integer d = new Integer(a);

super.a(d, (Object) c);

}

بعد استعمال طريقة الإخفاء

public synchronized void #~a(int @b, f# a~) {

Integer #~b = new Integer(@b);

super.#~a(#~b, (Object) a~);

}

2/حذف التعليقات:

أحيانا تقاس جودة مشروع ما بكمية التعليقات الموجودة فيه (مثل استعمال JavaDoc   في الـ Java)  قد تصل نسبة التعليقات في بعض المشاريع إلى نسبة 50%  من إجمالي الكود. تسهل وجود تعليقات مفصلة عملية التنقيح.

لذا فإن استعمال الـ  Obfuscation       يقضي نهائيا على هذه التعليقات

/**

* Comment1

* Comment2

* Comment3

* Comment4

* Comment5

* Comment6

*/

public Branch findBranch(String number) throws ProblemException {

Branch b = null;

try {

// Comment

if ( (b = (Branch) (entries.get(number))) != null ) {

System.out.println(“the branch ” + number + ” found”);

}

// Comment

else {

throw new ProblemException(“BankImpl.findBranch”, “no branch found !”);

}

} catch(ProblemException e) {

System.out.println(“BankImpl.findBranch : error in finding – ” + e.getMessage());

}

return b;

}

بعد حذف التعليقات

public Branch findBranch(String number) throws ProblemException {

Branch b = null;

try {

if ( (b = (Branch) (entries.get(number))) != null ) {

System.out.println(“the branch ” + number + ” found”);

}

else {

throw new ProblemException(“BankImpl.findBranch”, “no branch found !”);

}

} catch(ProblemException e) {

System.out.println(“BankImpl.findBranch : error in finding – ” + e.getMessage());

}

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 {

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());

}

}

}

بعد حذف النمط:

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;

5 /حذف أوامر الـتنقيح و الترجمة

يمكن في بعض الأحيان حذف هذه الأوامر .يخص هذا الأمر لغة البرمجة التي نطبق عليها 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.button_crypter = new System.Windows.Forms.Button();

this.DontDoAnyThing(“APXS”); // كود ميت

this.button_decrypter = new System.Windows.Forms.Button();

this.BlaBlaBla(new System.Windows.Forms.Button()); // كود ميت

this.button1 = new System.Windows.Forms.Button();

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

class methodeA extends Thread {

methodeA() {

}

public void run() {

// instruction 1

// instruction 2

}

}

// 2nd thread

class methodeB extends Thread {

methodeB() {

}

public void run() {

// instruction 3

}

}

void methodeC() {

methodeA mA = new methodeA();

mA.run();

methodeB mB = new methodeB();

mB.run();

}

  • تحويل كود من class إلى آخرى
  • تقسيم بعض الدوال إلى مجموعة دوال جزئية

مثال عن تقسيم دالة في الـ PHP

قبل

function [url=”http://fr.php.net/manual/fr/function.count.php”%5Dcount%5B/url%5D($id) {

$fichier=$id.”.cpt”;

if(![url=”http://fr.php.net/manual/fr/function.file-exists.php”%5Dfile_exists%5B/url%5D($fichier)) {

$fp=[url=”http://fr.php.net/manual/fr/function.fopen.php”%5Dfopen%5B/url%5D($fichier,”w&#8221;);

[url=”http://fr.php.net/manual/fr/function.fputs.php”%5Dfputs%5B/url%5D($fp,”0&#8243;);

[url=”http://fr.php.net/manual/fr/function.fclose.php”%5Dfclose%5B/url%5D($fp);

}

$fp=[url=”http://fr.php.net/manual/fr/function.fopen.php”%5Dfopen%5B/url%5D($fichier,”r+&#8221;);

$hits=[url=”http://fr.php.net/manual/fr/function.fgets.php”%5Dfgets%5B/url%5D($fp,10);

$hits++;

[url=”http://fr.php.net/manual/fr/function.fseek.php”%5Dfseek%5B/url%5D($fp,0);

[url=”http://fr.php.net/manual/fr/function.fputs.php”%5Dfputs%5B/url%5D($fp,$hits);

[url=”http://fr.php.net/manual/fr/function.fclose.php”%5Dfclose%5B/url%5D($fp);

}

بعد

function [url=”http://fr.php.net/manual/fr/function.count.php”%5Dcount%5B/url%5D($id) {

$fichier=$id.”.cpt”;

create($fichier);

write($fichier);

}

function create($fichier) {

if(![url=”http://fr.php.net/manual/fr/function.file-exists.php”%5Dfile_exists%5B/url%5D($fichier)) {

$fp=[url=”http://fr.php.net/manual/fr/function.fopen.php”%5Dfopen%5B/url%5D($fichier,”w&#8221;);

[url=”http://fr.php.net/manual/fr/function.fputs.php”%5Dfputs%5B/url%5D($fp,”0&#8243;);

[url=”http://fr.php.net/manual/fr/function.fclose.php”%5Dfclose%5B/url%5D($fp);

}

}

function write($fichier) {

$fp=open($fichier,”r+”);

$hits=[url=”http://fr.php.net/manual/fr/function.fgets.php”%5Dfgets%5B/url%5D($fp,10);

$hits = calculate($hits);

[url=”http://fr.php.net/manual/fr/function.fseek.php”%5Dfseek%5B/url%5D($fp,0);

[url=”http://fr.php.net/manual/fr/function.fputs.php”%5Dfputs%5B/url%5D($fp,$hits);

[url=”http://fr.php.net/manual/fr/function.fclose.php”%5Dfclose%5B/url%5D($fp);

}

function open($fichier, $opt) {

$fp=[url=”http://fr.php.net/manual/fr/function.fopen.php”%5Dfopen%5B/url%5D($fichier, $opt);

return $fp;

}

function calculate($hits) {

return $hits++;

}

  • تجميع أقسام بعض الدوال في دالة واحدة
  • استعمال دوال شبيهة بدل الدالة الأصلية

public Graphe Ajouterlesommet(Object s) {

if(!Recherchersommet(s)) {

Cellgraphe c = new Cellgraphe(s);

listcell.Insererapres(c);

}

return this;

}

مثال1 عن دالة شبيهة

// clone n°1

public Graphe AjouterUnSommet(Object sommet) {

if(!Recherchersommet(sommet)) {

Cellgraphe cell = new Cellgraphe(sommet);

listcell.Insererapres(cell);

}

return this;

}

مثال2 عن دالة شبيهة

// clone n°2

public Graphe AjouterSommet(Object s) {

if(Recherchersommet(s) != 0) {

Cellgraphe c = new Cellgraphe(s);

listcell.Insererapres(c);

}

return this;

}

مثال3 عن دالة شبيهة

/ clone n°3

public Graphe Ajouter1sommet(Object obj) {

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/

كاتب المقال الأصلي  :  Hugo Etiévant

الترجمة: djug

تمت الترجمة في 17/07/2008

لتحميل الدرس على صيغى PDF

http://www.4shared.com/file/96183934/e76d37bb/Obfuscation.html

3 thoughts on “Obfuscation: حماية البرنامج من الهندسة العكسية

التعليقات مغلقة.