MemoryModule将DLL从内存载入,试过了好使。
现在有如下需求:
需求1,官方没提供内存载入之前的实体信息,这个要自己改,在CTX上加入载入前的信息(pData, lenData)
需求2,官方演示了前半段,演示的不全。需要自己整理一下。
vs2019 x64 unicode debug mfc dlg
/**
* 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.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
// ...
}
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;
}
以下是刚做完实验的草稿代码,能看出以上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的载入操作应该都门清啊。
回头来看,果真如此。
如果不满足于使用第三方库,而是尽力再了解的深入一些(应该比普通应用再深入一点就行),就有很多好东西可以挖出来用。