• 0
ahmed.o.mohamed

الاستخدامات المتقدمة للدالة system

سؤال

السلام عليكم و رحمة الله و بركاته

 

سنتعرف في هذه المقالة على كيفية استخدام الدالة system في سي++.

لتوضيح أهمية هذه الدالة سنقدم عدداً من الأمثلة التي تعمل في بيئة الويندوز مثل التعامل مع حسابات المستخدمين (User Accounts) و إصلاح إدارة المهام (Task Manager) بالإضافة إلى كيفية إصلاح أداة الريجستري (register).

بعد ذلك, سننتقل إلى المعيار POSIX لنناقش الحالات التي تكون فيها system غير آمنة و قد تؤدي إلى فتح ثغرات لسباب معينة, قبل أن نتحدث عن البدائل المقترحة لهذه لدالة في أنظمة Unix-like.

 

post-219439-0-91409800-1381096889_thumb.

 

لنلق نظرة على الكود التالي :

#include <cstdlib>#include <iostream>int main(int argc, char** argv) {    std::cout << "Apprendre à utiliser la fonction system"<<std::endl;    int value_returned = std::system("PAUSE");    std::cout << "La valeur retournée est : "<<value_returned<< std::endl;    return EXIT_SUCCESS;}

قمنا بالتحقق من القيمة المُعادة من طرف الدالة system هذه المرة, لكن في بقية المقالة لن نقوم بمثل هذا التحقق اختصاراً للوقت و تجبنا للتكرار.

 

عند ترجمة الكود و تنفيذ البرنامج سيتم إظهار الجملة التالية في شاشة الـ console :

Apprendre à utiliser la fonction system

لنعد قليلا إلى السطر السادس :

int value_returned = std::system("PAUSE");

لماذا كتبنا العبارة السابقة  ؟

الهدف من العبارة السابقة هو إيقاف البرنامج حتى نَتَمَكن مِن مُشاهدة مخرجات البرنامج .. فكما تعلم أن الحاسوب بإمكانه القيام بآلاف العمليات الحسابية و المنطقية للدقيقة الواحدة بل أكثر! و نظرا لبطء الإنسان مقارنة مع الكومبيوتر فإننا نحتاج إلى إيقاف البرنامج حتى نشاهد النتائج.

 

ملاحظة:

الأمر PAUSE يعمل في بيئة الـ Windows فقط, إذا كنت تعمل على Linux فيمكنك استخدام الأمر read كالتالي :

read -rsp $'Appuyer sur une touche pour continuer ...\n'

ما هي الدالة  system ؟

تسمح الدالة system للمبرمج بتنفيذ أوامر DOS/Shell في كود سي++.

 

ما هي فائدة هذه الدالة؟

لنفرض أنك تعمل على نظام تشغيل لينكس أو ويندوز و تريد إدخال أوامر DOS/Shell في برنامجك, عندها ستتولى الدالة system هذه المهمة. لكن كيف؟ إليك المثال:

لنفترض أننا نريد مسح شاشة البـرنامج, طبعا بإمكاننا استخدام الدالة clrscr الموجودة في المكتبة conio.h لكننا هذه المرة سنستخدم الدالة system مع الأمر المناسب و هو cls إذا كُنتَ في الويندوز أو clear إذا كُنتَ في لينكس كالتالي:

system("cls");system("clear");

ملاحظة:

conio.h هو أحد الـ header files الخاصة بلغة سي, كان يُستخدم في التسعينيات من القرن الماضي حيث كان يتواجد أساسا في المترجمات التي تعمل في بيئة الويندوز غير أنه لم يرد في الكتاب الأب The C Programming Language و لا ينتمي إلى المكتبة القياسية للغة السي كما أن المعيار POSIX أيضا لا يدعمه.

 

 

ما هي وسائط هذه الدالة ؟

الدالة system تستقبل وسيط واحد عبارة عن الأمر المُراد تنفيذه (system command), الــ prototype الخاص بها هو :

#include<cstdlib>int system(const char* command);

مثلا في المثال الأول قمنا بإدخال الأمر PAUSE الذي يقوم بإظهار الجملة التالية :

Appuyer sur une touche pour continuer ...

حيث يظل البرنامج ينتظر... حتى يضغط المستخدم على زر من لوحة المفاتيح حينها يتم الانتقال إلى الخطوة القادمة وهي:

return 0;

و تعني الخروج من البرنامج.

الآن أصبح بإمكاننا كتابة الكود المكافئ للعبارة الموجودة في السطر السادس من المثال الأول :

std::cout << "Appuyer sur une touche pour continuer ..." << std::endl;std::cin.ignore(std::numeric_limits<int>::max(), '\n');

باختصار:

تلعب الدالة system دور الوسيط بين المبرمج و أوامر الـ console حيث تُعتبر مسؤولة عن تنفيذ أوامر النظام.

لا شك أن الاستخدامات البدائية لهذه الدالة بسيطة جدا لكن مع ذلك, من الضروري جدا الانتباه جيدا أثناء استخدام هذه الدالة و التحقق من الأخطاء التي قد تنتج أثناء استخدامها.

 

من ضمن الملاحظات الهامة التي تجب مراعاتها عند استخدام هذه الدالة :

  • يجب التحقق من أن الـ control processor مُتاح و على أتم الاستعداد.ا (1)
  • بشكل عام, يُؤدي استخدام الـ system functions إلى كود لا يتوافق مع مختلف المنصات لأن أوامر النظام تختلف من نظام لآخر و قد تختلف أيضا في نفس النسخة من نفس النظام.ا (2)
  • في الأصل, لا تُتيح مثل هذه الدوال تنفيذ عدة أوامر في آن واحد.
  • القيمة المُعادة من طرف الدالة ليست سوى تلك التي أعادها الأمر الذي تم تنفيذه, أيضا القيمة المُعادة تختلف باختلاف الـ implementation.
  • لا تسمح الدالة system بالتأكد من أن الأمر المُراد تنفيذه تم تنفيذه بنجاح أو لا.
  • أثناء تنفيذ الأمر, يتم تعطيل SIGCHLD و تجاهل كلا من SIGINT و SIGQUIT. ا (3)
  • لا تسمح system بفصل الـ controller عن الـ view إذا أردنا العمل وفقا لـ MVC.ا (4)
  • احذر من استخدام system في برنامج بصلاحيات set-user-ID أو set-group-ID.ا (5)

توضيحات :

  • (1) : باستخدام system(NULL)l : إذا كان الوسيط المُمرر عبارة عن مؤشر NULL, تقوم الدالة system بالتحقق من استعداد معالج الأوامر (control processor) دون أن تستدعي أي أمر آخر للتنفيذ.
  • (2) : على سبيل المثال, يمكن للمستخدم إذا كان root تغيير كافة أوامر النظام بأخرى أو تعريف alias جديدة.
  • (3) : SIGCHLD, SIGINT و SIGQUIT عبارة عن مجموعة signals مُعرفة في الملف signal.h. هذه الـ signals أو الإشارات توجد في الأنظمة التي تدعم المعيار POSIX.
  • (4) : على سبيل المثال, سيتم الإنتقال مباشرة من الـ execution أو التنفيذ (controller) إلى إظهار الناتج (view) دون إمكانية تغيير الأخير أو تغييره بشكل محدود جداً.
  • (5) : أي برنامج يعمل بصلاحيات set-user-ID , من المُحتمل جداً أن يكون سبب في التعرض لهجمات معينة أو الوقوع في ثغرات خصوصا إذا كان البرنامج يتفاعل (و لو يسيراً) مع مستخدم يختلف عن الذي قام ببرمجته و بالتالي يجب دائما أخذ مثل هذه الإعتبارات عند التعامل مع برنامج بصلاحيات مرتفعة.

 

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

 

المثال الأول:

اكتب برنامج يُظهر كلا من الوقت و التاريخ الحاليين مع إمكانية تغيـيـرهما.

في هذا المثال سنستعمل الأمرين time و date, الأمر الأول مسؤول عن الوقت أما الثاني فمسؤول عن التاريخ و الكود الآتي يوضح ما سبق:

#include <cstdlib>#include <iostream>using namespace std;int main(int argc, char** argv) {    system("TIME");    system("DATE");    system("CLS");    return EXIT_SUCCESS;}

أما إذا أردنا عدم تغيير الوقت فيمكننا استخدام الــ switch المخصص وهو /t هكذا:

system("TIME /t");

ملاحظة:

السي++ القياسية لا تحتوي على دوال لمعالجة الوقت و التاريخ لكن مع ذلك, إضافة إلى الدالة system توجد عدة طرق أخرى لمعرفة الوقت و التاريخ, نذكر منها على سبيل المثال (1) :

  • الدالة time الموجودة في الملف الرأسي ctime.
  • الدالة GetSystemTime الموجودة في الملف الرأسي windows.h.
  • الفئتين CTime و CTimeSpan بالنسبة لمبرمجي Visual CPP و MFC.
  • الفئتين TDate و TDateTime بالنسبة لمبرمجي C++ Builder و VCL.
  • المكتبة boost::date_time بالنسبة لمن يريد الحفاظ على الــ code portability.

 

المثال الثاني:

اكتب برنامج يُظهر أربع خيارات للمستخدم على الشكل التالي:

  1. إيقاف تشغيل الجهاز
  2. إعادة التشغيل
  3. إنهاء جلسة المستخدم
  4. الخروج من البرنامج

وعند الضغط على 1 يتم إيقاف الكومبيوتر, 2 تتم إعادة التشغيل و هكذا بالنسبة لبقية الأوامـر..

في هذا المثال سنحتاج إلى الأوامر التالية :

SHUTDOWN /S /T 00SHUTDOWN /R /T 00SHUTDOWN /LEXIT

ماهو الأمر SHUTDOWN ؟

الأمر SHUTDOWN يسمح بالانتقال بين مختلف حالات نظام التشغيل (shutdown, restart, standby, hibernate) و يملك العديد من الــ switches منها على سبيل المثال:

  • /i : التعامل مع مختلف وضعيات نظام التشغيل باستخدام واجهة رسومية.
  • /l : اغلاق جلسة المستخدم.
  • /s : ايقاف نظام التشغيل.
  • /r : إعادة التشغيل.
  • /h : وضع في حالة hibernate
  • /t xxx : تحديد الفترة الزمنية قبل تنفيذ الأمـر حيث تُحسب هذه الفترة بالثواني.
  • /a : الغاء ايقاف الجهاز.
  • /f : يُجبر جميع التطبيقات القيد التشغيل على التوقف و يقوم بإغلاق الجهاز بشكل فوري دون تحذير المستخدمين.

لمعرفة المزيد استعمل الأمر SHUTDOWN /HELP.

 

لم يبقى إلا الأمر EXIT و أعتقد أن الجميع يعرفه. الآن صار بإمكاننا كتابة الكود:

 #include <cstdlib>#include <iostream>using namespace std;int main(int argc, char** argv) {    char choix;    cout <<endl <<"\t\t***************************************"<<endl    <<endl <<"\t\t ******* La commande SHUTDOWN ******* "<<endl    <<endl <<"\t\t***************************************"<<endl;    cout <<endl <<"1 - Arreter l'ordinateur."<<endl            <<endl <<"2 - Redermarrer l'ordinateur."<<endl            <<endl <<"3 - Fermer la session."<<endl            <<endl <<"1 - Quitter le programme."<<endl;    cout <<endl <<"Donnez votre choix : "<<endl;    cin >>choix;    switch (choix) {        case '1':system("SHUTDOWN /S /T 00");            break;        case '2':system("SHUTDOWN /R /T 00");            break;        case '3':system("SHUTDOWN /L");            break;        case '4':system("EXIT");            break;        default:cout <<"Erreur, choix incorrect !"<<endl;    }    return EXIT_SUCCESS;}

الآن سنقوم بإضافة بسيطة للكود السابق... وهي تغيير لون الكتابة و لون الخلفية أيضا. سنستخدم الدالة system و بالضبط مع الأمر color  الذي يملك وسيطين يُمثلان لون الخلفية و لون الكتابة على التوالي, هذين الوسيطين يُكتبان على شكل ست عشري أو Hexadecimal و الجدول التالي يوضح كل رقم مع اللون الذي يقابله :

 

post-219439-0-42550300-1381096909.jpg

طيب الآن سنقوم بإضافة السطر:

system("COLOR 2E");

 ليصبح الكود هكذا:

char choix;system("COLOR 2E");cout <<endl <<"\t\t***************************************"<<endl<<endl <<"\t\t ******* La commande SHUTDOWN ******* "<<endl<<endl <<"\t\t***************************************"<<endl;

بالنسبة لي, اخترت اللونين الأصفر و الأخضر لأنهما يمثلان علم دولتي: موريتانيا و أنت يمكنك اختيار ما يعجبك, فقط راجع الجدول. و هذه واجهة البرنامج :

post-219439-0-51087000-1381096924_thumb.

 

ملاحظة:

في معظم الأحيان ستلاحظ أن استخدام الدالة system لتلوين الواجهة غير مجدي لأنها تسمح بظهور لون واحد فقط و بالتالي باستخدامها لا يمكنك تلوين عدة أسطر بألوان مختلفة. لفعل هذا الشيء يمكنك استخدام الدالة SetConsoleTextAttribute الموجودة في المكتبة windows.h و تأخذ وسيطين, الأول هو الــ HANDLE و الثاني هو الرقم الست عشري الذي يمثل اللون. و هذا مثال بسيط على استدعاء الدالة:

SetConsoleTextAttribute(GetStHandle(STD_OUTPUT_HANDLE), 14);

المثال الثالث :

اكتب برنامج يقوم بتغيير باسوورد المستخدم.

في هذا المثال سنحتاج إلى الأمر net user و هذا الأمر في الحقيقة هو أحد وسائط الأمر net لاي يسمح بإرسال أوامر  عن طريقة الشبكة مع العلم أن الأمر NET يملك العديد من subcommands منها:

  • NET SEND : لارسال رسالة إلى جهاز عن بعد.
  • NET START  لتشغيل خدمة.
  • NETSTAT  : تعرض اتصالات الشبكة الواردة والصادرة و جداول التوجيه وعدد من الاحصاءات الأخرى.
  • NET TIME  : عرض الوقت و التاريخ الحالي حسب توقيت الــ time server domain.
  • NET FILE  : اغلاق ملف تم اشتراكه على الشبكة و تحرير مصادر النظام التي كان يحجزها.

لنعد الآن إلى الأمر NET USER الذي يسمح بالتعامل مع حسابات المستخدمين في الويندوز. هذه بعض الــ switches الاكثر استعمالا :

  • /ADD : اضافة حساب جديد
  • /DELETE : حذف حساب معين
  • ACTIVE:{YES | NO  : تعطيل أو تفعيل الحساب
  • EXPIRES:date | NEVER  : امكانية تحديد تاريخ انتهاء صلاحية الحساب.
  • PASSWORDCHG:YES | NO  : تحديد ما إذا كان المستخدم يمكنه تغيير كلمة المرور الخاصة به.
  • PASSWORDREQ:YES | NO  : تحديد ما إذا كان المستخدم يمكنه تعريف كلمة مرور لحسابه أو لا.
  • LOGONPASSWORDCHG:YES|NO  : هل يجب يجب على المستخدم تغيير كلمة مروره عند تسجيل الدخول القادم.

للمزيد من المعلومات أدخل الأمر NET USER /HELP.

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

NET USER [user_name] [password]

في هذه الحالة فإن الأمر يستقبل واسطين الأول هو اسم الحساب و الثاني هو كلمة المرور الجديدة. الآن صار بإمكاننا الآن تغيير الباسوورد.. كما في الكود الآتي:

#include <cstdlib>#include <iostream>using namespace std;int main(int argc, char** argv) {    system("USER NET %USERNAME% 123456");    return EXIT_SUCCESS;}

ملاحظة:

الحصول على system error 5 عند تنفيذ الكود يعني أنك لا تملك الصلاحيات الكافية لتنفيذ الأمر NET USER من أجل تغيير كلمة المرور لذا قم بتشغيل الملف التنفيذي كــ administrator.

 

المثال الرابع:

معظم الفيروسات الصغيرة المنتشرة الآن تقوم بتعطيل إدارة المهمام حتى لا يتمكن المستخدم من الوصول إلى الــ process الخاص بالفيروس و توقيفه, نفس الشيء يحدث مع registry خوفا من حذف القيمة التي تُمكن الفيروس من العمل عند اقلاع الجهاز. طبعا توجد فيروسات أكثر ذكاء حيث تقوم بإخفاء الــ process من إدارة المهام و بالتالي قد يصعب التعامل معها يدويا في هذه الحالة.

في هذا المثال, سنقوم بكتابة كود يقوم بتفعي أو تعطيل Task Manager و registry و الهدف من هذا المثال هو امكانية اصلاح هذه الأدوات عند تعرضها للهجوم الذي ذكرناه سابقا.

الأمر الذي يُمكن من اصلاح ادارة المهام هو:

REG ADD hkcu\Software\Microsoft\Windows\CurrentVersion\policies\system /v DisableTaklMgr /t reg_dword /d 1

الأمر REG ADD  يمكن من إضافة مفتاح إلى الرجيسرتي.

  • hkcu\Software\Microsoft\Windows\CurrentVersion\policies\system  يُمثل مسار المفتاح المُضاف.
  • /v Disable TaskMgr   : اسم المفتاح.
  • /t reg_dword  : نوع المفتاح.
  • /d 1   : فيمة المفتاح (1 تعني تعطيل و 0 تعني تفعيل)

و هذا هو الكود :

#include <cstdlib>using namespace std;int main(int argc, char** argv) {system("REG ADD hkcu\\Software\\Microsoft\\Windows\\CurrentVersion"    "\\policies\\system /v DisableTaklMgr /t reg_dword /d 1");    system("PAUSE");    return EXIT_SUCCESS;}

  نفس الشيء بالنسبة للرجيستري:

#include <cstdlib>using namespace std;int main(int argc, char** argv) {    system("REG ADD hkcu\\Software\\Microsoft\\Windows\\CurrentVersion"    "\\policies\\system /v DisableRegistryTools /t reg_dword /d 0");    system("PAUSE");    return EXIT_SUCCESS;}

خدعة:

باستخدام الأمرين  REG ADD و SHUTDOWN  معا يمكننا إنشاء فيروس خطير و مزعج. كالتالي:

  • نقوم بإنشاء ملف BATCH لإعادة تشغيل الجهاز باستخدام الأمر SHUTDOWN  .
  • ننشئ ملف BATCH آخر لإضافة الملف السابق إلى الرجيستري من أجل تشغيله عند اقلاع الجهاز.
  • نحذف الملف السابق حتى لا نترك أثرا للفيروس.
  • نستخدم الأداة Iexpress لدمج الملفين في ملف واحد تنفيذي.

 

خطر استخدام الدالة system في المعيار POSIX:

لا شك أن جميع مستخدمي أنظمة لينكس يعرفون جيدا هذه الدالة, لكن على عكس عائلة exec (مثل execve()) تستخدم الدالة system الـ environment variables من أجل البحث عن الملف المُراد تنفيذه, أيضا, system تعتمد خصوصا على $PATH الذي يحتوي على لائحة المجلدات التي تحتوي الملفات التي يمكن استخدامها دون تحديد المسار الكامل للملف.(2)

على سبيل المثال, PATH يحتوي على /bin و بالتالي إذا اردت تنفيذ الامر /bin/commande يمكنك كتابة ما يلي داخل الـ Shell :

$ commande

لتنفيذ أمر أو استدعاء برنامج سي++, يُنصح دائما باستخدام أحد افراد العائلة exec لأن الدالة system تستخدم الــ environment variables لتحديد مسار الملف المُراد تنفيذه, السبب في الثغرة يعود إلى أن هذه المتغيرات يمكن للمستخدم تغييرها !

على سبيل المثال إذا كان لدينا كود يحوي الأمر التالي:

system("cat /etc/passwd");

يمكننا ببساطة تغيير الــ PATH بحيث يتم تحويل استدعاءات النظام إلى المسار الجديد:

export PATH=/home/snacker:$PATH

 في هذه الحالة, سيقوم الشل بالبحث عن الملف cat داخل المجلد post-219439-0-40713100-1381096937.png

لآن سنقوم بإنشاء برنامج ثاني يقوم باستدعاء cat الموجود داخل المجلد post-219439-0-40713100-1381096937.png  البرنامج الجديد يحتوي على الأمر التالي :

std::cout << "La fonction system est vulnérable"<<std::endl;

 نقوم بالترجمة post-219439-0-66439600-1381096979.png و التنفيذ post-219439-0-30563400-1381096965.png

مخرجات الكود ستكون La fonction system est vulnérable بدلا من محتوى الملف post-219439-0-04760700-1381096952.png و بالتالي نلاحظ جيدا أن الدالة system  قامت بتنفيذ cat الموجود في في الــ PATH الجديد.

 

عائلة exec و الدالة system, أيهما أفضل ؟

يقوم أعضاء هذه العائلة بعمل covering للـ current process بمعنى أنه يتم استبدال الـ process الذي قام بعملية الإستدعاء بآخر دون تغيير الـ PID. الوسيط الأول لكافة أفراد هذه العائلة عادة ما يكون مسار الملف المُراد تنفيبذه. (3)

تحتوي العائلة exec على مجموعة من الدوال تختلف باختلاف طبيعة عملها :

#include <unistd.h> extern char ** environ;int execl (const char * chemin, const char * arg, ...);int execlp (const char * fichier, const char * arg, ...);int execle (const char * chemin, const char * arg , ..., char * const envp[]);int execv (const char * chemin, char * const argv []);int execvp (const char * fichier, char * const argv []);int execve (const char * chemin, char * const argv [], char * const envp[]);

تستقبل دوال execl اسم البرنامج الذي سيتم تنفيذه كوسيط أول بينما يكون الوسيط الثاني : الأمر المُراد تنفيذه ثم تأتي بعد ذلك مجموعة الـ arguments التابعة لهذا الأخير و تنتهي بـ NULL. الوسيط الأول و الثاني يجب أن يكونا متطابقين بالنسبة لدوال execl.

مثال :

execl("/usr/bin/ls", "/usr/bin/ls", "-l", NULL);

أما بالنسبة لـ execv و execvp فيستخدمان مصفوفة مؤشرات تنتهي بـ NULL و ينتهي كل مؤشر فيها بالمحرف NUL. هذه المصفوفة تُمثل الوسائط الممكنة للأمر المُراد تنفيذه.

اتُفق على أن الوسيط الأول يُشير دائما إلى مسار الملف أو البرنامج الذي سيتم استدعاءه.

عندما يُعيد أحد أفراد exec قيمة معينة, فهذا يعني أنه قد حدث خطأ ما. عادة ما تكون القيمة المُعادة -1 حيث تحتوي errno على الكود المعاد من طرف الدالة.

 

بقي أن نذكر أن عائلة exec خاصة بأنظمة Unix-like, في الويندوز لا أعلم تحديدا ماالذي قد تُعطيه, لأنني أعتقد أنه لا يمكننا ملائمة عمل أفراد هذه العائلة في منصة الـ Windows نظرا إلى أن الـ managing processes تختلف بشكل كبير جداً.

إذا كنتَ لا ترغب في استخدام الدالة system في الـ Windows فيمكنك استدعاء الدالة createProcess. (في الحقيقة, فإن system تستدعي createProcess في بيئة الـ Windows)

 

أخيرا, عند استدعاء system سيتم اتباع الخطوات التالية : (للأسف, كتتبُتها بالفرنسية لأنني لم أجد ترجمة دقيقة للكلمات التي وضعتُها في المخطط)

 

post-219439-0-57383400-1383242713_thumb.

 

بينما يتم اتباع الخطوات الآتية عند استدعاء fork+exec :

 

post-219439-0-97627200-1383242693_thumb.

 

 

مالحل ؟

ليس من السهل ايجاد بديل حقيقي للدالة system, أحد الحلول الممكنة هو استخدام execl أو execle مع العلم أن طريقة العمل تختلف بشكل كامل لأن الملف المُستدعى لن يتم تنفيذه كــ subroutine و لكن سيحل محل الــ running processes. ا (4)

بشكل عام, يُنصح باستخدام المكتبات التي توفرها سي++ إن وُجدت و إلا فيمكن استخدام أحد أفراد العائلة exec خصوصا في الأنظمة التي تدعم POSIX و system في حالات معدودة و مُحددة.

 

 

المراجع

 

(1) Comment gérer les dates et les heures en CPP

(2) La faille de system

(3) Man page 3 : exec

(4) Éviter les failles de sécurité dès le développement d'une application

 

أرجو أن أكون قد وُفقت في الشرح و أرجو لكم الاستفادة أيضا و لا تنسوني من صالح دعائكم.

 

تحياتي.

تم تعديل بواسطه Snack3r
تحديث المقالة.
7

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

21 إجابة على هذا السؤال .

  • 0

وعليكم السلام ورحمة الله تعالى وركاته،

 

درس رائع ومفيد، لا حرمنا الله منك أخي العزيز

جزاك الله كل خير

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

جزاك الله خيرا , اسلوبك فعلا اكثر من رائع , مشكور اخي

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

شكرا على المقال

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

Merci à tous

 

je vous invite à lire l'article en ligne sur ma page personnelle DVP : Apprendre à utiliser la fonction system


Bonne lecture

تم تعديل بواسطه Snack3r
1

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

مبارك :) , بالمناسبة تنسيق المقالة هنا أجمل من DVP .. وفقك الله وسدد خطاك ..

مقالة رائعة بحق ,

ولدي بعض الأسئلة :

1- هل استعمال system باتجاه واحد أم باتجاهين  ؟

وأقصد : يمكننا ان نرسل أمراً , ولكن هل يمكننا معرفة نتيجة هذا الأمر ؟

 

وهناك مشكلة كبيرة في التعامل مع Windows 7 وهي مشكلة الصلاحيات .. فمن لا يستخدم صلاحيات الـAdministrator لا يمكنه تطبيق معظم الأمثلة .. مثل تغيير كلمة السر للمستخدمين وأيضا التعامل مع الريجستري ..

2- فكيف يمكن تفادي هذه المشكلة .. والتي تجعل أي تعامل مع system غير مسموحاً به Access is Denied ؟

3- عندما نرسل أمر للنظام فهو يستخدم دوال الـAPI الخاصة به .. أليس كذلك ؟ وبالتالي فالعمليتان متشابهتان بالمضمون سواء system أو استدعاء API فهل هناك اختلاف جوهري ؟ يعني هل هناك ما يمكن القيام به بـsystem  ولا يمكن القيام به بـ API ?

 

هناك المزيد من الأسئلة ولكن أكتفي بذلك للآن ..

بارك الله فيك :)

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

1- هل استعمال system باتجاه واحد أم باتجاهين  ؟

سيتم تنفيذ الأمر و سيعاد لك error code.

 

2- فكيف يمكن تفادي هذه المشكلة .. والتي تجعل أي تعامل مع system غير مسموحاً به Access is Denied ؟

تحتاج لتعديل manifest الخاص ببرنامجك، إقرأ هذا الموضوع.

 

3- عندما نرسل أمر للنظام فهو يستخدم دوال الـAPI الخاصة به .. أليس كذلك ؟ وبالتالي فالعمليتان متشابهتان بالمضمون سواء system أو استدعاء API فهل هناك اختلاف جوهري ؟

لا الدالة system تستدعى برنامج موجود داخل النظام او أمر مدمج داخل الـ shell فمثلا إذا استدعيت cls من داخل برنامج GUI لن تعمل و ذلك لعدم وجود active shell.

 

يعني هل هناك ما يمكن القيام به بـsystem  ولا يمكن القيام به بـ API ?

لا، و العكس هو الصحيح.

 

 

و الله ولي التوفيق

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

بالمناسبة تنسيق المقالة هنا أجمل من DVP ..

 

فريق الكتابة هناك يعتمد طريقة موحدة لكتابة أي مقالة, حيث نستخدم الأداة KitOOoDVP في OpenOffice لكتابة المقالات و Nano لرفعها (التعامل مع المقالات قبل الرفع يكون عن طريق XML فقط) لذا تجد أن التعامل مع الأدوات التي يُتيحها المنتدى سيكون أسهل و أجمل بطبيعة الحال :)

 

هل استعمال system باتجاه واحد أم باتجاهين  ؟

وأقصد : يمكننا ان نرسل أمراً , ولكن هل يمكننا معرفة نتيجة هذا الأمر ؟

 

  • القيمة المُعادة من طرف الدالة ليست سوى تلك التي أعادها الأمر الذي تم تنفيذه, أيضا القيمة المُعادة تختلف باختلاف الـ implementation.
  • لا تسمح الدالة system بالتأكد من أن الأمر المُراد تنفيذه تم تنفيذه بنجاح أو لا.

 

وهناك مشكلة كبيرة في التعامل مع Windows 7 وهي مشكلة الصلاحيات .. فمن لا يستخدم صلاحيات الـAdministrator لا يمكنه تطبيق معظم الأمثلة .. مثل تغيير كلمة السر للمستخدمين وأيضا التعامل مع الريجستري ..

 

كما أخبرك الأخ محمد, استخدم ملف الـ manifest.

 

 

عندما نرسل أمر للنظام فهو يستخدم دوال الـAPI الخاصة به .. أليس كذلك ؟ وبالتالي فالعمليتان متشابهتان بالمضمون سواء system أو استدعاء API فهل هناك اختلاف جوهري ؟ يعني هل هناك ما يمكن القيام به بـsystem  ولا يمكن القيام به بـ API ؟

 

في بيئة الـ Windows, تقوم الدالة system باستدعاء createProcess لذا فالـ API دائما هي التي عادة ما تتحكم في سير برنامجك.

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

شكرا لك أخي C++er

سيتم تنفيذ الأمر و سيعاد لك error code

1- إذا فالمعلومات المعادة محدودة بـ "تمت" , "لم تتم" , "بالانتظار " وليس أكثر من ذلك .. أي لا يوجد تواصل stream مثلاً ..

 

4- هناك سؤال آخر هام :

كيف يمكنني إعطاء عدة تعليمات .. أظن أن الجواب بكتابة عدة استدعاءات لــsystem في كل استدعاء أمر واحد (سطر واحد ) أي أنه لا يمكن تمرير سوى سطر واحد في كل استدعاء ولن تفيدنا n\ ..أليس كذلك ؟

 

5- مشكلة أخرى ... وهي بعد استدعاء system لبرنامج معيّن على نفس الــshell .. فسيتوقّف البرنامج بانتظار هذا الأخير ..

فمثلاً لو كتبنا :

system("debug");    system("a");    system("mov ah,2");    system("mov dl,41");    system("int 21");    system("ret");    system("");    system("g");

فسيتم تشغيل البرنامج debug.exe وسيتوقّف البرنامج عن تمرير الأوامر .. لحين الخروج من debug.exe .. ولن تنفعنا system  في التعامل معه .. هل هذا صحيح ؟

 

3-


فمثلا إذا استدعيت cls من داخل برنامج GUI لن تعمل و ذلك لعدم وجود active shell.

ألا يسبب هذا إنشاء shell جديدة خصيصاً لتنفيذ الأمر ؟

 

2-


تحتاج لتعديل manifest الخاص ببرنامجك

شكراً .. هذا سيؤدي إلى الطلب من المستخدم أن يوافق على الصلاحيات .. (ولا أظن أن هناك طريقة تجعل البرنامج admin بدون اذن المستخدم في Win7 ...  هذه ميزة قوية فيه )

 

3-


يعني هل هناك ما يمكن القيام به بـsystem  ولا يمكن القيام به بـ API ?

لا، و العكس هو الصحيح.

دائماً يكون جوابي بعد مثل هذه العبارة : إذا لماذا نستخدمها :D ربما هي أسهل ولكن في النهاية .. API  هو الأصل وطالما أن تعليمات كل نظام تختلف بينها .. فالأولى تعلّم API على تعلم الــ shell .

 

6- آخر سؤال :) : عندما نكتب برنامج عادي على الــconsole ثم نستخدم system فإنها تستعمل console البرنامج نفسها ثم ترجع القيادة للبرنامج .. ولكن : ماذا لو قمنا بإنشاء shell  جديدة مثلاً بالامر start .. كيف يمكننا نقل الأوامر التالية إلى الـshell الجديدة ؟

سؤالي يتّضح من الكود التالي :

system("start");Sleep(2000);system("color 0f");    system("start");Sleep(2000);system("color a0");    system("start");Sleep(2000);system("color 12");

 

وشكراً لكم (وعذراً للإطالة )

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

شكرا لك أخي C++er

1- إذا فالمعلومات المعادة محدودة بـ "تمت" , "لم تتم" , "بالانتظار " وليس أكثر من ذلك .. أي لا يوجد تواصل stream مثلاً ..

 

القيمة التي تعيدها system قد لاتحمل أي معنى، هذا الرقم هو نفسه الرقم الذي تعيده دالة main في البرنامج، مثلاً لو كتبت:

/* file2.c -> file2.exe */int main(int argc, char **argv) {  return 123;  // هناك دالة دمجها المصرف وتستدعي الدالة الرئيسية، ستقرأ القيمة التي تعيدها وتمررها لـ  // ExitProcess(main());}

واستدعيت البرنامج file2.exe باستخدام system:

#include <stdio.h>#include <stdlib.h>int main(int argc, char **argv) {  printf("%d", system("file2.exe"));  return 0;}

ستطبع:

123

يعني الرقم يمكن أن يكون اي شيء.

 

system ستستدعي سطر الأوامر (cmd.exe) سواءً الذي تعمل عليه أو ستنشيء عملية جديدة إذا كنت تستخدم برنامج رسومي، سيشغل البرنامج ويقرأ القيمة المعادة، ستخرج cmd.exe بنفس القيمة وستقرأ system هذه القيمة.

 

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

1

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0
ألا يسبب هذا إنشاء shell جديدة خصيصاً لتنفيذ الأمر ؟

تعتمد على نوع الأمر و الـ implemenation الخاص بـ system فمثلا قد تحتوى على قائمة بالأوامر الداخلية و عندما تطلب تنفيذ احداها تقوم بالتحقق من وجود active console window فإن لم يوجد لن يتم تنفيذه.

 

دائماً يكون جوابي بعد مثل هذه العبارة : إذا لماذا نستخدمها :D ربما هي أسهل ولكن في النهاية .. API  هو الأصل وطالما أن تعليمات كل نظام تختلف بينها .. فالأولى تعلّم API على تعلم الــ shell .

لنفس سبب عدم إعادة كتابتك لبعض دوال الـ API بدلا من استخدامهم مع العلم ان اغلب هذه الدوال التى تستخدمها هى wrapper لدوال أخرى، و لكننا نستخدمهم لأنهم يقومموا بالوظيفة التى نريدها منهم على افضل صورة تجعل إيجاد بديل لهم مجرد إضاعة للوقت.

 

 

6- آخر سؤال :) : عندما نكتب برنامج عادي على الــconsole ثم نستخدم system فإنها تستعمل console البرنامج نفسها ثم ترجع القيادة للبرنامج .. ولكن : ماذا لو قمنا بإنشاء shell  جديدة مثلاً بالامر start .. كيف يمكننا نقل الأوامر التالية إلى الـshell الجديدة ؟

الأمر start هو أمر داخلي يقوم بتشغيل نسخة جديدة من cmd.exe و لا تستطيع ان تتحكم بها.

 

 

و الله ولي التوفيق

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

@Mr.B


القيمة التي تعيدها system قد لاتحمل أي معنى، هذا الرقم هو نفسه الرقم الذي تعيده دالة main في البرنامج

معلومة هامّة شكراً لك ..


Mr.B

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

C++er

الأمر start هو أمر داخلي يقوم بتشغيل نسخة جديدة من cmd.exe و لا تستطيع ان تتحكم بها.

مشكلة الـinterfacing بين الأدوات :( ..

أظن أن الهدف الرئيسي منها قد زال مع الزمن بوجود التحكم بكل شيء عن طريق الواجهات الآن والله أعلم .. استخدامها فعّال في حال "غياب الواجهات" في نظام التشغيل "للمستخدم وليس للمبرمج" .. هذا ما فهمته من النقاش ..

 

شكرا لكما

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

بالنسبة للدالة System فكل ماتقوم به ببساطة شديدة هو إستدعاء الدالة CreateProcess ()، لاأذكر بالضبط الطريقة التي تستخدمها، لكنني أتذكر أنها ترسل الأمر الذي تكتبه للدالة System كـ CommandLine الى Cmd.exe بآستخدام أحد السويتشات، بالطبع يمكنك ببساطة الإستغناء عن الدالة System وآستخدام CreateProcess، دون أن تحدث أي فرق بالبرنامج، لكن يكون من الجيد إستخدام System في حال أردت إرسال أو تنفيذ العديد من أوامر النظام كـ netsh مثلاً دون الحاجة الى القلق حول بقية الإعدادات كـ StartupInfo و SecurityAttribute الخ ..

 

أما بالنسبة للـ UAC فلا أظن أن المنتدى يسمح بمناقشة طرق تجاوز الحمايات، لكن يمكنك أن تستخدم الـ manifest ..

 

يمكنك أن تقول أو تجزم بكل ثقة ;)  أن System () ليست سوى واجهة لـ CreateProcess ()

تم تعديل بواسطه zirek99i
1

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

يمكنك أن تقول أو تجزم بكل ثقة ;)  أن System () ليست سوى واجهة لـ CreateProcess ()

 

هذا في الـ windows طبعاً :)

لأن المقالة تتحدث عن الدالة system في Windows و Linux معاً.

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

نعم بالضبط .. يمكن أن نقول أن المقالة تتحدث عن الأوامر المتقدمة لـ dos بشكل رئيسي و shell ومقارنة بين عائلة exec و الدالة system

لا يزال عندي أسئلة فلم أنته من قراءة المقالة بعد :p

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0
لا يزال عندي أسئلة فلم أنته من قراءة المقالة بعد :P

 

 

جيد, انتهز الفرصة إذا بوجود الأساتذة معنا ليوضحوا لك أكثر :)

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

هذا في الـ windows طبعاً :)

لأن المقالة تتحدث عن الدالة system في Windows و Linux معاً.

 

ليس صحيحاً تماما، فالأمر سيان بالويندوز واللينكس، فالدالة System () تقوم بإرسال الأمر الذي تكتبه بها الى الـ Shell الذي يستخدمه نظام التشغيل:

 

بالنسبة للويندوز

 

7b03.jpg

 

 

بالنسبة للينكس تقوم System بمجموعة من الخطوات لتفادي الـ Race Condition، حيث تقوم بآستدعاء fork () ثم تستدعي الدالة execl () كالتالي، في هذه الحالة قمت فقط بآستخدام الكومند ls -l:

execl("/bin/sh", "sh", "-c", command, (char *) NULL);
تم تعديل بواسطه zirek99i
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

ليس صحيحاً تماما، فالأمر سيان بالويندوز واللينكس

 

 

لعلك لم تفهم ما أقصده :)

قلتُ أن system تستدعي CreateProcess فقط في الـ Windows و قد ذكرتُ هذا في المقال :

 

إذا كنتَ لا ترغب في استخدام الدالة system في الـ Windows فيمكنك استدعاء الدالة createProcess. (في الحقيقة, فإن system تستدعي createProcess في بيئة الـ Windows)

 

 

 

 

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

أنهيت المقالة بارك الله فيك , يمكنني تمييز أسلوبين مختلفين في المقالة ;)

hkcu\Software\Microsoft\Windows\CurrentVersion\policies\system
غير موجود عندي , أستعمل Win7 . (نسخة غير مرخصة طبعاً :D )
وكذلك بالنسبة للأمرين
NET SEND // مذكور في المقالnetch // مذكور في الردود

 سؤال أخير :
هل العبارة التالية صحيحة :
جميع أوامر Dos لها بديل رسومي في Windows .. ولكن MS-DOS غير مصمم للتحكم بجميع إمكانات الكيان الصلب أصلاً .

وشكرا لكم جميعاً ..
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

اشكرك على المقالة الرائعة

اتحفونا بفنونكم أيها الخبراء

 

 

هل لمدخلات ال main أهمية في المثال الثاني ؟

 

ما فائدة sctdlib في المثال الثاني ؟؟

تم تعديل بواسطه المـقـدام
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

لا لم يستخدم وسطاء الدالة main في المثال ..

بالنسبة للمكتبة cstdlib فهي التي تحوي الدالة system ..

بالتوفيق

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0
هل لمدخلات ال main أهمية في المثال الثاني ؟

 

 

لا, لكن مجرد استخدام الـ Coding Standard يُشعرني بالراحة :D

 

ما فائدة sctdlib في المثال الثاني ؟

 

 

كما ذكر الأخ مصطفى, cstdlib تحتوي على الدالة system و يُقابلها الملف الرأسي stdlib.h في لغة C.

تم تعديل بواسطه Snack3r
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

من فضلك سجل دخول لتتمكن من التعليق

ستتمكن من اضافه تعليقات بعد التسجيل



سجل دخولك الان

  • يستعرض القسم حالياً   0 members

    لا يوجد أعضاء مسجلين يشاهدون هذه الصفحة .