بسم الله الرحمن الرحيم.
في هذا المقال سنتعرف على المصفوفات والسلاسل النصية واستخداماتها وأهميتها في الهندسة العكسية.
من هذا المقال سنتوسع بالشرح — لأن المقالات المتبقية هي من أهم ما يفيدنا في الهندسة العكسية، ويجب إتقان التعامل معها.
المصفوفات والسلاسل النصية من أهم أنواع البيانات المستخدمة لتخزين البيانات ومعالجتها، وهما يختلفان في الاستخدام والهيكلية. فهم كيفية عملهما في الذاكرة هو أساس تحليل البرامج التنفيذية، خصوصاً فيما يتعلق بالكشف عن الثغرات.
1. المصفوفات (Arrays)
ما هي المصفوفة؟ مجموعة من العناصر المتشابهة (من نفس نوع البيانات) تُخزَّن في مواقع ذاكرة متجاورة. كل عنصر يُمكن الوصول إليه بفهرس (Index).
data_type array_name[size];
// data_type: نوع البيانات (مثل int، char، float).
// array_name: اسم المصفوفة.
// size: عدد العناصر التي يمكن تخزينها.
أمثلة
int numbers[5]; // مصفوفة لخمسة أعداد صحيحة
float prices[10]; // مصفوفة لعشرة أعداد عشرية
char letters[3]; // مصفوفة لثلاثة حروف
تهيئة المصفوفة
int numbers[5] = {1, 2, 3, 4, 5}; // تعيين كل القيم
int numbers[5] = {1, 2}; // الباقي يُصفَّر تلقائياً إلى 0
الوصول والتعديل
int x = numbers[2]; // الحصول على العنصر الثالث
numbers[0] = 10; // تعديل العنصر الأول
ملاحظة هامة جداً: أول عنصر في المصفوفة فهرسه
0— أي أنnumbers[0]هو الأول.
مثال
#include <stdio.h>
int main() {
int numbers[5] = {10, 20, 30, 40, 50};
for (int i = 0; i < 5; i++) {
printf("numbers[%d] = %d\n", i, numbers[i]);
}
return 0;
}
المصفوفات متعددة الأبعاد
مصفوفة مؤلفة من أسطر وأعمدة:
int matrix[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
الوصول للعنصر:
int x = matrix[1][2]; // السطر الثاني، العمود الثالث
2. السلاسل النصية (Strings)
ما هي السلسلة النصية؟ مصفوفة من الأحرف (characters) تُنهيها لغة C برمز إنهاء السلسلة '\0' (الحرف الفارغ). تذكره من مقال سلاسل الهروب.
char string_name[size];
تهيئة السلاسل النصية
1. يدوياً حرفاً حرفاً:
char name[4] = {'A', 'l', 'i', '\0'};
لاحظ أننا خصصنا 4 مواضع: ثلاثة أحرف +
\0.
2. كالنصوص المباشرة:
char name[] = "Ali"; // في هذه الحالة يُضاف \0 تلقائياً
الوصول للأحرف
char first_char = name[0]; // الحرف الأول
name[1] = 'e'; // تعديل الحرف الثاني
مثال
#include <stdio.h>
int main() {
char name[] = "Ahmed";
printf("Name: %s\n", name);
for (int i = 0; name[i] != '\0'; i++) {
printf("Character %d: %c\n", i, name[i]);
}
return 0;
}
دوال التعامل مع السلاسل النصية
هذه الدوال موجودة في المكتبة <string.h> — وتحتاج إتقانها لأنها الأكثر ظهوراً في تحليل الـ binaries:
| الدالة | وظيفتها | ملاحظة أمنية |
|---|---|---|
strlen | معرفة طول النص | آمنة (قراءة فقط) |
strcpy | نسخ نص إلى آخر | غير آمنة — لا تفحص حجم الوجهة |
strcat | دمج نصين | غير آمنة — لا تفحص حجم الوجهة |
strcmp | مقارنة نصين | آمنة (قراءة فقط) |
#include <string.h>
int length = strlen(name); // الطول
strcpy(destination, source); // النسخ
strcat(string1, string2); // الدمج
if (strcmp(string1, string2) == 0) { // المقارنة
printf("Strings are equal.\n");
}
مثال شامل
#include <stdio.h>
#include <string.h>
int main() {
char str1[20] = "Hello";
char str2[20] = "World";
strcat(str1, " ");
strcat(str1, str2);
printf("Concatenated String: %s\n", str1);
if (strcmp(str1, "Hello World") == 0) {
printf("Strings are equal.\n");
} else {
printf("Strings are not equal.\n");
}
printf("Length of str1: %zu\n", strlen(str1)); // %zu لأن strlen ترجع size_t
return 0;
}
الفرق بين المصفوفات والسلاسل النصية
| المصفوفات | السلاسل النصية | |
|---|---|---|
| النوع | أي نوع (int, float, char…) | char فقط |
| النهاية | لا رمز نهاية | تنتهي بـ \0 |
| الاستخدام | بيانات منظمة (أرقام، قيم…) | نصوص |
| في الذاكرة | عناصر متجاورة بنفس الحجم | أحرف متجاورة + \0 |
أهمية المصفوفات والسلاسل في الهندسة العكسية
أ. المصفوفة في الـ Assembly = عنوان بداية + إزاحة
المصفوفة لا تُمرَّر ككائن واحد، بل عنوان بدايتها في الذاكرة. الوصول numbers[2] يتحول إلى:
; numbers تبدأ عند العنوان X، و int = 4 بايت
lea eax, [X] ; عنوان البداية
mov edx, [eax + 2*4] ; numbers[2] = (العنوان + إزاحة)
الفهرس يتحول إلى إزاحة حسابية: index × حجم العنصر. لهذا تجد في الـ disassembly أنماطاً مثل [eax + ecx*4] — وهذا هو جوهر التعامل مع المصفوفات.
ب. العلاقة المباشرة بثغرات Buffer Overflow
مصفوفة char buf[10] على الـ stack ثم strcpy(buf, source) — إذا تجاوز المصدر 10 بايتات، ستكتب على ما يليها في الذاكرة (الـ EBP ثم عنوان الرجوع).
المصفوفة ليست “صندوقاً محمياً” — بل تتابع بايتات بلا حارس. فهم هذا هو الباب لفهم ثغرات Buffer Overflow التي ستشرحها في سلسلة الـ exploitation، ولهذا فإن
strcpyوstrcatتعتبران من أخطر الدوال في C (لهذا أنشئتstrncpyوstrlcpyالآمنتان).
ج. السلاسل في الـ Binaries
- السلاسل تُخزَّن في الملف القابل للتنفيذ كما هي (ASCII/UTF) في قسم
.rdata— لذلك تستخرجها أدوات مثلstringsبسهولة. - عندما يجد المحلل نصاً في الـ binary، يحدد مكانه ثم يبحث عن الاستدعاءات المتقاطعة (Cross-References / XREFs) ليرى أي دالة تستخدمه — هذا أول خط في التحليل.
د. أدوات التعامل
أدوات مثل IDA Pro و Ghidra تحلل عناوين الذاكرة وتحدد المصفوفات والنصوص المخزنة تلقائياً — لكن فهم ما يحدث تحت هذه الأدوات هو ما يميز المحلل الحقيقي.
تمرين عملي
- اكتب
char buf[16]وانسخ فيه نصاً أطول بـstrcpy(مثل 40 حرفاً) — شاهد البرنامج ينهار أو يتصرف بغرابة. هذا هو بدايات تجربة الـ overflow التي ستتوسع فيها لاحقاً. - اطبع عنوان المصفوفة بـ
%p(printf("%p", numbers)) — سترى عنواناً في الذاكرة، وهو نفس العنوان الذي ستراه في الـ assembly. - افحص برنامجك في IDA/Ghidra ولاحظ نمط
[eax + ecx*4]للوصول إلىnumbers[i].