الفريق العربي للهندسة العكسية
Proxy Version.dll - نسخة قابلة للطباعة

+- الفريق العربي للهندسة العكسية (https://www.at4re.net/f)
+-- قسم : منتديات البرمجة - Programming Forums (https://www.at4re.net/f/forum-5.html)
+--- قسم : البرمجة بلغة السى و السى بلس بلس ++C & C (https://www.at4re.net/f/forum-17.html)
+--- الموضوع : Proxy Version.dll (/thread-2734.html)



Proxy Version.dll - Cyros - 21-05-2021

السلام عليكم
احيانا عند كسر بعض البرامج وعمل 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)
[صورة مرفقة: KnownsDlls.PNG]
لا يمكن عمل 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
[صورة مرفقة: ModuleDef.PNG]
وايضا غير اسم Target name الى version.dll
[صورة مرفقة: TargetName_Change.PNG]
الان انسخ الملفين 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
[صورة مرفقة: UniquBytes.PNG]
  ثم نطرح ذلك العنوان ب 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() بهذا الشكل
[صورة مرفقة: AfterExecuting.PNG]
وعند الضغط Check سيتنفذ الكود الموضوع في FC()
[صورة مرفقة: RR.PNG]