0x01 前言
同學問過我進程體中EPROCESS的三條鏈斷了怎么枚舉模塊,這也是也騰訊面試題。我當時聽到也是懵逼的。
后來在網上看到了一些內存暴力枚舉的方法ZwQueryVirtualMemory。
0x02 使用ZwQueryVirtualMemory暴力枚舉模塊
NTSTATUS NtQueryVirtualMemory(HANDLE ProcessHandle, //目標進程句柄 PVOID BaseAddress, //查詢的基址 MEMORY_INFORMATION_CLASS MemoryInformationClass, //枚舉宏 PVOID MemoryInformation, //接收信息的結構體 SIZE_T MemoryInformationLength, //緩沖區大小 PSIZE_T ReturnLength); //返回實際長度 //枚舉宏 typedef enum _MEMORY_INFORMATION_CLASS { MemoryBasicInformation, MemoryWorkingSetList, MemorySectionName, MemoryBasicVlmInformation } MEMORY_INFORMATION_CLASS;
R0通過遍歷SSDT獲得函數地址。
我們要枚舉進程模塊信息, 需要用到兩類內存信息MemoryBasicInformation和MemorySectionName,
MemoryBasicInformation的緩沖結構體
typedef struct _MEMORY_BASIC_INFORMATION { PVOID BaseAddress; //查詢內存塊所占的第一個頁面基地址 PVOID AllocationBase; //內存塊所占的第一塊區域基地址,小於等於BaseAddress, DWORD AllocationProtect; //區域被初次保留時賦予的保護屬性 SIZE_T RegionSize; //從BaseAddress開始,具有相同屬性的頁面的大小, DWORD State; //頁面的狀態,有三種可能值MEM_COMMIT、MEM_FREE和MEM_RESERVE DWORD Protect; //頁面的屬性,其可能的取值與AllocationProtect相同 DWORD Type; //該內存塊的類型,有三種可能值:MEM_IMAGE、MEM_MAPPED和MEM_PRIVATE } MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;
MemorySectionName的緩沖結構體為
//MemorySectionName typedef struct _MEMORY_SECTION_NAME { UNICODE_STRING Name; WCHAR Buffer[260]; }MEMORY_SECTION_NAME,*PMEMORY_SECTION_NAME;
前者返回內存的基本信息, 比如: 內存區的基址,大小以及頁面的各種屬性等等, 而后者則返回內存段的名字, 也就是我們所要找的模塊名.
利用前者我們可以過濾出類型為MEM_IMAGE的內存段並得到內存段的基址和屬性, 利用后者我們可以得到模塊名.
代碼如下:
VOID ListModuleThread(PVOID Context) { NTSTATUS ntStatus = STATUS_UNSUCCESSFUL; ULONG StepAddress; ULONG Step2Address; ULONG BufferSize = 0x200; ULONG ReturnLength = 0; WCHAR LastImageName[260] = { 0 }; HANDLE HandleProcess; PMEMORY_SECTION_NAME SectionName = NULL; MEMORY_BASIC_INFORMATION BasicInformation; PTHREAD_CONTEXT ThreadContext = Context; PMODULE_INFO FoundModule = NULL; pFnZwQueryVirtualMemory ZwQueryVirtualMemory = NULL; ZwQueryVirtualMemory = (pFnZwQueryVirtualMemory) KeServiceDescriptorTable.ServiceTableBase[ServiceId_NtQueryVirtualMemory]; ntStatus = ObOpenObjectByPointer(ThreadContext->Process, OBJ_INHERIT, NULL, 0, *PsProcessType, ExGetPreviousMode(), &HandleProcess); if (!NT_SUCCESS(ntStatus)) { ExFreePoolWithTag(g_ModuleListHead, MEM_TAG); g_ModuleListHead = NULL; goto _End; } SectionName = ExAllocatePoolWithTag(PagedPool, BufferSize, MEM_TAG); for (StepAddress = 0; StepAddress <= 0x7FFFFFFF; StepAddress += 0x10000) { ntStatus = ZwQueryVirtualMemory(HandleProcess, (PVOID)StepAddress, MemoryBasicInformation, &BasicInformation, sizeof(MEMORY_BASIC_INFORMATION), &ReturnLength); if (!NT_SUCCESS(ntStatus) || BasicInformation.Type != SEC_IMAGE) continue; _Retry: ntStatus = ZwQueryVirtualMemory(HandleProcess, (PVOID)StepAddress, MemorySectionName, SectionName, BufferSize, &ReturnLength); if (!NT_SUCCESS(ntStatus)) { if (ntStatus == STATUS_INFO_LENGTH_MISMATCH) { ExFreePoolWithTag(SectionName, MEM_TAG); SectionName = ExAllocatePoolWithTag(PagedPool, ReturnLength, MEM_TAG); goto _Retry; } continue; } __try { if (memcmp(LastImageName, SectionName->SectionFileName.Buffer, SectionName->SectionFileName.Length) && SectionName->SectionFileName.Length < 520) { memcpy(LastImageName, SectionName->SectionFileName.Buffer, SectionName->SectionFileName.Length); LastImageName[SectionName->SectionFileName.Length / 2] = L'/0'; // // Step into and get the image size // for (Step2Address = StepAddress + BasicInformation.RegionSize; Step2Address < 0x7FFFFFFF; Step2Address += BasicInformation.RegionSize) { ntStatus = ZwQueryVirtualMemory(HandleProcess, (PVOID)Step2Address, MemoryBasicInformation, &BasicInformation, sizeof(MEMORY_BASIC_INFORMATION), &ReturnLength); if (NT_SUCCESS(ntStatus) && BasicInformation.Type != SEC_IMAGE) break; } FoundModule = ExAllocatePoolWithTag(NonPagedPool, sizeof(MODULE_INFO), MEM_TAG); FoundModule->BaseAddress = StepAddress; FoundModule->ImageSize = Step2Address - StepAddress; RtlStringCbPrintfW(FoundModule->ImagePath, 520, L"%s", LastImageName); InsertTailList(&g_ModuleListHead->ModuleListHead, &FoundModule->ModuleLink); g_ModuleListHead->NumberOfModules ++; } } __except (EXCEPTION_EXECUTE_HANDLER) { continue; } } ExFreePoolWithTag(SectionName, MEM_TAG); ObCloseHandle(HandleProcess, ExGetPreviousMode()); _End: KeSetEvent(&ThreadContext->SynEvent, IO_NO_INCREMENT, FALSE); PsTerminateSystemThread(STATUS_SUCCESS); }
此時的模塊名是NT Path需要轉成Dos Path,代碼如下
BOOLEAN NtPathToDosPathW(WCHAR* wzFullNtPath,WCHAR* wzFullDosPath); extern NTSTATUS NTAPI ZwQueryDirectoryObject ( __in HANDLE DirectoryHandle, __out_bcount_opt(Length) PVOID Buffer, __in ULONG Length, __in BOOLEAN ReturnSingleEntry, __in BOOLEAN RestartScan, __inout PULONG Context, __out_opt PULONG ReturnLength ); typedef struct _OBJECT_DIRECTORY_INFORMATION { UNICODE_STRING Name; UNICODE_STRING TypeName; } OBJECT_DIRECTORY_INFORMATION, *POBJECT_DIRECTORY_INFORMATION; ULONG NtQueryDosDevice(WCHAR* wzDosDevice,WCHAR* wzNtDevice, ULONG ucchMax);
BOOLEAN NtPathToDosPathW(WCHAR* wzFullNtPath,WCHAR* wzFullDosPath) { WCHAR wzDosDevice[4] = {0}; WCHAR wzNtDevice[64] = {0}; WCHAR *RetStr = NULL; size_t NtDeviceLen = 0; short i = 0; if(!wzFullNtPath||!wzFullDosPath) { return FALSE; } for(i=65;i<26+65;i++) { wzDosDevice[0] = i; wzDosDevice[1] = L':'; if(NtQueryDosDevice(wzDosDevice,wzNtDevice,64)) { if(wzNtDevice) { NtDeviceLen = wcslen(wzNtDevice); if(!_wcsnicmp(wzNtDevice,wzFullNtPath,NtDeviceLen)) { wcscpy(wzFullDosPath,wzDosDevice); wcscat(wzFullDosPath,wzFullNtPath+NtDeviceLen); return TRUE; } } } } } ULONG NtQueryDosDevice(WCHAR* wzDosDevice,WCHAR* wzNtDevice, ULONG ucchMax) { NTSTATUS Status; POBJECT_DIRECTORY_INFORMATION ObjectDirectoryInfor; OBJECT_ATTRIBUTES oa; UNICODE_STRING uniString; HANDLE hDirectory; HANDLE hDevice; ULONG ulReturnLength; ULONG ulNameLength; ULONG ulLength; ULONG Context; BOOLEAN bRestartScan; WCHAR* Ptr = NULL; UCHAR szBuffer[512] = {0}; RtlInitUnicodeString (&uniString,L"\\??"); InitializeObjectAttributes(&oa, &uniString, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = ZwOpenDirectoryObject(&hDirectory,DIRECTORY_QUERY,&oa); if(!NT_SUCCESS(Status)) { return 0; } ulLength = 0; if (wzDosDevice != NULL) { RtlInitUnicodeString (&uniString,(PWSTR)wzDosDevice); InitializeObjectAttributes(&oa,&uniString,OBJ_CASE_INSENSITIVE,hDirectory,NULL); Status = ZwOpenSymbolicLinkObject(&hDevice,GENERIC_READ,&oa); if(!NT_SUCCESS (Status)) { ZwClose(hDirectory); return 0; } uniString.Length = 0; uniString.MaximumLength = (USHORT)ucchMax * sizeof(WCHAR); uniString.Buffer = wzNtDevice; ulReturnLength = 0; Status = ZwQuerySymbolicLinkObject (hDevice,&uniString,&ulReturnLength); ZwClose(hDevice); ZwClose(hDirectory); if (!NT_SUCCESS (Status)) { return 0; } ulLength = uniString.Length / sizeof(WCHAR); if (ulLength < ucchMax) { wzNtDevice[ulLength] = UNICODE_NULL; ulLength++; } else { return 0; } } else { bRestartScan = TRUE; Context = 0; Ptr = wzNtDevice; ObjectDirectoryInfor = (POBJECT_DIRECTORY_INFORMATION)szBuffer; while (TRUE) { Status = ZwQueryDirectoryObject(hDirectory,szBuffer,sizeof (szBuffer),TRUE,bRestartScan,&Context,&ulReturnLength); if(!NT_SUCCESS(Status)) { if (Status == STATUS_NO_MORE_ENTRIES) { *Ptr = UNICODE_NULL; ulLength++; Status = STATUS_SUCCESS; } else { ulLength = 0; } break; } if (!wcscmp (ObjectDirectoryInfor->TypeName.Buffer, L"SymbolicLink")) { ulNameLength = ObjectDirectoryInfor->Name.Length / sizeof(WCHAR); if (ulLength + ulNameLength + 1 >= ucchMax) { ulLength = 0; break; } memcpy(Ptr,ObjectDirectoryInfor->Name.Buffer,ObjectDirectoryInfor->Name.Length); Ptr += ulNameLength; ulLength += ulNameLength; *Ptr = UNICODE_NULL; Ptr++; ulLength++; } bRestartScan = FALSE; } ZwClose(hDirectory); } return ulLength; }
0x03 參考
http://www.cnblogs.com/kedebug/archive/2010/12/22/2791753.html