السلام عليكم ورحمة الله وبركاته.
من هذا المقال سنتعامل مع لغة C بالأمور الـ low level.
قبل البدء: يجب أن تكون على معرفة بأساسيات لغة C — يمكنك مراجعة سلسلة الأساسيات المنشورة سابقاً.
في هذا المقال سنشرح العوامل البتية (Bitwise Operators) وعوامل الزيادة والنقصان (Increment/Decrement) والعامل الثلاثي (Ternary Operator). لن نركز على العمليات الحسابية البسيطة (جمع، طرح…) لأنها بديهية — هذه السلسلة تركز على الأمور الأقوى التي ستخدمنا في الهندسة العكسية.
شو هي عوامل البت؟ ولماذا نتعلمها؟
عوامل نستخدمها للتلاعب بالبتات الفردية في الأرقام. هي أساسية في البرمجة منخفضة المستوى لأنها تتعامل مباشرة مع الـ Binary (0 و 1) — تماماً كما يفعل المعالج.
1. العامل AND (&)
يقارن كل بت: النتيجة 1 فقط إذا كان كلا البتين 1 — يشبه الضرب في الجبر الرياضي.
| a | b | a & b |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 1 | 1 | 1 |
مثال: 5 & 3
0101 (5)
& 0011 (3)
----
0001 = 1
#include <stdio.h>
int main(){
int a = 5; // 0101
int b = 3; // 0011
int result = a & b; // 0001
printf("5 & 3 = %d\n", result); // النتيجة = 1
return 0;
}
2. العامل OR (|)
النتيجة 1 إذا كان أي من البتين 1.
| a | b | a | b |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 1 |
مثال: 5 | 3
0101 (5)
| 0011 (3)
----
0111 = 7
#include <stdio.h>
int main(){
int a = 3; // 0011
int b = 5; // 0101
int result = a | b; // 0111
printf("3 | 5 = %d\n", result); // النتيجة = 7
return 0;
}
3. العامل XOR (^)
النتيجة 1 فقط إذا كان البتان مختلفين.
| a | b | a ^ b |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
مثال: 5 ^ 3
0101 (5)
^ 0011 (3)
----
0110 = 6
#include <stdio.h>
int main() {
int a = 5; // 0101
int b = 3; // 0011
int result = a ^ b;
printf("5 ^ 3 = %d\n", result); // 6
return 0;
}
خاصية مهمة لـ XOR:
a ^ b ^ b == a— أي أن XOR على نفس القيمة مرتين يعيد القيمة الأصلية. هذه الخاصية هي أساس تشفير/فك تشفير الـ XOR المستخدم بكثرة في الـ malware — راجعها جيداً.
4. العامل NOT (~)
يقلب جميع البتات (0 → 1 و 1 → 0).
مثال: ~5
00000101 (5)
~ --------
11111010 = -6 بتعويض الاثنين (two's complement)
#include <stdio.h>
int main(){
int a = 5;
int result = ~a;
printf("~5 = %d\n", result); // النتيجة = -6
return 0;
}
النتيجة
-6لأن الأعداد الموقّعة تُخزن بتعويض الاثنين (two’s complement). من لم يفهم أنظمة العد يمكنه مراجعة ملف أنظمة الأعداد المرفق.
5. الإزاحة لليسار (Left Shift) <<
ينقل البتات إلى اليسار بمقدار محدد، ويملأ الفراغات بالأصفار. الإزاحة بمقدار 1 تعادل الضرب في 2.
0101 (5) << 1
----
1010 = 10
#include <stdio.h>
int main(){
int a = 5;
printf("5 << 1 = %d\n", a << 1); // 10 (5 × 2)
printf("5 << 2 = %d\n", a << 2); // 20 (5 × 4)
return 0;
}
6. الإزاحة لليمين (Right Shift) >>
ينقل البتات إلى اليمين. الإزاحة بمقدار 1 تعادل القسمة على 2.
1010 (10) >> 1
----
0101 = 5
#include <stdio.h>
int main(){
int a = 10;
printf("10 >> 1 = %d\n", a >> 1); // 5 (10 / 2)
printf("10 >> 2 = %d\n", a >> 2); // 2 (10 / 4)
return 0;
}
عوامل التعيين البتية
| العامل | المعنى |
|---|---|
a &= b | a = a & b |
a |= b | a = a | b |
a ^= b | a = a ^ b |
a <<= n | a = a << n |
a >>= n | a = a >> n |
int a = 5; // 0101
a &= 3; // 0101 & 0011 = 0001
printf("%d\n", a); // 1
a = 5;
a |= 3; // 0101 | 0011 = 0111
printf("%d\n", a); // 7
a = 5;
a ^= 3; // 0101 ^ 0011 = 0110
printf("%d\n", a); // 6
a = 5;
a <<= 1; // 0101 << 1 = 1010
printf("%d\n", a); // 10
أولوية العمليات (Precedence) — فخ حقيقي
انتبه: أولوية العوامل البتية أقل من عوامل المقارنة! هذا يسبب أخطاء خفية:
if (x & 3 == 1) // يُحلل كـ x & (3 == 1) ← خطأ! if ((x & 3) == 1) // الصحيح: بين الأقواسعند تحليل الكود، تحقق دائماً من الأقواس — هذا يهمك في قراءة الـ assembly وفي اكتشاف الشيفرات الخبيثة المعقدة.
عوامل الزيادة والنقصان (Increment & Decrement)
عوامل أحادية (Unary) تزيد أو تنقص قيمة المتغير بمقدار 1:
| العامل | السلوك |
|---|---|
++x (Prefix) | يزيد أولاً ثم يستخدم القيمة الجديدة |
x++ (Postfix) | يستخدم القيمة الأصلية ثم يزيد |
--x / x-- | نفس المبدأ للنقصان |
int x = 5;
printf("%d\n", ++x); // 6 (زيادة أولاً، ثم طباعة)
x = 5;
printf("%d\n", x++); // 5 (طباعة أولاً، ثم زيادة)
printf("%d\n", x); // 6 (بعد الزيادة)
مع المؤشرات
int arr[] = {1, 2, 3, 4};
int *p = arr;
printf("%d\n", *p++); // طباعة 1 ثم تحريك المؤشر للعنصر التالي
printf("%d\n", *p); // طباعة العنصر الثاني = 2
هذه الأنماط (
*p++,p++) تظهر باستمرار في حلقات فك التشفير في الـ assembly — لاحظ أنك ستراها مرات لا تحصى في تحليل malware.
العامل الثلاثي (Ternary Operator) ?:
العامل الشرطي الوحيد في لغة C — بديل مختصر لـ if/else:
Condition ? exp1 : exp2
إذا كان الشرط صحيحاً يُنفذ exp1، وإلا يُنفذ exp2:
#include <stdio.h>
int main(){
int x = 10, y = 5;
int max = (x > y) ? x : y; // max = 10
printf("The bigger is: %d\n", max);
return 0;
}
ربط الـ RE — لماذا هذه العوامل أساسية في التحليل
1. & 0xFF — اقتطاع البايت
الاستخدام الأشهر: إبقاء آخر بايت فقط. هذا ما رأيته في تحليل Flare-On: and eax, 0FFh في الـ assembly.
int value = 0x12345678;
int low_byte = value & 0xFF; // 0x78
2. | — إعداد الأعلام (Flags)
تعيين بتات محددة دون المساس بالباقي:
flags |= 0x01; // تفعيل العلم الأول
flags |= 0x80; // تفعيل العلم الأخير
3. ^ — فك تشفير XOR (الأهم في malware)
هذا النمط يظهر في آلاف العينات — فك تشفير البيانات بايتاً بايتاً:
unsigned char encrypted[] = {0x6A, 0x75, 0x73, 0x74};
for (int i = 0; i < 4; i++) {
encrypted[i] ^= 0x1F; // فك التشفير بمفتاح
}
والذي سيظهر في الـ assembly كنمط:
mov al, [esi] ; قراءة البايت المشفر
xor al, 0x1F ; فك التشفير
mov [edi], al
inc esi ; التقدم — وهنا *p++ / p++
4. & 0x80 — فحص بت محدد
اختبار علم معين (مثل علامة الإشارة):
if (flags & 0x80) {
// البت السابع مضبوط
}
5. >> و << — حسابات الضرب والقسمة
الإزاحات تظهر في حسابات العناوين وحجم البايفرات — وأحياناً يستخدمها المترجم بدل * 2 و / 2 لتحسين الأداء.
خلاصة
العوامل البتية هي اللغة المشتركة بين C والـ assembly — كل ما تراه من and/or/xor/shl/shr في الـ disassembly هو تطبيق مباشر لما تعلمته في هذا المقال. إتقانها يعني أنك تقرأ كوداً يُنفذه المعالج، لا مجرد نص برمجي.