بسم الله الرحمن الرحيم
السلام عليكم ورحمة الله وبركاته
اللهم علمنا ما ينفعنا وانفعنا بما علمتنا ، انك انت السميع العليم
==========> Import Address Table Hook, 32-bit, 64-bit <==========
الاخوة الاعزاء ، تكلمنا فيما سبق عن Trampoline Hook
واليوم سوف نتطرق الى IAT Hook
وبالطبع يحتاج فهم هذه الـHook الي فهم ما هو اساسا PE File , وما هي PE Format، وذلك قبل الولوج الى IAT/EAT Hooks
وبالطبع لن يتم شرح الـPE Format لان شرحه سوف يطول ويطول واشك اساسا في اننا سوف نوفيه حقه !
ويمكن القراءة عنه داخل MSDN - PE
ويمكن القراءة عنه داخل MSDN - PE
==========> آلية عمل Import Address Table Hook <==========
كما نعلم بان الملف الـDll عبارة عن ...
كما نرى ان الملف عبارة عن
MS-Dos Header
MS-Dos Stub
PE Header {
PE Signature
File Header
Optional Header
}
Section Header
Sections ...
...
الان، داخل الـSection المعروف باسم .idata ، يوجد الـImport Address Table
والذي يحتوي على RVA - Relative Virtual Address
الخاصة بالـFunctions التي سوف يتم استيرادها من Modules - Dll Files
اي انه عند استدعاء الـAPI يتم استدعاء الـAddress الخاص بهذه الـAPI من idata
ووظيفة هذه الـHook تغيير الـRVA الى عنوان Function الـHook الخاص بنا
حيث انه عند استدعاء الـFunction سوف يتم الولوج الى عنوان الـHook Function
اي اننا نحتاج الى الوصول الى PE Headers ، ومن ثم بعدها الى Optional Header وداخل الـSrtucture المعروف باسم IMAGE_OPTIONAL_HEADER ، سوف نلج الى DataDirectory لنقوم بجلب الـRVA الخاص بـidata
ومن ثم عمل loop حتى نجد عنوان الـAPI المطلوبة ، ونغير العنوان الى عنوان الـHook Function
==========> Import Address Table Hook CODE <==========
والذي يحتوي على RVA - Relative Virtual Address
الخاصة بالـFunctions التي سوف يتم استيرادها من Modules - Dll Files
اي انه عند استدعاء الـAPI يتم استدعاء الـAddress الخاص بهذه الـAPI من idata
ووظيفة هذه الـHook تغيير الـRVA الى عنوان Function الـHook الخاص بنا
حيث انه عند استدعاء الـFunction سوف يتم الولوج الى عنوان الـHook Function
اي اننا نحتاج الى الوصول الى PE Headers ، ومن ثم بعدها الى Optional Header وداخل الـSrtucture المعروف باسم IMAGE_OPTIONAL_HEADER ، سوف نلج الى DataDirectory لنقوم بجلب الـRVA الخاص بـidata
ومن ثم عمل loop حتى نجد عنوان الـAPI المطلوبة ، ونغير العنوان الى عنوان الـHook Function
==========> Import Address Table Hook CODE <==========
C++ 32-bit
#include <Windows.h>
#include <iostream>
typedef int (WINAPI* MsgBoxStruct) (
_In_opt_ HWND hWnd,
_In_opt_ LPCSTR lpText,
_In_opt_ LPCSTR lpCaption,
_In_ UINT uType);
MsgBoxStruct ReCall;
BOOL Hook_IAT_x86(char LibNameBigCaseName_SmallFormat[], char FunName[], LPVOID NewFun);
int WINAPI MsgBox(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);
BOOL WINAPI DllMain(HMODULE hDLL, DWORD Reason, LPVOID Reserved) {
switch (Reason) {
case DLL_PROCESS_ATTACH:
Hook_IAT_x86("USER32.dll", "MessageBoxA", MsgBox);
ReCall = (MsgBoxStruct)GetProcAddress(LoadLibraryA("USER32.dll"), "MessageBoxA");
}
return 1;
}
BOOL Hook_IAT_x86(char LibNameBigCaseName_SmallFormat[] /* USER32.dll */ ,char FunName[], LPVOID NewFun) {
DWORD hMod = (DWORD)GetModuleHandleA(0);
PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)hMod;
PIMAGE_NT_HEADERS NtHeaders = (PIMAGE_NT_HEADERS)(hMod + DosHeader->e_lfanew);
PIMAGE_IMPORT_DESCRIPTOR idata = (PIMAGE_IMPORT_DESCRIPTOR)(hMod + NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
for (idata; idata->Name != NULL; idata++) {
if (std::string((char*)(hMod + idata->Name)) == LibNameBigCaseName_SmallFormat) {
PIMAGE_THUNK_DATA32 ThunkData = (PIMAGE_THUNK_DATA32)(hMod + idata->OriginalFirstThunk);
DWORD* Address = (DWORD*)(hMod + idata->FirstThunk);
for (int i = 0; ThunkData->u1.ForwarderString != NULL; i++) {
std::string x = (char*)(hMod + ThunkData->u1.ForwarderString + 2);
if (x == "MessageBoxA") {
DWORD OldProt;
VirtualProtect(&Address[i], 4, PAGE_READWRITE, &OldProt);
Address[i] = (DWORD)NewFun;
VirtualProtect(&Address[i], 4, OldProt, &OldProt);
goto RE;
} ThunkData++;
}
}
}
RE:
return 1;
}
int WINAPI MsgBox(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) {
return ReCall(hWnd , lpText , "Hooked Successfully" , uType);
}
#include <iostream>
typedef int (WINAPI* MsgBoxStruct) (
_In_opt_ HWND hWnd,
_In_opt_ LPCSTR lpText,
_In_opt_ LPCSTR lpCaption,
_In_ UINT uType);
MsgBoxStruct ReCall;
BOOL Hook_IAT_x86(char LibNameBigCaseName_SmallFormat[], char FunName[], LPVOID NewFun);
int WINAPI MsgBox(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);
BOOL WINAPI DllMain(HMODULE hDLL, DWORD Reason, LPVOID Reserved) {
switch (Reason) {
case DLL_PROCESS_ATTACH:
Hook_IAT_x86("USER32.dll", "MessageBoxA", MsgBox);
ReCall = (MsgBoxStruct)GetProcAddress(LoadLibraryA("USER32.dll"), "MessageBoxA");
}
return 1;
}
BOOL Hook_IAT_x86(char LibNameBigCaseName_SmallFormat[] /* USER32.dll */ ,char FunName[], LPVOID NewFun) {
DWORD hMod = (DWORD)GetModuleHandleA(0);
PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)hMod;
PIMAGE_NT_HEADERS NtHeaders = (PIMAGE_NT_HEADERS)(hMod + DosHeader->e_lfanew);
PIMAGE_IMPORT_DESCRIPTOR idata = (PIMAGE_IMPORT_DESCRIPTOR)(hMod + NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
for (idata; idata->Name != NULL; idata++) {
if (std::string((char*)(hMod + idata->Name)) == LibNameBigCaseName_SmallFormat) {
PIMAGE_THUNK_DATA32 ThunkData = (PIMAGE_THUNK_DATA32)(hMod + idata->OriginalFirstThunk);
DWORD* Address = (DWORD*)(hMod + idata->FirstThunk);
for (int i = 0; ThunkData->u1.ForwarderString != NULL; i++) {
std::string x = (char*)(hMod + ThunkData->u1.ForwarderString + 2);
if (x == "MessageBoxA") {
DWORD OldProt;
VirtualProtect(&Address[i], 4, PAGE_READWRITE, &OldProt);
Address[i] = (DWORD)NewFun;
VirtualProtect(&Address[i], 4, OldProt, &OldProt);
goto RE;
} ThunkData++;
}
}
}
RE:
return 1;
}
int WINAPI MsgBox(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) {
return ReCall(hWnd , lpText , "Hooked Successfully" , uType);
}
نبدا من DllMain وكما قلنا تحتاج الى ان يكون لديك خبرة في PE Format، قمنا باستدعاء الـHook_IAT_x86 والتي تاخذ 3 Parameters
الاول هو اسم المكتبة الاسم كبير والصيغة صغيرة كما كالتالي USER32.dll
الثاني اسم الـFunction
الثالث الـFunction الجديدة
تقوم الـ GetModuleHandleA API بجلب Module Handle الحالي ، ببارميتر 0
ومن ثم قمنا بجلب الـDosHeader التي تمثل اول 20Byte
ويحتوي الـPIMAGE_DOS_HEADER او DosHeader على e_lfanew التي تعطي عنوان بداية PE Header
وبذلك تم الوصول الى PE Header الذي يمثله الـPIMAGE_NT_HEADERS
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER OptionalHeader;
} IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS;
وهو عبارة عن 3 Headers كما تكلمنا بالاعلى
ثم تم استخدام PIMAGE_IMPORT_DESCRIPTOR وهي Structure تحتوي على DataDirectory وتحتوي على معلومات القسم
وتم استخدامه لجلب معلومات القسم .idata حيث تم الولوج الى Optional Header داخل NtHeaders وجلب VA لـIMPORT Tableعن طريق DataDirectory والتي ترجع الـVA الخاص برقم الـSection لنقوم بجلب معلومات .idata وتخزينها في idata PIMAGE_IMPORT_DESCRIPTOR
وكما هو معروف اذا كان الملف يحتوي على 4 Sections فانه سوف يحتوي على 4 PIMAGE_IMPORT_DESCRIPTOR ايضا ، وهذه الـStructure تحتوي على خاصية Name التي ترجع اسم الملف الذي يحتوي على معلومات الـImport Table
لذلك نقوم بعمل Loop الى ان يكون Name = NULL داخل الـPIMAGE_IMPORT_DESCRIPTOR وبذلك نعرف انتهاء اعداد الـ Sections
بداية نقوم بالتحقق من ان اسم الـModule هو اسم المكتبة التي تحتوي على الـFunction حتى لا ياخذ وقت طويل
ثم قمنا بتعريف الـPIMAGE_THUNK_DATA32 والتي تحتوي على اسم الـFunction داخل خاصية
ForwarderString
وبعد ذلك قمنا بتعريف Pointer DWORD الذي يمثل نقطة بداية الـAddresses ايضا والذي يمثله خاصية FirstThunk
وبعدها نقوم بعمل Loop على ThunkData الى ان يعطي اسم الـThunk = NULL
نقوم بجلب الـstring الخاصة بالـThunk ( لان كل الخواص في Structures ترجع قيمة RVA )
ومقارنتها مع الـAPI المطلوبة
بعد ذلك نقوم بتغيير الحماية عن طريق VirtualProtect APIعلى عنوان الـAddress الحالي للـAPI الحالية
ليكون PAGE_READWRITE لاننا نحتاج الى الكتابة عليه والقراءة منه
وسوف يكون مساحة التغيير المطلوب 4 Byte لانه 32-bit
بعد ذلك نقوم بتغيير العنوان الى عنوان Hook Function
ومن ثم نقوم بارجاع الحماية كما كانت عليه
اما ReCall
فكما قلنا فان هذه الـHook على IAT وحتى نحتاج لاعادة استدعاء الـAPI نحتاج الى عنوانها الصحيح
حيث اننا قمنا بتعريف البنية الخاصة بالـMessageBoxA وبعد ذلك تعريف متغير لتخزين العنوان الخاص بالـAPI عن طريق GetProcAddress API و LoadLibraryA API
اما في 64 فهو نفس الاجرائات مع اختلاف الاحجام 64bit ليكون الـCode
إن أصبت فمن الله ، وإن اخطئت فمني ومن الشيطان
عمل هوك ، صنع هوك ، فلترة دالة ، فلترة API ، القضاء على Function ، انشاء حماية ، منع استخدام Function ، كيفية عمل هوك ، كيفية عمل Hook ، هوك Detours ، هوك trampoline ، هوكات IAT , EAT ، Kernel32.dll , System32 , User32.dll , هوك Ring3 ، User Mode Hook, Kernel Mode Hook , الكتابة على الذاكرة
ومن ثم قمنا بجلب الـDosHeader التي تمثل اول 20Byte
ويحتوي الـPIMAGE_DOS_HEADER او DosHeader على e_lfanew التي تعطي عنوان بداية PE Header
typedef struct _IMAGE_DOS_HEADER
{
WORD e_magic;
WORD e_cblp;
WORD e_cp;
WORD e_crlc;
WORD e_cparhdr;
WORD e_minalloc;
WORD e_maxalloc;
WORD e_ss;
WORD e_sp;
WORD e_csum;
WORD e_ip;
WORD e_cs;
WORD e_lfarlc;
WORD e_ovno;
WORD e_res[4];
WORD e_oemid;
WORD e_oeminfo;
WORD e_res2[10];
LONG e_lfanew;
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER OptionalHeader;
} IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS;
وهو عبارة عن 3 Headers كما تكلمنا بالاعلى
ثم تم استخدام PIMAGE_IMPORT_DESCRIPTOR وهي Structure تحتوي على DataDirectory وتحتوي على معلومات القسم
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
DWORD OriginalFirstThunk;
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name;
DWORD FirstThunk;
} IMAGE_IMPORT_DESCRIPTOR, *PIMAGE_IMPORT_DESCRIPTOR;
DWORD OriginalFirstThunk;
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name;
DWORD FirstThunk;
} IMAGE_IMPORT_DESCRIPTOR, *PIMAGE_IMPORT_DESCRIPTOR;
وتم استخدامه لجلب معلومات القسم .idata حيث تم الولوج الى Optional Header داخل NtHeaders وجلب VA لـIMPORT Tableعن طريق DataDirectory والتي ترجع الـVA الخاص برقم الـSection لنقوم بجلب معلومات .idata وتخزينها في idata PIMAGE_IMPORT_DESCRIPTOR
وكما هو معروف اذا كان الملف يحتوي على 4 Sections فانه سوف يحتوي على 4 PIMAGE_IMPORT_DESCRIPTOR ايضا ، وهذه الـStructure تحتوي على خاصية Name التي ترجع اسم الملف الذي يحتوي على معلومات الـImport Table
لذلك نقوم بعمل Loop الى ان يكون Name = NULL داخل الـPIMAGE_IMPORT_DESCRIPTOR وبذلك نعرف انتهاء اعداد الـ Sections
بداية نقوم بالتحقق من ان اسم الـModule هو اسم المكتبة التي تحتوي على الـFunction حتى لا ياخذ وقت طويل
ثم قمنا بتعريف الـPIMAGE_THUNK_DATA32 والتي تحتوي على اسم الـFunction داخل خاصية
ForwarderString
وبعد ذلك قمنا بتعريف Pointer DWORD الذي يمثل نقطة بداية الـAddresses ايضا والذي يمثله خاصية FirstThunk
typedef struct _IMAGE_THUNK_DATA32 {
union {
DWORD ForwarderString; // PBYTE
DWORD Function; // PDWORD
DWORD Ordinal;
DWORD AddressOfData; // PIMAGE_IMPORT_BY_NAME
} u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;
union {
DWORD ForwarderString; // PBYTE
DWORD Function; // PDWORD
DWORD Ordinal;
DWORD AddressOfData; // PIMAGE_IMPORT_BY_NAME
} u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;
وبعدها نقوم بعمل Loop على ThunkData الى ان يعطي اسم الـThunk = NULL
نقوم بجلب الـstring الخاصة بالـThunk ( لان كل الخواص في Structures ترجع قيمة RVA )
ومقارنتها مع الـAPI المطلوبة
بعد ذلك نقوم بتغيير الحماية عن طريق VirtualProtect APIعلى عنوان الـAddress الحالي للـAPI الحالية
ليكون PAGE_READWRITE لاننا نحتاج الى الكتابة عليه والقراءة منه
وسوف يكون مساحة التغيير المطلوب 4 Byte لانه 32-bit
بعد ذلك نقوم بتغيير العنوان الى عنوان Hook Function
ومن ثم نقوم بارجاع الحماية كما كانت عليه
اما ReCall
فكما قلنا فان هذه الـHook على IAT وحتى نحتاج لاعادة استدعاء الـAPI نحتاج الى عنوانها الصحيح
حيث اننا قمنا بتعريف البنية الخاصة بالـMessageBoxA وبعد ذلك تعريف متغير لتخزين العنوان الخاص بالـAPI عن طريق GetProcAddress API و LoadLibraryA API
اما في 64 فهو نفس الاجرائات مع اختلاف الاحجام 64bit ليكون الـCode
#include <Windows.h>
#include <iostream>
typedef int (WINAPI* MsgBoxStruct) (
_In_opt_ HWND hWnd,
_In_opt_ LPCSTR lpText,
_In_opt_ LPCSTR lpCaption,
_In_ UINT uType);
MsgBoxStruct ReCall;
BOOL Hook_IAT_x64(char LibNameBigCaseName_SmallFormat[], char FunName[], LPVOID NewFun);
int WINAPI MsgBox(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);
BOOL WINAPI DllMain(HMODULE hDLL, DWORD Reason, LPVOID Reserved) {
switch (Reason) {
case DLL_PROCESS_ATTACH:
Hook_IAT_x64("USER32.dll", "MessageBoxA", MsgBox);
ReCall = (MsgBoxStruct)GetProcAddress(LoadLibraryA("USER32.dll"), "MessageBoxA");
}
return 1;
}
BOOL Hook_IAT_x64(char LibNameBigCaseName_SmallFormat[] /* USER32.dll */ ,char FunName[], LPVOID NewFun) {
DWORD64 hMod = (DWORD64)GetModuleHandleA(0);
PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)hMod;
PIMAGE_NT_HEADERS64 NtHeaders = (PIMAGE_NT_HEADERS64)(hMod + DosHeader->e_lfanew);
PIMAGE_IMPORT_DESCRIPTOR idata = (PIMAGE_IMPORT_DESCRIPTOR)(hMod + NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
for (idata; idata->Name != NULL; idata++) {
if (std::string((char*)(hMod + idata->Name)) == LibNameBigCaseName_SmallFormat) {
PIMAGE_THUNK_DATA64 ThunkData = (PIMAGE_THUNK_DATA64)(hMod + idata->OriginalFirstThunk);
DWORD64* Address = (DWORD64*)(hMod + idata->FirstThunk);
for (int i = 0; ThunkData->u1.ForwarderString != NULL; i++) {
std::string x = (char*)(hMod + ThunkData->u1.ForwarderString + 2);
if (x == "MessageBoxA") {
DWORD OldProt;
VirtualProtect(&Address[i], 8, PAGE_READWRITE, &OldProt);
Address[i] = (DWORD64)NewFun;
VirtualProtect(&Address[i], 8, OldProt, &OldProt);
goto RE;
} ThunkData++;
}
}
}
RE:
return 1;
}
int WINAPI MsgBox(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) {
return ReCall(hWnd , lpText , "Hooked Successfully" , uType);
}
#include <iostream>
typedef int (WINAPI* MsgBoxStruct) (
_In_opt_ HWND hWnd,
_In_opt_ LPCSTR lpText,
_In_opt_ LPCSTR lpCaption,
_In_ UINT uType);
MsgBoxStruct ReCall;
BOOL Hook_IAT_x64(char LibNameBigCaseName_SmallFormat[], char FunName[], LPVOID NewFun);
int WINAPI MsgBox(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);
BOOL WINAPI DllMain(HMODULE hDLL, DWORD Reason, LPVOID Reserved) {
switch (Reason) {
case DLL_PROCESS_ATTACH:
Hook_IAT_x64("USER32.dll", "MessageBoxA", MsgBox);
ReCall = (MsgBoxStruct)GetProcAddress(LoadLibraryA("USER32.dll"), "MessageBoxA");
}
return 1;
}
BOOL Hook_IAT_x64(char LibNameBigCaseName_SmallFormat[] /* USER32.dll */ ,char FunName[], LPVOID NewFun) {
DWORD64 hMod = (DWORD64)GetModuleHandleA(0);
PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)hMod;
PIMAGE_NT_HEADERS64 NtHeaders = (PIMAGE_NT_HEADERS64)(hMod + DosHeader->e_lfanew);
PIMAGE_IMPORT_DESCRIPTOR idata = (PIMAGE_IMPORT_DESCRIPTOR)(hMod + NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
for (idata; idata->Name != NULL; idata++) {
if (std::string((char*)(hMod + idata->Name)) == LibNameBigCaseName_SmallFormat) {
PIMAGE_THUNK_DATA64 ThunkData = (PIMAGE_THUNK_DATA64)(hMod + idata->OriginalFirstThunk);
DWORD64* Address = (DWORD64*)(hMod + idata->FirstThunk);
for (int i = 0; ThunkData->u1.ForwarderString != NULL; i++) {
std::string x = (char*)(hMod + ThunkData->u1.ForwarderString + 2);
if (x == "MessageBoxA") {
DWORD OldProt;
VirtualProtect(&Address[i], 8, PAGE_READWRITE, &OldProt);
Address[i] = (DWORD64)NewFun;
VirtualProtect(&Address[i], 8, OldProt, &OldProt);
goto RE;
} ThunkData++;
}
}
}
RE:
return 1;
}
int WINAPI MsgBox(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) {
return ReCall(hWnd , lpText , "Hooked Successfully" , uType);
}
typedef struct _IMAGE_THUNK_DATA64 {
union {
ULONGLONG ForwarderString; // PBYTE
ULONGLONG Function; // PDWORD
ULONGLONG Ordinal;
ULONGLONG AddressOfData; // PIMAGE_IMPORT_BY_NAME
} u1;
} IMAGE_THUNK_DATA64;
typedef IMAGE_THUNK_DATA64 * PIMAGE_THUNK_DATA64;
union {
ULONGLONG ForwarderString; // PBYTE
ULONGLONG Function; // PDWORD
ULONGLONG Ordinal;
ULONGLONG AddressOfData; // PIMAGE_IMPORT_BY_NAME
} u1;
} IMAGE_THUNK_DATA64;
typedef IMAGE_THUNK_DATA64 * PIMAGE_THUNK_DATA64;
إن أصبت فمن الله ، وإن اخطئت فمني ومن الشيطان
اترككم في امان الله ورعايته
والسلام عليكم ورحمة الله وبركاته
DoneByM
عمل هوك ، صنع هوك ، فلترة دالة ، فلترة API ، القضاء على Function ، انشاء حماية ، منع استخدام Function ، كيفية عمل هوك ، كيفية عمل Hook ، هوك Detours ، هوك trampoline ، هوكات IAT , EAT ، Kernel32.dll , System32 , User32.dll , هوك Ring3 ، User Mode Hook, Kernel Mode Hook , الكتابة على الذاكرة
YcritanVinhi-Peoria Shannon Prieto click
ردحذفglicinpicpo