21-05-2021, 10:14 AM
السلام عليكم
احيانا عند كسر بعض البرامج وعمل Patching عليه بالذاكرة ثم حفظه فانه يكتشف التغيير عند تشغيله او حتى عندما نريد ان نستدعي دالة من دوال الويندوز Win32API لاظهار رسالة مثلا او تحميل مكتبة فان عناوين الدوال تتغير , في مثل هذه الحالات يكون عمل Dll Proxy لاحدى المكتبات التي يستخدمها البرنامج خيار مفتوح
او عند نعمل هاك للعبة بامكاننا عمل واجهة رسومية ليستطيع المستخدم التحكم بالقيم عن طريق عمل هوك لاحدى دوال الدايركت اكس IDirect3DDevice_Present او IDirect3DDevice_EndScene او تغير الاستدعاء بالمكان الذي تطلب اللعبة هذه التوابع لاحدى دوال التي في المكتبة التي عملنا لها DLL proxy ثم الكتابة على ال لBackBuffer
ال DLL Proxy هي مكتبة ديناميكية .DLL لها نفس اسم مكتبة يستخدمها البرنامج عند تحميله في الذاكرة مستبدلة بالمكتبة الاصلية ,قد تكون مكتبة موجودة في مجلد مكتبات النظام )C:\Windows\SysWOW64( ك version.dll,d3d8.dll,d3d9.dll,WS2_32.dll ,
توضع عادة في نفس مجلد البرنامج على ان البرنامج يحملها على انها المكتبة الاصلية وتصدر جميع الدوال التي تتيحها المكتبة الاصلية للبرنامج وعند استدعاء دالة من تلك الدوال تمررها للمكتبة الاصلية
المكتبات المعروفة KnownDlls هي معظم المكتبات الشائعة التي يستخدمها جميع البرامج في الويندوز
وجدت من اجل تسريع عملية تحميل هذه المكتبات ولاسباب امنية (تجدها في الريجيستري
لا يمكن عمل Dll proxy بنفس الطريقة المكتبات العادية فعند تشغيل البرنامج وتحميل المكتبات يبحث النظام اولا عنها ان كانت ضمن مكتبات KnownDlls ثم مجلد البرنامج ثم في مجلد مكتبات النظام
في ويندوز 2000 بالامكان عمل dll proxy لل KnownDlls وضع ملف .local ثم بداخله المكتبة
بالامكان عدم تضمين ال KnownsDlls عبر كتبابتها في الريجستري في HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager في القيمة ExcludeFromKnownDlls
تصدر ال DLL Proxy جميع الدوال التي في المكتبة الاصلية للبرنامج مثلا كل برنامج يحمل المكتبة Version.dll بامكانه استدعاء الدوال
يجب ان نجعل المكتبة تصدر هذه الدوال للبرنامج التي سيحملها
سبق وان كتب كود version.dll proxy على
Visual Studio > New Project > Win32 application
اختر DLL , Empty Project
P2
انشئ ملفين Version.cpp && Version.def
ثم افتح
Project > Properties > Linker > Input
ادخل في حقل Module Definistion
Version.def
وايضا غير اسم Target name الى version.dll
الان انسخ الملفين TryHard.cpp الى Version.cpp و Source.def الى Version.def
حيث Source.def يحتوي على تعريف بالدوال المصدرة export functions و TryHard.cpp يحتوي على نقطة البداية DllMain و تعريف الدوال وتحميل المكتبة الاصلية وتحويل الدوال للمكتبة الاصلية
الدالة Payload() ليست من دوال المكتبة version.dll لذا نحذفها
الان جرب ان تعمل build وتضع المكتبة بداخل اي برنامج يستخدم المكتبة version.dll ستلاحظ انه يحملها ويحمل ايضا المكتبة version.dll الموجودة في مجلد مكتبات النظام يعني هناك مكتبتين version.dll بنفس الاسم
سنجرب الان ان نعدل على crackme تستخدم المكتبة version.dll عند تحميلها في الذاكرة وعند الضغط على Check ستتنفذذ دالة FC() سنكتبها في هذه المكتبة (Proxy version.dll)
حسنا الان يجب ان نغير مكان استدعاء الدالة MessageBoxA في ال crackme ونجعله استدعاء الى دالتنا FC() يجب ان نعلم ال crackme هذه عناوينها ليس ثابتة تتغير عناوين الدوال كل مرة نفتحها لانها Randomized Base Address لذا يجب ان نبحث عن نمط بايتات فريدة قريبة من تعليمة استدعاء الدالة MessageBoxA() والتي تظهر في الصورة عند العنوان 01246D0F الى العنوان 01246D18 والتي هي
C7 45 FC FF FF FF FF 8B F4
ثم نطرح ذلك العنوان ب 0x11 فيكزن عنوان دفع اول قيمة مستخدمة الدالة MessageBoxA للمكدس
سنقوم بوضع CALL EAX الى FC() الموجودة في مكتبتنا قبل دفع قيم ال MessageBoxA() للمكدس ولكن قبل هذا لابد من ان يكون قيمة EAX تساوي عنوان دالتنا FC() لذا يجب كتابة MOV EAX,FCAddress
وسنقوم بكتابة تعليمات NOP على استدعاء الدالة MessageBoxA();
سنكتب كود يفعل هذا في نقطة البداية DllMain
ضع هذه الدالتان بعد يتضمين المكتبات #include لنستدعيها
FindPattern سنستخدم
اول بارامتر هو العنوان الذي تبدا منه بحث , سنستخدم الدالة GetModuleHandle لمعرفة العنوان الذي يبدأ منه Crackme.exe
ثاني بارامتر هو الطول يعني لاي عنوان توقف مضاف العنوان التي بدات منه , نستطيع معرفة طول ال crackme من ال MemoryMap
ثالث بارامتر هو البايتات الهدف ونكتبه بالهكس
رابع بارامتر هو البايتات المعلومة , بما ان كل بايتاتنا معلومة سنضعها كلها على انها "x"
اما عن الكتابة في ذاكرة البرنامج فهنالك طريقتان
1- استخدام الدالة WriteProcessMemory() ولكن احيانا تخفق هذه الدالة وترجع PARAMETER_INVALID
2- استخدام الدالة VirtualAlloc
3- الكتابة بشكل مباشر عبر المسجلات , مثال
MOV EAX,OurTargetAddress
mov ebx,bytesShouldBeWritten
mov dword ptr ds:[eax],ebx
تتطلب هذه الطريقة استخدام الدالة VirtualProtect لانه لايوجد صلاحيات الكتابة على .text section والا سيحدث استثناء Access violation
عند استدعاء دالتنا FC() سيقوم بالرجوع وتنفيذ تعليمات NOP مما سيئدي ببطئ التنفيذ (بشكل صغير جدا)
لذا سنقوم بالكتابة على عنوان الرجوع في الدالة FC()
|
سيكون كود الخاص بنا بهذا الشكل
"
وعند الضغط Check سيتنفذ الكود الموضوع في FC()
احيانا عند كسر بعض البرامج وعمل Patching عليه بالذاكرة ثم حفظه فانه يكتشف التغيير عند تشغيله او حتى عندما نريد ان نستدعي دالة من دوال الويندوز Win32API لاظهار رسالة مثلا او تحميل مكتبة فان عناوين الدوال تتغير , في مثل هذه الحالات يكون عمل Dll Proxy لاحدى المكتبات التي يستخدمها البرنامج خيار مفتوح
او عند نعمل هاك للعبة بامكاننا عمل واجهة رسومية ليستطيع المستخدم التحكم بالقيم عن طريق عمل هوك لاحدى دوال الدايركت اكس IDirect3DDevice_Present او IDirect3DDevice_EndScene او تغير الاستدعاء بالمكان الذي تطلب اللعبة هذه التوابع لاحدى دوال التي في المكتبة التي عملنا لها DLL proxy ثم الكتابة على ال لBackBuffer
ال DLL Proxy هي مكتبة ديناميكية .DLL لها نفس اسم مكتبة يستخدمها البرنامج عند تحميله في الذاكرة مستبدلة بالمكتبة الاصلية ,قد تكون مكتبة موجودة في مجلد مكتبات النظام )C:\Windows\SysWOW64( ك version.dll,d3d8.dll,d3d9.dll,WS2_32.dll ,
توضع عادة في نفس مجلد البرنامج على ان البرنامج يحملها على انها المكتبة الاصلية وتصدر جميع الدوال التي تتيحها المكتبة الاصلية للبرنامج وعند استدعاء دالة من تلك الدوال تمررها للمكتبة الاصلية
المكتبات المعروفة KnownDlls هي معظم المكتبات الشائعة التي يستخدمها جميع البرامج في الويندوز
وجدت من اجل تسريع عملية تحميل هذه المكتبات ولاسباب امنية (تجدها في الريجيستري
(HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs)
لا يمكن عمل Dll proxy بنفس الطريقة المكتبات العادية فعند تشغيل البرنامج وتحميل المكتبات يبحث النظام اولا عنها ان كانت ضمن مكتبات KnownDlls ثم مجلد البرنامج ثم في مجلد مكتبات النظام
في ويندوز 2000 بالامكان عمل dll proxy لل KnownDlls وضع ملف .local ثم بداخله المكتبة
بالامكان عدم تضمين ال KnownsDlls عبر كتبابتها في الريجستري في HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager في القيمة ExcludeFromKnownDlls
تصدر ال DLL Proxy جميع الدوال التي في المكتبة الاصلية للبرنامج مثلا كل برنامج يحمل المكتبة Version.dll بامكانه استدعاء الدوال
GetFileVersionInfoA
GetFileVersionInfoByHandle
GetFileVersionInfoExA
GetFileVersionInfoExW
GetFileVersionInfoSizeA
GetFileVersionInfoSizeExA
GetFileVersionInfoSizeExW
GetFileVersionInfoSizeW
GetFileVersionInfoW
VerFindFileA
VerFindFileW
VerInstallFileA
VerInstallFileW
VerLanguageNameA
VerLanguageNameW
VerQueryValueA
VerQueryValueW
ونفس الشيئ اذا اردنا ان نعمل Version.dll proxyيجب ان نجعل المكتبة تصدر هذه الدوال للبرنامج التي سيحملها
سبق وان كتب كود version.dll proxy على
[url=https://github.com/advancedmonitoring/ProxyDll/blob/master/01_TryHard/][font]https://github.com/advancedmonitoring/ProxyDll/blob/master/01_TryHard/[/font][/url]
افتحVisual Studio > New Project > Win32 application
اختر DLL , Empty Project
P2
انشئ ملفين Version.cpp && Version.def
ثم افتح
Project > Properties > Linker > Input
ادخل في حقل Module Definistion
Version.def
وايضا غير اسم Target name الى version.dll
الان انسخ الملفين TryHard.cpp الى Version.cpp و Source.def الى Version.def
حيث Source.def يحتوي على تعريف بالدوال المصدرة export functions و TryHard.cpp يحتوي على نقطة البداية DllMain و تعريف الدوال وتحميل المكتبة الاصلية وتحويل الدوال للمكتبة الاصلية
الدالة Payload() ليست من دوال المكتبة version.dll لذا نحذفها
الان جرب ان تعمل build وتضع المكتبة بداخل اي برنامج يستخدم المكتبة version.dll ستلاحظ انه يحملها ويحمل ايضا المكتبة version.dll الموجودة في مجلد مكتبات النظام يعني هناك مكتبتين version.dll بنفس الاسم
سنجرب الان ان نعدل على crackme تستخدم المكتبة version.dll عند تحميلها في الذاكرة وعند الضغط على Check ستتنفذذ دالة FC() سنكتبها في هذه المكتبة (Proxy version.dll)
حسنا الان يجب ان نغير مكان استدعاء الدالة MessageBoxA في ال crackme ونجعله استدعاء الى دالتنا FC() يجب ان نعلم ال crackme هذه عناوينها ليس ثابتة تتغير عناوين الدوال كل مرة نفتحها لانها Randomized Base Address لذا يجب ان نبحث عن نمط بايتات فريدة قريبة من تعليمة استدعاء الدالة MessageBoxA() والتي تظهر في الصورة عند العنوان 01246D0F الى العنوان 01246D18 والتي هي
C7 45 FC FF FF FF FF 8B F4
ثم نطرح ذلك العنوان ب 0x11 فيكزن عنوان دفع اول قيمة مستخدمة الدالة MessageBoxA للمكدس
سنقوم بوضع CALL EAX الى FC() الموجودة في مكتبتنا قبل دفع قيم ال MessageBoxA() للمكدس ولكن قبل هذا لابد من ان يكون قيمة EAX تساوي عنوان دالتنا FC() لذا يجب كتابة MOV EAX,FCAddress
وسنقوم بكتابة تعليمات NOP على استدعاء الدالة MessageBoxA();
سنكتب كود يفعل هذا في نقطة البداية DllMain
ضع هذه الدالتان بعد يتضمين المكتبات #include لنستدعيها
FindPattern سنستخدم
bool Mask(const BYTE* pData, const BYTE* bMask, const char* szMask)
{
for (; *szMask; ++szMask, ++pData, ++bMask)
if (*szMask == 'x' && *pData != *bMask)
return false;
return (*szMask) == NULL;
}
DWORD FindPattern(DWORD dwAddress, DWORD dwLen, BYTE *bMask, char * szMask)
{
for (DWORD i = 0; i<dwLen; i++)
if (Mask((BYTE*)(dwAddress + i), bMask, szMask))
return (DWORD)(dwAddress + i);
return 0;
}
اول بارامتر هو العنوان الذي تبدا منه بحث , سنستخدم الدالة GetModuleHandle لمعرفة العنوان الذي يبدأ منه Crackme.exe
ثاني بارامتر هو الطول يعني لاي عنوان توقف مضاف العنوان التي بدات منه , نستطيع معرفة طول ال crackme من ال MemoryMap
ثالث بارامتر هو البايتات الهدف ونكتبه بالهكس
رابع بارامتر هو البايتات المعلومة , بما ان كل بايتاتنا معلومة سنضعها كلها على انها "x"
اما عن الكتابة في ذاكرة البرنامج فهنالك طريقتان
1- استخدام الدالة WriteProcessMemory() ولكن احيانا تخفق هذه الدالة وترجع PARAMETER_INVALID
2- استخدام الدالة VirtualAlloc
3- الكتابة بشكل مباشر عبر المسجلات , مثال
MOV EAX,OurTargetAddress
mov ebx,bytesShouldBeWritten
mov dword ptr ds:[eax],ebx
تتطلب هذه الطريقة استخدام الدالة VirtualProtect لانه لايوجد صلاحيات الكتابة على .text section والا سيحدث استثناء Access violation
عند استدعاء دالتنا FC() سيقوم بالرجوع وتنفيذ تعليمات NOP مما سيئدي ببطئ التنفيذ (بشكل صغير جدا)
لذا سنقوم بالكتابة على عنوان الرجوع في الدالة FC()
|
سيكون كود الخاص بنا بهذا الشكل
"
#include <Windows.h>
DWORD CallMessageBoxAddress;
DWORD* FCAddress;
void FC() {
__asm {
mov eax, CallMessageBoxAddress
mov dword ptr ds:[esp+0xD0],eax // [esp+0xD0] = return address
}
MessageBoxA(NULL, "Hello from proxy Version.dll", "PROXY VERSION.DLLL ", 0);
}
bool Mask(const BYTE* pData, const BYTE* bMask, const char* szMask)
{
for (; *szMask; ++szMask, ++pData, ++bMask)
if (*szMask == 'x' && *pData != *bMask)
return false;
return (*szMask) == NULL;
}
DWORD FindPattern(DWORD dwAddress, DWORD dwLen, BYTE *bMask, char * szMask)
{
for (DWORD i = 0; i<dwLen; i++)
if (Mask((BYTE*)(dwAddress + i), bMask, szMask))
return (DWORD)(dwAddress + i);
return 0;
}
.....
BOOL WINAPI DllMain(HMODULE, DWORD r, LPVOID)
{
if (r == DLL_PROCESS_ATTACH)
{
VersionDllInit();
FCAddress = (DWORD)&FC; // Address of FC
DWORD OLDP;
VirtualProtect((LPVOID)GetModuleHandleA("crackme.exe"), 0x24000, PAGE_EXECUTE_READWRITE, &OLDP);
CallMessageBoxAddress = FindPattern((DWORD)GetModuleHandleA("crackme.exe") ,0x24000, (BYTE*) "\xC7\x45\xFC\xFF\xFF\xFF\xFF\x8B\xF4", "xxxxxxxxx");
if (CallMessageBoxAddress == 0) {MessageBoxA(NULL, "Could not find pattern", "ERRIR", 0);}
CallMessageBoxAddress -= 0x05; // Address of CALL MessageBoxA
__asm {
mov eax, CallMessageBoxAddress
mov ebx, 0x90909090
mov dword ptr ds : [eax], ebx
inc eax // the CALL MessageBoxA instuciotn 5 bytes so we need to write to last byte also
mov dword ptr ds : [eax], ebx
sub eax, 0x0D // Address of push first value to stack for MessageBoxA
mov byte ptr ds : [eax], 0xB8 // MOV EAX,FCAddress
inc eax
mov ebx, FCAddress
mov dword ptr ds : [eax], ebx
add eax, 0x04
mov word ptr ds:[eax],0xD0FF // CALL EAX
add eax,0x02
mov word ptr ds : [eax], 0x9090 // NOP another pushing instuciotn
add eax,0x02
mov CallMessageBoxAddress,eax
}
}
return TRUE;
}
بعد تنفيذ الاسمبلي كود سيكون مكان استدعاء الدالة MessageBoxA() بهذا الشكلوعند الضغط Check سيتنفذ الكود الموضوع في FC()