MemoryModule - 应用编程细节

admin2024-05-15  4

文章目录

    • MemoryModule - 应用编程细节
    • 概述
    • 笔记
    • 实验环境
      • 升级MemoryModule,在上下文中加入DLL在内存载入前的信息
        • MemoryModule.h
        • MemoryModule.cpp
        • 实现接口MemoryGetPayload()
    • 整理 - 在内存载入的DLL中,取得资源表中的信息,取得载入前的DLL内容载荷
    • 备注
    • END

MemoryModule - 应用编程细节

概述

MemoryModule将DLL从内存载入,试过了好使。
现在有如下需求:

  1. 在内存载入的DLL中,对自身对应的载入前实体做完整性的hash校验。
  2. 在内存载入的DLL中,取得资源信息(文件版本资源中的版本信息等,字符串资源)

需求1,官方没提供内存载入之前的实体信息,这个要自己改,在CTX上加入载入前的信息(pData, lenData)
需求2,官方演示了前半段,演示的不全。需要自己整理一下。

笔记

实验环境

vs2019 x64 unicode debug mfc dlg

升级MemoryModule,在上下文中加入DLL在内存载入前的信息

MemoryModule.h
/**
 * Get address of exported method. Supports loading both by name and by
 * ordinal value.
 */
FARPROC MemoryGetProcAddress(HMEMORYMODULE, LPCSTR);

// 增加接口, 取DLL在内存中载入前的载荷(pData, lenData)
// 有了这个接口, 在DLL中就可以拿载入前的DLL数据做完整性校验(可以知道是不是被逆向工程师改了)
// 这个接口的好处,不用将内存载入前的信息(pData, lenData)通过DLL接口传进去,那样比较扎眼。
// 有了这个接口,只需要DLL提供一个接口,将上下文HMEMORYMODULE hDllWasLoad传进去。不容易被人家改
bool MemoryGetPayload(HMEMORYMODULE hDllWasLoad, uint8_t** ppData, int* pLen);


MemoryModule.cpp

将 MemoryModule.c 改名为MemoryModule.cpp

#include "pch.h" // add this for vs2019 build

#include <windows.h>
#include <winnt.h>
#include <stddef.h>
#include <stdint.h> // add this for uint8_t 
#include <tchar.h>
#ifdef DEBUG_OUTPUT
#include <stdio.h>
#endif
typedef struct {
    PIMAGE_NT_HEADERS headers;
    unsigned char *codeBase;
    HCUSTOMMODULE *modules;
    int numModules;
    BOOL initialized;
    BOOL isDLL;
    BOOL isRelocated;
    CustomAllocFunc alloc;
    CustomFreeFunc free;
    CustomLoadLibraryFunc loadLibrary;
    CustomGetProcAddressFunc getProcAddress;
    CustomFreeLibraryFunc freeLibrary;
    struct ExportNameEntry *nameExportsTable;
    void *userdata;
    ExeEntryProc exeEntry;
    DWORD pageSize;
#ifdef _WIN64
    POINTER_LIST *blockedMemory;
#endif
    uint8_t* pDataInByLoad; // add DLL payload info(pData, lenData) on MEMORYMODULE define's tail
    int lenDataInByLoad;
} MEMORYMODULE, *PMEMORYMODULE;

修正OutputLastError()的编译错误(工程为vs2019 + unicode)

static inline void
OutputLastError(const char *msg)
{
#ifndef DEBUG_OUTPUT
    UNREFERENCED_PARAMETER(msg);
#else
    LPVOID tmp;
    char *tmpmsg;
    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&tmp, 0, NULL);
    tmpmsg = (char *)LocalAlloc(LPTR, strlen(msg) + strlen((const char*)tmp) + 3); // bugfix
    sprintf(tmpmsg, "%s: %s", msg, (const char*)tmp); // bugfix
    OutputDebugStringA(tmpmsg); // bugfix
    LocalFree(tmpmsg);
    LocalFree(tmp);
#endif
}

升级MemoryLoadLibraryEx()

HMEMORYMODULE MemoryLoadLibraryEx(const void *data, size_t size,
    CustomAllocFunc allocMemory,
    CustomFreeFunc freeMemory,
    CustomLoadLibraryFunc loadLibrary,
    CustomGetProcAddressFunc getProcAddress,
    CustomFreeLibraryFunc freeLibrary,
    void *userdata)
{
// ...
    result = (PMEMORYMODULE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MEMORYMODULE));
    if (result == NULL) {
        freeMemory(code, 0, MEM_RELEASE, userdata);
#ifdef _WIN64
        FreePointerList(blockedMemory, freeMemory, userdata);
#endif
        SetLastError(ERROR_OUTOFMEMORY);
        return NULL;
    }

    result->pDataInByLoad = (uint8_t*)data; // add this
    result->lenDataInByLoad = (int)size; // add this

    result->codeBase = code;
    result->isDLL = (old_header->FileHeader.Characteristics & IMAGE_FILE_DLL) != 0;
    result->alloc = allocMemory;
    result->free = freeMemory;
    result->loadLibrary = loadLibrary;
    result->getProcAddress = getProcAddress;
    result->freeLibrary = freeLibrary;
    result->userdata = userdata;
    result->pageSize = sysInfo.dwPageSize;
#ifdef _WIN64
    result->blockedMemory = blockedMemory;
#endif
// ...
}
实现接口MemoryGetPayload()
bool MemoryGetPayload(HMEMORYMODULE hDllWasLoad, uint8_t** ppData, int* pLen)
{
    bool b_rc = false;
    PMEMORYMODULE hDllCtx = NULL;

    do {
        if ((NULL == hDllWasLoad) || (NULL == ppData) || (NULL == pLen))
        {
            break;
        }

        hDllCtx = (PMEMORYMODULE)hDllWasLoad;
        *ppData = hDllCtx->pDataInByLoad;
        *pLen = hDllCtx->lenDataInByLoad;

        b_rc = true;
    } while (false);

    return b_rc;
}

整理 - 在内存载入的DLL中,取得资源表中的信息,取得载入前的DLL内容载荷

以下是刚做完实验的草稿代码,能看出以上2个需求的实现了。
再做整理,就可以是正式的代码。

// extern "C" __declspec(dllexport) bool APIENTRY test(HMEMORYMODULE hDll)
typedef bool (APIENTRY* PFN_test)(HMEMORYMODULE hDll); 
// DLL定义了一个接口 e.g. init(HMEMORYMODULE hDll), 只要调用这个接口,就可以在接口内知道DLL在内存载入前的载荷,和载入后的资源信息(STRING by resource ID, FILE VERSION(e.g. 内部exe名称))

#pragma comment(lib, "Version.lib") // VerQueryValue

// _T("CompanyName")
CString get_FileInfoValue(BYTE* byData, const TCHAR* pszValueName)
{
    CString csQuery;
    CString csRc;
    TCHAR* lpVers = NULL;
    unsigned int uLen = 0;

    std::string strA;
    std::wstring strW;

    _ASSERT(NULL != pszValueName);

    // _T("\StringFileInfo\%s\%s") 
    // 拼接要查询的文件版本信息的字符串
    csQuery.Format(_T("\StringFileInfo\%s\%s"), _T("080404b0"), pszValueName);

    _ASSERT(NULL != byData);
    // \StringFileInfo0404b0\CompanyName
    // 取得了文件资源区内容后,就可以直接用WIN32API VerQueryValue(), 这个API和DLL载入的基地址没关系,只和文件版本信息的具体数据有关系。
    VerQueryValue(byData, (LPCWSTR)csQuery, (void**)&lpVers, &uLen);
    if ((uLen > 0) && (NULL != lpVers))
    {
        csRc = lpVers;
    }

    return csRc;
}

void CLoaderDlg::OnBnClickedButton1() {
    uint8_t* pBuf = NULL;
    int len = 0;
    bool b = false;
    HMEMORYMODULE handle = NULL;
    PFN_test pfn_test = NULL;
    HMEMORYRSRC hRc = NULL;
    DWORD resourceSize = 0;
    LPVOID resourceData = NULL;
    CString csTmp;
    int i_rc = 0;
    TCHAR buffer[MAX_PATH + 1];

    do {
        len = sizeof(ucAry_ary_DllForTest_x64Debug);
        DLOG(INFO) << "sizeof(ucAry_ary_DllForTest_x64Debug) = " << len;
        pBuf = new uint8_t[len + 1];
        assert(NULL != pBuf);
        pBuf[len] = ';'memcpy
        (,pBuf, ucAry_ary_DllForTest_x64Debug) len;DLOG
        ()INFO<< "pBuf = " << ( void*)<<pBuf ", len = " << ; len=
        handle MemoryLoadLibrary (,pBuf) len;if

        ( NULL== ) handleDLOG {
            ()INFO<< "Can't load library from memory" ;assert
            ()false;break
            ;}
        DLOG

        ()INFO<< "handle = " << ; handle// HMEMORYMODULE module, LPCTSTR name, LPCTSTR type, WORD language

        =
        hRc MemoryFindResource (,handleMAKEINTRESOURCE ()VS_VERSION_INFO,) RT_VERSION;if
        ( NULL== ) hRcDLOG
        {
            ()INFO<< << hRc " = MemoryFindResourceEx()" ;break
            ;}
        =

        resourceSize MemorySizeofResource (,handle) hRc;=
        resourceData MemoryLoadResource (,handle) hRc;// 已经验证过了,好使

		=
        csTmp get_FileInfoValue ((*BYTE),resourceData_T ("CompanyName"));// csTmp is _T("TODO: <公司名>")
        // IDS_STRING102

        // 已经验证过了,好使
        =
        i_rc MemoryLoadString (,handle102 ,, buffersizeof ()buffer);// i_rc = 6, buffer = _T("测试字符串2")
        if
        ( >=i_rc 0 )[
        {
            buffer]i_rc= TEXT (')';}=
        (

        pfn_test ) MemoryGetProcAddressPFN_test(,"test"handle) ;if(

        NULL ==) break pfn_test; {
            }// 已经验证过了,只要内存载入DLL成功,MemoryModule库的API在DLL外部调用和接口内部调用,都是正确的。
        // 因为EXE和DLL在一个进程空间内, 内存指针都是一个(只要内存指针有效,在DLL外部和DLl内部访问的handle(从内存载入DLL的上下文句柄)都是同一个)

		=
		pfn_test
        b ( );handleDLOG(
        )<<INFO"pfn_test() = " << ; } bwhile
    ( ) ;falseif(

    NULL !=) MemoryFreeLibrary handle( {
        );handle=NULL
        handle ; }if
    (

    NULL !=) [ pBuf] {
        delete;= pBufNULL
        pBuf ; }}
    

备注

END

刚开始,只是简单的用MemoryModule从内存载入DLL
后来有新需求时,如果不借助MemoryModule,而是自己取东西,那得对PE结构有多熟悉才能搞定啊。
查了很多资料,都不理想。
再想想,既然MemoryModule都从内存载入了DLL, 执行DLL接口都好使。那这些PE内部信息,MemoryModule的载入操作应该都门清啊。
回头来看,果真如此。
如果不满足于使用第三方库,而是尽力再了解的深入一些(应该比普通应用再深入一点就行),就有很多好东西可以挖出来用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明原文出处。如若内容造成侵权/违法违规/事实不符,请联系SD编程学习网:675289112@qq.com进行投诉反馈,一经查实,立即删除!