bb107 / MemoryModulePP

MemoryModule which compatible with Win32 API and support exception handling
MIT License
357 stars 95 forks source link

被加载 dll 报错:系统找不到指定的文件 #37

Closed idigger closed 10 months ago

idigger commented 11 months ago

问题复现如下:

http://jacquelin.potier.free.fr/HeliumHexEditor 下载64位版,解压将其中的 HexControl64.dll 改为 HexControl64_orig.dll.

然后编译自己的 HexControl64.dll从内存加载HexControl64_orig.dll,主要程序如下:

#include <windows.h>

#define RETTYPE __int64
#define CREATEHEXCONTROLOBJECT "CreateHexControlObject"
#define DESTROYHEXCONTROLOBJECT "DestroyHexControlObject"

typedef RETTYPE (*CreateHexControlObject_t)();
typedef RETTYPE (*DestroyHexControlObject_t)();

CreateHexControlObject_t OrigCreateHexControlObject = NULL;
DestroyHexControlObject_t OrigDestroyHexControlObject = NULL;

void LoadOrigDll()
{
  size_t DllSize;
  PVOID lpDll = ReadDllFile("HexControl64_orig.dll", &DllSize);
  do {
     CHAR currPath[MAX_PATH+1];
     GetCurrentDirectory(MAX_PATH, currPath);
     HMODULE m = LoadLibraryMemoryExA(lpDll, 0, NULL, currPath, 0);
     OrigCreateHexControlObject = (CreateHexControlObject_t)GetProcAddress(m, CREATEHEXCONTROLOBJECT);
     OrigDestroyHexControlObject = (DestroyHexControlObject_t)GetProcAddress(m, DESTROYHEXCONTROLOBJECT);
  } while (0);
}

extern "C" __declspec(dllexport) RETTYPE CreateHexControlObject()
{
    RETTYPE r = 0;
    if (OrigCreateHexControlObject == NULL) {
        LoadOrigDll();
    }
    if (OrigCreateHexControlObject)
        r = OrigCreateHexControlObject();
    return r;
}

extern "C" __declspec(dllexport) RETTYPE DestroyHexControlObject()
{
    RETTYPE r = 0;
    if (OrigDestroyHexControlObject == NULL) {
        LoadOrigDll();
    }
    if (OrigDestroyHexControlObject)
        r = OrigDestroyHexControlObject();
    return r;
}

BOOL WINAPI DllMain(
    HINSTANCE hinstDLL,
    DWORD fdwReason,
    LPVOID lpReserved )
{
    switch( fdwReason ) 
    { 
        case DLL_PROCESS_ATTACH:
            break;
        case DLL_THREAD_ATTACH:
            break;
        case DLL_THREAD_DETACH:
            break;
        case DLL_PROCESS_DETACH:
            break;
    }
    return TRUE;
}

运行程序 HeliumHexEditor64.exe 后HexControl64_orig.dll加载成功,但弹窗报错,关闭报错窗口可继续使用。

aaaa

使用 MemoryModule 或 RDI 方法加载不会报错,但程序不稳定,启动10次会出现几次crash。

麻烦帮忙看看是哪里的问题造成的。

另外:LoadLibraryMemoryExA 的参数 Flag 没有传递给 LoadLibraryMemoryExW

bb107 commented 10 months ago

你好,感谢反馈。HexControl64.dll会访问源DLL文件,而内存加载使用了随机文件名,所以会报错。解决的方法有两个:

  1. 加载时指定DLL文件名和路径(文件必须存在)
    
    CHAR currPath[MAX_PATH + 1];
    GetCurrentDirectoryA(MAX_PATH, currPath);

HMODULE m = LoadLibraryMemoryExA(lpDll, 0, "HexControl64_orig.dll", currPath, 0);

2. 修补程序(不推荐,版本号2.6.6.1)
```  C++
unsigned char buf[] = { 0x00,0x85 };
HMODULE m = LoadLibraryMemoryExA(lpDll, 0, nullptr, nullptr, 0);
if (m) {
        WriteProcessMemory(GetCurrentProcess(), PBYTE(m) + 0xB01ED, &buf[1], 1, nullptr);
        WriteProcessMemory(GetCurrentProcess(), PBYTE(m) + 0xB03B8, &buf[0], 1, nullptr);
}

MMPP存在bug,方法1会导致程序崩溃,稍后我会更新代码

idigger commented 10 months ago

多谢回复,方法1试过,确实导致崩溃,静待佳音 顺便把 LoadLibraryMemoryExA 的参数 Flags 没有传给 LoadLibraryMemoryExW 的问题也更新一下吧

bb107 commented 10 months ago

代码已经合并到master分支,本地测试能正常运行。

idigger commented 10 months ago

经测试以上情景确实不再出错了,感谢快速修复。 以下情景还是报错:系统找不到指定的文件 1、将 HexControl64_orig.dll 改名为 HexControl64_orig.dat 从内存加载 2、将 HexControl64_orig.dll 编译到资源中从内存加载(此时硬盘中不存在HexControl64_orig.dll) 以上实际就是想实现纯粹从内存中加载HexControl64_orig.dll,而不是从硬盘读出再从内存加载 麻烦看看能不能实现这个功能。

还有一种情景也是出错 3、就是将 MMPP 编译成dll 打包到程序资源中从内存加载这个 MMPP.dll,再用内存中的 MMPP 加载内存中的HexControl64_orig.dll(此需求是因为我的程序是c编的,故需先将MMPP编译成dll以便调用)

以上3种情景用MM或sRDI方法是可以实现不报错的,只是不太稳定,运行10次会有几次崩溃。

bb107 commented 10 months ago
  1. 如果文件名不是以.dll结尾,MMPP会补上扩展名。实际要访问的文件是HexControl64_orig.dat.dll,因此会报错。
  2. MMPP是支持纯内存加载的,访问原文件是HexControl64_orig.dll的行为,与加载器无关。要实现这个目的需要修补这个文件,或者HOOK几个文件相关的API:CreateFileW; GetFileSize; ReadFile; CloseHandle。可以参考MmpDotNet.cpp的实现。
  3. MMPP支持编译为DLL并在内存自加载,需要在配置管理器中为MemoryModule选择Dll结尾的配置。编译为Dll的MMPP会导出ReflectiveLoader函数,调用这个函数就能实现自加载,返回值是MMPP的模块句柄。 cm

MM和RDI不报错的原因是它们没有处理PEB里的LDR链表。这也将导致进程运行不稳定。

//
// 自加载MMPP示例
//

#include "../MemoryModule/stdafx.h"
#include "../MemoryModule/LoadDllMemoryApi.h"
#include <cstdio>
#pragma comment(lib,"ntdll.lib")

PMMP_GLOBAL_DATA MmpGlobalDataPtr;

decltype(&LdrLoadDllMemoryExW)__LdrLoadDllMemoryExW;
decltype(&LdrUnloadDllMemory)__LdrUnloadDllMemory;

static void DisplayStatus() {
    printf(
        "\
MemoryModulePP [Version %d.%d%s]\n\n\t\
MmpFeatures = %08X\n\n\t\
LdrpModuleBaseAddressIndex = %p\n\t\
NtdllLdrEntry = %p\n\t\
RtlRbInsertNodeEx = %p\n\t\
RtlRbRemoveNode = %p\n\n\t\
LdrpInvertedFunctionTable = %p\n\n\t\
LdrpHashTable = %p\n\n\
",
        MmpGlobalDataPtr->MajorVersion,
        MEMORY_MODULE_GET_MINOR_VERSION(MmpGlobalDataPtr->MinorVersion),
        MEMORY_MODULE_IS_PREVIEW(MmpGlobalDataPtr->MinorVersion) ? " Preview" : "",
        MmpGlobalDataPtr->MmpFeatures,
        MmpGlobalDataPtr->MmpBaseAddressIndex->LdrpModuleBaseAddressIndex,
        MmpGlobalDataPtr->MmpBaseAddressIndex->NtdllLdrEntry,
        MmpGlobalDataPtr->MmpBaseAddressIndex->_RtlRbInsertNodeEx,
        MmpGlobalDataPtr->MmpBaseAddressIndex->_RtlRbRemoveNode,
        MmpGlobalDataPtr->MmpInvertedFunctionTable->LdrpInvertedFunctionTable,
        MmpGlobalDataPtr->MmpLdrEntry->LdrpHashTable
    );
}

static PVOID ReadDllFile(LPCSTR FileName) {
    LPVOID buffer;
    size_t size;
    FILE* f;
    fopen_s(&f, FileName, "rb");
    if (!f)return 0;
    _fseeki64(f, 0, SEEK_END);
    if (!(size = _ftelli64(f))) {
        fclose(f);
        return 0;
    }
    _fseeki64(f, 0, SEEK_SET);

    buffer = VirtualAlloc(0, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

    fread(buffer, 1, size, f);
    fclose(f);
    return buffer;
}

PVOID ReadDllFile2(LPCSTR FileName) {
    CHAR path[MAX_PATH + 4];
    DWORD len = GetModuleFileNameA(nullptr, path, sizeof(path));

    if (len) {
        while (len && path[len] != '\\') --len;

        if (len) {
            strcpy_s(&path[len + 1], sizeof(path) - len - 1, FileName);
            return ReadDllFile(path);
        }
    }

    return nullptr;
}

ULONG_PTR ReflectiveLoaderOffset() {
    ULONG_PTR offset = 0;

    auto hm = LoadLibrary(L"MemoryModule.dll");
    if (hm) {
        auto pfn = GetProcAddress(hm, "ReflectiveLoader");
        offset = ULONG_PTR(pfn) - ULONG_PTR(hm);

        auto header = RtlImageNtHeader(hm);
        auto section = IMAGE_FIRST_SECTION(header);
        for (int i = 0; i < header->FileHeader.NumberOfSections; ++i, ++section) {
            if (offset >= section->VirtualAddress && offset < section->VirtualAddress + section->SizeOfRawData) {
                offset = ULONG_PTR(pfn) - (ULONG_PTR(hm) + section->VirtualAddress) + section->PointerToRawData;
                break;
            }
        }

        FreeLibrary(hm);
    }

    return offset;
}

typedef ULONG_PTR(WINAPI* LOADER)(PVOID);

int main() {
    auto buffer = ReadDllFile2("MemoryModule.dll");
    auto loader = LOADER(ULONG_PTR(buffer) + 0x1221); //ReflectiveLoaderOffset() -> 0x1221
    auto hm = (HMODULE)loader(buffer);

    MmpGlobalDataPtr = *(PMMP_GLOBAL_DATA*)GetProcAddress(hm, "MmpGlobalDataPtr");
    __LdrLoadDllMemoryExW = (decltype(&LdrLoadDllMemoryExW))GetProcAddress(hm, "LdrLoadDllMemoryExW");
    __LdrUnloadDllMemory = (decltype(&LdrUnloadDllMemory))GetProcAddress(hm, "LdrUnloadDllMemory");

    DisplayStatus();

    return 0;
}
idigger commented 10 months ago

以下程序vs2019编译后运行结果 1: 0000018FF1820000 2: 0000018FF1860000 然后崩溃,查日志异常代码: 0xc0000005 如果注释掉以下两行

    MmpGlobalDataPtr = *(PMMP_GLOBAL_DATA*)GetProcAddress(hm, "MmpGlobalDataPtr");
    printf("3: %p\n", MmpGlobalDataPtr);

则运行结果如下 1: 0000016A13570000 2: 0000016A135B0000 4: 0000000000000000, 0000000000000000

MemoryModule.dll / MemoryModule.lib 是vs2019编译的最新版。 不知什么原因造成的获取的地址为0

#include "../MemoryModule/stdafx.h"
#include "../MemoryModule/LoadDllMemoryApi.h"
#include <cstdio>
#pragma comment(lib,"ntdll.lib")
#pragma comment(lib,"MemoryModule.lib")

PMMP_GLOBAL_DATA MmpGlobalDataPtr;

decltype(&LdrLoadDllMemoryExW)__LdrLoadDllMemoryExW;
decltype(&LdrUnloadDllMemory)__LdrUnloadDllMemory;

static void DisplayStatus() {
    printf(
        "\
MemoryModulePP [Version %d.%d%s]\n\n\t\
MmpFeatures = %08X\n\n\t\
LdrpModuleBaseAddressIndex = %p\n\t\
NtdllLdrEntry = %p\n\t\
RtlRbInsertNodeEx = %p\n\t\
RtlRbRemoveNode = %p\n\n\t\
LdrpInvertedFunctionTable = %p\n\n\t\
LdrpHashTable = %p\n\n\
",
        MmpGlobalDataPtr->MajorVersion,
        MEMORY_MODULE_GET_MINOR_VERSION(MmpGlobalDataPtr->MinorVersion),
        MEMORY_MODULE_IS_PREVIEW(MmpGlobalDataPtr->MinorVersion) ? " Preview" : "",
        MmpGlobalDataPtr->MmpFeatures,
        MmpGlobalDataPtr->MmpBaseAddressIndex->LdrpModuleBaseAddressIndex,
        MmpGlobalDataPtr->MmpBaseAddressIndex->NtdllLdrEntry,
        MmpGlobalDataPtr->MmpBaseAddressIndex->_RtlRbInsertNodeEx,
        MmpGlobalDataPtr->MmpBaseAddressIndex->_RtlRbRemoveNode,
        MmpGlobalDataPtr->MmpInvertedFunctionTable->LdrpInvertedFunctionTable,
        MmpGlobalDataPtr->MmpLdrEntry->LdrpHashTable
    );
}

static PVOID ReadDllFile(LPCSTR FileName) {
    LPVOID buffer;
    size_t size;
    FILE* f;
    fopen_s(&f, FileName, "rb");
    if (!f)return 0;
    _fseeki64(f, 0, SEEK_END);
    if (!(size = _ftelli64(f))) {
        fclose(f);
        return 0;
    }
    _fseeki64(f, 0, SEEK_SET);

    buffer = VirtualAlloc(0, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

    fread(buffer, 1, size, f);
    fclose(f);
    return buffer;
}

PVOID ReadDllFile2(LPCSTR FileName) {
    CHAR path[MAX_PATH + 4];
    DWORD len = GetModuleFileNameA(nullptr, path, sizeof(path));

    if (len) {
        while (len && path[len] != '\\') --len;

        if (len) {
            strcpy_s(&path[len + 1], sizeof(path) - len - 1, FileName);
            return ReadDllFile(path);
        }
    }

    return nullptr;
}

ULONG_PTR ReflectiveLoaderOffset() {
    ULONG_PTR offset = 0;

    auto hm = LoadLibrary("MemoryModule.dll");
    if (hm) {
        auto pfn = GetProcAddress(hm, "ReflectiveLoader");
        offset = ULONG_PTR(pfn) - ULONG_PTR(hm);

        auto header = RtlImageNtHeader(hm);
        auto section = IMAGE_FIRST_SECTION(header);
        for (int i = 0; i < header->FileHeader.NumberOfSections; ++i, ++section) {
            if (offset >= section->VirtualAddress && offset < section->VirtualAddress + section->SizeOfRawData) {
                offset = ULONG_PTR(pfn) - (ULONG_PTR(hm) + section->VirtualAddress) + section->PointerToRawData;
                break;
            }
        }

        FreeLibrary(hm);
    }

    return offset;
}

typedef ULONG_PTR(WINAPI* LOADER)(PVOID);

int main() {
    auto buffer = ReadDllFile2("MemoryModule.dll");
    printf("1: %p\n", buffer);
    auto loader = LOADER(ULONG_PTR(buffer) + 0x96a0); //ReflectiveLoaderOffset() -> 0x96a0
    auto hm = (HMODULE)loader(buffer);
    printf("2: %p\n", hm);

    MmpGlobalDataPtr = *(PMMP_GLOBAL_DATA*)GetProcAddress(hm, "MmpGlobalDataPtr");
    printf("3: %p\n", MmpGlobalDataPtr);
    __LdrLoadDllMemoryExW = (decltype(&LdrLoadDllMemoryExW))GetProcAddress(hm, "LdrLoadDllMemoryExW");
    __LdrUnloadDllMemory = (decltype(&LdrUnloadDllMemory))GetProcAddress(hm, "LdrUnloadDllMemory");
    printf("4: %p, %p\n", __LdrLoadDllMemoryExW, __LdrUnloadDllMemory);

    //DisplayStatus();

    return 0;
}
idigger commented 10 months ago

以下代码用vs2019编译运行无误 用 gcc(msys2) 或 clang 编译后运行均会崩溃

错误模块名称: MemoryModule.dll_unloaded,版本: 0.0.0.0,时间戳: 0x657d714a 异常代码: 0xc0000005 错误偏移量: 0x0000000000007be0

崩溃原因是 FreeLibrary(hm); 造成的。

#include <windows.h>
#include <stdio.h>
#pragma comment(lib, "ntdll.lib")

NTSYSAPI
PIMAGE_NT_HEADERS
NTAPI
RtlImageNtHeader(
    PVOID BaseOfImage
    );

ULONG_PTR ReflectiveLoaderOffset() {
    ULONG_PTR offset = 0;
    ULONG_PTR pfn;
    HMODULE hm;

    hm = LoadLibrary("MemoryModule.dll");
    if (hm) {
        pfn = (ULONG_PTR)GetProcAddress(hm, "ReflectiveLoader");
        offset = pfn - (ULONG_PTR)hm;
        PIMAGE_NT_HEADERS header = (PIMAGE_NT_HEADERS)RtlImageNtHeader(hm);
        PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(header);

        for (int i = 0; i < header->FileHeader.NumberOfSections; ++i, ++section) {
            if (offset >= section->VirtualAddress && offset < section->VirtualAddress + section->SizeOfRawData) {
                offset = pfn - ((ULONG_PTR)hm + section->VirtualAddress) + section->PointerToRawData;
                break;
            }
        }
        FreeLibrary(hm);
    }
    else {
        printf("not found MemoryModule.dll");
    }

    return offset;
}

int main() {
    printf("\nReflectiveLoader offset -> %p\n", ReflectiveLoaderOffset());

    return 0;
}
bb107 commented 10 months ago

不能获取到地址的问题我没能复现,不知道你有没有改过什么地方。建议把#pragma comment(lib,"MemoryModule.lib")这一行删除再试试。如果编译报错就把Loader.h的64~72行删除。 c

FreeLibrary崩溃的问题确实存在,因为MMPP会hook一些ntdll的函数,卸载后可能会发生异常。使用LoadLibrary和GetProcAddress是偷懒的做法,应该读取dll然后手动解析导出表。参考https://github.com/stephenfewer/ReflectiveDLLInjection/blob/master/inject/src/LoadLibraryR.c 的实现。

idigger commented 10 months ago

dll 版我没有改动任何地方。 我又测试了一下,你用的dll是编译的 Debug 版,确实没问题,你编译成 Release 版问题就可以复现了(可能是编译优化造成的)。 看样子以后 Debug 版和 Release 版都要测试一下才稳妥。

FreeLibrary崩溃的问题比较怪异,Vc编译的没问题,gcc和clang编译的有问题。

bb107 commented 10 months ago

Release编译会忽略所有的断言,包括里面的表达式也不会求值,所以导致了MMPP没有正确初始化。

idigger commented 10 months ago

可否改一下使Release版可用。

bb107 commented 10 months ago

代码已更新

idigger commented 10 months ago

修改神速,赞,Release版测试运行无误了,感谢感谢。

1: 000001C82C630000
2: 000001C82C860000
3: 000001C82C670000
4: 000001C82C868D70, 000001C82C869400
MemoryModulePP [Version 2.0]

        MmpFeatures = 0000007F

        LdrpModuleBaseAddressIndex = 00007FF9B8E94D98
        NtdllLdrEntry = 000001C82C69DE10
        RtlRbInsertNodeEx = 00007FF9B8D2F5F0
        RtlRbRemoveNode = 00007FF9B8D2ED50

        LdrpInvertedFunctionTable = 00007FF9B8EA8500

        LdrpHashTable = 00007FF9B8E93D60