【问题标题】:How to get module image name from an arbitrary address in Windows kernel space?如何从 Windows 内核空间中的任意地址获取模块映像名称?
【发布时间】:2018-12-10 13:40:31
【问题描述】:

我正在尝试查看如何从内核代码中的任意地址获取已加载的模块映像名称。

在用户模式下我会这样做:

void* pAddr;
VOID* pBase;
WCHAR buff[MAX_PATH] = {0};

//Get address of some function in some module (just to test it)
pAddr = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "GetCurrentProcess");

//Get module base address
RtlPcToFileHeader(pAddr, &pBase);

//Get module image file name
GetModuleFileNameEx(GetCurrentProcess(), (HMODULE)pBase, buff, SIZEOF(buff));

如果我有pAddr 可以指向内核或用户空间中的某个地址,有没有办法在内核模式下做同样的事情?


编辑:在等待答案时,我想出了自己的代码(使用未记录的遍历 PEB 的方式):

#ifdef CALLING_FROM_KERNEL_MODE
//Kernel mode
TEB* pTEB = (TEB*)PsGetCurrentThreadTeb();

#else
//User mode

#if defined(_M_X64)
//64-bit
TEB* pTEB = reinterpret_cast<TEB*>(__readgsqword(reinterpret_cast<DWORD_PTR>(&static_cast<NT_TIB*>(nullptr)->Self)));
#else
//32-bit
TEB* pTEB = reinterpret_cast<TEB*>(__readfsdword(reinterpret_cast<DWORD_PTR>(&static_cast<NT_TIB*>(nullptr)->Self)));
#endif

#endif

PEB* p_PEB = pTEB->ProcessEnvironmentBlock;
PEB_LDR_DATA* pPLD = p_PEB->Ldr;

const WCHAR* pModName = NULL;

LIST_ENTRY* pLE = &pPLD->InMemoryOrderModuleList;
LIST_ENTRY* pLE_Head = pLE;
while(pLE_Head != pLE->Flink)
{
    PLDR_DATA_TABLE_ENTRY pLDTE = CONTAINING_RECORD(pLE, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);

    size_t szcbSizeOfImg = (size_t)pLDTE->Reserved3[1];
    if((size_t)pAddr - (size_t)pLDTE->DllBase < szcbSizeOfImg)
    {
        pModName = pLDTE->FullDllName.Buffer;

        break;
    }

    pLE = pLE->Flink;
}

问题是虽然它在用户模式下工作,但从内核模式PsGetCurrentThreadTeb() 似乎返回NULL。这是否意味着内核线程没有TEB

【问题讨论】:

  • ZwQuerySystemInformationNt*(取决于以前的模式)与 SystemModuleInformation 并通过 RTL_PROCESS_MODULES 迭代
  • 你称之为什么线程?内核模式线程没有 TEB。它为 0。所以我假设您在内核模式线程的上下文中调用它
  • @RbMm 是的内核模式。那么PEB呢?还有一个侧面问题。如果没有 TEB 为什么会有nt!PsGetCurrentThreadTeb 函数?
  • 因为 PsGetCurrentThreadTeb 返回当前线程的 TEB 值。这里不清楚什么? 那么 PEB 怎么样? - 怎么样 - 用于什么过程?
  • 看起来你不明白。我的意思是在你调用的线程的上下文中。不是来自内核或用户模式。仅存在内核模式线程。存在用户模式线程。

标签: c windows kernel portable-executable


【解决方案1】:

这可以通过ZwQuerySystemInformationSystemModuleInformation 创建所有已加载模块的列表来完成

void fgt(PVOID *Callers, ULONG Count)
{
    NTSTATUS status;

    ULONG cb = 0x10000;
    do 
    {
        status = STATUS_INSUFFICIENT_RESOURCES;

        if (PRTL_PROCESS_MODULES prpm = (PRTL_PROCESS_MODULES)ExAllocatePool(PagedPool, cb))
        {
            if (0 <= (status = NtQuerySystemInformation(SystemModuleInformation, prpm, cb, &cb)))
            {
                do 
                {
                    PVOID Caller = *Callers++;

                    if (ULONG NumberOfModules = prpm->NumberOfModules)
                    {
                        PRTL_PROCESS_MODULE_INFORMATION Modules = prpm->Modules;

                        do 
                        {
                            if ((SIZE_T)Caller - (SIZE_T)Modules->ImageBase < Modules->ImageSize)
                            {
                                DbgPrint("%p> %s\n", Caller, Modules->FullPathName);
                                break;
                            }
                        } while (Modules++, --NumberOfModules);
                    }

                } while (--Count);
            }
            ExFreePool(prpm);
        }

    } while (status == STATUS_INFO_LENGTH_MISMATCH);
}

【讨论】:

  • 谢谢!顺便说一句,这是在循环中进行内存分配的一种非常聪明的方法。您的方法非常适合检索所有已加载的驱动程序。我也在考虑对用户模式地址做同样的事情(对于来自内核模式的代码)。在等待时,我正在研究自己的方法。我在上面的问题中发布了它。你知道为什么我在那里得到一个 NULL teb 吗?
  • 顺便说一句,here's definitions 以上代码中未记录的结构。
猜你喜欢
  • 2011-09-09
  • 1970-01-01
  • 2021-06-10
  • 2012-05-28
  • 2016-08-23
  • 2016-05-05
  • 1970-01-01
  • 2012-08-01
  • 2021-01-07
相关资源
最近更新 更多