السلام عليكم ورحمه الله وبركاته ،،
مقالنا اليوم يتكلم حول طرق سيئه "للغايه" لكتابه الكود بحيث لن يفهم هذا الكود الا القليل ، وهناك بعض الأسباب لعمل مثل هذه البرامج منها مثلا التسليه Fun ، التحدي ، حمايه كود البرنامج ، الفوز بمسابقه ما (هناك مسابقه عالميه تقام كل سنه حول هذا النوع من البرامج International Obfuscated C Code Contest ) .
أغلب هذه البرامج تكون مكتوبه بسي ، أو بيرل ، ونادرا ما نجد سي++ ، وذلك لأن سي تحتوي بعضا من الأمور قد تم تلافيها في سي++ ، منها مثلا استدعاء الداله main من داخل البرنامج .
أحب أن أذكر أن الطرق الموجوده هنا ، لا ينصح باستخدامها أبدا ، فهي طرق سيئه جدا ، وكما ذكرت فقط الغرض منها هو التشويش ...
ولنأخذ مثال "فاتح للشهيه" قبل أن نبدأ بالطرق ، قم بقرائه المثال ، وفي حال فهمت المثال كاملا وعرفت المخرجات ، فسوف أجزم أنك أحد هؤلاء المجانين :lol: .
#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 Arthmatic ، معامل الفاصله 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 بالفطره ، وأعتقد أنه سيفوز بمثل هذه المسابقات بكل سهوله . :lol:
نبدأ الأن بقطع من الكود ، ونحاول أن نغيرها من كود عادي الى مشوش ، وبعدها نأخذ مثال 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); // 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 arthmatic قمنا بالوصول الى عناصر المصفوفه .
#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 .
الأستدعاء الذاتي Recursion :
==================
الجميع يعلم هذا الموضوع ، وهي داله تنادي نفسها في حال تحقق شرط ما ، وهنا أغلب الأحيان نرسل قيمه وهي التي تدل على نهائيه الأستدعاء ، أو نقوم بعمل متغير 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);
}
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
)
;
}جميل أليس كذلك ! :rolleyes:
نبدا الأن .
الخطوه الأولى :
نعمل داله للطباعه ، نرسل اليها المصفوفه ، القيم التي في المصفوفه كتبناها بالنظام العشري .
void myFunction(int array[], int arraySize) {
int i;
for (i=0; i<arraySize; ++i) {
printf("%c", array);
}
}
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 Direcitve ، وفي المرفقات عده برامج من هذا النوع ....
هناك بعض البرامج ، يمكن أن تقوم بكشفها بسرعه مثل :
_(__,___,____){___/__<=1?_(__,___+1,____):!(___%__)?_(__,___+1,0):___%__==___/
__&&!____?(printf("%d\t",___/__),_(__,___+1,0)):___%__>1&&___%__<___/__?_(__,1+
___,____+!(___/__%(___%__))):___<__*__?_(__,___+1,____):0;}main(){_(100,0,0);}وهو يطبع الأعداد الأوليه من 1 الى 100 ، وهنا أولا أستعن بخاصيه Find & Replace الموجوده في المترجم أو أي محرر نصوص ، وغير كل : _ بمثلا 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 ونشاهد من هو صاحب الكود الأكثر تشويشا ؟
أو نضعها في موضوع منفصل ونسميه : ArabTeam Obfuscated C Code Contest :) :) .
تحياتي .

