مقالنا اليوم يتكلم حول طرق سيئه “للغايه” لكتابه الكود بحيث لن يفهم هذا الكود الا القليل ، وهناك بعض الأسباب لعمل مثل هذه البرامج منها مثلا التسليه Fun ، التحدي ، حمايه كود البرنامج ، الفوز بمسابقه ما (هناك مسابقه عالميه تقام كل سنه حول هذا النوع من البرامج International Obfuscated C Code Contest ) .
أغلب هذه البرامج تكون مكتوبه بسي ، أو بيرل ، ونادرا ما نجد سي++ ، وذلك لأن سي تحتوي بعضا من الأمور قد تم تلافيها في سي++ ، منها مثلا استدعاء الداله main من داخل البرنامج .
أحب أن أذكر أن الطرق الموجوده هنا ، لا ينصح باستخدامها أبدا ، فهي طرق سيئه جدا ، وكما ذكرت فقط الغرض منها هو التشويش …
ولنأخذ مثال “فاتح للشهيه” قبل أن نبدأ بالطرق ، قم بقرائه المثال ، وفي حال فهمت المثال كاملا وعرفت المخرجات ، فسوف أجزم أنك أحد هؤلاء المجانين .
#include <stdio.h> main(t,_,a)char *a;{return!0<t?t<3?main(-79,-13,a+main(-87,1-_, main(-86,0,a+1)+a)):1,t<_?main(t+1,_,a):3,main(-94,-27+t,a)&&t==2?_<13? main(2,_+1,"%s %d %d\n"):9:16:t<0?t<-72?main(_,t, "@n'+,#'/*{}w+/w#cdnr/+,{}r/*de}+,/*{*+,/w{%+,/w#q#n+,/#{l,+,/n{n+,/+#n+,/#\ ;#q#n+,/+k#;*+,/'r :'d*'3,}{w+K w'K:'+}e#';dq#'l \ q#'+d'K#!/+k#;q#'r}eKK#}w'r}eKK{nl]'/#;#q#n'){)#}w'){){nl]'/+#n';d}rw' i;# \ ){nl]!/n{n#'; r{#w'r nc{nl]'/#{l,+'K {rw' iK{;[{nl]'/w#q#n'wk nw' \ iwk{KK{nl]!/w{%'l##w#' i; :{nl]'/*{q#'ld;r'}{nlwb!/*de}'c \ ;;{nl'-{}rw]'/+,}##'*}#nc,',#nw]'/+kd'+e}+;#'rdq#w! nr'/ ') }+}{rl#'{n' ')# \ }'+}##(!!/") :t<-50?_==*a?putchar(31[a]):main(-65,_,a+1):main((*a=='/')+t,_,a+1) :0<t?main(2,2,"%s"):*a=='/'||main(0,main(-61,*a, "!ek;dc i@bK'(q)-[w]*%n+r3#l,{}:\nuwloca-O;m .vpbks,fxntdCeghiry"),a+1);}
حاول أن تتخيل ما هي المخرجات قبل أن تقوم بتشغيله ، تخيل فقط !!
كيف تكتب برنامج مشوش ؟
هنا علينا أن نرجع الى بدايه تعلمنا للغه ونتذكر ما هي أصعب الأشياء التي واجهتنا في التعلم ، أذكر أنني جلست كم يوم حتى فهمت ما هو المؤشر Pointer ، مممم أيضا الأستدعاء الذاتي مربك أيضا ، أيضا المصفوفات وخاصه عندما نستخدم Pointer Arithmetic ، معامل الفاصله Comma مربك ايضا لان تنفيذه غريب بعض الشيء ، جميع المواضيع السابقه التي كنت تعتبرها صعبه ، هنا ستكون هي سلاحك الأول
أيضا المتغيرات ذات الشكل المتشابه مثلا الحرف O و الحرف o ( الأول كبير والثاني صغير ) و الرقم 0 ، أيضا الحرف i والحرف l والحرف L الرقم 1 ، هذه المتغيرات تسبب لي التشاؤم من البرنامج ، خاصه عندما كنت أقرأ برنامج أحد الطلاب ، وهذه أحد سطور أحد الطلاب :
char clear,ch,c,bag[100],bag1[10][10],bag2[100]; int n=0,m,x,w=0,aa=0,z,l=0,nn[50],mm[50],qw,az,i,j; // other code for(int fg=1;fg<=az;fg+=2) { if (nn[fg-1]==nn[fg]){ bag2[fg-1]=bag1[nn[fg-1]][(mm[fg-1]+1)%5]; bag2[fg]=bag1[nn[fg-1]][(mm[fg]+1)%5]; } else if(mm[fg-1]==mm[fg]){ bag2[fg-1]=bag1[(nn[fg-1]+1)%5][mm[fg-1]]; bag2[fg]=bag1[(nn[fg]+1)%5][mm[fg]]; } else {bag2[fg-1]=bag1[nn[fg-1]][mm[fg]]; bag2[fg]=bag1[nn[fg]][mm[fg-1]]; }}
يبدوا لي أن هذا الطالب يكتب برنامج Obfuscated بالفطره ، وأعتقد أنه سيفوز بمثل هذه المسابقات بكل سهوله 🙂 .
نبدأ الأن بقطع من الكود ، ونحاول أن نغيرها من كود عادي الى مشوش ، وبعدها نأخذ مثال Hello World ومن ثم نحوله الى Hello Devil .
التلاعب بأسماء المتغيرات :
نأخذ مثال لبرنامج يطبع الحروف من A الى Z ، ونقوم ببعض التغيرات عليه في اسامي المتغيرات :
#include <stdio.h> int main () { for (int i='A' ; i<='Z' ; i++) printf("%c",i); return 0; }
الان قمنا بوضع جمله الطباعه في داله ، ومررنا لها الحرف الأول والأخير ، ومن ثم طبعنا الحروف :
#include <stdio.h> void _(int O , int _O) { for ( ; O<=_O ; O++) printf("%c",O); } int main () { // you can change A with 65 in decimal OR with 0x41 in hex // olse you can change Z to 90 in decimal or 0x5A in hex _(0x41,90); return 0; }
لاحظ استخدام الحروف المشابه O و _O ، أيضا ارسال الحرف A بقيمته في النظام Hex ، ويمكن أستخدام أي نظام أخر .
(طبعا عليك بازاله التعليق من البرنامج ، وهو أصلا موضوع لك أنت ، حيث البرنامج المفترض يشوش القارئ وليس يشرح للقارئ).
التلاعب بالمؤشرات والمصفوفات :
مراجعه سريعه للأمر ، تجدها في هذه السطور :
printf("\n\n%d\n",arr[1]); // print 20 printf("%d\n",*(arr+1)); // print 20 printf("%d\n",*(1+arr)); // print 20 printf("Address = %x\n",&arr[1]); // print Address for element 1 printf("Address = %x\n",arr+1); // print Address for element 1
الأن كيف يمكن أن نتلاعب هنا ، كلنا نعرف أذا أردنا أن نرسل مصفوفه لداله نقوم بارسال عنوانها ، حسنا ، ممكن أن نضع هذا العنوان في متغير int أولا ، ونقوم بارسال هذا العدد (العنوان) ، الان من داخل المصفوفه نقوم بتحويل هذا العدد int الى مؤشر عن طرق الCast .
أنظر الى المثال القادم ، وهو لطباعه الأعداد من 1 الى 10 ، والذي يليه هو بعد التلاعب فيه قليلا :
#include <stdio.h> void functionOne ( int* array , int size ) { for (int i=0 ; i<size; i++) printf("%d ",array[i]); // or *array++ or *(array+i) } int main() { int array[] = {1,2,3,4,5,6,7,8,9,10}; functionOne(array,10); return 0; }
الان النسخه المعدله ، ولاحظ أننا في الداله main قمنا بأخذ عنوان المصفوفه ، وارساله الى الداله functionOne ، وهناك قمنا بعمل cast لهذا الرقم وحولناه الى مؤشر ، بعدها باستخدام pointer arithmetic قمنا بالوصول الى عناصر المصفوفه .
#include <stdio.h> void functionOne ( int address , int size ) { int i=0; for (; i<size; i++) printf("%d ", *((int*)address+i)); } int main() { int array[] = {1,2,3,4,5,6,7,8,9,10}; int address = (int)array ; // (int) &array[0] functionOne(address,10); return 0; }
المعامل الشرطي الثلاثي Conditional Operator ومعامل الفاصله Comma Operator :
معامل الفاصله يعمل من اليسار الى اليمين ، ومن ثم يسند قيمه المتغير الموجود في الجهه اليمني الى المتغير اذا كان هناك. نأخذ مثال :
int a=0; int b=3; a = (b+=3, b-1);
الان عند تنفيذ السطر الثالث ، نقوم بزياده b بمقدار 3 وتصبح 6 ، بعدها نطرح من b واحد وتصبح 5 ، وأخيرا نسند b الى a (اي تصبح قيمه a هي 5 ).
المعامل الشرطي هو بديل لجمله If :
if (<test_condition>) { <variable_name> = <value_if_true>; } else { <variable_name> = <value_if_false>; }
وباستخدام Conditional Operator :
<variable_name> = (<test_condition>) ? (<value_if_true>) : (<value_if_false>);
في حال كان test_condition صحيح سوف ينفذ value_if_true والا value_if_false .
هذا المعامل له دور كبير جدا في هذه النوعيه من البرامج ، حيث يتم استبدال أي جمله شرطيه بهذا المعامل .
وفي حاله كانت هناك nested if ، يمكن استخدام المعامل الشرطي مع معامل الفاصله لكي يؤدي الغرض :
<variable_name> = (<test_condition>) ? (<expression_a1>, ..., <value_if_true>) : (<expression_b1>, ..., <value_if_false>);
مثال :
int a=1; int b=2; int c=3; if (a<0) { b+=3; a=b-1; } else { c+=b; a=c+2; }
الان نحوله الى conditional operator و comma operator :
int a=1; int b, c; a=(b=2, c=3, a<0) ? (b+=3, b-1) : (c+=b, c+2);
وهنا أولا سوف يسند 2 الى b ، بعدها 3 الى c ، بعدها يقارن هل a أصغر من 0 ، في حال كان ذلك ، يزيد على b ثلاثه ، ومن ثم يطرح واحد ، ويرجع القيمه ويسندها الى a . اما اذا كان a أكبر من 0 ، سوف يزيد على c مقدار b وهي 2 ، بعدها يزيد على c 2 ويسند الناتج الى a .
الأستدعاء الذاتي :
الجميع يعلم هذا الموضوع ، وهي داله تنادي نفسها في حال تحقق شرط ما ، وهنا أغلب الأحيان نرسل قيمه وهي التي تدل على نهائيه الأستدعاء ، أو نقوم بعمل متغير static لكن لا يجب أ ن تستخدم هذه الطريقه في حاله استدعيت الداله أكثر من مره .
نأخذ مثال لطباعه الأعداد من 1 – 10 ، وبعدها نحوله الى النداء الذاتي :
#include "stdio.h" int main() { int array[]={0,1,2,3,4,5,6,7,8,9}; int i; for (i=0; i<10; ++i) { printf("%d ",array[i]); } return 0; }
وها هو البرنامج بعد التعديل :
#include "stdio.h" void recursiveFunction(int array, int i, int stop) { printf("%d ",*((int*)array+i)); i++; if (i < stop) { recursiveFunction(array, i, stop); } } int main() { int array[]={0,1,2,3,4,5,6,7,8,9}; recursiveFunction((int)array, 0, 10); return 0; }
الأن نأخذ مثال شامل لـ Hello World ، المثال هو :
#include "stdio.h" int main() { printf("Hello World!"); return 0; }
وبعدها سندخل هذا المثال غرفه العمليات ، للقيام بعمليه تجميل ، ليخرج بالشكل :
#include "stdio.h" _(__,_0,O_,___){(O_<_0)?printf("%c",(O_==2) ||(O_==3)||(O_==9)?___++,O_++,108:*((int*)__ +O_++-___)),_(__,_0, O_, ___):0;}main(){int array []={72,'e',111,040,0127,0x6F,'r',0x64,041 };_(array , 12 , 0 , 0 ) ; }
جميل أليس كذلك !
نبدا الأن .
الخطوه الأولى :
نعمل داله للطباعه ، نرسل اليها المصفوفه ، القيم التي في المصفوفه كتبناها بالنظام العشري .
void myFunction(int array[], int arraySize) { int i; for (i=0; i<arraySize; ++i) { printf("%c", array[i]); } } int main() { int array[]={72,101,108,108,111,32,87,111,114,108,100,33}; myFunction(array,12); return 0; }
الخطوه الثانيه :
استخدمنا فكره ارسال متغير يحمل العنوان (التي شرحناها في الأعلى) وبعدها نقوم بعمل cast للمتغير ونحوله الى مؤشر :
#include "stdio.h" void myFunction(int integer, int arraySize) { int i; for (i=0; i<arraySize; ++i) { printf("%c", *((int*)integer+i)); } } int main() { int array[]={72,101,108,108,111,32,87,111,114,108,100,33}, integer=(int)array; myFunction(integer,12); return 0; }
الخطوه الثالثه :
غيرنا الداله وقمنا بعمل استدعاء ذاتي ، وغيرنا if الى Conditional Operator :
void myFunction(int integer, int arraySize, int i) { (i<arraySize) ? printf("%c", *((int*)integer + i++ )), myFunction(integer, arraySize, i) : 0; } int main() { int array[]={72,101,108,108,111,32,87,111,114,108,100,33}; myFunction((int)array,12, 0); return 0; }
الخطوه الرابعه :
نتخلص من بعض الحروف المكرره في المصفوفه (وهو الحرف L) يتكرر ثلاث مرات 108 ، في الموقع 2 و 3 و 9 :
#include "stdio.h" void myFunction(int integer, int arraySize, int i, int k) { (i<arraySize) ? (i==2)||(i==3)||(i==9) ? k++, i++, printf("%c",108) : printf("%c", *((int*)integer + i++ - k)), myFunction(integer,arraySize, i, k) : 0; } int main() { int array[]= { 72, 101, 111, 32, 87, 111, 114, 100, 33 }; myFunction((int)array, 12, 0, 0); return 0; }
الخطوه الخامسه :
نغير من ترتيب معامل الشرط في الداله :
void myFunction(int integer, int arraySize, int i, int k) { (i<arraySize) ? printf("%c",(i==2)||(i==3)||(i==9) ? k++, i++, 108 : *((int*)integer + i++ - k)), myFunction(integer,arraySize, i, k) : 0; } int main() { int array[]= { 72, 101, 111, 32, 87, 111, 114, 100, 33 }; myFunction((int)array, 12, 0, 0); return 0; }
الخطوه السادسه :
نغير من أسماء المتغيرات في الداله ، ودائما تغير اسماء المتغيرات يأتي في أخر مرحله حتى لا تضيع أنت وتشوش على نفسك اثناء كتابة البرنامج ، أيضا تغيير القيم في المصفوفه وجعلها بعده أنظمه Hex و octal وdecimal :
#include "stdio.h" void myFunction(int __, int _0, int O_, int ___) { (O_<_0) ? printf("%c",(O_==2)||(O_==3)||(O_==9) ? ___++, O_++, 108 : *((int*)__ + O_++ - ___)), myFunction(__,_0, O_, ___) : 0; } int main() { int array[]= { 72, 'e', 111, 040, 0127, 0x6F, 'r', 0x64, 041 }; myFunction((int)array, 12, 0, 0); return 0; }
الخطوه السابعه :
تغير اسم الداله ، حذف القيمه الراجعه ، وسوف يعتبرها المترجم هي int ، أيضا حذف نوع المتغير المرسل في الداله وأيضا سيتعبرها int :
_(__, _0, O_, ___) { (O_<_0) ? printf("%c",(O_==2)||(O_==3)||(O_==9) ? ___++, O_++, 108 : *((int*)__ + O_++ - ___)), _(__,_0, O_, ___) : 0; } main() { int array[]= { 72, 'e', 111, 040, 0127, 0x6F, 'r', 0x64, 041 }; _(array, 12, 0, 0); }
الخطوه الأخير :
نقوم بحذف المسافات بين الجمل ، وضغطها :
#include "stdio.h" _(__,_0,O_,___){(O_<_0)?printf("%c",(O_==2) ||(O_==3)||(O_==9)?___++,O_++,108:*((int*)__ +O_++-___)),_(__,_0, O_, ___):0;}main(){int array []={72,'e',111,040,0127,0x6F,'r',0x64,041 };_(array , 12 , 0 , 0 ) ; }
واوووو …… من كان يصدق أن هذا البرنامج Hello World !
نقطه أخيره
البرنامج السابق هو أسهل برنامج مشوش ، نعم !! هناك بعض البرامج معقده جدا وخاصه عندما يتم استخدام جمل الهاش # أو Preprocessor Directive
هناك بعض البرامج ، يمكن أن تقوم بكشفها بسرعه مثل :
_(__,___,____){___/__<=1?_(__,___+1,____):!(___%__)?_(__,___+1,0):___%__==___/ __&&!____?(printf("%d\t",___/__),_(__,___+1,0)):___%__>1&&___%__<___/__?_(__,1+ ___,____+!(___/__%(___%__))):___<__*__?_(__,___+1,____):0;}main(){_(100,0,0);}
وهو يطبع الأعداد الأوليه من 1 الى 100 ، وهنا أولا أستعن بخاصيه Find & Replace الموجوده في الDE أو في أي محرر نصوص ، وغير كل : _ بمثلا Function ، وبعدها سوف تصبح النتيجه :
Function(__,___,____){___/__<=1?Function(__,___+1,____):!(___%__)?Function(__,___+1,0):___%__==___/ __&&!____?(printf("%d\t",___/__),Function(__,___+1,0)):___%__>1&&___%__<___/__?Function(__,1+ ___,____+!(___/__%(___%__))):___<__*__?Function(__,___+1,____):0;}main(){Function(100,0,0);}
الان غير كل : __ بمثلا varOne وقم بعمل replace All :
Function(varOne,___,____){___/varOne<=1?Function(varOne,___+1,____):!(___%varOne)?Function(varOne,___+1,0):___%varOne==___/ varOne&&!____?(printf("%d\t",___/varOne),Function(varOne,___+1,0)):___%varOne>1&&___%varOne<___/varOne?Function(varOne,1+ ___,____+!(___/varOne%(___%varOne))):___<varOne*varOne?Function(varOne,___+1,____):0;}main(){Function(100,0,0);}
واستمر هكذا الى أن تنتهي من جميع أسماء المتغيرات العجيبه ، وبعدها قم بتنظيم البرنامج ، وهنا ستجد نفسك أمام الكود مباشره !
أخيرا ، لما لا يطرح كل منا كوده الـ Obfuscated ونشاهد من هو صاحب الكود الأكثر تشويشا ؟
راااااااائع !!
أصابني المقال بالدوار @_@ هه….لكن فكرة جميلة وإبداعية
جميل
وين القبلة