الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

Hex Editor Engine Using C++

بدأه Khaled.Alshaya في 23 مارس 2008 · 6 رد · 4,215 مشاهدة · في مشاريع ومسابقات القسم
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكم,,

خطرت لي فكرة برنامج بسيط كتطبيق لدروس أحاول إكمالها حالياً. الدروس هي كتاب Charles Petzold الشهير في برمجة Windows و شرح فلسفتها بطريقة جميلة جداً و مفصلة كثيراً, بالطبع هدفه هو شرح المفاهيم بدلاً من شرح الدوال أو شرح مكتبات Windows بشكل أصح.

أعرف أنني لم أشارك منذ مدة, و لكن ذلك يرجع لعدة ظروف, و لكن سنعود معكم من جديد بإذن الله.

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

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

كشرح بسيط لماذا نحتاج إلى محرر يعرض لنا مكونات أي ملف لدينا, على شكل أرقام بالنظام الـ Hexadecimal, في الحقيقة لم أجد لاسم النظام في رأسي لذلك كتبت المصطلح باللغة الانجليزية.

الميزة أن كل رقم في النظام الـ Hexadecimal يمثل عدد محدد من الـ Bits, بشكل أكثر تحديداً نصف byte أو ما يطلق عليه nibble, و بالتالي فإننا يمكننا أن نغير أي ثابت يحتفظ به البرنامج دون التأثير على بنية البرنامج نفسه أو حجمه. و هذا من الأشياء التي لا يمكن القيام بها في النظام العشري على حد علمي. لأن كل عدد عشري يحتاج إلى عدد غير صحيح لتمثليه على عكس النظام الـ Hexadecimal أو الثماني Octal,

في الحقيقة أن النظام الثماني يوفر هذه الخاصية أيضاً و لكن المبرمجين اعتادوا على النظام الـ Hexadecimal أكثر, لأن كل byte يمكن تمثيله برقمين, بدلاً من تمثيله بثلاثة أرقم كما في النظام الثماني.

ما هي مهمة محرك البرنامج إذاً ؟

مهمة محركنا هو تحويل مصفوفة عددية إلى نص, و هذا النص هو الأعداد نفسها و لكن بالنظام الـ Hexadecimal,

الأعداد في الحاسوب تمثل بالنظام الثنائي, و كل ما تراه على الشاشة هي نصوص و ليست أعداد. صحيح أن الحاسوب على مستوى المعالج يتعامل مع البيانات على شكل أعداد ثنائية, و لكن جميع ما يتم إخراجه للشاشة عبارة عن مجموعة من الحروف, فعلى سبيل المثال لو أن لدينا المثال التالي في ++C :

int x = 15;
cout << x;

ما الذي تعتقد أنه حصل ؟

الكائن cout يقوم بتحويل العدد إلى نص, فالعدد 15 يحتاج إلى أربع bits لتمثيله داخلياً و لكن عند عرضه نحتاج إلى حرفين. ربما الكثيرون في بداية مشوارهم البرمجي واجهتهم مشاريع صغيرة لإنشاء دالة لتحويل عدد ما إلى النظام الثنائي أو الثماني و هكذا....

لماذا أقول حروف و لا أقول 2 bytes, أي أن كل خانة تحتاج إلى byte عندما نحول العدد إلى نص تمهيداً لعرضه.

السبب هو أن تعريف الحرف يختلف من منصة لأخرى! فالترميز الذي نستعمله افتراضياً في برامجنا في ++C, هو ترميز ASCII,

هناك أنظمة أخرى أكثر كفاءة من هذا الترميز و خصوصاً عند الحديث عن لغات غير الانجليزية, أشهر ترميز يسمى UNICODE, في الحقيقة ذكرته هنا, لأن محركنا سيدعم ترميز UNICODE في نسخته القادمة.

بالمناسبة نظام Windows يستعمل ترميز UNICODE بشكل افتراضي في عملياته الداخلية, و أي دالة في Windows تقوم باستقبال نص كوسيط, هذا النص افتراضياً يكون بترميز الـ UNICODE. لذلك سنحتاج إلى إنشاء نسخة أخرى من محركنا تدعم UNICODE بدلاً من ASCII في وقت لاحق.

حتى لا يطول الحديث, تصوري لعملية التحويل يمكن إختزاله في هذا التعريف لدالتنا التي نريد إنشائها.

void convertToHex( unsigned char numbers[], char hexa[], unsigned int numbersCount );

دالتنا في في فكرتها بسيطة جداً! تقوم باستقبال مصفوفة من الأعداد. و سنقوم باستقبال مصفوفة من النوع unsigned char,

يمكن أن نقوم بإعادة تعريف الدالة لكي تستقبل أنواع أخرى, و لكن هذا لا فائدة منه. لسبب بسيط, كل 4 bits من هذه البيانات ستتحول لعدد في النظام الـ Hexadeciaml بغض النظر عن البيانات نفسها.

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

آخر وسيط هو حجم المصفوفة بالـ bytes. أي حجم المصفوفة []numbers,

الوسيط الذي في المنتصف هو مصفوفة من نوع char, حجمها = numbersCount*2 + 1

بما أن كل 4 bits في الدالة numbers ستتحول لحرف ASCII كامل, سنحتاج إلى 2 bytes لتمثيل كل byte على شكل نص!

و لا تنسى أن لدينا Null Terminator في آخر النص و هذا هو الحرف الزائد في معادلتنا!

كما قلنا أن دالتنا ستسقبل سلسلة من الأعداد, كل عدد عبارة عن byte كامل, لذلك إذا أردنا أن نرسل مصفوفة int تحتوي على عنصرين :

int nums[2] = { 15, 255 };
char hexa[17];
convertToHex( reinterpret_cast<unsigned char*>(nums), hexa, 17 );

std::cout << hexa;

أطمح لإنجاز المشروع كاملاً بإذن الله, فأنا أفكر بتطوير المشروع بحيث يصبح بالإمكان نقله لعدة معالجات, و يسمح بأنظمة ترميز مختلفة, أتكلم هنا عن المحرك و ليس عن البرنامج ككل :)

إذا يسر الله لنا هذا الأمر سأضع المشروع على sf.net لمن يحتاجه.

في المرة القادمة سأتكلم عن الـ Implementation الذي اخترته و من أين حصلت على الفكرة.

تحياتي,,

#2
اقتباس
كشرح بسيط لماذا نحتاج إلى محرر يعرض لنا مكونات أي ملف لدينا, على شكل أرقام بالنظام الـ Hexadecimal, في الحقيقة لم أجد لاسم النظام في رأسي لذلك كتبت المصطلح باللغة الانجليزية.

:lol: (أخي اسم النظام بالعربي(الست عشري

يبدو انك متحمس جدا لهذا المشروع

لم تذكر هل سيكون البرنامج باللغة العربية وماهي انظمة الترميز التي تدعم العربي

بالتوفيق

الفريق العربي للبرمجة

16359.png

userbarxp.png

624.gif
#3

السلام عليكم ,,

شكراً على الترجمة التي لم تخطر على بالي حين كتابة الموضوع :D

بالنسبة لواجهة البرنامج, ستكون باللغة العربية, و لكن لم أبدأ حتى الآن في واجهة البرنامج,

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

على العموم, الفكرة كلها تعتمد على استخدام الـ Lookup Tables بدلاً من إجراء عملية التحويل وقت التشغيل.

هذا هو الكود :

namespace HexAT{

	short int Hex[256] = {
		0x3030, 0x3130, 0x3230, 0x3330, 0x3430, 0x3530, 0x3630, 0x3730, 0x3830, 0x3930, 0x4130, 0x4230, 0x4330, 0x4430, 0x4530, 0x4630, 
		0x3031, 0x3131, 0x3231, 0x3331, 0x3431, 0x3531, 0x3631, 0x3731, 0x3831, 0x3931, 0x4131, 0x4231, 0x4331, 0x4431, 0x4531, 0x4631, 
		0x3032, 0x3132, 0x3232, 0x3332, 0x3432, 0x3532, 0x3632, 0x3732, 0x3832, 0x3932, 0x4132, 0x4232, 0x4332, 0x4432, 0x4532, 0x4632, 
		0x3033, 0x3133, 0x3233, 0x3333, 0x3433, 0x3533, 0x3633, 0x3733, 0x3833, 0x3933, 0x4133, 0x4233, 0x4333, 0x4433, 0x4533, 0x4633, 
		0x3034, 0x3134, 0x3234, 0x3334, 0x3434, 0x3534, 0x3634, 0x3734, 0x3834, 0x3934, 0x4134, 0x4234, 0x4334, 0x4434, 0x4534, 0x4634, 
		0x3035, 0x3135, 0x3235, 0x3335, 0x3435, 0x3535, 0x3635, 0x3735, 0x3835, 0x3935, 0x4135, 0x4235, 0x4335, 0x4435, 0x4535, 0x4635, 
		0x3036, 0x3136, 0x3236, 0x3336, 0x3436, 0x3536, 0x3636, 0x3736, 0x3836, 0x3936, 0x4136, 0x4236, 0x4336, 0x4436, 0x4536, 0x4636, 
		0x3037, 0x3137, 0x3237, 0x3337, 0x3437, 0x3537, 0x3637, 0x3737, 0x3837, 0x3937, 0x4137, 0x4237, 0x4337, 0x4437, 0x4537, 0x4637, 
		0x3038, 0x3138, 0x3238, 0x3338, 0x3438, 0x3538, 0x3638, 0x3738, 0x3838, 0x3938, 0x4138, 0x4238, 0x4338, 0x4438, 0x4538, 0x4638, 
		0x3039, 0x3139, 0x3239, 0x3339, 0x3439, 0x3539, 0x3639, 0x3739, 0x3839, 0x3939, 0x4139, 0x4239, 0x4339, 0x4439, 0x4539, 0x4639, 
		0x3041, 0x3141, 0x3241, 0x3341, 0x3441, 0x3541, 0x3641, 0x3741, 0x3841, 0x3941, 0x4141, 0x4241, 0x4341, 0x4441, 0x4541, 0x4641, 
		0x3042, 0x3142, 0x3242, 0x3342, 0x3442, 0x3542, 0x3642, 0x3742, 0x3842, 0x3942, 0x4142, 0x4242, 0x4342, 0x4442, 0x4542, 0x4642, 
		0x3043, 0x3143, 0x3243, 0x3343, 0x3443, 0x3543, 0x3643, 0x3743, 0x3843, 0x3943, 0x4143, 0x4243, 0x4343, 0x4443, 0x4543, 0x4643, 
		0x3044, 0x3144, 0x3244, 0x3344, 0x3444, 0x3544, 0x3644, 0x3744, 0x3844, 0x3944, 0x4144, 0x4244, 0x4344, 0x4444, 0x4544, 0x4644, 
		0x3045, 0x3145, 0x3245, 0x3345, 0x3445, 0x3545, 0x3645, 0x3745, 0x3845, 0x3945, 0x4145, 0x4245, 0x4345, 0x4445, 0x4545, 0x4645, 
		0x3046, 0x3146, 0x3246, 0x3346, 0x3446, 0x3546, 0x3646, 0x3746, 0x3846, 0x3946, 0x4146, 0x4246, 0x4346, 0x4446, 0x4546, 0x4646
	};



	inline void convertNumbersToHex( uchar numbers[], char hexaString[], uint numbersCount ){
	// numbers[] size = numbersCount
	// hexaString[] size = (numbersCount * 2) + 1

		usint *hexaStringPtr = reinterpret_cast<usint*>(hexaString);

		for( uint C = 0; C < numbersCount; C++ )
			hexaStringPtr[C] = Hex[ numbers[C] ];

		hexaString[(2 * numbersCount)] = 0;

		return;
	}
}

أود الإشارة إلى أن هذه الدالة ستعمل بشكل سليم 100% على معالجات 32 bit و التي تتطبع طريقة little endian في تخزين الأعداد,

أعتقد أن إنشاء عدة نسخ من المحرك لعدة منصات ليس بتلك الصعوبة,

إذا كان لديكم سؤال أو استفسار فأنا في الخدمة :)

تم تعديل هذه المشاركة بواسطة Khaled.Alshaya في 24 مارس 2008 في 00:41

#4

بسم الله الرحمن الرحيم

اخي خالد لا اخفيك سرا اني فعلا سعيد بعودتك

انت من الاشخاص الذين اتمنى لو اصبح نصفهم وهذا يكفيني

انت شخص رائع فعلا ليس تملقا هذا الكلام لكن بالفعل انت شخص بمنتهى الروعة

امنى لو اصبح مثلك وهاانا احاول وقد اكون بعيد جددااااااااااااااااااااا عن مستواك لكن طموحي واماني مشروعة اليس كذلك؟؟؟؟

اتمنى لك التوفيق وماشاءالله تبارك الله ولاحول ولاقوة الا بالله

على فكرة هل تقبلني تلميذ عندك؟

وانا تحت امرك وساكون سعيد لو قبلتني تلميذ في مدرسة خالد للبرمجة

تحياااااااااااتي11

تم تعديل هذه المشاركة بواسطة الذ11بح في 24 مارس 2008 في 11:48

يارب التأهل ياكريم

#5

السلام عليكم,

والله يا أخ خالد أنا مش فاهم بصراحة المغزى من البرنامج يا ريت توضح أكثر, يعنى إيه نغير ثابت داخل البرنامج؟ و ليه مينفعش فى النظام العشرى و ينفع فى ال hexa؟ كمان ليه إخترت القيم دى بالذات علشان تحطها فى ال lookup table ؟

#6

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

setiosflags(ios::hex)

Chao!!!

Great place, and people, but full of sectarianism.

#7

السلام عليكم ,,

اقتباس
السلام عليكم,

والله يا أخ خالد أنا مش فاهم بصراحة المغزى من البرنامج يا ريت توضح أكثر, يعنى إيه نغير ثابت داخل البرنامج؟ و ليه مينفعش فى النظام العشرى و ينفع فى ال hexa؟ كمان ليه إخترت القيم دى بالذات علشان تحطها فى ال lookup table ؟

بصراحة لا يوجد برنامج متكامل في رأسي,

ما أفكر فيه هو عبارة عن مكتبة مهمتها تحويل الأعداد من نظام لآخر, سواء كانت تلك الأعداد على شكل نصوص أو على شكل مصفوفة عددية من نوع تدعمه ++C,

بما أن هذا الموضوع بسيط و ليس بتلك الصعوبة, وجهت تركيزي للحصول على الكفائة القصوى في عملية التحويل و هنا بدأت الأمور تتعقد قليلاً!

بصراحة بدرت في ذهني الفكرة التالية :

نحن نعلم أن عملية التحويل قد تستدعي عمليات قد تبدو بسيطة و لكن لو كان لدينا كمية كبيرة من البيانات ماذا سنفعل ؟! تلك العمليات ستتكرر و تتكرر كثيراً!

و أنا أردت تحسين كفاءة العملية نفسها, أي عملية تحويل كل عدد في المصفوفة!

فكرت في الموضوع قليلاً و بدلاً من الحصول على القيم لحظة التحويل, يمكن أن تكون القيم جاهزة و مخزنة كثوابت في البرنامج نفسه, و عملية الحصول على القيم ستكون عملية نسخ للقيم لا أكثر :P

أي باختصار الاعتماد على الـ lookup tables,

الفكرة أتتني بعد قراءة هذه المقالة الرائعة حول الـ meta programming و طريقة التفكير من خلالها :

The art of metaprogramming

الآن ما الفائدة من هذه المكتبة,

يمكن استعمالها لتغيير قيمة معينة داخل ملف على سبيل المثال,

يمكن فتح ملف bmp و ارسال المصفوفة التي وضعت الملف فيها إلى المكتبة لتحوله إلى أعداد على شكل نص, و اللعب في Header الملف و تغيير قيمة فيه بكل سهولة و تمرير النص من جديد للمكتبة لتحويله إلى شكله الأصلي على شكل binary من جديد,

و ذلك بسرعة خاطفة,

يمكن أن يكون hex editor أو يمكن أن يكون أي شيء آخر,,

طبعاً المكتبة ستدعم نظم الأعداد التالية decimal و الـ hexadecimal و الـ octal و الـ binary و منذ فترة قرأت عن الـ 64 Encoding,

هذا هو الهدف و لكن حالياً لا أفكر بدعم النظام العشري و لا الـ 64,

ربما في النسخة الثانية من المكتبة,

كما تلاحظ, إذا قمت بطلب العنصر رقم 255 في المصفوفة فسأحصل على "FF" أي أن العدد هو رقم العنصر و العدد بالنظام الستعشري هو قيمة العنصر على شكل نص, بمعنى أبسط مالدي هو عبارة عن associative array,

في رد قادم بإذن الله سأقوم بتوضيح لماذا لا يمكننا تعديل الملفات الثنائية بسهولة إذا تم تمثيلها بالنظام العشري.

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

كود

setiosflags(ios::hex)

عزيزي, عملية التحويل تمت وقت الـ Compile Time, و ليس هناك عملية تحويل وقت التشغيل.

هذا إحدى مزايا الـ meta programming, بالنسبة للكود الذي وضعته فهو المطلوب, و لكن لسببين أقوم بتصميم المكتبة,

أولاً, الـ manipulators لا يمكن استعمالها إلا مع الـ Standard Streams, لا أعتقد أنه يتوفر مثل هذه الدوال في win32,

ثانياً, أطمح في الحصول على كفاءة أفضل, و دعم للعديد من المنصات,

هناك مشكلتان في الحقيقة تواجهني!

الأولى هي دعم ترميز ASCII و دعم Unicode, و عملية كتابة الكود ستكون على نفس منوال الطريقة في win32 أي أنني سأعتمد على الـ Conditional Compiling و flag لتحديد أي دالة نحتاج,

إضافة إلى ذلك ستواجهني مشكلة ترتيب الـ bytes أو الـ Endianess لكل منصة,

النسخة الأولى من المكتبة ستدعم نظام الأعداد الستعشري و الثماني و الثنائي ( ASCII و UNICODE, لمنصات 32 bits, سواء كانت big-endian أو little-endian )

تحياتي,,

تم تعديل هذه المشاركة بواسطة Khaled.Alshaya في 30 مارس 2008 في 06:52

مواضيع مشابهة