2010-11-22 36 views
10

Có cách nào để phát hiện progammatically khi một mô-đun - cụ thể là một DLL - đã được dỡ xuống từ một quá trình?Phát hiện khi một Mô-đun (DLL) được dỡ xuống

Tôi không có nguồn DLL, vì vậy tôi không thể thay đổi điểm nhập DLL của nó. Tôi cũng không thể thăm dò ý kiến ​​nếu DLL hiện đang được tải vì DLL có thể được dỡ xuống và sau đó tải lại giữa các cuộc thăm dò.

KẾT QUẢ:

tôi đã kết thúc bằng jimharks giải pháp của detouring điểm vào dll và bắt DLL_PROCESS_DETACH. Tôi tìm thấy detouring FreeLibrary() để làm việc là tốt, nhưng mã phải được thêm vào để phát hiện khi các mô-đun thực sự được dỡ bỏ hoặc nếu số tham chiếu chỉ là giảm. Necrolis 'liên kết về việc tìm kiếm số lượng tham chiếu là tiện dụng cho phương pháp làm như vậy.

Tôi nên lưu ý rằng tôi đã gặp sự cố với MSDetours không thực sự dỡ mô-đun khỏi bộ nhớ nếu đường vòng tồn tại bên trong nó.

+0

là yêu cầu này để kiểm tra mã hoặc trong khi gỡ lỗi? – Chubsdad

+0

@Chubsdad không chắc chắn chính xác ý bạn là gì bằng cách "kiểm tra mã" nhưng nó không phải để gỡ lỗi. –

+0

Bạn có muốn kiểm tra progammatically nếu module đã được dỡ xuống? – Chubsdad

Trả lời

4

Có thể một cách ít xấu sau đó Necrolis sẽ sử dụng Microsoft Research's Detours package để nối điểm vào của dll để xem thông báo DLL_PROCESS_DETACH.

Bạn có thể tìm ra điểm mấu chốt cho một HMODULE (như trả về bởi LoadLibrary) sử dụng chức năng này:

#include <windows.h> 
#include <DelayImp.h> 


PVOID GetAddressOfEntryPoint(HMODULE hmod) 
{ 
    PIMAGE_DOS_HEADER pidh = (PIMAGE_DOS_HEADER)hmod; 
    PIMAGE_NT_HEADERS pinth = (PIMAGE_NT_HEADERS)((PBYTE)hmod + pidh->e_lfanew); 
    PVOID pvEntry = (PBYTE)hmod + pinth->OptionalHeader.AddressOfEntryPoint; 

    return pvEntry; 
} 

thay entrypoint của bạn có thể có hành động trực tiếp hoặc tăng số đếm bạn nên kiểm tra cho trong vòng lặp chính của bạn hoặc nơi nó quan trọng đối với bạn. (Và hầu như chắc chắn sẽ gọi điểm vào ban đầu.)

Tôi hy vọng điều này sẽ hữu ích.

+0

Một vấn đề với Detours là bạn phải trả Microsoft $ 10,000 nếu bạn muốn tạo một chương trình 64-bit cho chương trình của mình. Phiên bản miễn phí thiếu hỗ trợ x64 và phiên bản không miễn phí cực kỳ tốn kém. Điều này đã loại trừ việc sử dụng Đường vòng mỗi khi tôi bị cám dỗ. :( –

+0

Ý tưởng hay.Tôi sẽ thử điều này.Trên một lưu ý: Tôi đã thử tẩy rửa FreeLibrary() để hoàn thành mục tiêu của tôi.Trong đường vòng tôi sẽ gọi FreeLibrary() trampoline và sau đó sử dụng GetModuleHandle() để xem Khi tôi sử dụng MSDetours, nó không hoạt động.Tôi không biết tại sao nhưng khi tôi sử dụng MSDetours để làm mất đi một hàm DLL, DLL dường như chưa bao giờ được dỡ bỏ trừ khi –

+0

+ ví dụ tốt ... – Baba

9

Một cách rất xấu (được sử dụng bởi starcraft 2), là làm cho chương trình của bạn gắn vào itsself sau đó theo dõi cho sự kiện dll dỡ bỏ debug (http://msdn.microsoft.com/en-us/library/ms679302(VS.85).aspx), nếu không bạn có muốn cần phải IAT móc FreeLibraryFreeLibraryEx trong quá trình hoặc hotpatch các hàm trong kernel32 chúng theo dõi các tên đang được truyền và số tham chiếu toàn cầu.

+0

Đây là một cách khá hợp lý. –

+0

Tôi có một số mã mà detours FreeLibrary() để làm điều này, nhưng sử dụng GetModuleHandle() để xem nếu mô-đun vẫn được nạp. Có cái gì mà chỉ có thể kiểm tra số lượng tham chiếu? –

+0

Bạn sẽ cần phải trải qua PEB, may mắn là tất cả trong các tiêu đề cửa sổ, bài viết này sẽ giúp: http://www.securityxploded.com/dllrefcount.php theo cách này bạn có thể kiểm tra xem tải cuối cùng của nó trước khi dỡ bỏ nó :) – Necrolis

5

Hãy thử sử dụng LdrRegisterDllNotification nếu bạn đang sử dụng Vista trở lên. Nó yêu cầu sử dụng GetProcAddress để tìm địa chỉ hàm từ ntdll.dll, nhưng đó là cách thích hợp để thực hiện nó.

+0

Đừng fiddle với các chức năng riêng tư, đây là một ý tưởng tồi. –

+1

Nó không kém phần "riêng tư" hơn bất cứ điều gì mà OP đang cố gắng làm. Vui lòng giải thích sự ác cảm kỳ lạ của bạn với API Native rõ ràng hơn một chút. – wj32

4

@Necrolis, liên kết của bạn tới “The covert way to find the Reference Count of DLL” quá hấp dẫn để tôi bỏ qua vì nó chứa các chi tiết kỹ thuật cần thiết để triển khai giải pháp thay thế này (mà tôi nghĩ đến ngày hôm qua, nhưng thiếu Windows Internals). Cảm ơn. Tôi đã bình chọn cho câu trả lời của bạn vì liên kết bạn đã chia sẻ.

Các bài viết liên quan cho thấy làm thế nào để đến được với các nội LDR_MODULE:

struct _LDR_MODULE 
    { 
     LIST_ENTRY InLoadOrderModuleList; 
     LIST_ENTRY InMemoryOrderModuleList; 
     LIST_ENTRY InInitializationOrderModuleList; 
     PVOID BaseAddress; 
     PVOID EntryPoint; 
     ULONG SizeOfImage; 
     UNICODE_STRING FullDllName; 
     UNICODE_STRING BaseDllName; 
     ULONG Flags; 
     USHORT LoadCount; 
     USHORT TlsIndex; 
     LIST_ENTRY HashTableEntry; 
     ULONG TimeDateStamp; 
    } LDR_MODULE, *PLDR_MODULE; 

Ngay ở đây chúng tôi có EntryPoint, con trỏ bên trong Window để điểm nhập của module. Đối với một dll là DllMain (hoặc chức năng thời gian chạy ngôn ngữ cuối cùng gọi là DllMain). Nếu chúng ta thay đổi điều đó thì sao? Tôi đã viết một bài kiểm tra và có vẻ như nó hoạt động, ít nhất là trên XP. Móc DllMain được gọi với lý do DLL_PROCESS_DETACH ngay trước khi tải DLL xuống.

BaseAddress có cùng giá trị với số HMODULE và hữu ích cho việc tìm kiếm đúng LDR_MODULE. Các LoadCount là ở đây vì vậy chúng tôi có thể theo dõi đó.Và cuối cùng, FullDllName hữu ích cho việc gỡ lỗi và giúp tìm kiếm tên DLL thay vì HMODULE.

Đây là tất cả nội bộ của Windows. Đó là (chủ yếu) được ghi lại, nhưng the MSDN documentation cảnh báo “ZwQueryInformationProcess có thể bị thay đổi hoặc không khả dụng trong các phiên bản sau của Windows.”

Dưới đây là ví dụ đầy đủ (nhưng không kiểm tra lỗi đầy đủ). Dường như nó hoạt động nhưng không thấy nhiều thử nghiệm.

// HookDllEntryPoint.cpp by Jim Harkins (jimhark), Nov 2010 

#include "stdafx.h" 
#include <stdio.h> 
#include <winternl.h> 

#include <process.h> // for _beginthread, only needed for testing 


typedef NTSTATUS(WINAPI *pfnZwQueryInformationProcess)(
    __in  HANDLE ProcessHandle, 
    __in  PROCESSINFOCLASS ProcessInformationClass, 
    __out  PVOID ProcessInformation, 
    __in  ULONG ProcessInformationLength, 
    __out_opt PULONG ReturnLength); 

HMODULE hmodNtdll = LoadLibrary(_T("ntdll.dll")); 

// Should test pZwQueryInformationProcess for NULL if you 
// might ever run in an environment where this function 
// is not available (like future version of Windows). 

pfnZwQueryInformationProcess pZwQueryInformationProcess = 
    (pfnZwQueryInformationProcess)GetProcAddress(
     hmodNtdll, 
     "ZwQueryInformationProcess"); 

typedef BOOL(WINAPI *PDLLMAIN) (
    __in HINSTANCE hinstDLL, 
    __in DWORD fdwReason, 
    __in LPVOID lpvReserved); 


// Note: It's possible for pDllMainNew to be called before 
// HookDllEntryPoint returns. If pDllMainNew calls the old 
// function, it should pass a pointer to the variable used 
// so we can set it here before we hook. 

VOID HookDllEntryPoint(
    HMODULE hmod, PDLLMAIN pDllMainNew, PDLLMAIN *ppDllMainOld) 
{ 
    PROCESS_BASIC_INFORMATION pbi = {0}; 
    ULONG ulcbpbi = 0; 

    NTSTATUS nts = (*pZwQueryInformationProcess)(
      GetCurrentProcess(), 
      ProcessBasicInformation, 
      &pbi, 
      sizeof(pbi), 
      &ulcbpbi); 

    BOOL fFoundMod = FALSE; 
    PLIST_ENTRY pcurModule = 
     pbi.PebBaseAddress->Ldr->InMemoryOrderModuleList.Flink; 

    while (!fFoundMod && pcurModule != 
     &pbi.PebBaseAddress->Ldr->InMemoryOrderModuleList) 
    { 
     PLDR_DATA_TABLE_ENTRY pldte = (PLDR_DATA_TABLE_ENTRY) 
       (CONTAINING_RECORD(
        pcurModule, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks)); 

     // Note: pldte->FullDllName.Buffer is Unicode full DLL name 
     //  *(PUSHORT)&pldte->Reserved5[1] is LoadCount 

     if (pldte->DllBase == hmod) 
     { 
      fFoundMod = TRUE; 
      *ppDllMainOld = (PDLLMAIN)pldte->Reserved3[0]; 
      pldte->Reserved3[0] = pDllMainNew; 
     } 

     pcurModule = pcurModule->Flink; 
    } 

    return; 
} 


PDLLMAIN pDllMain_advapi32 = NULL; 

BOOL WINAPI DllMain_advapi32(
    __in HINSTANCE hinstDLL, 
    __in DWORD fdwReason, 
    __in LPVOID lpvReserved) 
{ 
    char *pszReason; 

    switch (fdwReason) 
    { 
    case DLL_PROCESS_ATTACH: 
     pszReason = "DLL_PROCESS_ATTACH"; 
     break; 
    case DLL_PROCESS_DETACH: 
     pszReason = "DLL_PROCESS_DETACH"; 
     break; 
    case DLL_THREAD_ATTACH: 
     pszReason = "DLL_THREAD_ATTACH"; 
     break; 
    case DLL_THREAD_DETACH: 
     pszReason = "DLL_THREAD_DETACH"; 
     break; 
    default: 
     pszReason = "*UNKNOWN*"; 
     break; 
    } 

    printf("\n"); 
    printf("DllMain(0x%.8X, %s, 0x%.8X)\n", 
     (int)hinstDLL, pszReason, (int)lpvReserved); 
    printf("\n"); 

    if (NULL == pDllMain_advapi32) 
    { 
     return FALSE; 
    } 
    else 
    { 
     return (*pDllMain_advapi32)(
      hinstDLL, 
      fdwReason, 
      lpvReserved); 
    } 
} 

void TestThread(void *) 
{ 
    // Do nothing 
} 

// Test HookDllEntryPoint 
int _tmain(int argc, _TCHAR* argv[]) 
{ 
    HMODULE hmodAdvapi = LoadLibrary(L"advapi32.dll"); 
    printf("advapi32.dll Base Addr: 0x%.8X\n", (int)hmodAdvapi); 

    HookDllEntryPoint(
     hmodAdvapi, DllMain_advapi32, &pDllMain_advapi32); 

    _beginthread(TestThread, 0, NULL); 
    Sleep(1000); 

    return 0; 
} 
+0

@ilmcuts, cảm ơn chỉnh sửa/chỉnh sửa – jimhark

+0

Nếu tôi đang cố gắng phát hiện các tệp DLL được tải/không tải trong khi gỡ lỗi, cách tiếp cận này vẫn áp dụng? – adub3

Các vấn đề liên quan