إقتباس :الدرس منقول لــ: Ph-RevIng
الدرس 1 : كيف تبرمج Inject Dll / DLL باستعمال الدالة CreateRemoteThread
بسم الله الرحمن الرحيم
السلام عليكم ورحمة الله تعالى وبركاته
سنتكلم اليوم عن موضوع مهم جدا في مجال البرمجة " Api Hooking "
اضن ان اغلبكم يعرف ما المقصود بالـApi Hooking , ومن لا يعرف فالمقصود بشكل مبسط هو خطف ( ان صح التعبير ) الدوال او بمعنى اخرلديك برنامج شغال على الويندوز ويستدعي بعضا من دوال الـApi وخطرت لك فكرة ببالك سواء منع دالة (A) من العمل او عمل Filtering او اي شيئ ... اي انه عند استدعاء الدالة ( A ) سيتم استدعاء دالتك (B) بدلا من (A ) ولك القرار .
هدا هو المفهوم بشكل اجمالي .
ما سنستعلمه في هدا الموضوع هو الـGlobal Api Hooking بمعنى Hook على اي برنامج
يعمل على الويندوز او عليها كلها
الحكاية طويلة جدا سنتطرق الى عدة امور مع الشرح الدقيق هدا قررت تقسيم الموضوع
الى تمانية دروس (وجدت اول اثنين فقط ) :
الدرس 1 : كيف تبرمج Inject Dll / DLL باستعمال الدالة CreateRemoteThread
الدرس 2 : Inject DLL بالدالة SetWindowsHookEx
الدرس 3 : Inject DLL باستعمال Code injecting وتحويل EIP
الدرس 4 : Inject DLL باستعمال The IvanOv m4l4ri4 (اسم مخترعها )
الدرس 5 : Unload DLL
الدرس 6 : Api Hooking باستعمال الـIAT Patching
الدرس 7 : Api Hooking باستعمال الـ Hot Patching
الدرس 8 : Api Hooking باستعمال الـInline Patching
اللغة المستخدمة : سي / القليل من الاسمبلي
ان شاء الله يبقى الموضوع مرجعا لكل عربي باحت في هدا المجال .
كيف تبرمج DLL
Inject DLL باستعمال الدالة CreateRemoteThreadنبدأ ببرمجة DLL :
لن نتكلم كتيرا عن هدا الموضوع لان Dll التي سنربمجها بسيطة جدا
لنرى الكود الاتي:
#include<windows.h>
bool APIENTRY DllMain (HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) {
if(dwReason == DLL_PROCESS_ATTACH)
{
MessageBox(NULL,"Hello World","AT4RE",MB_OK);
}
return TRUE;
}
هدا كود DLL بسيط جدا ولن تختلف DLL التي سنربمجها عن هده
كما تلاحض في الكود وجود الدالة DllMain وهي تكافئ الدالة Main اي نقطة البدأ
Entry Point
HInstance وهو مقبض الـDLL
dwReason هده الخاصية يتم استلامها من قبل نضام التشغيل فور تحميل الـDLL
ولها اربع قيم
DLL_PROCESS_ATTACH
DLL_PROCESS_DETACH
DLL_THREAD_ATTACH
DLL_THREAD_DETACH
ما يهمنا هو DLL_PROCESS_ATTACH
اما البارامتر التالت فهو يخص نضام التشغيل
راجعو الرابط :
http://msdn.microsoft.com/en-us/library/ms682583%28VS.85%29.aspx
حسنا يبدو الكود واضحا الان فادا قام Thread بتحميل المكتبة داخل Adress Space للـProcess ستضهر رسالة MessageBox
وهده هي النتيجة التي سنصل اليها في درس اليوم
كما تلاحضون فقد تم حقن DLL في Process برنامج الميسانجر
نمر للمرحلة التانية
Inject DLL باستعمال الدالة CreateRemoteThread
لعمل الـGlobal Hook نحتاج الى حقن DLL داخل Process البرنامج الهدف وكود الـHook سيكون داخل الـDLL وتعتبر هده التي سنشرحها جيدة لعمل هدا الحقن .
ما هي الخطوات التي تتطلبها هده الطريقة ؟
1 جلب PID للـProcess الهدف (PID = Process Id
2 الحصول على الـProcess Handle
3 حجز مساحة داخل مساحة الـProcess لنتمكن من كتابة مسار الـDLL
4 انشاء Thread جديد داخل الـProcess دوره تحميل الـDLL
بعد ان عرفنا الخطوات سنقوم بشرحها واحدا واحدا
جلب PID للـProcess الهدف
سنستعمل الطريقة الروتينية بالدالة CreateToolhelp32Snapshot تم Process32Next
و بما اننا سنترك المستخدم يختار أي Process يريد حقن DLL به فسنعمل EditBox يدخل بها اسم الـ Process ونفس الامر مع الـDLL
سنقوم بجلب اسم الـProcess الدي ادخله المستخدم في الـEditBox عبر الدالة GetDlgItemText ودالك باعطائها مقبض الـEditBox
وسنعمل دالة نمرر لها الاسم المدخل و تجلب لنا الـPID ولنسمها GetPID
الدالة GetPID ستقوم بالدخول في حلقة جلب الـProcess الاول تم التاني تم التالت حتى نمر عليها كلها ودالك بالدالة Process32Next
وفي كل مرة نقارن اسم الـProcess الدي ادخله المستخدم بالاسم الحالي الدي حصلنا عليه
سيكون الكود كالاتي :
long GetPID(char* processName) {
HANDLE Snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
PROCESSENTRY32 PEN = {0};
PEN.dwSize = sizeof(PROCESSENTRY32);
if(Snap == INVALID_HANDLE_VALUE)
return 0;
if(Process32First(Snap,&PEN) == FALSE)
return 0;
while(Process32Next(Snap,&PEN) )
{
if(!strcmp(PEN.szExeFile,processName))
{
CloseHandle(Snap);
return PEN.th32ProcessID;
}
}
CloseHandle(Snap);
return 0;
}
حسنا يبدو الكود واضحا ( اي غموض ارجو طرحه )
الى هنا انتهت المرحلة الاولى .
الحصول على الـProcess Handle
تدكر ان الدالة السابقة اعادة لنا الـProcess ID (هناك فرق بين الـProcess ID والـProcess Handle )
لكي نجلب الـProcess Handle سنستخدم الدالة OpenProcess
HANDLE ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS,0,prid);
if(ProcessHandle == NULL)
return 0;
مررنا لها prid وهو الـProcess ID الدي اعادته لنا الدالة السابقة
انتهت المرحلة التانية
حجز مساحة داخل مساحة الـProcess لنتمكن من كتابة مسار الـDLL
سنقوم بحجز المساحة بالدالة VirtualAllocEx هده الدالة تتطلب الـProcess Handle
وهو البارامتر الاول لها ( تدكر اننا حصلنا عليه فوق ) , البارامتر التالت للدالة يتطلب حجم المساحة التي سيتم حجزها .
حسنا بما ان برنامجنا يسمح للمستخدم بحقن اي DLL شرط ان يدخل مسارها
سنقوم باخد هدا المسار كما اخدنا اسم الـProcess تم نحسب طوله بالدالة strlen
لكن حداري هنا فهده الدالة تحسب طول الـString دون الـNULL لدا علينا زيادة 1
يصبح الطول الدي سنحجزه يساوي طول المسار الدي ادخله المستخدم + 1
long StringL = strlen(DllPath) + 1;
DllPath هو متغير سنخزن فيه مسار DLL الدي ادخل المستخدم
نرجع للدالة VirtualAllocEx البارامتر الاخير لها يمتل نوع الحماية , سنضع PAGE_EXECUTE_READWRITE للاننا سنكتب على هدا العنوان
LPVOID EsAddr = VirtualAllocEx(ProcessHandle,NULL,StringL,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
if(EsAddr == NULL)
return 0;
بعد ان تم حجز المساحة بنجاح سنقوم بكتابة مسار DLL داخل المساحة المحجوزة
الكتابة تتم بالدالة WriteProcessMemory
البارامتر الاول لهده الدالة هو Process Handle ايضا , البارامتر التاني هو العنوان الدي تريد ان تبدأ عنده الكتابة وهنا سنستعمل عنوان المساحة التي قمنا بحجزها
البارامتر التالت هي السلسلة او ... التي تريد كتابتها سنقوم نحن هنا بكتابة مسار الـDLL
int RFW = WriteProcessMemory(ProcessHandle,EsAddr,DllPath,StringL,0);
if(RFW == 0)
return 0;
انتهت المرحلة التالتة
انشاء Thread جديد داخل الـProcess دوره تحميل الـDLL
قبل ان ننشأ Thread سنحتاج عنوان الدالة LoadLibrary لان هده الدالة هي التي ستحمل الـDLL من داخل البرنامج , لجلب عنوانها سنستعمل الدالة GetProcAdress
LPTHREAD_START_ROUTINE addrLoadLibrary = (LPTHREAD_START_ROUTINE)GetProcAddress(LoadLibrary("kernel32"),"LoadLibraryA");
حسنا الان بقيت لنا خطوة واحدة وهي انشاء Thread داخل الـDLL بعد تشغيله يقوم بتحمل DLL وتشغيلها
لنتمكن من عمل هدا الامر سنستخدم الدالة CreateRemoteThread
البارامتر الاول لهده الدالة هو الـProcess Handle والدي حصلنا عليه سابقا
في البارامتر الرابع سنضع عنوان الدالة LoadLibrary والبارامتر الخامس عنوان التي String تحمل مسار الـDLL
HANDLE retour = CreateRemoteThread(ProcessHandle,NULL,0,addrLoadLibrary,EsAddr,0,&ThreadID);
if(retour == NULL)
MessageBox(NULL,"Cannot load Dll","AT4RE",MB_OK);
return 0;
ما بقي علينا هو استعمال الدالة WaitForSingleObject للانتضار حتى يتم انشاء الـThread
تم نقوم بتحرير المساحة وجعلها free تم تحرير المقابض
WaitForSingleObject(retour,INFINITE); VirtualFreeEx(ProcessHandle,EsAddr,0,MEM_DECOMMIT); CloseHandle(ProcessHandle); CloseHandle(retour);
الكود النهائي للبرنامج :
#include <windows.h>
#include <stdio.h>
#include <tlhelp32.h>
#include "resource.h"
BOOL CALLBACK DialogProc(HWND,UINT,WPARAM,LPARAM);
int WINAPI WinMain (HINSTANCE,HINSTANCE,PSTR,int);
int InjectDll(long prid , char* DllPath);
long GetPID(char* processName);
char Process[100];
char DLLPATH[512];
HINSTANCE hInst;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
hInst=hInstance;
DialogBoxParam(hInstance, MAKEINTRESOURCE(101), NULL, (DLGPROC)DialogProc,0);
return 0;
}
BOOL CALLBACK DialogProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
switch(Message)
{
case WM_CLOSE:
{
PostQuitMessage(0);
EndDialog(hWnd,0);
}
break;
case WM_COMMAND:
{
switch ( LOWORD(wParam) ) {
case IDOK:
GetDlgItemText(hWnd,IDC_EDIT1,Process,100);
GetDlgItemText(hWnd,IDC_EDIT2,DLLPATH,512);
InjectDll(GetPID(Process),DLLPATH);
} break;
}
break;
}
return 0;
}
long GetPID(char* processName) {
HANDLE Snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
PROCESSENTRY32 PEN = {0};
PEN.dwSize = sizeof(PROCESSENTRY32);
if(Snap == INVALID_HANDLE_VALUE)
return 0;
if(Process32First(Snap,&PEN) == FALSE)
return 0;
while(Process32Next(Snap,&PEN) )
{
if(!strcmp(PEN.szExeFile,processName))
{
CloseHandle(Snap);
return PEN.th32ProcessID;
}
}
CloseHandle(Snap);
return 0;
}
int InjectDll(long prid , char* DllPath){
DWORD ThreadID;
long StringL = strlen(DllPath) + 1;
HANDLE ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS,0,prid);
if(ProcessHandle == NULL)
return 0;
LPVOID EsAddr = VirtualAllocEx(ProcessHandle,NULL,StringL,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
if(EsAddr == NULL)
return 0;
int RFW = WriteProcessMemory(ProcessHandle,EsAddr,DllPath,StringL,0);
if(RFW == 0)
return 0;
LPTHREAD_START_ROUTINE addrLoadLibrary = (LPTHREAD_START_ROUTINE)GetProcAddress(LoadLibrary("kernel32"),"LoadLibraryA");
HANDLE retour = CreateRemoteThread(ProcessHandle,NULL,0,addrLoadLibrary,EsAddr,0,&ThreadID);
if(retour == NULL)
MessageBox(NULL,"Cannot load Dll","AT4RE",MB_OK);
return 0;
MessageBox(NULL,"Dll Loaded","AT4RE",MB_OK);
WaitForSingleObject(retour,INFINITE);
VirtualFreeEx(ProcessHandle,EsAddr,0,MEM_DECOMMIT);
CloseHandle(ProcessHandle);
CloseHandle(retour);
return 1;
}
الدرس 2 : Inject DLL بالدالة SetWindowsHookEx
بسم الله الرحمن الرحيم
السلام عليكم ورحمة الله تعالى وبركاته
بعد اطلعنا في الدرس السابق عن الطريقة الاولى لحقن ملفات الـDLL
اليوم سنتكلم عن طريقة اخرى مهمة جدا باستعمال الدالة SetWindowsHookEx
موضوع اليوم يشرح هده الدالة من عدة جوانب
اولا اترككم لتتعرفو اكتر على هده الدالة من مكتبة MSDN
http://msdn.microsoft.com/en-us/library/ms644990(VS.85).aspx
يمكننا باستعمال هده الدالة عمل Hook على الرسائل التي يرسلها النضام لمختلف التطبيقات
ما المقصود بالرسائل ؟
ان كل تطبيق ( برنامج ) يحتوي على ما يسمى الـ Message Queues يمكن تسميتها بالعربي مكان لتخزين الرسائل , هده الرسائل يتم ارسالها من قبل النضام الى Message Queues فور حدوتها , فمتلا عندما تضغط فوق زر في النافدة فان النضام يرسل الرسالة WM_COMMAND
وتقوم الدالة GetMessage بجلب هده الرسالة وتمريرها للدالة DispatchMessage ومن هده الاخيرة الى الـWindow Procedure التي يقوم المستخدم بمعلاجة الرسائل داخلها داخلها
(ادا كانت الرسالة ضغطة من الكايبورد تمر الرسالة من GetMessage الى TranslateMessage تم DispatchMessage )
متلا ادا ضغط الزر كدا و كدا تضهر رسالة MessageBox
نفترض ان لدينا زر في النافدة والـID الخاص به هو 1005 نريد هدا الزر عند الضغط عليه ان يقوم بعمل ما ...
عندما يقوم المستخدم بالضغط فوق الزر فان النضام يولد الرسالة WM_COMAND في
الـ Message Queues تم تقوم الدالة GetMessage بجلبها وارسالها الـWindow Procedure حيت يتم تحليلها على انها WM_COMMAND
تم تحديد الـID ادا كان هو نفسه للزر الدي وضعنا نقوم بتنفيد ما كنا نود الزر ان يقوم به
اضن ان مفهوم الرسائل اصبح واضحا وطبعا مبرمجو الـ Api يعرفون هدا الامر حق المعرفة .
لنرجع لموضوعنا حول الدالة SetWindowHookEx ما تقوم به هده الدالة هو خطف هده الرسائل ونحن من سيبني الـWindow Procedure بمعنى ان الرسالة نحن من سيعالجها ولنا القرار نعمل اي شيئ نريده كمتال لاستعمال هده الدالة في الهندسة العكسية
لنفترض ان لديك برنامج , زر الدخول معطل Disabled تريد تفعيل هدا الزر طبعا ستستعمل احدى البرامج التي تتعامل مع النوافد متل برنامج الاخ السندباد Window Handler لكن بمجرد غلق البرنامج واعادة فتحه سيتم اعادة الزر الى وضعه السابق
لتجاوز هدا المشكل يمكن كسر البرنامج بتعديل بايت واحد فقط كما يمكن عمل هدا برمجيا باستعمال الدالة السابق دكرها .
كل ما تكلمنا عنه هو الـHook على رسائل النضام ( وليس Api Hooking )
كما يمكن للدالة عمل Hook على الكايبورد وعلى الماوس .....
لندرس بارامترات الدالة :
البارامتر الاول وهو نوع الـHook سنستعمل WH_GETMESSAGE
يعني Hook على الرسائل التي يرسلها النضام الى النافدة
البارامتر التاني وهو عنوان الـHook Procedure التي سنعالج من خلالها الرسائل ( راجع المقدمة اعلاه )
البارامتر التالت وهو مقبض الـDLL الموجودة بها الـHook Procedure
البارامتر الرابع وهو Thread ID للبرنامج الدي تريد ان تعمل عليه الـHook
قبل ان ندخل في طريقة حقن DLL بهده الدالة , ليكون الموضوع اكتر وضوحا سوف نعمل Local Hook بمعنى Hook على نافدة برنامجنا حتى نتعود على مفهوم عمل هده الدالة خطوة بخطوة وتكون مكتسباتنا كبيرة من الدرس .
سنقوم بعمل برنامج لا يحتوي على شيئ سوى نافدة ادا ضغط المستخدم على زر Entr متلا تضهر له رسالة او اي شيئ تريد ان تبرمجه حين يضغط المستخدم على الزر Entr
بما ان الـHook داخلي على برنامجنا فقط فلن نحتاج الى برمجة DLL
سنقوم بكتابة Hook Procedure داخل كود برنامجنا
نشاهد الكود اولا
#include <windows.h>
#include "resource.h"
BOOL CALLBACK DialogProc(HWND,UINT,WPARAM,LPARAM);
int WINAPI WinMain (HINSTANCE,HINSTANCE,PSTR,int);
LRESULT CALLBACK KeyProc(int,WPARAM,LPARAM);
HHOOK hHook;
HINSTANCE hInst;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
hInst=hInstance;
DialogBoxParam(hInstance, MAKEINTRESOURCE(101), NULL, (DLGPROC)DialogProc,0);
return 0;
} BOOL CALLBACK DialogProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
switch(Message)
{
case WM_CLOSE:
{
PostQuitMessage(0);
EndDialog(hWnd,0);
}
break;
case WM_COMMAND:
{
switch ( LOWORD(wParam) ) {
case IDC_BUTTON1:
{
hHook = SetWindowsHookEx(WH_MSGFILTER,KeyProc,0,GetCurrentThreadId());
} break;
case IDC_BUTTON2:
{
UnhookWindowsHookEx(hHook);
} break;
}
}
}
return 0;
}
LRESULT CALLBACK KeyProc(int nCode, WPARAM wParam, LPARAM lParam)
{
MSG *Message;
Message = (MSG*) lParam;
if(nCode != MSGF_DIALOGBOX) goto Ret;
if(Message->message == WM_KEYDOWN) {
if(Message->wParam == VK_RETURN){
MessageBox(Message->hwnd, "Hello World", "AT4RE", 0);
return 1;
}
}
Ret:
return (CallNextHookEx(hHook, nCode, wParam, lParam));
}
نتيجة البرنامج :
عند الضغط على زر Enable Hook سيتم تنصيب Hook على نافدة برنامجنا فادا تم الضغط على الزر Entr سيتم التعرف عليه واضهار رسالة MessageBox وادا تم الضغط على Disable Hook سيتم ازالة الـHook وبعدها حتى لو ضغطت Entr لن يتعرف برنامجنا على انك ضغطتها .
لنشرح الكود حتى نفهم كيف تتم العملية :
البارامتر الاول للدالة SetWindowHookEx يمتل نوع الـHook الدي تريد وضعه اخترت , بما انه على ضغطات الكايبورد فساضع WH_MSGFILTER
البارامتر التاني وهو الـ Hook Procedureالتي ستعالج الرسائل وقد سميتها KeyProc
البارامتر الثالث مقبض DLL التي تحتوي على الـCall Back بما اننا الان سنعمل Hook على نافدة برناجنا فقط فسنضعه 0 لاننا سنكتبه داخل كود البرنامج
البارامتر الرابع هو Thread ID الدي تود ان تضع عليه Hook سنضع Thread ID الخاص لبرنامجنا وهدا ما تقوم به الدالة GetCurrentThreadId
نأتي الان الى الـ Hook Procedureالتي ستعالج الرسائل
اولا يجب ان تعلم ان اي رسالة (نسبيا) سيتم ارسالها لنافدة برنامجنا ستمر من هنا اولا
بارامترات هدا الـCall Back او الـHook Proc مشروحة جيدا في مكتبة MSDN
http://msdn.microsoft.com/en-us/library/ms644976%28v=VS.85%29.aspx
في حالتنا هده سنستعمل LPARAM وهو يؤشر على Structure من النوع MSG
http://msdn.microsoft.com/en-us/library/ms644958%28VS.85%29.aspx
طبعا الكود اصبح واضحا نبدأ في تحليل البيانات فادا كانت الرسالة هي WM_KEYDOWN يعني ان هده الرسالة التي استقبلناها الان هي ضغطة من الكايبورد ومن WPARAM
(الخاص بـMSG ) يمكننا تحديد اي زر تم ضغطه حيت VK_RETURN تمتل الزر Entr
بالنسبة للدالة CallNextHookEx فهي لاستمرار في حلقة الـHook وجلب وتحليل الرسائل .
http://msdn.microsoft.com/en-us/library/ms644974(VS.85).aspx
يمكنك استعمال هده الطريقة في التحكم ببرناجك عبر الازراركما تستعمل في برامج الـKeyLogger والفايروسات التي تعطل او تبعتر ازرار الكايبورد
كما يمكنكا استعمالها في اعادة برمجة لوحة المفاتيح في حالة تعطل لك زر معين وتريد تعويضه بزر اخر لكن ستسعمل Hook شامل على الكايبورد WH_KEYBOARD_LL
( سنرى هدا في دروس لاحقة ) .
حسنا الان لنعدل قليلا على برنامجنا ونجعله يحقن ملف DLL في نفسه
بمجرد الضغط على Entr
DLL التي سيتم حقنها هي نفسها التي استعملنا في الدرس السابق
#include<windows.h>
bool APIENTRY DllMain (HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) {
if(dwReason == DLL_PROCESS_ATTACH)
{
MessageBox(NULL,"Hello World","AT4RE",MB_OK);
}
return TRUE;
}
كود البرنامج
#include <windows.h>
#include "resource.h"
BOOL CALLBACK DialogProc(HWND,UINT,WPARAM,LPARAM);
int WINAPI WinMain (HINSTANCE,HINSTANCE,PSTR,int);
LRESULT CALLBACK KeyProc(int,WPARAM,LPARAM);
HHOOK hHook;
HINSTANCE hInst;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
hInst=hInstance;
DialogBoxParam(hInstance, MAKEINTRESOURCE(101), NULL, (DLGPROC)DialogProc,0);
return 0;
}
BOOL CALLBACK DialogProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
switch(Message)
{
case WM_CLOSE:
{
PostQuitMessage(0);
EndDialog(hWnd,0);
}
break;
case WM_COMMAND:
{
switch ( LOWORD(wParam) ) {
case IDC_BUTTON1:
{
hHook = SetWindowsHookEx(WH_MSGFILTER,KeyProc,0,GetCurrentThreadId());
} break;
case IDC_BUTTON2:
{
UnhookWindowsHookEx(hHook);
} break;
}
}
}
return 0;
}
LRESULT CALLBACK KeyProc(int nCode, WPARAM wParam, LPARAM lParam)
{
MSG *Message;
Message = (MSG*) lParam;
if(nCode != MSGF_DIALOGBOX) goto Ret;
if(Message->message == WM_KEYDOWN) {
if(Message->wParam == VK_RETURN){
LoadLibrary("C:\\dllH.dll");
return 1;
}
}
Ret:
return (CallNextHookEx(hHook, nCode, wParam, lParam));
}
يبدو ان الكود واضح عوض رسالة MessageBox قمت بتحميل الـDLL بالدالة LoadLibrary وستضهر الرسالة Hello World ( الـDLL تم تحميلها داخل Adress Space لبرنامجنا )
حسنا الان نريد من هده DLL ان تحقن فور حدوت اي حدت على مستوى النافدة يعني بمجرد ان تتلقى النافدة رسالة ما تحقن الـDLL وليس بالضرورة ضغط Entr
الامر بسيط ، سنزيل فقط كود الفلترة ونضع على WH_GETMESSAGE
if(Message->message == WM_KEYDOWN) {
if(Message->wParam == VK_RETURN){
ليصبح الكود الجديد
#include <windows.h>
#include "resource.h"
BOOL CALLBACK DialogProc(HWND,UINT,WPARAM,LPARAM);
int WINAPI WinMain (HINSTANCE,HINSTANCE,PSTR,int);
LRESULT CALLBACK KeyProc(int,WPARAM,LPARAM);
HHOOK hHook;
HINSTANCE hInst;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
hInst=hInstance;
DialogBoxParam(hInstance, MAKEINTRESOURCE(101), NULL, (DLGPROC)DialogProc,0);
return 0;
}
BOOL CALLBACK DialogProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
switch(Message)
{
case WM_CLOSE:
{
PostQuitMessage(0);
EndDialog(hWnd,0);
}
break;
case WM_COMMAND:
{
switch ( LOWORD(wParam) ) {
case IDC_BUTTON1:
{
hHook = SetWindowsHookEx(WH_GETMESSAGE,KeyProc,0,GetCurrentThreadId());
} break;
case IDC_BUTTON2:
{
UnhookWindowsHookEx(hHook);
} break;
}
}
}
return 0;
}
LRESULT CALLBACK KeyProc(int nCode, WPARAM wParam, LPARAM lParam)
{
MSG *Message;
Message = (MSG*) lParam;
if(nCode != MSGF_DIALOGBOX) goto Ret;
LoadLibrary("C:\\dllH.dll");
return 1;
Ret:
return (CallNextHookEx(hHook, nCode, wParam, lParam));
}
يبدو ان الامر لحد الساعة واضح ، سننتقل الان الى Global Message Hooking
وهو نفس الامر بالنسبة لما سبق فقط عوض ان نقوم بـHook على برنامجنا سنقوم به على برنامج اخر كما يمكنك القيام به على جميع البرامج دفعة واحدة , وهو الدي سنستعمله في حقن الـDLL في برنامج خارجي
لعمل هدا الامر يجب ان تكون Hook Procedure داخل DLL
نرتب عملنا خطوة خطوة :
1 جلب Thread ID للبرنامج الدي نريد تنصيب الـHook عليه ( تنصيب الـHook غرضنا منه هو حقن ملف dllH.dll
2 برمجة DLL التي تحتوي على الـHook Procedure
3 تنصيب الـHook على البرنامج الهدف
جلب الـThread ID للبرنامج الهدف
تطرقنا الى هده الطريقة في الدرس السابق وكتبنا الكود الاتي
long GetTID(char* processName) {
HANDLE Snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
PROCESSENTRY32 PEN = {0};
PEN.dwSize = sizeof(PROCESSENTRY32);
if(Snap == INVALID_HANDLE_VALUE)
return 0;
if(Process32First(Snap,&PEN) == FALSE)
return 0;
while(Process32Next(Snap,&PEN) )
{
if(!strcmp(PEN.szExeFile,processName))
{
CloseHandle(Snap);
return PEN.th32ProcessID;
}
}
CloseHandle(Snap);
return 0;
}
يمكننا استعمال نفس هدا الكود لكن لنتفادى هدا التكرار ساريكم طريقة اخرى للحصول على Thread ID
لنشاهد الكود اولا
unsigned long GetTID(char *Process)
{
PROCESSENTRY32 PEN;
HANDLE Snap, ProcessHandle;
BOOL Next = false;
unsigned long TIDADDR, threadID;
Snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if(Snap == INVALID_HANDLE_VALUE)
{
return false;
}
PEN.dwSize = sizeof(PROCESSENTRY32);
Next = Process32First(Snap, &PEN);
while(Next)
{
if(StrStrI(PEN.szExeFile, Process) )
{
break;
}
Next = Process32Next(Snap,&PEN);
PEN.dwSize = sizeof(PROCESSENTRY32);
}
CloseHandleSnap);
_asm {
mov eax, fs:[0x18]
add eax, 36
mov [TIDADDR], eax
}
ProcessHandle = OpenProcess(PROCESS_VM_READ, false, PEN.th32ProcessID);
ReadProcessMemory(ProcessHandle, (const void *)TIDADDR, &threadID, 4, NULL);
CloseHandle(ProcessHandle);
return threadID;
}
نلاحظ في بداية الكود انه يشبه الكود الاول فقد استعملنا CreateToolhelp32Snapshot وحلقة ProcessNext/ProcessFirst
غيرت قليلا في حلقة While كما اسعملت الدالة StrStrI
لكن ان لاحضنا الكود السابق فهو يعيد مباشرة Thread ID عن طريق العنصر th32ProcessID التابع لـPROCESSENTRY32 STRUCTURE والدي يتم ملئها بمعلومات الـProcess في كل دورة
الكود الحالي نلاحض انه عندما يجد الـProcess يقوم بالخروج من الحلقة دون اعادة th32ProcessID والدي يحتوي على Thread ID
يتجه مباشرة الى كود الاسمبلي الاتي
_asm {
mov eax, fs:[0x18]
add eax, 36
mov [TIDADDR], eax
}
نقوم في هدا الكود بقرائة الـThread Information Block وهذا الامر
mov eax, fs:[0x18]
يضع عنوان Thread Information Block في eax تم عملنا ازاحة للوصول الى عنوان الـThread ID وقمنا بتخزينه في المتغير TIDADDR , الان وبما اننا حصلنا على العنوان الدي يبدأ عنده Thread id فما علينا هو قرائته بالدالة ReadProcessMemory
بهده الطريقة يمكننا معرفة معلومات كتيرة عن الـThread كما يمكن الوصول الى الـPEB وقرائة معلومات الـProcess ......
راجعو هدا الرابط :
http://en.wikipedia.org/wiki/Win32_Thread_Information_Block
انتهت الخطوة الاولى
2برمجة DLL التي تحتوي على الـHook Procedure
كما دكرنا سابقا لنعمل Message Hook على رسائل نافدة غير نافدة برنامجنا نحتاج
الى وضع الـHook Procedure في ملف DLL
لنرى كود DLL
#include <windows.h>
_declspec(dllexport) LRESULT CALLBACK InjectDllProc(int code, WPARAM wParam, LPARAM lParam);
bool APIENTRY DllMain (HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) {
switch(dwReason)
{
case DLL_PROCESS_ATTACH:
break;
case DLL_PROCESS_DETACH:
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
_declspec(dllexport) LRESULT CALLBACK InjectDllProc(int nCode, WPARAM wParam, LPARAM lParam)
{
MSG *Message;
Message = (MSG*) lParam;
if(nCode != MSGF_DIALOGBOX) goto Ret;
LoadLibrary("C:\\DllH.dll");
return 1;
Ret:
return (CallNextHookEx(0, nCode, wParam, lParam));
}
ملاحظة : حتى لا تتوهو هنا سيكون لدينا 2 DLL الاولى هي التي برمجنا في الدرس السابق والتي عند حقنها تضهر الرسالة Hello World والـDLL التانية التي تقوم بالـHook على الرسائل
نرجع للكود اعلاه ، كما تلاحظون لا جديد نفس الـHook Proc التي اسعملناها في Local Hook
الفرق اننا وضعناها في DLL لنتمكن من توزيعها على اي برنامج نريد
فقط نلاحض زيادة بسيطة هي الجملة
_declspec(dllexport)
هده الجملة تمتل نوع التصدير للدالة InjectDllProc لكي نتمكن من استدعائها من برنامجنا
الكلمة _declspec تستخدم لاعلام الـLinker ببعض المعلومات التي من المفترض كتابتها يدويا ادا استعملنا طريقة Module-Definition File في التصدير ( للمزيد من المعلومات راجع MSDN )
dllexport تعني وضع الدالة في جدول التصدير Export Table للمكتبة ( ملف الـPE عامة )
نواجه مشكلا هنا هو ان الدالة رغم ان اسمها InjectDllProc الا انها لن تصدر بهدا الاسم
يمكن التأكد من هدا بفتح المكتبة في برنامج LordPe تم Directories تم Export Table واضغط على التلات نقط ولاحض انها فعلا لم تصدر باسمها
الحل هو كليك يمين تم Edit وسمها InjectDllProc تم Ok Save
اضن الكود اصبح واضحا الان
الى هنا انتهت الخطوة التانية
تنصيب الـHook على البرنامج الهدف
ما بقي لنا الان هو جلب عنوان الدالة InjectDllProc ( عبر الدالة GetProcAddress ) لكي نستعمل في الدالة SetWindowHookEx
الكود :
#include <windows.h>
#include "resource.h"
#include <tlhelp32.h>
#include <Shlwapi.h>
BOOL CALLBACK DialogProc(HWND,UINT,WPARAM,LPARAM);
int WINAPI WinMain (HINSTANCE,HINSTANCE,PSTR,int);
unsigned long GetTID(char *Process);
bool SetHook(long);
char Process[260];
HINSTANCE hInst;
HHOOK hHook;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
hInst=hInstance;
DialogBoxParam(hInstance, MAKEINTRESOURCE(101), NULL, (DLGPROC)DialogProc,0);
return 0;
}
BOOL CALLBACK DialogProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
switch(Message)
{
case WM_CLOSE:
{
UnhookWindowsHookEx(hHook);
PostQuitMessage(0);
EndDialog(hWnd,0);
}
break;
case WM_COMMAND:
{
switch ( LOWORD(wParam) ) {
case IDC_BUTTON1:
{
GetDlgItemText(hWnd,IDC_EDIT1,Process,256);
SetHook(GetTID(Process));
} break;
}
}
}
return 0;
}
unsigned long GetTID(char *Process)
{
PROCESSENTRY32 PEN;
HANDLE Snap, ProcessHandle;
BOOL Next = false;
unsigned long TIDADDR, threadID;
Snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if(Snap == INVALID_HANDLE_VALUE)
{
return false;
}
PEN.dwSize = sizeof(PROCESSENTRY32);
Next = Process32First(Snap, &PEN);
while(Next)
{
if(StrStrI(PEN.szExeFile, Process) )
{
break;
}
Next = Process32Next(Snap,&PEN);
PEN.dwSize = sizeof(PROCESSENTRY32);
}
CloseHandle(Snap);
_asm {
mov eax, fs:[0x18]
add eax,36
mov [TIDADDR], eax
}
ProcessHandle = OpenProcess(PROCESS_VM_READ, false, PEN.th32ProcessID);
ReadProcessMemory(ProcessHandle, (const void *)TIDADDR, &threadID, 4, NULL);
CloseHandle(ProcessHandle);
return threadID;
}
bool SetHook(long Tid) {
HINSTANCE hDLL = LoadLibrary("C:\\DLL_SetWindowsHookEx.dll");
HOOKPROC InjectDllProc = (HOOKPROC) GetProcAddress(hDLL,"InjectDllProc");
hHook = SetWindowsHookEx(WH_GETMESSAGE,(HOOKPROC)InjectDllProc,hDLL,Tid);
return( (hHook != 0) ? 1:0);
}
" اللهم أحسن خاتمتنا وأخرجنا من الدنيا علي خير"