المخترق الذي يعرف عمله يحاول ألا يترك شيئاً على القرص، فيشغل برنامجه من الذاكرة، ويمسح سجل الأوامر، ويحذف الملف بعد تشغيله. ولكن ما دام البرنامج يعمل فهو موجود في ذاكرة الجهاز RAM، ومعه اتصالاته والأوامر التي كتبها في الـ Shell. وفي دليل أدوات التحقيق الرقمي مفتوحة المصدر ثبتنا Volatility 3 وعرضنا أشهر إضافاته، ووعدنا بمقال نحلل فيه نسخة ذاكرة حقيقية خطوة خطوة، وهذا هو المقال. وسوف نعمل على عينتين عامتين ينشرهما فريق Volatility نفسه في مستودع بيانات الاختبار، الأولى من سيرفر Debian والثانية من جهاز Windows 10 على Azure، فتستطيع أن تنزلهما وتعيد كل أمر في المقال وتحصل على المخرج نفسه.
دليلأدوات التحقيق الرقمي مفتوحة المصدر وكيف تستخدمهاالأدوات المفتوحة التي تحتاجها في كل مرحلة من التحقيق الرقمي، من نسخ القرص واسترجاع ملف محذوف إلى بناء خط زمني لما حدث.
دليلالبحث عن آثار الاختراق Hunt في كل سيرفراتك مع Velociraptorبنينا سيرفر Velociraptor وثلاثة عملاء وبحثنا عن آثار اختراق مزروعة بسبع عمليات Hunt، فظهر ما وجدته الأداة وما فاتها بإعداداتها الافتراضية.وسوف نتناول في هذا المقال ما يلي:
- من أين تأتي العينتان وكيف تتحقق من أنهما لم تتغيرا.
- كيف يقرأ Volatility ملف الذاكرة، ولماذا يحتاج جدول رموز Symbols، وأين يتعثر أغلب المبتدئين.
- تحليل ذاكرة Linux: البرامج، والاتصالات، وسجل Bash من الذاكرة، والبحث عن Rootkit في النواة.
- تحليل ذاكرة Windows: الشجرة والاتصالات وسطر الأوامر، وmalfind ونتائجها الخاطئة.
- الوقت والذاكرة التي احتاجتها كل إضافة، وما الذي يناسب جهاز التحليل.
العينتان ومن أين جاءتا
أكثر ما يتعب من يتعلم تحليل الذاكرة هو العثور على عينة يعرف مصدرها، فأغلب الملفات المتداولة في المقالات القديمة مرفوعة على مواقع مشاركة ملفات لا تعرف من رفعها، وبعضها من مسابقات CTF فيها قصص مصنوعة. والمصدر الذي اخترناه هو مستودع volatility3-test-data من مؤسسة Volatility، وفيه العينات التي يختبر بها الفريق كل تعديل على الكود قبل دمجه، ومعها ملفات الهاش وحزم الرموز. واخترنا منه ملفين:
| الملف | النظام | الحجم بعد فك الضغط | وقت أخذ النسخة |
|---|---|---|---|
linux-sample-1.bin | Debian 7، النواة 3.2.0-4-amd64 | 512 MiB | 24 يونيو 2014 |
win-10_19041-2025_03.dmp | Windows 10 الإصدار 19041 على Azure | 1.95 GiB | 6 مارس 2025 |
ولاحظ أن المستودع لا يقول ما الذي حدث على هذين الجهازين قبل أخذ النسخة، فهما ليسا قضية لها حل معروف، وهذا أقرب لعملك الحقيقي، فأنت تفتح الملف ولا تعرف ماذا سوف تجد. والسيرفر Debian 7 قديم بالطبع، ولكن الخطوات نفسها هي التي سوف تنفذها على نواة حديثة، والفرق الوحيد في ملف الرموز كما سيأتي.
نزلنا الملفات من صفحة الإصدار v0.0.1 وقارنا الهاش بالملف SHA256SUMS في المستودع قبل أي شيء آخر:
B=https://github.com/volatilityfoundation/volatility3-test-data/releases/download/v0.0.1
for f in linux-sample-1.bin.gz linux.zip win-10_19041-2025_03.dmp.gz symbols_win-10_19041-2025_03.zip; do
curl -sLO "$B/$f"
done
curl -sL https://raw.githubusercontent.com/volatilityfoundation/volatility3-test-data/main/images/SHA256SUMS
curl -sL https://raw.githubusercontent.com/volatilityfoundation/volatility3-test-data/main/symbols/SHA256SUMS
sha256sum *.gz *.zip910f272e5eeecdbf229e648d7b136a6722a11a5000f3995647364339417dfddd linux-sample-1.bin.gz
58bb7da2ed1e491ce922d04a59881d201e233b5605c9fd5a7f0c08ee528253c6 linux.zip
abd6500fba6a59c9a7c21f2c4b8a4d7edc2da417ec3161311a19a0e0beffd94e symbols_win-10_19041-2025_03.zip
b377ecd13bf40c90f59a8c41ffc8bc81166462627a7800ec82a67a73520c7f9d win-10_19041-2025_03.dmp.gzتطابقت القيم الأربع مع ما في المستودع، ثم فككنا الضغط وحسبنا الهاش للملفين بعد فك الضغط، وهذه القيم هي التي تكتبها في ملاحظات القضية، لأنها هاش الدليل الذي سوف تعمل عليه فعلاً:
gunzip linux-sample-1.bin.gz win-10_19041-2025_03.dmp.gz
sha256sum linux-sample-1.bin win-10_19041-2025_03.dmp8ce57b7c7b423173ce970fe8f51e7016c04ef1b4a5f73d5c5f1001d5c18780bc linux-sample-1.bin
ef92b50aa2ba2f830e3f816eb55ed8d15b0658fdfff0ab7af8adb68f2c8da460 win-10_19041-2025_03.dmpبيئة التحليل
شغلنا Volatility في Container من python:3.12-slim، وربطنا مجلد العينات للقراءة فقط :ro، والسبب أن الأداة لا يجب أن تكتب على الدليل أبداً، ولو أخطأت في أمر فالنظام نفسه يمنعها. وجعلنا للرموز مجلداً مستقلاً وللمخرج مجلداً ثالثاً:
docker run -d --name scf-vol \
-v "$PWD/data":/evidence:ro -v "$PWD/symbols":/symbols:ro -v "$PWD/out":/out \
python:3.12-slim sleep infinity
docker exec scf-vol pip install volatility3==2.28.2
docker exec scf-vol vol -h | head -1Volatility 3 Framework 2.28.2الإصدار 2.28.2 هو آخر إصدار من Volatility 3 وصدر في 17 سبتمبر 2026، وآخر تعديل على الكود كان في 12 أكتوبر 2026. والرخصة هي Volatility Software License وليست رخصة مفتوحة المصدر معتمدة، فالكود منشور ويمكنك استخدامه في عملك وفي قضاياك، ولكن اقرأ شروطها إذا كنت سوف تدمجه في منتج تبيعه.
كيف يقرأ Volatility ملف الذاكرة؟
ملف الذاكرة بايتات بلا أسماء ملفات ولا جداول، فالنواة Kernel تحفظ قائمة البرامج في هياكل Structures مرتبطة ببعضها بمؤشرات، ومكان كل حقل في كل هيكل يختلف من نسخة نواة إلى أخرى. ولذلك يحتاج Volatility شيئين: الطبقات Layers التي تحول العناوين الافتراضية إلى أماكن في الملف، وجدول الرموز Symbol Table الذي يقول له أين يبدأ كل هيكل وما حجم كل حقل فيه. والصورة التالية تبين ذلك:

والرموز تأتي بصيغة ISF، وهي ملف JSON، ومصدرها يختلف بين النظامين:
- Windows: تنشر Microsoft ملفات PDB لكل نسخة نواة على سيرفر الرموز العام، وVolatility ينزل الملف المناسب ويحوله وحده، بشرط أن يصل جهاز التحليل إلى الإنترنت.
- Linux: لا يوجد سيرفر مثل هذا، وعليك أن تبني ملف الرموز بنفسك بأداة dwarf2json من ملف
vmlinuxفيه معلومات التصحيح Debug Symbols للنواة نفسها بالضبط، أي الإصدار نفسه ورقم البناء نفسه. والتفصيل في صفحة جداول الرموز في التوثيق.
وفي هذه العينات وفر الفريق حزم الرموز جاهزة، فوضعناها في المجلد /symbols بالترتيب الذي يتوقعه Volatility، أي linux/ لملفات Linux وwindows/ntkrnlmp.pdb/<GUID>/ لملف Windows.
تحليل ذاكرة سيرفر Linux
أي نواة هذه؟
أول سؤال على ملف Linux هو نسخة النواة، لأنك لن تستطيع أن تفعل شيئاً قبل أن تجد لها ملف رموز. والإضافة banners.Banners تبحث في الملف عن النص الذي تطبعه النواة عند الإقلاع:
vol -q -f /evidence/linux-sample-1.bin banners.BannersOffset Banner
0x1400070 Linux version 3.2.0-4-amd64 ([email protected]) (gcc version 4.6.3 (Debian 4.6.3-14) ) #1 SMP Debian 3.2.57-3+deb7u2النواة 3.2.0-4-amd64 بالبناء 3.2.57-3+deb7u2، وفي حزمة الرموز ملف بهذا الاسم بالضبط Debian_3.2.57-3+deb7u2_3.2.0-4-amd64_x64.json.xz. وقد استغرق البحث 54 ثانية على ملف حجمه 512 MiB، لأنه يقرأ الملف كاملاً.
ماذا يحدث بدون ملف الرموز؟
قبل أن نعطيه الرموز، جربنا أن نطلب قائمة البرامج مباشرة، وهذا ما سوف تراه أنت غالباً في أول محاولة على سيرفر حقيقي:
vol -q -f /evidence/linux-sample-1.bin linux.pslist.PsListUnsatisfied requirement plugins.PsList.kernel.layer_name:
Unsatisfied requirement plugins.PsList.kernel.symbol_table_name:
A translation layer requirement was not fulfilled. Please verify that:
A file was provided to create this layer (by -f, --single-location or by config)
The file exists and is readable
The file is a valid memory image and was acquired cleanly
A symbol table requirement was not fulfilled. Please verify that:
The associated translation layer requirement was fulfilled
You have the correct symbol file for the requirement
The symbol file is under the correct directory or zip file
The symbol file is named appropriately or contains the correct bannerالرسالة تذكر الطبقة والرموز معاً، وقد تظن أن الملف تالف، ولكن السبب الحقيقي هنا هو الرموز وحدها، فالطبقة الافتراضية للنواة لا يمكن بناؤها في Linux قبل معرفة الرموز. والحل الأنسب ألا تنتظر الحادثة لتبحث عن الرموز، وإنما أن تبني ملف الرموز لكل نسخة نواة في سيرفراتك مسبقاً وتحفظه مع حقيبة الاستجابة، والسبب أن السيرفر بعد الحادثة قد يكون محدثاً أو معاد البناء، فلا تجد حزمة التصحيح للنسخة القديمة بسهولة.
البرامج العاملة وشجرتها
أعدنا الأمر مع الخيار -s /symbols:
vol -q -s /symbols -f /evidence/linux-sample-1.bin linux.pslist.PsListفظهرت 133 عملية في 37 ثانية، وهذه آخر الأسطر بعد حذف بعض الأعمدة:
PID PPID COMM UID CREATION TIME
3976 3968 bash 1000 2014-06-24 10:36:40.116245 UTC
8099 2 flush-8:0 0 2014-06-24 12:02:44.857271 UTC
8105 2 kworker/0:1 0 2014-06-24 12:10:16.003074 UTC
8471 2575 sleep 0 2014-06-24 12:52:39.968178 UTC
8492 2 kworker/0:2 0 2014-06-24 12:55:34.003716 UTC
8497 3373 sshd 0 2014-06-24 12:58:52.986083 UTC
8502 8497 sshd 1001 2014-06-24 12:58:55.472387 UTC
8503 8502 bash 1001 2014-06-24 12:58:55.474586 UTC
8599 3976 sudo 0 2014-06-24 13:00:21.592988 UTC
8600 8599 bash 0 2014-06-24 13:00:21.598157 UTC
8601 2 kworker/0:0 0 2014-06-24 13:00:36.003136 UTCالترتيب حسب وقت الإنشاء يعطيك قصة الجلسة الأخيرة: دخول عبر SSH في 12:58:52 لمستخدم رقمه 1001، ثم Shell بصلاحية root من الطرفية الرسومية في 13:00:21. ولمعرفة من هو المستخدم 1001 وما الذي شغله، نستخدم linux.psaux.PsAux الذي يعرض سطر الأوامر كاملاً، وlinux.pstree.PsTree الذي يرتب البرامج حسب الأب:
PID PPID COMM ARGS
8497 3373 sshd sshd: mark [priv]
8502 8497 sshd sshd: mark@pts/1
8503 8502 bash -bash
8599 3976 sudo sudo bash
8600 8599 bash bash
* 0x88001d375840 3373 3373 1 sshd
** 0x88001d06c800 8497 8497 3373 sshd
*** 0x88001d3a7180 8502 8502 8497 sshd
**** 0x88001ac9c740 8503 8503 8502 bashالمستخدم اسمه mark، دخل على الطرفية الثانية pts/1. ولاحظ أن سطر الأوامر لبرامج sshd يظهر فيه اسم المستخدم لأن sshd يغير اسمه بنفسه ليسهل عليك متابعته، وهذا من عادات البرامج التي يجب أن تعرفها، لأن المخترق يستطيع أن يفعل الشيء نفسه فيغير سطر أوامر برنامجه ليبدو مثل [kworker/0:1].
الاتصالات
الإضافة linux.sockstat.Sockstat تعرض كل Socket مفتوح لكل برنامج، وهي 1021 سطراً في هذا الملف أغلبها Unix Sockets بين برامج الجهاز نفسه، فاخترنا منها اتصالات الشبكة القائمة:
vol -q -s /symbols -f /evidence/linux-sample-1.bin linux.sockstat.Sockstat > sockstat.txt
awk -F'\t' '$7 ~ /AF_INET/ && $14=="ESTABLISHED"' sockstat.txt | cut -f2,3,9-14sshd 8497 TCP 192.168.201.161 22 192.168.201.1 59982 ESTABLISHED
sshd 8502 TCP 192.168.201.161 22 192.168.201.1 59982 ESTABLISHEDاتصال واحد فقط، وهو جلسة mark من العنوان 192.168.201.1، وهذا العنوان في الغالب جهاز المضيف لأن السيرفر نفسه 192.168.201.161 على الشبكة نفسها وكرت الشبكة من VMware كما يظهر من بداية عنوان MAC في linux.ip.Addr:
NetNS Index Interface MAC Promiscuous IP Prefix Scope Type State
- 2 eth0 00:0c:29:8f:ed:ca False 192.168.201.161 24 global UPولاحظ العمود Promiscuous، فلو كانت قيمته True لكان كرت الشبكة يلتقط كل ما يمر عليه، وهذا ما يفعله برنامج تنصت Sniffer، فهو سؤال يستحق أن تنظر إليه في كل سيرفر.
سجل Bash من الذاكرة
هذه الإضافة من أهم ما يقدمه تحليل الذاكرة لسيرفرات Linux، فهي تقرأ سجل الأوامر من ذاكرة كل عملية Bash مباشرة، وليس من الملف .bash_history، فإذا مسح المخترق الملف أو منع الكتابة فيه بقيت أوامره في الذاكرة ما دامت الجلسة مفتوحة:
vol -q -s /symbols -f /evidence/linux-sample-1.bin linux.bash.BashPID Process CommandTime Command
3976 bash 2014-06-24 10:36:40.000000 UTC su
3976 bash 2014-06-24 10:36:40.000000 UTC sudo bash
3976 bash 2014-06-24 10:36:42.000000 UTC sudo bash
3976 bash 2014-06-24 10:37:19.000000 UTC su
3976 bash 2014-06-24 12:44:41.000000 UTC svn co http://lime-forensics.googlecode.com/svn/trunk lime
3976 bash 2014-06-24 12:45:09.000000 UTC ls
3976 bash 2014-06-24 12:45:09.000000 UTC cd lime
3976 bash 2014-06-24 12:45:10.000000 UTC cd src
3976 bash 2014-06-24 12:45:11.000000 UTC clear
3976 bash 2014-06-24 12:45:11.000000 UTC ls
3976 bash 2014-06-24 12:45:13.000000 UTC make
3976 bash 2014-06-24 12:45:17.000000 UTC clear
3976 bash 2014-06-24 12:48:14.000000 UTC ls
3976 bash 2014-06-24 12:48:22.000000 UTC history
3976 bash 2014-06-24 12:48:40.000000 UTC sudo bash
3976 bash 2014-06-24 12:49:00.000000 UTC su
3976 bash 2014-06-24 12:55:17.000000 UTC sudo bash
3976 bash 2014-06-24 12:57:37.000000 UTC ifconfig
3976 bash 2014-06-24 12:57:53.000000 UTC /sbin/ifconfig
3976 bash 2014-06-24 13:00:22.000000 UTC sudo bash
8503 bash 2014-06-24 12:59:50.000000 UTC sudo
8503 bash 2014-06-24 12:59:52.000000 UTC sudo bash
8503 bash 2014-06-24 13:00:02.000000 UTC su
8503 bash 2014-06-24 13:00:12.000000 UTC cat /etc/passwd
8503 bash 2014-06-24 13:00:17.000000 UTC cat /etc/group
8600 bash 2014-06-24 13:00:22.000000 UTC halt
8600 bash 2014-06-24 13:00:22.000000 UTC exitفي المخرج أعلاه لاحظ التالي:
- الجلسة 3976 نزلت أداة LiME لأخذ نسخة الذاكرة وبنتها بالأمر
makeعلى الجهاز نفسه، وهذا يفسر وجود العينة. ولكن هذا ما لا يجب أن تفعله في حادثة حقيقية، والسبب أن التنزيل والبناء يكتبان ملفات على قرص الجهاز المشكوك فيه وقد يكتبان فوق ملفات محذوفة تحتاجها، فابن الأداة مسبقاً على جهاز مماثل وشغلها من قرص خارجي، كما ذكرنا في دليل التحقيق الرقمي للمبتدئين. - الأوامر الأربعة الأولى في الجلسة 3976 تحمل الوقت 10:36:40 و10:36:42 و10:37:19، وهو قريب جداً من وقت بدء الجلسة نفسها، والسبب أن Bash يقرأ الأوامر القديمة من ملف السجل عند بدء الجلسة ويعطيها ذلك الوقت، فلا تبن استنتاجاً على وقت أمر قبل أن تميز بين ما قرئ من الملف وما كتب في الجلسة.
- المستخدم mark الذي دخل عبر SSH جرب
sudoوsuثم قرأ/etc/passwdو/etc/group. ولا يوجد في شجرة البرامج أي Shell بصلاحية root تحت جلسته، فالظاهر أن محاولاته لم تعطه صلاحيات، ولكن الذاكرة وحدها لا تجيب هل نجحتsuثم خرج، والجواب في سجل الدخول على القرص/var/log/auth.log، وهذا مثال لماذا تحتاج الذاكرة والقرص معاً. - الجلسة 8600 بصلاحية root كتبت
haltلإيقاف الجهاز، وهو آخر ما حدث قبل أخذ النسخة.
هل في النواة Rootkit؟
البرنامج الذي يخفي نفسه بشكل جاد لا يكتفي بتغيير اسمه، وإنما يحمل وحدة نواة Kernel Module تعدل جدول دوال النظام System Call Table حتى لا يراه ps ولا ls، وهذا ما يسمى Rootkit. وفي Volatility ثلاث إضافات لهذا السؤال، فشغلناها:
vol -q -s /symbols -f /evidence/linux-sample-1.bin linux.malware.hidden_modules.Hidden_modules
vol -q -s /symbols -f /evidence/linux-sample-1.bin linux.malware.check_syscall.Check_syscall > syscall.txt
awk -F'\t' 'NR>4 && $5=="UNKNOWN"' syscall.txt | wc -l
vol -q -s /symbols -f /evidence/linux-sample-1.bin linux.lsmod.Lsmodhidden_modulesلم يجد أي وحدة مخفية من قائمة الوحدات.check_syscallعرض 661 مدخلاً في جداول دوال النظام، وكل مدخل يشير إلى دالة معروفة في النواة مثلsys_read، ولا يوجد أي مدخل يشير إلى عنوان غير معروف UNKNOWN، وهذا ما تبحث عنه، لأن الدالة التي يضعها الـ Rootkit تكون خارج النواة الأصلية.lsmodعرض 79 وحدة كلها من وحدات Debian المعروفة، ولا توجد بينها وحدة LiME، أي أنها أزيلت بعد أخذ النسخة أو أن النسخة أخذت بطريقة أخرى، والمستودع لا يذكر ذلك.
وشغلنا أيضاً linux.malware.malfind.Malfind الذي يبحث عن مناطق ذاكرة قابلة للكتابة والتنفيذ معاً في كل برنامج، فلم يجد شيئاً، وقد استغرق وحده 149 ثانية، أي أطول من كل الإضافات الأخرى مجتمعة.
تحليل ذاكرة Windows 10
معلومات النظام والرموز
في Windows لا تحتاج أن تعرف النسخة مسبقاً، فالإضافة windows.info تبحث عن توقيع ملف PDB الخاص بالنواة داخل الذاكرة، ثم تبحث عن ملف الرموز المناسب. وشغلناها أولاً بالخيار --offline ومعها مجلد الرموز الجاهز:
vol -q --offline -s /symbols -f /evidence/win-10_19041-2025_03.dmp windows.info.InfoVariable Value
Kernel Base 0xf8043601f000
DTB 0x6d4000
Symbols file:///symbols/windows/ntkrnlmp.pdb/167FE94B5641C005AC3036212A01F8DC-1.json
Is64Bit True
IsPAE False
layer_name 0 WindowsIntel32e
memory_layer 1 WindowsCrashDump64Layer
base_layer 2 FileLayer
NTBuildLab 19041.1.amd64fre.vb_release.1912
Major/Minor 15.19041
KeNumberProcessors 1
SystemTime 2025-03-06 17:59:20+00:00
NtSystemRoot C:\Windows
NtProductType NtProductWinNtالملف من نوع Crash Dump، وهو الشكل الذي يكتبه Windows نفسه عند الانهيار وتكتبه بعض أدوات النسخ، وVolatility تعرف ذلك وحدها فتضع الطبقة WindowsCrashDump64Layer. والوقت SystemTime هو وقت أخذ النسخة، وهذا الرقم أول ما تكتبه في التقرير.
ثم جربنا ما يحدث بدون مجلد الرموز. مع --offline ظهرت رسالة الرموز نفسها التي رأيناها في Linux، وبدون --offline نزل Volatility الملف وحده من سيرفر Microsoft:
Downloading http://msdl.microsoft.com/download/symbols/ntkrnlmp.pdb/167FE94B5641C005AC3036212A01F8DC1/ntkrnlmp.pdbثم حوله وحفظ الناتج في مجلد الرموز داخل مكتبة Python نفسها بحجم 578 KiB مضغوطاً، واستغرق التشغيل الأول كله نحو خمس دقائق، بينما استغرق windows.pslist تسع ثوان عندما كان ملف الرموز موجوداً. وهذا مهم إذا كان جهاز التحليل معزولاً عن الإنترنت كما يجب أن يكون في القضايا الحساسة، فعليك أن تجهز ملفات الرموز لنسخ Windows عندك مسبقاً، أو أن تسمح لجهاز التحليل بالوصول إلى msdl.microsoft.com فقط.
شجرة البرامج وسطر الأوامر
أظهرت windows.pslist 120 عملية، والشجرة windows.pstree هي التي تعطيك المعنى، فهذه البرامج التي شغلها المستخدم من واجهة Windows:
*** 784 4912 cmd.exe 0x840f6fc19080 2025-03-06 17:51:44.000000 UTC N/A \Device\HarddiskVolume4\Windows\System32\cmd.exe
**** 3896 784 conhost.exe 0x840f6fc35080 2025-03-06 17:51:49.000000 UTC N/A \Device\HarddiskVolume4\Windows\System32\conhost.exe
*** 2968 4912 notepad.exe 0x840f70106080 2025-03-06 17:52:33.000000 UTC N/A \Device\HarddiskVolume4\Windows\System32\notepad.exeالأب 4912 هو explorer.exe، أي أن المستخدم فتحهما بنفسه، والمسار هو مسار النظام الصحيح. وفي التحقيق الحقيقي تسأل نفسك عن كل برنامج: هل الأب منطقي؟ فـ cmd.exe تحت explorer.exe طبيعي، أما تحت w3wp.exe وهو خادم IIS، أو تحت winword.exe، فهذا من أوضح علامات الاختراق. ثم windows.cmdline لسطر الأوامر:
912 sshd.exe "C:\Program Files\OpenSSH\sshd.exe"
784 cmd.exe "C:\Windows\system32\cmd.exe"
2968 notepad.exe "C:\Windows\system32\notepad.exe"
7352 MpCmdRun.exe "C:\Program Files\Windows Defender\MpCmdRun.exe" SignatureUpdate -ScheduleJob -RestrictPrivileges -Reinvoke
5044 MpCmdRun.exe "C:\Program Files\Windows Defender\MpCmdRun.exe" SignaturesUpdateService -ScheduleJob -UnmanagedUpdateلا يوجد شيء مشفر بـ Base64 بعد powershell -enc، ولا برنامج من AppData\Local\Temp، وهما أكثر ما تجده في حوادث Windows. وسألنا windows.cmdscan عن الأوامر المكتوبة في نافذة cmd.exe فأجاب بأنه لم يجد سجل أوامر في الـ conhost التابع لها، أي أن المستخدم فتح النافذة ولم يكتب فيها شيئاً قبل أخذ النسخة، أو أن السجل لم يعد في الذاكرة.
الاتصالات
vol -q --offline -s /symbols -f /evidence/win-10_19041-2025_03.dmp windows.netscan.NetScan > netscan.txt
grep ESTABLISHED netscan.txt | cut -f2-10TCPv4 10.0.0.4 49710 168.63.129.16 32526 ESTABLISHED 1908 WaAppAgent.exe 2025-03-06 17:50:18.000000 UTC
TCPv4 10.0.0.4 49709 20.7.2.167 443 ESTABLISHED 1008 svchost.exe 2025-03-06 17:50:17.000000 UTC
TCPv4 10.0.0.4 3389 10.1.130.4 41982 ESTABLISHED 1016 svchost.exe 2025-03-06 17:49:21.000000 UTC
TCPv4 10.0.0.4 49679 168.63.129.16 32526 ESTABLISHED 1944 WindowsAzureGu 2025-03-06 17:49:50.000000 UTC
TCPv4 10.0.0.4 49688 168.63.129.16 80 ESTABLISHED 1944 WindowsAzureGu 2025-03-06 17:50:02.000000 UTC
TCPv4 10.0.0.4 49922 150.171.29.10 443 ESTABLISHED 5644 SearchApp.exe 2025-03-06 17:56:49.000000 UTC
TCPv4 10.0.0.4 49749 20.7.2.167 443 ESTABLISHED 1008 svchost.exe 2025-03-06 17:51:21.000000 UTCهنا تحتاج أن تعرف بيئتك حتى تفرق بين الطبيعي والغريب. فالعنوان 168.63.129.16 الذي يتصل به وكيل Azure هو العنوان الثابت الذي توثقه Microsoft لخدمات المنصة في كل جهاز افتراضي على Azure، والمنفذ 3389 هو جلسة سطح المكتب البعيد RDP من 10.1.130.4 داخل الشبكة الخاصة، وهي جلسة المستخدم الذي فتح cmd.exe وnotepad.exe. ولو رأيت المنفذ 3389 متصلاً بعنوان عام من الإنترنت لكان هذا أول ما تسأل عنه.
malfind ونتائجها الخاطئة
الإضافة windows.malware.malfind تبحث عن مناطق في ذاكرة البرامج صلاحيتها PAGE_EXECUTE_READWRITE وليست مرتبطة بملف على القرص، وهذا شكل الكود المحقون Injected Code الذي يكتبه المخترق في ذاكرة برنامج سليم. وعلى هذا الجهاز السليم أعطتنا ست نتائج:
PID Process Start VPN End VPN Tag Protection CommitCharge PrivateMemory
1944 WindowsAzureGu 0x1f6127d0000 0x1f6127dffff VadS PAGE_EXECUTE_READWRITE 2 1
1956 MsMpEng.exe 0x29ed9570000 0x29ed967cfff VadS PAGE_EXECUTE_READWRITE 269 1
1956 MsMpEng.exe 0x29ee1190000 0x29ee128ffff VadS PAGE_EXECUTE_READWRITE 256 1
1956 MsMpEng.exe 0x29ee6600000 0x29ee67fffff VadS PAGE_EXECUTE_READWRITE 512 1
1956 MsMpEng.exe 0x29ee6f50000 0x29ee705cfff VadS PAGE_EXECUTE_READWRITE 269 1
1956 MsMpEng.exe 0x29ee7610000 0x29ee771cfff VadS PAGE_EXECUTE_READWRITE 269 1خمس منها في MsMpEng.exe، وهو محرك Microsoft Defender نفسه، وواحدة في وكيل Azure. وهذه نتائج خاطئة False Positives معروفة، والسبب أن البرامج التي تولد الكود أثناء التشغيل JIT، مثل محرك مكافحة الفيروسات الذي يحاكي البرامج التي يفحصها وتطبيقات .NET مثل وكيل Azure، تحتاج ذاكرة قابلة للكتابة والتنفيذ معاً بشكل مشروع. وإذا نظرت إلى البايتات في المخرج الكامل تجد في مناطق MsMpEng تعليمات حفظ السجلات المعتادة في بداية الدوال push rsi; push rdi; push rbx… ومناطق مملوءة بالقيمة cc، وليس شكل ملف PE ولا Shellcode معروف. ولذلك لا تقم بإرسال نتيجة malfind في تقرير قبل أن تسأل: ما هذا البرنامج؟ وهل يولد كوداً أثناء التشغيل؟ وماذا في البايتات؟ ثم احفظ المنطقة بالخيار --dump وافحصها بقواعد YARA أو بأداة تحليل قبل أن تحكم.
وأخيراً شغلنا windows.malware.psxview الذي يقارن قائمة البرامج من أربعة مصادر مختلفة داخل النواة، والفكرة أن البرنامج الذي يخفي نفسه من قائمة واحدة يظهر في غيرها. وكانت الفروق كلها لبرامج انتهت قبل أخذ النسخة، مثل userinit.exe وMpCmdRun.exe الذي ظهر في المسح psscan وحده ومعه وقت انتهائه، أو لبرامج النظام مثل System وcsrss.exe التي لا تظهر في عمود csrss بطبيعتها.
ترتيب الأسئلة
الصورة التالية تجمع الأسئلة الخمسة التي سألناها بالترتيب، وما أجابت به كل عينة:

ولاحظ أن الترتيب يبدأ بالأسرع والأوضح، فقائمة البرامج تأخذ ثوان، وmalfind وpsxview يأخذان دقائق، والسبب أنك في حادثة حقيقية تريد أن تعرف بسرعة إلى أين تنظر قبل أن تشغل الإضافات الثقيلة. وإذا أردت أن تجمع المخرج في أداة أخرى فاستخدم الخيار -r json أو -r csv بدلاً من الجدول النصي، ومنه تستطيع أن تضعه في الخط الزمني للحادث كما سوف نرى في المقال القادم.
الوقت والذاكرة على جهاز التحليل
قسنا وقت كل إضافة في المختبر على ملف من الملفين، والأرقام تتغير حسب سرعة القرص والمعالج، ولكنها تعطيك الفكرة:
| الإضافة | Linux (512 MiB) | Windows (1.95 GiB) |
|---|---|---|
| banners / info | 54 ثانية | 53 ثانية |
| pslist | 37 ثانية | 9 ثوان |
| bash / cmdline | 8 ثوان | 21 ثانية |
| sockstat / netscan | 38 ثانية | 144 ثانية |
| malfind | 149 ثانية | 396 ثانية |
| psxview | غير متوفرة | 464 ثانية |
وعند إعادة تشغيل netscan وsockstat مع قياس الذاكرة، وصل أقصى استخدام للذاكرة إلى 247 MiB لـ netscan و351 MiB لـ sockstat، واستغرقا هذه المرة 317 و381 ثانية لأن الجهاز كان مشغولاً بأعمال أخرى. أي أن جهاز التحليل لا يحتاج ذاكرة كبيرة بالنسبة لحجم الملف، والذي يحدد الوقت هو قراءة القرص، فضع ملف الذاكرة على قرص SSD سريع، وحجز مساحة تكفي ضعف حجم الملف لما سوف تحفظه بالخيار --dump.
أما البديل التجاري فمثاله Volcano من Volexity، وهي أداة تحليل ذاكرة بواجهة رسومية وتعاون بين المحللين، ولا تنشر سعرها، فالصفحة الرسمية تطلب طلب عرض Demo عند تاريخ كتابة المقال في 25 أكتوبر 2026.
قبل أن تحتاجه في حادثة
- جهز الرموز: لكل نسخة نواة Linux على سيرفراتك ابن ملف الرموز بـ dwarf2json الآن، واحفظه مع اسم النسخة، والسبب أنك لن تحلل أي شيء بدونه. وتابع دليل البدء لـ Linux في التوثيق.
- جهز أداة النسخ: AVML من Microsoft ملف تنفيذي واحد لا يحتاج بناء وحدة نواة، وLiME يحتاج بناءها لكل نسخة نواة، فابنها مسبقاً على جهاز مماثل وليس على السيرفر المشكوك فيه.
- اكتب في القضية: هاش ملف الذاكرة، ونسخة Volatility، وملف الرموز المستخدم، والأمر الكامل لكل إضافة، حتى يستطيع غيرك إعادة النتيجة.
- لا تحكم بإضافة واحدة: كما رأينا في malfind، وكما رأينا في سجل Bash الذي يعطي الأوامر القديمة وقت بدء الجلسة.
الخلاصة
وصلنا لنهاية الموضوع، وأهم ما ظهر في المختبر:
- العينات الرسمية في مستودع volatility3-test-data هي أسهل طريقة للتعلم بملفات معروفة المصدر، ومعها الهاش وحزم الرموز.
- تحليل ذاكرة Linux يتوقف تماماً بدون ملف رموز للنواة نفسها، وWindows ينزل رموزه من Microsoft وحده إذا سمحت له بالإنترنت، فجهز الرموز قبل الحادثة.
- سجل Bash في الذاكرة أظهر 27 أمراً من ثلاث جلسات، منها محاولات
sudoوsuلمستخدم دخل عبر SSH، وهذا ما لا تجده إذا مسح أحد ملف السجل. - malfind أعطى ست نتائج على جهاز Windows سليم، كلها في Defender ووكيل Azure، فكل نتيجة منها سؤال وليست حكماً.
- الذاكرة تجيب عن «ما الذي كان يعمل الآن»، والقرص والسجلات تجيب عن «ما الذي حدث قبل ذلك»، والتحقيق يحتاج الاثنين.
في المقال القادم بإذن الله سوف نجمع السجلات وأوقات الملفات ونتائج الذاكرة في خط زمني واحد مع Plaso وTimesketch.
دليلالتحقيق الرقمي DFIR للمبتدئين: احفظ الأدلة قبل أن تنظف السيرفرماذا تفعل عندما تشك أن سيرفرك مخترق، ولماذا تمسح إعادة التشغيل والتنظيف الأدلة التي تحتاجها لتعرف ما حدث.
دليلأدوات التحقيق الرقمي مفتوحة المصدر وكيف تستخدمهاالأدوات المفتوحة التي تحتاجها في كل مرحلة من التحقيق الرقمي، من نسخ القرص واسترجاع ملف محذوف إلى بناء خط زمني لما حدث.
دليلالبحث عن آثار الاختراق Hunt في كل سيرفراتك مع Velociraptorبنينا سيرفر Velociraptor وثلاثة عملاء وبحثنا عن آثار اختراق مزروعة بسبع عمليات Hunt، فظهر ما وجدته الأداة وما فاتها بإعداداتها الافتراضية.
دليلالخط الزمني للحادث Timeline مع Plaso وTimesketchبنينا خطاً زمنياً شاملاً لحادثة اختراق سيرفر بأداة Plaso، فأعاد الهجوم بالدقيقة، ثم رفعناه إلى Timesketch وشغلنا محلل تخمين كلمة المرور.تدريب عملي Practice
- نزل العينة
win-xp-laptop-2005-06-25.img.gzمن المستودع نفسه، وشغل عليهاwindows.pstreeوwindows.netscan، وقارن ما تراه بجهاز Windows 10 في المقال. - شغل
linux.bash.Bashعلى العينة مع الخيار-r json، واكتب سكربتاً صغيراً يفصل الأوامر التي تحمل وقت بدء الجلسة عن الأوامر المكتوبة بعدها.
سجل التحديثات
- أكتوبر 2026: كتابة الدليل وتحليل عينتي Linux وWindows 10 من مستودع volatility3-test-data يوم 25 أكتوبر 2026 على Volatility 3 الإصدار 2.28.2.