MemoryModule - 应用编程细节
文章目录
- MemoryModule - 应用编程细节
- 概述
- 笔记
- 实验环境
- 升级MemoryModule,在上下文中加入DLL在内存载入前的信息
- MemoryModule.h
- MemoryModule.cpp
- 实现接口MemoryGetPayload()
- 整理 - 在内存载入的DLL中,取得资源表中的信息,取得载入前的DLL内容载荷
- 备注
- END
MemoryModule - 应用编程细节
概述
MemoryModule将DLL从内存载入,试过了好使。
现在有如下需求:
- 在内存载入的DLL中,对自身对应的载入前实体做完整性的hash校验。
- 在内存载入的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 _WIN64POINTER_LIST *blockedMemory;
#endifuint8_t* pDataInByLoad; // add DLL payload info(pData, lenData) on MEMORYMODULE define's tailint lenDataInByLoad;
} MEMORYMODULE, *PMEMORYMODULE;
修正OutputLastError()的编译错误(工程为vs2019 + unicode)
static inline void
OutputLastError(const char *msg)
{
#ifndef DEBUG_OUTPUTUNREFERENCED_PARAMETER(msg);
#elseLPVOID 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); // bugfixsprintf(tmpmsg, "%s: %s", msg, (const char*)tmp); // bugfixOutputDebugStringA(tmpmsg); // bugfixLocalFree(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 _WIN64FreePointerList(blockedMemory, freeMemory, userdata);
#endifSetLastError(ERROR_OUTOFMEMORY);return NULL;}result->pDataInByLoad = (uint8_t*)data; // add thisresult->lenDataInByLoad = (int)size; // add thisresult->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 _WIN64result->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);// \StringFileInfo\080404b0\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] = '\0';memcpy(pBuf, ucAry_ary_DllForTest_x64Debug, len);DLOG(INFO) << "pBuf = " << (void*)pBuf << ", len = " << len;handle = MemoryLoadLibrary(pBuf, len);if (NULL == handle) {DLOG(INFO) << "Can't load library from memory";assert(false);break;}DLOG(INFO) << "handle = " << handle;// HMEMORYMODULE module, LPCTSTR name, LPCTSTR type, WORD languagehRc = MemoryFindResource(handle, MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION);if (NULL == hRc){DLOG(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(handle, 102, buffer, sizeof(buffer));// i_rc = 6, buffer = _T("测试字符串2")if (i_rc >= 0){buffer[i_rc] = TEXT('\0');}pfn_test = (PFN_test)MemoryGetProcAddress(handle, "test");if (NULL == pfn_test) {break;}// 已经验证过了,只要内存载入DLL成功,MemoryModule库的API在DLL外部调用和接口内部调用,都是正确的。// 因为EXE和DLL在一个进程空间内, 内存指针都是一个(只要内存指针有效,在DLL外部和DLl内部访问的handle(从内存载入DLL的上下文句柄)都是同一个)b = pfn_test(handle);DLOG(INFO) << "pfn_test() = " << b;} while (false);if (NULL != handle) {MemoryFreeLibrary(handle);handle = NULL;}if (NULL != pBuf) {delete[] pBuf;pBuf = NULL;}
}
备注
刚开始,只是简单的用MemoryModule从内存载入DLL
后来有新需求时,如果不借助MemoryModule,而是自己取东西,那得对PE结构有多熟悉才能搞定啊。
查了很多资料,都不理想。
再想想,既然MemoryModule都从内存载入了DLL, 执行DLL接口都好使。那这些PE内部信息,MemoryModule的载入操作应该都门清啊。
回头来看,果真如此。
如果不满足于使用第三方库,而是尽力再了解的深入一些(应该比普通应用再深入一点就行),就有很多好东西可以挖出来用。
END
相关文章:
MemoryModule - 应用编程细节
文章目录 MemoryModule - 应用编程细节概述笔记实验环境升级MemoryModule,在上下文中加入DLL在内存载入前的信息MemoryModule.hMemoryModule.cpp实现接口MemoryGetPayload() 整理 - 在内存载入的DLL中,取得资源表中的信息,取得载入前的DLL内容…...
Java程序CPU持续高,如何排查?
首先找到进程ID jps然后找到该进程用占用cpu高的线程 top -Hp 进程ID将线程ID转化为十六进制 printf “0x%x” 线程ID使用jstack 工具跟踪堆栈定位问题 jstack 进程ID | grep 十六进制线程ID -A 5说明,最后-A 5是打印出来后5行。...
(Java)心得:LeetCode——15.三数之和
一、原题 给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k ,同时还满足 nums[i] nums[j] nums[k] 0 。请你返回所有和为 0 且不重复的三元组。 注意:答案中不可以包含重复的三元组。…...
Rust中忽略JSON反序列化时的不必要字段
在Rust中处理JSON数据时,经常会遇到JSON数据中包含一些在目标数据结构中不存在的字段的情况。如果你使用的是serde和serde_json这两个流行的库来处理JSON,那么有一些简单的方法可以忽略这些不必要的字段。 默认行为:忽略未知字段 在Rust中&…...

UDP多对多组播通信
广播和多播仅应用于UDP。TCP是一个面向连接的协议,TCP一定是点对点的,一点是两个主机来建立连接的,TCP肯定是单播。只有UDP才会使用广播和组播。 如下示例实现一个UDP多对多的组播通信,进程中有收、发两个线程,分别表…...

Linux技术---部署PXE服务器实现批量安装操作系统
部署PXE服务器实现批量安装操作系统 部署PXE服务器实现批量安装操作系统 部署PXE服务器实现批量安装操作系统1.安装相关服务组件1.1 安装tftp和xinetd1.2 安装DHCP服务1.3 准备 Linux 内核、初始化镜像文件、 PXE 引导程序、安装FTP服务并准备安装源1.4 配置启动菜单文件1.5 验…...

日志:打印技巧
一、概览 Unity日志打印技巧 常规日志打印彩色日志日志存储与上传日志开关日志双击溯源 二、常规日志打印 1、打印Hello World 调用堆栈可以很好的帮助我们定位问题,特别是报错的Error日志 Debug.Log("Hello World");Debug.Log("This is a log m…...

二叉树的常见操作
建立树 复制二叉树 计算深度 计算总结点数 计算叶子结点数...

CSS 根据子元素选择父元素,并设置父元素的样式
场景举例:当子元素有增加了一个class时,需要影响其父元素的样式 可以使用":has"伪类来实现选择父元素的效果 <style>.parent:has(.child){background-color: #eee;}p{width:100px;border:1px solid #000;} </style> <body>…...
onnx转trt时,关于动态shape自动配置默认值的脚本
onnx转trt时,关于动态shape自动配置默认值,一般需要指定3个shape,分别是最小最优与最大。但是我们在测试时不想写那么多的代码,能否自动实现3个shape的配置,这里实现了一版。 import osimport tensorrt as trt import…...

实验室无法培养的菌,原来可以这么研究!
厌氧氨氧化(anammox)细菌在全球氮循环和废水氮去除中发挥着至关重要的作用,由于anammox细菌生长缓慢、难以培养等特点,对其生态学和生物学特性知之甚少。近日,凌恩生物合作客户重庆大学陈猷鹏教授团队在《Science of t…...

Xed编辑器开发第一期:使用Rust从0到1写一个文本编辑器
这是一个使用Rust实现的轻量化文本编辑器。学过Rust的都知道,Rust 从入门到实践中间还隔着好几个Go语言的难度,因此,如果你也正在学习Rust,那么恭喜你,这个项目被你捡到了。本项目内容较多,大概会分三期左右陆续发布&a…...

农业自动气象监测站:赋能智慧农业的新动力
在信息化、智能化快速发展的今天,农业领域也迎来了前所未有的变革。其中,农业自动气象监测站作为智慧农业的重要组成部分,正在发挥着越来越重要的作用。它们如同农业生产的“眼睛”和“耳朵”,实时感知和记录着大气的微妙变化&…...

2-6 任务 猜数小游戏(单次版)
本任务要求编写一个猜数小游戏(单次版),游戏规则是计算机产生一个0到100之间的随机整数,用户通过输入猜测的数字进行猜测,根据猜测情况给出提示,直到猜对为止。编程思路是利用while循环和多分支结构实现永真…...
springboot 定时任务解决方案
Scheduled (springboot 自带的 注解) 基于注解Scheduled默认为单线程,开启多个任务时,任务的执行时机会受上一个任务执行时间的影响。 EnableScheduling注解: 在配置类上使用,开启计划任务的支持(类上)。…...

谷粒商城实战(024 业务-订单模块-分布式事务1)
Java项目《谷粒商城》架构师级Java项目实战,对标阿里P6-P7,全网最强 总时长 104:45:00 共408P 此文章包含第284p-第p290的内容 简介 模拟积分服务出异常,前方的锁库存事务未回滚,这时候就需要分布式事务 本地事务 事务的隔离…...
.NET使用Microsoft.IdentityModel.Tokens对SAML2.0登录断言校验
如题。使用SAML单点登录对IDP返回的Response断言使用微软提供的Microsoft.IdentityModel.Tokens对断言(Assertion)进行校验。 首先需要安装Muget包,Microsoft.IdentityModel.Tokens和Microsoft.IdentityModel.Tokens.Saml。 简易示例代码如…...

性能测试学习二
瓶颈的精准判断 TPS曲线 tps图 响应时间图 拐点在哪里呢? 这是一个阶梯式增加的场景,拐点在第二个压力阶梯上就出现了,因为响应时间增加了,tps增加的却不多,在第三个阶段时,tps增加的就更少了,响应时间也在不断增加,所以性能瓶颈在加剧,越往后越明显【tps的增长,…...

小丑的身份证和复印件 (BFS + Floyd)
本题链接:登录—专业IT笔试面试备考平台_牛客网 题目: 样例: 输入 2 10 (JOKERjoke #####asdr) 输出 12 思路: 根据题意,要求最短时间,实际上也可以理解为最短距离。 所以应该联想到有关最短距离的算法&…...

C++类与对象(上)
C类与对象 面向过程和面向对象初步认识类的引入类的定义类的两种定义方式: 类的访问限定符及封装访问限定符 封装类的作用域类的实例化类对象模型如何计算类对象的大小结构体内存对齐规则: this指针 面向过程和面向对象初步认识 C语言是面向过程的&…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型
摘要 拍照搜题系统采用“三层管道(多模态 OCR → 语义检索 → 答案渲染)、两级检索(倒排 BM25 向量 HNSW)并以大语言模型兜底”的整体框架: 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后,分别用…...

微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
【HTTP三个基础问题】
面试官您好!HTTP是超文本传输协议,是互联网上客户端和服务器之间传输超文本数据(比如文字、图片、音频、视频等)的核心协议,当前互联网应用最广泛的版本是HTTP1.1,它基于经典的C/S模型,也就是客…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用
文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么?1.1.2 感知机的工作原理 1.2 感知机的简单应用:基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...